Documentation Index
Fetch the complete documentation index at: https://docs.raydium.io/llms.txt
Use this file to discover all available pages before exploring further.
The invariant
CPMM maintains the classic constant-product invariant on its two vaults: wherex is the vault0 balance after any Token-2022 transfer fees on receipt, and similarly for y. Every swap must leave k' ≥ k after accounting for trade fees credited to the LP (the protocol, fund, and creator buckets are not counted toward k — they sit in the vault but are excluded from the curve view, see Fees on the curve below). k therefore grows monotonically over time as LPs accrue fees.
LP shares are priced by the pool’s reserves, not by k:
Burning ΔLP LP tokens returns exactly ΔLP × x / lpSupply of token0 and ΔLP × y / lpSupply of token1. Neither the curve nor k moves on deposit or withdrawal — only swaps change the price.
Fee model on the swap path
CPMM applies two independently-rated fees on every swap:- The trade fee is taken on the input side, charged at
AmmConfig.trade_fee_rate. It is then split into LP, protocol, and fund shares (the LP share stays in the vault and growsk; the protocol and fund shares are extracted from vault accounting). - The creator fee (active only when
enable_creator_fee == true) is charged atAmmConfig.creator_fee_rate. It is taken on the input side or the output side depending onPoolState.creator_fee_onand the swap direction (seeproducts/cpmm/fees). It is its own bucket — never a slice of the trade fee.
FEE_RATE_DENOMINATOR = 1_000_000trade_fee_rate— fromAmmConfig, e.g.,2500= 0.25% of the relevant volume sidecreator_fee_rate— fromAmmConfig, e.g.,1000= 0.10% of the relevant volume sideprotocol_fee_rate,fund_fee_rate— denominated in units of1/FEE_RATE_DENOMINATORof the trade fee, not of volume
protocol_fee + fund_fee + creator_fee amount is held in the vaults but tracked separately on the pool state (protocol_fees_token*, fund_fees_token*, creator_fees_token*). When the constant-product invariant checks k' ≥ k, it uses vault balances minus all three accrued-but-unswept fees — so LPs capture only lp_fee.
See products/cpmm/fees for the collection instructions and the worked numerical examples.
SwapBaseInput (input-exact)
“The user gives us exactlyamount_in of the input mint and receives at least minimum_amount_out of the output mint.”
Ignoring Token-2022 for a moment:
Δx_net = amount_in_after_trade_fee.
The program then updates the vault accounting such that the portion of trade_fee owed to protocol/fund/creator sits in “accrued” buckets (not included in the curve’s next x), while the LP share does join x for the next swap.
Token-2022 on the input side
If the input mint has a transfer-fee extension, the mint deducts its fee on the transfer from user → vault. So the vault actually receivesamount_in − transfer_fee_in. The CPMM program therefore computes:
amount_in_after_trade_fee. This matters because the curve price is computed off the net amount that landed in the vault, not off the user’s headline amount.
Token-2022 on the output side
If the output mint has a transfer fee, the pool sendsamount_out from its vault to the user. The mint will then skim its fee on the way out, so the user receives amount_out − transfer_fee_out(amount_out). The program computes amount_out from the curve as usual, but it is the integrator’s responsibility to convert the pool’s “vault send” number into a “user receive” number when showing quotes.
Slippage check
After computingamount_out:
minimum_amount_out so the slippage constant is denominated in what the user will actually receive, not in what the vault sends.
SwapBaseOutput (output-exact)
“The user will receive exactlyamount_out of the output mint and is willing to pay up to maximum_amount_in of the input mint.”
Inverting the curve for Δx_net:
The ceiling is important — it guarantees k' ≥ k after integer truncation. Then:
gross_needed.
Slippage check
Worked example
Pool state, ignoring Token-2022:x = 1_000_000_000_000(1,000,000.000000 of token0, 6 decimals)y = 2_000_000_000_000(2,000,000.000000 of token1, 6 decimals)AmmConfig:trade_fee_rate = 2500,protocol_fee_rate = 120_000,fund_fee_rate = 40_000,creator_fee_rate = 0
SwapBaseInput with amount_in = 1_000_000_000 (1,000.000000 of token0). Creator fee is disabled (enable_creator_fee = false).
enable_creator_fee = true with creator_fee_rate = 1000 (0.10%) on the input side, the program would charge total_input_fee = ceil(1_000_000_000 * 3500 / 1_000_000) = 3_500_000, then split it as creator_fee = 1_000_000 and trade_fee = 2_500_000. The protocol/fund/LP arithmetic on trade_fee is unchanged from the example above — the creator fee is its own bucket, accrued to creator_fees_token0 and excluded from curve_x along with the protocol and fund buckets.
If the input mint has a 1% Token-2022 transfer fee, the vault receives 990_000_000 tokens instead of 1_000_000_000, and every subsequent calculation uses that net amount.
Observation update rule
On every swap, the program evaluates whether to push a new observation into the ring buffer:- Cumulative price, not spot price. A single observation is not a price. To get a TWAP from time
t0tot1, read the observations closest to each end and compute(cumulative(t1) − cumulative(t0)) / (t1 − t0). - Samples are rate-limited. Back-to-back swaps in the same slot may share one observation. Reading an observation immediately after a swap can therefore look stale by one slot — this is normal.
products/clmm/accounts.
Fees on the curve
This is the subtle part and worth calling out. The curve arithmetic works against the net vault balances — i.e., raw SPL balance minus accrued protocol, fund, and creator fees (all three are independent buckets — seeproducts/cpmm/fees). A concrete picture:
- Do not quote from raw balances. Subtract the accrued-fee fields first, or call
SwapBaseInputas a simulation and take its return. CollectProtocolFeemoves tokens out of the vault. After collection,raw_vault_balancedrops butcurve_balanceis unchanged; the pool’s price does not move. This is deliberate.
Precision and overflow
- All curve arithmetic uses
u128intermediates to prevent overflow onx * y. - Division rounds toward zero except for
SwapBaseOutput’sΔx_net, which rounds up, and fee computation, which rounds up on thetrade_feeand down on the sub-splits. These rounding directions are chosen so the invariant never decreases due to integer truncation. - Pools with extreme vault ratios (billions : 1) may hit precision floors on small trades; the program returns
ZeroTradingTokensin that case. Seereference/error-codes.
Where to go next
products/cpmm/fees— the full fee tier and collection semantics.products/cpmm/instructions— the instructions that invoke this math.algorithms/constant-product— the derivation and edge cases ofx · y = kshared across AMM v4 and CPMM.
raydium-io/raydium-cp-swap— swap math instates/curve.rs- Raydium audit reports linked in
security/audits


