JAMMFactory Contract

JAMMFactory is the core factory contract of JAMM DEX, responsible for creating and managing all trading pairs. It uses CREATE2 deterministic deployment, supports a multi-tier fee system, and has built-in referrer management functionality. The Factory contract is the entry point and control center of the entire protocol.

Contract Basic Information

contract JAMMFactory is IJAMMFactory {
    // Contract Address: 0x6b5d54E6F73e96Ca960DBA71D778b98221939aa6 (Mainnet)
    // Contract Address: 0xbddd716a9d6325700d1c29562c819987e5b1f6a8 (Testnet)
}

Fee Constants

Supported Fee Tiers

uint24 public constant FEE_0_5_PERCENT = 50;   // 0.5%
uint24 public constant FEE_1_PERCENT = 100;    // 1.0%
uint24 public constant FEE_2_PERCENT = 200;    // 2.0%
uint24 public constant FEE_3_PERCENT = 300;    // 3.0%

These four constants define all fee tiers supported by JAMM DEX:

  • Base: 10000 (i.e., 100%)

  • 0.5%: 50/10000 = 0.005

  • 1.0%: 100/10000 = 0.01

  • 2.0%: 200/10000 = 0.02

  • 3.0%: 300/10000 = 0.03

State Variables

Management Addresses

address public override mintTo;        // Protocol fee recipient address
address public override feeTo;         // Trading fee recipient address
address public override feeToSetter;   // Fee setting administrator

Address Description:

  • mintTo: Address receiving protocol fees (in LP token form)

  • feeTo: Address receiving trading fees

  • feeToSetter: Administrator with exclusive permission to modify the above addresses

Trading Pair Registry

mapping(address => mapping(address => mapping(uint24 => address))) public override getPair;
address[] public override allPairs;

Data Structure:

  • getPair[tokenA][tokenB][fee]: Three-dimensional mapping to find trading pairs by token addresses and fee rate

  • allPairs: Array of all trading pair addresses for iteration

Referrer System

mapping(address => address) public referrer;

Records the referrer address corresponding to each user (tx.origin).

Initialization Code Hash

bytes32 public constant override INIT_CODE_PAIR_HASH =
    keccak256(type(JAMMPair).creationCode);

This hash value is used for CREATE2 address calculation to ensure deterministic trading pair addresses. The actual value is determined at contract deployment time.

Core Functions

Constructor

constructor(address _feeToSetter) {
    feeToSetter = _feeToSetter;
}

Sets the fee administrator address during deployment, which is the only initialization parameter.

Create Trading Pair

function createPair(
    address tokenA,
    address tokenB,
    uint24 fee
) external override returns (address pair) {
    require(tokenA != tokenB, "JAMMFactory: IDENTICAL_ADDRESSES");
    require(
        tokenA != address(0) && tokenB != address(0),
        "JAMMFactory: ZERO_ADDRESS"
    );
    (address token0, address token1) = tokenA < tokenB
        ? (tokenA, tokenB)
        : (tokenB, tokenA);
    require(
        fee == FEE_0_5_PERCENT ||
            fee == FEE_1_PERCENT ||
            fee == FEE_2_PERCENT ||
            fee == FEE_3_PERCENT,
        "JAMMFactory: INVALID_FEE"
    );

    require(
        getPair[token0][token1][fee] == address(0),
        "JAMMFactory: PAIR_EXISTS"
    );
    
    bytes memory bytecode = type(JAMMPair).creationCode;
    bytes32 salt = keccak256(abi.encodePacked(token0, token1, fee));
    assembly {
        pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
    }
    JAMMPair(pair).initialize(token0, token1, fee);
    getPair[token0][token1][fee] = pair;
    getPair[token1][token0][fee] = pair; // Bidirectional mapping
    allPairs.push(pair);
    emit PairCreated(token0, token1, fee, pair, allPairs.length);
}

Creation Process:

  1. Parameter Validation: Check token address validity and fee rate legality

  2. Token Sorting: Ensure token0 < token1 to guarantee uniqueness

  3. Duplicate Check: Ensure trading pair with same parameters doesn't exist

  4. CREATE2 Deployment: Deploy new JAMMPair contract using deterministic address

  5. Initialization: Call initialize function of new contract

  6. Registration: Update mapping tables and arrays

  7. Event Emission: Emit PairCreated event

Query Functions

function allPairsLength() external view override returns (uint) {
    return allPairs.length;
}

Returns the total number of created trading pairs.

Management Functions

