CREATE2是Solidity中的一个操作码,用于创建新的智能合约。它是在以太坊的君士坦丁堡硬分叉中引入的。
CREATE2 是 Solidity 中的一个操作码,用于创建新的智能合约。它是在以太坊的君士坦丁堡硬分叉中引入的。
在介绍CREATE2之前,我们先来回顾下CREATE操作码的工作原理:
CREATE操作码是 Solidity 中最常见的合约创建方式。合约地址由发起交易的账户地址和该账户的交易次数(nonce)共同决定。合约的地址是通过哈希计算(keccak256)生成的。
合约地址 = keccak256(rlp(sender_address, nonce))
1、确定性地址: 与标准 CREATE 操作码不同,CREATE2 允许您在实际部署新合约之前计算新合约的地址。
2、基于“加盐”操作: 它使用“盐”(随机值)作为地址计算的一部分,使开发人员可以更好地控制结果地址。
3、可预测的部署: 相同的初始化代码和盐将始终产生相同的地址,无论何时何地部署。
使用 CREATE2 部署的合约的地址计算如下:
address = keccak256(0xff ++ senderAddress ++ salt ++ keccak256(initCode))[12:]
0xff
: 是常量字节,如果表示成10进制就是255。senderAddress
:是创建新合约的合约地址。salt
:创建者选择的32字节的值initCode
:是待创建合约的初始化代码我们看如下代码:
// 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
操作码在部署之前计算合约地址,并在部署时确保合约被部署到预期地址。
在去中心化金融(DeFi)以及其他智能合约应用中,create2
的特性允许开发者提前预测和控制新合约的地址。这种功能在某些场景中非常有用,尤其是当合约地址的确定性对整个系统的运作至关重要时。以下是更详细的解释和例子:
在去中心化交易所(如 Uniswap)中,每个交易对都会有一个独立的流动性池合约,这些合约的地址由交易对的两个代币决定。如果使用 create2
部署流动性池合约,开发者可以确保对于给定的代币对(例如 ETH/USDT),流动性池的合约地址在创建前就可以被确定。这使得以下场景成为可能:
salt
和初始化代码,流动性池可以在相同地址重新部署,保证系统的一致性。在某些分布式应用(DApps)中,可能会有多个模块化合约,这些合约之间通过地址直接交互。create2
可以确保这些模块的合约地址在开发阶段就已知,从而简化合约之间的依赖管理。例如:
create2
部署合约的情况下,调用方合约可以在开发时就知道被调用合约的地址,从而避免在运行时传递不必要的参数。salt
部署新版本的合约,旧的模块地址可以指向新部署的合约,从而简化了升级过程。在某些情况下,预言机系统需要与合约进行通信,并确保结果被发送到一个确定的地址。使用 create2
,可以在预言机系统中提前设定目标合约的地址:
在一些钱包或智能合约账户的实现中,用户的账户地址可能是基于某些参数(如用户的公钥)生成的。create2
允许在部署这些账户合约之前,预先计算出这些地址:
create2
允许用户通过相同的输入恢复他们的钱包。在多方签名(multi-signature)和托管合约(custodial contracts)的应用中,使用 create2
可以确保合约的地址是预先确定的,从而在合约实际部署之前就可以依赖它进行多方协调:
create2
的可预测性使得开发者能够在合约部署之前确定合约地址,这在 DeFi 和智能合约系统中具有重要意义。它使得系统设计更为灵活和透明,尤其在需要地址确定性的场景下,create2
提供了强大的工具来管理合约的部署和交互。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!