Skip to content

Functions

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

Function Selectors

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.


User Functions

Function: bondZorgz(uint256 receiptId, uint256 weight, uint8 tier)

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(...)
Tier is configured Non-zero tiers require exitDelay != 0 and BPS <= boostBps <= MAX_BOOST_BPS
Not paused whenNotPaused
STEP ACTION
1 Read ethBondTerms into memory and resolve the selected LockTier
2 Pull weight ZORG from the caller via safeTransferFrom
3 Record bondedWeight, add to totalLoyaltyWeight
4 Write the EthBond with principal = msg.value, rewardIndex = loyaltyRewardPerWeight, maturityAt = now + maturity
5 Write the ReceiptLock snapshot (exitDelay, unlockAt, boostBps, tier)
6 Set _expectedZorgzId = receiptId + 1, pull the zOrgz, clear the flag
7 Re-verify the governor now owns the zOrgz, then _mint the receipt to the caller
VARIABLE CHANGE
bondedWeight[receiptId] 0 → weight
totalLoyaltyWeight += weight
ethBonds[receiptId] New EthBond struct
totalEthPrincipal += msg.value
receiptLocks[receiptId] New ReceiptLock struct
_expectedZorgzId 0 → receiptId + 1 → 0 within the call
CONDITION REVERT
Zero weight BadInput()
Bond already exists for this id BondAlreadyExists()
Caller does not own the zOrgz Unauthorized()
msg.value below the minimum EthBondTooSmall(provided, minimum)
Tier out of range or unconfigured LockTierUnavailable()
zOrgz did not actually arrive UnsupportedZorgzTransfer()
Contract paused Unauthorized()
Re-entered Reentrancy()
function _bondZorgz(uint256 receiptId, uint256 weight, uint8 tier) internal {
    if (weight == 0) revert BadInput();
    if (bondedWeight[receiptId] != 0) revert BondAlreadyExists();
    if (zorgz.ownerOf(receiptId) != msg.sender) revert Unauthorized();
    EthBondTerms memory ethTerms = ethBondTerms;
    if (msg.value < ethTerms.minimumBond) revert EthBondTooSmall(msg.value, ethTerms.minimumBond);
    LockTier memory selected = lockTiers[tier];
    if (tier == 0) {
        selected = LockTier({exitDelay: 0, boostBps: BPS});
    } else if (selected.exitDelay == 0 || selected.boostBps < BPS || selected.boostBps > MAX_BOOST_BPS) {
        revert LockTierUnavailable();
    }
    shares.safeTransferFrom(msg.sender, address(this), weight);
    bondedWeight[receiptId] = weight;
    totalLoyaltyWeight += weight;
    uint64 bondTime = uint64(block.timestamp);
    uint64 maturityAt = bondTime + ethTerms.maturity;
    ethBonds[receiptId] = EthBond({
        principal: msg.value,
        accruedLoyalty: 0,
        rewardIndex: loyaltyRewardPerWeight,
        bondedAt: bondTime,
        maturityAt: maturityAt,
        maturity: ethTerms.maturity,
        earlyExitTaxBps: ethTerms.earlyExitTaxBps,
        treasuryShareBps: ethTerms.treasuryShareBps
    });
    totalEthPrincipal += msg.value;
    uint64 lockedUntil = tier == 0 ? 0 : bondTime + selected.exitDelay;
    receiptLocks[receiptId] =
        ReceiptLock({exitDelay: selected.exitDelay, unlockAt: lockedUntil, boostBps: selected.boostBps, tier: tier});
    _expectedZorgzId = receiptId + 1;
    zorgz.safeTransferFrom(msg.sender, address(this), receiptId);
    _expectedZorgzId = 0;
    if (zorgz.ownerOf(receiptId) != address(this)) revert UnsupportedZorgzTransfer();
    _mint(msg.sender, receiptId);
    emit Bonded(msg.sender, receiptId, weight);
    emit EthBonded(msg.sender, receiptId, msg.value, maturityAt);
    emit BondLockSelected(msg.sender, receiptId, tier, selected.exitDelay, selected.boostBps);
    if (lockedUntil != 0) emit CommitmentExtended(receiptId, lockedUntil);
}

Function: allocate(uint256 receiptId, uint256 listingId, uint256 amount)

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
VARIABLE CHANGE
_listingSupport[listingId].conviction Advanced toward weight over the elapsed interval
_listingSupport[listingId].lastUpdated → block.timestamp
_listingSupport[listingId].weight ± (newEffective - oldEffective)
allocationOf[receiptId][listingId] → amount
allocatedByBond[receiptId] → newAllocated
CONDITION REVERT
Caller is not the receipt holder Unauthorized()
Increase while paused Unauthorized()
Increase toward an unlisted id UnknownListing()
Total allocation exceeds the bond AllocationExceeded()
function allocate(uint256 receiptId, uint256 listingId, uint256 amount) external nonReentrant {
    if (ownerOf(receiptId) != msg.sender) revert Unauthorized();
    uint256 oldAmount = allocationOf[receiptId][listingId];
    if (amount > oldAmount) {
        if (paused) revert Unauthorized();
        if (!ITokenListView(tokenList).isListed(listingId)) revert UnknownListing();
    }
    uint256 newAllocated = allocatedByBond[receiptId] - oldAmount + amount;
    uint256 weight = bondedWeight[receiptId];
    if (newAllocated > weight) revert AllocationExceeded();

    ListingSupport storage support = _listingSupport[listingId];
    _accrue(support);
    uint16 boostBps = receiptLocks[receiptId].boostBps;
    uint256 oldEffective = _effectiveWeight(oldAmount, boostBps);
    uint256 newEffective = _effectiveWeight(amount, boostBps);
    if (newEffective > oldEffective) support.weight += newEffective - oldEffective;
    else support.weight -= oldEffective - newEffective;
    allocationOf[receiptId][listingId] = amount;
    allocatedByBond[receiptId] = newAllocated;
    emit Allocated(listingId, receiptId, msg.sender, amount, weight - newAllocated);
}

Function: unbond(uint256 receiptId)

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.

ATTRIBUTE VALUE
Selector 0x27de9e32
Parameters receiptId
Access Receipt holder only, non-payable, nonReentrant. Deliberately not whenNotPaused
FLAG OBSERVATION
☑ Not gated on whenNotPaused. Combined with allocate allowing decreases while paused, a holder can unwind and exit regardless of admin state
☑ The exiting receipt is removed from totalLoyaltyWeight before its own tax is distributed, so it cannot claim a share of the penalty it just paid
☑ An approved operator can move the receipt but cannot redeem it — only ownerOf may call
☒ Eternal bonds can never call this. PermanentBond() is unconditional and there is no override, not even for the DAO
△ Every allocation must be zeroed first, which requires one allocate call per supported listing
◇ Zero unbonds have occurred at the snapshot
CONDITION REQUIREMENT
Caller holds the receipt if (ownerOf(receiptId) != msg.sender) revert Unauthorized()
Not eternal if (eternalBonds[receiptId]) revert PermanentBond()
No live allocations if (allocatedByBond[receiptId] != 0) revert BondHasActiveAllocations()
Lock commitment complete if (terms.unlockAt != 0 && block.timestamp < terms.unlockAt) revert ReceiptLocked(...)
STEP ACTION
1 Settle pending loyalty into accruedLoyalty
2 Compute the early-exit tax if block.timestamp < maturityAt
3 Remove the receipt's weight and principal from the global totals
4 Delete the bond, lock, ETH bond, and eternal flag records
5 Distribute the tax between treasury and the remaining loyalty pool
6 Credit principal - tax + loyalty to ethCredits[msg.sender]
7 Burn the receipt, return the ZORG, return the zOrgz
CONDITION REVERT
Not the holder Unauthorized()
Eternal bond PermanentBond()
Allocations still live BondHasActiveAllocations()
Still inside the hard lock ReceiptLocked(unlockAt)
function unbond(uint256 receiptId) external nonReentrant {
    if (ownerOf(receiptId) != msg.sender) revert Unauthorized();
    if (eternalBonds[receiptId]) revert PermanentBond();
    if (allocatedByBond[receiptId] != 0) revert BondHasActiveAllocations();
    ReceiptLock memory terms = receiptLocks[receiptId];
    if (terms.unlockAt != 0 && block.timestamp < terms.unlockAt) revert ReceiptLocked(terms.unlockAt);
    uint256 weight = bondedWeight[receiptId];
    _settleLoyalty(receiptId);
    EthBond memory ethBond = ethBonds[receiptId];
    uint256 loyalty = ethBond.accruedLoyalty;
    uint256 principal = ethBond.principal;
    uint256 tax;
    if (block.timestamp < ethBond.maturityAt) {
        tax = FixedPointMathLib.fullMulDiv(principal, ethBond.earlyExitTaxBps, BPS);
        principal -= tax;
    }
    totalLoyaltyWeight -= weight;
    totalEthPrincipal -= ethBond.principal;
    if (loyalty != 0) loyaltyRewardReserve -= loyalty;
    delete bondedWeight[receiptId];
    delete receiptLocks[receiptId];
    delete ethBonds[receiptId];
    delete eternalBonds[receiptId];
    if (tax != 0) _distributeEarlyExitTax(receiptId, tax, ethBond.treasuryShareBps, msg.sender);
    _sweepEmptyLoyaltyReserve();
    _creditEth(msg.sender, principal + loyalty);
    _burn(receiptId);
    shares.safeTransfer(msg.sender, weight);
    zorgz.safeTransferFrom(address(this), msg.sender, receiptId);
    emit Unbonded(msg.sender, receiptId, weight);
}

Function: eternalize(uint256 receiptId)

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()
Meets the floor if (weight < ETERNAL_MIN_ZORG) revert BadInput()
VARIABLE CHANGE
eternalBonds[receiptId] false → true
ethBonds[receiptId].principal → 0
ethBonds[receiptId].maturityAt → 0
totalEthPrincipal -= principal
treasuryEth += principal
function eternalize(uint256 receiptId) external nonReentrant {
    if (ownerOf(receiptId) != msg.sender) revert Unauthorized();
    if (eternalBonds[receiptId]) revert PermanentBond();
    uint256 weight = bondedWeight[receiptId];
    if (weight < ETERNAL_MIN_ZORG) revert BadInput();
    eternalBonds[receiptId] = true;
    EthBond storage ethBond = ethBonds[receiptId];
    uint256 principal = ethBond.principal;
    ethBond.principal = 0;
    ethBond.maturityAt = 0;
    totalEthPrincipal -= principal;
    treasuryEth += principal;
    emit Eternalized(msg.sender, receiptId, weight, principal);
}

Function: decreaseBond(uint256 receiptId, uint256 amount)

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
CONDITION REVERT
Zero amount, or would empty the bond BadInput()
Not the holder Unauthorized()
Eternal bond PermanentBond()
Inside the lock or the ETH maturity ReceiptLocked(timestamp)
Would fall below allocated weight AllocationExceeded()
function decreaseBond(uint256 receiptId, uint256 amount) external nonReentrant {
    if (amount == 0) revert BadInput();
    if (ownerOf(receiptId) != msg.sender) revert Unauthorized();
    if (eternalBonds[receiptId]) revert PermanentBond();
    uint64 unlockAt = receiptLocks[receiptId].unlockAt;
    if (unlockAt != 0 && block.timestamp < unlockAt) revert ReceiptLocked(unlockAt);
    uint64 maturityAt = ethBonds[receiptId].maturityAt;
    if (maturityAt != 0 && block.timestamp < maturityAt) revert ReceiptLocked(maturityAt);
    _settleLoyalty(receiptId);
    uint256 total = bondedWeight[receiptId] - amount;
    if (total == 0) revert BadInput();
    if (total < allocatedByBond[receiptId]) revert AllocationExceeded();
    bondedWeight[receiptId] = total;
    totalLoyaltyWeight -= amount;
    _sweepEmptyLoyaltyReserve();
    shares.safeTransfer(msg.sender, amount);
    emit BondDecreased(msg.sender, receiptId, amount, total);
}

Function: topUpZorg(uint256, uint256) / increaseBond(uint256, uint256)

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
function _increaseBond(uint256 receiptId, uint256 amount) internal {
    if (amount == 0) revert BadInput();
    if (ownerOf(receiptId) != msg.sender) revert Unauthorized();
    _settleLoyalty(receiptId);
    shares.safeTransferFrom(msg.sender, address(this), amount);
    uint256 total = bondedWeight[receiptId] + amount;
    bondedWeight[receiptId] = total;
    totalLoyaltyWeight += amount;
    EthBond storage ethBond = ethBonds[receiptId];
    uint64 maturityAt;
    if (!eternalBonds[receiptId]) {
        maturityAt = uint64(block.timestamp) + ethBond.maturity;
        ethBond.maturityAt = maturityAt;
    }
    ReceiptLock storage terms = receiptLocks[receiptId];
    if (terms.tier != 0 && !eternalBonds[receiptId]) {
        terms.unlockAt = uint64(block.timestamp) + terms.exitDelay;
        emit CommitmentExtended(receiptId, terms.unlockAt);
    }
    emit BondIncreased(msg.sender, receiptId, amount, total);
    if (maturityAt != 0) emit EthBondMaturityReset(receiptId, maturityAt);
}

Function: selectLockTier(uint256 receiptId, uint8 tier)

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
function selectLockTier(uint256 receiptId, uint8 tier) external {
    if (ownerOf(receiptId) != msg.sender) revert Unauthorized();
    if (eternalBonds[receiptId]) revert PermanentBond();
    if (allocatedByBond[receiptId] != 0) revert BondHasActiveAllocations();
    ReceiptLock storage current = receiptLocks[receiptId];
    if (current.tier != 0 || current.unlockAt != 0) revert BadInput();
    LockTier memory selected = lockTiers[tier];
    if (tier == 0 || selected.exitDelay == 0 || selected.boostBps < BPS || selected.boostBps > MAX_BOOST_BPS) {
        revert LockTierUnavailable();
    }
    current.exitDelay = selected.exitDelay;
    current.boostBps = selected.boostBps;
    current.tier = tier;
    current.unlockAt = uint64(block.timestamp) + selected.exitDelay;
    emit BondLockSelected(msg.sender, receiptId, tier, selected.exitDelay, selected.boostBps);
    emit CommitmentExtended(receiptId, current.unlockAt);
}

Function: claimLoyalty(uint256) / withdrawEthCredit(address)

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.

ATTRIBUTE VALUE
Selectors 0xc917b4b9, 0x58bc0a50
Access Receipt holder / credit owner, non-payable, nonReentrant
FLAG OBSERVATION
☑ Splitting credit from transfer means a recipient that rejects ETH cannot trap a zOrgz or its shares inside the governor
☑ The loyalty claim follows the receipt, not the address that created the bond
◇ loyaltyRewardPerWeight is still zero at the snapshot — no early exit has occurred, so no loyalty has ever been distributed
◇ withdrawEthCredit takes an arbitrary recipient, so credit can be routed away from the claiming address
function claimLoyalty(uint256 receiptId) external nonReentrant {
    if (ownerOf(receiptId) != msg.sender) revert Unauthorized();
    _settleLoyalty(receiptId);
    EthBond storage ethBond = ethBonds[receiptId];
    uint256 amount = ethBond.accruedLoyalty;
    if (amount == 0) revert NoEthCredit();
    ethBond.accruedLoyalty = 0;
    loyaltyRewardReserve -= amount;
    _creditEth(msg.sender, amount);
    emit LoyaltyAccrued(receiptId, amount);
}

function withdrawEthCredit(address payable recipient) external nonReentrant {
    if (recipient == address(0)) revert BadInput();
    uint256 amount = ethCredits[msg.sender];
    if (amount == 0) revert NoEthCredit();
    ethCredits[msg.sender] = 0;
    totalEthCredits -= amount;
    (bool ok,) = recipient.call{value: amount}("");
    if (!ok) revert EthTransferFailed();
    emit EthCreditClaimed(msg.sender, recipient, amount);
}

Function: claimZorgWei() / onERC721Received(...)

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
function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata)
    external
    view
    returns (bytes4)
{
    if (
        msg.sender != address(zorgz) || _expectedZorgzId != tokenId + 1 || operator != address(this)
            || bondedWeight[tokenId] == 0
            || from == address(0)
    ) revert UnsupportedZorgzTransfer();
    return this.onERC721Received.selector;
}

