今天來介紹部署合約 create 和 create2 相關的內容。
create
和 create2
都是花費 32,000 gascreate(value, offset, size)
create2(value, offset, size, salt)
value
: 送到新合約的 ETH 數量offset
: init code 的起始位置size
: init code 的大小salt
: 32 bytes 的鹽
from evm.codes:
minimum_word_size = (size + 31) / 32
init_code_cost = 2 * minimum_word_size
hash_cost = 6 * minimum_word_size
code_deposit_cost = 200 * deployed_code_size
static_gas = 32000
dynamic_gas = init_code_cost + hash_cost + memory_expansion_cost + deployment_code_execution_cost + code_deposit_cost
透過交易來部署合約,to
是空值,value
會將 ETH 打進合約,data
是由 solidity 編譯出來的 bytecode,後面加上 ABI 編譯後的 constructor arguments。
{
to: null,
value: 0,
data: concat([bytecode, encodedConstructorArgs])
}
address = keccak256(rlp_encode(creator_account, creator_account_nonce))[12:]
ethers v6 implement
import { getAddress, JsonRpcProvider } from 'ethers'
import { dataSlice, keccak256 } from 'ethers'
import RLP from 'rlp'
import 'dotenv/config'
const provider = new JsonRpcProvider(process.env.sepolia)
const creator = getAddress('0xd78B5013757Ea4A7841811eF770711e6248dC282')
const nonce = await provider.getTransactionCount(creator)
const address = dataSlice(keccak256(RLP.encode([creator, nonce])), 12)
console.log(address)
++
可以用 abi.encodePacked
來理解。address = keccak256(0xff ++ address ++ salt ++ keccak256(init_code))[12:]
init_code = creationCode ++ abi.encode(...constructor-args)
code
是來自執行 init code 後的 return data。PS: init code 只是代稱,在 ERC-4337 UserOp 內的 initCode 跟這邊完全不一樣,它是由 factory address ++ calldata to factory 組成。
使用 solidity 操作 create2 部署合約
function deploy(address _owner, bytes32 _salt) public payable returns (address) {
return address(new TestContract{salt: _salt}(_owner));
}
使用 assembly 操作 create2 部署合約
function deploy(bytes memory bytecode, uint256 _salt) public payable {
address addr;
assembly {
addr :=
create2(
callvalue(), // wei sent with current call
add(bytecode, 0x20), // offset
mload(bytecode), // size
_salt // Salt from function arguments
)
if iszero(extcodesize(addr)) { revert(0, 0) }
}
emit Deployed(addr, _salt);
}
解析:
mload(bytecode)
將 memory 中 bytecode 位置開始的前 32 bytes 載入至 stack。因為 bytecode 是 dynamic type,前 32 bytes 是它的 length,之後才是真的 bytecode。因此載入 bytecode length 至 stack,就是 create2 第三個參數所需要的 size。add(bytecode, 0x20)
是將 bytecode 的起始位置加上 32,因此會跳過 length,在 stack 上方加入 bytecode 真正開始的位置,符合 create2 第二個參數 offset。EOA 和合約都有以下四個屬性欄位,但 EOA 只有 nonce 和 balance 有值。
透過 JSON RPC eth_getTransactionCount
可以獲得某地址當前的 nonce。