Skip to main content
本页内容由 AI 自动翻译,所有内容以英文版本为准。查看英文版 →
本页与 products/clmm/accounts(账户是什么)和 products/clmm/math(数学是什么)配套。本页对参数和账户顺序具有权威性;具体的字节布局来自 IDL。

指令清单

大多数仅限管理员的指令(CreateAmmConfigUpdateAmmConfigUpdatePoolStatusCreateSupportMintAssociatedCreateOperationAccountUpdateOperationAccountCloseProtocolPosition)由程序的硬编码 admin 公钥门控。CreatePermissionPda / ClosePermissionPda 接受 admin 公钥专用的 permission_pda_admin 密钥。奖励流管理员指令(TransferRewardOwnerCollectRemainingRewards)由奖励资金提供者门控,而不是程序管理员。 V2 后缀表示”支持保管库/NFT 上的 Token-2022,需要位图扩展槽”。SDK 默认为新池选择 V2。

CreatePool

参数
账户(简化) 前置条件
  • token_mint_0 < token_mint_1(按字节顺序)。
  • amm_config.disable_create_pool == false
  • 铸币未被 Token-2022 扩展允许列表拒绝。
后置条件
  • pool_state.sqrt_price_x64 = sqrt_price_x64tick_current = floor(log_{1.0001}(price))
  • pool_state.liquidity = 0(尚无头寸)。
  • pool_state.fee_on = FromInput(旧版默认)。
  • pool_state.dynamic_fee_info 被清零(动态费用禁用)。

CreateCustomizablePool

推荐用于新池。与 CreatePool 效果相同,加上每池费用收集模式和可选的动态费用选择加入。 参数
账户(简化) — 与 CreatePool 相同,加上当 enable_dynamic_fee = true 时: 前置条件 — 与 CreatePool 相同。如果 enable_dynamic_fee = falsedynamic_fee_config 被忽略。 后置条件
  • pool_state.fee_on 设置为选定的 CollectFeeOn 变体。
  • 如果启用了动态费用:pool_state.dynamic_fee_info 从提供的 DynamicFeeConfig 初始化(复制五个校准参数;状态字段清零)。
  • 否则:pool_state.dynamic_fee_info 被清零(= 此池永远不活跃动态费用)。
fee_on 和动态费用启用位在池创建时设置。没有就地升级 — 通过旧版 CreatePool 创建的池无法追溯获得动态费用或单边费用。新部署应默认使用此指令。

CreatePermissionedPool

CreatePoolCreateCustomizablePool 都从 ["pool", amm_config, token_mint_0, token_mint_1] 派生池 PDA,因此每个 (config, mint0, mint1) 三元组恰好有一个规范池地址 — 在相同种子处的第二个 init 失败。CreatePermissionedPool 通过将客户端提供的 seed_index: u16 折叠到池 PDA 种子中来解除此限制,允许同一交易对和费用等级的多个池 — 每个在其自己的地址处。因为任意池地址是特权能力,支付者必须持有授权它的 Permission PDA。 关于池的其他一切与 CreateCustomizablePool 相同:它采用相同的 CreateCustomizableParams 并支持单边费用和动态费用选择加入。 参数
账户(简化) — 与 CreateCustomizablePool 相同,加上前面的: pool_state PDA 从 ["pool", amm_config, token_mint_0, token_mint_1, seed_index.to_le_bytes()] 派生。 前置条件
  • seed_index != 0seed_index0 保留给旧版池并在此处被拒绝;[0, 0] 种子分量是使旧版池地址折叠到经典四种子形式的原因。
  • payerpermission PDA 存在(由管理员通过 CreatePermissionPda 创建)。
  • CreatePool 相同的铸币/允许列表规则。
后置条件
  • 新的 pool_state 存在于 seed_index 派生的地址处,pool_state.seed_index = seed_index
  • 所有其他后置状态与 CreateCustomizablePool 匹配(费用模式、可选动态费用)。
此指令扩大一般池创建访问 — 无权限创建继续通过 CreatePool / CreateCustomizablePool,每对仍为一个池。CreatePermissionedPool 存在于白名单操作者需要同一交易对的多个池(例如不同的初始价格或启动队列)并持有由管理员授予的 Permission PDA 的特定情况。

OpenPositionV2 / OpenPositionWithToken22Nft

在现有池内创建新头寸。 参数
账户(简化) 数学 — 见 products/clmm/math。给定 base_flag,程序将 liquidity(amount_0_max, amount_1_max) 解析为实际 L 和消耗的实际代币金额。 前置条件
  • tick_lower < tick_upper,都是 pool.tick_spacing 的倍数,在 [MIN_TICK, MAX_TICK] 内。
  • 所需的 tick 数组已通过并初始化(或在交易中通过 InitTickArray CPI 创建)。
  • 用户在源 ATA 中至少有 amount_0_maxamount_1_max
后置条件
  • personal_position 存在,liquidity 设置,fee_growth_inside_last 快照。
  • tick_lowertick_upper 处的 tick 数组条目更新(liquidity_gross += Lliquidity_net ± L,维护费用增长快照)。
  • 如果头寸在范围内(tick_lower ≤ tick_current < tick_upper),pool_state.liquidity += L
  • 头寸 NFT 铸币记录 pool_state 作为冻结权限。铸币权限在单个 NFT 被铸造后被移除。记录冻结权限不改变 NFT 代币账户的状态。
  • NFT 代币账户保持解冻,除非指令是 OpenPositionV2OpenPositionWithToken22Nft任一保管库铸币的冻结权限与 CLMM 的受限发行者列表匹配。仅该匹配的 V2 路径冻结账户。OpenPosition V1 不冻结。
常见错误InvalidTickIndexNotApprovedZeroAmountSpecifiedTransactionTooLarge(如果太多 tick 数组)。
头寸冻结不添加声明的指令账户或参数。客户端可以使用现有的 V2 布局打开这些头寸。行为在链上从 vault_0_mintvault_1_mint 选择。

IncreaseLiquidityV2

向已打开的头寸添加流动性。 参数
账户 — 如 OpenPosition 减去 NFT 铸币(头寸已存在;NFT 作为所有者的 ATA 持有 1 个代币传递)。 效果
  • 从用户转移 amount_0_actual / amount_1_actual → 保管库。
  • 增加 personal_position.liquiditypool_state.liquidity(如果在范围内),以及端点 tick 的 liquidity_gross / liquidity_net
  • 收集自上次接触以来应付的费用和奖励并将其记入 tokens_fees_owed_{0,1} / reward_amount_owed。这些仅在 DecreaseLiquidityCollectReward 时支付,而不是在增加时。

DecreaseLiquidityV2

从头寸移除流动性。 参数
账户 — 与 IncreaseLiquidity 相同的形状。 效果
  • 给定当前 sqrt_price_x64,计算移除的 L(amount_0, amount_1)
  • 结算自上次接触以来累积的费用/奖励,与 IncreaseLiquidity 相同。
  • 从保管库转移 amount_0 + fees_owed_0amount_1 + fees_owed_1 到用户。
  • 递减流动性计数器;如果新的 personal_position.liquidity == 0,头寸符合 ClosePosition 的条件。
滑点amount_0_minamount_1_min 是用户在输出端 Token-2022 转移费用净额后接受的最小值。

ClosePosition

销毁头寸 NFT 并关闭 PersonalPositionState 声明的账户 剩余账户
  • 解冻的 NFT:不需要;额外的池账户是无害的,因为处理程序不读取它。
  • 冻结的 NFT:将 personal_position.pool_id 作为第一个剩余账户追加。程序将其加载为 PoolState 并使用其 PDA 种子签署解冻。
前置条件
  • personal_position.liquidity == 0
  • tokens_fees_owed_{0,1} == 0
  • 所有奖励计数器 reward_amount_owed == 0
(即,先收集所有内容并递减到零。) 效果
  • 如果 NFT 代币账户被冻结,验证第一个剩余账户等于 personal_position.pool_id,然后使用池 PDA 解冻。
  • 销毁 NFT。
  • 关闭 NFT 代币账户和 personal_position,将租金退款给 nft_owner。如果头寸 NFT 使用 Token-2022,它也关闭 NFT 铸币;经典 SPL Token 铸币无法关闭,保持供应量为零。
解冻、销毁和关闭是原子的。NFT 在这些步骤之间无法变为可转移。 条件客户端中断 — 声明的 IDL 布局未改变,因此旧版客户端继续关闭现有和解冻的头寸。省略池剩余账户的旧版构建器在关闭冻结头寸时失败,出现 AccountLack。为每次关闭传递池是最简单的兼容策略。

SwapV2

沿流动性曲线行走;精确输入精确输出取决于 is_base_input 参数
账户(简化) 调用者传递覆盖预期交换行走的排序 tick 数组列表;程序使用所需的数量。SDK 通过 PoolUtils.computeAmountOutFormat 或 API 的报价端点计算此列表。 前置条件
  • pool_state.status 允许交换。
  • now >= open_time
  • sqrt_price_limit_x64sqrt_price_x64 的正确一侧用于该方向。
常见错误ExceededSlippageSqrtPriceLimitOverflowTickArrayNotFoundLiquidityInsufficient SwapV2 在内部做什么调用者应该知道的(2025 年后发布):
  1. 动态费用附加费 — 如果 pool.dynamic_fee_info 非零,程序使用自上次交换以来遍历的 tick 距离更新波动率累加器(使用 products/clmm/fees 中的过滤/衰减规则)并在 AmmConfig.trade_fee_rate 之上添加 dynamic_fee_component。总费用上限为 10%(MAX_FEE_RATE_NUMERATOR / 1_000_000)。
  2. 限价单匹配 — 当价格行走穿过持有开放限价单的 tick 时,程序首先在该 tick 处填充可用的限价单流动性(按 order_phase 的 FIFO),然后沿 LP 流动性曲线继续。已填充的金额更新 tick.unfilled_ratio_x64tick.part_filled_orders_remaining 以供后续结算;订单本身保持未花费,直到其所有者调用 SettleLimitOrder
  3. 单边费用路由 — 当 pool.fee_on = Token0OnlyToken1Only 时,交换步骤仍计算相同的输入-输出交易;费用随后被路由到配置的一侧。对于配置的费用侧是输出的方向,费用从交换输出中扣除(用户接收 out − fee);对于配置的费用侧是输入的方向,行为与 FromInput 匹配。见 pool_state 上的 is_fee_on_input(zero_for_one)is_fee_on_token0(zero_for_one)
Swap(V1)实现与 SwapV2 相同的动态费用、单边费用路由和限价单匹配;它缺少的唯一功能是 Token-2022 支持 — 两个保管库都必须是经典 SPL Token。任何 Token-2022 铸币的池必须通过 SwapV2 交换。聚合器和 SDK 已经为每个 CLMM 腿优先选择 V2,所以调用者不必根据铸币类型分支。

OpenLimitOrder

在特定 tick 处下卖单。订单坐在每 tick FIFO 队列中,并在价格穿过时填充。 参数
账户(简化)
账户列表变化(2026-07 发布)。 OpenLimitOrder 现在也采用输出侧账户 — output_token_accountoutput_vaultoutput_vault_mint — 除了输入侧。它们仅用于验证:如果所有者的输入输出代币账户被冻结,程序拒绝订单。这保证填充实际上可以结算到所有者的输出 ATA,这对于允许列表/默认冻结的 Token-2022 铸币(例如许可代币)很重要,其中账户可能尚未解冻。针对旧版单侧账户列表构建的客户端必须添加三个输出账户。
前置条件
  • input_token_accountoutput_token_account 都未被冻结(否则 NotApproved)。
  • pool_state.status 允许交换(位 4)和限价单(位 5)操作(否则 NotApproved)。
  • tick_index % pool.tick_spacing == 0 并在 [MIN_TICK, MAX_TICK] 内。
  • tick_index 在**pool.tick_current 的正确一侧**用于选定的方向(卖 token0 → tick 必须在当前之上,反之亦然)。在已穿过的 tick 处卖出会立即匹配并被拒绝。
后置条件
  • limit_order 存在,快照打开时的 tick.order_phasetick.unfilled_ratio_x64
  • tick.orders_amount += amount(在当前队列中)。
  • limit_order_nonce.order_nonce += 1
  • 发出 OpenLimitOrderEvent
常见错误NotApproved(输入或输出代币账户冻结,或池禁用交换/限价单)、InvalidLimitOrderAmount(零或低于池的最小值)、InvalidTickIndex(超出 [MIN_TICK, MAX_TICK],或在 tick_current 的错误一侧用于选定的方向)、TickAndSpacingNotMatchtick_index % pool.tick_spacing != 0)、OrderPhaseSaturated

IncreaseLimitOrder

添加到现有开放订单。仅由订单的 owner 调用。 参数
账户 — 如 OpenLimitOrder 减去 nonce 账户;limit_order PDA 直接传递。 前置条件
  • limit_order.owner == signer
  • 订单仍在同一队列中(tick.order_phase == limit_order.order_phase)。如果队列已开始填充,订单部分结算 — 调用者应首先调用 DecreaseLimitOrderSettleLimitOrder 来前进。
效果
  • 从所有者 ATA 转移 amountinput_vault
  • limit_order.total_amount += amounttick.orders_amount += amount

DecreaseLimitOrder

减少或完全取消开放订单。将未成交的剩余部分支付回所有者,加上过去部分填充已结算的任何输出。 参数
账户 — 输入和输出代币两侧: 效果
  • 从打开以来的队列 unfilled_ratio_x64 重新计算订单的已填充金额。
  • 将已填充的输出发送到 output_token_account
  • amount 的未成交输入发送回 input_token_account
  • 相应地更新 limit_order。如果新的未成交剩余为零,程序关闭账户并将租金退款给 owner

SettleLimitOrder

将已填充的输出代币推送给所有者,而不改变订单的未成交剩余。当 auto_withdraw 保管人想要滴流支付长期运行的部分填充时很有用。 调用者 — 订单的 owner,或程序的 limit_order_admin(运行自动保管人循环的离线操作热钱包)。保管人没有其他权限 — 它无法将用户资金移出推送已填充的输出到订单的 owner ATA 之外。 账户 效果
  • 使用 (limit_order.unfilled_ratio_x64, tick.unfilled_ratio_x64) 计算累积应付输出。
  • 将增量转移到 output_token_account
  • 更新 limit_order.settled_output
  • 关闭订单;它对任何剩余输入仍然开放。

CloseLimitOrder

关闭完全消耗的订单账户。租金始终返回给 limit_order.owner,无论谁签署。 调用者ownerlimit_order_admin 前置条件
  • 订单有零未成交剩余(amount == total_amount 被填充和结算,或所有者之前将订单减少到零并忘记关闭)。
效果
  • 关闭 limit_order;租金发送到 limit_order.owner

CreateDynamicFeeConfig(管理员)

在 u16 索引下创建可重用的参数集。 参数
账户 常见错误 — 如果 decay_period <= filter_period 或任何 0 值字段超出范围,则 InvalidDynamicFeeConfigParams

UpdateDynamicFeeConfig(管理员)

修改现有 DynamicFeeConfig。已在创建时快照配置的池被追溯更新;仅引用此配置的新创建池将获取新值。 参数 — 与 CreateDynamicFeeConfig 相同的五个校准字段(filter_perioddecay_periodreduction_factordynamic_fee_controlmax_volatility_accumulator);index 在创建时固定,此处不重新传递。

CollectProtocolFee / CollectFundFee

与 CPMM 的 CollectProtocolFee / CollectFundFee 形状相同。签署者必须与 AmmConfig.owner / AmmConfig.fund_owner 匹配。从池的保管库清理累积的协议/基金费用到收件人,将相应的 PoolState.protocol_fees_* / fund_fees_* 字段清零。

InitializeReward

向池添加新的奖励流。最多 3 个流可能同时活跃。 参数
账户 前置条件
  • 池上当前活跃的流少于 3 个。
  • 资金提供者作为此指令的一部分将 total_emission = emissions_per_second × (end_time − open_time) 价值的奖励代币存入保管库。
  • operation_state 白名单的奖励铸币。

SetRewardParams

扩展、充值或更改现有奖励流的发放速率。通常由池创建者或 Raydium 多签调用。约束在链上:你通常可以扩展 end_time 或增加发放,而不是追溯缩小它们。检查 operation_state 的所有者列表。

UpdateRewardInfos

纯簿记 — 通过乘以 emissions_per_second × Δt / liquidityreward_growth_global_x64 结算到当前时间。由每个接触流动性的指令在内部调用。作为独立指令公开,因为外部参与者(UI、cranks)有时想要触发它。

CollectReward

头寸所有者声称应付的奖励代币。 账户 效果
  • 结算奖励增长(与费用相同的模式)。
  • 将应付金额转移到收件人 ATA,将 reward_amount_owed[i] 清零。

状态变化矩阵

接下来去哪里

来源: