メインコンテンツへスキップ

Documentation Index

Fetch the complete documentation index at: https://docs.raydium.io/llms.txt

Use this file to discover all available pages before exploring further.

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

不変量

CPMM は、2 つのボールトに対して古典的な定価公式の不変量を維持しています: xy=kx \cdot y = k ここで x はボール0の残高の Token-2022 転送手数料、y も同様です。すべてのスワップは、LP に付与されたトレード手数料を考慮した後、k' ≥ k を保つ必要があります。プロトコル、ファンド、およびクリエイター バケットはカウントされません(ボール内に存在しますが、曲線ビューから除外されます。下記の 曲線上の手数料 を参照)。したがって、k は LP が手数料を蓄積するにつれて、時間とともに単調に増加します。 LP シェアは k ではなく、プールの準備金によって価格設定されます: LP price in token0=xlpSupply,LP price in token1=ylpSupply\text{LP price in token0} = \frac{x}{\text{lpSupply}}, \qquad \text{LP price in token1} = \frac{y}{\text{lpSupply}} ΔLP の LP トークンを焼却すると、正確に ΔLP × x / lpSupply の token0 および ΔLP × y / lpSupply の token1 が返されます。デポジットまたはウィズドローアルではいずれも曲線も k も動きません。価格を変更するのはスワップのみです。

スワップパスのフィー モデル

CPMM は、すべてのスワップに対して2 つの独立した手数料レートを適用します:
  • トレード手数料はインプット側で取られ、AmmConfig.trade_fee_rate で請求されます。その後、LP、プロトコル、およびファンド シェアに分割されます。LP シェアはボール内に留まり、k が増加します。プロトコル シェアとファンド シェアはボール アカウンティングから抽出されます)。
  • クリエイター手数料enable_creator_fee == true の場合のみアクティブ)は AmmConfig.creator_fee_rate で請求されます。スワップの方向に応じて、インプット側またはアウトプット側で取られます(products/cpmm/fees を参照)。これはトレード手数料のスライスではなく、独自のバケットです。
以下を定義します:
  • FEE_RATE_DENOMINATOR = 1_000_000
  • trade_fee_rateAmmConfig から、例えば 2500 = 関連するボリューム側の 0.25%
  • creator_fee_rateAmmConfig から、例えば 1000 = 関連するボリューム側の 0.10%
  • protocol_fee_ratefund_fee_rate — ボリュームではなく、トレード手数料の 1/FEE_RATE_DENOMINATOR の単位で表示
クリエイター手数料がインプット側の場合:
total_input_fee = ceil(amount_in * (trade_fee_rate + creator_fee_rate) / FEE_RATE_DENOMINATOR)
creator_fee     = floor(total_input_fee * creator_fee_rate / (trade_fee_rate + creator_fee_rate))
trade_fee       = total_input_fee - creator_fee
amount_in_after_fees = amount_in - total_input_fee
クリエイター手数料がアウトプット側の場合:
trade_fee            = ceil(amount_in * trade_fee_rate / FEE_RATE_DENOMINATOR)
amount_in_after_fees = amount_in - trade_fee
amount_out_curve     = curve_output(amount_in_after_fees, ...)
creator_fee          = ceil(amount_out_curve * creator_fee_rate / FEE_RATE_DENOMINATOR)
amount_out           = amount_out_curve - creator_fee
どちらの場合でも、トレード手数料は同じ方法で分割されます:
protocol_fee   = floor(trade_fee * protocol_fee_rate / FEE_RATE_DENOMINATOR)
fund_fee       = floor(trade_fee * fund_fee_rate     / FEE_RATE_DENOMINATOR)
lp_fee         = trade_fee - protocol_fee - fund_fee     // creator_fee はここでは引かれません
protocol_fee + fund_fee + creator_fee の金額はボール内に保持されていますが、プール状態(protocol_fees_token*fund_fees_token*creator_fees_token*)で個別に追跡されます。定価公式の不変量が k' ≥ k をチェックするとき、ボール残高マイナスすべての 3 つの蓄積されたが未回収の手数料を使用します。したがって、LP は lp_fee のみを取得します。 徴収指示および計算例については、products/cpmm/fees を参照してください。

SwapBaseInput (入力確定)

