A documentation changelog entry. For the index of all updates, see
reference/changelog. For the protocol’s own historical timeline, see introduction/history-and-milestones.CreatePool / CreateCustomizablePool paths are unchanged.
TL;DR for integrators
- Pool IDs can now be non-canonical, behind a permission gate. Historically there was exactly one CLMM pool address per
(amm_config, token_mint_0, token_mint_1)— the PDA seeds pinned it. A new instruction,CreatePermissionedPool, folds a client-supplied non-zeroseed_index: u16into the pool PDA seeds, so a whitelisted operator can stand up several pools for the same pair and fee tier, each at its own address. Because arbitrary pool addresses are a privileged capability, the payer must hold aPermissionPDA granted by the admin. Two admin instructions,CreatePermissionPdaandClosePermissionPda, manage those grants. OpenLimitOrdernow validates the output side. The instruction takes three additional accounts —output_token_account,output_vault,output_vault_mint— and rejects the order withNotApprovedif the owner’s input or output token account is frozen. This guarantees the eventual fill can be delivered, which matters for allow-list / default-frozen Token-2022 mints (permissioned tokens). Clients built against the old single-sided account list must add the three output accounts.
What changed in the program
New instruction: CreatePermissionedPool
Same shape and parameters as CreateCustomizablePool (it takes the same CreateCustomizableParams, so single-sided fee and the dynamic-fee opt-in are available), plus:
- A
seed_index: u16argument, which must be non-zero. It is appended (little-endian) to the pool PDA seeds:["pool", amm_config, token_mint_0, token_mint_1, seed_index.to_le_bytes()]. - A
permissionaccount: the PDA["permission", payer]. Anchor rejects the call if it does not exist or is not owned by the program — its mere existence is the authorization. - A
pool_creatoraccount recorded aspool_state.owner, which may differ from the fee-payingpayer.
seed_index = 0 remains reserved for legacy pools: for those the seed component collapses to empty, reproducing the classic four-seed address, so no existing pool address changes.
See Instructions → CreatePermissionedPool.
New account: Permission
A capability account whose existence is the grant. It stores only the authority it was created for (plus padding). PDA seeds: ["permission", authority]. It confers the right to call CreatePermissionedPool and nothing else — it cannot move funds, change fees, or mutate any pool. See Accounts → Permission.
New admin instructions: CreatePermissionPda / ClosePermissionPda
CreatePermissionPdainitializes aPermissionPDA for a givenpermission_authority.ClosePermissionPdacloses it and refunds rent to the caller, revoking the grant. Pools already created under that grant are unaffected.
admin pubkey or a dedicated permission_pda_admin key (a distinct constant on mainnet vs devnet). See Admin keys and multisig → CLMM.
PoolState: seed_index field
Two bytes were carved out of an existing padding region (padding: [u8; 6] → seed_index: [u8; 2] + padding: [u8; 4]) directly after fee_on. The total account size is unchanged, and no indexer migration is required. For pools created via CreatePool / CreateCustomizablePool, seed_index is [0, 0]; for permissioned pools it carries the creator’s chosen index and is required to re-derive the pool address off-chain.
OpenLimitOrder: output-side accounts and frozen-account guard
The OpenLimitOrder account list gains output_token_account, output_vault, and output_vault_mint. They are used only for validation — no output is debited on open. The handler now requires that neither the input nor the output token account is frozen, returning NotApproved otherwise (the same code already returned when the pool has swap or limit-order disabled). This closes a gap where an order could be opened against a token account that could never receive the fill (e.g. a not-yet-thawed account for a default-frozen Token-2022 mint).
See Instructions → OpenLimitOrder.
Error codes
No new error codes. The existing6000 NotApproved now additionally covers the frozen input/output token account and the swap/limit-order-disabled cases on OpenLimitOrder, and the “signer is neither admin nor permission_pda_admin” case on the permission instructions. The error-code table and numbering are otherwise unchanged from the 2026-05-18 release. See Error codes → CLMM.
Migration notes
- Reading pool addresses: do not assume one pool per
(config, mint0, mint1). A pair may now have a canonical pool plus one or more permissioned pools atseed_index-derived addresses. Enumerate pools from the API / indexer rather than deriving a single PDA, and storeseed_indexwhen you need to re-derive a permissioned pool. - Placing limit orders: add the three output-side accounts to your
OpenLimitOrdertransaction, and surface a clear message whenNotApprovedis returned for a frozen account. - No account-size or layout break:
PoolStateis the same size; the retired padding bytes now holdseed_index.
Pages updated
products/clmm/overview— “What’s new” note on permissioned multi-pools and the limit-order guard.products/clmm/instructions— newCreatePermissionedPoolsection,CreatePermissionPda/ClosePermissionPdain the instruction table and admin-gating note, and the updatedOpenLimitOrderaccount list / preconditions / errors.products/clmm/accounts—Permissionin the inventory and its own section, thePoolState.seed_indexfield, PDA-derivation helpers (derivePermissionedPool,derivePermission), and the lifecycle quick reference.security/admin-and-multisig— newpermission_pda_adminrow and permission-scope explainer in the CLMM roles table.reference/error-codes— broadenedNotApproveddescription.
raydium-clmmsource (create_permissioned_pool.rs,admin/create_permission_pda.rs,admin/close_permission_pda.rs,states/permission.rs,states/pool.rs,limit_order/open_limit_order.rs,lib.rs).

