A-3 proxy
A-3 proxy
バイトコードが公開ソースと一致していても(A-1)、所有権の所在が分かっていても(A-2)、もし「ロジックを後から差し替えられるプロキシ」であれば、その確認はすべて静かに無効化されえます――実装ポインタを別のコードへ向け直すだけで。この項目では、live V1 の各契約がそうした差し替え経路を一切持たないこと、つまりデプロイされたコードがそのまま動き続けることを、ソースとオンチェーンの両面で確かめます。
コードが公開ソースと一致していて(A-1)、所有者が分かっていても(A-2)、もし「中身を後から差し替えられる裏口」があれば、その確認は静かに無効になります。自動販売機にたとえると、外から見える商品ボタンはそのままに、中身だけ別物にすり替えられる、という状態です。この裏口を「プロキシ」と呼びます。
この項目では、各契約にそうした差し替えの裏口が一切ないことを、ソースとオンチェーンの両面から確かめました。見つかった delegatecall も、自分自身の固定されたコードを呼ぶだけで、別のコードへ向け直す手段ではありません。つまり、いま見えているコードは後から差し替えられない、というのが結論です。
1. 主張(検証対象)
live V1 の全契約が、デプロイ後にロジックを差し替えられるアップグレード可能なプロキシではないこと。したがって、A-1(コード一致)や A-2(所有権)で確かめた内容が、あとから実装の差し替えによって覆される経路は存在しないこと。
2. 検証方法
ソースを全走査
全契約に対し、代表的なプロキシ/アップグレード機構(UUPS・Transparent・Beacon・Diamond・ERC1967・Clones(EIP-1167)・Initializable)の使用有無を確認。あわせて、初期化が constructor か(プロキシ前提の initializer でないか)、selfdestruct が無いかも見ました。
オンチェーンでスロットを読む
デプロイ済みの全アドレスについて、EIP-1967 の implementation/admin/beacon スロットがすべて空か、かつ EIP-1167 のクローン・バイトコードで始まらないか(=本体ロジックが乗っているか)を読み出す。
3. 判定
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契約にありました。隠さず出しますが、どちらもロジック差し替えではありません。
宛先は address(this)=自分自身
両者とも address(this).delegatecall(...)。呼ぶのは外部の差し替え可能な実装ではなく、自分自身の不変コードです。これは LayerZero メッセージ受信時に、自分の特定関数を呼び出すためのディスパッチ機構であって、実装ポインタを外部に向けるプロキシとは別物です。
実行できる関数はセレクタでホワイトリスト
require(isAuthorizedFunction(selector)) を通った関数しか実行できません。CawProfile は setWithdrawable の1つだけ、CawProfileL2 は depositAndUpdateOwners/authenticateAndUpdateOwners/mintAndUpdateOwners/updateOwners の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(非プロキシ)です。
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.
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.
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
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.
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
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 noselfdestruct. - 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
delegatecallis 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.
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.
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).
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.”
So we read “there is no door” with our own hands.






