本页内容由 AI 自动翻译,所有内容以英文版本为准。查看英文版 →
本页介绍每个账户的结构与作用。种子(Seeds)均为规范形式,详见
reference/program-addresses。CLMM 流动池比 CPMM 流动池涉及更多账户,原因在于流动性以稀疏方式分布在 tick 范围内——理解这种稀疏性是本页的核心内容。账户总览
一个运行中的 CLMM 流动池由以下几类账户描述。除两个代币 mint 及其 vault 外,所有账户均归属于 CLMM 程序。PoolState
流动池的实时状态,每次 swap 和仓位变更时均会读取。
sqrt_price_x64和tick_current表示流动池的价格状态,每次 swap 时同步更新。tick_current是log_{1.0001}(price)的向下取整值。liquidity是活跃流动性——即所有覆盖tick_current的仓位的L值之和。每当 swap 穿越一个 tick,或者某个仓位被开仓/关仓/调整大小时,该值都会变化。fee_growth_global_{0,1}_x64是流动池历史中每单位流动性累计赚取的手续费。仓位通过读取该值来计算应得的手续费。tick_spacing在初始化时从AmmConfig继承,此后不可更改。它决定了哪些 tick 索引可以作为仓位端点。tick_array_bitmap是一个内联位图,覆盖当前价格附近的常用 tick 范围。对于仓位范围延伸较远的流动池,溢出追踪由独立的TickArrayBitmapExtension账户负责。fee_on在流动池创建时固定。0(FromInput)复现经典 Uniswap-V3 的行为;1和2将 swap 手续费路由到账本的单一一侧——权衡取舍详见products/clmm/fees。dynamic_fee_info保存动态手续费附加费的波动率状态。启用后,每次 swap 都会在AmmConfig.trade_fee_rate基础上重新计算dynamic_fee_component。布局在下方DynamicFeeInfo中说明;未启用动态手续费的流动池该结构体全部为零。
AmmConfig
GET https://api-v3.raydium.io/main/clmm-config 确认):
protocol_fee_rate 和 fund_fee_rate 均为交易手续费的分成比例,与 CPMM 的约定相同。详见 products/clmm/fees。
TickArrayState
CLMM 不会为每个 tick 单独存储一条记录——那将产生数十亿个账户。取而代之的是,将 TICK_ARRAY_SIZE 个相邻 tick(根据程序版本,通常为 60 或 88 个)组合成一个 TickArrayState,在首次使用时惰性创建。
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 组成:Position NFT mint
供应量为 1 的 SPL Token mint。Mint 地址是确定性 PDA,持有者钱包中的 position NFT 只是一个持有该代币的 ATA。转让 NFT 即为转让仓位——程序将授权绑定到 NFT ATA 余额的当前持有者,而非状态中存储的某个 Pubkey。PersonalPositionState
每个开仓位置对应一个,以 NFT mint 为索引键。
ProtocolPositionState(已弃用)
旧版 CLMM 在一个
ProtocolPositionState PDA 中存储了按 (pool, tick_lower, tick_upper) 维度的聚合记录。新版本不再创建或读取该账户。 为保持 ABI 兼容性,该插槽在 OpenPosition / IncreaseLiquidity / DecreaseLiquidity 的账户列表中仍以 UncheckedAccount 形式出现,但程序不再向其写入。链上现有的该类账户为历史遗留;管理员可调用 CloseProtocolPosition 回收其租金。聚合范围记录现在直接从 TickArrayState 中两个端点 tick 的字段(liquidity_gross、liquidity_net 以及各 tick 的 fee_growth_outside_* / reward_growths_outside_x64)推导得出。手续费增长内部值公式 fee_growth_inside = global − outside_lower − outside_upper 无需聚合仓位账户仍可正常工作。Observation
(tick_cumulative[t1] − tick_cumulative[t0]) / (t1 − t0) 计算区间内的几何平均价格,再代入 price = 1.0001 ** tick 得到结果。详见 algorithms/clmm-math。
DynamicFeeConfig 与 DynamicFeeInfo
动态手续费参数存放在两处:可复用的模板 DynamicFeeConfig 由管理员管理,在选择加入的流动池间共享;每个流动池的运行时状态 DynamicFeeInfo 内嵌于 PoolState,并在每次 swap 时更新。
DynamicFeeConfig
["dynamic_fee_config", index.to_be_bytes()]。通过 create_dynamic_fee_config(需管理员权限)创建,通过 update_dynamic_fee_config 修改。以 enable_dynamic_fee = true 创建的流动池会在创建时将配置的五个校准参数(filter_period、decay_period、reduction_factor、dynamic_fee_control、max_volatility_accumulator)快照到自身的 DynamicFeeInfo 中;后续对 DynamicFeeConfig 的修改不会影响已有的流动池。
DynamicFeeInfo(内嵌于 PoolState)
DynamicFeeConfig 复制的校准参数。手续费计算和衰减规则详见 products/clmm/math 和 products/clmm/fees。
公式中使用的常量:
LimitOrderState
每个未完成的挂单对应一个账户。
- 开仓 — 用户调用
open_limit_order,存入total_amount数量的输入代币,挂单绑定到某个TickState批次。 - (可选)加仓 / 减仓 —
increase_limit_order增加total_amount;decrease_limit_order返还未成交代币(以及截至当时已结算的输出代币)。 - 结算 — 当批次全部或部分成交后,持有者或运营 keeper 调用
settle_limit_order,将输出代币推送到持有者的 ATA。 - 关仓 — 当
unfilled_amount == 0时,账户可关闭。租金始终返还给owner。
[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 冲突。
[user_wallet.as_ref(), &[nonce_index]]。大多数客户端使用 nonce_index = 0,用 order_nonce 来区分不同订单。
派生关键账户
reference/program-addresses 进行核对确认。
生命周期速查表
TickArrayState 账户永不由程序关闭——它们在流动池生命周期内持续存在。一个 tick 数组一旦初始化,即使其中所有 tick 的 liquidity_gross 归零,也会继续留存在链上。复用已有 tick 数组无需额外成本;只有第一个触及从未初始化的数组的仓位才需支付其租金。
各主题阅读指引
- Tick 数学与范围机制:
products/clmm/ticks-and-positions。 - Swap 遍历与手续费增长数学:
products/clmm/math。 - 指令账户列表:
products/clmm/instructions。 - 手续费与奖励累计:
products/clmm/fees。 - 规范程序 ID 与种子:
reference/program-addresses。

