最近在学习UniswapV2,今天尝试将UniswapV2的源码在本地编译和测试,过程中遇到了一个关于pair地址的问题,在此记录一下发现原因并解决的全过程。
最近在学习 Uniswap V2,今天尝试将 Uniswap V2 的源码在本地编译和测试,过程中遇到了一个关于 pair 地址的问题,在此记录一下发现原因并解决的全过程。
由于 Uniswap V2 是 4 年前发布的,当时的 solidity 版本较低,和现在主流版本有较大差异,为了在本地使用 0.8 以上的版本,需要对 Uniswap V2 的源码不兼容的地方进行一系列修改。修改完成并 build 通过后,就可以开始测试了。但是在测试的过程中,发现每个测试用例都报错,仔细排查后,发现是 UniswapV2Factory.sol
中getPair
方法获取的 pair 地址和 UniswapV2Library.sol
中的pairFor
方法计算的 pair 地址不一致导致的。按理说,同一个 factory 创建的关于同一对 token 的 pair,地址肯定是一样的。那问题出在哪呢?
如下图所示,UniswapV2Factory.sol
中有个createPair
方法,它通过 create2
方法创建了关于 token0 和 token1 的 pair 合约,然后将该合约地址保存到了名为 getPair
的 mapping 中。我们可以通过factory.getPair(token0Address, token1Address)
获取到 pair 地址:
而 UniswapV2Library.sol
中通过pairFor
方法获取的 pair 地址,是纯计算得来的。其利用了 create2
的特性:新地址 = hash(0xff ++ senderAddress ++ salt ++ hash(bytecode))
。只要知道创建者地址(即 factory)、salt(即 token0 和 token1 的 hash)、bytecode(即 UniswapV2Pair
的 字节码),就能计算得 token0 和 token1 的 pair 地址:
问题就出在 bytecode
上。Uniswap V2 在写 pairFor
方法时,为了节约 gas,直接给出了 bytecode
的 hash 值,即上图中的 hex'96e8ac...'
。这串 hash 的计算方式为:
bytes memory bytecode = type(UniswapV2Pair).creationCode;
bytes32 hash = keccak256(abi.encodePacked(bytecode));
但是,由于 solidity 版本、源代码、优化次数等会有差异,我们的本地代码和主网上 Uniswap V2 的代码大概率是不完全一样的,这就导致计算的 hash 值会不一样。而 pairFor
里用的是 Uniswap V2 写死的 hash 值,计算出来的地址肯定就和我们真实创建的地址不一样了。
我们需要自己计算出我们本地 UniswapV2Pair
代码的 hash 值,然后对 pairFor
方法中的那串 hash 进行替换。
如上图所示,在UniswapV2Factory.sol
加上这两句,运行后在控制台拿到打印的 hash 结果,替换掉 UniswapV2Library.sol
的 pairFor
函数里的那一串 hash 值(注意不要带0x
),这样,pairFor
计算的 pair 地址就和真实创建的地址一致了。
很多优秀的链上项目,在其代码优化过程中,可能会使用一些“黑科技”,以达到提高运行效率、节约 gas 成本等效果。在我们学习源码的过程中,需要特别注意这些优化之处,一方面是学习他们的优化技巧和背后的思想,另一方面也要小心不要“踩坑”。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!