Uniswap V2 第1章 总览:恒定乘积与合约架构

介绍 Uniswap V2 的恒定乘积做市原理、核心与外围合约的分工,以及 Pair/Factory/Router 的整体架构。

5 分钟阅读
Uniswap V2 第1章 总览:恒定乘积与合约架构

Uniswap V2 第 1 章:总览(Overview)

这一章带你建立对 Uniswap V2 的整体认识:它用什么数学做市(恒定乘积 x·y=k),由哪些合约组成(Core 与 Periphery),以及 Pair / Factory / Router 三者怎么分工。后面每一章都是在这个骨架上展开。


目录


1. Uniswap V2 是什么

Uniswap V2 是以太坊上最经典的去中心化交易所(DEX)自动做市商(AMM)。它没有订单簿,价格完全由池子里两种代币的数量比例决定。任何人都可以:

  • 交易(swap):用一种代币换另一种。
  • 提供流动性(add liquidity):存入两种代币,赚取交易手续费。
  • 创建交易对(create pool):为任意两种 ERC20 代币开一个新池子。

它的全部定价逻辑建立在一条极简的方程上——恒定乘积。


2. 恒定乘积做市:x · y = k

设池子里有 x 个代币 A 和 y 个代币 B,Uniswap V2 维持:

x · y = k (常数)
  • 交易时,乘积 k 保持不变(不考虑手续费的话)。
  • 你往池子里加入 A(x 增大),就必须取走一些 B(y 减小),才能让乘积不变。
  • 取走的 B 的数量,就是你”换到的量”。

这条曲线是一条双曲线,关键性质:

  • x、y 永远 > 0(双曲线不碰坐标轴)→ 池子永远不会被完全掏空
  • 交易越大,价格移动越多 → 滑点随交易规模增大

3. 案例:直观感受 x·y=k 的报价

设池子 x = 100 WETHy = 300,000 DAI,则 k = 100 × 300,000 = 30,000,000

当前价格(现货价)= y/x = 300,000/100 = 3,000 DAI/WETH

你想卖 1 个 WETH 换 DAI(先忽略手续费):

新 x = 100 + 1 = 101
新 y = k / 新x = 30,000,000 / 101 = 297,029.70 DAI
你换到的 DAI = 300,000 - 297,029.70 = 2,970.30 DAI

注意:现货价是 3,000,但你实际只换到 2,970.3 DAI,差的 29.7 就是滑点(因为这不是无穷小交易)。卖得越多,平均成交价越差。这就是恒定乘积的核心特征。


4. Core 与 Periphery:两层合约架构

Uniswap V2 的代码分两个仓库、两层职责:

仓库职责特点
Core(核心)v2-core存资产、维护 x·y=k、铸销 LP极简、最小化、永不升级(安全第一)
Periphery(外围)v2-periphery帮用户算路由、加滑点保护、处理 ETH/WETH方便、可替换、面向用户

设计哲学:Core 尽量简单、把钱锁死的逻辑做到最小(减少出 bug 的可能);所有”方便但复杂”的逻辑放到 Periphery,即使 Periphery 有问题也不会动到 Core 里的资产安全。

你日常用钱包/前端交易时,调用的是 Periphery 的 Router,由它再去调 Core 的 Pair。直接调 Core 是高级用法(需要自己处理安全检查)。


5. 三个核心合约:Factory / Pair / Router

合约所在层作用
UniswapV2FactoryCore工厂:为任意两种代币创建并登记 Pair(每对代币唯一一个池子)
UniswapV2PairCore池子本体:存两种代币、维护 x·y=kswap/mint/burn、是一个 ERC20(LP token)
UniswapV2Router02Periphery路由:多跳交易、滑点保护、deadline、ETH↔WETH 转换,是用户主要入口

关键点:

  • 每个 Pair 同时也是一个 ERC20——它发行的代币就是 LP token(流动性凭证)。
  • Factory 保证 每对代币只有一个 Pair(避免流动性分散)。
  • Router 自己不存钱,只是帮你把调用编排好、转发给 Pair。

6. 一次 swap 的合约调用链

以”用 WETH 换 MKR,路径 WETH → DAI → MKR”为例:

用户
 │ 调用 router.swapExactTokensForTokens(amountIn, ..., path=[WETH,DAI,MKR])

Router02
 │ ① 用 Library.getAmountsOut 算出每一跳能换多少
 │ ② 把 WETH 转入第一个 Pair(WETH/DAI)
 │ ③ 依次调用各 Pair.swap(...)

Pair(WETH/DAI) → Pair(DAI/MKR)
 │ 每个 Pair 校验 x·y=k 仍成立(含手续费),把输出币转给下一跳/用户

用户收到 MKR

记住这个分层:Router 负责”编排和计算”,Pair 负责”守住 k 和转账”。 后面各章会逐个拆开这些函数。


7. 本章小结

  1. Uniswap V2 是基于恒定乘积 x·y=k 的 AMM:池子永不被掏空,但滑点随交易规模上升。
  2. 现货价 = y/x;实际成交含滑点,交易越大成交价越差。
  3. 代码分 Core(v2-core,极简安全)Periphery(v2-periphery,方便面向用户) 两层。
  4. 三大合约:Factory(建池)、Pair(池子本体,也是 LP token 的 ERC20)、Router(用户入口,多跳/滑点/ETH 处理)。
  5. swap 调用链:用户 → Router(算路由、转发)→ 一个或多个 Pair(守 k、转账)。

8. 动手练习

目标:先在主网分叉上读取一个真实 Pair 的储备量,亲手算一次现货价和一笔 swap 的滑点。

练习:读取 Pair 储备并手算报价

主网分叉,针对 DAI/WETH 的 Pair(可用 Factory 的 getPair(DAI, WETH) 查到,或用常量 UNISWAP_V2_PAIR_DAI_WETH):

interface IUniswapV2Pair {
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32);
    function token0() external view returns (address);
    function token1() external view returns (address);
}

思路:

  1. getReserves() 拿到 reserve0reserve1,用 token0() 确认哪个是 DAI、哪个是 WETH。
  2. 计算现货价 reserve_DAI / reserve_WETH,打印(应接近当前 ETH 市价)。
  3. 手算”卖 1 WETH 能换多少 DAI”(用第 3 节的 新y = k/新x 方法,先不含费)。
  4. (进阶)再算上 0.3% 手续费:输入按 amountIn × 997/1000 计入(见第 2 章公式),对比含费和不含费的差别。

运行

forge test --evm-version cancun --fork-url $FORK_URL \
  --match-path test/UniswapV2Overview.t.sol -vvv

下一章(第 2 章 Swap)深入兑换:getAmountsOut/getAmountsIn 的精确公式(含 0.3% 手续费)、pair.swap 怎么守住 k,以及滑点的数学。

💬 评论