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 | 0x0000006d...32a9a8 (etherscan) |
| Network | Ethereum Mainnet |
| Analysis Date | 2026-08-09 |
Overview
This analysis began from a discrepancy rather than from a contract address. The zFi interface describes bonding zOrgz NFTs to add "conviction" to token listings and sorting those listings by conviction strength. The TokenList registry — deployed three days before this contract and analysed the day before this analysis — contains no such mechanism; its only ordering field, rank, is an onlyOwner setter under a 2-of-3 multisig. Neither the ZORG token nor the zOrgz collection contains the word "bond" or "conviction" anywhere in its verified source.
Locating the real implementation meant working backwards from behaviour to bytecode. Scanning recent transactions from z0r0z's canonical Externally Owned Account (EOA) surfaced repeated calls to allocate(uint256,uint256,uint256) against an address that had not appeared in any prior DNZN analysis. That address resolved to a verified contract named ZorgConviction, and its ITokenListView interface — one method, isListed — settled the relationship to the registry immediately.
From there the work was ordinary source review with on-chain verification at every step. The source is verified as an exact match, so no decompilation was needed, but verified source establishes only what the code says, not what state it holds. Every structural claim in these documents was checked against a live read: the constructor arguments against the seven immutable getters, the storage layout against a raw cast storage dump rather than a compiler-derived guess, the accounting invariants against the contract's actual ZORG and ETH balances, and the participant tables against a full replay of the event log.
The source carries developer comments explaining why particular defences exist — the ragequit reasoning behind the escrow invariant, the front-running scenario behind the maturity gate on decreaseBond, the time-homogeneity argument behind exact halving. Per the analysis rules, purpose was derived from code, not from comments; the comments were used to decide where to look harder, and each claimed defence was then read directly in the code that implements it.
One finding was not visible in the contract at all. Diffing the original renderer against its replacement — both verified, one day apart — revealed a warning added about eternalizing an unlocked bond. Tracing that warning back into the source showed a one-way interaction between eternalize and selectLockTier that no single function reveals on its own, and confirmed on-chain that one receipt had already fallen into it.
Thought Process
mindmap
root((ZorgConviction))
Discovery
Interface claims conviction ranking
TokenList has no such mechanism
rank is onlyOwner, multisig
Grep ZORG and zOrgz sources
zero hits for bond or conviction
Trace deployer EOA transactions
allocate calls to unknown address
Resolve to verified ZorgConviction
Phase 0 Obtain
cast code, 20324 bytes
Etherscan getsourcecode
exact match, solc 0.8.36
6 files, 873-line core
Creation tx is a factory call
CREATE2 vanity, salt extracted
Decode 8 constructor arguments
Phase 1 Discovery
Standalone Solady ERC721
no proxy, no upgrade path
Map 7 external dependencies
TokenList read-only, isListed
zorgz and shares escrowed
weiNames holds the role name
renderer and receiptArt mutable
Catalog 81 ABI entries
35 declared in own source
Derive storage from declaration order
verify with raw cast storage
Phase 2 Deep Dive
Bond lifecycle
bondZorgz to allocate to unbond
receipt id equals escrowed zOrgz id
Conviction math
exponential half-life
exact composition defeats poking
asymptotic bound at 2x weight
Loyalty accounting
reward index scaled 1e27
never executed on mainnet
Access paths
onlyDAO immutable
exec role is a transferable name
Phase 3 Risk
Centralization
arbitrary execute, two invariants
zorgz denylist vs shares assertion
Economic
eternalize is forfeiture
tier-0 eternal caps rate forever
unbounded minimumBond
External dependency
conviction has no on-chain effect
share lock would trap escrow
Verify solvency independently
ETH balance equals liabilities
ZORG balance equals bonded weight
Phase 4 Documentation
Six standard documents
Diff both renderers
Reconstruct positions from logs
Cross-link TokenList analysis
Verification Guide
All on-chain reads were performed against Ethereum mainnet at block 25,714,806 using Foundry cast and the Etherscan v2 API. Every figure comes from a public RPC endpoint or the Etherscan API rather than from an aggregator's interpretation. Log-derived figures — the participant tables and the event counts — depend on the completeness of the Etherscan log index, and were cross-checked against the per-id getters.
External Resources
| RESOURCE | KNOWLEDGE PROVIDED |
|---|---|
| Etherscan — ZorgConviction | Verified source, ABI, compiler settings, constructor arguments |
| Etherscan v2 API | Verified source, the Application Binary Interface (ABI), creation record, event logs, and account transaction lists — via getsourcecode, getabi, getcontractcreation, getLogs, txlist |
| Foundry Book — cast | call, storage, code, codesize, tx, sig, sig-event, balance |
| Solady ERC721 | Why ownership records do not occupy sequential storage slots |
| Solady FixedPointMathLib | Saturation behaviour of powWad, overflow bounds on divWad |
| EIP-1167 | Identifying the DAO and ZORG token as 45-byte minimal proxies |
| EIP-4906 | The metadata-update events this contract does not emit |
| EIP-170 | Code-size ceiling; context for the rendering split |
| DNZN — TokenList Contract Analysis | Listing id derivation, rank semantics, the registry's multisig owner |
| DNZN — z0r0z Entity Profile | Deployer attribution and prior deployment patterns |
Commandline Tools
Tip
Commands below use cast from the Foundry Toolkit. To run the commands below, you must set the RPC URL environment variable:
Locate the Contract from Observed Behaviour
The starting point was a claim in an interface with no matching mechanism in the contract it describes. Confirming the absence came first, then finding where the mechanism actually lives.
# CONFIRM THE REGISTRY'S ORDERING FIELD IS ADMIN-ONLY, NOT STAKE-DRIVEN
cast call 0x0000006013dF75A31678B786061C2B54bf531524 "owner()(address)"
# CONFIRM NEITHER THE TOKEN NOR THE NFT IMPLEMENTS BONDING
curl -s "https://api.etherscan.io/v2/api?chainid=1&module=contract&action=getsourcecode\
&address=0x00a6bA94BBb5474725515De88fE04F854f2dCb12&apikey=$ETHERSCAN_API_KEY" \
| jq -r '.result[0].SourceCode' | grep -ci "conviction\|bond"
# TRACE RECENT ACTIVITY FROM THE STACK'S CANONICAL DEPLOYER EOA
curl -s "https://api.etherscan.io/v2/api?chainid=1&module=account&action=txlist\
&address=0x1C0Aa8cCD568d90d61659F060D1bFb1e6f855A20&startblock=25690000&sort=desc\
&apikey=$ETHERSCAN_API_KEY" | jq -r '.result[] | "\(.to) \(.functionName)"' | sort -u
Establish Contract Identity and Provenance
# RUNTIME SIZE AGAINST THE EIP-170 CEILING
cast codesize 0x0000006d936BA3653b8854490E16e782cd32a9a8
# CREATION RECORD — CREATOR, TX, BLOCK, TIMESTAMP
curl -s "https://api.etherscan.io/v2/api?chainid=1&module=contract\
&action=getcontractcreation&contractaddresses=0x0000006d936BA3653b8854490E16e782cd32a9a8\
&apikey=$ETHERSCAN_API_KEY" | jq -r '.result[0]'
# FIRST TRANSACTION THE DEPLOYER EOA EVER SAW — THE FUNDING SOURCE
curl -s "https://api.etherscan.io/v2/api?chainid=1&module=account&action=txlist\
&address=0xacfba7ce872c6ead99d535586f84b0d68ade4082&sort=asc\
&apikey=$ETHERSCAN_API_KEY" | jq -r '.result[0] | "\(.from) -> \(.to) \(.value)"'
# CONFIRM THE SAME EOA DEPLOYED THE REGISTRY THIS CONTRACT READS
curl -s "https://api.etherscan.io/v2/api?chainid=1&module=contract\
&action=getcontractcreation&contractaddresses=0x0000006013dF75A31678B786061C2B54bf531524\
&apikey=$ETHERSCAN_API_KEY" | jq -r '.result[0].contractCreator'
Verify Immutables Against Constructor Arguments
The constructor takes eight arguments and every one has a public getter. Comparing the two catches any value that has been changed since deployment — which is how the renderer swap was found.
# EACH GETTER SHOULD MATCH ITS CONSTRUCTOR ARGUMENT — EXCEPT renderer, WHICH DOES NOT
ZC=0x0000006d936BA3653b8854490E16e782cd32a9a8
for f in "dao()(address)" "shares()(address)" "zorgz()(address)" "weiNames()(address)" \
"tokenList()(address)" "renderer()(address)" "receiptArt()(address)" \
"halfLife()(uint64)"; do
echo "$f -> $(cast call $ZC "$f")"
done
Verify the Storage Layout Rather Than Deriving It
Declaration order gives a predicted layout. Reading the slots confirms it, and catches packing that is easy to get wrong by hand — here, four of five booleans sharing slot 1 while the fifth spills into slot 2 alone.
# RAW SLOT DUMP FOR THE DECLARED RANGE
for i in $(seq 0 20); do
echo "slot $i: $(cast storage 0x0000006d936BA3653b8854490E16e782cd32a9a8 $i)"
done
# CROSS-CHECK PACKED SLOT 1 AGAINST THE GENERATED GETTERS
cast call 0x0000006d936BA3653b8854490E16e782cd32a9a8 "receiptArt()(address)"
cast call 0x0000006d936BA3653b8854490E16e782cd32a9a8 "halfLife()(uint64)"
cast call 0x0000006d936BA3653b8854490E16e782cd32a9a8 "domainClaimed()(bool)"
cast call 0x0000006d936BA3653b8854490E16e782cd32a9a8 "paused()(bool)"
Verify the Accounting Invariants Independently
The contract asserts two invariants inside _execute. Both can be checked from outside, which is a stronger test than reading the assertion in the source.
# ZORG SIDE: BONDED WEIGHT SHOULD EQUAL THE CONTRACT'S ACTUAL TOKEN BALANCE
ZC=0x0000006d936BA3653b8854490E16e782cd32a9a8
cast call $ZC "totalLoyaltyWeight()(uint256)"
cast call 0x00a6bA94BBb5474725515De88fE04F854f2dCb12 "balanceOf(address)(uint256)" $ZC
# ETH SIDE: HELD BALANCE SHOULD COVER THE SUM OF ALL FOUR RECORDED LIABILITIES
cast balance $ZC
cast call $ZC "totalEthPrincipal()(uint256)"
cast call $ZC "loyaltyRewardReserve()(uint256)"
cast call $ZC "treasuryEth()(uint256)"
cast call $ZC "totalEthCredits()(uint256)"
Reconstruct Participants and Positions from Logs
The contract exposes no enumeration, so the set of receipts and the set of supported listings both have to be replayed from events.
# FULL EVENT LOG FROM DEPLOYMENT
curl -s "https://api.etherscan.io/v2/api?chainid=1&module=logs&action=getLogs\
&address=0x0000006d936BA3653b8854490E16e782cd32a9a8&fromBlock=25694754&toBlock=latest\
&apikey=$ETHERSCAN_API_KEY" > logs.json
# RESOLVE TOPIC HASHES TO EVENT NAMES
cast sig-event "Allocated(uint256,uint256,address,uint256,uint256)"
cast sig-event "Eternalized(address,uint256,uint256,uint256)"
# CONFIRM EACH RECONSTRUCTED POSITION AGAINST THE PER-ID GETTERS
for r in 9953 4205 339 9241 4816 77 156; do
cast call $ZC "bondedWeight(uint256)(uint256)" $r
cast call $ZC "allocatedByBond(uint256)(uint256)" $r
cast call $ZC "eternalBonds(uint256)(bool)" $r
done
Confirm the Relationship to the Registry
The structural claim the rest of the analysis rests on is that conviction cannot move TokenList's ordering. It is checked from both sides.
# THE ONLY CALL THIS CONTRACT MAKES INTO THE REGISTRY
cast call 0x0000006013dF75A31678B786061C2B54bf531524 "isListed(uint256)(bool)" 0
# THE REGISTRY'S OWN ORDERING FIELD IS OWNED BY A DIFFERENT ADDRESS ENTIRELY
cast call 0x0000006013dF75A31678B786061C2B54bf531524 "owner()(address)"
# -> 0x006CD14F36F65eCbB29b2519cCBe63A0DC8549F2 (2-of-3 multisig, not the Moloch DAO)
cast call 0x0000006d936BA3653b8854490E16e782cd32a9a8 "dao()(address)"
# -> 0x5E58BA0e06ED0F5558f83bE732a4b899a674053E (Moloch clone, no role at the registry)
Identify the Governance Chain
# THE ADMIN IS A 45-BYTE MINIMAL PROXY, NOT AN EOA OR A SAFE
cast code 0x5E58BA0e06ED0F5558f83bE732a4b899a674053E
cast code 0x00a6bA94BBb5474725515De88fE04F854f2dCb12
# THE CONSTRUCTOR'S SHARE-LOCK ASSUMPTION, CHECKED TODAY
cast call 0x00a6bA94BBb5474725515De88fE04F854f2dCb12 "transfersLocked()(bool)"
# STAKE CONCENTRATION IN THE TOKEN THAT GOVERNS BOTH
cast call 0x00a6bA94BBb5474725515De88fE04F854f2dCb12 "totalSupply()(uint256)"
cast call 0x00a6bA94BBb5474725515De88fE04F854f2dCb12 "balanceOf(address)(uint256)" \
0x1C0Aa8cCD568d90d61659F060D1bFb1e6f855A20
Diff the Replaced Renderer
Both renderers are verified, so the swap can be examined directly instead of being taken on trust.
# FETCH BOTH SOURCES AND EXTRACT THE RENDERER FILE FROM EACH STANDARD-JSON BUNDLE
for a in 0x000000115b1b95b9e04128a2bcd9ed9d24ab141c \
0x0000006b980ae5e796B3eF484e767993d0E29979; do
curl -s "https://api.etherscan.io/v2/api?chainid=1&module=contract&action=getsourcecode\
&address=$a&apikey=$ETHERSCAN_API_KEY" | jq -r '.result[0].SourceCode' > "$a.json"
done
# THEN DIFF src/dao/ZorgConvictionRenderer.sol FROM EACH BUNDLE
Limitations
Verified source proves compilation, not absence of defects
An exact-match verification proves the deployed bytecode was compiled from the published source. It proves nothing about whether that source is free of defects. We read the code and reasoned about it; we did not fuzz it, formally verify it, or run it against adversarial inputs.
The loyalty distribution path has no production history
_distributeEarlyExitTax, the reward index, the dust-reconciliation arithmetic, and _sweepEmptyLoyaltyReserve have never executed on mainnet, and loyaltyRewardPerWeight is still zero. No early exit has occurred, so our reading of the contract's densest arithmetic rests on the source alone, with no observed behaviour behind it.
Upstream governance was not analysed
The dao immutable points at a Moloch clone. We confirmed it is an ERC-1167 proxy, identified its implementation, and counted its proposals. We did not analyse the Moloch implementation itself, so statements about what the DAO can do at this contract are grounded in this contract's code, while statements about how hard it is for someone to make the DAO do it are not.
Three days of history
Seven bonds across four addresses, with no exits. Nothing here has been tested by a contested vote, a market move, a delisting, or an adversary.
The zOrgz denylist finding has no demonstrated exploit path
We did not identify a working path for a DAO execute to move escrowed zOrgz. What we record is a structural asymmetry between how the two escrowed assets are protected, not a demonstrated exploit.
Assumptions
Listing id semantics come from the registry, not from this contract
allocate treats listingId as an opaque number and checks only isListed. Our mapping of ids to tokens — uint256(uint160(token)) for Ethereum tokens, a wider hashed id for foreign listings — comes from the TokenList analysis and was confirmed by calling isListed and json on the registry for each id observed in the logs.
Effective weight was reconstructed, not read
The contract stores support.weight already boosted and does not expose the per-receipt contribution to a listing. We derived each contribution by combining allocationOf with the receipt's receiptLocks.boostBps, then confirmed the sums reproduce the stored listingState weight exactly for all eight supported listings.
The renderer is treated as the reference interface
html() is described in the source as serving the canonical TokenList view, and it is what the public interface presents. We treat it as the reference client on that basis; we did not measure which client holders actually use. Any other client reading the contract directly would behave differently, and nothing on-chain constrains it.
Independent Verification
Highest-value checks to run without relying on these documents, in rough order of how much of the analysis each one confirms.
The registry relationship
Call owner() on TokenList and dao() on ZorgConviction. They are different addresses with no overlapping role. Then search the verified ZorgConviction source for tokenList — six occurrences: the declaration, three in the constructor, one that passes the address to the renderer, and exactly one call into the registry, which is isListed.
The solvency invariants
Compare totalLoyaltyWeight against the contract's ZORG balance, and cast balance against the sum of the four ETH liability counters. Both matched exactly at our snapshot block. If they ever diverge, something has happened that this analysis did not anticipate.
The tier-0 eternalization trap
Read eternalBonds(9953) and receiptLocks(9953). The first returns true, the second returns a boostBps of 10,000. Then read selectLockTier in the source and confirm its first guard. The position cannot be changed.
The emergency role
Read rolesInstalled(). While it returns false, emergencyPause and emergencyExecute are unreachable. If it ever returns true, call weiNames.ownerOf(execZorgWeiId) to find out who holds the credential, and check whether it has moved since.
The share-lock assumption
Read transfersLocked() on the ZORG token. It is false. If it ever becomes true, existing bonds lose their exit path, and no function on this contract can restore it.
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, receipt and listing figures, dates and elapsed intervals, compiler version, selector values, source line counts, every quantitative claim | Live chain state via cast at a pinned block, Etherscan API v2, the cached verified source and event log, and the two verified renderer sources |
| Standards | Address display format, explorer links on every on-chain reference, table casing, section separators, icon semantics, glossary term wrapping, acronym expansion, nav registration | CLAUDE.md, docs/glossary.md, mkdocs.yml, and the templates under .claude/skills/research-ethereum-contract/ |
| 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, endorsements of the design, speculation about intent or users, absolute security assertions | CLAUDE.md writing standards and the epistemic-humility rules in the analysis skill |
| Filler | Rhetorical padding — meta-narration, telegraphed structure, formulaic emphasis, repeated antithesis | 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. Where two passes reached different conclusions about the same figure, the underlying event log was decoded directly to settle it. A claim was only changed where the contradicting value could be reproduced from a command in the Verification Guide above.
Token Cost Breakdown
| PHASE | DESCRIPTION | TOKENS |
|---|---|---|
| Discovery | Tracing the conviction mechanism from interface claim to contract address | 25 tok |
| Phase 0 | Obtain the Contract | 15 tok |
| Phase 1 | Discovery & Understanding | 30 tok |
| Phase 2 | Deep Dive Analysis | 45 tok |
| Phase 3 | Risk & Trust Analysis | 30 tok |
| Phase 4 | Documentation Generation | 65 tok |
| Phase 5 | Document Verification | 175 tok |
| TOTAL | Complete Contract Analysis | 385 tok |
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.