跳轉到主要內容

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 針對每筆交換收取兩筆獨立費率的手續費
  1. 交易手續費 — 按照 AmmConfig.trade_fee_rate 收取,分配至三個目的地:
    • LP 份額 — 留存在金庫中並增加 k。透過燒毀 LP 代幣隱式領取。
    • 協議份額 — 累積至 PoolState.protocol_fees_token*;由 protocol_owner 透過 CollectProtocolFee 領取。
    • 基金份額 — 累積至 PoolState.fund_fees_token*;由 fund_owner 透過 CollectFundFee 領取。
  2. 創作者手續費(選擇性,每個池) — 按照 AmmConfig.creator_fee_rate 收取,獨立於交易手續費,累積至 PoolState.creator_fees_token*,由 pool_state.pool_creator 透過 CollectCreatorFee 領取。僅在池以 enable_creator_fee = true 創建時有效。
創作者手續費不是交易手續費的一部分。這兩筆費率在交換輸入時是相加的,但各自構成獨立的桶 — 協議與基金份額永遠只來自 trade_fee,絕不來自 creator_fee。一個池若 creator_fee_rate = 1000(0.10%)且 trade_fee_rate = 2500(0.25%),在創作者手續費從輸入側收取的交換中,將對輸入收取合計 0.35%,其中創作者保留 0.10%,交易手續費桶獲得 0.25%。 交易手續費費率(trade_fee_rateprotocol_fee_ratefund_fee_rate)與 creator_fee_rate 都位於 AmmConfig。每個池的 enable_creator_fee 旗標與 creator_fee_on 模式(創作者手續費從交易的哪一側收取)位於 PoolState。參見 products/cpmm/accounts

費率與單位

所有費率都是 u64,以 1 / FEE_RATE_DENOMINATOR 為單位計算,其中 FEE_RATE_DENOMINATOR = 1_000_000
  • trade_fee_rate交換量的分數。2500 ⇒ 相關側(輸入或輸出,取決於 creator_fee_on — 參見下方「手續費從交易的哪一側收取」)的 0.25%。
  • creator_fee_rate交換量的分數,獨立於交易手續費收取。1000 ⇒ 相關側的 0.10%。
  • protocol_fee_ratefund_fee_rate交易手續費的分數,而非交換量的分數。120_000 ⇒ 交易手續費的 12%。
主網上 AmmConfig[index=0](「標準」0.25% 池)的預設參數,供參考:
欄位有效百分比
trade_fee_rate2500交換量的 0.25%(交易手續費桶)
protocol_fee_rate120000交易手續費的 12% ≈ 交換量的 0.030%
fund_fee_rate40000交易手續費的 4% ≈ 交換量的 0.010%
creator_fee_rate0(預設)0%(獨立桶)
→ LP 份額有效交換量的 0.210%
因此在對 AmmConfig[0] 進行 $1,000 交換且 enable_creator_fee = false 時:總交易手續費 $2.50,其中 $2.10 留給 LP,$0.30 流向協議,$0.10 流向基金。創作者桶為 0,因為創作者手續費已停用。 如果同一池有 enable_creator_fee = truecreator_fee_rate = 1000(0.10%),使用者額外支付 $1.00 給創作者桶 — 從 creator_fee_on 設定的同一側收取 — 總手續費為 $3.50。交易手續費桶及其協議/基金分配保持不變。 確認目前的主網數值:GET https://api-v3.raydium.io/main/cpmm-config — 費率可由管理員修改,應該重新取得而非硬編碼。

程式碼中的分配

// 改寫自 raydium-cp-swap/programs/cp-swap/src/curve/{calculator,fees}.rs。
// 實際程式碼根據 `is_creator_fee_on_input` 分支;兩個分支都保持
// creator_fee 是獨立費率(永不為 trade_fee 的一部分)的不變式。

const FEE_RATE_DENOMINATOR_VALUE: u64 = 1_000_000;

pub struct FeeBreakdown {
    pub amount_in_after_fees: u64,    // 輸入減去在輸入側收取的所有手續費
    pub amount_out_after_fees: u64,   // 輸出減去在輸出側收取的任何創作者手續費
    pub trade_fee:    u64,            // → 分配至下面 LP / 協議 / 基金桶
    pub protocol_fee: u64,            // 交易手續費的份額
    pub fund_fee:     u64,            // 交易手續費的份額
    pub creator_fee:  u64,            // 獨立桶(輸入或輸出側)
}

fn take_fees_on_input(
    amount_in: u64,
    trade_rate: u64,
    creator_rate: u64,
    protocol_rate: u64,
    fund_rate: u64,
) -> (u64 /* trade_fee */, u64 /* creator_fee */, u64 /* amount_in_after_fees */) {
    // 兩筆費率相加,以便在合計手續費上只進行一次四捨五入,然後按比例分配
    // — 這純粹是一個四捨五入/效率技巧。分配遵守費率確切值:
    // creator_fee/trade_fee == creator_rate/trade_rate。
    let total_fee = ((amount_in as u128) * ((trade_rate + creator_rate) as u128))
        .div_ceil(FEE_RATE_DENOMINATOR_VALUE as u128) as u64;

    let creator_fee = ((total_fee as u128) * (creator_rate as u128)
                       / ((trade_rate + creator_rate) as u128)) as u64;
    let trade_fee   = total_fee - creator_fee;

    (trade_fee, creator_fee, amount_in - total_fee)
}

fn take_creator_fee_on_output(amount_out_swapped: u64, creator_rate: u64) -> (u64, u64) {
    // 當 creator_fee_on 將創作者手續費路由至輸出側時,
    // trade_fee 以單一費率從輸入收取,creator_fee
    // 根據曲線輸出計算。
    let creator_fee = ((amount_out_swapped as u128) * (creator_rate as u128))
        .div_ceil(FEE_RATE_DENOMINATOR_VALUE as u128) as u64;
    (amount_out_swapped - creator_fee, creator_fee)
}

fn split_trade_fee(
    trade_fee: u64,
    protocol_rate: u64,
    fund_rate: u64,
) -> (u64 /* protocol_fee */, u64 /* fund_fee */) {
    let protocol_fee = (trade_fee as u128 * protocol_rate as u128
                        / FEE_RATE_DENOMINATOR_VALUE as u128) as u64;
    let fund_fee     = (trade_fee as u128 * fund_rate as u128
                        / FEE_RATE_DENOMINATOR_VALUE as u128) as u64;
    (protocol_fee, fund_fee)
}
注意事項:
  • 輸入上的總手續費向上四捨五入,以便池不會少收。
  • trade_fee 的子分配(協議、基金)向下四捨五入,使其總和永不超過 trade_fee;剩餘部分是 LP 份額。
  • lp_share = trade_fee − protocol_fee − fund_fee(creator_fee 不會在此扣除,因為它是獨立桶)。
  • 創作者手續費根據 PoolState.creator_fee_on 從輸入或輸出側收取(參見下一章節)。費率無論哪種方式都不變。

手續費從交易的哪一側收取

CPMM 有一個每池的 creator_fee_on 設定(BothToken / OnlyToken0 / OnlyToken1),決定創作者手續費是從給定交換的輸入側還是輸出側收取。執行時輔助函式 is_creator_fee_on_input(direction) 將其折疊為每筆交換的布林值:
creator_fee_on交換 0 → 1交換 1 → 0
BothToken (0)輸入側輸入側
OnlyToken0 (1)輸入側輸出側
OnlyToken1 (2)輸出側輸入側
當創作者手續費在輸入側時,交易手續費與創作者手續費都在曲線執行前從 amount_in 扣除。報價數學:從輸入取出合計 trade_rate + creator_rate 當創作者手續費在輸出側時,只有交易手續費從 amount_in 扣除;曲線產生未收費的輸出,然後從該輸出扣除創作者手續費。報價數學:從輸入取出 trade_rate;從輸出取出 creator_rate 交易手續費本身永遠從輸入側收取(標準 Uniswap-V2 模式)。只有創作者手續費可以在輸出側。

