Uniswap 是一个 DeFi 应用程序,它使交易者能够以无需信任的方式将一种代币兑换为另一种代币。它是早期用于交易的自动做市商之一(尽管不是第一个)。
自动做市商是订单簿的替代方案,假设读者已经熟悉订单簿的概念。
AMM 的工作原理
自动做市商在资金池(一个智能合约)中持有两种代币(代币 X 和代币 Y)。它允许任何人从池中提取代币 X,但他们必须存入一定数量的代币 Y,使得池中资产的“总和”不会减少,这里我们将“总和”视为两种资产数量的乘积。
这里 和 是交易后资金池的代币余额, 和 是交易前资金池的代币余额。
这保证了资金池的资产持有量只能保持不变或增加。
大多数资金池都会强制收取某种手续费。不仅余额的乘积应该增加,而且它应该至少增加一定的量以包含手续费。
资产由流动性提供者提供给资金池,他们收到所谓的 LP 代币来代表他们在资金池中的份额。流动性提供者的余额追踪方式类似于 ERC 4626 的工作原理。AMM 和 ERC 4626 之间的区别在于,ERC 4626 仅支持一种资产,而 AMM 有两种代币。就像金库一样,流动性提供者在资金池中的份额保持不变,但乘积 会变大,因此他们分得的切片也会更大。
AMM 的优势
AMM 没有买卖价差
在 AMM 中,价格发现是自动的。它由池中资产的比率决定。具体来说,如果我们有代币 和代币 ,价格决定如下:
对于 也是如此。具体来说,投入池中的资产 越多,它就越“丰富”, 的价格就会下降。
不需要等待合适的“买入”或“卖出”订单出现。它始终存在。
如果 AMM 中的价格与其他交易所之间存在偏差,那么交易者将套利该差价,使价格重新恢复平衡。
我们需要强调的是,这是“现货”或“边际”价格。如果你购买任意数量的 ,你实际支付的价格将比此计算结果更差。
AMM 兼作预言机
由于资产价格是自动决定的,其他智能合约可以使用 AMM 作为价格预言机。然而,AMM 价格很容易被闪电贷操纵,因此以这种方式使用 AMM 时需要设置安全保障措施。尽管如此,能免费提供价格数据依然是非常有价值的。
与订单簿相比,AMM 的 Gas 效率极高
订单簿需要大量的记账工作(bookkeeping,无意双关)。而 AMM 只需要持有两种代币并根据简单的规则进行转移。这使得它们在实现上更加高效。
AMM 的缺点
自动做市商有两个主要缺点:1)价格总是在变动;2)流动性提供者会面临无常损失。
即使是小额订单也会导致 AMM 价格变动
如果你下了一个购买 100 股 Apple 股票的订单,你的订单不会引起价格变动,因为在你指定的价格上有成千上万股可供出售。但这对于自动做市商是不存在的。每一笔交易,无论多小,都会引起价格变动。
这带来两个影响。与订单簿模型相比,买入或卖出订单通常会遭遇更大的滑点,而且兑换机制容易引来三明治攻击。
在 AMM 中几乎无法避免三明治攻击
因为每个订单都会引起价格变动,MEV(最大可提取价值)交易者会等待足够大的买单出现,然后在受害者的订单之前下达买单,并在其之后下达卖单。领跑的买单会推高原始交易者的价格,从而给他们带来更差的执行价格。这被称为三明治攻击,因为受害者的交易被“夹”在攻击者之间。
- 攻击者的第一次买入(抢跑):为受害者推高价格
- 受害者的买入:进一步推高价格
- 攻击者的卖出:将第一次买入的资产获利卖出
流动性提供者无法控制其资产出售的价格
出于我们稍后将讨论的原因,流动性提供者只能按照资金池中代币的当前比率按比例提供资产。例如,如果有 100 个代币 和 200 个代币 ,新的流动性提供者提供的代币 数量必须是代币 的两倍。
在传统的订单簿中,做市商可以在他们认为反映了理想买卖价的水平上下达限价单(例如,在当前市场价格下方下达买单或在当前市场价格上方下达卖单),但这对于自动做市商是不可能的。请记住,自动做市商使用公式根据池中的资产比率来设定价格,因此做市商无法设定他们希望出售其资产的具体价格。
AMM 的流动性提供者可能会遭受无常损失
假设在一个假想场景中,Ether 的起始价格为 10 美元,后来变成了 1,000 美元。
如果有人拥有 1 ETH 和 10 USD 的投资组合,那么他们的投资组合起始价值为 20 美元,最终价值为 1010 美元(1 ETH + 10 美元)。他们的总利润是 990 美元。
如果他们把钱放在 AMM 中,他们就会错过大部分收益。价格变动后,AMM 将会有 0.1 ETH 和 100 USD。这正确地将 ETH 的价格定为 1,000 美元,但资金池的净值将小于 990 美元。下面是价格变动前后的资金池持有量情况
虽然稳定币的数量增加了 10 倍,但 Ether 的数量减少了。最终结果是,我们将资产放入资金池时,其价值的增长量少于将资产分开持有的情况。
下表显示了在资金池中持有 ETH 和 USD 与仅仅持有它们的相对表现。
| Ether 资金池余额 | 稳定币资金池余额 | ETH × 稳定币 | 1 ETH 的美元价值 | 提供流动性时的资产价值 | 仅持有的资产价值 | ||
|---|---|---|---|---|---|---|---|
| 变动前 | 1 | 10 | 10 | 10 | $\$20 \; (\$10 \text{ ETH} + 10 \text{ USD})$ | $\$20 \; (\$10 \times 1 \text{ ETH} + 10 \text{ USD})$ | |
| 变动后 | 0.1 | 100 | 10 | 1000 | $\$200 \; (\$100 \text{ ETH} + 100 \text{ USD})$ | $\$1010 \;(\$1000 \times 1 \text{ ETH} + 10 \text{ USD})$ | |
| 收益 | $\$180$ | $\$990$ | |||||
错失的收益被称为“无常损失(impermanent loss)”。在上表中,无常损失为 。
Uniswap V2 架构
Uniswap V2 的架构出奇地简单。其核心是 UniswapV2Pair 合约,该合约持有两种 ERC 20 代币,交易者可以对其进行兑换,或者流动性提供者可以为其提供流动性。每一种可能的 UniswapV2Pair 都由一个不同的 UniswapV2Pair 合约进行管理。如果所需的 UniswapV2Pair 合约不存在,可以通过 UniswapV2Factory 合约无需许可地创建一个新的。UniswapV2Pair 合约也是 ERC 20 代币(它们继承自 ERC 20),该代币用于跟踪存款,其方式类似于 ERC 4626 的工作原理。
虽然高级交易者或智能合约可以直接与 pair 合约交互,但大多数用户会通过 router 合约与 pair 交互,该路由合约包含了一些便捷的函数,例如在单笔交易中通过多个 pair 进行交易,以在不存在配对的情况下创建一个“合成”配对。
就是这样!在 Uniswap V2 系统中,真正在起作用的智能合约只有三个。
Factory: github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Factory.sol
Pair: (继承自 ERC20):github.com/Uniswap/v2-core/blob/master/contracts/UniswapV2Pair.sol
Router: github.com/Uniswap/v2-periphery/tree/master/contracts
核心 - 外围模式
观察到上面的路由合约位于名为“v2 periphery(外围)”的代码库中,而配对合约位于“v2 core(核心)”代码库中。Uniswap V2 遵循“核心 / 外围”设计模式,其中最核心的逻辑保存在核心(core)中,而“可选的”逻辑则保存在外围(periphery)中。
其背后的意图是让核心包含尽可能少的代码,从而降低核心业务逻辑中出现漏洞的可能性。
给定两个代币地址,如何定位一个资金池
智能合约并没有通过访问代币对到资金池地址的映射,而是通过预测 create2 地址(作为代币地址和工厂地址的函数)来计算资金池的地址。由于不需要访问存储,这在 Gas 方面非常高效。下面是 UniswapV2Library 提供的用于计算 Pair 合约地址的辅助函数。
// calculates the CREATE2 address for a pair without making any external calls
function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
(address token0, address token1) = sortTokens(tokenA, tokenB);
pair = address(uint(keccak256(abi.encodePacked(
hex'ff',
factory,
keccak256(abi.encodePacked(token0, token1)),
hex'96e8ac4277198f8fbbf785487aa39f430f63b76db002cb326e37da348845f' // init code hash
))));
}
为什么不使用克隆
EIP 1167 clone 模式用于创建一组相似的合约,那为什么这里不使用它呢?尽管部署成本会更低,但由于使用了 delegatecall,每笔交易会额外引入 2,600 Gas。由于资金池需要被频繁使用,部署节省的成本在数百次交易后最终会被抵消,因此将资金池部署为新合约是值得的。
练习题
计算兑换所需代币数量时很容易出错,这可能导致资金池被耗尽。通过以下安全挑战进行练习:Ethernaut 22 Dex
在 RareSkills 了解更多
本文是一个系列的一部分。其余部分请参见 Uniswap V2 Book。有关其他课程,另请参阅我们的 Blockchain Bootcamps。
最初发布于 2023 年 11 月 15 日