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:
Initial guess:
x₀ = y/2 + 1
Iteration formula:
x_{n+1} = (y/x_n + x_n) / 2
Stop iteration when
x_{n+1} >= x_n
Special Case Handling:
y = 0
: Return 0y = 1, 2, 3
: Return 1y > 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 occurredThrow 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 occurredThrow 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 occurredSpecial 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 amountGeometric 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
, thenn/x < √n
If
x < √n
, thenn/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
Initial Guess Optimization:
uint x = y / 2 + 1; // Closer to actual value than y/2
Special Case Handling:
if (y > 3) { // Use iterative algorithm } else if (y != 0) { z = 1; // Return result directly }
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