Skip to main content
このページは AI による自動翻訳です。すべての内容は英語版を正とします。英語版を表示 →

ティックが存在する理由

CLMM のリクイディティは価格範囲に集中しています。範囲をオンチェーンで扱いやすくするため、価格は整数のティックに量子化されます。各ティックは前のティックの定数倍です: price(i)=1.0001i\text{price}(i) = 1.0001^{\,i} 1 つのティックは 0.01% の価格変動、つまり約 1 ベーシスポイントに相当します。マッピングは以下の通りです: MIN_TICKMAX_TICK は、sqrt_price_x64 が両端で u128 に収まるように選択されています。すべてのプールは tick_lower >= MIN_TICKtick_upper <= MAX_TICK を強制します。実際には、Web UI は範囲をはるかに狭くクランプして、ユーザーがリクイディティを到達不可能なティックにロックするのを防ぎます。

ティック間隔

プールの AmmConfigティック間隔を固定します。これはポジションがエンドポイントとして使用できる唯一のティックです。tick_spacing = 60 の場合、…, −120, −60, 0, 60, 120, … のティックのみが有効です。エンドポイント 31 でポジションを開こうとすると、InvalidTickIndex で失敗します。 一般的に公開されている間隔: 間隔が粗いほど、初期化するティック配列が少なくなり、広いポジションを開くコストが低くなり、価格境界がぼやけます。ボラティリティの高いペアは通常 120 間隔層に存在し、ステーブルコインは 1 間隔層に存在します。

ティック配列

プールは個別のアカウントにティックごとの状態を保存しません。代わりに、TICK_ARRAY_SIZE 個の隣接するティック(現在の Raydium CLMM では 60)が単一の TickArrayState にパックされます。配列の最初のティックは start_tick_index であり、正確に TICK_ARRAY_SIZE * tick_spacing 個の整数ティック単位をカバーします。 tick_spacing = 60TICK_ARRAY_SIZE = 60 の場合:
  • 各ティック配列は 60 × 60 = 3600 個の整数ティックにまたがります。
  • start_tick_index は 3600 の倍数です:…, -7200, -3600, 0, 3600, 7200, …
tick_spacing = 60 でのポジションエンドポイント t = 2040 は、start_tick_index = 0 のティック配列に存在します。ポジションエンドポイント t = 4200 は、start_tick_index = 3600 の配列に存在します。

配列が作成される時期

ティック配列は遅延的です。その内部のティックを参照する最初のポジションが配列を初期化し、レントを支払います。スワップはティック配列を初期化しません。ビットマップを使用して初期化されていない配列をスキップします。SDK の open-position フローは選択された範囲を検査し、それが接するティック配列のリストを計算し、不足している場合は OpenPosition と同じトランザクション内に init_tick_array 命令を追加します。

ティック配列は閉じられない

ティック配列が初期化されると、プールの存続期間中存続します。プログラムは、initialized_tick_count がゼロに戻った後でも、ティック配列を閉じるパスを公開しません。ティック配列のレント回収はありません。配列に接する最初のポジションが支払ったレントは、そのアカウントに永久にロックされます。これは意図的なトレードオフです。既存のティック配列を再利用することは、その後のすべてのポジションに対して無料であるため、取引量の多いプールは、チャーンに関係なく (pool, start_tick_index) スロットごとに 1 回だけレントコストを支払います。

ビットマップ

「現在のティックの左/右の次の初期化されたティックを見つける」は高速である必要があります。スワップは多くのティックを横切る可能性があります。プールは、ティック 0 の周囲 ±1,024 配列の範囲に対して、1 ビット/ティック配列のビットマップを PoolState にインラインで保存します。その範囲外(フルレンジポジション、エキゾチックなセットアップ)では、TickArrayBitmapExtension がオーバーフローを提供します。 スワップはビットマップをウォークします:lowest_set_bit_above(tick_current_array_index) は、スワップが向かっている側に初期化されたティックを持つ次の配列を提供します。その配列内で、同様のビットスキャンが次の初期化されたティックを特定します。

liquidity_grossliquidity_net

