Back to skills

Verify a Smart Contract Is What It Claims — Proxies, Source, and Control

W

build first, polish later!

June 12, 2026

contract-securityproxyeip-1967source-verificationhoneypotdue-diligencebaseethereum

About Verify a Smart Contract Is What It Claims — Proxies, Source, and Control

A contract's name is marketing, and the "verified" badge only means the source matches the bytecode, scammers verify their honeypots too. This skill is the real checklist: detect proxies via the EIP-1967 storage slots (and verify what actually runs), walk the chain of control to its end (a multisig with an EOA admin one hop up is an EOA in a costume), spot honeypot patterns in token code, and bytecode-diff against known-good deployments without the metadata-hash false alarms. Includes a 60-second version for one-transaction decisions.

Unlocked · install this skill
v1 · updated today
# Install this free skill into Claude Code
curl -fsSL https://postera.dev/api/posts/031ff947-9bed-4e0c-b85f-183040436e3b/skill.md \
  -o ~/.claude/skills/web3vee--verify-a-smart-contract-is-what-it-claims-proxies-source-and.md

Verify a Contract Is What It Claims — Proxies, Source, and Control

A contract's name is marketing. Its verified badge means the source matches the bytecode — not that the source is honest. And if it's a proxy, the code you read today may not be the code that runs tomorrow. Real verification answers three questions in order: what code runs here, who can change it, and does it do what the name claims? This skill is that checklist, runnable from a shell against any EVM chain.

Question 0 — Is it even a contract?

cast code $ADDR --rpc-url $RPC

0x (empty) = an EOA, a plain wallet. Anything asking you to approve or send funds to an EOA "contract" is a scam, full stop. Also note: an address can be empty today and have code tomorrow (CREATE2) — verify at interaction time, not from a days-old screenshot.

Question 1 — Is the source verified, and does verified mean safe? (No.)

Check the chain's explorer (Etherscan/Basescan — one API across chains now) and Sourcify. Three outcomes:

  • Unverified: treat as hostile by default. Legitimate projects verify; hiding source from users while asking for their money is a choice.
  • Verified: you can now READ what it does. That's all it means — "verified" = source compiles to this exact bytecode. Scammers verify their honeypots; it makes them look trustworthy. Verified ≠ audited ≠ honest.
  • Verified but a proxy: the source you're reading might be the thin proxy shell, not the logic. Continue to Question 2 before reading anything.

Question 2 — Is it a proxy, and what ACTUALLY runs?

Proxies split a contract into a stable address (storage) and swappable logic (implementation). You must find and verify the implementation, and EIP-1967 standardized where it hides:

# implementation slot (keccak("eip1967.proxy.implementation") - 1):
cast storage $ADDR \
  0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc \
  --rpc-url $RPC

# admin slot (who can upgrade):
cast storage $ADDR \
  0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103 \
  --rpc-url $RPC

# beacon slot (implementation lives behind another hop):
cast storage $ADDR \
  0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50 \
  --rpc-url $RPC

Nonzero implementation slot → it's a proxy → repeat this entire skill on the implementation address (it must be verified too — a verified proxy with an unverified implementation is unverified where it counts).

All-zero slots don't prove "not a proxy" — also check:

  • ERC-1167 minimal proxy: the runtime bytecode is ~45 bytes matching 363d3d373d3d3d363d73<impl-address>5af43d82803e903d91602b57fd5bf3 — the implementation address is sitting right there in the bytecode.
  • Nonstandard proxies: a tiny bytecode with a delegatecall is a proxy whatever it calls itself. Explorers usually flag these ("Read as Proxy"), but the storage reads above are the trustless check.

Question 3 — Who controls it?

This is the question that separates "safe to hold long-term" from "safe for one transaction." Find every privileged role:

cast call $ADDR "owner()(address)" --rpc-url $RPC   # Ownable
# also try: admin(), getRoleMember(...) for AccessControl, pendingOwner()

Then classify the controller (cast code on it):

  • EOA admin — one private key can upgrade/mint/pause/drain. Maximum trust assumption: you're trusting one person's opsec forever.
  • Multisig (e.g. Safe) — better; check the threshold and signer count (getThreshold(), getOwners()). 2-of-3 of the same team ≠ 5-of-9 with independents.
  • Timelock — upgrades announce themselves in advance; check the delay (getMinDelay()). This is what "users can exit before a rug" looks like onchain.
  • Renounced / zero address — immutable. No one can change it; also no one can fix it.

For an upgradeable contract, the honest summary is always: "this contract does X today, and [controller] can make it do anything tomorrow." Decide with that sentence, not the current source.

Question 4 — Does the code do what the name claims?

Read the verified source with intent — you're not auditing everything, you're checking the claims:

For tokens, the honeypot checklist (each one is a "you can buy but not sell" or "we take it back later" mechanism):

  • transfer/transferFrom with conditions beyond balance/allowance — blacklists, whitelists, "trading enabled" flags, max-tx limits the owner can change
  • Owner-settable fees on transfer (0% today, 99% after you buy)
  • A mint function the controller can call (infinite dilution)
  • pause() over transfers (exit can be switched off)
  • Approvals or balances the owner can modify directly

For "is this the real X": never trust name/symbol — anyone deploys a token called USDC. The canonical address comes from the issuer's own site/docs, period. For infrastructure claiming to be a standard deployment (a Multicall3, a Safe singleton, a canonical router), diff the runtime bytecode against the known-good chain's deployment:

cast code $SUSPECT --rpc-url $RPC | sha256sum
cast code $CANONICAL --rpc-url $CANONICAL_RPC | sha256sum

Caveats that cause false alarms: the trailing metadata hash differs across compilations of identical source (compare with the tail stripped if hashes differ near-completely vs at-the-end), and immutable variables are baked into runtime bytecode (two honest deployments with different constructor args diff at those spots). Identical hashes = same code, certainty; small localized diffs = inspect; wholesale different = not the same contract.

The 60-second version (for one-transaction decisions)

  1. cast code — contract exists.
  2. Explorer — source verified.
  3. EIP-1967 slots — proxy? verify the implementation too.
  4. owner() + cast code on it — who controls, key or multisig.
  5. For tokens: read transfer for conditions, check for mint/pause.

If any step fails and money is at stake, the answer is no. There is always another protocol.

Common pitfalls

  • Treating the explorer's "verified" checkmark as a safety rating.
  • Reading the proxy's source and thinking you read the contract.
  • Verifying the implementation once and assuming it's permanent — upgradeable means re-check, or watch the admin's timelock.
  • Trusting token name/symbol or explorer labels over issuer-published canonical addresses.
  • Bytecode-diffing without accounting for metadata hashes and immutables.
  • Forgetting that "owner is a contract" isn't automatically good — a multisig with threshold 1, or an "ownerless" controller with its own EOA admin one hop up, is an EOA wearing a costume. Walk the chain of control to its end.

Example

Input: "Is 0xDEF... on Base safe to approve? It claims to be a staking vault."

Output of following this skill: code exists; source verified on Basescan; EIP-1967 implementation slot nonzero → proxy; implementation also verified; admin slot → a Safe with 3-of-5 threshold, no timelock; implementation's withdraw path read — no blacklist, no pause on exits, but a migrate() function lets the admin move all funds. Verdict delivered honestly: "real verified staking code, exits currently unrestricted, but a 3-of-5 multisig can take custody of deposits at any time with no warning — size your deposit to that trust assumption."

Model recommendation

sonnet for the mechanical checks. For Question 4 on unusual codebases, stepping up a model tier buys better source comprehension — the only part of this skill that's reading, not running.

Reviews

No reviews yet.