乐于分享
好东西不私藏

DeFi 源码逐行读 #4 | 读懂 UniswapV2 迁移器:Migrator.sol

DeFi 源码逐行读 #4 | 读懂 UniswapV2 迁移器:Migrator.sol

朋友们大家好,之前三篇内容我们一起看完了core库里的三段代码,今天继续看periphery库,核心有3个文件,分别是migrator、router01和router02。

合约背景与关键库引入

migrator的本意是迁移的意思,这个文件的主要目的就是帮助用户将流动性从Uniswap V1 迁移到 V2。

这已经是6年前的项目了,指定的编译器版本是0.6.6

引入了几个库,第一个是TransferHelper,从名字大致可以推断这是一个协助做transfer的库,从注释可以了解到这个库是为了兼容不同的erc20token,在外边包了一层返回结果,使其总能标准化地返回true/false。

接下来的几个库都是在同一个文件夹下。

状态变量与构造函数

合约名叫UniswapV2Migrator继承自接口IUniswapV2Migrator。定义了两个immutable的变量,之前我们提到过immutable的变量一经设定不能修改,可以在构造函数中设定初始值。

在构造函数中,通过入参分别把地址 _factoryV1和地址 _router,赋值给了factoryV1和router,将地址转换为对应的接口类型。

receive函数的设计权衡

第22行,这是一个receive函数,用于单纯接受ETH。

Migrator的合约的主要功能是把流动性从V1迁移到V2,第一步从v1交易对中移除流动性,拿到ETH和代币,然后再把这些资产存入V2。

在这个过程中合约需要接受ETH,按理说应该做一些限制,比如只接收来自V1router和exchange发送的代币。但如果要这样做就要去校验发送代币的地址,需要额外的gas费用,而且这是一个完成迁移任务的合约不会额外产生收益。

所以干脆不做检查了,接受一切来源的ETH转账,风险是如果有人误转钱进来就会被锁死因为没有提款功能。但风险是可以被承受的,毕竟对于合约本身来说没啥损失。

migrate函数逐行解析

24行,函数migrate,这是迁移函数的主体。

输入是5个参数,从V1迁移到V2的token地址,在V2上用户可以接受的最少token数量amountTokenMin,在V2上用户可以接受的最少ETH数量amountETHMin,接收V2LP的to地址,交易截止时间deadline。在external外部函数之后,还有一个关键字是overrideoverride是重写的意思,在Solidity0.6以上,重写父合约或者接口中声明的函数,强制要带上override关键字,更加可读好理解。

我们逐行来看迁移函数是如何工作的。

第一步,对应28行,找到需要迁移token在V1上的交易对合约地址,把地址存在exchangeV1中;

第二步,对应29行,获取V1交易对地址内的LP余额,存在liquidityV1中;

第三步,对应30行,把liquidityV1的余额转到迁移器中,这里是require判定和transferFrom嵌套在一起,转账不成功会返回'TRANSFER_FROM_FAILED'。

第四步,对应31行,移除V1流动性,把ETH和代币取回,这里用的是removeLiquidity,取回LP余额liquidityV1对应的代币和ETH。

第五步,对应32行,授权router路由合约amountTokenV1个代币,approve函数我们在V2中见过类似的。

第六步,router路由合约在V2上添加流动性,把从V1中取出的ETH和代币注入V2,数额写入amountTokenV2和amountETHV2。

41至46行在处理剩余的代币和重置授权额度。

由于V1和V2的定价模型不同,V1中的代币对数量关系和V2的不同,因此在添加流动性时,会按 V2 当前价格比例计算最优存入量,总会有一个代币刚好用完,另一个可能有剩余,剩余的部分会返回给用户。在退还之前会把授权额度重置为0,在注释里有一句话:be a good blockchain citizen, reset allowance to 0,做区块链的好公民,避免恶意攻击。

这个文件非常短,并且是一次性的迁移处理工作,用户迁移完成后就不必再调用,下次我们将继续看V2中的router文件。