本頁內容由 AI 自動翻譯,所有內容以英文版本為準。查看英文版 →
products/clmm/accounts(帳戶說明)和 products/clmm/math(數學說明)配套。本頁對參數和帳戶順序具有權威性;具體的位元組佈局來自 IDL。
2026-09 程式升級在 Anchor 1.0.2 / Solana 3.1.10 上重建了 CLMM,新增了管理員指令 CollectExcessLamports,並改變了 CreateAmmConfig 寫入 owner / fund_owner 的內容。沒有使用者面向的指令改變其帳戶、參數或數學。請參閱 2026-09-30 變更日誌條目。
2026-10-22 程式升級將 CreatePermissionedPool 重新命名為 CreateCustomizablePoolV2,使其可由任何人呼叫,並移除了 Permission 帳戶及其兩個管理員指令。請參閱 2026-10-22 變更日誌條目。
指令清單
沒有初始化 tick 陣列指令,也不需要。 Tick 陣列在
OpenPosition* / IncreaseLiquidity* 內由 TickArrayState::get_or_create_tick_array 建立,由 payer 支付。將(可能仍未初始化、系統擁有的)tick 陣列 PDA 作為 tick_array_lower / tick_array_upper 傳遞,程式會在缺失時分配它。OpenLimitOrder 對其單個 tick_array 執行相同操作。CreateAmmConfig、UpdateAmmConfig、UpdatePoolStatus、CreateOperationAccount、UpdateOperationAccount、CloseProtocolPosition)由程式的硬編碼 admin 公鑰把守。CreateSupportMintAssociated / CloseSupportMintAssociated 接受 admin 公鑰或專用的支援鑄幣所有者金鑰;CollectExcessLamports 接受 admin 公鑰或專用的收集 lamport 錢包。獎勵流管理員指令(TransferRewardOwner、CollectRemainingRewards)由獎勵資金提供者把守,而非程式管理員。
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_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 的 13 個已聲明帳戶。dynamic_fee_config 不是已聲明帳戶。
前置條件 — 與 CreatePool 相同。如果 enable_dynamic_fee = false,不需要 remaining_account,傳遞的任何帳戶僅被掃描以查找支援鑄幣記錄。
後置條件
pool_state.fee_on設定為選定的CollectFeeOn變體。- 如果動態費用已啟用:
pool_state.dynamic_fee_info從提供的DynamicFeeConfig初始化(複製五個校準參數;狀態欄位清零)。 - 否則:
pool_state.dynamic_fee_info被清零(= 此池永遠動態費用不活躍)。
fee_on 和動態費用啟用位僅在池建立時設定。沒有就地升級 — 通過舊版 CreatePool 建立的池無法追溯獲得動態費用或單邊費用。新部署應預設為此指令。
CreateCustomizablePoolV2
CreatePool 和 CreateCustomizablePool 都從 ["pool", amm_config, token_mint_0, token_mint_1] 推導池 PDA,因此每個 (config, mint0, mint1) 三元組恰好有一個規範池位址——在相同種子上的第二次 init 會失敗。CreateCustomizablePoolV2 透過將客戶端提供的 seed_index: u16 折疊到池 PDA 種子中來解除此限制,允許同一交易對和費用層級的多個池——每個都在自己的位址上。任何人都可以呼叫它。
池的其他所有方面都與 CreateCustomizablePool 相同:它採用相同的 CreateCustomizableParams 並支援單邊費用和動態費用選擇加入。
在 2026-10-22 升級 之前,此指令稱為
CreatePermissionedPool,需要支付者持有管理員簽發的 Permission PDA。升級後重新命名,移除了 permission 帳戶並刪除了權限機制。判別器隨著名稱改變,因此針對 CreatePermissionedPool 構建的客戶端必須重新構建。remaining_accounts:Token-2022 mint 需要時的支援 mint 登錄 PDA,以及當 enable_dynamic_fee = true 時最後的 DynamicFeeConfig——如同 CreateCustomizablePool。
前置條件
seed_index != 0。seed_index為0保留給舊版池,此處會被拒絕;[0, 0]種子元件是使舊版池位址崩潰為經典四種子形式的原因。- 在推導位址上不存在池。
- 與
CreatePool相同的 mint/允許清單規則。
- 新的
pool_state存在於seed_index推導的位址,其中pool_state.seed_index = seed_index。 - 所有其他後狀態與
CreateCustomizablePool相符(費用模式、可選動態費用)。
OpenPositionV2 / OpenPositionWithToken22Nft
在現有池內建立新位置。
引數
OpenPositionV2 宣告 22 個;OpenPositionWithToken22Nft 宣告 20 個。
OpenPositionV2,按順序:
OpenPositionWithToken22Nft 是相同的清單,但移除了 metadata_account(5)和 metadata_program(19)——它改為透過 NFT mint 上的 Token-2022 中繼資料擴充寫入位置的中繼資料——給出 20 個帳戶。其 position_nft_mint 是裸 Signer,position_nft_account 是 UncheckedAccount。
數學——見 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 陣列 PDA。它們不需要已存在——
get_or_create_tick_array在此指令內以payer的費用分配缺失的。沒有單獨的初始化 tick 陣列指令。 - 使用者在來源 ATA 中至少有
amount_0_max和amount_1_max。
personal_position存在,liquidity設定,fee_growth_inside_last快照。tick_lower和tick_upper的 tick 陣列項目已更新(liquidity_gross += L、liquidity_net ± L、費用成長快照已維護)。- 如果位置在範圍內(
tick_lower ≤ tick_current < tick_upper),pool_state.liquidity += L。 - 位置 NFT mint 記錄
pool_state作為凍結授權。在鑄造單個 NFT 後移除 mint 授權。記錄凍結授權不會改變 NFT 代幣帳戶的狀態。 - NFT 代幣帳戶保持未凍結,除非指令是
OpenPositionV2或OpenPositionWithToken22Nft且任一保管庫 mint 的凍結授權與 CLMM 的受限發行者清單相符。只有該相符的 V2 路徑凍結帳戶。OpenPositionV1 不凍結。
TickInvalidOrder(tick_lower >= tick_upper)、TickAndSpacingNotMatch(端點不是 tick_spacing 的倍數)、InvalidTickIndex(在 [MIN_TICK, MAX_TICK] 外)、MissingTickArrayBitmapExtensionAccount(範圍在內聯位圖外且未附加擴充)、NotApproved(pool_state.status 阻止開啟)、ZeroAmountSpecified。
位置凍結不會新增宣告的指令帳戶或引數。客戶端可以使用現有的 V2 配置開啟這些位置。行為在鏈上從
vault_0_mint 和 vault_1_mint 選擇。IncreaseLiquidityV2
向已開啟的位置新增流動性。
引數
OpenPosition 推導:沒有 rent、沒有 system_program、沒有 associated_token_program 和沒有中繼資料帳戶。
當位置的範圍在內聯位圖外時,將
TickArrayBitmapExtension PDA 前置為 remaining_accounts[0]。
效果
- 從使用者轉移
amount_0_actual/amount_1_actual→ 保管庫。 - 遞增
personal_position.liquidity和pool_state.liquidity(如果在範圍內),以及端點 tick 的liquidity_gross/liquidity_net。 - 收集自上次觸及以來應得的費用和獎勵並將其記入
token_fees_owed_{0,1}/reward_amount_owed。這些僅在DecreaseLiquidity/DecreaseLiquidityV2上支付,不在增加時支付——沒有獨立的收集指令。
DecreaseLiquidityV2
從位置移除流動性。
引數
IncreaseLiquidityV2 的相同形狀或順序:personal_position 和 pool_state 互換,保管庫在 tick 陣列之前,使用者端帳戶命名為 recipient_token_account_*,還有額外的 memo_program。
剩餘帳戶——每個活躍獎勵被收集時三個,按順序
reward_token_vault(W)、recipient_token_account(W)、reward_vault_mint。當位置的範圍在內聯位圖外時,前置 TickArrayBitmapExtension PDA。
這也是唯一收集費用和獎勵的方式。要在不改變位置的情況下收集,使用
liquidity = 0、amount_0_min = 0、amount_1_min = 0 呼叫它。- 給定目前
sqrt_price_x64,計算移除L的(amount_0, amount_1)。 - 結算自上次觸及以來累積的費用/獎勵,與
IncreaseLiquidity相同。 - 從保管庫轉移
amount_0 + fees_owed_0和amount_1 + fees_owed_1給使用者。 - 遞減流動性計數器;如果新的
personal_position.liquidity == 0,位置符合ClosePosition的資格。
amount_0_min 和 amount_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 鑄造無法關閉,供應量保持為零。
AccountLack 而失敗。為每次關閉傳遞池是最簡單的相容策略。
SwapV2
沿著流動性曲線行走;根據 is_base_input 進行精確輸入或精確輸出。
引數
呼叫者傳遞涵蓋預期交換行走的排名 tick 陣列列表;程式使用所需的數量。SDK 透過
PoolUtils.computeAmountOutFormat 或 API 的報價端點計算此列表。
前置條件
pool_state.status允許交換。now >= open_time。sqrt_price_limit_x64在sqrt_price_x64的正確方向上。
TooLittleOutputReceived(精確輸入滑點)、TooMuchInputPaid(精確輸出滑點)、SqrtPriceLimitOverflow、NotEnoughTickArrayAccount、InvalidFirstTickArrayAccount、MissingTickArrayBitmapExtensionAccount、LiquidityInsufficient、NotApproved(在 pool_state.status 上設定交換位元)。CLMM 沒有 ExceededSlippage 變體 — 那是 CPMM 的 — 也沒有 TickArrayNotFound。
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 時,程式首先填充該 tick 處可用的限價單流動性(按
order_phase的 FIFO),然後沿著 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 支援 — 兩個保管庫都必須是經典 SPL Token。任何具有 Token-2022 鑄造的池必須透過 SwapV2 進行交換。聚合器和 SDK 已經為每個 CLMM 腿優先選擇 V2,因此呼叫者不必根據鑄造類型進行分支。
OpenLimitOrder
在特定 tick 處下達賣單。訂單位於每個 tick 的 FIFO 隊列中,並在價格行走經過時填充。
引數
剩餘帳戶 —
[0] tick_array_bitmap_extension,僅在初始化起始索引落在池的內聯位圖之外的 tick 陣列時需要。否則不傳遞任何內容。
帳戶列表變更(2026-07 發佈)。
OpenLimitOrder 現在也採用輸出側帳戶 — output_token_account、output_vault 和 output_vault_mint — 除了輸入側。它們僅用於驗證:如果擁有者的輸入或輸出代幣帳戶被凍結,程式會拒絕訂單。這保證填充實際上可以結算到擁有者的輸出 ATA,這對於允許清單/預設凍結 Token-2022 鑄造(例如許可代幣)很重要,其中帳戶可能尚未解凍。針對舊版單邊帳戶列表建構的客戶端必須新增三個輸出帳戶。input_token_account和output_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_phase和tick.unfilled_ratio_x64。tick.orders_amount += amount(在目前隊列中)。limit_order_nonce.order_nonce += 1。- 發出
OpenLimitOrderEvent。
NotApproved(輸入或輸出代幣帳戶凍結,或池已禁用交換/限價單)、ZeroAmountSpecified(輸入側轉帳費用後 amount == 0)、InvalidLimitOrderAmount(數量會在該 tick 處產生低於 1 個基本單位的輸出,或溢出 u64)、InvalidTickIndex(超出 [MIN_TICK, MAX_TICK],或在所選方向的 tick_current 錯誤一側)、TickAndSpacingNotMatch(tick_index % pool.tick_spacing != 0)、OrderPhaseSaturated。
IncreaseLimitOrder
新增至現有開放訂單。僅可由訂單的 owner 呼叫。
引數
system_program。
前置條件
limit_order.owner == signer。- 訂單仍在同一隊列中(
tick.order_phase == limit_order.order_phase)。如果隊列已開始填充,訂單已部分結算 — 呼叫者應先呼叫DecreaseLimitOrder或SettleLimitOrder以向前推進。
- 從擁有者 ATA 轉帳
amount至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 保管人想要逐步支付長期運行的部分填充時很有用。
呼叫者 — 訂單的 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。
呼叫者 — owner 或 limit_order_admin。
前置條件
- 訂單的未填充餘額為零(要麼
amount == total_amount已填充並結算,要麼所有者之前將訂單減少到零並忘記關閉)。
- 關閉
limit_order;租金發送給limit_order.owner。
CreateDynamicFeeConfig(管理員)
在 u16 索引下建立可重複使用的參數集。
引數
常見錯誤 — 如果
decay_period <= filter_period 或任何 0 值欄位超出範圍,則為 InvalidDynamicFeeConfigParams。
UpdateDynamicFeeConfig(管理員)
修改現有的 DynamicFeeConfig。已在建立時快照配置的資金池不會被追溯更新;只有新建立的參考此配置的資金池才會採用新值。
引數 — 與 CreateDynamicFeeConfig 相同的五個校準欄位(filter_period、decay_period、reduction_factor、dynamic_fee_control、max_volatility_accumulator);index 在建立時固定,此處不重新傳遞。
CollectProtocolFee / CollectFundFee
從資金池的金庫掃除累積的協議/基金費用到收款人,將對應的 PoolState.protocol_fees_* / fund_fees_* 欄位清零。這不是 CPMM 的佈局 — CLMM 沒有 authority 帳戶,收款人欄位名為 recipient_token_account_{0,1} 而不是 recipient_token_{0,1}_account。
引數 — amount_0_requested: u64、amount_1_requested: u64。
2026-09 中的變更:新配置不再從簽署者取得其費用所有者。
create_amm_config 現在將程式的硬編碼 protocol_fee_owner::ID 寫入 owner,將 fund_fee_owner::ID 寫入 fund_owner。之前,它將管理員簽署者的金鑰複製到兩者。在主網上,這些已經是儲存在所有 21 個現有配置上的相同金鑰。地址在 reference/program-addresses。上面接受的簽署者保持不變。管理員仍然可以從任何配置收集,現有的 AmmConfig 帳戶不會被重寫。從帳戶讀取 owner / fund_owner 而不是假設任一值。UpdateAmmConfig 參數 3 和 4 仍然會輪換它們。CollectExcessLamports
管理員掃除 CLMM 控制的帳戶上超過租金豁免最低額的 lamport。它在 2026-09 升級中新增,以回收 SIMD-0437 租金減少 在每個步驟之前建立的帳戶上留下的過度資金。
只有超額部分移動。代幣餘額、帳戶資料、所有者、資金池狀態和價格曲線保持不變。已在最低額的帳戶保持原樣,因此在每次推出步驟後可以安全地重新執行指令。
引數:無。
帳戶
範圍:每次呼叫一個資金池。 CLMM 沒有程式範圍的金庫權限。每個資金池的金庫由其自己的
PoolState 擁有。因此,每個代幣程式來源必須在位置 2 中有 pool_state 作為其權限:token_vault_0、token_vault_1 或該資金池的獎勵金庫之一。如果你傳遞另一個資金池的金庫或使用者的代幣帳戶,代幣程式的所有者檢查失敗,整個指令還原。它不會被跳過。位置 NFT 鑄造無法被掃除,因為當位置開啟時其鑄造權限被撤銷。
每個來源帳戶的處理方式
程式對 remaining_accounts 進行兩次傳遞:首先是每個代幣程式 CPI,然後是直接扣款。交錯這兩者會以執行時的 UnbalancedInstruction 錯誤中止,因此順序在程式中是固定的,你不必自己排序列表。
交易大小是對一次呼叫中有多少來源適配的真實限制。這與
solana-fundamentals/rent-and-reclaimable-rent 中描述的錢包端掃除的約束相同。
常見錯誤:
NotApproved(6000):錯誤的簽署者。LamportsCalculateError(6052):wSOL 往返沒有淨化為零。- 代幣程式所有者不匹配錯誤:代幣來源不由
pool_state擁有。 InsufficientFunds:程式擁有的帳戶持有少於其自身租金最低額。
@raydium-io/raydium-sdk-v2 不提供此管理員路徑的建構器。從 IDL 手動編碼。
InitializeReward
向資金池新增新的獎勵流。最多 3 個流可同時活躍。
引數 — 單個結構體,param: InitializeRewardParam:
param 物件而不是三個位置引數。
帳戶
前置條件
- 資金池上目前少於 3 個流活躍。
- 資金提供者作為此指令的一部分將
total_emission = emissions_per_second × (end_time − open_time)價值的獎勵代幣存入金庫。 - 根據
operation_state的白名單獎勵鑄造。
SetRewardParams
延長、補充或更改現有獎勵流的發放率。通常由資金池建立者或 Raydium 多簽呼叫。約束在鏈上:你通常可以延長 end_time 或增加發放,而不是追溯縮減。檢查 operation_state 的所有者列表。
UpdateRewardInfos
純粹的簿記 — 通過將 emissions_per_second × Δt / liquidity 相乘將 reward_growth_global_x64 結算到當前時間。由每個涉及流動性的指令內部呼叫。作為獨立指令公開,因為外部參與者(UI、crank)有時想要觸發它。
收集獎勵
欠位置的獎勵由DecreaseLiquidity / DecreaseLiquidityV2 支付。要在不改變位置的情況下收集,請使用 liquidity = 0、amount_0_min = 0、amount_1_min = 0 呼叫它。獎勵金庫和收款人帳戶在 remaining_accounts 中按每個活躍獎勵的三個一組進行,順序為 reward_token_vault(W)、recipient_token_account(W)、reward_vault_mint。
CollectRemainingRewards 是另一回事:它讓獎勵資金提供者在流的 end_time 之後掃除從未分配給任何位置的代幣。
狀態變更矩陣
後續步驟
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