「ユーザーが入力ミントの正確に amount_in をくれて、アウトプット ミントの少なくとも minimum_amount_out を受け取ります。」 当分の間 Token-2022 を無視すると:
amount_in_after_trade_fee = amount_in - trade_fee
amount_out                = y − (x * y) / (x + amount_in_after_trade_fee)
代数的には: amount_out=yΔxnetx+Δxnet\text{amount\_out} = \frac{y \cdot \Delta x_{\text{net}}}{x + \Delta x_{\text{net}}} ここで Δx_net = amount_in_after_trade_fee です。 プログラムは次にボール アカウンティングを更新して、プロトコル/ファンド/クリエイターが負う trade_fee の部分は「蓄積」バケットに存在します(次の x の曲線に含まれない)。一方、LP シェアは次のスワップに向けて x に参加します。

インプット側の Token-2022

入力ミントに転送手数料の拡張機能がある場合、ミントはユーザー → ボール への転送時に手数料を控除します。したがって、ボールは実際に amount_in − transfer_fee_in(amount_in) を受け取ります。CPMM プログラムは以下を計算します:
amount_actually_received = amount_in − transfer_fee_in(amount_in)
trade_fee                = ceil(amount_actually_received * trade_fee_rate / FEE_RATE_DENOMINATOR)
amount_in_after_trade_fee = amount_actually_received − trade_fee
そして、amount_in_after_trade_fee に対して曲線を実行します。これは重要です。曲線の価格はボール内に到着したネット金額から計算されるため、ユーザーの見出し金額ではありません。

アウトプット側の Token-2022

アウトプット ミントに転送手数料がある場合、プールはボール内の amount_out をユーザーに送信します。その後、ミントは出時に手数料をスキムするため、ユーザーは amount_out − transfer_fee_out(amount_out) を受け取ります。プログラムは通常通り曲線から amount_out を計算しますが、見積もりを表示するときにボールの「ボール送信」数をユーザーの「ユーザー受取」数に変換するのは、インテグレーターの責任です。

スリッページ チェック

amount_out を計算した後:
require(amount_out >= minimum_amount_out, "AmountSpecifiedLessThanMinimum")
アウトプット ミントが転送手数料を請求する場合、SDK はスリッページ定数がボールが送信するものではなく、ユーザーが実際に受け取るもので表示されるように、minimum_amount_out を設定する前に転送手数料を適用します。

SwapBaseOutput (出力確定)

「ユーザーはアウトプット ミントの正確に amount_out を受け取り、入力ミントの最大 maximum_amount_in まで支払う意思があります。」 Δx_net の曲線を反転します: Δxnet=xamount_outyamount_out\Delta x_{\text{net}} = \left\lceil \frac{x \cdot \text{amount\_out}}{y - \text{amount\_out}} \right\rceil 上限は重要です。整数の切り詰めの後、k' ≥ k が保証されます。その後:
// ネット インから グロス インに逆算します。
// 手数料はグロスに適用されるため:
//   net = gross − ceil(gross * rate / D)
//       ≈ gross * (D − rate) / D
// 適切な場所で上限を反転します:
gross_needed = ceil(Δx_net * D / (D − trade_fee_rate))
Token-2022 インプットで、以下でラップします:
gross_needed_before_mint_fee
  = inflate_for_transfer_fee(gross_needed, input_mint)
ユーザーが十分に支払うため、ミントの転送手数料控除後、プールは依然として gross_needed を受け取ります。

スリッページ チェック

require(gross_needed_before_mint_fee <= maximum_amount_in, "AmountSpecifiedExceedsMaximum")

計算例

プール状態、Token-2022 を無視:
  • x = 1_000_000_000_000 (token0 の 1,000,000.000000、6 小数点)
  • y = 2_000_000_000_000 (token1 の 2,000,000.000000、6 小数点)
  • AmmConfig: trade_fee_rate = 2500protocol_fee_rate = 120_000fund_fee_rate = 40_000creator_fee_rate = 0
ユーザー:SwapBaseInputamount_in = 1_000_000_000 (token0 の 1,000.000000)。クリエイター手数料は無効です(enable_creator_fee = false)。
trade_fee                = ceil(1_000_000_000 * 2500 / 1_000_000)       = 2_500_000
  protocol_fee           = floor(2_500_000 * 120_000 / 1_000_000)       = 300_000
  fund_fee               = floor(2_500_000 *  40_000 / 1_000_000)       = 100_000
  lp_fee                 = 2_500_000 − 300_000 − 100_000                 = 2_100_000
creator_fee              = 0                                              // 無効

amount_in_after_trade_fee = 1_000_000_000 − 2_500_000                    = 997_500_000

amount_out = y − (x * y) / (x + Δx_net)
           = 2_000_000_000_000
             − (1_000_000_000_000 * 2_000_000_000_000)
               / (1_000_000_000_000 + 997_500_000)
           ≈ 1_995_015_009