「累積」手續費如何與曲線互動

一個重要的微妙之處:協議、基金與創作者手續費實際上停留在金庫中,直到各自的 Collect* 指令被呼叫。但它們被排除在曲線對金庫餘額的檢視之外。 一次交換後的具體情況:
raw_vault_0_balance = getTokenAccountBalance(vault_0).amount
                    = lp_entitled + protocol_fees_token0
                      + fund_fees_token0 + creator_fees_token0

curve_x = raw_vault_0_balance − (protocol_fees_token0
                                  + fund_fees_token0
                                  + creator_fees_token0)
當強制執行 k' ≥ k 時,程式使用 curve_x(及類似的 curve_y)。這就是非 LP 手續費到達其目的地而不增加 LP 池份額的方式。 您應該圍繞此設計的後果:
  • 從原始餘額報價是錯誤的。 如果您基於 getTokenAccountBalance 建立報價工具,您會持續高估池將履行的價格。請務必扣除累積手續費或透過 SwapBaseInput / API 模擬。
  • CollectProtocolFee 不會改變價格。 它將代幣移出金庫protocol_fees_token* 計數器清零,所以 curve_xcurve_y 不變。
  • LP 手續費不累積至計數器。 它們隱含在金庫餘額中。LP 對累積 LP 手續費的權利透過燒毀 LP 代幣(即透過 Withdraw)行使 — 沒有 CollectLpFee

與 Token-2022 轉帳手續費的互動

Token-2022 轉帳手續費由鑄幣廠應用,不是由 CPMM 應用。它們作用於每筆代幣轉帳 — 交換、存入、提取與 Collect* 領取。CPMM 的交易手續費數學是根據實際到達金庫的金額計算,即淨扣除輸入鑄幣廠的轉帳手續費(如有)。 因此在最壞的情況下,使用者在輸入精確交換上支付三筆不同的稅:
  1. 輸入鑄幣廠在 amount_in 上的轉帳手續費(流向鑄幣廠的手續費權限持有者)。
  2. 池在其餘額上的 trade_fee(按上述分配)。
  3. 輸出鑄幣廠在 amount_out 上的轉帳手續費(流向鑄幣廠的手續費權限持有者)。
SDK 的報價工具考慮所有三筆,所以 minimum_amount_out 以使用者實際收到的金額計算。如果您正在編寫自己的報價工具,請鏡像該行為,否則您的滑點檢查將系統性地過於寬鬆。 詳見 algorithms/token-2022-transfer-fees

創作者手續費

創作者手續費是可選的,且每池一次。費率位於 AmmConfig.creator_fee_rate啟用旗標側別creator_fee_on)位於 PoolState
  • 在池創建時啟用。 Initialize 預設設定 enable_creator_fee = false;透過 InitializeWithPermission 創建的池(由 LaunchLab 畢業與其他有門檻的路徑使用)可以傳遞 enable_creator_fee = true 並選擇 creator_fee_on
  • 費率與費率層級共享。 費率本身是 AmmConfig.creator_fee_rate,在與該設定綁定的每個池中都是相同值。每個池隨後決定是否收取它(enable_creator_fee)以及從交換的哪一側收取它(creator_fee_on)。當 enable_creator_fee = false 時,無論設定值如何,池的有效創作者手續費率都是零(參見來源中的 PoolState::adjust_creator_fee_rate)。
  • 獨立於交易手續費。 創作者手續費絕不會減少 LP / 協議 / 基金份額 — 它是自己的費率,單獨應用,累積在自己的計數器中。
  • 透過 CollectCreatorFee 領取,由 PoolState.pool_creator 簽署。
  • 創建後無法重新啟用或重新路由。enable_creator_fee = false 初始化的池將永不收取創作者手續費;以特定 creator_fee_on 初始化的池無法切換側別。
