Fee Structure

Overview

JAMM DEX implements a flexible, multi-tier fee system, allowing different trading pairs to select an appropriate fee level based on their characteristics. This design provides an optimized trading experience for assets with varying risk and liquidity profiles.

Fee Tiers

Supported Fees

Four preset fee tiers are defined in JAMMFactory.sol:

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

Fee Validation

When a pair is created, the system validates the fee tier:

// from JAMMFactory.sol
require(
    fee == FEE_0_5_PERCENT ||
        fee == FEE_1_PERCENT ||
        fee == FEE_2_PERCENT ||
        fee == FEE_3_PERCENT,
    "JAMMFactory: INVALID_FEE"
);

Fee Calculation

Trading Fees

Trading fees are deducted from the input token when calculating the swap amount. This logic is implemented in JAMMLibrary.sol:

// from JAMMLibrary.sol
function getAmountOut(
    uint amountIn,
    uint reserveIn,
    uint reserveOut,
    uint24 fee
) internal pure returns (uint amountOut) {
    require(amountIn > 0, "JAMMLibrary: INSUFFICIENT_INPUT_AMOUNT");
    require(reserveIn > 0 && reserveOut > 0, "JAMMLibrary: INSUFFICIENT_LIQUIDITY");
    
    // Fee is deducted from the input amount
    uint amountInWithFee = amountIn * (10000 - fee);
    uint numerator = amountInWithFee * reserveOut;
    uint denominator = reserveIn * 10000 + amountInWithFee;
    amountOut = numerator / denominator;
}

Fee Distribution Mechanism

JAMM DEX features a unique dual fee distribution mechanism, implemented in the _collectFee function in JAMMPair.sol, which depends on whether a referrer is present:

// from JAMMPair.sol
function _collectFee(
    uint amount0In,
    uint amount1In,
    address _referrer
) private {
    address referrer = IJAMMFactory(factory).referrer(tx.origin);
    address feeTo = IJAMMFactory(factory).feeTo();

    if (_referrer == address(0) && referrer == address(0)) {
        // Case 1: No referrer
        _safeTransferFee(token0, feeTo, (amount0In * fee) / 50000);
        _safeTransferFee(token1, feeTo, (amount1In * fee) / 50000);
    } else {
        // Case 2: With referrer
        _safeTransferFee(token0, feeTo, (amount0In * fee) / 100000);
        _safeTransferFee(token1, feeTo, (amount1In * fee) / 100000);
        if (referrer != address(0)) {
            _safeTransferFee(token0, referrer, (amount0In * fee) / 100000);
            _safeTransferFee(token1, referrer, (amount1In * fee) / 100000);
        } else {
            IJAMMFactory(factory).setReferrer(_referrer);
            _safeTransferFee(token0, _referrer, (amount0In * fee) / 100000);
            _safeTransferFee(token1, _referrer, (amount1In * fee) / 100000);
        }
    }
}

Fee Distribution Explained

Case 1: No Referrer

  • Protocol Fee: Collects (amountIn * fee) / 50000 from the input token.

  • Referrer Fee: 0.

Case 2: With Referrer

  • Protocol Fee: Collects (amountIn * fee) / 100000 from the input token.

  • Referrer Fee: Collects (amountIn * fee) / 100000 from the input token.

  • The fee is split evenly between the protocol and the referrer.

Protocol Fee Mechanism

In addition to direct trading fees, JAMM DEX can also collect a protocol fee by minting new LP tokens. This fee is sent to the mintTo address and is managed by the _mintFee function.

// from JAMMPair.sol
function _mintFee(
    uint112 _reserve0,
    uint112 _reserve1
) private returns (bool feeOn) {
    address mintTo = IJAMMFactory(factory).mintTo();
    feeOn = mintTo != address(0);
    uint _kLast = kLast;
    if (feeOn) {
        if (_kLast != 0) {
            uint rootK = Math.sqrt(uint(_reserve0) * uint(_reserve1));
            uint rootKLast = Math.sqrt(_kLast);
            if (rootK > rootKLast) {
                // calculate liquidity to mint as protocol fee
                uint numerator = totalSupply * (rootK - rootKLast) * 8;
                uint denominator = rootK * 17 + rootKLast * 8;
                uint liquidity = numerator / denominator;
                if (liquidity > 0) _mint(mintTo, liquidity);
            }
        }
    } else if (_kLast != 0) {
        kLast = 0;
    }
}

Protocol Fee Calculation

The protocol fee is calculated based on the growth of the liquidity pool (increase in the k value). When the total value of the pool grows due to accumulated trading fees, the protocol mints a proportional amount of new LP tokens as revenue.

Constant Product Validation (K-Value Check)

After each swap, the system verifies the constant product formula to ensure balance after fees are deducted. The swap function in JAMMPair.sol includes this check:

// from JAMMPair.sol
{
    balance0 = IERC20(token0).balanceOf(address(this));
    balance1 = IERC20(token1).balanceOf(address(this));
    
    // adjust balances for fee calculation
    uint balance0Adjusted = (balance0 * 10000 - (amount0In * fee * 4) / 5);
    uint balance1Adjusted = (balance1 * 10000 - (amount1In * fee * 4) / 5);
    
    // ensure the product of adjusted balances is not less than the previous reserves product
    require(
        balance0Adjusted * balance1Adjusted >=
            uint(_reserve0) * uint(_reserve1) * (10000 ** 2),
        "JAMM: K"
    );
}

Fee Tier Selection Guide

0.5% Fee

  • Use Case: Stablecoin pairs (e.g., USDC/USDT), highly liquid and low-volatility assets.

  • Characteristics: Lowest trading cost, suitable for large trades and arbitrage.

1.0% Fee

  • Use Case: Mainstream token pairs (e.g., JU/USDC), medium-volatility assets.

  • Characteristics: Standard fee level, balancing user cost and LP returns.

2.0% Fee

  • Use Case: Medium-risk token pairs, new project tokens.

  • Characteristics: Provides higher returns for LPs to compensate for higher impermanent loss risk.

3.0% Fee

  • Use Case: High-risk, high-volatility tokens, niche or experimental tokens.

  • Characteristics: Highest LP returns to compensate for high risk and impermanent loss.

Fee Event

Every fee transfer triggers a Fee event:

// from JAMMPair.sol
event Fee(
    address indexed sender,
    address indexed referrer,
    address token,
    uint amount
);

function _safeTransferFee(address token, address to, uint value) private {
    _safeTransfer(token, to, value);
    if (value > 0) {
        emit Fee(tx.origin, to, token, value);
    }
}

This event logs the details of the fee distribution, allowing for on-chain data analysis and tracking.

Summary

The JAMM DEX fee structure has the following features:

  1. Multi-Tier Fees: Four predefined fee tiers meet the needs of different assets.

  2. Referral Incentives: A unique fee-sharing mechanism encourages community promotion.

  3. Protocol Sustainability: Protocol fees collected by minting LP tokens ensure long-term development.

  4. Transparency: All fee calculations and distributions are performed publicly on-chain.

  5. Flexibility: Allows for the selection of the most appropriate fee tier for different types of asset pairs.

Understanding the fee structure is crucial for optimizing trading costs and maximizing LP returns.