1. create
1.1 简介
概念:
智能合约可以由其他合约和普通账户利用 create
操作码创建。在这两种情况下,新合约的地址都以相同的方式计算:创建者的地址(通常为部署的钱包地址或者合约地址)和nonce
(该地址发送交易的总数,对于合约账户是创建的合约总数,每创建一个合约nonce+1))的哈希。
计算式:
新地址 = hash(创建者地址, nonce)
创建者地址不会变,但nonce可能会随时间而改变,因此create
创建的合约地址不好预测。
2.create2
2.1 简介
create2
的用法和create
有点类型,同样是new一个合约,并传入新合约构造函数所需的参数,不同点在于要多传入一个salt(盐)参数:
1
| Contract X = new Contract{salt: _salt, value: _value}(params);
|
解读:Contract是要创建的合约名,X是合约对象(address),_salt是指定的盐;如果构造函数是payable,可以在创建时传入_value数量的ETH,params是新合约构造函数的参数。
2.2 计算create2地址
注:如果需要进行合约交互,且待部署合约的构造器带参,则需要对待传参数进行abi.encode()打包,再使用 abi.encodePacked()对bytecode和abi.encode()的结果进行紧打包
如代码所示:
1 2 3 4 5 6
| function getBycode() internal pure returns(bytes memory result) { // type().creationCode 写死 bytes memory temp = type(Student).creationCode; // 这里的参数值我写死了(Student 构造器的参数) result = abi.encodePacked(temp, abi.encode(1,"biyou")); }
|
❗❗❗abi.encodePacked(temp, abi.encode(1,"biyou"))
外层的abi.encodePacked()将不会对abi.encode(1,"biyou")
的值进行紧打包了,举例如下:
代码1:
1 2 3 4 5 6 7
| // 非紧打包 -- 涉及了合约构造函数的参数 function encodeBycode(uint id, string memory name) external pure returns(bytes memory result) { bytes memory temp = type(Student).creationCode; // 将字节码和参数进行紧打包 result = abi.encodePacked(temp, abi.encode(id,name)); }
|
结果1:

代码2:
1 2 3 4 5 6
| // 非紧打包 -- 涉及了合约构造函数的参数 function encodeBycode1(uint id, string memory name) external pure returns(bytes memory result) { bytes memory temp = type(Student).creationCode; // 将字节码和参数进行非紧打包 result = abi.encode(temp, abi.encode(id,name)); }
|
结果2:

测试合约;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| // SPDX-License-Identifier: MIT pragma solidity ^0.8.0;
// 一个用于测试的合约 contract Student { uint256 public id ; string public name;
constructor(uint256 _id, string memory _name) { id = _id; name = _name; }
function getId() external view returns(uint256) { return id; }
function getName() external view returns(string memory) { return name; } }
|
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| // SPDX-License-Identifier: MIT pragma solidity ^0.8.0;
// create2合约 contract create2 {
// 使用create2计算地址,我只需要改变salt(string类型)的值就可以就算出不一样的地址 function create2Address(string memory salt) external view returns(address result){ result = address( uint160( uint( keccak256(abi.encodePacked( // bytes1(0xff), uint8(0xff), // 固定的一个字节的常数 0xff address(this), //当前合约地址(create2合约地址) keccak256(abi.encodePacked(salt)), //对盐进行紧打包后再进行hash keccak256(getBycode()) //获取要计算的合约的字节码 再进行hash )) ) ) ); } // 使用create2计算地址,传入代部署合约的字节码,我只需要改变salt(string类型)的值就可以就算出不一样的地址 function create2Address(bytes memory bycode,string memory salt) external view returns(address result){ result = address( uint160( uint( keccak256(abi.encodePacked( // bytes1(0xff), uint8(0xff), // 固定的一个字节的常数 0xff address(this), //当前合约地址(create2合约地址) keccak256(abi.encodePacked(salt)), //对盐进行紧打包 keccak256(bycode) //要计算的合约的字节码 )) ) ) ); }
// 获取部署合约的字节码(内部合约) function getBycode() internal pure returns(bytes memory result) { // type().creationCode 写死 bytes memory temp = type(Student).creationCode; // 这里的参数值我写死了(Student 构造器的参数) result = abi.encodePacked(temp, abi.encode(1,"biyou")); }
// 通过create2部署合约,new的形式 function makeSudentByCreate2(string memory _salt, uint256 id, string memory name) external returns(Student student){ student = new Student{salt: keccak256(abi.encodePacked(_salt))}(id, name); } }
|
一个灵活一点的代码:
1 2 3 4 5 6 7 8 9 10 11
| function getCreate2Address(address addToDeploy, uint256 salt, bytes memory bycode) external pure returns(address result) { result = address(uint160(uint( keccak256(abi.encodePacked( uint8(0xff), // 固定的一个字节的常数 0xff addToDeploy, //当前合约地址(create2合约地址) keccak256(abi.encodePacked(salt)), //对盐进行紧打包 keccak256(bycode) //要计算的合约的字节码 )) ))); } }
|
2.3 使用内联汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| // /* 使用内联汇编自带的 create2() */ function create2NewContractByInline(uint salt) public returns (address) { bytes memory code = type(test).creationCode; // 如果待部署合约的构造器中有参数,则使用如下方式进行获取code // bytes memory code = abi.encodePacked(type().creationCode, abi.encode()); bytes32 _salt = keccak256(abi.encodePacked(salt));
address result; assembly { result := create2( 0, // value 表示要向新合约发送的以太币数量 add(code, 32), // 表示跳过数组长度 mload(code), // 读取code的长度 _salt // 盐 ) } return result; }
|
2.4 暴破求解create2地址
————计算出 将来的地址合约含有某个字段
操作方法 ====》计算指定合约地址(含某个字段)
相关链接