返回博客列表

DEX Swap 计算:正向推导与反向推导

web3
DEXAMMSwapUniswap

⚠️ 免责声明:本文内容仅供技术学习与研究参考,不构成任何投资建议。请读者独立思考,谨慎决策。

DEX Swap 计算:正向推导与反向推导

在去中心化交易所(DEX)中,自动做市商(AMM)使用数学公式自动计算交易价格。本文将用通俗易懂的方式,讲解两个核心问题:

  1. 正向推导:卖出一定数量的代币 A,能买入多少代币 B?
  2. 反向推导:想买入指定数量的代币 B,需要卖出多少代币 A?

核心公式回顾

Uniswap 等 AMM 采用恒定乘积公式

xy=kx \cdot y = k

其中:

  • xx:池中代币 A 的储备量
  • yy:池中代币 B 的储备量
  • kk:恒定乘积(流动性常数)

交易前后,kk 值保持不变(暂不考虑手续费)。


一、正向推导:已知卖出量,求买入量

场景描述

假设当前池中有:

  • 代币 A:1000 个
  • 代币 B:2000 个

你想卖出 100 个代币 A,能买入多少代币 B?

计算步骤

第一步:确定初始状态

x0=1000,y0=2000,k=x0y0=2000000x_0 = 1000, \quad y_0 = 2000, \quad k = x_0 \cdot y_0 = 2000000

第二步:计算交易后的代币 A 数量

你卖出 100 个代币 A 进入池子:

x1=x0+Δx=1000+100=1100x_1 = x_0 + \Delta x = 1000 + 100 = 1100

第三步:根据恒定乘积求交易后的代币 B 数量

y1=kx1=200000011001818.18y_1 = \frac{k}{x_1} = \frac{2000000}{1100} \approx 1818.18

第四步:计算获得的代币 B 数量

Δy=y0y1=20001818.18=181.82\Delta y = y_0 - y_1 = 2000 - 1818.18 = 181.82

通用公式

Δy=y0x0y0x0+Δx=y0Δxx0+Δx\Delta y = y_0 - \frac{x_0 \cdot y_0}{x_0 + \Delta x} = \frac{y_0 \cdot \Delta x}{x_0 + \Delta x}

代码示例

// 正向推导:计算卖出 dx 个代币A能获得多少代币B
function getAmountOut(reserveA, reserveB, amountIn) {
  return (reserveB * amountIn) / (reserveA + amountIn);
}

// 示例
const amountOut = getAmountOut(1000, 2000, 100);
console.log(amountOut); // 181.818181...

二、反向推导:已知目标买入量,求所需卖出量

场景描述

同样的池子:

  • 代币 A:1000 个
  • 代币 B:2000 个

你想精确买入 200 个代币 B,需要卖出多少代币 A?

计算步骤

第一步:确定初始状态

x0=1000,y0=2000,k=2000000x_0 = 1000, \quad y_0 = 2000, \quad k = 2000000

第二步:计算交易后的代币 B 数量

你想获得 200 个代币 B,所以池子里的代币 B 会减少:

y1=y0Δy=2000200=1800y_1 = y_0 - \Delta y = 2000 - 200 = 1800

第三步:根据恒定乘积求交易后的代币 A 数量

x1=ky1=200000018001111.11x_1 = \frac{k}{y_1} = \frac{2000000}{1800} \approx 1111.11

第四步:计算需要卖出的代币 A 数量

Δx=x1x0=1111.111000=111.11\Delta x = x_1 - x_0 = 1111.11 - 1000 = 111.11

所以需要卖出约 111.11 个代币 A。

通用公式

Δx=x0Δyy0Δy\Delta x = \frac{x_0 \cdot \Delta y}{y_0 - \Delta y}

代码示例

// 反向推导:计算买入 dy 个代币B需要卖出多少代币A
function getAmountIn(reserveA, reserveB, amountOut) {
  return (reserveA * amountOut) / (reserveB - amountOut);
}

// 示例
const amountIn = getAmountIn(1000, 2000, 200);
console.log(amountIn); // 111.111111...

三、考虑手续费的情况

实际交易中,DEX 会收取手续费(如 Uniswap V2 收取 0.3%)。手续费从输入代币中扣除。

正向推导(含手续费)

卖出 Δx\Delta x 个代币 A,实际进入池子的只有 (1r)Δx(1 - r) \cdot \Delta x,其中 rr 为手续费率。

Δy=y0(1r)Δxx0+(1r)Δx\Delta y = \frac{y_0 \cdot (1-r) \cdot \Delta x}{x_0 + (1-r) \cdot \Delta x}
// Uniswap V2 风格(0.3% 手续费)
function getAmountOutWithFee(reserveA, reserveB, amountIn, feeRate = 0.003) {
  const amountInWithFee = amountIn * (1 - feeRate);
  return (reserveB * amountInWithFee) / (reserveA + amountInWithFee);
}

反向推导(含手续费)

需要卖出更多代币来补偿手续费:

Δx=x0Δy(y0Δy)(1r)\Delta x = \frac{x_0 \cdot \Delta y}{(y_0 - \Delta y) \cdot (1-r)}
function getAmountInWithFee(reserveA, reserveB, amountOut, feeRate = 0.003) {
  return (reserveA * amountOut) / ((reserveB - amountOut) * (1 - feeRate));
}

四、对比总结

方向已知求解公式
正向卖出 Δx\Delta x获得 Δy\Delta yΔy=y0Δxx0+Δx\Delta y = \frac{y_0 \cdot \Delta x}{x_0 + \Delta x}
反向目标 Δy\Delta y需卖 Δx\Delta xΔx=x0Δyy0Δy\Delta x = \frac{x_0 \cdot \Delta y}{y_0 - \Delta y}

直观理解

  • 正向推导:池子变大(输入代币增加),输出代币减少
  • 反向推导:先确定输出量,反推需要多少输入才能维持 kk 不变

五、实际应用场景

场景一:交易预览

用户在 DEX 界面输入卖出数量,前端实时显示预计获得的代币数量——这就是正向推导。

场景二:精确交易

某些策略需要精确控制买入数量(如套利、清算),这时需要反向推导计算输入量。

场景三:滑点保护

计算预期输出后,设置滑点容忍度:

const minAmountOut = expectedAmountOut * (1 - slippageTolerance);

如果实际输出低于 minAmountOut,交易回滚。


总结

AMM 的计算核心是恒定乘积公式 xy=kx \cdot y = k

  1. 正向推导:已知输入求输出,直接代入公式
  2. 反向推导:已知输出求输入,先算池子新状态再反推
  3. 手续费:从输入端扣除,公式稍作调整

理解这两个方向的计算,是开发 DEX 前端、编写交易策略的基础。


本文仅供技术学习参考,不构成任何投资建议。