Skip to main content
本頁內容由 AI 自動翻譯,所有內容以英文版本為準。查看英文版 →
本頁說明每個帳戶的結構與用途。種子(Seeds)為標準定義,詳見 reference/program-addresses。CLMM 流動性池比 CPMM 流動性池需要更多帳戶,因為流動性是稀疏分佈於 tick 範圍內;理解這種稀疏性是本頁的核心主題。

帳戶總覽

一個運行中的 CLMM 流動性池由以下幾類帳戶組成。除了兩個 mint 及其 vault 之外,所有帳戶皆由 CLMM 程式擁有。

PoolState

流動性池的即時狀態,在每次 swap 和每次倉位變更時都會被讀取。
你實際上會用到的欄位:
  • sqrt_price_x64tick_current 代表流動性池的價格狀態。每次 swap 時會同步更新。tick_currentlog_{1.0001}(price) 的下取整值。
  • liquidity有效流動性——即所有範圍包含 tick_current 的倉位其 L 值的總和。每當 swap 穿越一個 tick,或每當倉位被開啟、關閉或調整大小時,此值都會改變。
  • fee_growth_global_{0,1}_x64 是整個流動性池歷史中每單位流動性累積獲得的費用。倉位透過讀取此值來計算應得的費用。
  • tick_spacing 在初始化時鎖定至 AmmConfig,之後不會改變。它決定哪些 tick 索引可作為倉位的端點。
  • tick_array_bitmap 是一個內聯 bitmap,覆蓋現價附近常用的 tick 範圍。對於倉位延伸至遠端的流動性池,溢出追蹤則存放於獨立的 TickArrayBitmapExtension 帳戶中。
  • fee_on 在流動性池創建時固定。0(FromInput)重現經典 Uniswap-V3 的行為;12 會將 swap 費用路由至訂單簿的單一方向——詳見 products/clmm/fees 中的取捨說明。
  • dynamic_fee_info 儲存動態費用附加費的波動率狀態。啟用後,每次 swap 都會在 AmmConfig.trade_fee_rate 之上重新計算 dynamic_fee_component。結構說明詳見下方的 DynamicFeeInfo;未啟用動態費用的流動性池此結構全部為零。

AmmConfig

