3、Uniswap V2 详解:核心合约架构、交易流程、滑点与价格影响、流动性池的创建与销毁
Uniswap V2 是我个人非常喜欢的一个协议。它简洁、优雅,而且逻辑清晰。说白了,它就是一套完全跑在链上的自动做市商(AMM)系统。你不需要挂单,不需要撮合,只需要往池子里扔两种代币,就能成为做市商。今天我就带你把它拆开看看,里面到底是怎么运作的。
核心观点: Uniswap V2 的核心就是 x * y = k 这个恒定乘积公式。所有交易、流动性、滑点,都围绕这个公式展开。
3.1 核心合约架构
Uniswap V2 由三组核心合约组成:工厂合约(Factory)、交易对合约(Pair) 和 路由器合约(Router)。我刚开始研究时,觉得这三者关系有点绕,后来画了张图才彻底搞明白。
嗯,这张图你看懂了吗?我来解释一下:
- Factory 合约:就像一个注册中心。它负责创建新的交易对合约,并记录所有已存在的交易对。你想想看,如果没有 Factory,每次创建池子都得重新部署一套完整代码,那得多麻烦。
- Pair 合约:这是真正干活的地方。它持有两种代币的流动性,并执行
x * y = k的数学逻辑。每个交易对都是一个独立的 Pair 合约实例。 - Router 合约:这是用户直接交互的接口。它封装了复杂的交易逻辑,比如计算滑点、处理多跳交易等。我个人习惯把 Router 理解为「用户友好的包装器」。
小提示: 在实际项目中,我建议你直接通过 Router 合约交互,而不是直接调用 Pair 合约。Router 会帮你处理很多边界情况,比如滑点保护、手续费计算等。
3.2 交易流程
交易流程其实很简单。假设你想用 ETH 换 USDC。整个过程分三步:
- 用户发起交易:你通过 Router 合约的
swapExactTokensForTokens函数,指定输入代币和输出代币的数量。 - Router 计算:Router 根据当前池子的储备量,用
x * y = k公式计算出你能得到多少输出代币。 - Pair 执行交换:Router 调用 Pair 合约的
swap函数,完成代币转移。
这里有个关键点:交易前后,池子里的两种代币的乘积保持不变。举个例子:
// 假设 ETH/USDC 池子
// 初始状态:x = 100 ETH, y = 200,000 USDC, k = 20,000,000
// 你想用 1 ETH 换 USDC
// 交易后:x' = 101 ETH, y' = k / x' = 20,000,000 / 101 ≈ 198,019.8 USDC
// 你得到的 USDC = 200,000 - 198,019.8 = 1,980.2 USDC
你看,你投入 1 ETH,得到 1,980.2 USDC。但这里有个问题——滑点。
3.3 滑点与价格影响
滑点,说白了就是「你实际成交的价格和预期价格之间的差距」。为什么会这样?因为你的交易改变了池子的储备比例,从而改变了价格。
我记得有一次在项目中做回测,发现一个大户的交易直接把价格拉偏了 5%。当时我就意识到,滑点不是小问题。
滑点的大小取决于两个因素:
- 交易规模:你交易的量越大,滑点越高。
- 池子深度:池子里的流动性越多,滑点越低。
用公式表示就是:
价格影响 = (Δy / y) / (Δx / x) - 1
其中 Δx 是你投入的代币数量,Δy 是你得到的代币数量
举个例子,假设一个池子有 100 ETH 和 200,000 USDC:
| 交易量 (ETH) | 得到 USDC | 实际价格 (USDC/ETH) | 滑点 |
|---|---|---|---|
| 1 | 1,980.2 | 1,980.2 | 0.99% |
| 10 | 18,181.8 | 1,818.2 | 9.09% |
| 50 | 66,666.7 | 1,333.3 | 33.33% |
看到没?交易量越大,滑点越夸张。所以做市商在提供流动性时,一定要考虑池子的深度。
避坑指南: 我曾经在测试网上部署过一个流动性很浅的池子,结果一笔 5 ETH 的交易直接把价格打偏了 20%。从那以后,我每次部署新池子都会先检查流动性是否充足。
3.4 流动性池的创建与销毁
创建流动性池,其实就是往 Pair 合约里存入两种代币。销毁流动性池,就是取回你的代币。这两个操作都通过 Router 合约完成。
3.4.1 创建流动性池
创建池子的流程如下:
- 调用 Factory 的 createPair:传入两种代币的地址,Factory 会部署一个新的 Pair 合约。
- 向 Pair 合约转入代币:你需要把两种代币按当前市场价格的比例转入 Pair 合约。
- 调用 Router 的 addLiquidity:Router 会调用 Pair 的
mint函数,给你铸造 LP Token。
这里有个细节:首次添加流动性时,你决定初始价格。因为池子一开始是空的,你存入多少代币,就决定了初始的 x 和 y 值。后续的交易都会基于这个初始比例。
// 首次添加流动性示例
// 你存入 10 ETH 和 20,000 USDC
// 初始价格 = 20,000 / 10 = 2,000 USDC/ETH
// 你获得 LP Token 数量 = sqrt(10 * 20,000) = 447.21 LP
3.4.2 销毁流动性池
销毁池子就是取回你的代币。流程如下:
- 调用 Router 的 removeLiquidity:传入 LP Token 数量。
- Router 调用 Pair 的 burn:Pair 合约销毁你的 LP Token,并返还对应的两种代币。
你拿回多少代币,取决于你持有的 LP Token 占总量的比例。比如你持有 10% 的 LP Token,就能拿回池子里 10% 的 ETH 和 10% 的 USDC。
重要提醒: 销毁流动性时,你拿回的是当前池子里的代币数量。如果池子产生了交易手续费(每笔交易 0.3%),这些手续费会累积到池子里,所以你拿回的可能比当初存入的更多。这就是做市商的收益来源。
嗯,到这里 Uniswap V2 的核心内容就讲完了。你想想看,其实整个逻辑并不复杂,就是围绕 x * y = k 这个公式展开的。但真正在实战中,滑点控制、流动性管理、手续费计算这些细节,才是决定成败的关键。