Skip to content

Methodology

DISCLAIMER // NFA // DYOR

This analysis is based on observations of the contract behavior. We are not smart contract security experts. This document aims to explain what the contract appears to do based on the code. It should not be considered a comprehensive security audit or financial advice. Always verify critical information independently and consult with blockchain security professionals for important decisions.

⊙ generated by robots | curated by humans

METADATA
Contract Address 0x00000060...531524 (etherscan)
Network Ethereum Mainnet
Analysis Date 2026-08-08

Overview

The contract is verified on Etherscan with an exact source match, so this analysis worked from Solidity rather than from decompiled bytecode. That removes the usual guesswork about function names and control flow, but it introduces a different discipline: the source carries extensive NatSpec, including narrative explanations of past bugs and design reversals. Per the analysis standard, those comments were treated as leads to verify, never as evidence in themselves. Every behavioural claim here traces to executable code or an on-chain read.

Work proceeded in five phases. Phase 0 fetched the verified source bundle and split its eleven files to disk, then established deployment facts — creator, factory, block, compiler settings and runtime size. Phase 1 identified the contract as a standalone, non-upgradeable ERC-721 registry rather than a proxy, mapped its two-contract architecture, and enumerated the full 62-function Application Binary Interface (ABI) with computed selectors. Phase 2 read every function against its storage effects and verified the declared layout against live storage, including decoding the packed Token struct word byte by byte. Phase 3 traced the governance chain upward from the owner address — through a minimal proxy, a Multisig implementation, and a TimelockExecutor module — because the owner turned out not to be an Externally Owned Account (EOA). Phase 4 produced this documentation set.

Reading past the immediate contract surfaced two findings. The owner is a 2-of-3 multisig with a one-hour delay that a unanimous signature set can bypass, which required reading two additional verified contracts to establish. And the live renderer address differs from the one passed to the constructor, meaning setRenderer had already been exercised; both renderer sources were fetched and diffed to determine what that swap changed.

Governance attribution needed a third source. The registry's event log shows only the transactions that reached it, which is the second half of a two-step timelock, so the multisig's own transaction history was read separately to establish who proposed each action rather than who broadcast it. Those answers differ here: a second signer queued two of the eight actions, including the renderer swap.

Thought Process

mindmap
  root((TokenList Analysis))
    Phase 0 Obtain
      Etherscan getsourcecode
        Verified exact match
        11 files split to disk
        Standard-JSON settings
      Deployment facts
        Block 25675344
        CREATE2 vanity factory
        Runtime 24243 bytes
        333 B under EIP-170
    Phase 1 Discovery
      Contract type
        Not a proxy
        No delegatecall
        No implementation slot
      Standards
        ERC-721 soulbound
        ERC-5192 locked
        ERC-4906 metadata
        ERC-7572 contractURI
      Surface
        62 ABI entries
        Selectors via cast sig
        19 admin 9 user 34 view
    Phase 2 Deep Dive
      Provenance mechanism
        list takes no name param
        pull reads via staticcall
        setForeignText blocked when synced
        sync is permissionless
      Id derivation
        Local id equals address
        Foreign id keccak plus bit 255
        Reservation domain tag
      Storage verification
        Slots 0 to 7 read live
        Token struct 8 slots
        Packed word decoded
        Solady manual slots
    Phase 3 Risk and Trust
      Governance chain
        Owner is minimal proxy
        Multisig 2 of 3
        Delay 3600 seconds
        TimelockExecutor bypass
        Unanimous required
      Renderer mutability
        Live differs from ctor arg
        Diffed both sources
        Swap was presentational
        lockRenderer never called
      Fund surface
        No receive no withdraw
        Payable inherited functions
        Balance zero
    Phase 4 Documentation
      Six output files
      Verification commands
      Cross-links to zFi and Tacit
    Phase 5 Verification
      Facts re-derived from chain
      Standards and links
      Cross-document consistency
      Voice and filler

Verification Guide

All on-chain values quoted in this analysis were read from Ethereum mainnet using Foundry's cast against an archive-capable RPC endpoint, and all historical events were pulled from the Etherscan v2 API. Every command below is reproducible.

External Resources

RESOURCE WHAT IT PROVIDED
Etherscan — TokenList Verified source bundle, ABI, compiler settings, creation transaction, complete event log
Etherscan — TokenListRenderer (active) Verified source of the renderer authoring every card since 2026-08-05
Etherscan — TokenListRenderer (original) Verified source of the constructor-supplied renderer, used as the diff baseline
Etherscan — Multisig implementation Execution model behind the owner's minimal proxy: threshold, delay, executor bypass semantics
Etherscan — TimelockExecutor Established that the delay bypass requires unanimous rather than threshold signatures
Etherscan API v2 getsourcecode, getcontractcreation, getLogs, txlist endpoints
Solady Reference for ERC721, Ownable, Multicallable, MetadataReaderLib, LibSort, LibString, Base64, Base58, LibBytes behaviour and manual storage slot placement
Foundry Cast reference Command syntax for storage reads, slot derivation, selector and topic computation
EIP-5192 / EIP-4906 / ERC-7572 Interface semantics the contract advertises
EIP-170 The 24,576-byte code size ceiling that shaped the two-contract split

Commandline Tools

Tip

Commands below use cast from the Foundry Toolkit. To run the commands below, you must set the RPC URL environment variable:

export ETH_RPC_URL=https://eth.llamarpc.com

Establish Contract Identity and Size

Confirms the contract is deployed, measures it against the EIP-170 ceiling, and shows it holds no funds. The nonce of 1 reflects the contract having performed no CREATE of its own.

# DEPLOYED RUNTIME SIZE IN BYTES - COMPARE AGAINST THE 24,576 EIP-170 LIMIT

cast codesize 0x0000006013dF75A31678B786061C2B54bf531524


# ETH HELD BY THE REGISTRY - EXPECTED TO BE ZERO

cast balance 0x0000006013dF75A31678B786061C2B54bf531524

Read Registry Configuration

The renderer returned here is the value to compare against the constructor argument. A mismatch means setRenderer has been called.

# CURATOR ADDRESS - RESOLVES TO A MULTISIG, NOT AN EOA

cast call 0x0000006013dF75A31678B786061C2B54bf531524 "owner()(address)"


# ACTIVE RENDERER AND WHETHER THAT CHOICE HAS BEEN SEALED

cast call 0x0000006013dF75A31678B786061C2B54bf531524 "renderer()(address)"
cast call 0x0000006013dF75A31678B786061C2B54bf531524 "rendererLocked()(bool)"


# LISTING COUNT AND COLLECTION IDENTITY

cast call 0x0000006013dF75A31678B786061C2B54bf531524 "total()(uint256)"
cast call 0x0000006013dF75A31678B786061C2B54bf531524 "name()(string)"
cast call 0x0000006013dF75A31678B786061C2B54bf531524 "symbol()(string)"

Enumerate Listings

summariesPaged is the read designed for this — it returns every scalar field plus name and symbol while omitting the logo, so the response stays a predictable size.

# EVERY LISTING ID, HIGHEST SORT WEIGHT FIRST

cast call 0x0000006013dF75A31678B786061C2B54bf531524 "rankedIds()(uint256[])"


# ALL LISTINGS WITHOUT THE UNBOUNDED FIELDS

cast call 0x0000006013dF75A31678B786061C2B54bf531524 \
  "summariesPaged(uint256,uint256)((uint256,bytes32,uint64,uint8,uint8,uint8,bool,bool,bool,uint24,uint32,bool,string,string)[])" \
  0 30

Verify the Provenance Claim

The core assertion of this contract is that a local token's identity fields come from the token, not from the curator. Two checks establish it. First, list has no name parameter — visible in the ABI. Second, the only function that can write those fields directly refuses to run on a synced listing.

# THE WETH LISTING - NOTE synced = true AND THAT NAME MATCHES WETH's OWN name()

cast call 0x0000006013dF75A31678B786061C2B54bf531524 \
  "get(address)((bytes32,uint64,uint8,uint8,uint8,bool,bool,bool,uint24,uint32,bool,string,string,string,string,string,string))" \
  0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2


# THE SOURCE OF TRUTH - READ DIRECTLY FROM THE TOKEN CONTRACT

cast call 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 "name()(string)"
cast call 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 "symbol()(string)"
cast call 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 "decimals()(uint8)"

Verify Soulbound Behaviour

The listing NFT for a local token is held by the token contract itself, and cannot move.

# THE WETH LISTING IS HELD BY WETH - ID IS LITERALLY THE ADDRESS AS A uint256

cast call 0x0000006013dF75A31678B786061C2B54bf531524 "ownerOf(uint256)(address)" \
  1097077688018008265106216665536940668749033598146


# THE REGISTRY HOLDS ONLY THE CARDS WITH NO LOCAL SUBJECT - NATIVE ETH AND THE TACIT ASSET

cast call 0x0000006013dF75A31678B786061C2B54bf531524 "balanceOf(address)(uint256)" \
  0x0000006013dF75A31678B786061C2B54bf531524


# ERC-5192 LOCK STATUS AND INTERFACE ADVERTISEMENT

cast call 0x0000006013dF75A31678B786061C2B54bf531524 "locked(uint256)(bool)" 0
cast call 0x0000006013dF75A31678B786061C2B54bf531524 "supportsInterface(bytes4)(bool)" 0xb45a3c0e

Verify Storage Layout

Slot 4 holds the listing count directly. Slot 7 packs the renderer address with the lock flag. The Token struct occupies eight consecutive slots at a derived base, and the second of those carries ten packed scalars.

# SEQUENTIAL SLOTS 0-7 AS DECLARED IN TokenList

for i in 0 1 2 3 4 5 6 7; do
  cast storage 0x0000006013dF75A31678B786061C2B54bf531524 $i
done


# SOLADY OWNABLE USES A MANUALLY PLACED HIGH SLOT, NOT SLOT 0

cast storage 0x0000006013dF75A31678B786061C2B54bf531524 \
  0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927


# BASE SLOT OF THE NATIVE ETH LISTING - keccak256(abi.encode(0, 3))

cast index uint256 0 3


# MEMBERSHIP TEST AND ADDRESS BINDING

cast storage 0x0000006013dF75A31678B786061C2B54bf531524 $(cast index uint256 0 5)
cast storage 0x0000006013dF75A31678B786061C2B54bf531524 \
  $(cast index address 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 6)

Trace the Governance Chain

The owner address has 45 bytes of code, which is a minimal proxy rather than a wallet. Following it to its implementation reveals the actual execution model.

# THE OWNER IS A CONTRACT - 45 BYTES IS A MINIMAL PROXY

cast codesize 0x006CD14F36F65eCbB29b2519cCBe63A0DC8549F2
cast code 0x006CD14F36F65eCbB29b2519cCBe63A0DC8549F2


# MULTISIG CONFIGURATION - SIGNERS, THRESHOLD, DELAY, AND EXECUTOR

cast call 0x006CD14F36F65eCbB29b2519cCBe63A0DC8549F2 "getOwners()(address[])"
cast call 0x006CD14F36F65eCbB29b2519cCBe63A0DC8549F2 "threshold()(uint16)"
cast call 0x006CD14F36F65eCbB29b2519cCBe63A0DC8549F2 "delay()(uint32)"
cast call 0x006CD14F36F65eCbB29b2519cCBe63A0DC8549F2 "executor()(address)"


# WHETHER THE TIMELOCK BYPASS IS ENABLED FOR THIS MULTISIG

cast call 0x00000000a72A30AdBf38e14d36BCE2610ec3973F "forwardEnabled(address)(bool)" \
  0x006CD14F36F65eCbB29b2519cCBe63A0DC8549F2

Reconstruct the Curation History

