Fee-bearing token
Fees accrue in the input token:zero_for_one = trueadvances token0 fee growth.zero_for_one = falseadvances token1 fee growth.
Trading fee and protocol share
The pool fee is expressed in fee pips, or parts per million. A step first calculates its integer total fee under the swap math. The configured protocol fraction then splits it:fee_protocol may be zero or 4 through 10. The floor remainder belongs to lp_fee.
Protocol fees accumulate in separate slot balances. They do not appear in LP fee growth and cannot be claimed by positions.
Global growth
For active liquidityL and the step’s LP fee F:
u256::U256:
Liquidity epochs
A liquidity epoch is a period during which one active-liquidity set earns fee growth. Epoch boundaries include:- Minting active liquidity
- Increasing or decreasing active liquidity
- Freezing a live position
- Crossing an initialized tick
- Reinitializing a removed boundary
Tick outside growth
Each initialized tick stores outside growth for token0 and token1. At a crossing:Growth inside a range
For either token, define:2^256.
The branch conditions implement:
Position checkpoint
A publicPosition stores:
- Current liquidity
fee_growth_inside0_last_x_128fee_growth_inside1_last_x_128- Integer
tokens_owed0 - Integer
tokens_owed1
tokens_owed. They become settled integer amounts during increase, decrease, collect, or freeze.
Settlement events
This ordering prevents new liquidity from sharing old growth and prevents removed liquidity from losing growth already earned.
Burn performs no fee settlement. It removes a position only after liquidity and both owed balances are zero.
Worked example
Suppose one step has:Rounding and dust
Global growth floors once per fee-bearing step. The aggregate shortfall is less than one native token base unit because active liquidity is au128 and the fee-growth scale is 2^128.
Position settlement floors again. Its shortfall is less than one native base unit for that position and token at that settlement.
Advancing the checkpoint discards that position fraction. It is not stored in a residual field and is not reassigned to another LP.
The contract has no generic sweep for the resulting surplus. collect_protocol can withdraw only recorded protocol fees.
Institutional reconciliation should use:
Public observability
Global growth, tick outside growth, position checkpoints, position liquidity, and settled owed amounts are public mapping state. Collection requests are also public. ThePositionNFT record authorizes the holder without storing the owner’s address in Position. The position’s range and fee economics remain public.
An observer with a token ID and a consistent state snapshot can estimate pending fees. The estimate does not identify the private immutable withdrawal address.
Frozen positions
Freezing a live position removes its entire liquidity and settles fees plus principal intotokens_owed. The freeze then blocks collect. Unfreeze restores access to the owed value but does not restore active liquidity.
Fees stop accruing after liquidity becomes zero. The holder must increase or mint again after unfreeze to return capital to the active set.
Off-chain calculation rules
An indexer or wallet should:- Read slot, lower tick, upper tick, and position at one state height.
- Pack each
u256::U256from its two limbs. - Calculate below, above, and inside separately for token0 and token1.
- Use modulo
2^256subtraction. - Multiply by position liquidity using arbitrary precision.
- Shift right by 128 bits.
- Reject a result above
u128maximum, matching the contract. - Add stored
tokens_owed. - Keep the result in the token’s native base units and apply decimals only for display.