3MIKAN
仮想通貨直コン

verified contractは安全?compiler・constructor・proxyまで確認する方法

Explorerのverified sourceを安全保証と誤解せず、creation/runtime bytecode、compiler設定、constructor、library、metadata、proxy先から再検証します。

3MIKANのブランドキャラクターが設計図と2つの金属製ビルドを検査台で比較し、compiler・optimizer・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です。

sourceとcompiler設定、libraryをcompileしてcreation bytecodeを作り、constructor argumentsを加えたdeployment inputからruntime bytecodeを得てon-chain codeと比較する流れ

この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相当の入力を保存します。最低限、次を固定します。

  1. compilerの完全なversionとcommit
  2. optimizerのenabled、runs、詳細設定
  3. EVM version
  4. viaIRの有無
  5. source unit名、path、remapping、import内容
  6. libraryの完全修飾名とaddress
  7. metadataのbytecodeHash、CBOR追加設定
  8. 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」と即断せず、次の順で扱います。

  1. compiler outputのlinkReferencesimmutableReferencesを取得する
  2. on-chain codeの該当rangeを記録する
  3. known transformationだけを正規化する
  4. それ以外のbyteを比較する
  5. 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が安全とは限りません。

proxy addressのruntime source、ERC-1967 implementation・beacon・admin、current implementationのruntime sourceと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へ提出する前の再現手順

  1. chain、address、fixed block、runtime code hashを保存する
  2. creation transactionとinputを取得する
  3. source全file、source unit名、remappingを揃える
  4. compiler完全version、optimizer、runs、EVM、via-IRを固定する
  5. libraryをcompile時にlinkする
  6. constructor argsをABI encodeしてcreation bytecodeへ連結する
  7. runtimeのlibrary / immutable transformationをcompiler outputから適用する
  8. functional bytecodeとmetadata fingerprintを別々に比較する
  9. proxyならimplementation / beacon / adminを分離する
  10. 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調査の最短経路です。

確認した一次情報