verified contractは安全?compiler・constructor・proxyまで確認する方法
Explorerのverified sourceを安全保証と誤解せず、creation/runtime bytecode、compiler設定、constructor、library、metadata、proxy先から再検証します。

Explorerに「Contract Source Code Verified」と表示されても、そのcontractが安全、監査済み、upgrade不能、公式tokenという意味にはなりません。まず証明されるのは、特定のsourceとビルド条件から、on-chain bytecodeへ対応するcompile結果を再現できることです。
調査では、chain、address、block、runtime bytecodeを固定し、creation bytecode、compiler、optimizer、EVM version、via-IR、library address、constructor arguments、metadataを照合します。proxyなら外側と現在のimplementationを別contractとして確認します。
この記事では2026-08-31時点の公式仕様を参照し、Foundry 1.8.0とSolidity 0.8.36で4つのmatch状態をローカル再ビルドしました。public RPC、wallet、実資産、外部transactionは使っていません。
verified sourceが証明すること/しないこと
source verificationは、次の問いへ答えるための仕組みです。
このchainのこのaddressにあるbytecodeを、提示されたsourceとcompile条件から説明できるか。
反対に、次は別の調査です。
- owner、admin、role、multisig、timelockが妥当か
- proxyが誰にupgradeされ得るか
- oracle、bridge、外部call、token accountingに欠陥がないか
- sourceがaudit scopeと同じcommitか
- audit後にimplementationや設定が変わっていないか
- 同名・同symbolのcontractが公式か
- sponsor、広告、Explorer上のbadgeが信頼根拠になるか
verified sourceは「読めるcodeとdeployed codeの対応」を強くしますが、business logicの安全性を自動評価しません。tokenのidentityはsymbolではなくchainとcontract addressから確認するため、トークンが表示されないときの確認手順も合わせてください。
最初にchain・address・block・runtimeを固定する
比較前に調査対象を一行へ固定します。
chainId / address / blockNumber / blockHash / runtimeCodeHash
同じaddressでもchainが違えば別contractです。proxyはblockによってimplementationが変わり得ます。latestのまま別の日に読み直すと、sourceではなく調査時点が変わった可能性を排除できません。
runtime bytecodeはeth_getCode(address, block)で取得できます。empty codeなら、chain・block・address、selfdestructや作成前block、EOAとの取り違えを先に確認します。Explorerの表示だけを保存せず、raw hexとhashを残します。
creation bytecodeとruntime bytecodeを分ける
deployment transactionのinputは、通常「creation bytecode + ABI encodingしたconstructor arguments」です。EVMがcreation codeを実行し、返したbytesがaddressへ保存されるruntime bytecodeです。
この2つを同じものとして比較すると、正しいsourceでも不一致になります。
| 比較対象 | 主な内容 | 変わり得るもの |
|---|---|---|
| creation bytecode | constructorを含むdeployment用code | compiler設定、library link、metadata |
| transaction input | creation bytecodeの後ろにconstructor args | owner、初期値、salt以外の引数 |
| runtime bytecode | addressに保存される実行code | immutable、linked library、metadata |
| storage | runtimeとは別にaddressへ残るstate | constructor / initializer、通常transaction |
constructor argumentsはsource本文に含まれません。Etherscanの公式error guideも、constructorを使う場合はdeployment時の引数をhexで提示する必要があると説明しています。input末尾を推測だけで切らず、ABI typeでencodeし直してcreation bytecodeへ連結します。
compiler条件はversion名だけでは足りない
再ビルドには、source一式とStandard JSON相当の入力を保存します。最低限、次を固定します。
- compilerの完全なversionとcommit
- optimizerのenabled、runs、詳細設定
- EVM version
viaIRの有無- source unit名、path、remapping、import内容
- libraryの完全修飾名とaddress
- metadataの
bytecodeHash、CBOR追加設定 - constructor arguments
{
"language": "Solidity",
"sources": {
"src/VerificationFixture.sol": { "content": "…" }
},
"settings": {
"optimizer": { "enabled": true, "runs": 200 },
"viaIR": false,
"evmVersion": "prague",
"metadata": { "bytecodeHash": "ipfs" },
"libraries": {
"src/VerificationFixture.sol": {
"BuildLibrary": "0x0000000000000000000000000000000000001001"
}
},
"outputSelection": {
"*": { "*": ["metadata", "evm.bytecode", "evm.deployedBytecode"] }
}
}
}
Solidityのcompiler documentationは、複雑な自動処理ではStandard JSON interfaceを推奨しています。sourceをflattenするとpathやmetadataが変わり、元のビルドのfingerprintを失うことがあります。元のbuild-infoまたはStandard JSON inputを優先します。
libraryはcompile時にlinkする
external library callを含む未link bytecodeには、fully qualified library名から作るplaceholderとlinkReferencesがあります。addressを間違えれば実行codeが変わります。
Solidity公式は、生成後のbytecodeへ手作業でaddressを差す方法を推奨していません。metadataにはcompile時のlibrary情報が入り、後からlinkしてもmetadataは更新されないためです。再現ビルドではStandard JSONのlibrariesへsource unit、library名、addressを入れます。
ローカル再現データでは、unlinked runtimeのbyte offset 324、20 bytesがlibrary referenceでした。同じaddressを手動で差すとmetadataを除いた実行部分は一致しましたが、raw runtime全体は一致しませんでした。compile時linkでは設定とmetadataも対応します。
immutableとconstructor差は既知の変換として扱う
immutableはconstructorで値が決まり、creation codeがruntime codeの指定位置へ値を書き込みます。Solidityの説明どおり、compiler outputのimmutableReferencesに位置が出ます。
再現データではownerとminimumに合計4つの32-byte referenceがあり、minimumを7から9へ変えるとraw runtime hashが変わりました。一方、compilerが示したimmutable範囲だけを正規化すると、残りのruntimeは一致しました。
「raw hexが違うから別source」と即断せず、次の順で扱います。
- compiler outputの
linkReferencesとimmutableReferencesを取得する - on-chain codeの該当rangeを記録する
- known transformationだけを正規化する
- それ以外のbyteを比較する
- metadata fingerprintを別に比較する
位置を自作の正規表現や「末尾だけ」と決めつけないでください。compiler versionやcode構造でoffsetは変わります。
metadata hashはbuild全体のfingerprintになる
Solidityは通常、canonical metadata fileのIPFS hashとcompiler情報をCBORでbytecode末尾へ付けます。末尾2 bytesはCBOR部分のlengthです。metadataにはsource hash、compiler、settings、ABI、NatSpecなどが含まれます。
そのため、実行結果を変えないcomment、source path、license、変数名の変更でもmetadata hashは変わり得ます。bytecodeHash: "none"はmetadata hashを付けない設定ですが、CBOR追加自体を止めるappendCBOR: falseとは別です。
ローカル再現データの通常ビルドはruntime 522 bytesで、そのうち末尾53 bytesがCBOR trailerでした。同じsource・optimizerでbytecodeHashだけをnoneへ変えると、metadataを除いた469 bytesは一致し、raw bytecodeは不一致になりました。
Exact Match・Match・mismatch・similarを分ける
Sourcifyの現行documentationは、以前のperfect matchをExact Match、以前のpartial matchをMatchと呼びます。名称はExplorerごとに同じとは限りません。参照した公式ページは末尾の出典一覧に記録しています。
| 状態 | 実行部分 | metadata fingerprint | 読み方 |
|---|---|---|---|
| Exact Match | 既知変換を除いて一致 | 一致 | source内容とmetadataまで対応 |
| Match(旧partial) | 一致 | 不一致 | 動作部分は対応するがpath・comment等の差が残り得る |
| mismatch | 不一致 | 多くは不一致 | compiler条件、source、library、targetを見直す |
| similar candidate | まだ結論ではない | 未確定 | 既存候補を使って再compileし、通常のmatch判定を完了する |
EtherscanのExact / Similar Match表示とSourcifyのmatch名を混ぜず、利用したservice、label、確認日、creation/runtimeのどちらが照合されたかを記録します。別chainや別Explorerへverificationが自動で伝わるとも限りません。
Sourcifyのsimilarity verificationも、似たbytecodeから候補のcompile inputを見つけ、その候補を通常のverification flowへ通します。「似ている」と表示された時点でsource一致を確定する仕組みではありません。
ローカル再ビルドで4状態を固定した
Foundry 1.8.0、Solidity 0.8.36+commit.8a079791、Prague EVMを使い、同じsourceを4 profileでビルドしました。
| case | 変更 | functional bytes | metadata | 結果 |
|---|---|---|---|---|
| exact | optimizer on / runs 200 / IPFS | 一致 | 一致 | exact_match |
| metadata-only | bytecodeHash: none |
一致 | 不一致 | match |
| optimizer mismatch | optimizer off | 不一致 | 不一致 | mismatch |
| similar candidate | immutable minimum 7 → 9 | known range正規化後に一致 | 一致 | full verificationが必要 |
creation bytecodeは693 bytes、constructor argsは64 bytes、deployment inputは757 bytesでした。runtimeは522 bytes、metadataを除く実行部分は469 bytesです。公開再現JSONにはビルド条件一覧、source/config hash、constructor encoding、library/immutable reference、raw SHA-256、4判定を保存しています。
Foundryのローカル検証は3件すべて成功し、constructor値によるruntime hash差、固定library addressでのexternal call、proxyとimplementationの異なるcode hashを確認しました。再現処理はローカルcompilerとFoundryの検証用VMだけで完結します。
proxyとimplementationは別々にverifyする
proxy addressのruntime sourceがverifiedでも、userのcallを実行するcurrent implementation sourceまでverifiedとは限りません。implementationがverifiedでも、proxyのadmin、beacon、upgrade authority、storage layoutが安全とは限りません。
ERC-1967なら、fixed blockでimplementation、beacon、admin slotを読みます。beaconを使う場合はbeacon contractのimplementation()も同じblockで確認します。
詳しいslot値、Transparent・UUPS・Beaconのcall path、initializer、upgrade event、storage collisionはERC-1967 Proxyの読み方でローカル再現データ付きで確認できます。
最低でも次を別recordにします。
- proxy addressのruntime bytecode、source、compiler条件
- current implementation addressのruntime bytecode、source、compiler条件
- beacon、admin、ProxyAdmin、owner、role、timelock
- implementation変更eventとblock-specific slot
- storage layoutとinitializer状態
Explorerがproxy ABIを結合表示しても、verification targetまで一つになったわけではありません。
Explorerへ提出する前の再現手順
- chain、address、fixed block、runtime code hashを保存する
- creation transactionとinputを取得する
- source全file、source unit名、remappingを揃える
- compiler完全version、optimizer、runs、EVM、via-IRを固定する
- libraryをcompile時にlinkする
- constructor argsをABI encodeしてcreation bytecodeへ連結する
- runtimeのlibrary / immutable transformationをcompiler outputから適用する
- functional bytecodeとmetadata fingerprintを別々に比較する
- proxyならimplementation / beacon / adminを分離する
- Explorerのservice名、match label、確認日を記録する
ABI、calldata、event logをsourceと照合する前提はABI・calldata・logsを読む記事、Explorerで直接読み書きする前のaddress・function・シミュレーション確認はEtherscanからcontractを操作する記事へ進んでください。
verified後に見る安全性checklist
- source verificationの対象chain・address・blockを固定した
- proxyとimplementationを別々に確認した
- owner、admin、roles、pause、upgrade authorityを読んだ
- deploy・upgrade・ownership eventを追った
- storage layoutとinitializerを確認した
- audit reportのscope、commit、date、除外項目を照合した
- oracle、bridge、external call、token accountingを調べた
- 同名tokenやsimilar contractを公式性の根拠にしていない
- sponsor・広告表示をverification結果から分離した
verification tool、Explorer、audit、monitoring serviceがsponsorの場合でも、広告表示は独立して明示します。sponsor契約、badge、掲載順位はbytecode matchや安全判定を変えません。この記事にもaffiliate linkはありません。
まとめ
verified sourceは、調査を始められる重要な証拠です。ただし結論はbadgeではなく、chain・address・block、creation/runtime、compiler settings、constructor、library、immutable、metadata、proxy先の対応から作ります。
verified表示を結論にせず、chain・address・runtime bytecode・ビルド条件・proxy先を同じ記録へ固定することが、再現可能なcontract調査の最短経路です。
確認した一次情報
- Solidity Contract Metadata確認日: 2026/08/31
- Solidity Using the Compiler and Standard JSON確認日: 2026/08/31
- Solidity immutable variables確認日: 2026/08/31
- Sourcify Exact Match vs Match確認日: 2026/08/31
- Sourcify similarity verification確認日: 2026/08/31
- Sourcify Solidity metadata確認日: 2026/08/31
- Etherscan common verification errors確認日: 2026/08/31
- Etherscan verification with Foundry確認日: 2026/08/31
- Blockscout contract verification UI確認日: 2026/08/31
- ERC-1967 Proxy Storage Slots確認日: 2026/08/31
- Foundry 1.8.0確認日: 2026/08/31



