CREATE2入門:salt・init codeからcontract addressを予測して安全にdeployする
CREATE2 addressをfactory・salt・init codeから計算し、constructor、library、metadata、proxy、clone、collision、counterfactual deploymentの境界を検証します。

CREATE2を使うと、contractをdeployする前にaddressを計算できます。ただし、「saltが同じなら同じaddress」「runtime bytecodeだけあれば計算できる」「codeがないaddressは予約済み」という理解は正しくありません。
addressを決めるのは、CREATE2を実行するfactory address、32-byte salt、init codeのhashです。init codeにはcreation bytecodeだけでなくconstructor argumentsも入り、linked library、compiler metadata、proxy initializerの設計によっても変わります。
この記事はdeterministic deployment factory、smart account、proxy / clone factoryを実装する開発者向けです。chain → factory → effective salt → creation bytecode → constructor / initializer → init-code hash → predicted address → code・nonce・balance → deployment receiptの順に固定し、予測と安全性を分けます。
検証にはSolidity 0.8.36、Foundry / Anvil 1.8.0、OpenZeppelin Contracts 5.6.1、viem 2.56.0を使いました。chain ID 31345の隔離したlocal chainだけで実行し、public RPC、mainnet fork、browser wallet、実資産は使っていません。
CREATEとCREATE2はaddressの入力が違う
通常のCREATEは、作成者addressとnonceから次のaddressを導きます。factoryが先に別contractを作るとnonceが進むため、予定addressも変わります。
CREATE = keccak256(rlp([creator, nonce]))[12:]
CREATE2はnonceの代わりに、次の固定長preimageを使います。
CREATE2 = keccak256(
0xff ++ factory ++ salt ++ keccak256(init_code)
)[12:]
EIP-1014では、0xffが1 byte、factory addressが20 bytes、saltが32 bytes、init-code hashが32 bytesです。最終hashへ渡すpreimageは合計85 bytesになります。
ここでいうfactoryは、EOAのtransaction senderではありません。CREATE2 opcodeを実行するcontractのaddressです。同じEOA、salt、init codeでも、別factoryからdeployすれば別addressになります。
init codeはruntime bytecodeではない
EIP-1014のinit_codeは、実行されてruntime bytecodeを返す一時的なcodeです。Solidityでconstructor付きcontractを作る場合は、概念上次の組み合わせになります。
bytes memory initCode = abi.encodePacked(
type(Target).creationCode,
abi.encode(initialValue, recipient)
);
bytes32 initCodeHash = keccak256(initCode);
type(Target).runtimeCodeや、deploy後にeth_getCodeで読んだcodeを代入してはいけません。constructorはstorageを初期化し、最後に別のruntime codeを返します。
local検証では、同じfactoryとsaltでconstructorのinitialValueだけを77から78へ変えました。
| constructor value | init-code hash | predicted address | runtime-code hash |
|---|---|---|---|
77 |
0x1f91…0d68 |
0x282A…7648 |
0xaa3d…fddf |
78 |
0xb8b5…caa7 |
0xe1F0…3402 |
0xaa3d…fddf |
2つのruntime-code hashは同じですが、constructor dataを含むinit-code hashは異なり、addressも異なりました。同じruntime codeになることは、同じCREATE2 addressになる条件ではありません。
Solidity手計算とviemを独立して一致させる
factory側の計算は、次のように式をそのまま実装できます。
function computeAddress(bytes32 salt, bytes32 initCodeHash)
public
view
returns (address)
{
return address(uint160(uint256(keccak256(abi.encodePacked(
bytes1(0xff),
address(this),
salt,
initCodeHash
)))));
}
TypeScript側では、creation bytecodeとABI encoded constructor argumentsを連結してからviemへ渡します。
const initCode = concatHex([
creationBytecode,
encodeAbiParameters(
parseAbiParameters('uint256, address'),
[77n, recipient],
),
])
const predicted = getContractAddress({
from: factory,
opcode: 'CREATE2',
salt,
bytecode: initCode,
})
固定したローカル検証では、Solidity factoryのview関数、85-byte preimageを組み立てるTypeScript手計算、viem getContractAddressの3経路がすべて0x282AE3Ef80fB8F5F68D934dA24201EeD1bf67648で一致しました。
1つのhelperだけを予測と検証の両方に使うと、同じencoding bugを共有して一致してしまいます。可能ならSolidityとoff-chain codeを独立実装し、init codeそのものとhashも比較します。
libraryとcompiler metadataもdeployment inputに含める
Solidityのexternal library callを含むbytecodeにはlink placeholderがあり、compile時に実library addressが埋め込まれます。したがって、library addressが違えばcreation bytecode、init-code hash、CREATE2 addressも変わります。
同じsourceを2つのlibrary addressでlinkした固定結果です。
| linked library | bytecode | init-code hash | predicted address |
|---|---|---|---|
0x11…11 |
301 bytes | 0xf670…34d1 |
0xE655…fca4 |
0x22…22 |
301 bytes | 0x924a…c941 |
0x9c5F…2418 |
Solidity metadataも見落としやすい入力です。metadataにはcompiler version、settings、source hash、library情報などが入り、変更によってbytecodeが変わり得ます。同じsourceとSolidity 0.8.36で、metadata settingだけを変えた結果です。
| metadata setting | creation bytecode | init-code hash | predicted address |
|---|---|---|---|
| CBORなし | 259 bytes | 0xb601…d38b |
0x1116…6ac8 |
| IPFS metadata | 313 bytes | 0xe731…0b18 |
0x8754…21aF |
sourceが同じという説明だけでは、再現可能な構築になりません。solc version、optimizer、runs、via-IR、EVM version、metadata、remapping、library addressをmanifestへ残します。bytecode再構築の詳しい手順は、runtime bytecodeを同じcompiler条件で再構築する記事で確認できます。
saltは値ではなく、導出規則まで固定する
bytes32 saltに何を入れるかはfactoryの設計です。permissionless factoryがuser入力をそのまま使い、init codeが全callerで同じなら、別callerが先に同じaddressへdeployできます。
local例では、ownerをconstructorへbindせず、deploy後のinitializeで決めるcontractを使いました。最初のcallerがraw saltでdeployとinitializeを行うと、次のcallerは同じaddressとのcollisionでdeployできません。
| 順番 | caller | result |
|---|---|---|
| 1 | 0x7099…79C8 |
0x9384…83A4へdeployしownerになる |
| 2 | 0xf39F…2266 |
同じaddressとのcollisionでzero address |
ただし、これをすべて「salt front-runningでownershipが盗まれる」と一般化してはいけません。ownerや設定値がconstructor argumentsへ入っていれば、攻撃者が別ownerでdeployしたinit codeはhashが違うため別addressになります。攻撃者が被害者と同じinit codeを先に使える場合も、constructorが被害者ownerを固定していれば主な結果は占有によるDoSです。
対策の1つは、user saltをcaller namespaceへ入れる方法です。
bytes32 effectiveSalt = keccak256(abi.encode(msg.sender, userSalt));
固定例では、同じuser saltでも2つのcallerの予測addressは0xcb5f…7501と0x3437…78b8へ分かれました。ただし、namespacingだけでfactoryが安全になるわけではありません。誰がdeployできるか、init codeを誰が選べるか、ownerがどこでbindされるか、initializeが同じtransactionで完了するかも確認します。
CREATEとCREATE2の違いを調査の順番へ落とす
予測が合わないときは、表示されたaddressを何度も見比べるより、入力を左から固定します。
| 確認順 | 残す値 | 典型的なずれ |
|---|---|---|
| 1 | chain ID、block、RPC | 別networkを見ている |
| 2 | factory address、code hash、owner / authority | 別factory、proxy先、権限変更 |
| 3 | raw user salt、effective salt、導出式 | padding、caller namespace、文字列hash |
| 4 | creation bytecode | runtime bytecodeを使用、別artifact |
| 5 | constructor ABI data | 型、順序、address、bytes encoding |
| 6 | solc / optimizer / EVM / metadata | 構築環境差 |
| 7 | library addresses、implementation | link先やproxy先の差 |
| 8 | init-code hash、expected address | off-chain / on-chain計算の不一致 |
chain ID自体はEIP-1014の式へ直接入りません。それでもchainを最初に残すのは、factoryがそのchainに同じaddress・同じcode・同じauthorityで存在するとは限らないためです。
collision、revert、wrong factoryを別failureにする
predicted addressが正しくても、deploymentは失敗します。ローカル検証では次を別々に再現しました。
| failure | transaction後の観測 | 原因 |
|---|---|---|
| existing code collision | CREATE2 returnはzero address | 同一factory / salt / init codeが既にdeploy済み |
| constructor revert | returnはzero、code 0 bytes、nonce 0 | init code実行中にrevert |
| wrong factory | 予測0x282A…7648、実際のfactory側0x838E…972b |
opcode実行addressが異なる |
| wrong constructor args | 別init-code hash、別address | ABI dataが異なる |
| wrong library / metadata | 別creation bytecode、別address | compiler inputが異なる |
EIP-1014は、destinationのnonceがnonzeroまたはcodeが空でない場合にcreationが失敗するcollision ruleを説明しています。同じsaltでもinit codeが違えば通常は別addressです。「saltを一度使ったらfactory全体で永久に使用禁止」という規則ではありません。
constructor revertもcollisionと同じzero addressへ見える低level factoryがあります。factoryはzero addressを必ずrejectし、可能ならrevert reason、receipt status、trace、post-stateを残します。
SELFDESTRUCT後の再deployはforkとtransaction境界が追加で必要です。EIP-6780後のcode残存とCREATE2再deployでは、Shanghai / Cancunを別local ruleとして比較しています。
proxy initializerはどこで実行するかでaddressへの影響が変わる
ERC1967Proxyは、constructorへimplementation addressとinitializer dataを渡し、そのdataをimplementationへdelegatecallできます。この形ではinitializer dataがproxyのconstructor argumentsに含まれるため、init codeの一部です。
固定例では、同じimplementation、factory、saltでも、initializerのvalueを77から78へ変えると予測addressが0x42A5…772cから0x6cDC…8C61へ変わりました。
一方、空のinitializerでproxyをdeployし、後transactionでinitializeする設計なら、後から渡すownerやvalueはCREATE2 preimageへ入りません。しかし未初期化の時間を作ると、第三者にinitializeされる危険があります。initializerをaddressへ含めるかどうかだけでなく、deployとinitializeをatomicに完了するかをreviewします。
proxyのimplementation、admin / upgrade authority、storage slotの読み分けは、ERC-1967 proxyの確認手順も参照してください。
minimal cloneはimplementationがinit codeへ入り、initializerは後段になる
OpenZeppelin Clones.cloneDeterministicは、ERC-1167 minimal proxyのinit codeへimplementation addressを埋め込み、factoryとsaltからaddressを予測します。固定例では次を一致させました。
| field | fixed value |
|---|---|
| implementation | 0x9fE4…a6e0 |
| effective salt | 0xae1a…8d01 |
| clone init-code hash | 0x4798…e0ba |
| predicted / deployed | 0xd563…33Db |
| atomic initialized owner / value | 0xf39F…2266 / 99 |
通常のminimal cloneではinitializer calldataはclone init codeの外にあります。つまりinitializer valueを変えてもclone address計算へ直接入らない設計があります。そのためfactoryがcloneを作った直後、同じtransactionでinitializeすることが重要です。
OpenZeppelinは、implementationにcodeがないcloneではinitialize callが成功したように見えても効果がなく、未初期化のまま残り得ると警告しています。予測前とdeploy前にimplementation code hashを確認します。
counterfactual addressは「存在前に使える」が「予約済み」ではない
EIP-1014の目的には、まだon-chainに存在しないaddressとのcounterfactual interactionがあります。smart accountではfactory、owner、saltから予定addressを計算し、署名やUserOperationを先に組み立てることがあります。
local chainで予定accountを3段階に分けました。
| phase | code | nonce | balance |
|---|---|---|---|
| 予測直後 | 0 bytes | 0 | 0 |
| local test ETHを事前送付 | 0 bytes | 0 | 0.25 ETH |
| CREATE2 deploy後 | 108 bytes | 1 | 0.25 ETH |
codeもnonceも0のaddressへnative balanceを置けること、後で同じaddressへdeployするとbalanceが残ることを確認しました。しかし、これは実資産の事前送付を勧める結果ではありません。
予測一致だけでは、factoryが将来もdeploy可能か、ownerが正しいか、init codeが監査済みか、constructorがrevertしないか、資産を取り出せるcodeになるかは分かりません。counterfactual signatureのfactory calldataとrollback検証は、ERC-6492の未deploy account署名検証で扱っています。
deployment manifestに固定する項目
addressだけを共有するのではなく、再計算できる入力と実行結果を1件にまとめます。
| group | fields |
|---|---|
| network | chain ID、genesis / fork、RPCの用途、block |
| factory identity | address、runtime code hash、implementation、owner / deploy authority |
| salt | raw value、型、encoding、namespace、effective bytes32 |
| compiler | solc version、optimizer、runs、via-IR、EVM version、metadata setting |
| link | fully qualified library name、library address、remapping |
| init code | creation bytecode hash、constructor types / values、initializer data hash |
| prediction | expected address、Solidity result、off-chain result |
| pre-state | eth_getCode、nonce、balance |
| deployment | transaction hash、receipt status、created address、event |
| post-state | code hash、nonce、balance、owner / initialized state |
manifestにsecretを入れてはいけません。private key、seed、API secret、個人情報は対象外です。予測addressへ権限や資産を結び付ける前に、別担当者が同じmanifestから再計算できる状態にします。
review checklist
- CREATE2を実行するfactory addressとruntime code hashを確認したか。
- user saltとeffective saltを分け、encodingとcaller namespaceを固定したか。
- runtime bytecodeではなく、constructor data付きcreation bytecodeをhashしたか。
- compiler、optimizer、EVM、metadata、library addressを固定したか。
- proxy initializerがconstructor内かdeploy後かを確認したか。
- clone / accountのinitializeを同じtransactionで完了するか。
- predicted addressのcode、nonce、balanceをdeploy直前に読み直したか。
- collision、constructor revert、wrong factoryを別errorとして扱うか。
- factoryのdeploy権限、upgrade権限、pause条件を確認したか。
- address予測をcontract安全性、deploy成功、資産回収の保証として表示していないか。
まとめ
CREATE2 addressは、saltだけでは決まりません。factory address、effective salt、constructorやinitializerを含むinit code、そのcompiler / library条件を固定して初めて再計算できます。
予測が一致したら、次はcode・nonce・balance、factory権限、collision、constructor result、initializeのatomicityを確認します。deterministicなのはaddress導出であり、deploymentの成功やcontractの安全性ではありません。
確認した一次情報
- EIP-1014 Skinny CREATE2確認日: 2026/08/31
- Solidity salted contract creation確認日: 2026/08/31
- Solidity contract metadata確認日: 2026/08/31
- Solidity library linking確認日: 2026/08/31
- OpenZeppelin Create2 utility確認日: 2026/08/31
- OpenZeppelin deterministic clones確認日: 2026/08/31
- OpenZeppelin ERC1967Proxy constructor initializer確認日: 2026/08/31
- viem getContractAddress確認日: 2026/08/31



