JAMM System Architecture

Architecture Overview

JAMM DEX uses a layered, modular architecture to ensure scalability, security, and maintainability. The system consists of a core contract layer, a library layer, and an interface layer.

Core Contracts

JAMMFactory

Responsibilities: Creates and manages trading pairs.

  • Maintains a registry of all trading pairs.

  • Deploys pair contracts deterministically using CREATE2.

  • Manages protocol fee settings.

  • Handles the registration for the referral system.

Key Functions:

// Creates a new trading pair for tokenA and tokenB with a specific fee
function createPair(address tokenA, address tokenB, uint24 fee) external returns (address pair);

// Retrieves the address of an existing pair
function getPair(address tokenA, address tokenB, uint24 fee) external view returns (address pair);

// Sets a referrer for the message sender
function setReferrer(address _referrer) external;

JAMMPair

Responsibilities: Implements the specific liquidity pool.

  • Maintains reserves of two tokens.

  • Executes token swap logic.

  • Manages adding and removing liquidity.

  • Calculates and allocates trading fees.

Key Functions:

// Mints LP tokens
function mint(address to) external returns (uint liquidity);

// Burns LP tokens
function burn(address to) external returns (uint amount0, uint amount1);

// Swaps tokens
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data, address _referrer) external;

// Returns the token reserves and the last block timestamp
function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);

JAMMRouter

Responsibilities: Provides a user-friendly interface for trading.

  • Offers high-level functions for trades.

  • Handles multi-hop swap paths.

  • Manages deadline checks.

  • Supports automatic conversion of ETH/WJU.

Key Functions:

// Swaps an exact amount of input tokens for as many output tokens as possible
function swapExactTokensForTokens(uint amountIn, uint amountOutMin, address[] calldata path, uint24[] calldata fees, address to, address referrer, uint deadline) external returns (uint[] memory amounts);

// Adds liquidity to a pair
function addLiquidity(address tokenA, address tokenB, uint24 fee, uint amountADesired, uint amountBDesired, uint amountAMin, uint amountBMin, address to, uint deadline) external returns (uint amountA, uint amountB, uint liquidity);

// Removes liquidity from a pair
function removeLiquidity(address tokenA, address tokenB, uint24 fee, uint liquidity, uint amountAMin, uint amountBMin, address to, uint deadline) external returns (uint amountA, uint amountB);

JAMMERC20

Responsibilities: Standard implementation of LP tokens.

  • Implements the ERC-20 standard.

  • Supports the EIP-2612 permit function.

  • Manages the minting and burning of LP tokens.

Key Functions:

// Approves spending tokens via a signature
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

WJU

Responsibilities: Implementation of the wrapped JU token.

  • Provides wrapping and unwrapping of the JU token.

  • Fully compatible with the ERC-20 standard.

  • Supports receiving ETH directly.

Key Functions:

// Wraps JU into WJU
function deposit() public payable;

// Unwraps WJU back to JU
function withdraw(uint amount) public;

Libraries

JAMMLibrary

Provides core mathematical calculations and utility functions:

  • sortTokens(): Sorts token addresses.

  • pairFor(): Calculates the pair address.

  • getReserves(): Fetches reserves.

  • getAmountOut(), getAmountIn(): Calculates swap amounts.

  • getAmountsOut(), getAmountsIn(): Calculates amounts for multi-hop swaps.

Math

Provides safe mathematical operations:

  • sqrt(): Square root calculation (using Babylonian method).

  • min(): Finds the minimum of two numbers.

  • Uses Solidity >=0.8.0 for built-in overflow/underflow protection.

TransferHelper

Provides safe token transfer functions:

  • safeTransfer(): Safe token transfer.

  • safeTransferFrom(): Safe token transfer from another address.

  • safeTransferETH(): Safe ETH transfer.

  • safeApprove(): Safe token approval.

UQ112x112

Provides support for fixed-point number arithmetic:

  • Used for price accumulation.

  • Enables high-precision math.

Data Flow Architecture

Trading Flow

User β†’ JAMMRouter β†’ JAMMPair β†’ Token Contracts
  ↓        ↓         ↓
Check Params β†’ Calc Path β†’ Execute Swap β†’ Update Reserves

Liquidity Management Flow

User β†’ JAMMRouter β†’ JAMMPair β†’ JAMMERC20
  ↓        ↓         ↓         ↓
Add Liquidity β†’ Transfer Tokens β†’ Mint LP β†’ Issue LP Tokens

Security Architecture

Re-entrancy Guard

All critical functions are protected by a re-entrancy lock:

modifier lock() {
    require(unlocked == 1, "JAMM: LOCKED");
    unlocked = 0;
    _;
    unlocked = 1;
}

Mathematical Safety

  • Uses Solidity ^0.8.21 for built-in overflow and underflow protection.

  • The Math library provides a safe sqrt function.

  • The JAMMLibrary includes checks for zero reserves and amounts.

Access Control

  • Key functions in JAMMFactory can only be called by feeToSetter.

  • The initialize function in JAMMPair can only be called by the factory.

  • A referrer can only be set once per user.

Fee Architecture

Fee Tiers

uint24 public constant FEE_0_5_PERCENT = 50;   // 0.5%
uint24 public constant FEE_1_PERCENT = 100;    // 1%
uint24 public constant FEE_2_PERCENT = 200;    // 2%
uint24 public constant FEE_3_PERCENT = 300;    // 3%

Fee Distribution

  • Trading Fees: A portion of the swap fee (fee/50000 or fee/100000) is sent to a designated feeTo address. If a referrer is active, the fee is split between the protocol and the referrer.

  • Protocol Fees: A portion of the liquidity provider fees can be minted as new LP tokens and sent to a mintTo address, calculated based on the growth of the k value.

Address Generation

CREATE2 Deterministic Deployment

Pair addresses are generated using CREATE2, ensuring predictability:

// salt is calculated from the sorted token addresses and the fee
bytes32 salt = keccak256(abi.encodePacked(token0, token1, fee));
// create2 opcode is used to deploy the pair
pair := create2(0, add(bytecode, 32), mload(bytecode), salt)

Init Code Hash

// The hash of the JAMMPair creation code, used in pairFor calculation
bytes32 public constant INIT_CODE_PAIR_HASH = keccak256(type(JAMMPair).creationCode);

Current Value: 0x2e599419feff8382bdfc47abf847537b06e96b0525db3802b2a9fb0bfe068ed8

Event System

Core Events

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

  • Swap(address indexed sender, uint amount0In, uint amount1In, uint amount0Out, uint amount1Out, address indexed to)

  • Mint(address indexed sender, uint amount0, uint amount1)

  • Burn(address indexed sender, uint amount0, uint amount1, address indexed to)

  • Sync(uint112 reserve0, uint112 reserve1)

  • Fee(address indexed sender, address indexed referrer, address token, uint amount)

  • Referrer(address indexed sender, address indexed referrer)

These events provide a complete on-chain data trail for front-end applications and analytics tools.

Last updated