Two independent fees, four destinations
CPMM levies two separately-rated fees on every swap:- Trade fee — charged at
AmmConfig.trade_fee_rateand split between three destinations:- LP share — stays in the vault and grows
k. Claimed implicitly by burning LP tokens. - Protocol share — accrued to
PoolState.protocol_fees_token*; swept by theprotocol_ownerviaCollectProtocolFee. - Fund share — accrued to
PoolState.fund_fees_token*; swept by thefund_ownerviaCollectFundFee.
- LP share — stays in the vault and grows
- Creator fee (optional, per-pool) — charged at
AmmConfig.creator_fee_rateindependently of the trade fee, accrued toPoolState.creator_fees_token*, swept bypool_state.pool_creatorviaCollectCreatorFee. Active only when the pool was created withenable_creator_fee = true.
trade_fee only, never from creator_fee. A pool with creator_fee_rate = 1000 (0.10%) and trade_fee_rate = 2500 (0.25%) charges a combined 0.35% of the input on a creator-fee-on-input swap, of which the creator keeps the 0.10% and the trade-fee bucket gets the 0.25%.
The trade-fee rates (trade_fee_rate, protocol_fee_rate, fund_fee_rate) and the creator_fee_rate all live on AmmConfig. The per-pool enable_creator_fee flag and the creator_fee_on mode (which side of the trade the creator fee is taken from) live on PoolState. See products/cpmm/accounts.
Rates and units
All rates areu64s denominated in units of 1 / FEE_RATE_DENOMINATOR where FEE_RATE_DENOMINATOR = 1_000_000.
trade_fee_rateis a fraction of swap volume.2500⇒ 0.25% of the relevant side (input or output, depending oncreator_fee_on— see “Which side of the trade the fees are taken from” below).creator_fee_rateis a fraction of swap volume, taken separately from the trade fee.1000⇒ 0.10% of the relevant side.protocol_fee_rateandfund_fee_rateare fractions of the trade fee, not of volume.120_000⇒ 12% of the trade fee.
AmmConfig[index=0] (the “standard” 0.25% pool) on mainnet, for reference:
| Field | Value | Effective percent |
|---|---|---|
trade_fee_rate | 2500 | 0.25% of volume (trade-fee bucket) |
protocol_fee_rate | 120000 | 12% of trade fee ≈ 0.030% of volume |
fund_fee_rate | 40000 | 4% of trade fee ≈ 0.010% of volume |
creator_fee_rate | 0 (default) | 0% (separate bucket) |
| → LP share effective | 0.210% of volume |
AmmConfig[0] with enable_creator_fee = false: $2.50 total trade fee, of which $2.10 stays with LPs, $0.30 goes to protocol, $0.10 to the fund. The creator bucket is 0 because creator fee is disabled.
If the same pool had enable_creator_fee = true and creator_fee_rate = 1000 (0.10%), the user pays an additional $1.00 to the creator bucket — taken on the same side of the trade configured by creator_fee_on — for $3.50 of total fees. The trade-fee bucket and its protocol/fund splits are unchanged.
Confirm the current mainnet values against GET https://api-v3.raydium.io/main/cpmm-config — rates are admin-mutable and should be read fresh rather than hardcoded.
The split, in code
- The total fee on input rounds up so the pool never undercharges.
- The sub-splits of
trade_fee(protocol, fund) round down so their sum never exceedstrade_fee; the remainder is the LP share. lp_share = trade_fee − protocol_fee − fund_fee(creator_fee is not subtracted here because it is its own bucket).- The creator fee is taken from input or output depending on
PoolState.creator_fee_on(see next section). The rate is unchanged either way.
Which side of the trade the fees are taken from
CPMM has a per-poolcreator_fee_on setting (BothToken / OnlyToken0 / OnlyToken1) that determines whether the creator fee is taken from the input side or the output side of a given swap. The runtime helper is_creator_fee_on_input(direction) collapses that to a boolean per swap:
creator_fee_on | Swap 0 → 1 | Swap 1 → 0 |
|---|---|---|
BothToken (0) | input side | input side |
OnlyToken0 (1) | input side | output side |
OnlyToken1 (2) | output side | input side |
amount_in before the curve runs. Quote math: take the combined trade_rate + creator_rate off the input.
When the creator fee is on the output side, only the trade fee is deducted from amount_in; the curve produces an unfee’d output, then the creator fee is deducted from that output. Quote math: take trade_rate off the input; take creator_rate off the output.
Trade fee itself is always taken on the input side (the standard Uniswap-V2 pattern). Only the creator fee can land on output.
How “accrued” fees interact with the curve
An important subtlety: the protocol, fund, and creator fees stay physically in the vault until their respectiveCollect* instruction is called. But they are excluded from the curve’s view of the vault balance.
A concrete picture after one swap:
curve_x (and the analogous curve_y) when enforcing k' ≥ k. This is how the non-LP fees reach their destinations without inflating the LP share of the pool.
Consequences you should design around:
- Quoting off raw balances is wrong. If you build a quoter off
getTokenAccountBalance, you will consistently overstate the price the pool will honor. Always subtract accrued fees, or simulate viaSwapBaseInput/ the API. CollectProtocolFeedoes not move the price. It moves tokens out of the vault and zeros theprotocol_fees_token*counters, socurve_xandcurve_yare unchanged.- LP fees do not accrue to a counter. They are implicit in the vault balance. LP’s entitlement to accumulated LP fees is exercised by burning LP tokens (i.e., via
Withdraw) — there is noCollectLpFee.
Interaction with Token-2022 transfer fees
Token-2022 transfer fees are applied by the mint, not by CPMM. They act on every token transfer — swap, deposit, withdrawal, and theCollect* sweeps. CPMM’s trade-fee math is computed against the amount that actually landed in the vault, i.e., net of the input mint’s transfer fee (if any).
So in the worst case a user pays three distinct taxes on an input-exact swap:
- The input mint’s transfer fee on
amount_in(to the mint’s fee authority). - The pool’s
trade_feeon the remainder (split per above). - The output mint’s transfer fee on
amount_out(to the mint’s fee authority).
minimum_amount_out is denominated in what the user actually receives. If you are writing your own quoter, mirror that behavior, or your slippage checks will be systematically too generous.
See algorithms/token-2022-transfer-fees for the detailed derivation.
Creator fee
The creator fee is optional and per-pool. The rate lives onAmmConfig.creator_fee_rate; the enable flag and the side (creator_fee_on) live on PoolState:
- Enabled at pool creation.
Initializesetsenable_creator_fee = falseby default; pools created viaInitializeWithPermission(used by LaunchLab graduations and other gated paths) can passenable_creator_fee = trueand choosecreator_fee_on. - Rate is shared with the fee tier. The rate itself is
AmmConfig.creator_fee_rate, the same value across every pool bound to that config. Each pool then decides whether to charge it (enable_creator_fee) and which side of the swap to charge it on (creator_fee_on). Whenenable_creator_fee = false, the pool’s effective creator-fee rate is zero regardless of the config value (seePoolState::adjust_creator_fee_ratein the source). - Independent of trade fee. The creator fee never reduces the LP / protocol / fund shares — it is its own rate, applied separately, accrued in its own counters.
- Swept via
CollectCreatorFee, signed byPoolState.pool_creator. - Cannot be re-enabled or re-routed after creation. A pool initialized with
enable_creator_fee = falsewill never charge a creator fee; one initialized with a particularcreator_fee_oncannot switch sides.
CollectCreatorFee indefinitely.
Collection operational flow
| Signer | Instruction | Source counters zeroed | Typical cadence |
|---|---|---|---|
amm_config.protocol_owner | CollectProtocolFee | protocol_fees_token{0,1} | Weekly or programmatic |
amm_config.fund_owner | CollectFundFee | fund_fees_token{0,1} | Weekly or programmatic |
pool_state.pool_creator | CollectCreatorFee | creator_fees_token{0,1} | Anytime |
security/admin-and-multisig. The creator signer is the account that ran Initialize.
Changing a fee tier
Fee rates can be changed by the admin viaUpdateAmmConfig (see products/cpmm/instructions). Changes take effect on the next swap for every pool bound to that AmmConfig — there is no migration, because pools load the config each swap.
What the admin cannot do:
- Move a pool from one
AmmConfigto another. - Retroactively reprice already-accrued fees.
- Collect fees without the
protocol_owner/fund_ownersigner.
Reading fees from a running pool
Comparison with CLMM and AMM v4
Seereference/fee-comparison for a side-by-side matrix. Summary:
- AMM v4 uses a fixed 0.25% trade fee with a different LP/protocol split and no fund fee.
- CLMM fees are per-tick-spacing tier, accrued per-position (not per-pool), and claimed via
DecreaseLiquidityorCollectFees.
Where to go next
products/cpmm/math— where the trade-fee deduction plugs into the curve.products/cpmm/instructions— theCollect*instruction account lists.algorithms/token-2022-transfer-fees— how to combine a pool trade fee with a mint transfer fee correctly.