すべての初期化されたティックは 2 つのリクイディティ値を保存します:
  • liquidity_gross — このティックをエンドポイントとして参照するすべてのポジションの L の合計。liquidity_gross がゼロに達すると、ティックは初期化されなくなり、ビットマップから削除できます。
  • liquidity_net — 価格がこのティックを上向きに(ティック空間で左から右に)横切るときのプールレベルの liquidity への符号付き変化。このティックがサイズ L のポジションの下限である場合、+L に寄与します。そのポジションの上限である場合、−L に寄与します。
実例:同じプール上の 2 つのポジション。
  • ポジション A:tick_lower = -120tick_upper = 0、リクイディティ L_A = 100
  • ポジション B:tick_lower = -60tick_upper = 60、リクイディティ L_B = 50
ティックごとの状態: 異なる tick_current 値に対するプールレベルの liquidity
  • tick_current = -180liquidity = 0(任意のポジション前)
  • tick_current = -90liquidity = 100(A のみ内部)
  • tick_current = -30liquidity = 150(A と B 内部)
  • tick_current = 30liquidity = 50(B のみ内部)
  • tick_current = 90liquidity = 0(両方を超えて)
スワップ中のティック横切りごとに、プログラムは liquidity_net(負の可能性あり)を PoolState.liquidity に追加します。これは正確な Uniswap-v3 メカニズムです。

NFT としてのポジション

Raydium CLMM ポジションは NFT です。ポジションを開くと、供給量 1 の新しいミントを呼び出し元のウォレットにミントし、ミントの権限は CLMM プログラムです。プログラムはポジション所有権を、CPI 時にそのミントの ATA で残高を保有するにキーイングします。 結果:
  • ポジションは通常転送可能です。 ウォレットは NFT を転送することでポジションを売却またはエアドロップできます。新しい保有者は CollectRewardsIncreaseLiquidity などを呼び出すことができます。例外は、以下の制限発行者パスの下で凍結されたポジションです。
  • ポジションは CLMM 外でアドレス指定可能です。 マーケットプレイスとウォレットは他の NFT のようにポジションを表示します。SDK はミントメタデータに合理的な name/symbol を設定します。
  • ポジションの PDA はNFT ミントから派生します。 現在誰がそれを保有しているかを知らなくても PersonalPositionState を見つけることができます。

制限発行者ポジション

2026-08 アップグレード後に作成されたすべてのポジション NFT ミントは、その CLMM pool_state を凍結権限として記録します。これはすべての新しいポジションが凍結されることを意味しません。 通常のプールとすべての非マッチングポジションについて、NFT トークンアカウントは凍結されず、転送可能なままです。プール PDA は CLMM プログラム外で署名できず、CLMM は汎用凍結命令を公開しません。 凍結には、これら両方の条件が必要です:
  1. ポジションが OpenPositionV2 または OpenPositionWithToken22Nft を通じて開かれている。
  2. 少なくとも 1 つのプールボルトミントが、プログラムのハードコードされた制限発行者リストからの凍結権限を持っている。
両方の条件が成立する場合のみ、CLMM はミント直後に新しく作成されたポジション NFT アカウントを凍結します。OpenPosition V1 はこのフィルターを適用しません。現在のリストについては reference/program-addresses を参照してください。 凍結されたポジション:
  • その NFT を別のトークンアカウントに転送できません。
  • NFT トークンアカウントの所有者を変更できません。
  • 記録された所有者が署名するときに、リクイディティを増加または減少させ、手数料またはリワードを収集できます。
  • 閉じることはできます。ClosePosition はプール PDA を使用して NFT アカウントを解凍し、NFT を焼却し、同じ命令でポジションアカウントを閉じます。
既存のポジションは遡及的に移行または凍結されません。アップグレード前に作成されたポジション NFT ミントは、以前の凍結権限設定を保持します。
凍結されたポジションを閉じるクライアントは、ポジションの pool_stateClosePosition への最初の残りアカウントとして追加する必要があります。宣言された IDL アカウントリストは変更されていないため、古いクライアントは凍結されたポジションを正常に開くことができますが、後で AccountLack で閉じるのに失敗します。これらのプールをサポートする前に close ビルダーを更新してください。

