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 derived from the verified Application Binary Interface (ABI). Entries marked ERC721 come from the Solady base contract rather than this contract's own source.
SELECTOR
FUNCTION SIGNATURE
CATEGORY
0x07e8b38a
bondZorgz(uint256,uint256)
User
0xa86e18ca
bondZorgz(uint256,uint256,uint8)
User
0x0223449b
selectLockTier(uint256,uint8)
User
0xe554ab0a
topUpZorg(uint256,uint256)
User
0x4da20bd9
increaseBond(uint256,uint256)
User
0x9f559cf5
decreaseBond(uint256,uint256)
User
0x27de9e32
unbond(uint256)
User
0x2fd4ecb0
eternalize(uint256)
User
0x1a0ffd57
allocate(uint256,uint256,uint256)
User
0xc917b4b9
claimLoyalty(uint256)
User
0x58bc0a50
withdrawEthCredit(address)
User
0x9161d9c0
claimZorgWei()
User
0x150b7a02
onERC721Received(address,address,uint256,bytes)
User (callback)
0x8ad92797
installExecRoleName(address)
Admin
0xdf63790d
burnExecRole()
Admin
0x22ff2a8e
setHalfLife(uint64)
Admin
0xdf0ce57a
setLockTier(uint8,uint64,uint16)
Admin
0xdd9e9837
setEthBondTerms(uint256,uint64,uint16,uint16)
Admin
0x56d3163d
setRenderer(address)
Admin
0xb715e011
setReceiptArt(address)
Admin
0xd360b1ab
releaseTreasuryEth(address,uint256)
Admin
0xb61d27f6
execute(address,uint256,bytes)
Admin
0x046f7da2
resume()
Admin
0x51858e27
emergencyPause()
Admin (exec role)
0x56b494de
emergencyExecute(address,uint256,bytes)
Admin (exec role)
0xcea5b888
convictionOf(uint256)
View
0x257c8cad
supportOf(uint256)
View
0x1ba8024c
listingState(uint256)
View
0x515f94ee
availableBondWeight(uint256)
View
0x1827ac8f
loyaltyOf(uint256)
View
0x934d2a0a
ethExitQuote(uint256)
View
0x33c34ac3
html()
View
0xc87b56dd
tokenURI(uint256)
View
0x06fdde03
name()
View
0x95d89b41
symbol()
View
0xe0fb17b2
bondedWeight(uint256)
View (getter)
0x09807420
allocatedByBond(uint256)
View (getter)
0x53e45ba5
allocationOf(uint256,uint256)
View (getter)
0x10feefe2
receiptLocks(uint256)
View (getter)
0x467339e2
eternalBonds(uint256)
View (getter)
0x0574ba2b
ethBonds(uint256)
View (getter)
0x322ee5de
lockTiers(uint8)
View (getter)
0x6bd16840
ethBondTerms()
View (getter)
0x387552eb
ethCredits(address)
View (getter)
0x7bc90d1c
halfLife()
View (getter)
0xde9e5054
totalLoyaltyWeight()
View (getter)
0x07c89481
loyaltyRewardPerWeight()
View (getter)
0x95b64569
loyaltyRewardReserve()
View (getter)
0x25b629a9
totalEthPrincipal()
View (getter)
0x00c7f3fb
treasuryEth()
View (getter)
0xc76444ab
totalEthCredits()
View (getter)
0x4e528de8
domainClaimed()
View (getter)
0x31c4c6b4
rolesInstalled()
View (getter)
0x6f8f48b7
execRoleRevoked()
View (getter)
0x5c975abb
paused()
View (getter)
0x8ada6b0f
renderer()
View (getter)
0x2e4989ab
receiptArt()
View (getter)
0x03314efa
shares()
View (immutable)
0x0c8757f0
zorgz()
View (immutable)
0xb9ec0d88
weiNames()
View (immutable)
0x4162169f
dao()
View (immutable)
0x9e2c58ca
tokenList()
View (immutable)
0xd883e1f9
zorgWeiId()
View (immutable)
0x26459b76
execZorgWeiId()
View (immutable)
0x249d39e9
BPS()
View (constant)
0x52a9674b
DOMAIN()
View (constant)
0x5e5cad44
ETERNAL_MIN_ZORG()
View (constant)
0x3d9a581a
MAX_BOOST_BPS()
View (constant)
0x8f125201
MAX_ETH_MATURITY()
View (constant)
0x4616283a
MAX_EXIT_DELAY()
View (constant)
0xa6ac7673
MAX_LOCK_TIER()
View (constant)
0x6352211e
ownerOf(uint256)
View (ERC721)
0x70a08231
balanceOf(address)
View (ERC721)
0x081812fc
getApproved(uint256)
View (ERC721)
0xe985e9c5
isApprovedForAll(address,address)
View (ERC721)
0x01ffc9a7
supportsInterface(bytes4)
View (ERC721)
0x095ea7b3
approve(address,uint256)
User (ERC721)
0xa22cb465
setApprovalForAll(address,bool)
User (ERC721)
0x23b872dd
transferFrom(address,address,uint256)
User (ERC721)
0x42842e0e
safeTransferFrom(address,address,uint256)
User (ERC721)
0xb88d4fde
safeTransferFrom(address,address,uint256,bytes)
User (ERC721)
Summary
CATEGORY
COUNT
Total Functions
81
User Functions
18 (13 declared here, 5 from ERC721)
Admin Functions
12
View Functions
51
Of the 81 ABI entries, 35 are declared in this contract's own source. The remaining 46 break down as 36 compiler-generated getters (22 state variables, 7 immutables, 7 constants) and 10 Solady ERC721 base functions.
Creates a bond. Escrows the caller's zOrgz with id receiptId, weight ZORG shares, and the ETH sent with the call, then mints a receipt NFT reusing the same id. The overload without tier calls the same internal path with tier 0.
ATTRIBUTE
VALUE
Selectors
0xa86e18ca (3-arg), 0x07e8b38a (2-arg)
Parameters
receiptId (must be a zOrgz the caller owns), weight (ZORG, wei), tier (0–3)
Access
Any zOrgz owner, payable, whenNotPaused, nonReentrant
FLAG
OBSERVATION
☑
The receipt id is not a counter — it is the escrowed zOrgz id, so custody and authority cannot drift apart
☑
Selected tier terms are copied onto the receipt, so a later setLockTier cannot retroactively change this holder's exit right or boost
△
An ETH payment of at least ethBondTerms.minimumBond is mandatory. The DAO can raise that minimum without bound for future bonds
△
Both assets must be approved to the governor beforehand. A zOrgz approval left standing after an aborted attempt is a live approval to a contract that can pull it
◇
Tier 0 is valid and yields boostBps = 10_000 (1.00×) with no lock. Five of the 7 bonds opened at tier 0; receipts 4816 and 156 selected tier 3 at bond time, and receipt 4205 selected it afterwards
◇
Emits four events on a locked bond — Bonded, EthBonded, BondLockSelected, CommitmentExtended
CONDITION
REQUIREMENT
Non-zero weight
if (weight == 0) revert BadInput()
Not already bonded
if (bondedWeight[receiptId] != 0) revert BondAlreadyExists()
Caller owns the zOrgz
if (zorgz.ownerOf(receiptId) != msg.sender) revert Unauthorized()
Sufficient ETH
if (msg.value < ethTerms.minimumBond) revert EthBondTooSmall(...)
Sets a receipt's allocation to one listing to an absolute amount. This is the only way weight reaches a listing, and the only way it leaves one. It is also the single point at which this contract touches TokenList.
ATTRIBUTE
VALUE
Selector
0x1a0ffd57
Parameters
receiptId, listingId (a TokenList id), amount (absolute, not a delta)
Access
Receipt holder only, non-payable, nonReentrant
FLAG
OBSERVATION
☑
Only increases are gated. Decreases work while paused and against delisted ids — the source names this explicitly as the fix for a bug that turned a pause or a delisting into permanent confiscation of every bond pointed at that listing
☑
Accrues the listing's conviction before changing its weight, so the accumulator always reflects the interval it actually spent at the old weight
☑
_accrue is idempotent under repeated calls because halving composes exactly, so poking a listing once a block gains nothing
◇
The recorded weight is amount × boostBps / 10_000, capped at 1.5×. The raw allocation is what counts against bondedWeight
◇
This is the only external call to TokenList in the whole contract, and it is isListed(uint256) — a bool
△
isListed is checked at allocation time only. A listing delisted afterwards keeps its accumulated conviction until someone reduces the allocation
△
Nothing here writes to TokenList. The score produced has no on-chain effect on the registry's own rank field
CONDITION
REQUIREMENT
Caller holds the receipt
if (ownerOf(receiptId) != msg.sender) revert Unauthorized()
Increases only: not paused
if (paused) revert Unauthorized()
Increases only: id is listed
if (!ITokenListView(tokenList).isListed(listingId)) revert UnknownListing()
Bond covers the total
if (newAllocated > weight) revert AllocationExceeded()
STEP
ACTION
1
Read the receipt's existing allocation to this listing
2
If the new amount is larger, check paused and isListed
3
Recompute the receipt's total allocation and check it against bondedWeight
4
Accrue the listing's conviction up to now
5
Apply the boosted delta to support.weight
6
Write the new allocation and the new per-receipt total
Full exit. Burns the receipt and returns the exact bonded ZORG plus the escrowed zOrgz. ETH principal, less any early-exit tax, plus accrued loyalty is credited for separate withdrawal.
Converts a bond into a permanent position. The zOrgz and every bonded ZORG share become unrecoverable, the ETH principal transfers to the DAO treasury, and the receipt stays transferable and can still allocate support.
ATTRIBUTE
VALUE
Selector
0x2fd4ecb0
Parameters
receiptId
Access
Receipt holder only, non-payable, nonReentrant
FLAG
OBSERVATION
☒
Irreversible with no recovery path. unbond and decreaseBond both revert PermanentBond() afterwards, and there is no admin override
☒
Does not check the receipt's lock tier. selectLockTier refuses to run on an eternal bond, so eternalizing at tier 0 fixes the rate at 1.00× forever
△
The ETH principal is not refunded — it moves to treasuryEth, from where only releaseTreasuryEth (DAO) can spend it
☑
The receipt continues to accrue loyalty, and _increaseBond skips the maturity and lock renewal for eternal bonds
◇
Minimum 10,000 ZORG (ETERNAL_MIN_ZORG). Both eternalizations to date sat at or just above that floor
◇
Two receipts are eternal: 9953 (tier 0, 10,000 ZORG) and 4205 (tier 3, 10,001 ZORG)
CONDITION
REQUIREMENT
Caller holds the receipt
if (ownerOf(receiptId) != msg.sender) revert Unauthorized()
Not already eternal
if (eternalBonds[receiptId]) revert PermanentBond()
Partial withdrawal of bonded ZORG, down to whatever the receipt still has allocated. The bond cannot be emptied — a full exit is unbond's job.
ATTRIBUTE
VALUE
Selector
0x9f559cf5
Parameters
receiptId, amount (ZORG, wei)
Access
Receipt holder only, non-payable, nonReentrant. Not whenNotPaused
FLAG
OBSERVATION
☑
Gated on the ETH maturity as well as the lock tier. Without that gate a tier-0 holder could front-run a pending early exit with a large topUpZorg, take most of the loyalty half of the tax, and withdraw in the next call for the cost of gas
☑
Cannot reduce a bond below what it has allocated, so allocation accounting stays consistent
☒
Reverts for eternal bonds
◇
total == 0 reverts with BadInput(), which prevents a live receipt whose bond bondZorgz would still read as unbonded
Two names for the same internal path. Adds ZORG to an existing bond without minting another custody NFT. increaseBond is described in the source as a compatibility alias for the first bonded-receipt interface.
ATTRIBUTE
VALUE
Selectors
0xe554ab0a (topUpZorg), 0x4da20bd9 (increaseBond)
Parameters
receiptId, amount
Access
Receipt holder only, whenNotPaused, nonReentrant
FLAG
OBSERVATION
☑
New shares inherit the current reward index, so they can direct support immediately but cannot claim loyalty ETH earned before they arrived
△
Renews both the ETH maturity clock and the lock-tier unlockAt from the moment of the top-up. A small top-up on a tier-3 receipt restarts a 365-day commitment on the entire bond
◇
Skips both renewals for eternal bonds, which have no exit to gate
◇
Used once at the snapshot, on receipt 4205, which was eternalized two blocks (24 seconds) later
Lets a tier-0 receipt take on a hard commitment after the fact, in exchange for a support boost. The tier becomes active immediately and cannot be changed again.
ATTRIBUTE
VALUE
Selector
0x0223449b
Parameters
receiptId, tier (1–3)
Access
Receipt holder only, non-payable
FLAG
OBSERVATION
☑
Requires allocatedByBond == 0, which sidesteps the accounting problem of re-scaling every existing allocation under a new boost
☒
Reverts for eternal bonds. This is the mechanism behind the tier-0 eternalization trap: once eternal, the rate can never be raised
☒
One-way. current.tier != 0 || current.unlockAt != 0 reverts, so a tier cannot be changed or downgraded once selected
◇
Not nonReentrant, which is consistent — it makes no external calls
◇
Used once at the snapshot, on receipt 4205, selecting tier 3 (365 days, 1.50×). The other two tier-3 receipts chose their tier inside bondZorgz
Two halves of the ETH payout path. claimLoyalty moves a receipt's accrued loyalty into the caller's credit balance; withdrawEthCredit pushes a credit balance out to a chosen address.
Two one-purpose entry points. claimZorgWei pulls the zorg.wei name into the contract, points it at the DAO, and sets it as the primary name. onERC721Received is the escrow gate for incoming zOrgz.
ATTRIBUTE
VALUE
Selectors
0x9161d9c0, 0x150b7a02
Access
claimZorgWei: whoever owns zorg.wei, once. onERC721Received: callback only
FLAG
OBSERVATION
☑
The receive hook names the exact expected token id rather than setting a boolean flag, so a second ERC-721 hop inside the same call could not slip through
☑
It also requires operator == address(this) and bondedWeight[tokenId] != 0, which together mean only _bondZorgz can satisfy it
◇
claimZorgWei was consumed on 2026-08-06 by 0x1C0Aa8cC...855A20 — 0xedcfb44b...13166a (tx); domainClaimed is now true
△
The contract now holds zorg.wei and weiNames is not on the _execute denylist, so a DAO execute could move the name out
The DAO's arbitrary-call escape hatch. Forwards any call with any value, then asserts two invariants before accepting the result.
ATTRIBUTE
VALUE
Selector
0xb61d27f6
Parameters
target, value, data
Access
onlyDAO, non-payable, nonReentrant
FLAG
OBSERVATION
☑
Asserts the governor's ZORG balance did not fall and its ETH balance still covers all recorded liabilities, rather than trusting a denylist alone
☑
The ETH invariant includes treasuryEth, so even the DAO's own treasury share must go through releaseTreasuryEth and cannot be swept here
☑
Bubbles the target's revert data verbatim instead of masking it
△
The escrowed zOrgz are protected only by the target == address(zorgz) denylist, not by a custody assertion of the kind used for shares. The source explains why a denylist was insufficient for shares but does not apply the same reasoning to the NFT
△
weiNames is not denylisted, so the zorg.wei name the contract holds is reachable
☒
No timelock or cancellation at this contract. Any delay is a property of the upstream Moloch DAO, not of this code
The emergency operator's two powers. emergencyPause sets paused; emergencyExecute runs the same arbitrary-call path as execute, but only while paused.
ATTRIBUTE
VALUE
Selectors
0x51858e27, 0x56b494de
Access
Holder of the exec.zorg.wei name, via _hasRole
FLAG
OBSERVATION
☒
The credential is a transferable Wei Name Service subdomain, not an address in storage. Whoever ends up holding that name holds the role, including a buyer or a thief
☒
The operator can create the precondition for its own arbitrary-call power: pause first, then execute. No second party is required
☑
Not installed at the snapshot — rolesInstalled is false, so _hasRole returns false and both functions are unreachable
☑
Only the DAO can resume(), so an operator cannot un-pause to cover its tracks
☑
Both calls run through the same two invariants as execute, so escrowed ZORG and user ETH are protected by the same assertions
◇
burnExecRole() retires the role permanently. The source explains the design: the credential is transferable, so the DAO cannot retire it by holding it
CONDITION
REQUIREMENT
Role installed and not revoked
if (!rolesInstalled \|\| execRoleRevoked) return false
Caller holds the name
weiNames.ownerOf(execZorgWeiId) == account, wrapped in try/catch
Sets the global decay constant used by every listing's conviction accumulator.
ATTRIBUTE
VALUE
Selector
0x22ff2a8e
Access
onlyDAO
Current value
259,200 seconds (3 days), unchanged since deployment
FLAG
OBSERVATION
△
The only bound is non-zero. A value of 1 makes conviction snap to live weight almost instantly; a very large value freezes existing standings in place
△
_accrue runs only inside allocate, so a listing's lastUpdated can be days stale. A change to halfLife reprices that entire un-accrued interval retroactively for every listing that has not been touched since
☑
Applies uniformly to all listings, so it cannot be used to advantage one listing over another
The two menus the DAO controls. Both govern terms offered to future bonds; every live receipt keeps the terms it snapshotted at mint.
ATTRIBUTE
VALUE
Selectors
0xdf0ce57a, 0xdd9e9837
Access
onlyDAO
FLAG
OBSERVATION
☑
Neither can rewrite a live holder's exit right, boost, or early-exit quote
☑
setLockTier is bounded: tier 1–3, exitDelay at most 365 days, boostBps between 10,000 and 15,000
△
setEthBondTerms places no upper bound on minimumBond. A large value would make new bonding impractical without any other change
△
earlyExitTaxBps may be set as high as 10,000 — a 100% early-exit tax on future bonds. A prospective bonder must read the current terms rather than assume the defaults
◇
Neither has been called. All values match the constructor defaults
Replace the two contracts that produce all human-facing output. renderer serves html(), the canonical interface; receiptArt serves tokenURI.
ATTRIBUTE
VALUE
Selectors
0x56d3163d, 0xb715e011
Access
onlyDAO
FLAG
OBSERVATION
△
No lock. Unlike TokenList, which offers lockRenderer(), there is no way to make either pointer permanent
△
setReceiptArt changes the metadata of every receipt at once but emits only ReceiptArtSet. No ERC-4906BatchMetadataUpdate is emitted, and the contract does not advertise interface id 0x49064906, so caching clients have no signal to refresh
◇
setRenderer was called once, on 2026-08-07 — 0xf90b79da...f8dd80 (tx) — replacing 0x00000011...ab141c (etherscan) with the current 0x0000006b...E29979 (etherscan). Both are verified; the diff is 23 lines and entirely presentational
☑
Both require the new address to have code, which rules out pointing at an empty address
☑
Neither can affect escrow. tokenURI reverting would not block allocation or redemption
Four narrow DAO controls: spend the treasury share of early-exit taxes, grant the emergency credential, retire it permanently, and un-pause.
ATTRIBUTE
VALUE
Selectors
0xd360b1ab, 0x8ad92797, 0xdf63790d, 0x046f7da2
Access
onlyDAO
FLAG
OBSERVATION
☑
releaseTreasuryEth is bounded by treasuryEth, so it cannot reach user principal, the loyalty reserve, or credited ETH
☑
installExecRoleName is one-shot and verifies the registered subdomain id matches the immutable execZorgWeiId before accepting it
☑
burnExecRole is one-way and does not require holding the credential
△
Un-pausing is DAO-only. If the DAO becomes unable to act, a paused contract stays paused — though unbond, decreaseBond and allocation decreases all remain available in that state
◇
treasuryEth stands at 0.02 ETH, entirely from the two eternalizations. No early-exit tax has ever been levied
The scores a consumer reads. supportOf is live boosted weight plus accrued conviction and is the number the canonical interface ranks by. convictionOf returns only the accrued half. listingState returns the score and the raw weight together.
ATTRIBUTE
VALUE
Selectors
0x257c8cad, 0xcea5b888, 0x1ba8024c
Access
Public view
FLAG
OBSERVATION
☑
All three compute accrual on the fly from lastUpdated, so a stale listing still reports a correct current value without needing a poke
◇
Conviction approaches — but never exceeds — live weight, so supportOf is asymptotically bounded at twice the boosted allocation
◇
An allocation takes effect on the live half immediately; the accrued half is what a long-held position earns
△
These are the only outputs of the whole mechanism, and nothing on-chain consumes them. TokenList's own rank is set by a separate multisig and is not affected
△
There is no enumeration. Finding every listing that carries support means replaying Allocated logs
graph LR
A["allocate() sets<br/>support.weight"] --> B["_accrue() advances<br/>conviction toward weight"]
B --> C["conviction moves<br/>half the gap per halfLife<br/>(3 days)"]
C --> D["supportOf = weight + conviction"]
D --> E["read by html() renderer<br/>and any external client"]
E -.->|"no on-chain effect"| F["TokenList.rank<br/>(multisig-set, unaffected)"]
style D fill:#e1f0ff
style F fill:#ffe1e1
Position-level views. ethExitQuote returns what redeeming right now would yield; loyaltyOf returns settled plus unsettled loyalty; availableBondWeight returns unallocated bond capacity.
ATTRIBUTE
VALUE
Selectors
0x934d2a0a, 0x1827ac8f, 0x515f94ee
Access
Public view
FLAG
OBSERVATION
☑
ethExitQuote reports the tax explicitly rather than only the net, which makes the penalty visible before the transaction
☑
loyaltyOf includes the unsettled portion, so a holder does not need to call a state-changing function to see what is owed
△
Neither reflects the lock-tier gate. A quote can look attractive on a receipt that unbond would still reject with ReceiptLocked
◇
availableBondWeight returns 0 for an unbonded id rather than reverting
Presentation. tokenURI forwards to receiptArt; html() forwards to renderer and is described in the source as the canonical TokenList interface. name() and symbol() are hard-coded pure functions.
ATTRIBUTE
VALUE
Selectors
0xc87b56dd, 0x33c34ac3, 0x06fdde03, 0x95d89b41
Access
Public view / pure
Values
name() = "zOrgz Bond", symbol() = "zORGZ"
FLAG
OBSERVATION
☑
tokenURI calls ownerOf(receiptId) first, so it reverts for a non-existent token rather than returning metadata for one
☑
Both are view paths that write no state, so neither can alter escrow; metadata failures reach a cosmetic fallback
△
Both forward to DAO-replaceable contracts with no lock, so the entire human-facing surface — including the interface that ranks listings by conviction — is mutable by a single DAO call
◇
symbol() returns "zORGZ", which differs from the escrowed collection's name ("zOrgz") only by case; that collection's own symbol is "zzz". Interfaces showing a receipt beside its escrowed token may be hard to tell apart