{"content":{"title":"Create2 创建合约、预测合约地址，看这一篇就够了","body":"### Target 合约 （本文使用 create2 创建这个合约）\r\n\r\n```solidity\r\ncontract Target {\r\n    address public immutable owner;\r\n    uint256 public count;\r\n\r\n    constructor(address _owner, uint256 _count) {\r\n        owner = _owner;\r\n        count = _count;\r\n    }\r\n\r\n    function test() public {\r\n        require(msg.sender == owner, \"only owner\");\r\n        count = 100;\r\n    }\r\n}\r\n```\r\n\r\n这里故意给Target 合约设置 2 个构造函数参数：\\_owner 和 \\_count，目的是完整演示Create2创建合约的方法。下面需要传这个两个参数创建合约。\r\n\r\n### Create2 工厂合约\r\n\r\n> create2 有两种方式，这里一一列出\r\n\r\n1. create2 第一种创建合约的方法，这是最新的，旧版需要汇编\r\n\r\n```\r\n// 以下的各个方法都在 Factory 合约内，为了突出重点，下面的方法不再给出Factory{}包裹\r\ncontract Factory {\r\n    function deploy(address _owner, uint256 _count, bytes32 _salt) \r\n        public \r\n        returns (address) \r\n    {\r\n        return address(new Target{salt: _salt}(_owner, _count));\r\n    }\r\n}\r\n```\r\n\r\n2. create2 第二种创建合约的方法，旧版汇编的方式\r\n\r\n```\r\n// @_bytecode Target合约字节码，下面有方法可以方便获取\r\n// @_salt 盐值，下面有公共方法可以获取盐值\r\nfunction deployAssembly(bytes memory _bytecode, bytes32 _salt) public returns (address addr) {\r\n    assembly {\r\n        addr := create2(\r\n            0,\r\n            add(_bytecode, 32),\r\n            mload(_bytecode),\r\n            _salt\r\n        )\r\n\r\n        if iszero(extcodesize(addr)) {\r\n            revert(0, 0)\r\n        }\r\n    }\r\n}\r\n```\r\n\r\n3. 第 2 步 Target 合约的字节码，可以抽象出一个公共方法来获取合约字节码\r\n\r\n```\r\n// 要部署合约的字节码, _owner 和 _count 是构造函数的参数\r\nfunction getBytecode(address _owner, uint256 _count) public pure returns (bytes memory) {\r\n    bytes memory bytecode = type(Target).creationCode;\r\n    return abi.encodePacked(bytecode, abi.encode(_owner, _count));\r\n}\r\n```\r\n\r\n4. 预测 Target 合约地址\r\n\r\n```\r\nfunction getAddress(bytes memory _bytecode, bytes32 _salt) public view returns (address) {\r\n    bytes32 addrHash = keccak256(abi.encodePacked(\r\n        bytes1(0xff), // 这里可以写 hex'ff'  不能写 \"0xff\"\r\n        address(this), // 这里是调用create2的合约，在本文中就是当前的Factory合约\r\n        _salt, // 盐值\r\n        keccak256(_bytecode) // 这里可以提前进行 keccak256 后写到这里，写法是 bytes32(0xABC...) 或 hex'ABC...'，不能写 \"0xABC...\"\r\n    ));\r\n    // 将最后 20 个字节的哈希值转换为地址\r\n    return address(uint160(uint(addrHash)));\r\n}\r\n```\r\n\r\n5. 公共方法\r\n\r\n```\r\n// 生成盐值，盐值可以是任意值，也可以是地址，字符串等，这里只是个例子\r\nfunction hash(uint256 n) public pure returns (bytes32) {\r\n    return keccak256(abi.encode(n));\r\n}\r\n\r\n// 生成合约字节码的hash，用于提前hash字节码，通过参数传递给预测地址的方法\r\n// 预测地址的时候，除了上面 getAddress 方法传递字节码以外，还可以将字节码hash后传入，这种情况要注意写法，上面getAddress方法每个参数有说明，不再赘述\r\nfunction hash(bytes memory bytecode) public pure returns (bytes32) {\r\n    return keccak256(bytecode);\r\n}\r\n```"},"author":{"user":"https://learnblockchain.cn/people/2540","address":"0x2b624faC1616D08684Cf1d21793c2f39CC1895a0"},"history":null,"timestamp":1687006343,"version":1}