The registry has no direct transactions — every owner call arrives as an internal call from the multisig — so the history has to be read from event logs rather than from txlist.

# EVENT TOPIC HASHES FOR DECODING THE LOG

cast keccak "Listed(uint256,bytes32,uint64,string,string,uint8,bool)"
cast keccak "Updated(uint256,bytes32)"
cast keccak "RendererSet(address)"
cast keccak "Delisted(uint256,bytes32)"
cast keccak "Froze(uint256)"


# FULL EVENT HISTORY VIA THE ETHERSCAN V2 API

curl -s "https://api.etherscan.io/v2/api?chainid=1&module=logs&action=getLogs\
&address=0x0000006013dF75A31678B786061C2B54bf531524\
&fromBlock=0&toBlock=latest&page=1&offset=1000&apikey=$ETHERSCAN_API_KEY"

Render a Card End to End

Confirms the renderer path works and lets the produced SVG and attribute set be inspected directly.

# FULL ERC-721 METADATA DOCUMENT - BASE64 JSON WITH AN EMBEDDED BASE64 SVG

cast call 0x0000006013dF75A31678B786061C2B54bf531524 "tokenURI(uint256)(string)" \
  3718177199436581777268575498026466751093263122


# THE COMPACT MACHINE-READABLE FORM CONSUMERS ARE MEANT TO BATCH

cast call 0x0000006013dF75A31678B786061C2B54bf531524 "json(uint256)(string)" \
  104165018710067097353655755692819801489527232022561016148205125677286991358696

Document Verification

The drafted documents were re-checked before publication in five independent passes, each with a narrow scope and each reading from primary sources rather than from the draft. Running them separately matters: a claim can be correctly formatted, non-redundant, neutral in tone, free of filler, and still be factually wrong, and a single reviewer holding all five concerns at once reliably drops the last one.

DIMENSION WHAT IT CHECKS CHECKED AGAINST
Facts Addresses, block heights, listing counts, dates, compiler version, selector values, every quantitative claim Live chain state via cast, Etherscan API v2, the verified source bundle for the registry and both renderers
Standards Address display format, explorer links on every on-chain reference, table casing, section separators, icon semantics, glossary term wrapping, nav registration CLAUDE.md, docs/glossary.md, mkdocs.yml
Redundancy Duplicated tables and paragraphs across the six documents, and the same claim stated with different values in two places The document set itself
Voice Superlatives asserted without a verification clause, opinion adjectives, speculation about intent or users CLAUDE.md writing standards
Filler Rhetorical padding — meta-narration, telegraphed structure, formulaic emphasis CLAUDE.md anti-patterns

Every disagreement the fact pass raised was re-derived from chain or source before any edit was made, on the assumption that the checker is as capable of being wrong as the draft. A claim was only changed where the contradicting value could be reproduced from a command in the Verification Guide above.

The governance section is where that discipline mattered most. Attribution read cleanly from the registry's own event log and was still incomplete, because that log records only the executing half of a two-step timelock — the multisig's txlist had to be read separately to establish who proposed each action.


Token Cost Breakdown

PHASE DESCRIPTION TOKENS
Phase 0 Obtain the Contract 15 tok
Phase 1 Discovery & Understanding 20 tok
Phase 2 Deep Dive Analysis 30 tok
Phase 3 Risk & Trust Analysis 20 tok
Phase 4 Documentation Generation 35 tok
Phase 5 Document Verification 1.4 mtok
TOTAL Complete Contract Analysis 1.5 mtok

Note: Token costs are estimates based on typical conversation lengths and complexity. Actual consumption may vary by ±10-15% depending on API responses, iterative refinement, and verification steps.

Phase 5 dominates the total by roughly forty to one, and the fact-check pass accounts for nearly all of it. Every quantitative claim was re-derived from chain state or source rather than carried forward from the drafting phase, so most on-chain reads in this analysis were performed at least twice, independently. That ratio is the intended shape of the work — producing a plausible document is cheap, and establishing that it is correct is not.