{"author":{"address":"0xCE733Fa2f9dd9Aee9353248fB0F237b0522af73E","user":"https://learnblockchain.cn/people/19059"},"content":{"body":"最近在学习 Uniswap V2，今天尝试将 Uniswap V2 的源码在本地编译和测试，过程中遇到了一个关于 pair 地址的问题，在此记录一下发现原因并解决的全过程。\r\n\r\n## 问题背景\r\n由于 Uniswap V2 是 4 年前发布的，当时的 solidity 版本较低，和现在主流版本有较大差异，为了在本地使用 0.8 以上的版本，需要对 Uniswap V2 的源码不兼容的地方进行一系列修改。修改完成并 build 通过后，就可以开始测试了。但是在测试的过程中，发现每个测试用例都报错，仔细排查后，发现是 `UniswapV2Factory.sol` 中`getPair`方法获取的 pair 地址和 `UniswapV2Library.sol` 中的`pairFor` 方法计算的 pair 地址不一致导致的。按理说，同一个 factory 创建的关于同一对 token 的 pair，地址肯定是一样的。那问题出在哪呢？\r\n\r\n## 原因分析\r\n如下图所示，`UniswapV2Factory.sol` 中有个`createPair`方法，它通过 `create2`方法创建了关于 token0 和 token1 的 pair 合约，然后将该合约地址保存到了名为 `getPair` 的 mapping 中。我们可以通过`factory.getPair(token0Address, token1Address)` 获取到 pair 地址：\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/07/rdU8niMT66a903d5caba7.png)\r\n\r\n而 `UniswapV2Library.sol` 中通过`pairFor` 方法获取的 pair 地址，是纯计算得来的。其利用了 `create2`的特性：新地址 = `hash(0xff ++ senderAddress ++ salt ++ hash(bytecode))`。只要知道创建者地址（即 factory）、salt（即 token0 和 token1 的 hash）、bytecode（即 `UniswapV2Pair` 的 字节码），就能计算得 token0 和 token1 的 pair 地址：\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/07/PzxUEsGt66a9046cf02d2.png)\r\n\r\n问题就出在 `bytecode` 上。Uniswap V2 在写 `pairFor` 方法时，为了节约 gas，直接给出了 `bytecode` 的 hash 值，即上图中的 `hex'96e8ac...'` 。这串 hash 的计算方式为：\r\n\r\n\r\n```solidity\r\nbytes memory bytecode = type(UniswapV2Pair).creationCode;\r\nbytes32 hash = keccak256(abi.encodePacked(bytecode));\r\n```\r\n\r\n但是，由于 solidity 版本、源代码、优化次数等会有差异，我们的本地代码和主网上 Uniswap V2 的代码大概率是不完全一样的，这就导致计算的 hash 值会不一样。而 `pairFor` 里用的是 Uniswap V2 写死的 hash 值，计算出来的地址肯定就和我们真实创建的地址不一样了。\r\n\r\n## 解决办法\r\n我们需要自己计算出我们本地 `UniswapV2Pair` 代码的 hash 值，然后对 `pairFor` 方法中的那串 hash 进行替换。\r\n\r\n![image.png](https://img.learnblockchain.cn/attachments/2024/07/mSq54P6g66a905a594eee.png)\r\n\r\n如上图所示，在`UniswapV2Factory.sol`加上这两句，运行后在控制台拿到打印的 hash 结果，替换掉 `UniswapV2Library.sol` 的 `pairFor` 函数里的那一串 hash 值（注意不要带`0x`），这样，`pairFor` 计算的 pair 地址就和真实创建的地址一致了。\r\n\r\n很多优秀的链上项目，在其代码优化过程中，可能会使用一些“黑科技”，以达到提高运行效率、节约 gas 成本等效果。在我们学习源码的过程中，需要特别注意这些优化之处，一方面是学习他们的优化技巧和背后的思想，另一方面也要小心不要“踩坑”。\r\n\r\n## 扩展资料\r\n- [EIP-1014: Skinny CREATE2](https://eips.ethereum.org/EIPS/eip-1014)\r\n- [一文详解solidity中的CREATE2操作码](https://learnblockchain.cn/article/8866)\r\n- [Uniswap-v2 Factory合约分析](https://learnblockchain.cn/article/3604)","title":"本地运行 Uniswap V2，创建和计算的 pair 地址为什么不一样？"},"history":null,"timestamp":1722355006,"version":1}