Admin Functions

Function: execute(address target, uint256 value, bytes calldata data)

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
◇ Never called at the snapshot
function _execute(address target, uint256 value, bytes calldata data, bool emergency)
    internal
    returns (bytes memory result)
{
    if (target == address(0)) revert BadInput();
    if (target == address(zorgz) || target == shares) revert ProtectedAsset();
    uint256 escrow = SafeTransferLib.balanceOf(shares, address(this));
    uint256 ethEscrow = _ethLiability();
    (bool ok, bytes memory returned) = target.call{value: value}(data);
    if (!ok) {
        assembly ("memory-safe") {
            revert(add(returned, 0x20), mload(returned))
        }
    }
    if (SafeTransferLib.balanceOf(shares, address(this)) < escrow) revert EscrowReduced();
    if (address(this).balance < ethEscrow) revert EthEscrowReduced();
    emit Executed(target, value, data, msg.sender, emergency);
    return returned;
}

Function: emergencyPause() / emergencyExecute(address,uint256,bytes)

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
emergencyExecute only paused must already be true
function emergencyPause() external {
    if (!_hasRole(execZorgWeiId, msg.sender)) revert Unauthorized();
    paused = true;
    emit PauseSet(true, msg.sender);
}

function emergencyExecute(address target, uint256 value, bytes calldata data)
    external
    nonReentrant
    returns (bytes memory result)
{
    if (!_hasRole(execZorgWeiId, msg.sender) || !paused) revert Unauthorized();
    result = _execute(target, value, data, true);
}

function _hasRole(uint256 tokenId, address account) internal view returns (bool) {
    if (!rolesInstalled || execRoleRevoked) return false;
    try weiNames.ownerOf(tokenId) returns (address owner_) {
        return owner_ == account;
    } catch {
        return false;
    }
}

Function: setHalfLife(uint64 halfLife_)

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
function setHalfLife(uint64 halfLife_) external onlyDAO {
    if (halfLife_ == 0) revert BadInput();
    halfLife = halfLife_;
    emit HalfLifeSet(halfLife_);
}

Function: setLockTier(uint8,uint64,uint16) / setEthBondTerms(uint256,uint64,uint16,uint16)

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
function setEthBondTerms(uint256 minimumBond, uint64 maturity, uint16 earlyExitTaxBps, uint16 treasuryShareBps)
    external
    onlyDAO
{
    if (
        minimumBond == 0 || maturity == 0 || maturity > MAX_ETH_MATURITY || earlyExitTaxBps == 0
            || earlyExitTaxBps > BPS || treasuryShareBps == 0 || treasuryShareBps > BPS
    ) revert EthTermsOutOfRange();
    ethBondTerms = EthBondTerms({
        minimumBond: minimumBond,
        maturity: maturity,
        earlyExitTaxBps: earlyExitTaxBps,
        treasuryShareBps: treasuryShareBps
    });
    emit EthBondTermsSet(minimumBond, maturity, earlyExitTaxBps, treasuryShareBps);
}

Function: setRenderer(address) / setReceiptArt(address)

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-4906 BatchMetadataUpdate 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
function setRenderer(IZorgConvictionRenderer renderer_) external onlyDAO {
    if (address(renderer_).code.length == 0) revert BadInput();
    renderer = renderer_;
    emit RendererSet(address(renderer_));
}

function setReceiptArt(IZorgReceiptArt receiptArt_) external onlyDAO {
    if (address(receiptArt_).code.length == 0) revert BadInput();
    receiptArt = receiptArt_;
    emit ReceiptArtSet(address(receiptArt_));
}

Function: releaseTreasuryEth(address payable,uint256) / installExecRoleName(address) / burnExecRole() / resume()

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
function releaseTreasuryEth(address payable recipient, uint256 amount) external onlyDAO nonReentrant {
    if (recipient == address(0) || amount == 0 || amount > treasuryEth) revert BadInput();
    treasuryEth -= amount;
    (bool ok,) = recipient.call{value: amount}("");
    if (!ok) revert EthTransferFailed();
    emit TreasuryEthReleased(recipient, amount);
}

function installExecRoleName(address execHolder) external onlyDAO {
    if (!domainClaimed) revert DomainNotClaimed();
    if (execHolder == address(0)) revert BadInput();
    if (rolesInstalled || execRoleRevoked) revert ExecRoleAlreadyInstalled();
    uint256 execId = weiNames.registerSubdomainFor("exec", zorgWeiId, execHolder);
    if (execId != execZorgWeiId) revert NotZorgWei();
    rolesInstalled = true;
    emit ExecRoleInstalled(execId, execHolder);
}

View Functions

Function: supportOf(uint256) / convictionOf(uint256) / listingState(uint256)

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
function convictionOf(uint256 listingId) external view returns (uint256) {
    return _accruedConviction(_listingSupport[listingId]);
}

function supportOf(uint256 listingId) external view returns (uint256) {
    return _score(_listingSupport[listingId]);
}

function _score(ListingSupport storage support) internal view returns (uint256) {
    return support.weight + _accruedConviction(support);
}

function _advance(uint256 current, uint256 target, uint256 elapsed) internal view returns (uint256) {
    if (elapsed == 0 || current == target) return current;
    uint256 remaining =
        uint256(FixedPointMathLib.powWad(0.5e18, int256(FixedPointMathLib.divWad(elapsed, halfLife))));
    uint256 gap = current > target ? current - target : target - current;
    gap = FixedPointMathLib.fullMulDiv(gap, remaining, 1e18);
    return current > target ? target + gap : target - gap;
}
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

Function: ethExitQuote(uint256) / loyaltyOf(uint256) / availableBondWeight(uint256)

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
function ethExitQuote(uint256 receiptId)
    external
    view
    returns (uint256 returnedPrincipal, uint256 tax, uint256 loyalty)
{
    EthBond storage ethBond = ethBonds[receiptId];
    uint256 principal = ethBond.principal;
    if (block.timestamp < ethBond.maturityAt) tax = FixedPointMathLib.fullMulDiv(principal, ethBond.earlyExitTaxBps, BPS);
    return (principal - tax, tax, loyaltyOf(receiptId));
}

Function: tokenURI(uint256) / html() / name() / symbol()

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
function name() public pure override returns (string memory) {
    return "zOrgz Bond";
}

function symbol() public pure override returns (string memory) {
    return "zORGZ";
}

function tokenURI(uint256 receiptId) public view override returns (string memory) {
    ownerOf(receiptId);
    return receiptArt.tokenURI(address(this), receiptId);
}

function html() external view returns (string memory) {
    return renderer.html(address(this), tokenList);
}

Getters and Constants

Compiler-generated getters and immutables, grouped for brevity. All were read directly at block 25,714,806; see Artifacts for the raw values.

FUNCTION RETURNS SNAPSHOT VALUE
halfLife() uint64 259,200 (3 days)
paused() bool false
domainClaimed() bool true
rolesInstalled() bool false
execRoleRevoked() bool false
totalLoyaltyWeight() uint256 371,470 ZORG
loyaltyRewardPerWeight() uint256 0
loyaltyRewardReserve() uint256 0
totalEthPrincipal() uint256 0.05 ETH
treasuryEth() uint256 0.02 ETH
totalEthCredits() uint256 0
ethBondTerms() struct 0.01 ETH / 7 days / 2,000 bps / 5,000 bps
lockTiers(1..3) struct 90d @ 11,000 bps; 180d @ 12,500 bps; 365d @ 15,000 bps
BPS() / MAX_BOOST_BPS() uint16 10,000 / 15,000
MAX_LOCK_TIER() uint8 3
MAX_EXIT_DELAY() / MAX_ETH_MATURITY() uint64 365 days each
ETERNAL_MIN_ZORG() uint256 10,000 ZORG
DOMAIN() string "zorg.wei"
supportsInterface() bool true for 0x01ffc9a7, 0x80ac58cd, 0x5b5e139f; false for 0xb45a3c0e (ERC-5192) and 0x49064906 (ERC-4906)