new_vault0_raw   = x + amount_in                                        = 1_001_000_000_000
new_vault1       = y − amount_out                                       ≈ 1_998_004_984_991

// ボール0で受け取った 1_000_000_000 のうち、400_000 は「蓄積手数料」
// (プロトコル+ファンド)、曲線が除外する必要があります:
curve_x          = new_vault0_raw − (protocol_fees_token0 + fund_fees_token0)
                 = 1_001_000_000_000 − 400_000
                 = 1_000_999_600_000

k' = curve_x * new_vault1 ≈ 2.000_002_501_E24  ≥  k = 2.0E24   ✓
同じプールが enable_creator_fee = truecreator_fee_rate = 1000 (0.10%) をインプット側に持っていた場合、プログラムは total_input_fee = ceil(1_000_000_000 * 3500 / 1_000_000) = 3_500_000 を請求し、次に creator_fee = 1_000_000 および trade_fee = 2_500_000 に分割します。上記の例から trade_fee のプロトコル/ファンド/LP 算術は変わりません。クリエイター手数料は独自のバケットで、creator_fees_token0 に蓄積され、プロトコルおよびファンド バケットとともに curve_x から除外されます。 入力ミントに 1% の Token-2022 転送手数料がある場合、ボールは 1_000_000_000 トークンではなく 990_000_000 トークンを受け取り、以降のすべての計算はそのネット金額を使用します。

オブザベーション更新ルール

すべてのスワップで、プログラムは新しいオブザベーションをリング バッファにプッシュするかどうかを評価します:
let since_last = now − observations[head].block_timestamp;
if since_last >= MIN_OBSERVATION_INTERVAL {
    let price0 = (vault1 << 32) / vault0;            // Q32.32-ish
    let price1 = (vault0 << 32) / vault1;
    let head' = (head + 1) % OBSERVATION_NUM;
    observations[head'] = Observation {
        block_timestamp: now,
        cumulative_token0_price_x32:
            observations[head].cumulative_token0_price_x32 + price0 * since_last,
        cumulative_token1_price_x32:
            observations[head].cumulative_token1_price_x32 + price1 * since_last,
    };
    head = head';
}
2 つのプロパティ:
  • 累積価格、スポット価格ではない。 単一のオブザベーションは価格ではありません。時刻 t0 から t1 まで TWAP を取得するには、各端に最も近いオブザベーションを読み、(cumulative(t1) − cumulative(t0)) / (t1 − t0) を計算します。
  • サンプルはレート制限されます。 同じスロット内のバック・ツー・バック スワップは 1 つのオブザベーションを共有できます。スワップの直後にオブザベーションを読むと、1 スロット古い可能性があります。これは正常です。
詳細は products/clmm/accounts を参照してください。

曲線上の手数料

これは微妙な部分で、呼び出す価値があります。曲線の算術はネットボール残高に対して機能します。つまり、生の SPL 残高からプロトコル、ファンド、およびクリエイター手数料を蓄積したもの(3 つすべて独立したバケットです。products/cpmm/fees を参照)を差し引いたもの。具体的なイメージ:
raw_vault_balance   = RPC getTokenAccountBalance が返すもの
accrued_fees        = protocol_fees_token{0,1} + fund_fees_token{0,1} + creator_fees_token{0,1}
curve_balance       = raw_vault_balance − accrued_fees
invariant           = curve_balance0 * curve_balance1
インテグレーターの結果:
  • 生の残高から見積もらないでください。 最初に蓄積手数料フィールドを差し引くか、SwapBaseInput をシミュレーションとして呼び出してその戻り値を取得してください。
  • CollectProtocolFee がトークンをボール外に移動します。 徴収後、raw_vault_balance が低下しますが、curve_balance は変わりません。プールの価格は移動しません。これは意図的です。

精度およびオーバーフロー

  • すべての曲線算術は x * y でオーバーフローを防ぐために u128 中間値を使用します。
  • 除算はゼロに向かって丸められます。ただし、SwapBaseOutputΔx_net は切り上げ、手数料計算では trade_fee で切り上げ、部分分割で切り下げされます。これらの丸め方向は、不変量が整数の切り詰めのため減少しないよう選択されています。
  • 極端なボール比率(数十億:1)を持つプール は、小さなトレードで精度の床にヒットしてください。その場合、プログラムは ZeroTradingTokens を返します。reference/error-codes を参照してください。

次に進むこと

出典: