本页内容由 AI 自动翻译,所有内容以英文版本为准。查看英文版 →
环境准备
raydium-sdk-V2-demo/src/clmm 中的一个文件,每个小节旁边都附有对应的 GitHub 链接。初始化方式参考 demo 仓库中的 config.ts.template(源文件)。对于任何非简单的集成,建议设置 disableFeatureCheck: true:
创建 CLMM 池
源文件:src/clmm/createPool.ts
- 在推导之前,按字节顺序对
mint1/mint2排序。 - 计算
sqrt_price_x64 = floor(sqrt(initialPrice × 10^(dB−dA)) × 2^64)。 - 创建
observation和tick_array_bitmap_extension账户。 - 支付由
ammConfig定义的池创建费用。
在指定区间开仓
源文件:src/clmm/createPosition.ts
OpenPosition* 会自行分配缺失的 tick array,费用由付款方承担。
增加现有仓位的流动性
源文件:src/clmm/increaseLiquidity.ts
减少流动性(同时领取手续费)
源文件:src/clmm/decreaseLiquidity.ts 和 src/clmm/closePosition.ts
liquidity = new BN(0) 调用 decreaseLiquidity。该指令的副作用是结算 token_fees_owed_{0,1} 和 reward_amount_owed 并将其转出,这是领取两者的唯一方式。
若要在将流动性和手续费清零后完全关闭仓位,请在最后一次 decreaseLiquidity 调用中传入 ownerInfo: { closePosition: true }。SDK 会追加 ClosePosition 并销毁 NFT。
对于直接使用的 Anchor 客户端,请保持声明的账户不变,并追加池账户:
poolId。CLMM 仅在 positionNftAccount 处于冻结状态时才会读取它,这样同一套客户端代码即可同时兼容旧仓位和新仓位。
领取奖励
源文件:src/clmm/harvestAllRewards.ts
harvestAllRewards 会遍历所传入的每个池中的每一个仓位,将用于结算手续费和奖励的零流动性 DecreaseLiquidity 调用(以及任何 UpdateRewardInfos)打包在一起,必要时拆分到多笔交易中。
Swap
来源:src/clmm/swap.ts
本示例与 src/clmm/swap.ts 保持一致,它使用 swapInternal 进行模拟,而不是 PoolUtils.computeAmountOutFormat。
amountCalculated),以及兑换将涉及的精确账户列表(accounts)。
务必传入模拟返回的 remainingAccounts:数量不足时,兑换会在遍历过程中以 NotEnoughTickArrayAccount 回滚;多余的账户则只会浪费计算资源。
PoolUtils.computeAmountOutFormat 仍然存在,但它需要一个 ComputeClmmPoolInfo(即由 getPoolInfoFromRpc 返回的 computePoolInfo,而非 API 池对象),此外还需要另外两个必填参数 —— tickarrayBitmapExtension 和 blockTimestamp。而且 raydium.clmm.fetchTickArrays 方法并不存在(fetchTickArrays 是一个独立函数;模块级辅助方法是 PoolUtils.fetchMultiplePoolTickArrays,以及 getPoolInfoFromRpc 返回的 tickData / tickArrays)。创建可自定义的 CLMM 池
createCustomizablePool 是在创建池时开放动态手续费和单边手续费开关的入口。它的参数与 createPool 相同,但额外增加了两个参数:
createPool 在默认手续费、无动态手续费的场景下仍然可用。只要需要上述任一功能,就改用 createCustomizablePool。链上账户列表请参阅 products/clmm/instructions。
限价单
限价单会将用户的输入资产挂在单一 tick 上,当兑换跨过该 tick 时按先进先出(FIFO)的顺序成交。产出资金会在结算时转入所有者的 ATA;所有者无需在线即可完成成交。开启限价单
该池不存在按池配置的限价单账户,也没有getClmmLimitOrderConfigs() 辅助方法 —— 订单以 tick 作为索引。
(owner, nonce PDA, order nonce) 推导出 LimitOrderState PDA,递增每个钱包的 LimitOrderNonce,并将订单插入该 tick 对应的 FIFO 队列中。
增加 / 减少已开启的订单
decreaseLimitOrder 只能从订单中未成交的部分移除资产;已成交的部分会被锁定,直到结算完成。如果订单已全部成交,这两条指令都会以 InvalidOrderPhase 回滚。
结算已成交的订单
settleLimitOrder 会根据跟踪器读取订单的 unfilled_ratio_x64,计算已成交的产出,并将其转入所有者的 ATA。所有者可以自行调用;limit_order_admin(链下运维 keeper)也可以代为调用,但产出仍然归所有者。
如需关闭已完全结算的订单以回收租金,可使用 closeLimitOrder(单个)或 closeAllLimitOrder(批量)。如需一次结算多个订单,settleAllLimitOrder 会将尽可能多的 SettleLimitOrder 调用打包进一笔 v0 交易。
列出钱包中挂单的订单(链下)
totalAmount / filledAmount / pendingSettle 区分各个阶段)。如需查询已关闭订单的历史,请使用 /limit-order/history/order/list-by-user?wallet=…(按钱包查询,通过 nextPageId 分页);如需查看某个订单的完整事件日志,请使用 /limit-order/history/event/list-by-pda?pda=…。
Rust CPI 骨架
SwapV2 的剩余账户顺序如下:
常见陷阱
- tick 端点未对齐间距 →
TickAndSpacingNotMatch。请始终通过TickUtil.getPriceAndTick(注意是单数的TickUtil)进行对齐。 SwapV2中提供的 tick array 数量不足 →NotEnoughTickArrayAccount。请从swapInternal(...).accounts中获取该列表。- 全范围仓位未使用 bitmap 扩展 → 扩展 PDA 必须可写。SDK 会自动处理这一点。
- 把
sqrt_price_x64误当作price→ 这里的 2 倍系数错误尤其难排查。如有疑问,请让 SDK 根据可读的价格进行计算。 - 过于频繁地领取奖励 → 每次领取都是一笔零流动性的
DecreaseLiquidity,且各自占用一笔交易。请通过harvestAllRewards批量处理多个仓位,并注意其execute需要传入{ sequentially: true }。 - 自行关闭 NFT 账户 →
ClosePosition会销毁 NFT 并关闭其 ATA。对于 Token-2022 NFT mint,它同样会将其关闭;而经典 SPL Token mint 则会保留,供应量为零,因为该程序无法关闭 mint。请勿单独关闭受支持的账户,否则指令会回滚。 - 在未对齐间距的 tick 上开立限价单 →
TickAndSpacingNotMatch。请始终通过导出的getOrderTick辅助函数进行量化。 - 对已完全成交的订单调用
decreaseLimitOrder→InvalidOrderPhase。请改用settleLimitOrder,然后再调用closeLimitOrder。 - 以为存在
enableDynamicFee标志 → 实际上并不存在。省略dynamicFeeConfig只会创建一个静态费率池,不会有任何提示或报错。如果你需要动态费率,请传入配置账户的PublicKey,可从/main/clmm-dynamic-config中选取。
下一步
sdk-api/typescript-sdk— 完整的 SDK 接口说明。sdk-api/rest-api— 报价与池元数据接口。user-flows/create-clmm-pool— 无需编码的操作流程说明。integration-guides/aggregator— 将 CLMM 纳入聚合路由路径的方法。

