Solidity

為什麼 create2 預測地址不等於實際地址?

  • March 16, 2022

由於某種原因,當創建的合約接受它的建構子參數時,預測/預先計算的地址不等於實際地址address,但是當建構子參數為uint. 我已經在下面的腳本中展示了這兩個範例。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract Factory {

   // example 1:
   function createTest1(uint number1, uint number2) public {

       bytes32 salt = keccak256(abi.encodePacked(number1, number2));

       // source: https://docs.soliditylang.org/en/latest/control-structures.html#salted-contract-creations-create2
       address predictedAddress = address(uint160(uint(keccak256(abi.encodePacked(
           bytes1(0xff),
           address(this),
           salt,
           keccak256(abi.encodePacked(
               type(Test1).creationCode,
               number1,
               number2
           ))
       )))));

       Test1 test1 = new Test1{salt: salt}(number1, number2);
       require(address(test1) == predictedAddress); // Does NOT revert
   }

   // example 2:
   function createTest2(address address1, address address2) public {

       bytes32 salt = keccak256(abi.encodePacked(address1, address2));

       address predictedAddress = address(uint160(uint(keccak256(abi.encodePacked(
           bytes1(0xff),
           address(this),
           salt,
           keccak256(abi.encodePacked(
               type(Test2).creationCode,
               address1,
               address2
           ))
       )))));

       Test2 test2 = new Test2{salt: salt}(address1, address2);
       require(address(test2) == predictedAddress); // Does revert
   }    
}

contract Test1 {

   uint public number1;
   uint public number2;

   constructor(uint _number1, uint _number2) {
       number1 = _number1;
       number2 = _number2;
   }
}

contract Test2 {

   address public address1;
   address public address2;

   constructor(address _address1, address _address2) {
       address1 = _address1;
       address2 = _address2;
   }
}

由於某種原因,當創建的合約接受其建構子參數的地址時,預測/預先計算的地址不等於實際地址,但是當建構子參數為 uint 時很好

那是因為您沒有abi.encode在參數上使用,而是abi.encodePacked. 將元素填充到abi.encode32 字節時,如果您的參數已經是 32 字節長(如 uint),則沒有區別。在缺少填充的情況下address會導致不同的輸出。

因此,更通用和正確的版本是:

 address predictedAddress = address(uint160(uint(keccak256(abi.encodePacked(
       bytes1(0xff),
       address(this),
       salt,
       keccak256(abi.encodePacked(
           type(Test2).creationCode,
           abi.encode(address1, address2) // <-- abi.encode the parameters
       ))
   )))));

我設法讓它工作,我所要做的就是將地址轉換為 uint

是的,因為您隱含地呼叫abi.encode了參數,因為它的abi.encode(address_variable)輸出完全相同,因為uint(uint160(address1))它只是一種特殊情況,不適用於長度超過 32 字節的類型,而 usingabi.encode將始終有效,因為它始終會產生確切的部署字節碼 + abi。編碼參數。

引用自:https://ethereum.stackexchange.com/questions/123967