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 and accrued toPoolState.creator_fees_token*. The creator can sweep it throughCollectCreatorFee, or any payer can trigger the destination-constrainedCollectCreatorFeePermissionlesspath. Active only when the pool was created withenable_creator_fee = true. Since the 2026-09-19 upgrade the accrued creator fee is split once more at collection time: a configurable share is moved into the pool’s protocol bucket and only the remainder reaches the creator — see Protocol share of the creator fee.
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 bucket gets the 0.10% and the trade-fee bucket gets the 0.25%.
The protocol’s share of the creator fee runs the other way round and is easy to confuse with the above: it is carved out of the creator bucket, not out of the trade fee, and not on the swap — it is applied when CollectCreatorFee or CollectCreatorFeePermissionless settles the accrued balance. Swap math is unchanged by it.
The trade-fee rates (trade_fee_rate, protocol_fee_rate, fund_fee_rate), the creator_fee_rate and the default creator_fee_share_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. A per-creator override of the share rate lives on its own CreatorFeeShare PDA. 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.creator_fee_share_rateis a fraction of the accrued creator fee, not of volume and not of the trade fee.200_000⇒ 20% of whatever sits increator_fees_token*at the moment it is collected.0(the default) leaves the whole creator fee with the creator.
AmmConfig[index=0] (the “standard” 0.25% pool) on mainnet, for reference:
So on a $1,000 swap against
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:
When the creator fee is on the input side, both the trade fee and the creator fee are deducted from
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
CollectCreatorFeeorCollectCreatorFeePermissionless. The original path requiresPoolState.pool_creatorto sign. The permissionless path lets any payer trigger collection but fixes both destinations to the creator’s canonical ATAs. Both paths settle the protocol’s share first — see the next section. - 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.
Protocol share of the creator fee
Since the 2026-09-19 upgrade the protocol can retain a configurable share of the creator fee. Nothing about the swap changes: the creator fee is still charged atcreator_fee_rate and still accrues in full to creator_fees_token{0,1}. The split happens once, at collection time, inside CollectCreatorFee and CollectCreatorFeePermissionless.
Where the rate comes from
Two sources, in priority order:CreatorFeeSharePDA — seeds["creator_fee_share", creator, amm_config]. When this account exists and is owned by CPMM, itsshare_ratewins. It lets the protocol negotiate a per-creator rate on a given fee tier without touching the tier itself.AmmConfig.creator_fee_share_rate— the default for every creator on that fee tier. Used whenever the PDA does not exist.
u64s over the same FEE_RATE_DENOMINATOR = 1_000_000, and both are capped at the denominator. The collection instructions always take the creator_fee_share account, even when it has never been created — the program checks whether it is empty and falls back to the config. Passing the wrong address fails the PDA constraint, not the fallback.
What the split does
creator_fees_token_0 and creator_fees_token_1, then:
creator_amount_{0,1}is transferred out of the vaults to the creator’s token accounts.shared_amount_{0,1}is added toprotocol_fees_token_{0,1}and stays in the vault until the protocol owner sweeps it withCollectProtocolFee. There is no separate instruction and no separate counter for it.creator_fees_token_{0,1}are zeroed, exactly as before.
- Rounding favours the creator. The protocol share floors, so dust stays with the creator — the same direction as
protocol_feeandfund_fee, which also carve a share out of an already-accrued fee. - Value is conserved.
creator_amount + shared_amount == creator_feefor every rate and every fee, includingu64::MAX. share_rate = 0is a no-op. Both the default config value and the absence of aCreatorFeeSharePDA leave the whole creator fee with the creator, which is the pre-upgrade behaviour.
What it means for integrators
- LPs and quoting are unaffected. The shared amount moves between two counters that are both already excluded from the curve’s view of the vault (
vault_amount_without_fee), socurve_xandcurve_ydo not move across a collection.kis untouched. - A creator-fee estimator that reads
creator_fees_token*now overstates the payout. Multiply by(1 − share_rate / 1_000_000)using the rate that actually applies to that(creator, amm_config)pair, not the config default. protocol_fees_token*grows outside of swaps. A monitor that reconciles protocol accrual against swap volume will see jumps at each creator-fee collection. Protocol accrual is no longertrade_fee × protocol_fee_ratealone.- The rate can change between accrual and collection. It is read at collection time, so fees that accrued under one rate settle at whatever rate is in force when someone calls
Collect*.
CreatorFeeShare account is created and closed by the admin or a dedicated creator-fee-share authority through CreateCreatorFeeShare / CloseCreatorFeeShare; closing it returns the pair to AmmConfig.creator_fee_share_rate. Account layout in products/cpmm/accounts, addresses in reference/program-addresses.
Collection operational flow
Protocol and fund owners are the Raydium multisig on mainnet; see
security/admin-and-multisig. On the original creator-only path, the creator signer is the account recorded in PoolState. On the permissionless path, the caller pays to create either missing creator ATA. The program constrains creator to pool_state.pool_creator and derives each destination from that creator plus the corresponding vault mint and token program, so the caller cannot redirect funds.
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
Resolve the share rate on-chain, not from a cached config.
creator_fee_share_rate is a newly added AmmConfig field, so read it off the account rather than assuming the REST config payload carries it, and check whether a CreatorFeeShare PDA exists at ["creator_fee_share", creator, ammConfig] before quoting a creator their payout. An absent PDA is the common case and means the config default applies.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, including thecreator_fee_shareaccount both creator paths now require.algorithms/token-2022-transfer-fees— how to combine a pool trade fee with a mint transfer fee correctly.

