iT邦幫忙

2024 iThome 鐵人賽

DAY 26
0
Modern Web

web3 短篇集系列 第 26

部署合約 (create & create2)

  • 分享至 

  • xImage
  •  

今天來介紹部署合約 create 和 create2 相關的內容。

Opcodes

  • opcode createcreate2 都是花費 32,000 gas
create(value, offset, size)
create2(value, offset, size, salt)

value: 送到新合約的 ETH 數量
offset: init code 的起始位置
size: init code 的大小
salt: 32 bytes 的鹽

create2 gas calculation

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])
}

Create

  • deterministic: 一個 EOA 下次部署合約的地址是可預測的。
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)

Create2

  • ++ 可以用 abi.encodePacked 來理解。
  • solidity 編譯後的 bytecode 是由 creation code 和 runtime code 組成。creation code 是 constructor 的部分,runtime code 是 functions 的部分。編譯後的 bytecode 不會包含 constructor arguments。
address = keccak256(0xff ++ address ++ salt ++ keccak256(init_code))[12:]

init_code = creationCode ++ abi.encode(...constructor-args)
  • init code 是由 creation code 和 constructor arguments 組成。
  • 合約四個屬性其中之一的 code 是來自執行 init code 後的 return data。

PS: init code 只是代稱,在 ERC-4337 UserOp 內的 initCode 跟這邊完全不一樣,它是由 factory address ++ calldata to factory 組成。

使用 Create2

使用 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);
    }

解析:

  • 在 assembly 中,函式的參數是該參數在 memory 的起始位置
  • 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。

Nonce

EOA 和合約都有以下四個屬性欄位,但 EOA 只有 nonce 和 balance 有值。

  • nonce
  • balance (in wei)
  • hash of storage trie root
  • code

透過 JSON RPC eth_getTransactionCount 可以獲得某地址當前的 nonce。

合約的 Nonce

  • EIP-161 讓合約的 nonce 從 1 開始算,在此之前是從 0。
  • 當合約部署另一個合約時,nonce 會增加。
  • 如果是從 A 合約打到 B 合約再部署 C 合約,nonce 是加在 A 合約身上?(不確定)
  • 沒有內建方法可以在合約中取得自己或其他地址的 nonce。(也許可以靠合約自己算做到。)

Reference


上一篇
Commit-Reveal Scheme
下一篇
使用 eth_getProof 證明帳戶的歷史餘額
系列文
web3 短篇集30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言