本页内容由 AI 自动翻译,所有内容以英文版本为准。查看英文版 →
本页是权威的指令参考。如需实际编写这些指令的代码,请参阅
products/cpmm/code-demos。错误代码含义见 reference/error-codes。2026-09 程序升级在 Anchor 1.0.2 / Solana 3.1.10 上重建了 CPMM,添加了管理员指令 CollectExcessLamports,移除了硬编码的 Token-2022 mint 白名单,并改变了 CreateAmmConfig 写入 protocol_owner / fund_owner 的内容。没有用户面向的指令改变其账户、参数或数学逻辑。详见 2026-09-09 更新日志条目。指令概览
状态位掩码:每个池的
status 是一个 u8,其中位 0 = 禁用存款,位 1 = 禁用提取,位 2 = 禁用交换(程序中的 PoolStatusBitIndex { Deposit, Withdraw, Swap })。清除位表示允许操作;设置位表示暂停。UpdatePoolStatus 接受原始 u8 并覆盖现有值。
后续部分详细介绍每一条。账户顺序遵循 CPMM IDL;SDK 和 raydium-cp-swap/programs/cp-swap/src/instructions 中的 Rust 客户端匹配此顺序。
Initialize
创建新的 CPMM 池。
参数
* pool_state 仅在随机密钥对路径上签名;规范 PDA 路径运行时不需要 pool_state 签名。
前置条件
- Mint 已排序(
token_0_mint < token_1_mint按字节顺序)。 - 两个 mint 都不使用 CPMM 允许列表外的扩展(
TransferFeeConfig、MetadataPointer、TokenMetadata、InterestBearingConfig、ScaledUiAmount)— 见products/cpmm/accounts。其SupportMintAssociatedPDA(种子[b"support_mint", mint])存在的 mint 跳过扩展检查 — 但你必须将该 PDA 附加到remaining_accounts。程序仅扫描你传递的账户,从不自己加载 PDA,所以依赖注册表而不提供账户仍会失败,返回NotSupportMint(6007)。顺序无关紧要(按密钥匹配);为每个需要绕过的 mint 传递一个条目。该注册表是唯一的绕过,因为 2026-09 升级移除了硬编码的四个 mint 白名单。 creator在各自的 ATA 中至少有init_amount_0和init_amount_1。amm_config.disable_create_pool == false。
pool_state.lp_supply = sqrt(init_amount_0 * init_amount_1)— 完整的平方根。创建者被铸造lp_supply − 100;100 个锁定的基础单位被计入lp_supply但从不铸造。- 因此在池的生命周期内
lp_mint.supply == pool_state.lp_supply − 100。所有 LP 份额数学(存款、提取)除以lp_supply,所以使用该字段,不要替换 mint 的链上供应。如果sqrt(...) < 100则回退为InitLpAmountTooLess。 observation_state被初始化;observation_index = 0和pool_id = pool_state.key()。create_pool_feelamport 从创建者转移到接收者并同步为原生 SOL(它是 wSOL ATA)。- 池的状态位掩码是
0(存款/提取/交换全部启用)。 enable_creator_fee = false和creator_fee_on = BothToken。Initialize不支持启用创建者费用 — 该路径是InitializeWithPermission。- 如果调用者传递的值
<= block_timestamp,open_time被提升到block_timestamp + 1。交换在open_time之前被拒绝;存款和提取立即工作。
reference/error-codes)
InvalidInput— mint 未排序或相同。NotSupportMint— 被阻止的 Token-2022 扩展。ExceededSlippage— 罕见;如果init_amount_0/1由于小数位不匹配导致零 LP。
Deposit
按池的比例在两个代币中添加流动性。
参数
数学
vault_amount_without_fee,即原始余额减去累积的协议、基金和创建者计数器),而不是原始金库余额;滑点上限针对支付者实际转移的内容检查,在 Token-2022 转账费用添加后,而不是针对总金库移动。
k 的比例性没有变化 — 两个总额和 lp_supply 按相同因子缩放。
后置条件
lp_supply += lp_token_amount。vault_0 += needed_token_0(扣除输入上任何 Token-2022 转账费用)。vault_1 += needed_token_1(扣除输入上任何 Token-2022 转账费用)。
ExceededSlippage、ZeroTradingTokens、如果存款被暂停则 InvalidStatus。
Withdraw
销毁 LP 代币并按比例接收两个基础代币。
参数
前 13 个账户与
Deposit 相同,lp_mint 是可写的,因为 LP 代币被销毁。Withdraw 另外接受第 14 个账户 memo_program(受约束 address = memo::ID)— Deposit 不接受。13 账户的 Withdraw 失败 Anchor 反序列化,所以 LP 无法退出。
数学
lp_supply -= lp_token_amount。- 金库发送
out_token_0/out_token_1(总额;用户接收扣除任何 Token-2022 转账费用的净额)。
SwapBaseInput
精确输入交换。
参数
顺序输入 → 输出是按用户的方向,而不是池的规范
token_0 / token_1。程序通过匹配 mint 来确定哪个金库是哪个。
数学 — 见 products/cpmm/math。
前置条件
open_time <= now。pool_status允许交换。- 对于此权限,两个 mint 都未暂停或冻结。
amount_in > 0。
ExceededSlippage—amount_out < minimum_amount_out。ZeroTradingTokens— 交易舍入为零。NotApproved— 池通过UpdatePoolStatus暂停交换。InvalidInput— mint 不匹配池的任一金库 mint。
SwapBaseOutput
精确输出交换。
参数
SwapBaseInput 相同。
数学 — 反向曲线带上限,见 products/cpmm/math。
常见错误 — ExceededSlippage(gross_in > max_amount_in)、ZeroTradingTokens、InvalidInput、NotApproved。
CollectProtocolFee
从金库中清扫累积的协议费用到协议目标。
参数 — 无。
账户
效果
amm_config.protocol_owner 也不是程序管理员,则 InvalidOwner(6001)。(此路径上没有 NotApproved。)
CollectFundFee
与 CollectProtocolFee 形状相同,但由 amm_config.fund_owner 签名 — 或再次由程序管理员 — 并将 fund_fees_* 计数器清零。错误签名者时返回相同的 InvalidOwner。
CollectCreatorFee
由 pool_state.pool_creator 签名。它结算累积的创建者费用并将创建者的部分转移到创建者的代币账户。
参数 — 无。
账户
效果
CollectProtocolFee。两个计数器已被排除在曲线对金库的视图之外,所以池的价格不会移动。完整推导见 products/cpmm/fees。
常见错误 — 当两个创建者计数器都为零时 NoFeeCollect(在分割前检查),如果解析的 share_rate 超过 1_000_000 则 InvalidInput(6003),如果预订份额会溢出 protocol_fees_token_* 则 MathOverflow(6011),以及如果 creator_fee_share 不是规范 PDA 则 Anchor 的 ConstraintSeeds 错误。
CollectCreatorFeePermissionless
任何人都可以触发创建者费用收集。指令始终将创建者的部分发送到由 pool_state.pool_creator 拥有的规范关联代币账户;调用者无法选择另一个创建者或目标。如果任一 ATA 缺失,支付者为其创建提供资金。
原始的 CollectCreatorFee 仍然可调用,所以想要为自己的收集签名的创建者仍然可以。
参数 — 无。
账户
效果 — 与上面的
CollectCreatorFee 相同:份额从 creator_fee_share 或 amm_config 解析,协议的部分被记入 protocol_fees_token_{0,1},创建者的部分被转移到创建者 ATA,两个创建者计数器被清零,recent_epoch 被更新。当两个计数器都为零时返回 NoFeeCollect。
UpdatePoolStatus
暂停或恢复池上的单个操作。status 字段是一个位掩码:
参数
管理员密钥是编译到程序中的公钥(
crate::admin::ID),而不是 BPF 升级权限 — 更改它需要程序升级。见 reference/program-addresses 了解值,以及 security/admin-and-multisig 了解谁持有它。
CreateAmmConfig
创建新的费用等级。
参数
前置条件
- 不存在具有相同
index的AmmConfig。 protocol_fee_rate + fund_fee_rate <= FEE_RATE_DENOMINATOR_VALUE。
2026-09 中更改:新配置的费用所有者不再来自签名者。
create_amm_config 现在将程序的硬编码 protocol_fee_owner::ID 写入 protocol_owner 和 fund_fee_owner::ID 写入 fund_owner,而不是将管理员签名者的密钥复制到两者。地址在 reference/program-addresses。后果:新创建的 AmmConfig 上的费用落入专用费用钱包,而不是管理员的。管理员确实仍然是收集的接受签名者 — CollectProtocolFee / CollectFundFee 接受 amm_config.protocol_owner / fund_owner 或 crate::admin::ID — 所以没有什么需要轮换来清扫;改变的只是收益默认去向的地方。现有的 AmmConfig 账户不被重写 — 存储在它们上的任何内容仍然管理,所以始终从账户读取 protocol_owner / fund_owner 而不是假设任一值。UpdateAmmConfig 参数 3 和 4 仍然轮换它们。UpdateAmmConfig
更改现有 AmmConfig 上的费率或所有权。接受 param: u8(要更新的字段)和 value: u64。完整的分派表:
param = 0→trade_fee_rate(断言trade_fee_rate + creator_fee_rate < 1_000_000)param = 1→protocol_fee_rate(断言≤ 1_000_000和+ fund_fee_rate ≤ 1_000_000)param = 2→fund_fee_rate(断言≤ 1_000_000和+ protocol_fee_rate ≤ 1_000_000)param = 3→protocol_owner。新密钥不在value中:将其附加为remaining_accounts[0](只读可以)。它不能是默认公钥,省略账户会在unwrap()上恐慌。param = 4→fund_owner。与3相同的机制。param = 5→create_pool_feeparam = 6→disable_create_pool(任何非零value禁用)param = 7→creator_fee_rate(断言creator_fee_rate + trade_fee_rate < 1_000_000)param = 8→creator_fee_share_rate(断言≤ 1_000_000)。添加于 2026-09-19。协议在此等级上的创建者费用的默认份额;见products/cpmm/fees。它与protocol_fee_rate无关,后者分割交易费用。
param 返回 InvalidInput。
更改由管理员签名并影响绑定到此 AmmConfig 的每个池在下一次交换时。无迁移;池简单地读取新值。
CreateCreatorFeeShare
为一个 (creator, amm_config) 对设置自定义协议份额的创建者费用,覆盖 AmmConfig.creator_fee_share_rate 用于该创建者在该费用等级上拥有的每个池。添加于 2026-09-19 创建者费用份额升级。
参数
前置条件
share_rate <= 1_000_000,否则InvalidInput(6003)。- PDA 不能已存在 — Anchor 的
init在同一对的第二次调用时失败。要更改费率,关闭账户并重新创建。
creator_fee_share存储bump、creator、amm_config和share_rate。- 每个后续的
CollectCreatorFee/CollectCreatorFeePermissionless在由creator在amm_config下创建的池上从此账户解析份额,而不是配置。
池创建者不是此指令的一方,也不签名。费率在收集时读取,所以在费用已经累积后创建的覆盖适用于该累积余额。
CloseCreatorFeeShare
移除覆盖。该对回退到 AmmConfig.creator_fee_share_rate。
参数 — 无。
账户
后置条件
- 账户被关闭,其 lamport 去到
owner。 - 该对的收集再次从
amm_config.creator_fee_share_rate解析份额 — 除非管理员已设置UpdateAmmConfig参数8,否则为0。
CollectExcessLamports
管理员清扫 CPMM 控制的账户上超过租金豁免最低值的 lamport。添加于 2026-09 升级,以便协议可以回收 SIMD-0437 租金减少 在每个步骤后在升级前创建的账户上留下的过度资金。
只有超额移动。代币余额、账户数据、所有者、池状态和曲线保持不变,指令对已在最低值的账户是无操作 — 所以在每个推出步骤后重新运行是安全的。
参数 — 无。
账户
顺序修复,2026-09-19。 程序现在对
remaining_accounts 进行两次传递 — 每个代币程序 CPI 首先,然后是 CPMM 拥有的 PDA 的直接借记。当 PDA 在 CPI 之前被借记时,它们会中止,返回运行时的 UnbalancedInstruction(“指令前后账户余额之和不匹配”),因为调用者的待处理 lamport 更改仅在 CPI 实际执行时刷新到账户。调用者不必自己对列表进行分组或排序。owner 分派:
因为它接受无界的
remaining_accounts 列表,交易大小是真正的限制 — 与 solana-fundamentals/rent-and-reclaimable-rent 中描述的钱包侧清扫相同的约束。
常见错误 — InvalidOwner(6001,错误的签名者)、LamportsCalculateError(6015,wSOL 往返没有净为零)、以及当账户持有少于其自己的租金最低值时来自程序拥有路径的 InsufficientFunds。
无 SDK 构建器。 @raydium-io/raydium-sdk-v2 不提供此指令的构建器,raydium-sdk-V2-demo 仓库也不提供 — 它是管理员路径。手动编码,就像 solana-fundamentals/rent-and-reclaimable-rent 中的钱包侧清扫对代币程序指令所做的那样。
状态变化矩阵
后续步骤
products/cpmm/code-demos— 上述内容的可运行 TypeScript 示例。reference/error-codes— 完整的 Anchor 错误表。products/cpmm/fees—Collect*Fee指令清扫的费用累积模型。