創作者手續費是 Raydium 「燒毀與賺取」模式背後的機制:LP 代幣在 LP Lock 程式下被鎖定,所以創作者無法提取流動性,但可以無限期地領取 CollectCreatorFee

收集操作流程

簽署者指令清零的來源計數器典型頻率
amm_config.protocol_ownerCollectProtocolFeeprotocol_fees_token{0,1}每週或程式化
amm_config.fund_ownerCollectFundFeefund_fees_token{0,1}每週或程式化
pool_state.pool_creatorCollectCreatorFeecreator_fees_token{0,1}任何時間
協議與基金擁有者是主網上的 Raydium 多簽;參見 security/admin-and-multisig。創作者簽署者是執行 Initialize 的帳戶。

更改費率層級

費率可由管理員透過 UpdateAmmConfig 更改(參見 products/cpmm/instructions)。變更在下筆交換時對與該 AmmConfig 綁定的每個池生效 — 沒有遷移,因為池在每筆交換時載入設定。 管理員無法做的事:
  • 將池從一個 AmmConfig 移動至另一個。
  • 回溯重新定價已累積的手續費。
  • 在沒有 protocol_owner / fund_owner 簽署者的情況下收集手續費。

從執行中的池讀取手續費

// 鏈下:每個桶中目前累積的手續費
const pool = await connection.getAccountInfo(poolStatePda);
const decoded = PoolState.decode(pool.data);
console.log(
  "Protocol accrued:",
  decoded.protocolFeesToken0.toString(),
  decoded.protocolFeesToken1.toString(),
);
console.log(
  "Fund accrued:",
  decoded.fundFeesToken0.toString(),
  decoded.fundFeesToken1.toString(),
);
console.log(
  "Creator accrued:",
  decoded.creatorFeesToken0.toString(),
  decoded.creatorFeesToken1.toString(),
);

// 鏈下:今日有效費率。
// trade_fee_rate、creator_fee_rate 是交換量的分數(分母 1e6)。
// protocol_fee_rate、fund_fee_rate 是*交易手續費*的分數(相同分母)。
const config = await fetch("https://api-v3.raydium.io/main/cpmm-config")
  .then((r) => r.json());
const tier = config.data.find((t) => t.index === decoded.ammConfigIndex);

const tradeFeeOfVolume   = tier.tradeFeeRate / 1_000_000;
const protocolOfTradeFee = tier.protocolFeeRate / 1_000_000;
const fundOfTradeFee     = tier.fundFeeRate / 1_000_000;
const lpOfTradeFee       = 1 - protocolOfTradeFee - fundOfTradeFee;

console.log("LP fee effective:",       (tradeFeeOfVolume * lpOfTradeFee * 100).toFixed(4), "%");
console.log("Protocol fee effective:", (tradeFeeOfVolume * protocolOfTradeFee * 100).toFixed(4), "%");
console.log("Fund fee effective:",     (tradeFeeOfVolume * fundOfTradeFee * 100).toFixed(4), "%");
console.log(
  "Creator fee effective:",
  decoded.enableCreatorFee
    ? ((tier.creatorFeeRate / 1_000_000) * 100).toFixed(4) + " %"
    : "0 % (disabled on this pool)",
);

與 CLMM 和 AMM v4 的比較

參見 reference/fee-comparison 以取得並列矩陣。摘要:
  • AMM v4 使用固定 0.25% 交易手續費,具有不同的 LP/協議分配,無基金手續費。
  • CLMM 手續費按每個刻度間距層級計算,按每個位置累積(不是按池),並透過 DecreaseLiquidityCollectFees 領取。

接下來去哪裡

來源: