Contract Analysis
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
Analysis Date: 2026-08-08
Metadata
Primary Contract
| PROPERTY | VALUE |
|---|---|
| Contract Address | 0x00000060...531524 (etherscan) |
| Network | Ethereum Mainnet |
| Contract Type | Standalone (non-upgradeable) ERC-721 registry |
| Contract Name | TokenList |
| Deployment Date | 2026-08-03 15:43:59 UTC |
| Deployment Block | 25675344 |
| Contract Creator | 0xAcFBA7Ce...DE4082 (etherscan) |
| Creation TX | 0xe5e00f96...21dd38 (tx) |
| Compiler Version | Solidity v0.8.36+commit.8a079791, EVM prague, optimizer enabled (20 runs) |
| Runtime Size | 24,243 bytes (333 bytes below the EIP-170 limit) |
| Total Functions | 62 (public/external ABI entries) |
| External Contract Dependencies | 2 categories — 1 renderer contract, plus a staticcall read of every listed token |
| Upgrade Mechanism | ☒ None — no proxy, no delegatecall, no implementation slot. The renderer is replaceable; the registry is not |
| Verification Status | ☑ Verified on Etherscan (exact match, standard-JSON input, 11 source files) |
| Audit Status | △ No published audit report located. Source comments reference internal "audit fixes" and per-listing audit links, but no third-party report was found |
Related Addresses
| TYPE | ADDRESS | NOTES |
|---|---|---|
| Owner (curator) | 0x006CD14F...8549F2 (etherscan) |
2-of-3 Multisig with a 1-hour execution delay. Holds every onlyOwner power |
| Multisig implementation | 0xD54cb652...9f4FB0 (etherscan) |
The owner is a minimal proxy delegating here. Verified as Multisig |
| Multisig signer 1 | 0x1C0Aa8cC...855A20 (etherscan) |
z0r0z's canonical deployer Externally Owned Account (EOA). Broadcast 7 of the 8 transactions that reached the registry |
| Multisig signer 2 | 0x999657A4...B44E1C (etherscan) |
Plain EOA, no code. Queued two of the eight actions through the multisig, including the renderer swap |
| Multisig signer 3 | 0xCB059258...006aa2 (etherscan) |
Plain EOA, no code. No observed activity against this registry |
| Timelock executor | 0x00000000...c3973F (etherscan) |
TimelockExecutor singleton. Bypasses the 1-hour delay on unanimous (3-of-3) signatures. Enabled for this multisig |
| Renderer (active) | 0x00000096...1eDcF4 (etherscan) |
TokenListRenderer, set 2026-08-05. Authors tokenURI, json and contractURI |
| Renderer (original) | 0x000000d5...DbbE87 (etherscan) |
Constructor-supplied renderer, superseded. Deployed directly by z0r0z's EOA |
| Deployment factory | 0x00000000...4e6f2a (etherscan) |
CREATE2 vanity factory used for the registry and both renderers |
Executive Summary
TokenList is an on-chain token registry. A listing here is not a row in a mapping that happens
to be readable. It is an ERC-721 token, and that NFT is minted to the address of the token being
described. The listing for WETH is held by the WETH contract
itself; anyone inspecting WETH on a block explorer or in an NFT-aware wallet sees a card carrying
its logo, symbol, decimals and links. Minting is listing, burning is delisting.
The intended audience is tooling rather than end users: wallets, swap frontends, indexers and
other contracts that need a canonical answer to "what is this token, and what should it look
like". The registry ships read paths shaped for that: a compact JSON form, a ranked id list, a
summary page that omits the multi-kilobyte logo field, and Solady's Multicallable so a
consumer can batch reads for exactly the rows on screen.
The contract splits facts from presentation. For a token
deployed on this chain, name, symbol and decimals are never accepted as arguments. They are
read out of the token contract by gas-capped staticcall through Solady's MetadataReaderLib,
and the sync(uint256) function that re-reads them is permissionless. The curator cannot type a
name for a local token even if they want to. What the curator does author is everything with no on-chain
source: logo, theme colour, sort weight, project and audit links, description, and an open-ended
bytes32 => string extension mapping. Foreign listings (other chains, Bitcoin-rooted assets, and
the native asset at address(0)) have no readable source here, so their text is curator-supplied
and stored with synced = false so consumers can render the distinction.
Trust concentrates in two places. The first is the owner: a 2-of-3 multisig
(0x006CD14F...8549F2) whose signers include z0r0z's canonical EOA, carrying a 1-hour execution
delay that a unanimous 3-of-3 signature set can bypass through an enabled TimelockExecutor. It
alone can list, delist, re-rank and re-skin. The second is the renderer. tokenURI, json and contractURI all forward to a
replaceable contract, and a renderer is free to print any text it likes regardless of what
storage holds. Storage-level provenance is real; display-level provenance is only as good as the
current renderer. That power has already been exercised once: the renderer was swapped on
2026-08-05, two days after deployment.
The risks here concern curation, not funds. The contract holds no balances, charges no fee and
has no withdrawal path. The realistic failure modes are a compromised or
careless curator attaching a legitimate-looking card to a malicious token, a renderer swap that
misrepresents every listing at once, and the inherited renounceOwnership path, which would
freeze the list permanently, including the ability to remove a listing whose project later turns
hostile.
As of this analysis the registry is five days old, holds 17 listings, and has never had a listing
delisted, frozen, reserved, or activated.
Architecture
graph TB
subgraph Governance["Governance"]
Z["z0r0z EOA<br/>0x1C0Aa8cC...855A20<br/>(EIP-7702 delegated)"]
S2["Signer 2<br/>0x999657A4...B44E1C"]
S3["Signer 3<br/>0xCB059258...006aa2"]
MS["Multisig (owner)<br/>0x006CD14F...8549F2<br/>2-of-3, delay 3600s"]
TLE["TimelockExecutor<br/>0x00000000...c3973F<br/>3-of-3 = instant"]
end
subgraph Registry["Registry (immutable)"]
TL["TokenList<br/>0x00000060...531524<br/>ERC-721 + Ownable + Multicallable"]
ST["_tokens / _ids / _position<br/>_boundLocalId / _extra*"]
end
subgraph Presentation["Presentation (replaceable)"]
R["TokenListRenderer<br/>0x00000096...1eDcF4<br/>pure, stateless, ownerless"]
RO["Renderer v1 (superseded)<br/>0x000000d5...dbbe87"]
end
subgraph Subjects["Listed subjects"]
T1["WETH, USDC, USDT, DAI<br/>wstETH, stETH, rETH, WBTC<br/>BOLD, LUSD, ZORG, ZAMM, FWA"]
T2["zOrgz, WNS<br/>(ERC-721 collections)"]
T3["Native ETH (id 0)<br/>Tacit Coin (Bitcoin-rooted)"]
end
Z --> MS
S2 --> MS
S3 --> MS
Z --> TLE
TLE -->|executor bypass| MS
MS -->|onlyOwner calls| TL
TL --- ST
TL -->|tokenURI / json / contractURI| R
RO -.->|replaced 2026-08-05| R
TL -->|"staticcall name/symbol/decimals<br/>60k gas cap"| T1
TL -->|staticcall + ERC-165 probe| T2
TL -->|listing NFT minted to token| T1
TL -->|listing NFT minted to token| T2
TL -->|listing NFT held by registry| T3
Anyone(["Anyone"]) -->|"sync(id) — permissionless"| TL
Consumers(["Wallets / dapps / indexers"]) -->|read| TL
style TL fill:#e1f0ff
style MS fill:#ffe1e1
style R fill:#fff4e1
System Overview
The registry keys every listing by a deterministic id. For a token on this chain the id is
literally its address cast to uint256, so a consumer that knows the address knows the id without
a lookup. Everything else — other chains, Solana mints, Bitcoin-rooted assets, and reservations
for tokens not yet deployed — is keyed by keccak256 over its namespace tuple with bit 255 set,
which keeps the two id spaces disjoint by construction.
- Stores a curated set of token identities and renders each one as a self-contained SVG card and
compact JSON, entirely on-chain, with no IPFS or HTTP dependency for the card itself - Reads
name,symbol,decimalsand the ERC-165 standard directly from local token contracts,
and refuses to let the owner author those fields for a local listing - Marks provenance explicitly:
synced = trueonly on listings whose text came from an on-chain
read, and consumers get that flag inget,summariesPagedand the JSONvfield - Makes every listing soulbound (ERC-5192) — the card cannot drift away from its subject
- Does not validate that a listed token is safe, solvent, or non-malicious. A listing is a
curatorial statement about identity and appearance, nothing more - Does not protect the displayed text from the owner.
setRendererremains open, and a
renderer can print anything regardless of storage - Does not hold funds, and has no
receive()or withdrawal path — ETH sent to any of its
inheritedpayablefunctions is unrecoverable
Design Patterns Used
- Soulbound NFT (ERC-5192):
_beforeTokenTransferrevertsSoulbound()whenever bothfromand
toare non-zero, so mint and burn are the only transitions.locked(id)returnstruefor
every existing listing, andLockedis emitted at every mint site - Subject-as-holder: the listing NFT is minted to the token contract it describes via
_mint
rather than_safeMint, deliberately — the subject has no receiver hook and must not be able
to refuse the card. Listings with no local subject (native ETH, foreign assets, reservations)
are held by the registry itself - Derived-fact / authored-presentation split:
_pullwritesname/symbol/decimals/standard
from the token;_setArtand theset*family write only fields with no on-chain source - Renderer indirection: all three metadata reads forward to an external
purecontract, keeping
the immutable registry under EIP-170 while leaving the card improvable.contractURIforwards
in hand-written assembly to avoid the decode/re-encode round trip - Sparse sort weights:
rankis a weight, not a position, and the seeded values step by 1,000 so
a listing can be inserted between two others without renumbering. Note thatrankis the
registry's ordering, not the only one in circulation — see
ZorgConviction, which scores the same listing ids by
bonded stake and reads this contract only throughisListed - Escape-hatch extension mapping: a bounded (32-key)
bytes32 => stringstore per listing, so a
new metadata field costs a transaction rather than a redeployment and a re-listing - Defence-in-depth sanitisation:
_cleanstrips the six characters that break out of a JSON
string or an SVG attribute on the way into storage, and the renderer's_safeapplies the same
filter on the way out, so a caller other than the registry cannot induce malformed output
Access Control
Roles & Permissions
| ROLE | ASSIGNED BY | REVOKABLE | CALL COUNT |
|---|---|---|---|
| Owner (curator) | Constructor (_initializeOwner) |
Yes — transferOwnership, renounceOwnership, or the two-step Solady handover |
Unlimited |
| Anyone | Implicit | N/A | Unlimited — sync(id) and all view functions |
| Listing holder (token contract) | _mint at list time |
No | Zero — every transfer path reverts Soulbound() |
The owner is not an EOA. It is a minimal proxy to a Multisig implementation configured with
threshold = 2, ownerCount = 3 and delay = 3600. A normal 2-of-3 execution queues the call
and becomes executable one hour later. The TimelockExecutor at 0x00000000...c3973F is
registered as the multisig's executor, and forwardEnabled is true for this multisig, so a
unanimous 3-of-3 signature set executes immediately with no delay. Threshold-signed cancellation
of a queued call is always available through the same executor.
Permission Matrix
| FUNCTION | OWNER | LISTING HOLDER | ANYONE |
|---|---|---|---|
list / listForeign / reserve |
☑ | ☒ | ☒ |
activateReserved |
☑ | ☒ | ☒ |
delist |
☑ | ☒ | ☒ |
setArt / setLogoSVG / setRank |
☑ | ☒ | ☒ |
setAudit / setExtra |
☑ | ☒ | ☒ |
setStandard / setOnchainSvg / setForeignText |
☑ | ☒ | ☒ |
freeze |
☑ | ☒ | ☒ |
setRenderer / lockRenderer |
☑ | ☒ | ☒ |
transferOwnership / renounceOwnership |
☑ | ☒ | ☒ |
sync |
☑ | ☑ | ☑ |
get / json / tokenURI / search / all reads |
☑ | ☑ | ☑ |
transferFrom / safeTransferFrom |
☒ | ☒ | ☒ |
sync is permissionless by design — refreshing a fact from its own
source is not treated as a privilege, which means the owner can never be the reason a stale symbol
persists. And no role, including the owner, can transfer a listing; the soulbound check is applied
in _beforeTokenTransfer and has no bypass.
Time Locks & Delays
| ACTION | TIME LOCK | CAN CANCEL | PURPOSE |
|---|---|---|---|
Any onlyOwner call, 2-of-3 path |
Yes — 3,600 s queued in the multisig | Yes — threshold-signed cancelQueued via the executor |
☑ One-hour public warning before curation changes take effect |
Any onlyOwner call, 3-of-3 path |
☒ None — TimelockExecutor.forward executes immediately |
No | △ Unanimous signers can act with no delay; forwardEnabled is currently true |
freeze(id) |
None at the registry level | No — irreversible | ☑ Permanently seals one listing's owner-authored fields |
lockRenderer() |
None at the registry level | No — irreversible | ☑ Permanently removes the ability to change what cards display. Not yet called |
renounceOwnership() |
None at the registry level | No — irreversible | ☒ Would permanently end all curation, including the ability to delist a hostile project |
Economic Model
This contract does not handle funds or implement economic mechanics. It holds no token balances,
charges no fee, and has no withdrawal, sweep, or receive() function. Its ETH balance at the time
of analysis is 0.
Ten functions are declared payable through inheritance: approve, transferFrom, both
safeTransferFrom overloads, transferOwnership, renounceOwnership,
requestOwnershipHandover, cancelOwnershipHandover, completeOwnershipHandover, and
multicall. Nine of those ten will accept ETH that can never be retrieved, since no code path
moves ETH out of the contract. multicall is the exception — Solady's implementation opens with
if (msg.value != 0) revert(), so it rejects value outright. Triggering the trap requires caller
error, no such transaction has occurred, and the balance is 0, but there is no recovery path.
Summary of Observations
TokenList is a curated, on-chain token registry that models each listing as a soulbound ERC-721
minted to the token it describes. What the code enforces is narrow and specific: the identity
fields of a Token List cannot be authored by the curator, while everything aesthetic must be
authored by someone.
That goal is met at the storage layer, and the mechanism is straightforward to verify. list
takes no name, symbol or decimals parameter. _pull obtains them by gas-capped staticcall
through MetadataReaderLib, sets synced = true, and setForeignText — the only function that
can write those fields directly — reverts NotSyncable() on any listing where synced is true.
sync is callable by anyone. For the 15 local listings currently present — 13 ERC-20 tokens
and 2 ERC-721 collections — the displayed name and symbol came from the token contract
rather than from the curator.
Three qualifications follow. _pull sets synced = true unconditionally at
the end, even when every read returned empty; an empty read is treated as "no answer" and leaves
the stored value untouched, so a listing can legitimately read synced = true while carrying text
from an earlier successful read, or no text at all. Second, the provenance guarantee is about
identity fields only — the logo, description, links, colour and rank on every card are curator
statements with no on-chain backing. Third, and most consequentially, the guarantee covers storage. Display is a
separate matter. tokenURI, json and contractURI all forward to a replaceable renderer,
and a renderer may print whatever it likes. lockRenderer exists to close that gap permanently
and has not been called. The renderer has already been replaced once, on 2026-08-05; comparing the
two verified sources shows that change to be purely presentational (a Bitcoin chain label, an
initials fallback when a listing has no logo, restyled provenance chips, numeric trait types), but
nothing in the contract constrains a future swap to be equally benign.
On governance, the owner is a 2-of-3 Multisig rather than a bare EOA, and it carries
a one-hour delay on the ordinary path. The delay is bypassable by unanimous signature through an
executor module that is currently enabled, so the effective worst case is that all three signers
acting together can change any listing instantly.
Reconstructing who actually drives that governance requires reading the multisig's own transaction
history, not the registry's event log. The registry only ever sees the second half of a two-step
process: an execute call queues an action, and a later executeQueued applies it. z0r0z's
canonical EOA 0x1C0Aa8cC...855A20 (etherscan)
broadcast 7 of the 8 transactions that reached the registry — the eighth is the deployment itself,
sent by the funded deployer EOA. But signer 2 0x999657A4...B44E1C (etherscan)
queued two of those eight actions, the wstETH listing and the renderer swap, each force-executed
by z0r0z minutes later through the unanimous bypass. Signer 2 is an active proposer rather than a
passive co-signer, and the renderer swap in particular did not originate with z0r0z.
The attribution to z0r0z and the zFi stack still rests on direct on-chain evidence rather than
inference from naming: the same EOA funded the deployer four minutes before deployment and
deployed the original renderer itself.
On funds, there is nothing to observe. The contract never holds or moves value. The only economic
note is the set of inherited payable functions with no withdrawal path, which makes accidentally
attached ETH unrecoverable.
Patterns that appear deliberate and self-consistent:
- ☑ No proxy, no
delegatecall, and no upgrade path for the registry itself. The identity rule
consumers are expected to cache cannot be changed out from under them. - ☑ A separation between derived facts and authored presentation that can be checked directly:
listaccepts no identity parameters,_pullreads them from the subject, and
setForeignTextrefuses any listing already flaggedsynced. - ☑ Refresh is permissionless.
syncis callable by anyone, so the curator is never the reason a
stale symbol persists, and it is exempt from thefreezegate because it copies a fact rather
than authoring one. - ☑ Soulbound Token listings, applied in
_beforeTokenTransferwith no bypass, so a card
cannot drift away from the subject it describes. - ☑ The same character filter applied on both the write side (
_clean) and the render side
(_safe), so a renderer handed hostile input by some other caller still emits well-formed
output. - ☑ Bounded loops in the paths that matter, including a 32-key cap on extensions specifically so
thatdelistalways fits in a block. - ☑ No whole-struct paginated reads, which would exceed
eth_calllimits once 24 KB logos are
counted.summariesPagedomits the unbounded fields by design.
Trade-offs and trust assumptions:
- △ The renderer remains swappable, so the strongest claim the contract makes about identity is
defeasible by the owner at the display layer.lockRendererwould close this permanently and
has not been called. - △
renounceOwnershipis inherited and open. In a registry, ownerless means uncorrectable —
including the inability to remove a listing whose project later turns hostile. - △ The contract sits 333 bytes below the EIP-170 limit with no upgrade path, so a fix
requiring more space would mean redeploying under fresh ids and migrating every consumer. - △ No third-party audit report was located, despite source comments referring to audit fixes.
- ◇ At five days old with 17 listings, the registry has no operating history against adversarial
conditions. Reservation, activation, delisting and freezing have never been exercised on
mainnet.
Three questions remain open for the team: whether lockRenderer will be called once the card
design settles, whether forwardEnabled is meant to stay true given it makes the one-hour delay
optional, and what the curation policy is for adding a listing.
This analysis was performed for educational purposes. It is not an official security audit and it
is not financial advice. Verify the claims here yourself using the commands in
Methodology, and form your own view.
References
| RESOURCE | NOTES |
|---|---|
| Etherscan — TokenList | Verified source, ABI, bytecode, event logs |
| Etherscan — TokenListRenderer (active) | Verified source of the renderer currently authoring every card |
| Etherscan — TokenListRenderer (original) | Constructor-supplied renderer, used to diff what the swap actually changed |
| Etherscan — Multisig implementation | Verified source behind the owner's minimal proxy; threshold, delay, executor semantics |
| Etherscan — TimelockExecutor | Verified source establishing that the delay bypass requires unanimous signatures |
| Solady | ERC721, Ownable, Multicallable, MetadataReaderLib, LibSort, LibString, Base64, Base58 — the nine library files bundled in the verified source |
| EIP-5192: Minimal Soulbound NFTs | The locked(uint256) interface and 0xb45a3c0e interface id advertised by the contract |
| EIP-4906: Metadata Update Extension | MetadataUpdate / BatchMetadataUpdate semantics used to invalidate cached cards |
| ERC-7572: Contract-level metadata | contractURI() collection metadata forwarded to the renderer |
| EIP-170: Contract code size limit | The 24,576-byte ceiling that motivated splitting the renderer out of the registry |
| EIP-1153: Transient storage | Context for the prague EVM target |
| Foundry Cast reference | Command syntax for all on-chain verification in this analysis |
| z0r0z entity profile | DNZN's prior research on the developer and deployment patterns matched here |
| zFi Project Overview | Project context for the zFi stack this registry serves |
| Tacit Project Overview | Background on the Bitcoin-rooted asset listed as Standard.TACIT |
Change Log
| DATE | AUTHOR | NOTES |
|---|---|---|
| 2026-08-08 | Artificial. | Generated by robots. Gas: 1.5 mtok |
| 2026-08-08 | Denizen. | Reviewed, edited, and curated by humans. |