3、Uniswap V2 详解:核心合约架构、交易流程、滑点与价格影响、流动性池的创建与销毁

Uniswap V2 是我个人非常喜欢的一个协议。它简洁、优雅,而且逻辑清晰。说白了,它就是一套完全跑在链上的自动做市商(AMM)系统。你不需要挂单,不需要撮合,只需要往池子里扔两种代币,就能成为做市商。今天我就带你把它拆开看看,里面到底是怎么运作的。

核心观点: Uniswap V2 的核心就是 x * y = k 这个恒定乘积公式。所有交易、流动性、滑点,都围绕这个公式展开。

3.1 核心合约架构

Uniswap V2 由三组核心合约组成:工厂合约(Factory)交易对合约(Pair)路由器合约(Router)。我刚开始研究时,觉得这三者关系有点绕,后来画了张图才彻底搞明白。

Factory 合约 创建 Pair 合约 管理所有交易对 Pair 合约 持有流动性 执行 x*y=k Router 合约 用户交互入口 组装交易数据 用户 调用 Router 架构关系:Factory 创建 Pair,Router 调用 Pair

嗯,这张图你看懂了吗?我来解释一下:

  • Factory 合约:就像一个注册中心。它负责创建新的交易对合约,并记录所有已存在的交易对。你想想看,如果没有 Factory,每次创建池子都得重新部署一套完整代码,那得多麻烦。
  • Pair 合约:这是真正干活的地方。它持有两种代币的流动性,并执行 x * y = k 的数学逻辑。每个交易对都是一个独立的 Pair 合约实例。
  • Router 合约:这是用户直接交互的接口。它封装了复杂的交易逻辑,比如计算滑点、处理多跳交易等。我个人习惯把 Router 理解为「用户友好的包装器」。

小提示: 在实际项目中,我建议你直接通过 Router 合约交互,而不是直接调用 Pair 合约。Router 会帮你处理很多边界情况,比如滑点保护、手续费计算等。

3.2 交易流程

交易流程其实很简单。假设你想用 ETH 换 USDC。整个过程分三步:

  1. 用户发起交易:你通过 Router 合约的 swapExactTokensForTokens 函数,指定输入代币和输出代币的数量。
  2. Router 计算:Router 根据当前池子的储备量,用 x * y = k 公式计算出你能得到多少输出代币。
  3. 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 创建流动性池

创建池子的流程如下:

  1. 调用 Factory 的 createPair:传入两种代币的地址,Factory 会部署一个新的 Pair 合约。
  2. 向 Pair 合约转入代币:你需要把两种代币按当前市场价格的比例转入 Pair 合约。
  3. 调用 Router 的 addLiquidity:Router 会调用 Pair 的 mint 函数,给你铸造 LP Token。

这里有个细节:首次添加流动性时,你决定初始价格。因为池子一开始是空的,你存入多少代币,就决定了初始的 xy 值。后续的交易都会基于这个初始比例。

// 首次添加流动性示例
// 你存入 10 ETH 和 20,000 USDC
// 初始价格 = 20,000 / 10 = 2,000 USDC/ETH
// 你获得 LP Token 数量 = sqrt(10 * 20,000) = 447.21 LP

3.4.2 销毁流动性池

销毁池子就是取回你的代币。流程如下:

  1. 调用 Router 的 removeLiquidity:传入 LP Token 数量。
  2. Router 调用 Pair 的 burn:Pair 合约销毁你的 LP Token,并返还对应的两种代币。

你拿回多少代币,取决于你持有的 LP Token 占总量的比例。比如你持有 10% 的 LP Token,就能拿回池子里 10% 的 ETH 和 10% 的 USDC。

重要提醒: 销毁流动性时,你拿回的是当前池子里的代币数量。如果池子产生了交易手续费(每笔交易 0.3%),这些手续费会累积到池子里,所以你拿回的可能比当初存入的更多。这就是做市商的收益来源。

嗯,到这里 Uniswap V2 的核心内容就讲完了。你想想看,其实整个逻辑并不复杂,就是围绕 x * y = k 这个公式展开的。但真正在实战中,滑点控制、流动性管理、手续费计算这些细节,才是决定成败的关键。


专注资料整理