How a swap gets its price
Work through the swap equation, fees, price impact, and the virtual reserves inside a CLMM range.

An automated market maker quotes trades from its state rather than matching two orders. The familiar constant-product market maker (CPMM) is a useful starting point. grapl's liquidity pool is a CLMM: the same local curve idea, applied to overlapping price ranges with changing active liquidity.
A constant-product swap
Let x be the input reserve and y the output reserve, in token atoms. Ignoring fees and rounding, the curve is:
x × y = k
After adding Δx and removing Δy, (x + Δx) × (y − Δy) = k. Solving for the output gives:
Δy = y × Δx / (x + Δx)
For reserves of 1,000 A and 1,000 B, a 100 A input buys approximately 90.909 B. The average execution price differs from the starting marginal price because the trade moves along the curve. A larger trade relative to reserves moves the price more.
These decimal numbers explain the model. Implementations use integer atoms, wide intermediate arithmetic, and explicit rounding.
Follow the input through the fee
For fee rate f in parts per million and gross input budget a, first calculate the affordable net budget:
reserved_fee = ceil(a × f / 1,000,000)
net_budget = a − reserved_fee
quoted_out = floor(y × net_budget / (x + net_budget))⌊1,000,000 × 99,700 / 1,099,700⌋ = 90,661The pool retains 909,339 B atoms. Increasing your input reduces the output per input atom: each additional atom trades at a different point on the curve.
CPMM teaching model. grapl swaps traverse CLMM segments; this is not an executable pool quote.
With x = y = 1,000,000 atoms, a = 100,000, and f = 3,000 (0.3%), the reserved fee is 300 atoms, the affordable net budget is 99,700, and quoted output is 90,661 atoms. Rounding keeps the curve solvent. Fees are tracked separately from tradable principal; a physical vault balance is not automatically a pricing reserve.
Execution can consume less than the budget because of a boundary or integer precision. The fee on actual consumed net input n is ceil(n × f / (1,000,000 − f)); unspent input remains explicit. The curve recomputes the smallest gross input sufficient for its affordable net input, which can retain a rounding remainder. Do not equate the budget with the wallet's final debit. Inspect SwapOutcome.
The Pool 6 source increment retains 10% of the input trading fee with fractional carry, then splits the remainder into the configured LP share and up to four beneficiary shares. A hook may also request a separately bounded output fee. See fee accounting and swap modes before combining those amounts: they can be denominated in different tokens.
What changes in a CLMM?
A position supplies liquidity only between two prices. Write s = sqrt(P), where P is the atom price B per A, and let L be active liquidity. Within one segment:
(L / s) × (L × s) = L²
The segment behaves like virtual reserves A = L / s and B = L × s.
These are virtual mathematical reserves, not the balances of the pool's token accounts. For a position bounded by s_lower and s_upper, its principal while the price is inside the range is:
A = L × (1/s − 1/s_upper)
B = L × (s − s_lower)The range endpoints subtract the liquidity that an infinite constant-product curve would need outside the position's chosen interval. That is how a narrower range can make the same capital provide more active liquidity near its price.
Physical balances are not pricing reserves
Suppose L = 1,000, s = 1, s_lower = 0.5, and s_upper = 2. Ignoring integer scaling for this illustration, the position holds 500 A and 500 B. Its virtual reserves are 1,000 A and 1,000 B.
A net input of 100 A decreases the square-root price:
s_next = L × s / (L + net_A × s) = 0.909090…
B_out = L × (s − s_next) = 90.909090…Pricing against the physical 500/500 balances would incorrectly quote 83.333… B. Always use active L, square-root price, and tick boundaries for the CLMM.
For B input, the square-root price rises:
s_next = s + net_B / L
A_out = L × (1/s − 1/s_next)At an initialized tick, positions enter or leave the active set. grapl applies the signed liquidity delta and continues with the next segment's L. One global x × y = k calculation cannot replace that traversal.
Ticks, decimals, and market capitalization
The core stores sqrt(P) × 2^64 in Q64.64. Ticks discretize prices approximately as P = 1.0001^tick; tick spacing controls valid position boundaries. It does not round every swap to a tick.
To convert atom price to a displayed B-per-A price:
display_price = atom_price × 10^(decimals_A − decimals_B)
market_cap = display_price × circulating_supply_AMarket capitalization needs a supply definition and a quote currency. The tick domain limits representable prices, not a universal market-cap interval. Read tick arrays and full range for the supported range and storage model.
Where the same formula appears again
The bonding curve uses configured virtual reserves to shape price discovery. It also tracks real inventory, real quote principal, and fee liabilities independently. Virtual liquidity never authorizes a transfer of tokens the vault does not hold.
Source: programs/grapl-amm/src/math/{curve.rs,liquidity.rs,swap/}. The equations above explain the model; current code and tests define checked bounds and exact rounding.
Continue learning
- Concentrated liquidity: what happens when positions enter or leave the active range.
- Ticks and full range: how the core finds the next boundary.
- Integer numerics: checked bounds, fixed-point representations, and rounding.