Math Library

Overview

The Math library is a mathematical operation utility library in JAMM DEX, providing safe basic mathematical operation functions. It includes square root calculation, minimum value comparison, and arithmetic operations with overflow protection, providing a reliable mathematical foundation for the entire protocol.

Library Function Details

Minimum Value Function

function min(uint x, uint y) internal pure returns (uint z) {
    z = x < y ? x : y;
}

Function: Returns the smaller of two unsigned integers

Purpose:

  • Select smaller ratio in liquidity calculations

  • Set maximum value limits

  • Handle boundary conditions

Example:

uint minAmount = Math.min(amountA, amountB);

Square Root Function

function sqrt(uint y) internal pure returns (uint z) {
    if (y > 3) {
        z = y;
        uint x = y / 2 + 1;
        while (x < z) {
            z = x;
            x = (y / x + x) / 2;
        }
    } else if (y != 0) {
        z = 1;
    }
}

Algorithm: Babylonian method (special case of Newton's method)

Calculation Principle:

  1. Initial guess: x₀ = y/2 + 1

  2. Iteration formula: x_{n+1} = (y/x_n + x_n) / 2

  3. Stop iteration when x_{n+1} >= x_n

Special Case Handling:

  • y = 0: Return 0

  • y = 1, 2, 3: Return 1

  • y > 3: Use iterative algorithm

Purpose:

  • Calculate geometric mean

  • LP token amount calculation: √(amount0 × amount1)

  • Square root of k value in protocol fee calculation

Addition Function

function add(uint x, uint y) internal pure returns (uint z) {
    require((z = x + y) >= x, "ds-math-add-overflow");
}

Security Features:

  • Check addition overflow

  • If x + y < x, overflow occurred

  • Throw exception on overflow

Note: In Solidity 0.8.x, overflow checking is built-in; this function is mainly for compatibility

Subtraction Function

function sub(uint x, uint y) internal pure returns (uint z) {
    require((z = x - y) <= x, "ds-math-sub-underflow");
}

Security Features:

  • Check subtraction underflow

  • If x - y > x, underflow occurred

  • Throw exception on underflow

Multiplication Function

function mul(uint x, uint y) internal pure returns (uint z) {
    require(y == 0 || (z = x * y) / y == x, "ds-math-mul-overflow");
}

Security Features:

  • Check multiplication overflow

  • Verify through division: if (x × y) / y ≠ x, overflow occurred

  • Special handling for y = 0 case

Division Function

function div(uint x, uint y) internal pure returns (uint z) {
    require(y > 0, "ds-math-div-zero");
    z = x / y;
}

Security Features:

  • Check division by zero error

  • Ensure divisor is greater than 0

Applications in JAMM DEX

Liquidity Calculation

Initial Liquidity Calculation

// In JAMMPair.mint()
if (_totalSupply == 0) {
    liquidity = Math.sqrt(amount0 * amount1) - MINIMUM_LIQUIDITY;
    _mint(address(0), MINIMUM_LIQUIDITY);
}

Geometric Mean:

  • Use √(amount0 × amount1) to calculate initial LP token amount

  • Geometric mean is more suitable for constant product AMM systems than arithmetic mean

  • Subtract MINIMUM_LIQUIDITY to prevent pool from being completely drained

Subsequent Liquidity Calculation

// In JAMMPair.mint()
liquidity = Math.min(
    (amount0 * _totalSupply) / _reserve0,
    (amount1 * _totalSupply) / _reserve1
);

Minimum Value Selection:

  • Ensure liquidity is added according to existing ratio

  • Prevent unilateral liquidity addition from breaking price

  • Protect liquidity provider interests

Protocol Fee Calculation

// In JAMMPair._mintFee()
uint rootK = Math.sqrt(uint(_reserve0) * uint(_reserve1));
uint rootKLast = Math.sqrt(_kLast);
if (rootK > rootKLast) {
    uint numerator = totalSupply * (rootK - rootKLast) * 8;
    uint denominator = rootK * 17 + rootKLast * 8;
    uint liquidity = numerator / denominator;
    if (liquidity > 0) _mint(mintTo, liquidity);
}

K Value Growth Calculation:

  • Calculate square root of current k value

  • Calculate square root of last k value

  • Calculate protocol fee based on k value growth

Mathematical Principles

Babylonian Square Root Algorithm

The Babylonian method is an ancient algorithm for calculating square roots, based on the following observation:

If x is an approximation of the square root of n, then n/x is also an approximation, and:

  • If x > √n, then n/x < √n

  • If x < √n, then n/x > √n

Therefore, (x + n/x) / 2 is a better approximation.

Convergence:

  • Algorithm has quadratic convergence

  • Effective digits approximately double with each iteration

  • For 256-bit integers, usually only a few iterations are needed

Overflow Detection Principles

Addition Overflow Detection

If x + y < x, then overflow occurred

Principle: In unsigned integers, if the addition result is smaller than either operand, wraparound occurred.

Multiplication Overflow Detection

If y ≠ 0 and (x * y) / y ≠ x, then overflow occurred

Principle: If no overflow occurred, (x * y) / y should equal x.

Usage Examples

Square Root Calculation

// Calculate geometric mean
const amount0 = ethers.utils.parseEther("100");
const amount1 = ethers.utils.parseEther("200");

// In contract, Math.sqrt(amount0 * amount1) would be called
const geometricMean = Math.sqrt(amount0.mul(amount1));
console.log("Geometric mean:", ethers.utils.formatEther(geometricMean));

Minimum Value Selection

// Select smaller liquidity ratio
const ratio0 = amount0.mul(totalSupply).div(reserve0);
const ratio1 = amount1.mul(totalSupply).div(reserve1);

// In contract, Math.min(ratio0, ratio1) would be called
const liquidity = ratio0.lt(ratio1) ? ratio0 : ratio1;

Safe Operations

// Although Solidity 0.8.x has built-in checks, understanding principles is important

// Addition overflow check
function safeAdd(a, b) {
    const result = a.add(b);
    if (result.lt(a)) {
        throw new Error("Addition overflow");
    }
    return result;
}

// Multiplication overflow check
function safeMul(a, b) {
    if (b.eq(0)) return ethers.BigNumber.from(0);
    const result = a.mul(b);
    if (!result.div(b).eq(a)) {
        throw new Error("Multiplication overflow");
    }
    return result;
}

Performance Considerations

Square Root Algorithm Efficiency

Time complexity of Babylonian method:

  • Worst case: O(log log n)

  • Average case: Usually 3-4 iterations

  • Gas consumption: Relatively low, suitable for on-chain computation

Optimization Techniques

  1. Initial Guess Optimization:

    uint x = y / 2 + 1; // Closer to actual value than y/2
  2. Special Case Handling:

    if (y > 3) {
        // Use iterative algorithm
    } else if (y != 0) {
        z = 1; // Return result directly
    }
  3. Loop Optimization:

    while (x < z) { // Use < instead of !=
        z = x;
        x = (y / x + x) / 2;
    }

Test Cases

Square Root Tests

describe("Math.sqrt", function() {
    it("should calculate square root correctly", function() {
        expect(Math.sqrt(0)).to.equal(0);
        expect(Math.sqrt(1)).to.equal(1);
        expect(Math.sqrt(4)).to.equal(2);
        expect(Math.sqrt(9)).to.equal(3);
        expect(Math.sqrt(16)).to.equal(4);
        expect(Math.sqrt(100)).to.equal(10);
    });
    
    it("should handle large numbers", function() {
        const large = ethers.BigNumber.from("1000000000000000000"); // 10^18
        const result = Math.sqrt(large);
        expect(result).to.equal(ethers.BigNumber.from("1000000000")); // 10^9
    });
});

Overflow Tests

describe("Math overflow protection", function() {
    it("should detect addition overflow", function() {
        const maxUint = ethers.constants.MaxUint256;
        expect(() => Math.add(maxUint, 1)).to.throw("ds-math-add-overflow");
    });
    
    it("should detect multiplication overflow", function() {
        const large = ethers.BigNumber.from("2").pow(128);
        expect(() => Math.mul(large, large)).to.throw("ds-math-mul-overflow");
    });
});

Last updated