Uniswap V3 第 1 章:简介——集中流动性(Concentrated Liquidity)
Uniswap V3 最核心的创新只有一个词:集中流动性(concentrated liquidity)。理解了它,V3 的一切(tick、sqrtPriceX96、NFT 头寸、费用追踪)都顺理成章。这一章用通俗的语言讲清它解决什么问题、和 V2 的根本区别,以及合约架构概览。
目录
- 1. V2 的痛点:资金利用率太低
- 2. V3 的答案:把流动性集中到价格区间
- 3. 案例:同样的钱,V3 深度高几十倍
- 4. tick:价格的离散刻度
- 5. V2 vs V3 对照
- 6. 头寸变成 NFT
- 7. 合约架构概览
- 8. 本章小结
- 9. 动手练习
1. V2 的痛点:资金利用率太低
回忆 V2 的恒定乘积 x·y=k:流动性均匀分布在 0 到 ∞ 的所有价格上。
问题来了:对一个稳定币对(比如 USDC/DAI),价格几乎永远在 0.99~1.01 之间波动。但 V2 把你的钱也铺在了”1 USDC = 5 DAI”或”1 USDC = 0.2 DAI”这种永远不会发生的价格上。
结果:你的绝大部分资金都是闲置的,只有极小一部分真正在当前价格附近提供深度。资本效率极低。
2. V3 的答案:把流动性集中到价格区间
V3 让 LP 自己选择一个价格区间 [Pa, Pb],只在这个区间内提供流动性。
- 你认为 ETH 会在 2800~3200 美元之间波动?就把流动性全集中在这个区间。
- 在这个区间内,你的钱提供的深度远超 V2 里同样金额铺满全价格的深度。
- 代价:如果价格走出你的区间,你的头寸会变成单一资产,且不再赚手续费(直到价格回来)。
这就像 V2 是”撒胡椒面”(铺满全场),V3 是”精准下注”(集中在你看好的区间)。
3. 案例:同样的钱,V3 深度高几十倍
假设你有 10,000 美元做 ETH/USDC 的 LP,当前 ETH=3000。
- V2:钱铺在 0~∞,当前价附近实际可用的深度很薄。
- V3,区间 [2700, 3300](±10%):同样 10,000 美元,全部集中在这个窄区间,当前价附近的深度可能是 V2 的 几十倍。
资本效率提升的倍数大致与”区间收窄的程度”成正比。区间越窄,同样的钱深度越高、手续费收入越多——但价格走出区间的风险也越大。这是 V3 LP 的核心权衡:收益 vs 区间风险。
极端情况:把区间设到极窄(接近一个点),就像在做”限价单”——这也是 V3 能模拟挂单的原因。
4. tick:价格的离散刻度
V3 不允许任意实数价格,而是把价格离散成一个个 tick(刻度)。定义:
价格 P(i) = 1.0001^i (i 就是 tick 序号,可正可负)
- 相邻两个 tick 的价格差正好 0.01%(1.0001 倍)。
- tick 是整数,价格区间的端点 Pa、Pb 必须落在 tick 上。
tick = 0对应价格 1;tick = 1对应 1.0001;tick = -1对应 1/1.0001。
为什么用 1.0001^i?因为它让”价格的对数”变成等间距的整数,便于在链上高效地索引、跨越和计算流动性区间(后面 tick bitmap 会用到)。
5. V2 vs V3 对照
| 维度 | Uniswap V2 | Uniswap V3 |
|---|---|---|
| 流动性分布 | 全价格 0~∞ 均匀 | LP 自选区间 [Pa,Pb] 集中 |
| 资本效率 | 低 | 高(窄区间可达几十~几千倍) |
| 同对池子数 | 唯一 | 按费率档位多个(0.01%/0.05%/0.3%/1%) |
| LP 凭证 | 同质化 ERC20(LP token) | 非同质化 NFT(每个头寸独立) |
| 价格表示 | reserve 比例 | tick + sqrtPriceX96 |
| 价格走出区间 | 不存在该概念 | 头寸变单一资产、停止赚费 |
| 手续费 | 自动复利进池 | 单独累计,需手动 collect |
6. 头寸变成 NFT
V2 里所有 LP 的份额是同质的(都是同一种 LP token)。但 V3 里每个人的区间 [Pa,Pb] 不同,无法用同质化代币表示。
所以 V3 把每个流动性头寸做成一个 NFT(通过 NonfungiblePositionManager):
- 每个 NFT 记录:区间 [tickLower, tickUpper]、流动性 L、未领取的手续费。
- 你可以增加/减少流动性、领取手续费、转让整个头寸(转 NFT)。
7. 合约架构概览
| 合约 | 层 | 作用 |
|---|---|---|
| UniswapV3Factory | core | 按 (tokenA, tokenB, fee) 创建池子;管理费率档位与 tickSpacing |
| UniswapV3Pool | core | 池子本体:swap、mint/burn/collect、tick 与费用追踪、价格预言机 |
| NonfungiblePositionManager | periphery | 把头寸包装成 NFT,面向 LP 的主要入口 |
| SwapRouter02 | periphery | 面向交易者:单池/多池、定输入/定输出兑换 |
| Quoter | periphery | 链下报价(模拟 swap) |
和 V2 一样是 core(安全、最小)+ periphery(方便、面向用户) 的分层,但 V3 的 core 复杂得多(要管理 tick、区间、费用)。
8. 本章小结
- V2 把流动性铺满全价格 → 资本效率低;V3 让 LP 自选区间集中流动性 → 效率高几十倍以上。
- 代价:价格走出区间则头寸变单一资产、停止赚费——收益与区间风险的权衡。
- tick:价格离散为
P(i)=1.0001^i,相邻 tick 差 0.01%,便于链上索引。 - 同一对代币按费率档位(0.01/0.05/0.3/1%)有多个池子。
- 每个头寸是一个 NFT(区间不同无法同质化),由
NonfungiblePositionManager管理。 - 架构:Factory / Pool(core)+ PositionManager / SwapRouter / Quoter(periphery)。
9. 动手练习
目标:直观感受 tick 与价格的换算。
练习:tick ↔ price 换算
写一个脚本或 Foundry 测试:
- 实现
price = 1.0001^tick,分别算tick = 0, 1, -1, 10000, -10000对应的价格。 - 反过来:给定价格
P,求tick = log(P) / log(1.0001),向下取整。 - 验证:
tick=10000对应价格 ≈1.0001^10000 ≈ 2.718(接近 e,因为1.0001^10000 ≈ e^(10000·0.0001) = e^1)。 - (进阶)链上读一个真实 V3 池的
slot0().tick,用上面公式换算成价格,和市场价对比(注意 token0/token1 顺序和精度,下一章细讲)。
interface IUniswapV3Pool {
function slot0() external view returns (
uint160 sqrtPriceX96, int24 tick, uint16, uint16, uint16, uint8, bool
);
}
运行
forge test --evm-version cancun --fork-url $FORK_URL \
--match-path test/UniswapV3Intro.t.sol -vvv
下一章(第 2 章 现货价)讲 V3 独特的价格存储方式:
slot0、sqrtPriceX96、tick三者怎么互相换算,以及为什么存”价格的平方根”。