A-3 proxy


バイトコードが公開ソースと一致していても(A-1)、所有権の所在が分かっていても(A-2)、もし「ロジックを後から差し替えられるプロキシ」であれば、その確認はすべて静かに無効化されえます――実装ポインタを別のコードへ向け直すだけで。この項目では、live V1 の各契約がそうした差し替え経路を一切持たないこと、つまりデプロイされたコードがそのまま動き続けることを、ソースとオンチェーンの両面で確かめます。

やさしく言うと

コードが公開ソースと一致していて(A-1)、所有者が分かっていても(A-2)、もし「中身を後から差し替えられる裏口」があれば、その確認は静かに無効になります。自動販売機にたとえると、外から見える商品ボタンはそのままに、中身だけ別物にすり替えられる、という状態です。この裏口を「プロキシ」と呼びます。

この項目では、各契約にそうした差し替えの裏口が一切ないことを、ソースとオンチェーンの両面から確かめました。見つかった delegatecall も、自分自身の固定されたコードを呼ぶだけで、別のコードへ向け直す手段ではありません。つまり、いま見えているコードは後から差し替えられない、というのが結論です。

このページは検証台帳A-3 項目の詳細記録です。コードの同一性(A-1)・所有権の所在(A-2)と組み合わさって、初めて「動いているコードは固定されている」と言えます。

1. 主張(検証対象)

live V1 の全契約が、デプロイ後にロジックを差し替えられるアップグレード可能なプロキシではないこと。したがって、A-1(コード一致)や A-2(所有権)で確かめた内容が、あとから実装の差し替えによって覆される経路は存在しないこと。

2. 検証方法

1

ソースを全走査

全契約に対し、代表的なプロキシ/アップグレード機構(UUPS・Transparent・Beacon・Diamond・ERC1967・Clones(EIP-1167)・Initializable)の使用有無を確認。あわせて、初期化が constructor か(プロキシ前提の initializer でないか)、selfdestruct が無いかも見ました。

2

オンチェーンでスロットを読む

デプロイ済みの全アドレスについて、EIP-1967 の implementation/admin/beacon スロットがすべて空か、かつ EIP-1167 のクローン・バイトコードで始まらないか(=本体ロジックが乗っているか)を読み出す。

3. 判定

ロジック差し替えの経路なし
全契約が非プロキシ。EIP-1967 スロットは空、EIP-1167 クローンも無し。ソースにもアップグレード機構・selfdestruct は一切存在しません。delegatecall は CawProfile/CawProfileL2 の「自己 delegatecall」のみで、差し替えには使えません。

  • 一致 ソース全走査でアップグレード/プロキシ機構はゼロ。UUPS・Transparent・Beacon・Diamond・ERC1967・Clones・Initializable いずれも不使用。全契約が constructor 初期化で、selfdestruct も無し。
  • 確認 オンチェーンで全契約の EIP-1967 impl/admin スロットが空、EIP-1167 クローンも無し。本体コードは数万バイト規模(例:L2 で 33,355/39,461/19,713/13,047 バイト)=ロジックが実体として乗っている(プロキシなら極小)。
  • 要説明 delegatecall は CawProfile/CawProfileL2 の自己 delegatecall のみ(宛先=自分自身の不変コード)。ロジック差し替えには使えません(下記4で詳述)。

デプロイ後にロジックを別のコードへ差し替える経路は、見つかりませんでした。コードは公開ソースに固定され、その固定そのものが動かせない、というのがこの項目の結論です。

4. 正直な但し書き ── 自己 delegatecall

delegatecall はプロキシの心臓部でもあるので、見つけたら一旦立ち止まるのが筋です。V1 では CawProfile と CawProfileL2 の2契約にありました。隠さず出しますが、どちらもロジック差し替えではありません

1

宛先は address(this)=自分自身

両者とも address(this).delegatecall(...)。呼ぶのは外部の差し替え可能な実装ではなく、自分自身の不変コードです。これは LayerZero メッセージ受信時に、自分の特定関数を呼び出すためのディスパッチ機構であって、実装ポインタを外部に向けるプロキシとは別物です。

2

実行できる関数はセレクタでホワイトリスト

require(isAuthorizedFunction(selector)) を通った関数しか実行できません。CawProfile は setWithdrawable の1つだけ、CawProfileL2 は depositAndUpdateOwnersauthenticateAndUpdateOwnersmintAndUpdateOwnersupdateOwners の4つだけ。監査済み(2026-04-06)で、ソースコメントにも「これらの契約はデプロイ後 immutable で、新しい認可関数は追加できない」「endpoint は不変(constructor で一度設定、変更不可)」と明記されています。

どちらも「自分の不変コードの、決められた関数」を呼ぶだけ。実装ポインタのように、後から任意の新コードへ向け直せる可変点ではありません。

5. receipt(公開証明)

誰でも再現できるよう、読み取り対象のスロットと手順を残します。EIP-1967 が使う固定スロットは次の3つです。

スロット キー(bytes32)
implementation 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
admin 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
beacon 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50

各契約アドレスについて、上記スロットを読み(全件 0x000…000)、コードが EIP-1167 プレフィックスで始まらず、十分大きいことを確認します。1契約あたりの最小の読み取りはこれだけです。

再現コマンドは、自分のノードと同じ ethers v6(node)curl 版を、下のツールの「コマンドで確認」にコピー安全な形で用意しています。期待値は 3スロットとも 0x0…0、コードは EIP-1167 プレフィックス 0x363d3d373d3d3d363d73 で始まらないこと。全契約 PASS(非プロキシ)です。

▶ ブラウザでこの検証を再現する(別窓)

押すと、3チェーンの7契約について EIP-1967 の3スロットが空か+EIP-1167 クローンでないかを一括照合します。「わざと壊す」で実際に赤くなることや、生のスロット値・叩いたリクエスト、開発者ツールの Network タブで実通信を確認できます。再現コマンド(ethers v6/curl)もツール内「コマンドで確認」に。

6. 注記・限界

この項目(A-3)が扱うのは「デプロイ後にロジックを差し替える経路の不在」です。焼き込まれた immutable 値が正当なアドレスを指しているか(A-5)、所有権の所在(A-2)は、台帳の別項目で扱います。なお非アップグレード性はバグの裏返しではなく、意図的な設計です。ソースコメント自身が「これらの契約はデプロイ後 immutable」と明記しています。

差し替えられる扉が一つでもあれば、検証はいつでも無効化できる。
だから「扉が無いこと」を、自分の手で読む。

Even if the bytecode matches the public source (A-1) and we know where ownership sits (A-2), if it were “a proxy whose logic can be swapped later,” all of that could be silently nullified — just by re-pointing the implementation pointer to different code. This item confirms, in source and on-chain, that no such swap path exists in any live V1 contract — i.e. the deployed code keeps running as-is.

In plain terms

Even if the code matches the public source (A-1) and we know the owner (A-2), if there is a back door that lets the contents be swapped later, that confirmation quietly becomes void. Think of a vending machine: the product buttons you see from outside stay the same, while only the contents get swapped for something else. That back door is called a “proxy.”

This item confirms, in source and on-chain, that no such swap back door exists in any contract. The delegatecall we did find only calls the contract’s own fixed code — it is not a means to re-point to different code. So the conclusion: the code you see now cannot be swapped later.

This page is the detailed record for item A-3 of the Verification Ledger. Only combined with code identity (A-1) and where ownership sits (A-2) can we say “the running code is fixed.”

1. Claim (what is verified)

That every live V1 contract is not an upgradeable proxy whose logic can be swapped after deployment. Therefore, there is no path by which what we confirmed in A-1 (code match) or A-2 (ownership) could later be overturned via an implementation swap.

2. Method

1

Scan all source

For every contract, check for the use of the common proxy/upgrade mechanisms (UUPS, Transparent, Beacon, Diamond, ERC1967, Clones (EIP-1167), Initializable). Also check that initialization is via constructor (not a proxy-style initializer) and that there is no selfdestruct.

2

Read the slots on-chain

For every deployed address, read whether the EIP-1967 implementation/admin/beacon slots are all empty, and whether the code does not start with an EIP-1167 clone prefix (i.e. real logic is present).

3. Verdict

No path to swap the logic
Every contract is non-proxy. EIP-1967 slots are empty, no EIP-1167 clones. The source has no upgrade mechanism and no selfdestruct. The only delegatecall is the self-delegatecall in CawProfile/CawProfileL2, which cannot be used for a swap.

  • match A full source scan finds zero upgrade/proxy mechanisms. No UUPS, Transparent, Beacon, Diamond, ERC1967, Clones, or Initializable. Every contract initializes via constructor, with no selfdestruct.
  • confirmed On-chain, every contract has empty EIP-1967 impl/admin slots and no EIP-1167 clone. The runtime code is tens of thousands of bytes (e.g. 33,355 / 39,461 / 19,713 / 13,047 on L2) — real logic is present (a proxy would be tiny).
  • needs note The only delegatecall is the self-delegatecall in CawProfile/CawProfileL2 (target = the contract’s own immutable code). It cannot be used for a logic swap (detailed in §4 below).

No path was found to swap the logic for different code after deployment. The code is fixed to the public source, and that fixing itself cannot be moved — that is this item’s conclusion.

4. An honest caveat — the self-delegatecall

delegatecall is also the heart of a proxy, so it’s right to pause when you find one. In V1 it appears in two contracts, CawProfile and CawProfileL2. We show them without hiding, but neither is a logic swap.

1

The target is address(this) — itself

Both are address(this).delegatecall(...). What they call is not an external, swappable implementation but the contract’s own immutable code. This is a dispatch mechanism for invoking a specific function of itself when a LayerZero message arrives — different from a proxy that points an implementation pointer outward.

2

The executable functions are whitelisted by selector

Only functions that pass require(isAuthorizedFunction(selector)) can run. CawProfile allows just one — setWithdrawable; CawProfileL2 allows four — depositAndUpdateOwners, authenticateAndUpdateOwners, mintAndUpdateOwners, updateOwners. Audited (2026-04-06), and the source comment itself states “these contracts are immutable post-deployment, so no new authorized functions can be added” and “the endpoint is immutable (set once in the constructor, can never change).”

Both only call “a fixed function of their own immutable code.” Unlike an implementation pointer, this is not a mutable point that can later be re-aimed at arbitrary new code.

5. Receipt (public proof)

So anyone can reproduce it, we leave the target slots and the steps. EIP-1967 uses these three fixed slots.

Slot Key (bytes32)
implementation 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
admin 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
beacon 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50

For each contract address, read the slots above (all 0x000…000) and confirm the code does not start with the EIP-1167 prefix and is sufficiently large. That is the minimal read per contract.

Reproduction commands — the same ethers v6 (node) / curl you would run on your node — are provided copy-safe under “CLI” in the tool below. Expected: all three slots 0x0…0 and the code does not start with the EIP-1167 prefix 0x363d3d373d3d3d363d73. All contracts PASS (non-proxy).

▶ Reproduce this check in your browser (new tab)

Checks all 7 contracts across 3 chains for empty EIP-1967 slots and a non-EIP-1167 codebase. Hit “Sanity-break” to see it turn red, inspect the raw slot values and the real traffic in the DevTools Network tab; the ethers v6 / curl commands are under “CLI” in the tool.

6. Notes & limits

What this item (A-3) covers is the absence of a path to swap the logic after deployment. Whether the baked-in immutable values point to legitimate addresses (A-5) and where ownership sits (A-2) are handled in other items. Non-upgradeability here is not the flip side of a bug but an intentional design: the source comment itself states “these contracts are immutable post-deployment.”

If even one swappable door exists, the verification can be voided at any time.
So we read “there is no door” with our own hands.
Copyright ©  CAW JAPAN.com All Rights Reserved.
PAGE TOP
夜の薔薇園へ