一組典型的 CLMM 費率層級(請對照 GET https://api-v3.raydium.io/main/clmm-config 確認): protocol_fee_ratefund_fee_rate 均為交易費用的分成比例,慣例與 CPMM 相同。詳見 products/clmm/fees

TickArrayState

CLMM 不會為每個 tick 儲存單獨的記錄,否則將產生數十億個帳戶。取而代之的是,它將 TICK_ARRAY_SIZE 個相鄰 tick(通常為 60 或 88,依程式版本而定)分組至一個 TickArrayState 中,並在首次使用時才惰性創建。
四個限價單欄位在從未用於限價單的 tick 上全部為零。當訂單在某個 tick 上開啟時,程式會以一系列批次(cohort)來追蹤它們:
  • order_phase 是批次 ID。每當一個批次從「全部未成交」轉換為「部分成交」時遞增。
  • orders_amount 是當前(最新)批次的輸入代幣總量。
  • part_filled_orders_remaining 追蹤正在被持續 swap 填充的前一個批次。
  • unfilled_ratio_x64 是附在批次上的 Q64.64 乘數:當某次 swap 填充了該批次的 X%,此比率便乘以 (1 − X)。每個開啟的訂單在開倉時會儲存自己的 (order_phase, unfilled_ratio_x64) 快照,因此結算數學只需比較快照即可。
規則:
  • 倉位端點 tick t 必須滿足 t % tick_spacing == 0。程式會拒絕不符合間距的倉位。
  • tick 所屬的陣列位於 floor(t / (TICK_ARRAY_SIZE * tick_spacing)) * (TICK_ARRAY_SIZE * tick_spacing)
  • tick 陣列採惰性初始化:第一個接觸到未初始化陣列的倉位或 swap 會創建它,並支付租金。
  • 程式永遠不會關閉 tick 陣列。一旦分配後,即使其中所有 tick 的 liquidity_gross 都歸零,它也會在流動性池的整個生命週期內持續存在。後續的倉位和 swap 可免費重複使用現有帳戶,無需額外租金。不存在由 ClosePosition 驅動的 tick 陣列清理路徑。

TickArrayBitmapExtension

PoolState.tick_array_bitmap(內聯)涵蓋「接近現價」的範圍——±1,024 個 tick 陣列。超出該範圍(對應極端 tick 值)時,程式會維護一個擴充帳戶:
如果你的倉位範圍屬於「正常」範圍,完全不需要考慮擴充帳戶。全範圍倉位(例如 (MIN_TICK, MAX_TICK))才需要用到它;SDK 會自動為你解析。

倉位

一個 CLMM 倉位是由三個帳戶加一個 mint 組成的組合

倉位 NFT mint

一個供給量為 1 的 SPL Token mint。mint 的地址是確定性 PDA;持有者錢包中的倉位 NFT 只是一個持有該單一代幣的 ATA。轉移 NFT 是倉位易手的方式——程式將授權綁定至 NFT ATA 餘額的當前持有者,而非狀態中儲存的某個 Pubkey。

PersonalPositionState

每個開放倉位一個,以 NFT mint 為鍵。

ProtocolPositionState(已棄用)

舊版 CLMM 在 ProtocolPositionState PDA 中儲存每個 (pool, tick_lower, tick_upper) 組合的彙總帳務。新版已不再創建或讀取此帳戶。 為了 ABI 相容性,該欄位仍以 UncheckedAccount 的形式出現在 OpenPositionIncreaseLiquidityDecreaseLiquidity 的帳戶清單中,但程式不會對其進行寫入。鏈上現有的帳戶屬於殘留資料;管理員可呼叫 CloseProtocolPosition 回收其租金。彙總範圍帳務現在直接從 TickArrayState 中的兩個端點 tick(liquidity_grossliquidity_net,以及每個 tick 的 fee_growth_outside_* / reward_growths_outside_x64)推導而來。費用成長內部公式 fee_growth_inside = global − outside_lower − outside_upper 在不需要彙總倉位帳戶的情況下依然有效。

Observation

CLMM 的觀察緩衝區儲存的是累積 tick,而非累積價格。外部使用者透過 (tick_cumulative[t1] − tick_cumulative[t0]) / (t1 − t0) 計算某段時間內的幾何平均價格,再以 price = 1.0001 ** tick 轉換。詳見 algorithms/clmm-math

DynamicFeeConfigDynamicFeeInfo

動態費用參數分存於兩個位置。可重複使用的範本——DynamicFeeConfig——由管理員管理,並在選擇加入的流動性池之間共用。每個池的運行時狀態——DynamicFeeInfo——則嵌入於 PoolState 中,並在每次 swap 時更新。

DynamicFeeConfig

PDA 種子:["dynamic_fee_config", index.to_be_bytes()]。透過 create_dynamic_fee_config(需管理員權限)創建,並透過 update_dynamic_fee_config 修改。以 enable_dynamic_fee = true 創建的流動性池,會在創建時將設定的五個校準參數(filter_perioddecay_periodreduction_factordynamic_fee_controlmax_volatility_accumulator)快照至自身的 DynamicFeeInfo 中;之後對 DynamicFeeConfig 的編輯不會追溯影響現有流動性池。

DynamicFeeInfo(嵌入於 PoolState

後四個欄位為狀態;前五個欄位為從 DynamicFeeConfig 複製的校準參數。費用計算公式與衰減規則詳見 products/clmm/mathproducts/clmm/fees 公式使用的常數:

LimitOrderState

每個開放限價單一個帳戶。
生命週期:
  1. 開啟——用戶呼叫 open_limit_order,存入 total_amount 的輸入代幣,訂單被綁定至某個 TickState 批次。
  2. (可選)增加 / 減少——increase_limit_order 增加 total_amountdecrease_limit_order 退還未成交代幣(以及截至當時的已結算輸出)。
  3. 結算——當批次完全或部分成交後,訂單擁有者運維守護者呼叫 settle_limit_order,將輸出代幣推送至擁有者的 ATA。
  4. 關閉——一旦 unfilled_amount == 0,該帳戶即可關閉。租金始終退還至 owner
PDA 種子:[owner.as_ref(), limit_order_nonce.key().as_ref(), limit_order_nonce.order_nonce.to_be_bytes().as_ref()]。因此,訂單 PDA 對於每個 (owner, nonce_index, order_nonce) 組合都是唯一的。

LimitOrderNonce

每個 (錢包, nonce_index) 的計數器,讓單一用戶能並行執行多條限價單管線而不發生 PDA 碰撞。
PDA 種子:[user_wallet.as_ref(), &[nonce_index]]。大多數客戶端使用 nonce_index = 0,並讓 order_nonce 承載基數。

推導關鍵帳戶

確切的種子字串應始終對照鏈上 IDL 及 reference/program-addresses 進行雙重確認。

生命週期快速參考

TickArrayState 帳戶永遠不會被程式關閉——它們在流動性池的整個生命週期內持續存在。一旦 tick 陣列被初始化,即使其中所有 tick 的 liquidity_gross 均歸零,它仍會保留在鏈上。重複使用現有 tick 陣列是免費的;只有第一個接觸從未初始化陣列的倉位才需要支付租金。

各主題閱讀指引

來源: