本页内容由 AI 自动翻译,所有内容以英文版本为准。查看英文版 →
本页与
products/clmm/accounts(账户说明)和 products/clmm/math(数学原理)配套阅读。本页是参数与账户顺序的权威参考,具体字节布局以 IDL 为准。指令总览
大多数管理员专用指令(
CreateAmmConfig、UpdateAmmConfig、UpdatePoolStatus、CreateSupportMintAssociated、CreateOperationAccount、UpdateOperationAccount、CloseProtocolPosition)由程序硬编码的 admin 公钥控制。奖励流管理指令(TransferRewardOwner、CollectRemainingRewards)由奖励资助方控制,而非程序管理员。
V2 后缀表示”支持 vault/NFT 的 Token-2022,需要 bitmap 扩展槽位”。SDK 默认对新池使用 V2。
CreatePool
参数
前置条件
token_mint_0 < token_mint_1(按字节序)。amm_config.disable_create_pool == false。- Mint 未被 Token-2022 扩展白名单拒绝。
pool_state.sqrt_price_x64 = sqrt_price_x64,tick_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 = false,则忽略 dynamic_fee_config。
后置条件
pool_state.fee_on设置为所选的CollectFeeOn变体。- 若启用了动态费率:
pool_state.dynamic_fee_info从提供的DynamicFeeConfig初始化(复制五个校准参数,状态字段清零)。 - 否则:
pool_state.dynamic_fee_info清零(此池永久禁用动态费率)。
fee_on 和动态费率启用位仅在池创建时设置。没有就地升级路径——通过旧版 CreatePool 创建的池无法追溯获得动态费率或单侧费率。新部署应默认使用本指令。
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 数组已传入并初始化(或在本交易中通过
InitTickArrayCPI 创建)。 - 用户源 ATA 中至少有
amount_0_max和amount_1_max数量的代币。
personal_position存在,liquidity已设置,fee_growth_inside_last已快照。tick_lower和tick_upper的 tick-array 条目更新(liquidity_gross += L,liquidity_net ± L,维护费率增长快照)。- 若头寸在区间内(
tick_lower ≤ tick_current < tick_upper),pool_state.liquidity += L。
InvalidTickIndex、NotApproved、ZeroAmountSpecified、TransactionTooLarge(tick 数组过多)。
IncreaseLiquidityV2
向已开仓的头寸追加流动性。
参数
OpenPosition 类似,无需 NFT mint(头寸已存在;NFT 以所有者持有 1 个代币的 ATA 形式传入)。
效果
- 将
amount_0_actual/amount_1_actual从用户转入 vault。 - 递增
personal_position.liquidity和pool_state.liquidity(若在区间内),并相应调整端点 tick 的liquidity_gross/liquidity_net。 - 归集自上次操作以来欠付的费用和奖励,记入
tokens_fees_owed_{0,1}/reward_amount_owed。这些金额仅在DecreaseLiquidity或CollectReward时付出,追加流动性时不支付。
DecreaseLiquidityV2
从头寸中移除流动性。
参数
IncreaseLiquidity 相同。
效果
- 根据当前
sqrt_price_x64计算移除L对应的(amount_0, amount_1)。 - 与
IncreaseLiquidity相同,结算自上次操作以来累积的费用/奖励。 - 将
amount_0 + fees_owed_0和amount_1 + fees_owed_1从 vault 转出给用户。 - 递减流动性计数器;若新的
personal_position.liquidity == 0,该头寸可调用ClosePosition。
amount_0_min 和 amount_1_min 是用户在输出端扣除 Token-2022 转账费用后可接受的最低金额。
ClosePosition
销毁头寸 NFT 并关闭 PersonalPositionState。
前置条件
personal_position.liquidity == 0。tokens_fees_owed_{0,1} == 0。- 所有奖励计数器
reward_amount_owed == 0。
- 销毁 NFT。
- 关闭 NFT mint 账户和
personal_position账户,将租金退还给payer。
SwapV2
沿流动性曲线执行兑换;根据 is_base_input 决定精确输入或精确输出模式。
参数
调用方传入一组按优先级排列的 tick 数组,覆盖预期的兑换路径;程序按需使用。SDK 通过
PoolUtils.computeAmountOutFormat 或 API 的报价端点计算此列表。
前置条件
pool_state.status允许兑换。now >= open_time。sqrt_price_limit_x64在当前sqrt_price_x64的正确一侧(与兑换方向一致)。
ExceededSlippage、SqrtPriceLimitOverflow、TickArrayNotFound、LiquidityInsufficient。
调用方应了解的 SwapV2 内部逻辑(2025 年后版本):
- 动态费率附加 — 若
pool.dynamic_fee_info非零,程序使用自上次兑换以来穿越的 tick 距离更新波动率累积器(应用products/clmm/fees中的过滤/衰减规则),并在AmmConfig.trade_fee_rate基础上叠加dynamic_fee_component。总费率上限为 10%(MAX_FEE_RATE_NUMERATOR / 1_000_000)。 - 限价单匹配 — 当价格路径穿越持有未成交限价单的 tick 时,程序优先以 FIFO 顺序(按
order_phase)成交该 tick 的限价单流动性,再沿 LP 流动性曲线继续前进。成交金额更新tick.unfilled_ratio_x64和tick.part_filled_orders_remaining,供后续结算使用;挂单本身直到所有者调用SettleLimitOrder前不会被消耗。 - 单侧费率路由 — 当
pool.fee_on = Token0Only或Token1Only时,兑换步骤计算的输入输出量不变,费用路由至配置的一侧。若配置的费率侧为输出方,费用从兑换输出中扣除(用户收到out − fee);若为输入方,行为与FromInput相同。详见PoolState上的is_fee_on_input(zero_for_one)和is_fee_on_token0(zero_for_one)。
Swap(V1)与 SwapV2 实现了相同的动态费率、单侧费率路由和限价单匹配,唯一区别是不支持 Token-2022——两个 vault 必须为经典 SPL Token。任何包含 Token-2022 mint 的池必须通过 SwapV2 进行兑换。聚合器和 SDK 已对所有 CLMM 腿优先使用 V2,调用方无需根据 mint 类型进行分支判断。
OpenLimitOrder
在指定 tick 挂卖单。挂单进入该 tick 的 FIFO 队列,价格穿越时成交。
参数
前置条件
tick_index % pool.tick_spacing == 0且在[MIN_TICK, MAX_TICK]范围内。tick_index须在当前pool.tick_current的正确一侧(卖出 token0 时 tick 须高于当前价,反之亦然)。在已穿越的 tick 挂单会被立即撮合,因此会被拒绝。pool_state.status允许限价单操作(位 5)。
limit_order存在,快照开仓时的tick.order_phase和tick.unfilled_ratio_x64。tick.orders_amount += amount(在当前队列中)。limit_order_nonce.order_nonce += 1。- 触发
OpenLimitOrderEvent。
InvalidLimitOrderAmount(金额为零或低于池的最低限额)、InvalidTickIndex(超出 [MIN_TICK, MAX_TICK] 范围,或在选定方向下位于 tick_current 错误一侧)、TickAndSpacingNotMatch(tick_index % pool.tick_spacing != 0)、OrderPhaseSaturated。
IncreaseLimitOrder
向已有挂单追加金额。只能由挂单的 owner 调用。
参数
OpenLimitOrder 类似,无需 nonce 账户;直接传入 limit_order PDA。
前置条件
limit_order.owner == signer。- 挂单仍处于同一队列(
tick.order_phase == limit_order.order_phase)。若队列已开始成交,说明挂单已部分结算——调用方应先调用DecreaseLimitOrder或SettleLimitOrder推进状态。
- 将
amount从所有者 ATA 转入input_vault。 limit_order.total_amount += amount;tick.orders_amount += amount。
DecreaseLimitOrder
减少或完全取消挂单。将未成交余额退还给所有者,同时支付历史部分成交已结算的输出金额。
参数
效果
- 根据开仓以来队列的
unfilled_ratio_x64重新计算挂单的已成交金额。 - 将已成交的输出发送至
output_token_account。 - 将
amount数量的未成交输入退还至input_token_account。 - 相应更新
limit_order。若新的未成交余额为零,程序关闭该账户并将租金退还给owner。
SettleLimitOrder
将已成交的输出代币推送给所有者,不改变挂单的未成交余额。适用于 auto_withdraw keeper 对长期部分成交挂单进行分批支付的场景。
调用方 — 挂单的 owner,或程序的 limit_order_admin(运行自动化 keeper 循环的链下运营热钱包)。keeper 无其他权限——不能将用户资金转移至挂单 owner ATA 以外的地方。
账户
效果
- 使用
(limit_order.unfilled_ratio_x64, tick.unfilled_ratio_x64)计算累计应付输出。 - 将差额转入
output_token_account。 - 更新
limit_order.settled_output。 - 不关闭挂单;剩余输入仍处于挂单状态。
CloseLimitOrder
关闭已完全成交的挂单账户。无论谁签名,租金始终返回给 limit_order.owner。
调用方 — owner 或 limit_order_admin。
前置条件
- 挂单未成交余额为零(即
amount == total_amount已全部成交并结算,或所有者此前已将挂单减至零但未关闭)。
- 关闭
limit_order;租金发送至limit_order.owner。
CreateDynamicFeeConfig(admin)
在 u16 索引下创建可复用的参数集。
参数
常见错误 — 若
decay_period <= filter_period 或任何值为 0 的字段超出范围,返回 InvalidDynamicFeeConfigParams。
UpdateDynamicFeeConfig(admin)
修改已有的 DynamicFeeConfig。已在创建时快照该配置的池不会被追溯更新;只有新创建的引用此配置的池才会使用新值。
参数 — 与 CreateDynamicFeeConfig 相同的五个校准字段(filter_period、decay_period、reduction_factor、dynamic_fee_control、max_volatility_accumulator);index 在创建时固定,此处无需重传。
CollectProtocolFee / CollectFundFee
与 CPMM 的 CollectProtocolFee / CollectFundFee 结构相同。签名者须匹配 AmmConfig.owner / AmmConfig.fund_owner。将池 vault 中累积的协议/基金费用归集到指定接收方,并将对应的 PoolState.protocol_fees_* / fund_fees_* 字段清零。
InitializeReward
为池添加新的奖励流。最多可同时激活 3 个奖励流。
参数
前置条件
- 池当前激活的奖励流少于 3 个。
- 资助方在本指令中将
total_emission = emissions_per_second × (end_time − open_time)数量的奖励代币存入 vault。 - 奖励 mint 须在
operation_state中列入白名单。
SetRewardParams
延长、充值或修改已有奖励流的释放速率。通常由池创建者或 Raydium 多签调用。链上约束:通常可以延长 end_time 或提高释放量,但不能追溯降低。请参阅 operation_state 的所有者列表。
UpdateRewardInfos
纯状态更新——通过计算 emissions_per_second × Δt / liquidity 将 reward_growth_global_x64 结算到当前时间。由所有涉及流动性变更的指令内部调用。同时作为独立指令对外暴露,供 UI、crank 等外部参与方主动触发。
CollectReward
头寸所有者领取应得的奖励代币。
账户
效果
- 结算奖励增长(与费用结算模式相同)。
- 将应付金额转入接收方 ATA,将
reward_amount_owed[i]清零。
状态变更矩阵
延伸阅读
products/clmm/code-demos— 可运行的 TypeScript 示例。products/clmm/fees— 费用与奖励累积详解。reference/error-codes— 完整的 CLMM Anchor 错误码表。
raydium-io/raydium-clmm—programs/amm/src/instructions- Raydium SDK v2 —
@raydium-io/raydium-sdk-v2

