基本概念:
CREATE2 是 Solidity 中的一个操作码,用于创建新的智能合约。它是在以太坊的君士坦丁堡硬分叉中引入的。
主要特性:
1、确定性地址: 与标准 CREATE 操作码不同,CREATE2 允许您在实际部署新合约之前计算新合约的地址。
2、基于“加盐”操作: 它使用“盐”(随机值)作为地址计算的一部分,使开发人员可以更好地控制结果地址。
3、可预测的部署: 相同的初始化代码和盐将始终产生相同的地址,无论何时何地部署。
工作原理:
使用 CREATE2 部署的合约的地址计算如下:
address = keccak256(0xff ++ senderAddress ++ salt ++ keccak256(initCode))[12:]
其中:
0xff: 是常量字节,如果表示成10进制就是255。senderAddress:是创建新合约的合约地址。salt:创建者选择的32字节的值initCode:是待创建合约的初始化代码
在solidity中如何使用:
我们看如下代码:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SimpleContract {
uint256 public value;
constructor(uint256 _value) {
value = _value;
}
}
contract Factory {
event Deployed(address addr);
function deploy(bytes32 salt, uint256 _value) public returns (address) {
// 合约的字节码
bytes memory bytecode = abi.encodePacked(
type(SimpleContract).creationCode,
abi.encode(_value)
);
address addr;
assembly {
// 使用 CREATE2 操作码部署合约
addr := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
// 检查合约是否部署成功
if iszero(extcodesize(addr)) {
revert(0, 0)
}
}
emit Deployed(addr);
return addr;
}
function computeAddress(bytes32 salt, bytes32 bytecodeHash) public view returns (address) {
return address(uint160(uint256(keccak256(abi.encodePacked(
bytes1(0xff),
address(this),
salt,
bytecodeHash
)))));
}
function getBytecodeHash(uint256 _value) public pure returns (bytes32) {
bytes memory bytecode = abi.encodePacked(
type(SimpleContract).creationCode,
abi.encode(_value)
);
return keccak256(bytecode);
}
}
代码解释:
SimpleContract: 一个简单的合约,带有一个状态变量 value 和一个构造函数用于初始化这个变量。
Factory: 工厂合约,包含以下几个函数:
deploy: 使用 CREATE2 操作码部署 SimpleContract 合约。
salt: 用于生成确定性地址的盐值。_value: 传递给SimpleContract构造函数的参数。- 使用
assembly代码调用CREATE2操作码,部署合约并检查部署是否成功。
computeAddress: 计算给定盐值和字节码哈希的合约地址。
salt: 用于生成确定性地址的盐值。bytecodeHash: 部署合约字节码的哈希值。
getBytecodeHash: 计算 SimpleContract 合约字节码的哈希值。
_value: 传递给SimpleContract构造函数的参数。
部署和使用步骤
1、部署 Factory 合约: 部署 Factory 合约到以太坊网络上。
2、计算 SimpleContract 合约字节码的哈希值:调用 getBytecodeHash 函数,传递 _value 参数,获取 SimpleContract 的字节码哈希值。
3、计算合约地址: 调用 computeAddress 函数,传递 salt 和前一步获取的 bytecodeHash,计算将要部署的 SimpleContract 合约的地址。
4、部署 SimpleContract 合约: 调用 deploy 函数,传递 salt 和 _value 参数,使用 CREATE2 操作码部署 SimpleContract 合约。
这里有一个细节需要注意:salt需要传递一个bytes32的类型,如果你输入一个int类型,需要转换一下。
通过上述步骤,可以使用 CREATE2 操作码在部署之前计算合约地址,并在部署时确保合约被部署到预期地址。