为何UniswapRouter.getAmountsOut返回的WETH数量少于储备量?
问题描述
我编写以下代码熟悉Uniswap机制:
- 获取UNI-WETH交易对合约
- 查询交易对储备量
- 调用
getAmountsOut传入全部UNI代币储备量进行兑换计算
代码如下:
const pairAddress = await _V2Factory.getPair(uniToken, wethToken); const pairContract = new ethers.Contract( pairAddress, IUniswapV2Pair.abi, _provider ); const tmpReserves = await pairContract.getReserves(); const reserves = [tmpReserves.reserve0, tmpReserves.reserve1]; // 修正原代码笔误:原代码误用reserves变量未定义 console.log("reserves", reserves); const output = await router.getAmountsOut(reserves[0], [ uniToken.address, wethToken.address, ]); console.log("output", output);
输出结果:
- reserves:
[ 36851089197799104779745113323n, 281312509765248795074n ] - output:
[ 36851089197799104779745113323n, 140444953548298972803n ]
改用getAmountOut测试,结果类似:
const output2 = await router.getAmountOut( reserves[0], reserves[0], reserves[1] ); console.log("output2", output2);
output2: 143271499822164119007n
我的疑问:为何output的第二个元素不等于交易对中的全部WETH储备量281312509765248795074n,仅返回140444953548298972803n?
(注:本地Hardhat节点及主网测试结果一致,无第三方干扰)
核心原因:Uniswap V2的恒定乘积机制+手续费规则
Uniswap V2的兑换逻辑基于恒定乘积公式(x * y = k,x、y为交易对两种代币的储备量,k为恒定值),同时交易收取0.3%的手续费(99.7%的交易金额参与兑换计算,0.3%留在流动性池作为LP收益),这两个规则共同导致你无法通过卖出全部UNI储备来取走所有WETH。
具体计算逻辑
当调用getAmountsOut或getAmountOut时,兑换输出的计算遵循以下公式:
amountOut = (amountIn * reserveOut * 997) / (reserveIn * 1000 + amountIn * 997)
其中:
amountIn:你要卖出的代币数量(这里是全部UNI储备36851089197799104779745113323n)reserveIn:交易对中你卖出代币的当前储备量(和amountIn数值相同)reserveOut:交易对中目标代币的当前储备量(281312509765248795074n)
代入你的数值计算:
amountOut = (36851089197799104779745113323n * 281312509765248795074n * 997) / (36851089197799104779745113323n * 1000 + 36851089197799104779745113323n * 997)
约去分子分母中的36851089197799104779745113323n,简化为:
amountOut = (281312509765248795074n * 997) / 1997 ≈ 140444953548298972803n
这个结果和你得到的output完全一致,是Uniswap V2的正常计算结果。
为什么不能取走全部WETH?
如果强行取走全部WETH,意味着交易对中的WETH储备变为0,根据恒定乘积公式x*y=k,此时UNI储备需要趋近于无穷大才能维持k不变,这显然不可能——恒定乘积机制的核心就是保持流动性池的两种代币储备始终存在,无法通过单次交易耗尽某一种代币的储备。
另外,getAmountOut和getAmountsOut的结果略有差异,是因为getAmountsOut会考虑路由中每一步的手续费(这里是单路由,差异来自公式计算的精度细节),但本质逻辑完全一致。
内容的提问来源于Stack Exchange,提问作者user133422

