Pair Contract Reference
The Pair contract is the core liquidity pool implementation for Lithos, handling token swaps, liquidity provision, and fee accrual. Each pair can be either stable (using a concentrated liquidity curve) or volatile (using a constant product formula). This reference documents all externally callable functions, their expected inputs, return values, and operational notes.
Key Concepts
Pool Types – Pairs are either
stable(concentrated liquidity for similar assets) orvolatile(constant product for dissimilar assets), determined at creation.Token Order –
token0andtoken1are sorted by address to ensure deterministic pair addresses.LP Tokens – Liquidity providers receive ERC-20 tokens representing their share of the pool.
Fee Accumulation – Trading fees are segregated from pool reserves and can be claimed separately by LPs.
TWAP Oracle – Each pair maintains a time-weighted average price (TWAP) oracle with 30-minute observation periods.
State Variables
Pool Configuration
name– Human-readable pool name (e.g., "StableV1 AMM - USDC/USDT")symbol– Pool symbol (e.g., "sAMM-USDC/USDT")decimals– Always 18 for LP tokensstable– Boolean indicating if this is a stable pooltoken0,token1– The two tokens in the pool (sorted by address)factory– Address of the PairFactory that created this pairfees– Address of the PairFees contract handling fee distribution
Reserves and Balances
reserve0,reserve1– Current token reserves in the pooltotalSupply– Total LP tokens mintedbalanceOf– ERC-20 balance mapping for LP tokensallowance– ERC-20 allowance mapping
Fee Tracking
index0,index1– Global fee indexes for each tokensupplyIndex0,supplyIndex1– User-specific fee indexesclaimable0,claimable1– Accumulated but unclaimed fees per user
Oracle Data
observations– Array of TWAP observations (timestamp, reserve0Cumulative, reserve1Cumulative)periodSize– 30 minutes (1800 seconds) between observationsreserve0CumulativeLast,reserve1CumulativeLast– Last cumulative reserve values
Utility Queries
metadata() → (uint256 dec0, uint256 dec1, uint256 r0, uint256 r1, bool st, address t0, address t1)
metadata() → (uint256 dec0, uint256 dec1, uint256 r0, uint256 r1, bool st, address t0, address t1)Returns comprehensive pool metadata including token decimals, current reserves, stability flag, and token addresses.
tokens() → (address, address)
tokens() → (address, address)Returns the two token addresses in the pool.
isStable() → bool
isStable() → boolReturns true if this is a stable pool, false if volatile.
getReserves() → (uint256 _reserve0, uint256 _reserve1, uint256 _blockTimestampLast)
getReserves() → (uint256 _reserve0, uint256 _reserve1, uint256 _blockTimestampLast)Returns current reserves and the timestamp of the last reserve update.
observationLength() → uint256
observationLength() → uint256Returns the number of TWAP observations recorded.
lastObservation() → Observation
lastObservation() → ObservationReturns the most recent TWAP observation.
Price Oracle Functions
current(address tokenIn, uint256 amountIn) → uint256 amountOut
current(address tokenIn, uint256 amountIn) → uint256 amountOutReturns the current TWAP price for swapping amountIn of tokenIn. Uses the most recent observation period.
quote(address tokenIn, uint256 amountIn, uint256 granularity) → uint256 amountOut
quote(address tokenIn, uint256 amountIn, uint256 granularity) → uint256 amountOutReturns an average TWAP price over multiple observation periods. granularity specifies how many periods to average.
prices(address tokenIn, uint256 amountIn, uint256 points) → uint256[] memory
prices(address tokenIn, uint256 amountIn, uint256 points) → uint256[] memoryReturns an array of historical TWAP prices for the last points observation periods.
sample(address tokenIn, uint256 amountIn, uint256 points, uint256 window) → uint256[] memory
sample(address tokenIn, uint256 amountIn, uint256 points, uint256 window) → uint256[] memoryLow-level function for sampling TWAP prices. points is the number of samples, window is the step size between observations.
currentCumulativePrices() → (uint256 reserve0Cumulative, uint256 reserve1Cumulative, uint256 blockTimestamp)
currentCumulativePrices() → (uint256 reserve0Cumulative, uint256 reserve1Cumulative, uint256 blockTimestamp)Returns the current cumulative reserve values, adjusted for time elapsed since the last update.
Swap Functions
getAmountOut(uint256 amountIn, address tokenIn) → uint256 amountOut
getAmountOut(uint256 amountIn, address tokenIn) → uint256 amountOutCalculates the expected output amount for a swap, accounting for trading fees. This is a view function that doesn't execute the swap.
swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) →
swap(uint256 amount0Out, uint256 amount1Out, address to, bytes calldata data) →Executes a token swap. Transfers output tokens to to and handles fee accrual. Supports flash loans via the data parameter.
Parameters:
amount0Out– Amount of token0 to send out (0 if not swapping token0)amount1Out– Amount of token1 to send out (0 if not swapping token1)to– Recipient address for output tokensdata– Optional data for flash loan callbacks
Requirements:
At least one output amount must be > 0
Output amounts must be less than current reserves
Pool must not be paused (checked via factory)
tocannot be either token address
Events:
Emits
Swapevent with input/output amounts and recipient
Liquidity Management
mint(address to) → uint256 liquidity
mint(address to) → uint256 liquidityMints LP tokens for the caller based on tokens transferred to the pool. Called by the router after token transfers.
Parameters:
to– Recipient of minted LP tokens
Returns:
liquidity– Amount of LP tokens minted
Logic:
For initial liquidity:
liquidity = sqrt(amount0 * amount1) - MINIMUM_LIQUIDITYFor additional liquidity:
liquidity = min((amount0 * totalSupply) / reserve0, (amount1 * totalSupply) / reserve1)Burns
MINIMUM_LIQUIDITYtokens to address(0) on initial mint
burn(address to) → (uint256 amount0, uint256 amount1)
burn(address to) → (uint256 amount0, uint256 amount1)Burns LP tokens and returns underlying tokens to to. Called by the router.
Parameters:
to– Recipient of underlying tokens
Returns:
amount0,amount1– Amounts of each token returned
Logic:
Calculates proportional amounts based on current pool balances
Requires both amounts > 0
Fee Management
claimFees() → (uint256 claimed0, uint256 claimed1)
claimFees() → (uint256 claimed0, uint256 claimed1)Claims accumulated trading fees for the caller. Fees are accrued based on the user's LP token balance and the global fee indexes.
Returns:
claimed0,claimed1– Amounts of each token claimed
Events:
Emits
Claimevent with amounts and recipient
claimStakingFees() →
claimStakingFees() →Withdraws accumulated staking fees to the staking fee handler configured in the factory. Can only be called by the staking fee handler.
ERC-20 Functions
approve(address spender, uint256 amount) → bool
approve(address spender, uint256 amount) → boolERC-20 approve function. Updates fee tracking for both owner and spender.
permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) →
permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) →EIP-2612 permit function for gasless approvals. Updates fee tracking for the owner.
transfer(address dst, uint256 amount) → bool
transfer(address dst, uint256 amount) → boolERC-20 transfer function. Updates fee tracking for both sender and recipient.
transferFrom(address src, address dst, uint256 amount) → bool
transferFrom(address src, address dst, uint256 amount) → boolERC-20 transferFrom function. Handles allowance updates and fee tracking for all parties.
Administrative Functions
skim(address to) →
skim(address to) →Transfers any excess tokens (balance > reserves) to to. Used to recover tokens accidentally sent to the pair.
sync() →
sync() →Forces reserve updates to match current token balances. Used to recover from accounting discrepancies.
Fee Calculation Details
Trading fees are split into two components:
Referral Fee – 12% of trading fees sent to the Dibs contract as protocol revenue
LP Fee – 88% of trading fees distributed to liquidity providers via fee indexes
The fee calculation process:
Calculate total fee:
amountIn * feeRate / 10000Subtract referral fee (12%) and transfer to Dibs
Update LP fee index with remaining fee (88%):
index += (lpFee * 1e18) / totalSupply
Note: NFT fees are currently set to 0% and are not collected.
TWAP Oracle Implementation
The oracle records cumulative reserve values every 30 minutes:
reserve0Cumulative += reserve0 * timeElapsedreserve1Cumulative += reserve1 * timeElapsed
Price calculations use the difference between observations:
avgReserve0 = (currentCumulative0 - observationCumulative0) / timeElapsedavgReserve1 = (currentCumulative1 - observationCumulative1) / timeElapsed
Integration Notes
Initial Liquidity – First liquidity providers must account for
MINIMUM_LIQUIDITY(1000 tokens) being burnedFee Claims – Users must call
claimFees()to receive accumulated trading feesFlash Loans – Supported via the
dataparameter inswap()functionReentrancy Protection – All state-changing functions are protected by a reentrancy guard
Balance Updates – Fee tracking is updated on all balance changes (mint, burn, transfer)
Oracle Usage – TWAP observations are only recorded when more than 30 minutes have elapsed since the last observation
Error Codes
ILM– INSUFFICIENT_LIQUIDITY_MINTEDILB– INSUFFICIENT_LIQUIDITY_BURNEDIOA– INSUFFICIENT_OUTPUT_AMOUNTIL– INSUFFICIENT_LIQUIDITYIT– INVALID_TOIIA– INSUFFICIENT_INPUT_AMOUNTK– Constant product invariant check failedEXPIRED– Permit deadline expiredINVALID_SIGNATURE– Invalid EIP-712 signature
Last updated