Token-2022 ポジション

CLMM は OpenPositionV2 を通じて classic SPL Token の下でポジション NFT をミントするか、OpenPositionWithToken22Nft を通じて Token-2022 の下でミントできます。両方の V2 パスはプールボルトミントを検査し、同じ制限発行者凍結ルールを適用します。OpenPosition V1 はレガシー classic-token パスであり、Token-2022 ボルトミントを持つプールにサービスを提供できません。ウォレットとマーケットプレイスの互換性は異なります。Raydium の UI は両方の NFT プログラムを追跡します。

許可範囲ルール

OpenPosition 時にプログラムは以下を強制します:
  1. tick_lower < tick_upper
  2. tick_lower % tick_spacing == 0tick_upper % tick_spacing == 0
  3. MIN_TICK <= tick_lowertick_upper <= MAX_TICK
  4. 呼び出し元が tick_lowertick_upper を含むティック配列を提供している。既に初期化されているか、同じトランザクション内の init_tick_array を通じて。
  5. このポジションが拡張範囲に拡張する場合、ビットマップ拡張アカウント。
いずれかのチェックが失敗すると、命令は InvalidTickIndexNotApproved、または InsufficientLiquidity で失敗します。どの制約かによります。reference/error-codes を参照してください。

「インレンジ」対「アウトオブレンジ」

ポジションは tick_lower <= tick_current < tick_upper のときにインレンジです。インレンジポジションのみが PoolState.liquidity に寄与し、したがってスワップ手数料を獲得します。 アウトオブレンジポジション:
  • その範囲が過ぎた1 つのトークンの 100% を保有します。具体的には、tick_current < tick_lower の場合、ポジションはトークン 1 のみを保有します(価格が離れることで既に「売却」されています)。tick_current >= tick_upper の場合、トークン 0 のみを保有します。
  • スワップ手数料を獲得しません
  • プールのリワードストリームがアウトオブレンジリクイディティに放出する場合、リワードの蓄積を継続します。ただし、Raydium のデフォルト動作は「インレンジのみに放出」で、Uniswap v3 規約と一致します。products/clmm/fees を参照してください。
CLMM ポジションを管理する LP は、価格が移動するにつれてポジションをインレンジに保つことに注意を集中させます。

一般的な統合の落とし穴

  • オフ間隔エンドポイント。 ターゲット価格からティックを計算するコードは、OpenPosition に渡す前に tick_spacing の倍数にスナップする必要があります。SDK ヘルパー(TickUtils.getTickWithPriceAndTickspacing)はこれを行います。自作の数学はしばしば行いません。
  • 不足しているティック配列。 広いポジションを開くには、複数のティック配列を初期化する必要がある場合があります。それらを書き込み可能なアカウントとして渡すのを忘れると失敗します。SDK の openPositionFromBase はリストを返します。
  • スワップ後の古いティック。 tick_current は 1 つのスワップで多くのティックを横切ることができます。UX が 1 つの RPC 呼び出しから「現在のティック」を表示し、その後の別の呼び出しでポジションを開く場合、ライブ価格に対する相対的なポジションは数十ティック離れている可能性があります。署名する直前に再取得してください。
  • 追加メタデータを持つポジション NFT。 Raydium ポジションを認識するウォレットを構築する場合、ハードコードされたメタデータフィールドではなく、ポジション PDA / プログラムデータを使用してください。新しいポジションミントは、作成時にプール PDA をミント権限と凍結権限として使用します。単一の NFT がミントされた後、ミント権限は削除されます。
  • すべてのポジションが転送可能であると仮定する。 転送、マーケットプレイス、エスクロー、または Burn & Earn アクションを表示する前に、NFT トークンアカウントの isFrozen 状態を読み取ってください。

次に進む場所

  • 数学 — スワップステップスルーとティック境界が参加する手数料成長導出。
  • アカウントTickArrayStatePositionState レイアウト。
  • 手数料とリワード — インレンジネスが手数料蓄積をゲートする方法。
  • algorithms/clmm-math — 集中リクイディティ公式の共有導出。
ソース: