2025.09.24
CREATE3 アドレス予測の安定化
こんにちは。次世代システム研究室のT.M です。
はじめに
Ethereum をはじめとする EVM チェーンでは、新しいスマートコントラクトをデプロイする際に アドレスがどのように決まるか が重要です。アドレス決定方式は、コントラクト間の相互作用やウォレットからの参照に直接影響を与えます。
これまでに EVM には
-
CREATE
-
CREATE2
の 2 つの仕組みが存在しましたが、それぞれに課題がありました。
CREATE3 ライブラリは、これらの課題を解決し、より直感的かつ予測可能なコントラクトデプロイを可能にします。
この記事では、CREATE → CREATE2 → CREATE3 の進化をたどり、その仕組みとメリットを解説します。
CREATE の仕組み
EVM における最も基本的なデプロイ方法が CREATE 命令です。
CREATE
で生成されるコントラクトのアドレスは以下の式で決まります:
keccak256(rlp([sender, nonce]))[12:]
-
sender
… コントラクトをデプロイするアカウントアドレス -
nonce
… デプロイ元アカウントのトランザクション数
このため、アドレスは「デプロイ元のアドレス」と「その時点の nonce」に依存します。
問題点
-
事前にアドレスを計算するには「nonce の正確な値」を知る必要がある。
-
1 回でもデプロイに失敗すると nonce が進み、アドレスがズレてしまう。
-
つまり「確実に同じアドレスにデプロイする」ことが難しい。
CREATE2 の登場とメリット
こうした問題を解決するために、EIP-1014 (CREATE2) が導入されました。
CREATE2 では、アドレスは以下で決定されます:
keccak256(0xff ++ sender ++ salt ++ keccak256(init_code))[12:]
-
sender
… デプロイ元アドレス -
salt
… 任意に指定できる値 -
init_code
… コントラクトの初期化コード
これにより、事前にコントラクトアドレスを「先読み」できるようになりました。
Uniswap V2 など、多くのプロジェクトが CREATE2 を活用しています。
メリット
-
同じ
sender
・salt
・init_code
なら、必ず同じアドレスになる。 -
コントラクトがデプロイされる前でも、アドレスを予測可能。
問題点
-
init_code
が異なると、別のアドレスになる。
CREATE3 のコンセプト
こうした問題を解決するのが CREATE3 です。
CREATE3 は EVM 命令として存在するわけではなく、Solidity/ライブラリによって実現されたパターンです。
代表的な実装は Solmate の CREATE3 ライブラリ です。
CREATE3 の仕組み
CREATE3 は「中間のデプロイアカウント(デプロイヤー)」を活用して、安定したアドレス生成を実現します。
アドレス決定の流れ
-
CREATE2
を使って「一時的なデプロイアカウント」を生成する-
ここで
salt
を利用する
-
-
その一時デプロイアカウントが、通常の
CREATE
を使って目的のコントラクトをデプロイする-
init_code
が渡される
-
これにより、最終的なコントラクトアドレスは以下で決まります:
address = keccak256(rlp([deployer, 1]))[12:]
ここで deployer
はステップ1で生成された「一時アカウント」なので、同じ salt
と init_code
なら必ず同じアドレスが導出されます。deployer
のinit_code は固定なので、deployer
は同じアドレスでデプロイすることができます。
CREATE
でデプロイをしたコントラクトアドレスは、init_code
によらずにアドレスが決まります。つまり、CREATE3
でデプロイされるコントラクトのアドレスは、init_code
によらずに同じアドレスでデプロイすることができます。
ライブラリコード解説
以下は Solmate の CREATE3.sol の一部抜粋です:
function deploy( bytes32 salt, bytes memory creationCode, uint256 value ) internal returns (address deployed) { bytes memory proxyChildBytecode = PROXY_BYTECODE; address proxy; /// @solidity memory-safe-assembly assembly { // Deploy a new contract with our pre-made bytecode via CREATE2. // We start 32 bytes into the code to avoid copying the byte length. proxy := create2(0, add(proxyChildBytecode, 32), mload(proxyChildBytecode), salt) } require(proxy != address(0), "DEPLOYMENT_FAILED"); deployed = getDeployed(salt); (bool success, ) = proxy.call{value: value}(creationCode); require(success && deployed.code.length != 0, "INITIALIZATION_FAILED"); }
proxy
… デプロイヤーコントラクト
まとめ
-
CREATE … nonce に依存するためアドレスの先読みが困難
-
CREATE2 … salt と init_code で先読み可能
-
CREATE3 … 中間アカウントを用いることで、アドレスを安定化しつつ再利用を実現
CREATE2のおかげでアドレスの先読みができるようになりました。さらにCREATE3では、init_codeによらずアドレスの先読みができるようになりました。opcodeではなく、solidityのライブラリであり、また、ガスの消費量が大きいというデメリットがありますが、コントラクトのコードが変更になっても、事前に先読みしたアドレスが使えるというメリットがあります。コントラクトの管理に役立つライブラリです。
次世代システム研究室では、グループ全体のインテグレーションを支援してくれるアーキテクトを募集しています。インフラ設計、構築経験者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ募集職種一覧からご応募をお願いします。
皆さんのご応募をお待ちしています。
グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。
Follow @GMO_RD