现在,我们即将实现高级交换,包括链式交换(例如,通过token B 将token A 交换为token C)。在实现之前,我们需要了解 Uniswap 如何计算输出量。让我们先弄清楚金额与价格的关系。
什么是价格?就是你用一种东西换取另一种东西的一个单位。在交易中,价格在某种意义上是一个中间实体:重要的是你拥有的token数量和你换取的token数量。
在恒定产品交换中,价格只是储备之间的一种关系--我们已经在 ZuniswapV2Library 的报价功能中实现了价格计算。然而,在实际进行交换时,这个价格是不正确的,因为它只代表了某一时刻储备之间的关系。但在进行互换时,储备量会发生变化,我们实际上期望的是价格随着储备量的变化而下降。
为了说明这一切,让我们回顾一下常量乘积公式:
x∗y=k
其中x和y是一对reserve(reserve0和reserve1)。
在进行互换时,x 和 y 会发生变化,但 k 不变(实际上,由于互换费用的
存在,k 会缓慢增长)。我们可以将其写成一个公式:
(x+rΔx)(y−Δy)=xy
其中,r 为 1 - 交易费(1 - 0.3% = 0.997)、
Δx 是我们为换取的金额, Δy 是我们得到的金额。
这是一个非常简洁明了的公式,它表明swap后储备金的乘积必须等于swap前储备金的乘积,这就是恒等乘积公式的定义。我们可以用这个公式来计算我们在互换过程中得到的金额。在进行一些基本的代数运算后,我们可以得到以下结果:
如您所见,这是一个两个储备金的关系式 (y/x),其中考虑了投入金额 (rΔx),包括手续费。
现在让我们对这个公式进行编程:
function getAmountOut(
uint256 amountIn,
uint256 reserveIn,
uint256 reserveOut
) public pure returns (uint256) {
if (amountIn == 0) revert InsufficientAmount();
if (reserveIn == 0 || reserveOut == 0) revert InsufficientLiquidity();
...
amountIn is Δx, reserveIn is x, reserveOut is y.
由于 Solidity 不支持浮点除法,我们需要将分子和分母乘以 1000,然后从应用于 amountIn 的乘数中减去 3--这将把 0.3% 的费用应用于 amountIn:
uint256 amountInWithFee = amountIn * 997;
uint256 numerator = amountInWithFee * reserveOut;
uint256 denominator = (reserveIn * 1000) + amountInWithFee;
return numerator / denominator;