第三章:Uniswap V2核心机制:交易流程拆解、流动性池设计、ERC20与ETH的配对处理

好,咱们直接进入正题。Uniswap V2,说白了就是一套跑在链上的自动做市商协议。你不需要挂单,不需要对手方,直接跟一个智能合约池子交易。听起来很神奇?其实核心逻辑就三块:交易怎么算、池子怎么建、ETH怎么处理。我一个个拆给你看。

3.1 交易流程拆解:从输入到输出的数学魔法

先问个问题:你在Uniswap上拿Token A换Token B,合约里到底发生了什么?

核心公式就一个:x * y = k。x是A的储备量,y是B的储备量,k是常数。交易前后,这个乘积保持不变。嗯,这里要注意,实际交易会收手续费,所以k会略微增大,但原理不变。

假设池子里有100个A和200个B,k=20000。你想用1个A换B。交易后,A变成101个,那B应该变成多少?

101 * y' = 20000y' = 20000 / 101 ≈ 198.02

所以你实际拿到的B是:200 - 198.02 = 1.98个。但等等,你投入了1个A,为什么只拿到1.98个B?因为滑点!你交易量越大,滑点越明显。我在项目中遇到过有人拿大额交易测试,结果滑点高得吓人,直接亏了一笔。

核心要点:交易量越大,对池子储备的影响越大,滑点越高。这就是为什么大额交易要拆分成多笔。

代码实现上,Uniswap V2的swap函数会先计算输入金额对应的输出金额,然后更新储备量,最后转账。我习惯把计算逻辑单独抽出来,方便测试。

// 计算输出金额
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
    uint amountInWithFee = amountIn * 997;
    uint numerator = amountInWithFee * reserveOut;
    uint denominator = reserveIn * 1000 + amountInWithFee;
    amountOut = numerator / denominator;
}

看到那个997和1000了吗?0.3%的手续费就是这么来的。输入金额先扣掉0.3%,剩下的才参与计算。我刚开始看这段代码时,觉得这个设计很巧妙——手续费在计算前就扣除了,不会影响k值的稳定性。

3.2 流动性池设计:LP Token与储备比例

流动性池子,说白了就是一个资金池。你存入两种代币,获得LP Token作为凭证。池子设计有几个关键点:

  • 比例恒定:首次添加流动性时,两种代币的比例决定了初始价格。之后添加,必须按当前比例来。
  • LP Token铸造:你存入的流动性占总池子的比例,决定了你能获得多少LP Token。
  • 手续费分配:每次交易的手续费会累积到池子里,LP Token持有者可以按比例提取。

举个例子:池子初始有100个A和200个B,总价值假设是1000美元。你存入10个A和20个B,占总流动性的10%,那你就能获得总LP Token供应量的10%。

个人经验:我曾经在测试网上部署过Uniswap V2,发现一个坑——首次添加流动性时,如果比例设置不对,会导致价格剧烈波动。建议先用小额测试,确认比例没问题再大额投入。

LP Token的设计也很讲究。它本身是一个ERC20代币,可以转账、交易,甚至用在其他DeFi协议里做抵押品。我见过有人把LP Token质押到其他池子里赚双重收益,但风险也翻倍——一旦原池子出问题,LP Token可能归零。

3.3 ERC20与ETH的配对处理:WETH的巧妙设计

这里有个关键问题:ETH本身不是ERC20代币,它没有transfer函数,不能直接放进Uniswap池子里。那怎么实现ETH和ERC20的交易?

Uniswap V2的解决方案是:用WETH(Wrapped Ether)作为桥梁。WETH是一个ERC20代币,1:1锚定ETH。你把ETH存进去,获得WETH;把WETH销毁,拿回ETH。

所以,Uniswap V2里其实没有ETH/DAI这样的交易对,而是WETH/DAI。前端会帮你做一层包装:你输入ETH,合约自动帮你换成WETH,然后执行交易,最后再把WETH换回ETH(如果需要的话)。

避坑指南:我曾经在写合约时,直接往Uniswap池子里转ETH,结果交易失败了。后来才发现,必须通过WETH合约来包装。记住:池子里只认ERC20,不认原生ETH。

代码层面,Uniswap V2的Pair合约有一个swap函数,支持ETH和ERC20的混合处理。如果交易涉及ETH,合约会调用WETH的depositwithdraw函数。我建议你在写集成代码时,先判断交易对是否涉及WETH,再决定是否要包装或解包。

// 处理ETH的包装和解包
if (token0 == WETH) {
    IWETH(WETH).deposit{value: amount0Out}();
    IWETH(WETH).transfer(to, amount0Out);
} else if (token1 == WETH) {
    IWETH(WETH).deposit{value: amount1Out}();
    IWETH(WETH).transfer(to, amount1Out);
}

这个设计其实很优雅。它让Uniswap V2可以统一处理所有ERC20代币,同时保留了ETH的原生特性。你想想看,如果没有WETH,那ETH和ERC20的交易逻辑就要单独写一套,代码复杂度会翻倍。

3.4 知识体系总览

为了让你更直观地理解Uniswap V2的核心机制,我画了一张流程图。它展示了交易、流动性、ETH处理这三块是怎么串联起来的。

Uniswap V2 核心机制总览 交易流程 流动性池设计 ERC20与ETH配对 x*y=k 公式 滑点计算 0.3%手续费 LP Token铸造 比例恒定 手续费分配 WETH包装 deposit/withdraw 统一ERC20接口 三者协同:交易依赖池子,池子依赖LP,ETH依赖WETH 任何一环出问题,整个系统都会崩溃

这张图把Uniswap V2的核心逻辑串起来了。交易流程是引擎,流动性池是燃料,ETH处理是适配器。三者缺一不可。

3.5 实战中的注意事项

最后,分享几个我在实际项目中踩过的坑:

  • 滑点保护:用户交易时,一定要设置滑点容忍度。我见过有人没设滑点,结果被MEV机器人夹击,亏了20%。
  • 流动性添加:首次添加流动性时,两种代币的比例决定了初始价格。如果比例跟市场价偏差太大,会被套利者瞬间搬平。
  • ETH处理:如果你在合约里直接跟Uniswap交互,记得用WETH。我曾经犯过这个错,交易一直失败,查了半天才发现是ETH没包装。

总结一下:Uniswap V2的核心就是x*y=k这个公式,加上LP Token的流动性管理,再加上WETH的桥接设计。理解这三块,你就掌握了链上自动做市商的精髓。

好了,这一章就到这里。记住,纸上得来终觉浅,绝知此事要躬行。建议你找个测试网,亲手部署一个Uniswap V2池子,跑几笔交易试试。实践出真知。


专注资料整理