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.
Selectors were computed from the verified ABI via cast sig "<signature>".
Summary
CATEGORY
COUNT
Total Functions
62
User Functions
9
Admin Functions
19
View Functions
34
Of the 9 user functions, five are the inherited ERC-721 transfer and approval surface, and every
transfer path reverts. Only sync performs a state change that a non-owner can meaningfully use.
User Functions
Function: sync(uint256 id)
Re-reads a local token's name, symbol, decimals and ERC-165 standard from the token
contract and writes them into the listing. This is the function that makes the registry's
provenance claim operational: the curator is never the reason a stale symbol persists, because
anyone can refresh it.
ATTRIBUTE
VALUE
Selector
0xb1357bf9
Parameters
id — the listing id
Access
Public, permissionless, non-payable
FLAG
OBSERVATION
☑
Deliberately not onlyOwner. Refreshing a fact from its own source is not treated as a privilege
☑
Exempt from the freeze gate — it uses _mustExist, not _mustEdit. Freezing seals what governance authored, not what the token says
☑
Re-checks token.code.length so a selfdestructed subject cannot have synced = true re-asserted against empty reads
△
_pull sets synced = true unconditionally at the end, even when every read returned empty. An unreadable token still reports as onchain-sourced
◇
Empty reads are treated as "no answer" rather than "the answer is empty", so a transient read failure cannot blank a good listing
Solady's Multicallable. Batches several calls to this contract in one transaction, which is
the mechanism the contract's own comments point consumers at instead of providing struct-level
pagination — a caller batches get(id) or json(id) for exactly the rows on screen.
ATTRIBUTE
VALUE
Selector
0xac9650d8
Parameters
data — array of ABI-encoded calls
Access
Public, payable
FLAG
OBSERVATION
☑
The intended read path for dapps: one eth_call over an arbitrary set of ids
☑
Declared payable but rejects value — if (msg.value != 0) revert() runs before anything else, so this is not a route for stranding ETH in the contract
◇
Batches by self-delegatecall, so each inner call executes in this contract's storage context and sees the original msg.sender
functionmulticall(bytes[]calldatadata)publicpayablevirtualreturns(bytes[]memory){// Revert if `msg.value` is non-zero by default to guard against double-spending.if(msg.value!=0)revert();// `_multicallDirectReturn` returns the results directly and terminates the call context._multicallDirectReturn(_multicall(data));}
Functions: transferFrom / safeTransferFrom ×2
The ERC-721 transfer surface. Every one of these paths reverts. _beforeTokenTransfer rejects
any transition where both from and to are non-zero, which leaves mint (list) and burn
(delist) as the only state transitions a listing can undergo.
ATTRIBUTE
VALUE
Selectors
0x23b872dd, 0x42842e0e, 0xb88d4fde
Access
Public, payable, but unconditionally reverting
FLAG
OBSERVATION
☑
Listings are permanently bound to their subject. A card cannot drift away from the token it describes
☑
Advertised correctly: supportsInterface(0xb45a3c0e) returns true and locked(id) returns true for every existing listing
◇
Locked is emitted at mint; Unlocked deliberately does not exist because no listing is ever unlocked
Inherited surface that remains callable but has no useful effect here. approve and setApprovalForAll record approvals for transfers that can never succeed. requestOwnershipHandover lets any address register a 48-hour handover request that only the
current owner can complete; cancelOwnershipHandover withdraws the caller's own request.
ATTRIBUTE
VALUE
Selectors
0x095ea7b3, 0xa22cb465, 0x25692962, 0x54d1f13d
Access
Public. approve, requestOwnershipHandover and cancelOwnershipHandover are payable; setApprovalForAll is not
FLAG
OBSERVATION
◇
Approvals are inert given the soulbound check, but are not blocked
△
A pending handover request is not an ownership change; only completeOwnershipHandover by the current owner transfers control
Admin Functions
All functions in this section carry Solady's onlyOwner modifier and are therefore callable only
by 0x006CD14F...8549F2 (etherscan), the 2-of-3 Multisig.
Lists a token deployed on this chain. The signature is the clearest expression of the
contract's central design choice: there is no name, symbol or decimals parameter. Those
are read from the token contract itself. The owner supplies only presentation.
ATTRIBUTE
VALUE
Selector
0x6c37705d
Parameters
token, color (0xRRGGBB), rank (sort weight), logo, url_, description_
Access
onlyOwner, non-payable, returns uint256 id
FLAG
OBSERVATION
☑
Identity fields cannot be authored by the curator — they come from _pull
☑
Requires the subject to have code. A staticcall to a codeless address succeeds with empty returndata, which would produce a blank listing still flagged synced
☑
Uses _mint, not _safeMint, deliberately: the subject holds its own card and must not be able to refuse it via a receiver hook
☑
Double-listing is blocked on both the id and the address binding
△
The owner still authors logo, colour, rank, link and description — none of which have any on-chain backing
functionlist(addresstoken,uint24color,uint32rank,stringcalldatalogo,stringcalldataurl_,stringcalldatadescription_
)publiconlyOwnerreturns(uint256id){if(token.code.length==0)revertBadInput();id=idOf(token);if(_position[id]!=0||_boundLocalId[token]!=0)revertExists();Tokenstoraget=_tokens[id];t.account=bytes32(uint256(uint160(token)));t.chainId=uint64(block.chainid);t.kind=Kind.EVM;t.deployed=true;_setArt(t,color,rank,logo,url_,description_);_pull(t,token);_index(id);_boundLocalId[token]=id;_mintListing(token,id);// deliberately not _safeMint: the subject is the holder_emitListed(id,t);}
Lists an asset whose metadata cannot be read from this chain — another chain's token, a Solana
mint, a Bitcoin-rooted asset, or this chain's native asset at address(0). Because no
on-chain source exists, the text is curator-supplied and the listing is stored with synced = false so consumers can render the distinction.
ATTRIBUTE
VALUE
Selector
0x99c4f235
Parameters
Namespace tuple plus curator-authored text and presentation
Access
onlyOwner, non-payable, returns uint256 id
FLAG
OBSERVATION
☑
Cannot be used to bypass list — a live local token is rejected with NotSyncable()
☑
Enforces chainId == 0 for non-EVM namespaces so one asset cannot be listed twice under two different chain ids
☑
Card is minted to the registry itself, not to the current owner, so an ownership handover does not leave the previous curator as ownerOf forever
△
Every text field here is a curator assertion with no verification whatsoever
A two-stage flow for tokens that do not exist yet. reserve creates a listing with a stable
hash-derived id from a curator-chosen key, marked deployed = false. activateReserved later binds the real address, pulls its facts, and moves the card from the
registry to the token — preserving the original id so galleries and governance references never
have to migrate.
ATTRIBUTE
VALUE
Selectors
0x61c85c45, 0xc506a2a9
Access
onlyOwner, non-payable
Usage to date
Never called — no Reserved or Activated events exist
FLAG
OBSERVATION
☑
activateReserved uses _mustExist, not _mustEdit, so a frozen reservation can still be bound to its deployed token. Freezing seals what governance authored, not whether the subject shipped
☑
Activation requires the target to actually resolve as ERC-20 via _standardOf, and to be unbound and unlisted
☑
Burns and re-mints in the same transaction, restoring the subject-as-holder invariant
◇
Reservations are current-chain only, because activation must read code at the eventual address
△
The reservation card renders a full-frame "RESERVED / NOT DEPLOYED" banner, but a consumer reading storage must check deployed itself
Burns the listing NFT and removes the entry entirely — the record, the index position, the
address binding, and every extension key.
ATTRIBUTE
VALUE
Selector
0x964bc33f
Parameters
id
Access
onlyOwner, non-payable
Usage to date
Never called
FLAG
OBSERVATION
☑
Remains available on frozen listings by design, so a project that later turns hostile can still be removed
☑
The extension key loop is bounded by the 32-key cap enforced in setExtra, so delisting always fits in a block
◇
Swap-and-pop removal means tied ranks are deterministic but not stable across removals — the last entry inherits the removed one's index
△
Delisting is silent to holders: the subject contract loses its card with no notice mechanism beyond the Delisted event
STEP
ACTION
1
Read account before the record is wiped
2
Clear _boundLocalId if this was a deployed local EVM listing with a non-zero account
3
Swap the last id into this position, update _position[last], pop _ids
4
Delete _position[id] and _tokens[id]
5
Walk _extraKeys[id] in reverse, deleting each _extra and _extraPos entry, then delete the key array
6
_burn(id) and emit Delisted(id, account)
CONDITION
REVERT MESSAGE
Caller is not owner
Unauthorized()
_position[id] == 0
Unknown()
functiondelist(uint256id)publiconlyOwner{uint256pos=_position[id];if(pos==0)revertUnknown();Tokenstoraget=_tokens[id];bytes32account=t.account;// read before the entry is wipedif(t.deployed&&t.kind==Kind.EVM&&t.chainId==block.chainid&&account!=bytes32(0)){delete_boundLocalId[address(uint160(uint256(account)))];}uint256last=_ids[_ids.length-1];_ids[pos-1]=last;_position[last]=pos;_ids.pop();delete_position[id];delete_tokens[id];bytes32[]storagekeys=_extraKeys[id];for(uint256i=keys.length;i>0;--i){bytes32key=keys[i-1];delete_extra[id][key];delete_extraPos[id][key];}delete_extraKeys[id];_burn(id);emitDelisted(id,account);}
Rewrites the owner-authored presentation of a listing in one call: theme colour, sort weight,
logo URI, project link and description. This is the single most-used admin function in the
contract's history — 13 of the 19 Updated events emitted carry the field label "art", ahead
of nftArt and extra at 2 each and text and audit at 1 each.
ATTRIBUTE
VALUE
Selector
0xdf47d810
Access
onlyOwner, non-payable
FLAG
OBSERVATION
☑
Gated by _mustEdit, so a frozen listing rejects it
☑
Logo must be data:image/, https:// or ipfs:// — a bare data: payload such as data:text/html is rejected, which stops markup being handed to whatever renders the card
☑
_uri rejects quote, angle bracket, ampersand, backslash and control characters outright rather than escaping them
△
Rewrites all five fields at once. Passing an empty logo clears it rather than leaving it unchanged
Accepts raw SVG markup, base64-encodes it on-chain into a data:image/svg+xml URI, and stores
that as the logo. The convenience path for putting genuinely on-chain art on a card without the
caller having to encode it.
ATTRIBUTE
VALUE
Selector
0x1ba41b3d
Access
onlyOwner, non-payable
FLAG
OBSERVATION
☑
Requires the markup to contain the SVG namespace string. A standalone SVG without xmlns renders as a broken image in every wallet, so this converts a silent list-wide display failure into a failed transaction
☑
The 24,576-byte cap is applied to the encoded result, not the raw markup — base64 costs four bytes per three, so bounding the input instead would let this path store roughly a third more than setArt accepts for the same field
The only function that can write name, symbol or decimals directly, and the guard on it is
what makes the whole provenance model hold. It reverts on any listing where synced is true.
ATTRIBUTE
VALUE
Selector
0x1d66e5ab
Access
onlyOwner, non-payable
FLAG
OBSERVATION
☑
if (t.synced) revert NotSyncable(); — the curator cannot overwrite a fact that was read from a token
☑
Since _pull sets synced = true on every local listing, this function is permanently unavailable for all 15 local entries
△
For foreign listings there is no verification at all; the text is whatever the curator types
Sets or clears an open-ended metadata field. This is the contract's escape hatch: the Token
struct is fixed at deployment, but the fields a token list gets asked for are not, so a new
field costs a transaction rather than a redeployment and a re-listing.
ATTRIBUTE
VALUE
Selector
0xbc341f98
Access
onlyOwner, non-payable
Usage to date
2 keys set, both on the Tacit listing: "etch tx" and "issued"
FLAG
OBSERVATION
☑
Capped at 32 keys per listing specifically because delist walks every key — an unbounded key set would be an unbounded loop in the only path that can remove a listing
☑
Values pass through the same _clean filter as every other display string
☑
Clearing a key that was never set returns early rather than emitting events for a write that did not happen
◇
An empty value removes the key entirely, so extraKeys never reports a field that reads back empty
STEP
ACTION
1
_mustEdit(id) and reject a zero key
2
Sanitise the value to at most 256 bytes
3
If empty and present, swap-and-pop the key out of _extraKeys and delete both mappings
4
If non-empty and new, enforce the 32-key cap, then push and record position
5
Write the value, emit ExtraSet and Updated(id, "extra")
Function: setStandard(uint256 id, Standard standard_) / setOnchainSvg(uint256 id, bool onchainSvg_)
setStandard declares what a foreign listing represents, and is gated the same way as setForeignText — a local listing derives its standard from the token and the owner must not
be able to overwrite it. setOnchainSvg marks an NFT collection whose selected token ids
resolve to on-chain SVG art.
ATTRIBUTE
VALUE
Selectors
0x4a63bb9b, 0x1419d229
Access
onlyOwner, non-payable
FLAG
OBSERVATION
☑
setStandard reverts NotEditable() on any synced listing
☑
Both functions clear or gate onchainSvg on collection standards only, so a fungible token cannot pose as a per-token artwork source
☑
The enum cannot grow after deployment — an out-of-range value reverts at ABI decode, so unnamed asset types must be carried as extension fields
◇
onchainSvg is a rendering hint, not a trust bypass; a client still reads and validates the target tokenURI itself
◇
Currently true on two listings: zOrgz and Wei Name Service
Permanently seals a listing's owner-authored fields. After this the curator cannot touch art,
links, rank, audit, foreign text or extras for that id, ever.
ATTRIBUTE
VALUE
Selector
0xd7a78db8
Access
onlyOwner, non-payable, irreversible
Usage to date
Never called — no listing is frozen
FLAG
OBSERVATION
☑
Every owner-authored setter routes through _mustEdit, so a future setter cannot silently escape the seal
☑
Uses _mustEdit itself, so freezing twice reverts rather than emitting an event for a change that did not happen
☑
sync, activateReserved and delist deliberately still work on frozen listings
△
A freeze is only as strong as the renderer. While setRenderer remains open, the owner can still change what a frozen listing displays, even though it cannot change what the listing stores
setRenderer points the registry at a new card renderer; lockRenderer gives up that power
permanently. Together these are the most consequential admin functions in the contract, because
the renderer authors everything a wallet or marketplace actually displays.
ATTRIBUTE
VALUE
Selectors
0x56d3163d, 0x9c8a2bfd
Access
onlyOwner, non-payable
Usage to date
setRenderer called once (2026-08-05); lockRenderer never called
FLAG
OBSERVATION
△
A renderer may print any text it likes regardless of storage. Swapping it changes what every listing appears to say, including frozen ones
☑
Code-length checked, for the same reason list checks it: an Externally Owned Account (EOA) here would make tokenURI return empty for every listing rather than revert — a silent, list-wide failure
☑
Emits BatchMetadataUpdate(0, type(uint256).max) and ContractURIUpdated() so clients refresh every cached card and the collection tile
☑
A replacement renderer must implement tokenURI, json and contractURI, or those reads revert
◇
Consumers that need unmediated facts should read get/json struct fields from storage rather than parsing the card
Two narrow setters. setRank adjusts a listing's sort weight — higher sorts first, 0 is
unranked. setAudit points a listing at an audit or security report, which the card renders as
a footer link and omits entirely when empty.
ATTRIBUTE
VALUE
Selectors
0xf45d6b86, 0xf5222379
Access
onlyOwner, non-payable
FLAG
OBSERVATION
☑
Seeded ranks are deliberately sparse (steps of 1,000 and 500) so a listing can be inserted between two others without renumbering
◇
Rank is the only ordering the contract promises; ties break by index position, which is deterministic but not stable across delistings
△
It is not the only ordering that exists over these listings. ZorgConviction, deployed three days after this registry, keeps a stake-weighted score per listing id and is what an interface sorting "by conviction" reads. That contract calls only isListed here and has no path to setRank, so the two orderings are independent and nothing on-chain reconciles them
△
audit is an unverified URL. The presence of an audit link on a card is a curator claim, not evidence a report exists or that it was favourable
◇
Only one listing currently carries an audit link — the Tacit entry
Solady Ownable administration. transferOwnership hands curation to a new address directly; completeOwnershipHandover accepts a pending request; renounceOwnership sets the owner to
the zero address permanently.
ATTRIBUTE
VALUE
Selectors
0xf2fde38b, 0x715018a6, 0xf04e283e
Access
onlyOwner, payable
FLAG
OBSERVATION
☒
renounceOwnership is inherited and deliberately left open. Calling it would permanently end all curation — no listing, no delisting, no correcting a bad link, by anyone, ever
△
The constructor rejects a zero initialOwner so the list cannot be born ownerless, but nothing prevents reaching that state later
△
Declared payable with no withdrawal path — attached ETH would be locked
◇
The stated trade-off is that renouncing declares the list final; the cost is that a project that later rugs becomes a permanent billboard nobody can take down
// Inherited unmodified from Solady's Ownable.// _OWNER_SLOT = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927
View Functions
Function: get(uint256 id) / get(address token)
Returns the full Token struct for a listing. The address overload routes through idFor so
an activated reservation resolves correctly despite keeping its hash-derived id.
ATTRIBUTE
VALUE
Selectors
0x9507d39a, 0xc2bc2efc
Access
Public view
FLAG
OBSERVATION
☑
The recommended read for consumers that need unmediated facts — it bypasses the renderer entirely
◇
Returns the heavy fields including the logo, so batching many of these through multicall can produce large returndata
◇
There is deliberately no getMany(ids); Multicallable already batches over an arbitrary set, which is strictly more flexible
The three renderer-forwarded reads. json returns a compact machine-readable form with
single-letter keys, tokenURI returns the full ERC-721 metadata document with an embedded SVG
card, and contractURI returns ERC-7572 collection metadata.
ATTRIBUTE
VALUE
Selectors
0x74e18e96, 0xc87b56dd, 0xe8a3d485
Access
Public view
FLAG
OBSERVATION
△
All three depend on the mutable renderer pointer. What these return is not constrained by what the registry stores
☑
The registry flattens the extension mapping via _extrasOf before forwarding, because the renderer is pure and cannot read state itself
☑
contractURI forwards in hand-written assembly, returning the callee's returndata verbatim rather than decoding and re-encoding a dynamic string — the contract had only a few hundred bytes of EIP-170 headroom
◇
The JSON v field reports whether name/symbol/decimals were read from the token. It is a statement about provenance, not about the token being trustworthy
◇
Cards are fully self-contained: the SVG is inlined and base64-encoded into a data URI with no external fetch
The list-building reads. rankedIds returns every id sorted by descending rank; rankedIdsPaged slices that; summariesPaged returns listings without the unbounded fields —
no logo, no description, no links — which is what a token picker or dropdown actually needs.
ATTRIBUTE
VALUE
Selectors
0xdf7ca268, 0xbe9a4952, 0x9ca6a2bc
Access
Public view
FLAG
OBSERVATION
☑
rankedIds packs rank and index into one word so a single LibSort.sort orders both keys, then reverses for descending order
☑
summariesPaged is bounded per row, so page size is predictable. A whole-struct page would be megabytes once 24 KB logos are counted, and would hit provider eth_call limits
◇
Intended for eth_call, not on-chain use — rankedIds allocates and sorts the full set on every invocation
◇
Ties break by array position, which changes when a delisting swap-pops the last entry into the removed slot
The three id derivation rules. A local EVM listing's id is literally its address as a uint256; everything else is a keccak256 hash with bit 255 set; reservations use a separate
domain tag.
ATTRIBUTE
VALUE
Selectors
0xd94fe832, 0x7695a541, 0xdf7667d7
Access
Public pure / view
FLAG
OBSERVATION
☑
The two id spaces are disjoint by construction — local ids are below 2^160, foreign ids have bit 255 set
☑
The native asset resolves to id 0 under both overloads, so get(address(0)) and idOf(address(0)) agree
☑
EVM accounts must fit in 160 bits, else BadInput(). Without that check a foreign EVM listing could be created whose address rendering reverts, leaving a listing that get returns but json and tokenURI fail on
◇
The identity rule is the one part of the schema that cannot be migrated once consumers cache it
ERC-5192 and interface advertisement. locked returns true for every existing listing. supportsInterface reports ERC-4906 (0x49064906), ERC-5192 (0xb45a3c0e) and, through
Solady's base implementation, ERC-721 and ERC-165.
ATTRIBUTE
VALUE
Selectors
0xb45a3c0e, 0x01ffc9a7
Access
Public view
FLAG
OBSERVATION
☑
Advertising ERC-5192 matters: a wallet that cannot detect the lock renders transfer controls that always revert
☑
Locked is emitted at every mint site, so an indexer classifying soulbound collections from logs alone sees the lock without probing every id
◇
locked reverts Unknown() on a non-existent id rather than returning false
Note that themeOf returns the value as stored. The renderer applies a Rec. 601 luma check and
lightens colours below a threshold before painting, so what a card displays and what themeOf
reports can legitimately differ.