Set Protocol Fee Recipient Address

function setMintTo(address _mintTo) external override {
    require(msg.sender == feeToSetter, "JAMMFactory: FORBIDDEN");
    mintTo = _mintTo;
}

Set Trading Fee Recipient Address

function setFeeTo(address _feeTo) external override {
    require(msg.sender == feeToSetter, "JAMMFactory: FORBIDDEN");
    feeTo = _feeTo;
}

Set Fee Administrator

function setFeeToSetter(address _feeToSetter) external override {
    require(msg.sender == feeToSetter, "JAMMFactory: FORBIDDEN");
    feeToSetter = _feeToSetter;
}

Access Control: All management functions can only be called by the current feeToSetter.

Referrer Management

function setReferrer(address _referrer) external override {
    require(
        referrer[tx.origin] == address(0),
        "JAMMFactory: REFERER_EXISTS"
    );
    referrer[tx.origin] = _referrer;
    emit Referrer(tx.origin, _referrer);
}

Features:

  • Uses tx.origin instead of msg.sender

  • Each user can only set referrer once

  • Cannot be modified after setting

Event System

PairCreated Event

event PairCreated(
    address indexed token0,
    address indexed token1,
    uint24 fee,
    address pair,
    uint
);

Records the creation of new trading pairs, including:

  • token0: First token address after sorting

  • token1: Second token address after sorting

  • fee: Fee rate

  • pair: Address of newly created trading pair

  • Last parameter: Current total number of trading pairs

Referrer Event

event Referrer(address indexed sender, address indexed referrer);

Records the establishment of referrer relationships.

CREATE2 Address Calculation

Address Pre-calculation

Trading pair addresses can be pre-calculated using the following method:

function computePairAddress(
    address factory,
    address tokenA,
    address tokenB,
    uint24 fee
) pure returns (address pair) {
    (address token0, address token1) = tokenA < tokenB 
        ? (tokenA, tokenB) 
        : (tokenB, tokenA);
    
    pair = address(uint160(uint256(keccak256(abi.encodePacked(
        hex"ff",
        factory,
        keccak256(abi.encodePacked(token0, token1, fee)),
        INIT_CODE_PAIR_HASH // Use actual hash value from Factory contract
    )))));
}

Salt Calculation

bytes32 salt = keccak256(abi.encodePacked(token0, token1, fee));

Salt consists of three parameters:

  • token0: Smaller token address

  • token1: Larger token address

  • fee: Fee rate value

Usage Examples

Create New Trading Pair

const factory = new ethers.Contract(factoryAddress, factoryABI, signer);

// Create USDC/USDT trading pair with 0.5% fee rate
const tx = await factory.createPair(
    "0xUSDC_ADDRESS",
    "0xUSDT_ADDRESS", 
    50 // 0.5%
);

const receipt = await tx.wait();
const pairCreatedEvent = receipt.events.find(e => e.event === 'PairCreated');
const pairAddress = pairCreatedEvent.args.pair;

console.log("New trading pair address:", pairAddress);

Query Trading Pairs

// Query specific trading pair
const pairAddress = await factory.getPair(tokenA, tokenB, fee);
if (pairAddress === ethers.constants.AddressZero) {
    console.log("Trading pair does not exist");
} else {
    console.log("Trading pair address:", pairAddress);
}

// Query all trading pairs
const totalPairs = await factory.allPairsLength();
console.log("Total trading pairs:", totalPairs.toString());

for (let i = 0; i < totalPairs; i++) {
    const pairAddress = await factory.allPairs(i);
    console.log(`Trading pair ${i}:`, pairAddress);
}

Set Referrer

// Check current referrer
const currentReferrer = await factory.referrer(userAddress);
if (currentReferrer === ethers.constants.AddressZero) {
    // Set referrer
    const tx = await factory.setReferrer(referrerAddress);
    await tx.wait();
    console.log("Referrer set successfully");
} else {
    console.log("Existing referrer:", currentReferrer);
}

Listen to Events

// Listen to new trading pair creation
factory.on("PairCreated", (token0, token1, fee, pair, totalPairs) => {
    console.log("New trading pair created:");
    console.log("- Token0:", token0);
    console.log("- Token1:", token1);
    console.log("- Fee rate:", fee);
    console.log("- Address:", pair);
    console.log("- Total:", totalPairs.toString());
});

// Listen to referrer setting
factory.on("Referrer", (sender, referrer) => {
    console.log(`User ${sender} set referrer to ${referrer}`);
});

Last updated