This entry covers an upcoming CPMM program update. It was verified against the local release branch (
0dde43d, 11 September 2026) before deployment. Confirm the deployed program before relying on the new instructions or the changed account lists.creator_fee_rate and still accrues the whole amount to creator_fees_token_{0,1}. When CollectCreatorFee or CollectCreatorFeePermissionless runs, the accrued balance is divided, the protocol’s part is re-labelled as a protocol fee on the same pool, and only the creator’s part leaves the vault. Quotes, the curve, k, and every LP-facing path are unaffected.
TL;DR for integrators
- Both creator-fee collection instructions changed their account lists. This is breaking.
CollectCreatorFeegainscreator_fee_shareat position 5.CollectCreatorFeePermissionlessgainsamm_configat 5 andcreator_fee_shareat 6. Both insertions sit before the vaults, so everything after shifts. Rebuild these transactions; do not patch them. creator_fee_sharemust be passed even when it does not exist. It is declared with a seed constraint but read as an unchecked account, so the address must be the canonical PDA at["creator_fee_share", creator, amm_config]while the account itself is optional. When it is empty, the program falls back toAmmConfig.creator_fee_share_rate.AmmConfiggainscreator_fee_share_rate, carved out of the padding. The account is still 236 bytes and every existing config keeps deserializing — but the firstu64of the oldpadding: [u64; 15]is now a live field. Decoders that model the tail as a 15-element array read the share rate aspadding[0].PoolStateis unchanged. 637 bytes, same offsets, same fields. The protocol’s share is booked into the existingprotocol_fees_token_{0,1}counters — there is no new counter and no new collection instruction for it.protocol_fees_token*now grows outside of swaps. Any monitor that reconciles protocol accrual against trade volume will see jumps at each creator-fee collection.- A creator-payout estimator that reads
creator_fees_token*now overstates. Multiply by(1 − share_rate / 1_000_000), resolved for that(creator, amm_config)pair. - Two admin instructions are added:
CreateCreatorFeeShareandCloseCreatorFeeShare. One newUpdateAmmConfigparam:8→creator_fee_share_rate. - No new error codes. The new paths reuse
InvalidOwner(6001),InvalidInput(6003) andMathOverflow(6011).6000–6015are unchanged. - An IDL refresh is required — two new instructions, one new account type, two changed account lists, one new config field.
How the split works
Resolution, in priority order:CreatorFeeSharePDA at["creator_fee_share", creator, amm_config]— when the account exists and is owned by CPMM, itsshare_ratewins.AmmConfig.creator_fee_share_rate— the fee tier’s default, used otherwise.
u64 over FEE_RATE_DENOMINATOR_VALUE = 1_000_000 and both are checked against that ceiling. Then, per token side:
- Rounding favours the creator. The share floors, so the dust stays with the creator — the same direction as
Fees::protocol_feeandFees::fund_fee, which also carve a share out of an already-accrued fee. 20% of a 1-unit fee is 0, not 1. - Value is conserved.
creator_amount + shared_amount == creator_feefor every rate and every fee up tou64::MAX. share_rate = 0is exactly the old behaviour. Both the default config value and a missing PDA give the creator the whole fee, so nothing changes for any existing pool until an admin sets a rate.
protocol_fees_token* and creator_fees_token* are both already subtracted in vault_amount_without_fee, moving value between them does not change the curve’s view of the vault. No LP sees a price change across a creator-fee collection, and the k check is untouched.
The rate is read at collection, not at accrual. Fees that accumulated while the rate was
0 settle at whatever rate is in force when someone finally calls Collect*. There is no per-epoch or per-swap snapshot.Account-list changes
CollectCreatorFee — one insertion:
CollectCreatorFeePermissionless — two insertions:
Full account tables in
products/cpmm/instructions.
CreateCreatorFeeShare and CloseCreatorFeeShare
CreateCreatorFeeShare(share_rate: u64) inits the PDA; CloseCreatorFeeShare closes it and returns the rent to the signer. Both accept the shared program admin or a dedicated creator-fee-share owner — a new hardcoded key pair following the same devnet/mainnet cfg pattern as the program’s other delegated authorities. Addresses in reference/program-addresses.
Points worth noting:
- The pool creator is not a party to either instruction and does not sign. The
creatoraccount is unchecked — the PDA can be created for a key that owns no pool yet. - One account covers a
(creator, amm_config)pair, so it governs every pool that creator owns on that fee tier. A creator with pools on two tiers needs two accounts to be covered on both. - There is no update path.
initfails on a second create for the same pair; to change a rate, close and re-create.
UpdateAmmConfig param 8
protocol_fee_rate (param 1), which splits the trade fee — a point worth being careful about in admin tooling, since the two read alike and both land in protocol_fees_token*.
Riding along
CollectExcessLamports ordering fix. The instruction now makes two passes over remaining_accounts — every token-program CPI first, then the direct debits of CPMM-owned PDAs — instead of dispatching in caller order. Interleaving the two aborted with the runtime’s UnbalancedInstruction (“sum of account balances before and after instruction do not match”) whenever a PDA was debited ahead of a CPI, because the caller’s pending lamport changes are only flushed into the accounts a CPI actually carries. The instruction’s interface is unchanged; callers still pass sources in any order, and now that is genuinely safe.
Verifiable-build metadata. The workspace Cargo.toml declares [workspace.metadata.cli] solana = "3.1.10", so a verifiable build resolves the same Solana CLI the program was built against. No on-chain effect.
What did not change
PoolState— 637 bytes, same fields, same offsets. The protocol’s share reuses the existing protocol bucket rather than adding counters of its own.AmmConfig::LEN— still 236 bytes.- Swap math, quoting, and the
kcheck. The creator fee is charged exactly as before. CollectProtocolFee/CollectFundFee— same accounts, same signers.CollectProtocolFeesimply has more to collect.- Error codes.
6000–6015unchanged; nothing appended. - Every other instruction, and the program ID.
Pages updated
products/cpmm/fees— new “Protocol share of the creator fee” section covering rate resolution, the split arithmetic, rounding, and the integrator consequences;creator_fee_share_rateadded to the rates/units list and the default-parameters table; collection-flow table reworked.products/cpmm/instructions— breaking-change warning at the top; full account tables for both creator-fee paths; newCreateCreatorFeeShareandCloseCreatorFeeSharesections;UpdateAmmConfigparam8;CollectExcessLamportsordering note; summary and state-change matrix rows.products/cpmm/accounts— newCreatorFeeShareaccount section;AmmConfiglayout and padding-carve warning;PoolStatefee-counter notes; account-lifecycle rows.products/cpmm/overview— creator-fee callout and the “Predictable fees” bullet.products/cpmm/math— a note that the split is deliberately absent from swap math.products/cpmm/code-demos— warning that pre-upgrade SDK builders emit the old account lists; accrued-fee snippet annotated.reference/program-addresses— new “CPMM creator-fee-share authority” section;creator_fee_shareadded to the PDA-seed block.reference/fee-comparison—creator_fee_share_ratecalled out as a fourth CPMM rate with a different base.

