本页内容由 AI 自动翻译,所有内容以英文版本为准。查看英文版 →
不变量
CPMM 在其两个金库上维护经典的恒定乘积不变量: 其中x 是 vault0 的余额在收到任何 Token-2022 转账费后,y 亦然。每次交换必须在计入分配给 LP 的交易费后保持 k' ≥ k(协议、基金和创建者桶不计入 k — 它们位于金库中但从曲线视图中排除,见下文曲线上的费用)。因此 k 随着时间单调增长,因为 LP 累积费用。
LP 份额由池的储备而非 k 定价:
销毁 ΔLP 个 LP 代币会返回恰好 ΔLP × x / lpSupply 的 token0 和 ΔLP × y / lpSupply 的 token1。存款或提款时曲线和 k 都不会移动 — 只有交换改变价格。
交换路径上的费用模型
CPMM 在每次交换上应用两个独立费率的费用:- 交易费在输入端收取,费率为
AmmConfig.trade_fee_rate。然后分为 LP、协议和基金份额(LP 份额留在金库中并增长k;协议和基金份额从金库会计中提取)。 - 创建者费(仅当
enable_creator_fee == true时激活)按AmmConfig.creator_fee_rate收取。它在输入端或输出端收取,取决于PoolState.creator_fee_on和交换方向(见products/cpmm/fees)。它是自己的桶 — 永远不是交易费的一部分。
创建者费的协议份额不会出现在本页的任何地方,这是有意的。 它在
CollectCreatorFee 在曲线已排除的两个计数器之间移动累积费用时应用 — 不是在交换期间。交换数学、报价和 k 无论有无它都相同。见products/cpmm/fees。FEE_RATE_DENOMINATOR = 1_000_000trade_fee_rate— 来自AmmConfig,例如2500= 相关交易量端的 0.25%creator_fee_rate— 来自AmmConfig,例如1000= 相关交易量端的 0.10%protocol_fee_rate、fund_fee_rate— 以1/FEE_RATE_DENOMINATOR的交易费单位计,不是交易量
protocol_fee + fund_fee + creator_fee 的金额保存在金库中但在池状态上单独跟踪(protocol_fees_token*、fund_fees_token*、creator_fees_token*)。当恒定乘积不变量检查 k' ≥ k 时,它使用金库余额减去所有三个累积但未清扫的费用 — 所以 LP 只获得 lp_fee。
见products/cpmm/fees了解收集指令和详细的数值示例。
SwapBaseInput(输入精确)
“用户给我们恰好amount_in 的输入代币,并接收至少 minimum_amount_out 的输出代币。”
暂时忽略 Token-2022:
Δx_net = amount_in_after_trade_fee。
程序然后更新金库会计,使得 trade_fee 中欠协议/基金/创建者的部分位于”累积”桶中(不包括在曲线的下一个 x 中),而 LP 份额加入 x 用于下一次交换。
输入端的 Token-2022
如果输入代币有转账费扩展,代币在从用户 → 金库的转账时扣除其费用。所以金库实际接收amount_in − transfer_fee_in(amount_in)。CPMM 程序因此计算:
amount_in_after_trade_fee 运行曲线。这很重要,因为曲线价格是根据实际进入金库的净金额计算的,而不是根据用户的标题金额。
输出端的 Token-2022
如果输出代币有转账费,池从其金库向用户发送amount_out。代币然后会在途中扣除其费用,所以用户接收 amount_out − transfer_fee_out(amount_out)。程序照常从曲线计算 amount_out,但集成商有责任在显示报价时将池的”金库发送”数字转换为”用户接收”数字。
滑点检查
计算amount_out 后:
minimum_amount_out 之前应用转账费,所以滑点常数以用户实际接收的金额计,而不是金库发送的金额。
SwapBaseOutput(输出精确)
“用户将接收恰好amount_out 的输出代币,并愿意支付最多 maximum_amount_in 的输入代币。”
反演曲线求 Δx_net:
向上取整很重要 — 它保证整数截断后 k' ≥ k。然后:
gross_needed。
滑点检查
详细示例
池状态,忽略 Token-2022:x = 1_000_000_000_000(1,000,000.000000 的 token0,6 位小数)y = 2_000_000_000_000(2,000,000.000000 的 token1,6 位小数)AmmConfig:trade_fee_rate = 2500、protocol_fee_rate = 120_000、fund_fee_rate = 40_000、creator_fee_rate = 0
SwapBaseInput,amount_in = 1_000_000_000(1,000.000000 的 token0)。创建者费被禁用(enable_creator_fee = false)。
enable_creator_fee = true,creator_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 转账费,金库接收 990_000_000 个代币而不是 1_000_000_000,所有后续计算都使用该净金额。
观察更新规则
在每次交换时,程序评估是否应将新观察推入环形缓冲区:- 累积价格,不是现货价格。 单个观察不是价格。要从时间
t0到t1获得 TWAP,读取最接近每一端的观察并计算(cumulative(t1) − cumulative(t0)) / (t1 − t0)。 - 样本受速率限制。 同一槽中的连续交换可能共享一个观察。在交换后立即读取观察因此可能看起来过时一个槽 — 这是正常的。
products/clmm/accounts。
曲线上的费用
这是微妙的部分,值得特别说明。曲线算术针对净金库余额工作 — 即原始 SPL 余额减去累积的协议、基金和创建者费(所有三个都是独立的桶 — 见products/cpmm/fees)。一个具体的图景:
- 不要从原始余额报价。 先减去累积费用字段,或调用
SwapBaseInput作为模拟并取其返回值。 CollectProtocolFee将代币移出金库。 收集后,raw_vault_balance下降但curve_balance不变;池的价格不会移动。这是有意的。
精度和溢出
- 所有曲线算术使用
u128中间值以防止x * y上的溢出。 - 除法向零舍入,除了
SwapBaseOutput的Δx_net向上舍入,以及费用计算向上舍入trade_fee并向下舍入子分割。这些舍入方向被选择以确保不变量由于整数截断而永远不会减少。 - 具有极端金库比率(数十亿:1)的池可能在小交易上遇到精度下限;程序在这种情况下返回
ZeroTradingTokens。见reference/error-codes。
接下来去哪里
products/cpmm/fees— 完整的费用等级和收集语义。products/cpmm/instructions— 调用此数学的指令。algorithms/constant-product—x · y = k的推导和边界情况,在 AMM v4 和 CPMM 中共享。

