# Audit report

> moneycanbuy.fun audit round 3: round-2 fix check, new holes, doer-bond risk, full repeat, landing check, mainnet verdict. Audit this repository at the pinned commit c680e49eee80901a398fb0cc60a2ee2d4ec3ecb0, as it is, for a mainnet launch: contracts/src and contracts/script/LaunchPlan.sol, the business logic in README.md, docs/MECHANICS.md, docs/TOKENOMICS.md, docs/LAUNCH-TASK.md, docs/AUDIT-INTERNAL.md, and the static site in landing/. Foundry project is contracts/: run forge from there, import as "src/...". Do not modify, delete or reformat any existing file. Findings go only into the report and into NEW test files under contracts/test/scratch/.
>
> Third audit of the same board. Since b88083a7 (7714a24: 1 medium, 5 low, 22 info, no critical/high), the owner fixed 13a/13b/13c/16, left 20 (DareTrophy) docs-only, moved the cardinality step earlier (19), kept the doer bond at 3% (22, final), and added a calculator in landing/. Treat contracts/src as a full second pass, not a diff; fix checks are reproduced, not assumed.
>
> REPORT FORMAT, MANDATORY. One finding-style entry per item below, same order, even "no issue" (info if none). Missing item = report INCOMPLETE. Entry 0 first: VERDICT, mainnet ready or not, every blocker, max 8 lines. 1-5 FIX CHECKS: closed yes/no, new issue yes/no (file:line, PoC if so). 6 DOER BOND: docs-vs-code only, no fix requested. 7-8 and 9-14: severity, file:line, impact, input/state, smallest fix, Foundry PoC (full source, contracts/test/scratch/, fails now passes fixed, no fork/ffi) if real, else why not. 15 GAS. 16 LANDING. 17-20 BUSINESS: who gains/loses, cost, recommendation. 21 COVERAGE: read in full, not reached, forge result.
>
> Severity: critical = theft, permanent loss or bricked launch; high = loss under realistic conditions or a core promise broken; medium = bounded loss, cheap griefing or a documented guarantee that fails; low = edge cases; info = docs drift, hardening, "no issue".
>
> FIX CHECKS, audit b88083a7, each reproduced, not assumed:
> 1. 13a WALLETS. Round-pass threshold counts distinct NFT owners (ownerOf), not callers: closed yes/no; does delegate.xyz to several hot wallets still pass "3 wallets" via one real holder.
> 2. 13b COOLDOWN. The 7-day post-round cooldown applies only if that round got a keep vote, or the owner closed it: closed yes/no; can an uncontested round still block the next honest hide vote.
> 3. 13c SNIPE/RESTORE. A last-hour hide vote that flips the result extends roundEnd by 1h; owner setState(VISIBLE) over a running round now starts the cooldown too: both closed yes/no.
> 4. 16 WITHDRAW RENEWAL. announceWithdraw (TaskVault, YoloSweetener) reverts while a submission is active or a notice is live, instead of overwriting it: closed yes/no; can a legitimate withdrawal still always get its 1h notice.
> 5. 19/20 DOCS. LAUNCH-TASK.md's cardinality step runs before launch 2; docs/MECHANICS.md or docs/AUDIT-INTERNAL.md records DareTrophy.price() using the current version's oracle (not the task's) as an accepted unfixed low: both present, matching code, closed yes/no.
>
> 6. DOER BOND, finding 22, owner decision 15.10, final, stays 3%, not up for debate, risk/docs only: docs/MECHANICS.md "Why a small doer bond is enough" vs code: max(1 IMD, 0.5 IMD + 3% R); a caught fake proof loses the whole bond and the reward (stays in the task); forfeited bond splits 50/50 disputer/correct NFT voters (desks/DisputeDesk.sol). Report any text/code mismatch and the risk of self-judging staying profitable under the $300 threshold, at most low; do not ask to raise the bond.
> 7. NEW HOLE, HIDDENLIST POST-FIX (should be none, code review not 1-5 again). Can the owner-restore cooldown + 1h round extension be chained to keep an item hidden/visible indefinitely, griefed cheaply, or clash with item 1's ownerOf count.
> 8. NEW HOLE, WITHDRAW POST-FIX. SubmissionActive/NoticeLive reverts (TaskVault, VaultTypes.sol, YoloSweetener): any path where a legitimate contributor never gets a valid notice, or the revert is bypassed to restore the pre-fix front-run.
> 9. REPEAT MONEY, full pass over contracts/src again, same rigor as rounds 1-2. Every outcome and split's rounding, incl. the new HiddenList fields and withdraw-guard errors: steal, double pay, stuck money?
> 10. REPEAT STATE MACHINE. Timers, queue, pending top-ups, busyEpoch, emergencyExit, HiddenList round/cooldown state: ordering, gas or DoS letting one side pick the outcome.
> 11. REPEAT SWARM PATH. AttestationChecker, Intake pricing/callback-gas, questionPrefix, timeouts, silence: confirm no regression since round 2.
> 12. REPEAT NFT VOTING. VoteLib and HiddenList together: one count per token across both, ERC-1271 gas cap, relayer reimbursement abuse.
> 13. REPEAT PRICES/MEV. ChainlinkUniV3PriceOracle, YoloPoolSamples; is the earlier cardinality step (item 5) enough, or can submit() still revert 'OLD' right after launch 2 starts.
> 14. REPEAT ROLES/DOCS VS CODE. ParamsRegistry, ProtocolDirectory, HiddenList owner powers over tasks or deposits; every promise in README.md, docs/ or NatSpec the code does not hold.
> 15. GAS. Launches 1-3 against the EIP-7825 16,777,216 cap and the 24,576-byte contract-size limit with current bytecode (HiddenList, the two withdraw guards, larger comments); do docs/LAUNCH-TASK.md's figures still match.
> 16. LANDING, static site, in scope only here. landing/ makes no network call beyond its declared outbound links (X, Telegram, GitHub, Etherscan, the IMD explorer) and its own local assets: no fetch, no XMLHttpRequest, no analytics, no third-party script src. Calculator constants (doerBondBps 300, bondBase 0.5, minBond 1, disputeBondBps 100, feeBps 1000, nftFeeBps 600, winnerBondBps 5000, minRewardUsd 5, largeRewardUsd 300, authorFeeEth 0.0005, swarmCheckFeeImd 0.5) match VaultParams.sol, desks/Desk.sol, desks/DisputeDesk.sol, TaskVault.sol; no page text promises a behavior the code lacks.
> 17. BOARD ABUSE. Author-doer collusion for a sweetener/trophy; cheap Sybil cluster swinging a dispute or HiddenList vote (incl. via item 1's fix); cheap spam griefing honest users. Price each in IMD, IMD = $7.65.
> 18. DEPOSIT/FEE ECONOMICS besides the bond (item 6): the 10% settlement fee, 1% dispute bond, NFT-voter/relayer split, against realistic task sizes.
> 19. TOKENOMICS. 54% unlocked day 0; the 15% "Stabilizer / MM fund" is an owner decision, kept (risk/disclosure comment only, do not recommend removing it); what gives $YOLO demand; fees funding the project; vesting vs docs/TOKENOMICS.md.
> 20. HARMFUL DARES. Do questionPrefix and the fixed HiddenList layer (incl. items 1-2) form an adequate, cheap, enforceable gate against an illegal dare, and does landing/'s calculator or copy (item 16) overstate it? Cheapest addition still missing?
> 21. COVERAGE, as set out in Report format above.
>
> Not findings: placeholder wallets (0xF001-0xF005, <TREASURY_WALLET>, HiddenList's owner arg); test/mocks; items acknowledged in docs/AUDIT-INTERNAL.md ("Swarm audit 117cf05a" and any round-2 note on b88083a7) unless wrong or a fix regressed; the Stabilizer/MM fund and the 3% bond as to existence/size (item 6 is docs-and-risk only); brand/. Intake, ProjectFactory, Uniswap, Chainlink, delegate.xyz, identity.md NFT and landing/'s outbound links are out of scope except how this code/page trust them.

| | |
|---|---|
| Repository | https://github.com/sergey-mavr-agent/moneycanbuy.fun.git |
| Commit | `c680e49eee80901a398fb0cc60a2ee2d4ec3ecb0` |
| Job | `7cc1034d-9667-4279-8d99-930e2b042042` |
| Judged | 2026-10-15 19:24 UTC |
| Findings | 1 medium · 4 low · 17 info |

Four agents audited the code as it is at `c680e49`, each in one area (math, permissions, economics, control flow),
and a judge reproduced, merged and ranked what they found, then read the code once more itself. Nothing in the repository was changed or deployed.

## Findings

### 8. Medium: 7 NEW HOLE HIDDENLIST POST-FIX: a self-contested round (1 hide NFT + 1 keep NFT of the same actor) buys the 7-day cooldown, repeatable every 8 days; keep-side snipe asymmetry and chainable extension (

`contracts/src/HiddenList.sol:154`

```
                if ((s.keepNfts > 0 || s.ownerCooldown) && block.timestamp < s.roundEnd + COOLDOWN) return;
```

The 13b fix exempts only rounds with keepNfts == 0. 'Contested' is satisfied by one keep vote the round opener casts himself: wallet A votes hide with token 1 (opens a 24h round), wallet B (a second wallet, or a delegate.xyz hot wallet of the same holder, since the keep side has no distinct-owner rule) votes keep with token 2. The round ends 1 vs 1, failed and contested, and line 154 silently returns every hide vote (no revert, no event, counted 0) until roundEnd + 7 days. Repeated every 8 days (2 transactions, 2 NFTs, no owner involvement) the item can be hidden by vote only inside 24h windows the defender himself chooses, which is the 13b outcome the fix meant to close. Impact: a scam or harmful dare's author with 2 identity.md NFTs defeats vote moderation; no money moves (HiddenList is standalone) and the owner's setState still works, so medium (cheap griefing of a documented guarantee), not high. Related lows (same file, not chained to indefinite): (7b) the 1h extension at :206 fires only for a hide vote, so a keep vote at roundEnd-1 that flips a passing round closes it with no reply window and, being contested, starts the 7-day cooldown; and the condition is `_won(s)` after the vote, not 'this vote flipped it', so a passing round is extended 1h by every extra 1-NFT hide vote in its last hour (status reads VISIBLE meanwhile). No clash with item 1's ownerOf count beyond item 1 itself. Smallest fix: start the contested cooldown only after a real attempt, `((s.keepNfts > 0 && s.hideNfts >= QUORUM_NFTS) || s.ownerCooldown)` at :154 (verified: the proof passes, suite green); for 7b, compute wonBefore and extend when any last-hour vote changes _won in either direction, with a total cap. HiddenList has 20,815 bytes and ~9.7M gas of headroom (item 15).

**Reproduction**

forge test --match-path test/scratch/Proof_d7f8192f573d.t.sol from contracts/: on c680e49 all 3 tests FAIL ('genuine hide votes were swallowed by the self-contested cooldown: 1 != 10'; also for the one-owner delegate variant and the every-8-days loop). Same tests against a copy of HiddenList.sol with the one-line fix (test/scratch/Judge_FixedSelfContest.t.sol): 3 PASS. 7b reproduced by test/scratch/Judge_HiddenListAsymmetry.t.sol: keep 11 NFTs at roundEnd-1 vs 10 hide/3 owners -> roundEnd unchanged, VISIBLE, a 10-NFT hide vote the next day opens nothing until roundEnd+7d; six 1-NFT hide votes each at roundEnd-1 move roundEnd by ~6h.

**Proof**: a Foundry test that fails on this code and passes once it is fixed.

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {Test} from "forge-std/Test.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {HiddenList} from "src/HiddenList.sol";

/// @dev Minimal ERC-721 stand-in: ownerOf only (what HiddenList reads).
contract ScratchNft {
    mapping(uint256 => address) public owners;

    function mint(address to, uint256 id) external {
        owners[id] = to;
    }

    function ownerOf(uint256 id) external view returns (address o) {
        o = owners[id];
        require(o != address(0), "none");
    }
}

/// @dev delegate.xyz v2 stand-in: `to` is a delegate of `from` for everything once `set(from, to)` was called.
contract ScratchDelegates {
    mapping(address => mapping(address => bool)) public ok;

    function set(address from, address to) external {
        ok[from][to] = true;
    }

    function checkDelegateForERC721(address to, address from, address, uint256, bytes32) external view returns (bool) {
        return ok[from][to];
    }
}

/// @title Audit round 3, item 7: a self-contested round buys the 7-day cooldown with two NFTs
/// @notice Finding 13b (round 2) made an *uncontested* failed round retryable at once, so that one NFT could not keep
///         an item un-hideable by opening an empty round every 8 days. But "contested" only means `keepNfts > 0`,
///         and the keep vote may come from the same actor: wallet A opens the round with one hide NFT, wallet B (the
///         same holder's second NFT, or a delegate.xyz hot wallet of the same holder voting with his second NFT)
///         answers with one keep vote. 24h later the round has failed *contested* and `HiddenList.sol:154` blocks
///         every hide vote for 7 days. Repeated every 8 days (two transactions, two NFTs, no owner involvement) the
///         item can be hidden by vote only inside the 24h window the defender himself chooses to open.
///         These tests FAIL on c680e49 (the genuine 10-NFT / 3-owner hide vote the day after is swallowed, the round
///         never opens) and PASS once a failed round below hide quorum no longer starts the cooldown (or once a keep
///         vote has to come from an owner other than the round's hide owners / outnumber them to count as contested).
contract HiddenListSelfContestTest is Test {
    HiddenList list;
    ScratchNft nft;
    ScratchDelegates reg;

    address constant OWNER = address(0x7EA5);
    address constant VAULT = address(0xBA07);
    uint8 constant KIND_TASK = 1;
    uint256 constant ITEM = 42;

    address defenderA = address(0xA11CE);
    address defenderB = address(0xB0B);

    function setUp() public {
        nft = new ScratchNft();
        reg = new ScratchDelegates();
        list = new HiddenList(OWNER, address(nft), address(reg));
        vm.warp(1_700_000_000);
    }

    function _ids1(uint256 id) internal pure returns (uint256[] memory a) {
        a = new uint256[](1);
        a[0] = id;
    }

    /// @dev A genuine hide cluster: 10 NFTs held by 3 distinct owners (4 + 3 + 3), each voting from its own wallet.
    function _genuineHideVote() internal {
        uint256 id = 100;
        for (uint256 w; w < 3; ++w) {
            address holder = address(uint160(0xC000 + w));
            uint256 n = w == 0 ? 4 : 3;
            uint256[] memory ids = new uint256[](n);
            for (uint256 i; i < n; ++i) {
                nft.mint(holder, id);
                ids[i] = id++;
            }
            vm.prank(holder);
            list.vote(VAULT, KIND_TASK, ITEM, true, ids);
        }
    }

    /// @notice Two NFTs in two wallets of one actor: hide from A, keep from B, wait 24h. The honest cluster's vote the
    ///         next day must open (and win) a round; today it is blocked by the 7-day cooldown of a round the
    ///         defender contested against himself.
    function test_SelfContestedRound_TwoWallets_BlocksHonestHideFor7Days() public {
        nft.mint(defenderA, 1);
        nft.mint(defenderB, 2);

        vm.prank(defenderA);
        list.vote(VAULT, KIND_TASK, ITEM, true, _ids1(1)); // opens a round: hide 1
        vm.prank(defenderB);
        list.vote(VAULT, KIND_TASK, ITEM, false, _ids1(2)); // keep 1: "contested"

        (, uint32 hide0, uint32 keep0,, uint64 end0) = list.status(VAULT, KIND_TASK, ITEM);
        assertEq(hide0, 1);
        assertEq(keep0, 1);
        assertEq(end0, uint64(block.timestamp + 24 hours));

        vm.warp(end0 + 1); // the round failed (1 hide, 1 keep: no quorum, no majority)

        _genuineHideVote(); // 10 NFTs, 3 owners, the day after

        (uint8 state, uint32 hide, uint32 keep,, uint64 end) = list.status(VAULT, KIND_TASK, ITEM);
        // expected: a fresh round opened with the 10 hide NFTs and 3 owners
        assertEq(hide, 10, "genuine hide votes were swallowed by the self-contested cooldown");
        assertEq(keep, 0);
        assertGt(end, end0, "no new round opened");
        vm.warp(end + 1);
        (state,,,,) = list.status(VAULT, KIND_TASK, ITEM);
        assertEq(state, list.HIDDEN_BY_VOTE(), "item not hidden by vote");
    }

    /// @notice Same with ONE owner: he holds both NFTs and delegates the second to a hot wallet (delegate.xyz). The
    ///         13a fix counts owners for the hide threshold only; the keep side needs no distinct owner at all.
    function test_SelfContestedRound_OneOwnerViaDelegate_BlocksHonestHideFor7Days() public {
        nft.mint(defenderA, 1);
        nft.mint(defenderA, 2);
        reg.set(defenderA, defenderB); // B is A's delegate.xyz hot wallet

        vm.prank(defenderA);
        list.vote(VAULT, KIND_TASK, ITEM, true, _ids1(1));
        vm.prank(defenderB);
        list.vote(VAULT, KIND_TASK, ITEM, false, _ids1(2));
        (, uint32 hide0, uint32 keep0,, uint64 end0) = list.status(VAULT, KIND_TASK, ITEM);
        assertEq(hide0, 1);
        assertEq(keep0, 1);

        vm.warp(end0 + 1);
        _genuineHideVote();

        (, uint32 hide,,, uint64 end) = list.status(VAULT, KIND_TASK, ITEM);
        assertEq(hide, 10, "one owner with two NFTs blocked the honest hide vote for 7 days");
        assertGt(end, end0, "no new round opened");
    }

    /// @notice Cost of keeping the item un-hideable for good: repeat every 8 days. After the cooldown the same two
    ///         wallets open and self-contest again; the honest vote in between is always blocked.
    function test_SelfContestedRound_RepeatsEvery8Days() public {
        nft.mint(defenderA, 1);
        nft.mint(defenderB, 2);
        for (uint256 k; k < 3; ++k) {
            vm.prank(defenderA);
            list.vote(VAULT, KIND_TASK, ITEM, true, _ids1(1));
            vm.prank(defenderB);
            list.vote(VAULT, KIND_TASK, ITEM, false, _ids1(2));
            (,,,, uint64 end) = list.status(VAULT, KIND_TASK, ITEM);
            vm.warp(end + 2 days); // well inside the 7-day cooldown
            _genuineHideVote();
            (, uint32 hide,,,) = list.status(VAULT, KIND_TASK, ITEM);
            assertEq(hide, 10, "honest hide vote blocked");
            vm.warp(end + 7 days + 1); // the defender is back first
        }
    }
}
```

### 7. Low: 6 DOER BOND (docs vs code only): 'half the forfeited bond goes to whoever disputed it' holds only when the NFT decided right, after a relayer-shortfall cut, and never for an author-disputer; cited lin

`docs/MECHANICS.md:232`

```
been profitable. Half the forfeited bond goes to whoever disputed it, the other half to the NFT holders who voted
```

Formula matches: Desk.sol:117-120 gives max(minBond 1 IMD, bondBase 0.5 IMD + 300 bps x R_imd), all penalty; a caught fake loses the whole bond (DisputeDesk.sol:504) and the reward stays in the pot (TaskVault.closeSubmission reduces pot only on Accepted). Mismatches in the paragraph: (a) before any split a winning caller's fee (:510-514) and up to 20% relayer shortfall (:518-522) come off the penalty; (b) the 50/50 split exists only when nftRight (:534); when the NFT did not decide and the swarm rejected, 100% goes to the winners, and when the mandatory check rejected with no disputer, 100% goes to the treasury (:536); (c) a disputer who is a contributor (the task author, the natural disputer below $300) gets only his bond back and his share is redirected to voters or the treasury (:591-592, :539-540; claimDisputer :226-227 for a later top-up), so the person with the strongest motive to catch a fake earns nothing from the bond; (d) the cited `desks/DisputeDesk.sol:522-525`, `:557-584` point at the shortfall cap and _payReward, the split is at :534-540 and :569-596. Risk statement (<= low, no bond change requested, owner decision 15.10): at IMD $7.65 a $299 task bonds 0.5 + 3% x 39.1 = 1.67 IMD ($12.8) against a $269 net reward, a $100 task bonds the 1 IMD floor against $90; a fake is +EV unless caught with probability above ~95% / ~92%; a third-party catcher earns 0.5-0.84 IMD for a 1 IMD + 0.0003 ETH stake, so enforcement below $300 rests on the author disputing his own task. Fix the paragraph and its line references; the landing calculator (item 16) repeats the unconditional 50/50.

**Reproduction**

Compare docs/MECHANICS.md:226-240 with contracts/src/desks/DisputeDesk.sol:509-552 and :569-596. State: Rejected, NFT undecided, swarm 'no', single author-disputer: nftRight false -> toVoters 0; _payWinners: contributorBondTotal == bondTotal -> redirect = rest -> toTreasury; author's claimDisputer returns info.bond only.

### 9. Low: 8 NEW HOLE WITHDRAW POST-FIX: no bypass of SubmissionActive/NoticeLive; but a standing notice covers money topped up after it, so the 1h warning does not hold for later top-ups (TaskVault and YoloSwee

`contracts/src/TaskVault.sol:333`

```
                fromActive = c.active;
```

Bypass search: withdrawNoticeAt is written only by announceWithdraw (:314) and deleted on use (:341); an active share needs at != 0, now in [at, at+1d] and activeSub == 0 (:330-333); Expired/Unwound need no notice but cannot be forced by a contributor. The pre-fix front-run is gone. Residual A (new, low): announceWithdraw records a time only; topUp with no active submission adds to c.active (:289) and withdrawContribution takes all of c.active (:333). An author announces a $5 reward at T, tops up $1000 at T+59m, pulls $1005 at T+60m; WithdrawAnnounced carried 5. YoloSweetener: sweeten after announceWithdraw then withdraw takes all of givenBy (:153). Bounded: the live notice is public (withdrawNoticeAt / pendingWithdraw) and a doer who submits first locks the pot, so only a doer acting on a reward shown for minutes is hurt. Fix: delete the caller's live notice in topUp/sweeten, or snapshot the announced amount and cap withdrawContribution at it. Residual B (cosmetic): error NoticeLive(uint64 validUntil) (VaultTypes.sol:330, YoloSweetener.sol:98) is raised with the due time `existing`, 24h before the value its name promises; revert with existing + WITHDRAW_NOTICE_VALID. Residual C (accepted): a notice always lapses during any submission (25h validity < 24h window + settle), so a serial junk submitter re-blocks at a doubling bond; contributor money is delayed, never lost.

**Reproduction**

Reading of TaskVault.sol:274-296 and :304-348, YoloSweetener.sol:110-175. State: createTask(USDC, 5e6); announceWithdraw at T (at = T+1h, event amount 5e6); topUp(1000e6) at T+59m with activeSub == 0 -> c.active 1005e6; withdrawContribution at T+1h -> fromActive = 1005e6 in one call. Expected: the added 1000e6 waits its own hour. NoticeLive: announce at T, announce again at T+2h -> NoticeLive(T+1h) though the notice is valid until T+25h.

### 15. Low: 14 REPEAT ROLES/DOCS VS CODE: owner powers as documented and no owner function touches tasks or deposits; MECHANICS.md cites stale HiddenList/DisputeDesk line numbers and the unconditional 50/50 split

`docs/MECHANICS.md:34`

```
| NFT holder / delegate (moderation) | `vote` to hide or keep a task or proof on the site (separate from the dispute vote above): 24h round, passes at >= 10 hide NFTs, more hide than keep, >= 3 distinct hide wallets | `HiddenList.sol:115` |
```

ParamsRegistry owner (Ownable2Step): propose params/attesters behind the 48h TIMELOCK with ParamsBounds, cancel, pause createTask only; last attester cannot be removed. ProtocolDirectory safe: timelocked listing only, nothing on chain reads it. HiddenList owner: setState only; the contract has no payable/receive, calls only ownerOf and checkDelegateForERC721, and no core contract imports it (grep -r HiddenList contracts/src shows no importer), so the promise 'cannot freeze or redirect any task, bond or reward' holds. TaskVault has no owner; bank hooks are onlyDesk. Documented trust assumptions stand: a new parameter version may name another intake/nft/oracle for future tasks; setState(VISIBLE) over a live honest round imposes the 7-day cooldown and the owner can repeat it every 7 days (equivalent to KEPT, which the owner already has). Drift: MECHANICS.md:33 cites HiddenList.sol:90 (the onlyOwner modifier; setState is :111) and :34 cites :115 (inside setState; vote is :140); :34 says '>= 3 distinct hide wallets' while the code gate is distinct owners (and item 1's transfer caveat); :232-233 bond-split prose and line references (item 6); LAUNCH-TASK.md:124 '19/13b' label.

**Reproduction**

grep -n onlyOwner/onlySafe/TIMELOCK in ParamsRegistry.sol, ProtocolDirectory.sol; HiddenList.sol external surface = setState, vote, status; compare docs/MECHANICS.md:33-34 and :232-233 with HiddenList.sol:90/111/140 and DisputeDesk.sol:522-525/534-540/557-596.

### 21. Low: 20 HARMFUL DARES: questionPrefix gates only what the swarm sees (R >= $300 or a paid escalation); an undisputed sub-$300 illegal dare is paid by code; the hide list is display-only and can be stalled

`contracts/src/VaultParams.sol:166`

```
        p.questionPrefix =
```

Enforceable gate today: a task >= $300 always meets the prefix through the mandatory check ('no' rejects, bond to the treasury); below $300 the prefix is reached only if someone pays a dispute (1 IMD + 0.0003 ETH) and the NFT vote or an appeal reaches the swarm. A $50 dare with a harmful condition and an undisputed proof is accepted by CaseDesk.step at windowEnd with no human or swarm in the loop, and HiddenList (owner instant, or 10 NFTs / 3 owners / 24h, weakened by items 1 and 7) removes it from the site only: no core contract reads it, the task stays submittable and payable, and the author can withdraw after an hour's notice. Landing: 'judged by NFT holders and the $IMD swarm' and the calculator do not claim a ban, moderation or a legality check, so no overstatement, only an omission that an undisputed dare is judged by nobody. Cheapest addition still missing (no money-rule change): the project wallet disputes anything it hides, within the 24h window, and calls the swarm if the NFT vote is undecided (bond returned on a win; the treasury wallet is not a contributor so it even earns half the doer bond); make the owner's HIDDEN/KEPT state a front-end gate on submit and sweeten; and add one line on the site that undisputed dares are paid automatically. A params change lowering largeRewardUsd (48h timelock) widens the swarm gate without code.

**Reproduction**

Flow: createTask(category irl, R = $50 USDC, condition describing a harmful act); submit (not large, Desk.sol:150); no dispute for 24h; step() at windowEnd finalizes Accepted (CaseDesk.sol:160-161); the swarm and its prefix never run; HiddenList.setState(HIDDEN) changes none of these calls (grep -r HiddenList contracts/src: no importer).

### 1. Info: 0 VERDICT: mainnet ready for launches 0-2; launch 3 (HiddenList) should take the one-line self-contest fix first; no critical/high

`contracts/src/HiddenList.sol:154`

```
                if ((s.keepNfts > 0 || s.ownerCooldown) && block.timestamp < s.roundEnd + COOLDOWN) return;
```

VERDICT (round 3, commit c680e49): no critical, no high. Fix checks 1-5 reproduce as closed (round-2 proofs re-run against the code, HiddenList/TaskVault/YoloSweetener re-read in full). Blocker-class item, launch 3 only: MEDIUM (item 7) the 13b fix is bypassed by a self-contested round: 1 hide NFT + 1 keep NFT of the same actor (two wallets, or one holder plus a delegate.xyz hot wallet) ends the round 'contested' and line 154 drops every honest hide vote for 7 days, repeatable every 8 days. HiddenList is immutable and standalone (no money), so fix before launch 3; one-line change verified (proof fails on c680e49, passes with the change, full suite stays green). Lows: 7b keep-side last-second snipe has no reply window and a passing round is extendable 1h per spare NFT; 1 one holder passes the 3-owner bar by plain transfers (documented Sybil limit, docs should say so); 8 a standing notice covers top-ups made after it (TaskVault.topUp / YoloSweetener.sweeten); 6/14/16 docs and landing describe the 50/50 bond split as unconditional. Operational, not code: the increaseObservationCardinalityNext(180) + observe([1800,0]) gate before launch 2 is the only thing between launch 2 and every ETH/USDC submit reverting 'OLD'. Money, state machine, swarm path, NFT voting, roles: no new defect; forge: 393 passed, 0 failed, 5 skipped (fork). Sizes match LAUNCH-TASK.md byte for byte.

**Reproduction**

Summary entry; the concrete inputs are in items 1-21. Scratch: forge test --match-path 'test/scratch/*' from contracts/: the 4 specialist proofs (8 tests) all FAIL on c680e49 with '1 != 10'; Judge_FixedSelfContest.t.sol (same tests against a copy of HiddenList.sol with line 154 reading `(s.keepNfts > 0 && s.hideNfts >= QUORUM_NFTS) || s.ownerCooldown`) 3 PASS.

### 2. Info: 1 FIX CHECK 13a WALLETS: closed yes; delegate.xyz hot wallets count as one owner; residual: plain transfers to 3 own wallets still pass (documented Sybil limit)

`contracts/src/HiddenList.sol:243`

```
        return s.hideNfts >= QUORUM_NFTS && s.hideNfts > s.keepNfts && s.hideOwners >= MIN_WALLETS;
```

Closed: YES. _won checks hideOwners (distinct ownerOf results, HiddenList.sol:264-267 via _mayVote :273-289) against MIN_WALLETS; hideWallets (distinct callers) is display only. A holder delegating 10 NFTs to two hot wallets raises hideWallets to 3 but hideOwners stays 1, the round does not pass. New issue: NO code hole; the same holder can transfer 3+3 NFTs to two fresh EOAs before voting (ownerOf is read at vote time, never re-checked) and pass with one real person. Cost: 6 ERC-721 transfers of gas, no capital at risk, tokens movable back after the round. This is the inherent on-chain limit (also true of the dispute vote); the NatSpec (:15-17) and MECHANICS.md:34 say 'distinct owners' without this caveat. Docs-only: state that the 3-owner bar resists delegation, not transfers; the owner's setState remains the real guard.

**Reproduction**

Reproduced: contracts/test/scratch/Judge_HiddenListAsymmetry.t.sol test_TransferSplitPassesThreeOwnerBar: holder H mints 1..10, votes hide [1..4], transfers 5-7 to B and 8-10 to C, B and C vote hide; after 24h status == HIDDEN_BY_VOTE (3 owners from one person). Delegate path: Proof_d7f8192f573d test_SelfContestedRound_OneOwnerViaDelegate shows a delegate's keep vote counts (keep side) while the hide-owner rule holds; round-2 proof test/audit/round2/Proof_476db268aaec.t.sol passes in the suite (393/393).

### 3. Info: 2 FIX CHECK 13b COOLDOWN: closed yes for an uncontested round; an uncontested round no longer blocks the next honest hide vote (self-contest residual in item 7)

`contracts/src/HiddenList.sol:154`

```
                if ((s.keepNfts > 0 || s.ownerCooldown) && block.timestamp < s.roundEnd + COOLDOWN) return;
```

Closed: YES as stated. The 7-day cooldown is gated by `keepNfts > 0 || ownerCooldown`; a lone round with no keep vote can be retried the moment it ends (test_UncontestedFailedRoundRetriesAtOnce in test/HiddenList.t.sol passes, round-2 proof Proof_5d8c4edc729c passes asserting the honest vote now opens a round). Can an uncontested round still block: NO. But 'contested' is any keep vote including one from the round opener's own second NFT, so the block re-appears at the price of two NFTs: item 7 (medium, proof).

**Reproduction**

Re-run: forge test --no-match-path 'test/scratch/*' -> 393 passed incl. test/audit/round2/* and HiddenList.t.sol. Self-contest: Proof_d7f8192f573d.t.sol 3 FAIL on c680e49 (1 != 10).

### 4. Info: 3 FIX CHECK 13c SNIPE/RESTORE: both closed yes (late passing hide vote extends 1h; owner setState(VISIBLE) over a started round opens the 7-day cooldown)

`contracts/src/HiddenList.sol:206`

```
        if (hide && activeRound && block.timestamp + 1 hours >= s.roundEnd && _won(s)) {
```

Closed: YES and YES. Lines 206-209 move roundEnd to now+1h when a hide vote inside the last hour leaves _won true (never shortens). Lines 118-127: setState with state VISIBLE and roundEnd != 0 sets roundEnd = now and ownerCooldown = true, so line 154 refuses a new hide round for 7 days whatever keepNfts is; cleared only when a round actually opens (:191). Round-2 proof Proof_ced07852b14f and HiddenList.t.sol test_OwnerRestoreVisibleStartsCooldown pass. Follow-on asymmetry (keep vote never extends; extension fires for already-passing rounds) is item 7b, low.

**Reproduction**

Judge_HiddenListAsymmetry.t.sol test_NonFlippingHideVotesChainExtension confirms the extension path fires (roundEnd moves by 1h per late hide vote); test_KeepSnipeClosesRoundAndLocksSevenDays confirms a keep vote at roundEnd-1 does not extend. Repo tests for both 13c halves pass in the 393.

### 5. Info: 4 FIX CHECK 16 WITHDRAW RENEWAL: closed yes in TaskVault and YoloSweetener; a legitimate withdrawal always gets its 1h notice whenever no submission is active

`contracts/src/TaskVault.sol:310`

```
        if (existing != 0 && block.timestamp <= existing + WITHDRAW_NOTICE_VALID) revert NoticeLive(existing);
```

Closed: YES. TaskVault.announceWithdraw reverts SubmissionActive while t.activeSub != 0 (:308) and NoticeLive while an earlier notice is inside [announce, at+1 day] (:310); YoloSweetener.announceWithdraw mirrors both on the vault's busy flag (:126-128). A standing always-due notice can no longer be rolled over. Legitimate path: announce at T while idle, withdraw in [T+1h, T+25h] with activeSub == 0 (:330-333), notice deleted on use (:341). A submission landing inside the hour locks the share for that case's life (documented intent); the notice lapses at T+25h and the contributor re-announces once idle, which is always eventually possible (every submission ends: window, vote, appeal, or emergencyExit). Griefing by serial junk submissions costs the doer bond x2^rejected (max x256) lost on a won dispute; bounded. Note: YoloSweetener reads busy without advancing timers, so a finished-but-unpoked submission makes announceWithdraw revert until anyone pokes; consistent with the vault's rules.

**Reproduction**

Reading of TaskVault.sol:304-348 and YoloSweetener.sol:120-175 (full). Repo tests on the notice path (Tasks test_announcedWithdrawalGivesTheDoerAnHour, Sweetener S2 fix test) pass in the 393. Input: announce at T; announce again at T+30m -> NoticeLive(T+1h); doer submits at T+40m -> withdrawContribution at T+1h gives fromActive 0 (activeSub != 0).

### 6. Info: 5 FIX CHECK 19/20 DOCS: cardinality gate listed before launch 2; DareTrophy.price() current-oracle rule recorded as accepted low; both match code, closed yes

`docs/LAUNCH-TASK.md:124`

```
6. **Gate before launch 2, not after launch 3** (audit round 2, finding 19/13b): `increaseObservationCardinalityNext(180)`
```

Both present, closed: YES. docs/LAUNCH-TASK.md step 6 (:124-131) runs increaseObservationCardinalityNext(180), 30 minutes of swaps and a live observe([1800,0]) check before launch 2 (step 7) and describes the code path correctly (Desk._priceReward -> imdWeiPerUsdc -> pool.observe at ChainlinkUniV3PriceOracle.sol:132, no try/catch). DareTrophy.sol:101-109 NatSpec and code read registry.core(registry.currentVersion()).priceOracle; docs/AUDIT-INTERNAL.md:381 and docs/MECHANICS.md:311 record it as an accepted, unfixed low bounded by maxYolo. Minor drift only: the step-6 label says 'finding 19/13b' (13b is the cooldown finding); MECHANICS.md:33-34 cite HiddenList.sol:90 and :115 for setState/vote, which are now :111 and :140.

**Reproduction**

Read docs/LAUNCH-TASK.md:120-132, contracts/src/DareTrophy.sol:100-112, docs/AUDIT-INTERNAL.md:381, docs/MECHANICS.md:311, docs/MECHANICS.md:33-34 against HiddenList.sol line numbers.

### 10. Info: 9 REPEAT MONEY: every outcome and split re-traced, no steal, double pay or stuck money; new HiddenList fields and withdraw-guard errors move no money

`contracts/src/desks/DisputeDesk.sol:534`

```
            if (nftRight) toVoters = penalty - penalty * p.winnerBondBps / BPS;
```

Full pass over DisputeDesk._settleDecided/_payWinners/_payReward and the lazy claims: penalty = bond + callerLost; feeBack <= penalty; shortPay <= 20% of the rest; tie: toDoer + toTreasury = penalty; decided: toVoters + winnerShare = penalty with contributor shares redirected to voters (nftRight) or treasury; buckets (disputerReward/Bonds, voterImd/Fee/VotesLeft, shortfallPay/Left, ethLeft/FeesLeft) pay remaining-over-remaining so the last claimant takes the dust; claimed flags stop double claims. Accepted: pot -= reward (= pot frozen at submit), doer R - fee, treasury fee - toVoters. Pending top-ups rejoin only when a busy period ends without acceptance (busyEpoch) and stay withdrawable after acceptance. YoloSweetener payout caps at the large threshold, returnBase shrinks by given so mulDiv returns exactly the rest; withdraw zeroes givenBy. HiddenList adds uint16 hideOwners and bool ownerCooldown only, no payable/receive. SubmissionActive/NoticeLive are pure reverts before any state write. Only oddity: a disputer who became a contributor after disputing has his share sent to the treasury even when nftRight (claimDisputer :227) where _payWinners would send it to voters; inconsistent, not exploitable.

**Reproduction**

Code reading of DisputeDesk.sol:207-279 and :478-596, TaskVault.sol:272-348 and :418-440, YoloSweetener.sol:149-213. Worked: penalty 1 IMD, nftRight, two disputers 1 IMD each (one the author): toVoters 0.5, winnerShare 0.5, toContributors 0.25 redirected to voters (voterImd 0.75), non-contributor claims 0.25 + bond. Invariant.t.sol (liabilities <= balance) passes in the 393.

### 11. Info: 10 REPEAT STATE MACHINE: timers, queue, pending top-ups, busyEpoch, emergencyExit and HiddenList round/cooldown ordering give no side a free pick of the outcome

`contracts/src/TaskVault.sol:607`

```
        for (uint256 i; i < 8; ++i) {
```

_advance plays at most 8 transitions per call and every user action advances first; swarm silence is judged by the signed time plus a fixed grace, never by who pokes; the NFT decision is taken only at voteEnd on the full tally; appeal void/silence returns to Decided/NoDecision with a fresh window; queue head/tail and busyEpoch move only in closeSubmission (next queued sub activates in the same tx, pending merges only when the queue is empty after a rejection); emergencyExit first advances in a gas-bounded self-call and needs stepDeadline + 30d. HiddenList: round state is one slot, written only by a vote that counted >= 1 token (empty/all-bad arrays open nothing), roundEnd doubles as the cooldown anchor and is overwritten only when a round opens, setState bumps round so stale bitmaps never leak. The only timing levers found are the HiddenList items in 7 (who opens a round, who moves last).

**Reproduction**

Code reading of TaskVault.sol:350-378 and :604-629, CaseDesk.sol:140-204 and :355-372, HiddenList.sol:140-210. Trace: Rejected with queue -> next activated same tx; Rejected without queue -> pot += pending, busyEpoch++; Accepted -> pot -= reward, pending untouched. 393 tests incl. Scenarios/Invariant pass.

### 12. Info: 11 REPEAT SWARM PATH: AttestationChecker binding, Intake pricing/callback gas, questionPrefix, timeouts and silence unchanged since round 2; no regression

`contracts/src/TaskVault.sol:585`

```
        if (!ok && gasleft() < ASK_GAS_RESERVE) revert InsufficientGas();
```

onOracleResult binds msg.sender to the Intake stored at ask time and the request id to an unanswered record, dedupes, never reverts; ask() measures spent IMD by balance difference, refuses spent > price or a reused id, and reverts InsufficientGas only when a starved call could be mistaken for an Intake failure (:585). Silence = swarmTimeout 6h + 1h grace or the Intake reporting completed. questionPrefix and the ParamsBounds size checks (prefix + 2 x maxTextBytes + 234 <= 2000, body <= 16384) are unchanged; a live price rise voids a prepaid appeal with refund. git diff 7714a24..c680e49 touches HiddenList, the two announceWithdraw guards, VaultTypes errors, docs and landing only; the swarm path has no diff.

**Reproduction**

git diff --stat 7714a24..c680e49; code reading of TaskVault.sol:444-475 and :561-600, CaseDesk.sol:355-372, VaultParams.sol:166-171 and :231-252. Round-2 swarm regression tests (test/audit/swarm117) pass in the 393.

### 13. Info: 12 REPEAT NFT VOTING: one count per token per submission (VoteLib) and per hide round (HiddenList), separate bitmaps by design; ERC-1271 capped at 500k gas; relayer reimbursement bounded

`contracts/src/libraries/VoteLib.sol:180`

```
            let success := staticcall(ERC1271_GAS, voter, add(data, 0x20), mload(data), 0x00, 0x20)
```

VoteLib: per-submission tokenId bitmap, one side per wallet, ownership/delegation at batch time with try/catch, ERC-1271 via STATICCALL with ERC1271_GAS (500k) and a 32-byte return copy (:180). HiddenList keeps its own per-round bitmap and _walletSide; the same NFT may vote in a dispute and in a hide round, which is intended (different questions), so 'one count across both' is not a requirement. Relayer: paid min(measured + base + calldata, base + 60k x counted) x basefee x 1.1 from the ETH pot, then as IMD shortfall capped at 20% of the penalty; a self-relaying voter cannot exceed the per-vote cap. C-04 (zero bytes at 16 gas) and C-06 (flash-borrowed NFTs) remain acknowledged. No regression.

**Reproduction**

Code reading of VoteLib.sol:72-124 and :142-205, DisputeDesk.sol:88-120 and :413-456, HiddenList.sol:164-210 and :249-269. Project vote tests pass in the 393.

### 14. Info: 13 REPEAT PRICES/MEV: the pre-launch-2 cardinality step is sufficient if its observe check is really run; the code path has no try/catch so a TWAP revert still blocks every ETH/USDC submit (operationa

`contracts/src/oracles/ChainlinkUniV3PriceOracle.sol:132`

```
        (int56[] memory cum,) = usdcImdPool.observe(ago);
```

Uniswap v3 observe reverts 'OLD' only when now-1800 is older than the ring's oldest observation. With cardinality 1 the oldest is the last swap, so every ETH/USDC submit reverts for 30 minutes after any swap. After increaseObservationCardinalityNext(180) the ring grows one slot per swap block and keeps old observations, so once two observations span 30 minutes 'OLD' cannot recur unless 180+ swap blocks land inside 30 minutes (unrealistic for this pool). The docs' gate (step 6: 30 minutes of swaps then a live observe([1800,0]) check) is therefore the right condition. Residual, not new: Desk._priceReward calls imdValue with no try/catch for ETH/USDC tasks, so an 'OLD' or Chainlink StalePrice revert blocks submit on those tasks until it clears; $YOLO tasks are shielded by _priceYolo's try/catch; createTask reads Chainlink only. YoloPoolSamples two-block confirmation and highest-sample rule unchanged. Recommend recording cardinality and the observe result in the launch-2 PR.

**Reproduction**

Reading of ChainlinkUniV3PriceOracle.sol:128-135, Desk.sol:140-188, docs/LAUNCH-TASK.md:124-131 against Uniswap v3 observeSingle semantics. State: cardinality 1, swap 10 minutes ago -> observe([1800,0]) reverts 'OLD' -> CaseDesk.submit on an ETH/USDC task reverts; after the ring holds an observation >= 30 minutes old it succeeds.

### 16. Info: 15 GAS/SIZE: forge build --sizes on c680e49 equals LAUNCH-TASK.md byte for byte (DisputeDesk margin 1,896); launch gas figures are fork-measured and not re-run here

`docs/LAUNCH-TASK.md:59`

```
**DisputeDesk 1,896 bytes**, CaseDesk 4,009, TaskVault 3,853; YoloSweetener 16,992, DareTrophy 12,163, ProtocolDirectory
```

Measured runtime sizes / margin under 24,576: DisputeDesk 22,680 / 1,896; CaseDesk 20,567 / 4,009; TaskVault 20,723 / 3,853; YoloSweetener 7,584 / 16,992; DareTrophy 12,413 / 12,163; ProtocolDirectory 4,546 / 20,030; HiddenList 3,761 / 20,815; ParamsRegistry 16,141 / 8,435: every figure in docs/LAUNCH-TASK.md:55-63 matches. Launch gas (launch 2 14.59M of 16,777,216, margin 2.19M; launch 3 7.09M, margin 9.69M) comes from the fork rehearsal (skipped here, no FORK_URL); the commits since the measurement touch no Solidity, so bytecode and deployment gas are unchanged. The binding constraint is DisputeDesk's 1,896 bytes; the item-7 fix lives in HiddenList (launch 3) and costs a few bytes.

**Reproduction**

cd contracts && forge build --sizes (table above); forge test: ReviewLaunch size tests pass in the 393; fork tests 5 skipped.

### 17. Info: 16 LANDING: no fetch/XHR/beacon/analytics/third-party script; calculator constants match VaultParams; the 'Disputer wins half' receipt overstates the author-disputer case and omits the ETH dispute fee

`landing/main.js:68`

```
    doerBondBps: 300, bondBase: 0.5, minBond: 1, disputeBondBps: 100,
```

Network: grep over landing/ finds no fetch(, XMLHttpRequest, sendBeacon, WebSocket, EventSource, gtag/analytics, @import or remote url(); the only script tag is <script src="main.js"> (index.html:267); fonts, images and video are under assets/. Outbound hrefs: x.com, t.me, github.com, etherscan.io, explorer.imd.fun and imd.fun (plain link to the protocol's own site, not in the brief's list, no request). Constants (main.js:68-72): doerBondBps 300, bondBase 0.5, minBond 1, disputeBondBps 100, feeBps 1000, nftFeeBps 600, winnerBondBps 5000, minRewardUsd 5, minRewardImd 0.5, largeRewardUsd 300, largeRewardImd 40, authorFeeEth 0.0005 equal LaunchParams.initial (VaultParams.sol:133-162); swarmCheckFeeImd 0.5 is labelled an example live price. bondFor mirrors Desk._bondFor. Copy vs code: (a) main.js:124 and the receipts pay the disputer and voters half each of the whole doer bond; code first takes up to 20% relayer shortfall, pays 100% to disputers when the NFT did not decide, pays an author-disputer nothing beyond his bond, and sends the whole bond to the treasury when the mandatory check rejected (item 6); (b) the disputer's 'Put in' omits the 0.0003-0.01 ETH dispute fee; (c) main.js:97 'forfeited either way' ignores the refund when the Intake cannot be asked at activation (CaseDesk._activate); (d) index.html 'Either side can appeal to the $IMD swarm': the doer may appeal only a rejection. No page text promises moderation, a hide list or a legality check. Fix: a footnote under the calculator ('one outside disputer, no relayer shortfall, plus the ETH dispute fee').

**Reproduction**

Commands: grep -rnoE 'fetch\(|XMLHttpRequest|sendBeacon|WebSocket|EventSource|gtag|analytics|<script[^>]*>|@import|url\(https?' landing/; grep -noE 'href="https?://[^"/]+' landing/index.html; compare landing/main.js:68-72 with contracts/src/VaultParams.sol:133-162. Calculator $100 USDC 'fake' tab shows Disputer +0.5 IMD; on chain an author-disputer gets 1 IMD back and 0 profit (_payWinners redirect).

### 18. Info: 17 BOARD ABUSE at IMD = $7.65: sweetener collusion caps at $300 of others' YOLO per task; a dispute swing needs 10 net NFTs and is undone by a ~1.5-2 IMD appeal; a hide block costs 2 NFTs (item 7); sp

`contracts/src/desks/Desk.sol:118`

```
        b = p.bondBase + rewardImd * bps / BPS;
```

(a) Author-doer collusion: author posts a $5 task (0.0005 ETH fee, ~$1.5-2, plus $5 that returns minus 10%), his second wallet submits (1 IMD bond refunded), nobody disputes, accepted after 24h; third-party YOLO sweeteners pay out up to $300-worth (cap without a swarm 'yes', YoloSweetener.sol:190-205) minus 10%, the rest returns to sweeteners. Gain <= ~$270 of other people's YOLO per task; a trophy burns $1.50 of the doer's own YOLO, no gain. Who loses: whoever sweetened. (b) Sybil dispute swing: endDelta 10, minWinnerWallets 1, so 10 NFTs in one wallet decide an uncontested vote; the loser appeals for max(1, 0.5 + 1% R_imd) + live fee (~0.5 IMD), about $11-15, refunded from the loser on a win. (c) HiddenList: 10 NFTs across 3 own wallets (6 transfers, ~$2 gas) pass a hide round (item 1); 2 NFTs and 2 transactions per 8 days keep an item un-hideable by vote (item 7); 11 NFTs at the last second beat a 10-NFT honest round (7b). (d) Spam: a junk task costs 0.0005 ETH; a junk proof 1 IMD ($7.65) lost if disputed, doubling per rejection to 256 IMD; freezing one task for ~12 days costs 1+2+4+8 = 15 IMD (~$115) and each catch costs the author 1 IMD + 0.0003 ETH, bond back, no share of the bond. Recommendation: fix item 7 before launch 3; sweeten from the incentives wallet only after a human look; consider minWinnerWallets 2-3 via the timelocked parameter once real turnout is known.

**Reproduction**

Arithmetic from VaultParams.sol:133-163, Desk.sol:117-130, DisputeDesk.sol:57-62 and :141-173, YoloSweetener.sol:181-213; HiddenList costs from the scratch tests of items 1 and 7.

### 19. Info: 18 DEPOSIT/FEE ECONOMICS: 10% fee, 1% (floor 1 IMD) dispute bond and 60/40 voter/treasury split are coherent at $5-$1000; third-party disputing and relaying small cases is roughly break-even, so enfor

`contracts/src/VaultParams.sol:142`

```
        p.doerBondBps = 300;
```

Treasury takes 10% of an accepted reward plus 0.0005 ETH per task; after a dispute the NFT decided right, 6% of R goes to correct votes and 4% to the treasury. The dispute bond floors at 1 IMD for every task under ~$390 (0.5 + 1% x 51 IMD), so a disputer stakes ~$7.65 plus 0.0003-0.01 ETH for half the doer bond (0.5 IMD at R < ~$127, 0.84 IMD at $300) if the NFT is right, or all of it if the swarm decided; the ETH fee returns only net of relayer gas. Correct voters on a $100 case share 0.5 IMD + $6 over >= 10 NFTs. Relayer: ~90k gas per counted vote from the ETH pot (min 0.0003 ETH), IMD shortfall capped at 20% of a 1 IMD penalty (0.2 IMD), so at a few gwei relaying small cases is a net loss unless the project runs a relayer; unrelayed ballots mean NoDecision and the tie rule accepts the doer. Nothing here loses protocol money; the thin third-party incentive is the price of small bonds (owner decision). Suggest stating on the site that the project runs a relayer at launch.

**Reproduction**

Numbers from VaultParams.sol:133-163, DisputeDesk.sol:431-456 and :518-540, CaseDesk.sol tie rule. Worked: R = $1000 USDC -> R_imd 130.7, doer bond 4.42 IMD, dispute bond 1.81 IMD; rejected with NFT right: non-contributor disputer ~2.2 IMD, voters 2.2 IMD; accepted with NFT right: voters $60 + fee share, treasury $40.

### 20. Info: 19 TOKENOMICS: 54% day-0 figure and vesting table match the code's shares and clocks; the 15% Stabilizer/MM fund is a fully unlocked project wallet (disclosure only, kept)

`docs/TOKENOMICS.md:13`

```
| Stabilizer / MM fund | 15% | 150,000,000 | unlocked at launch |
```

Day 0 liquid = pool 150M + swarm 100M + stabilizer 150M + incentives at-once 62.5M + treasury at-once 74.07M = 536.6M = 53.7% ('54%'); schedules (incentives 730d, treasury 365d, dev/team 365d, no cliff, clocks started by the split, release pull-only) match the vesting contracts' NatSpec and the round-1/2 verification (token/* not re-read line by line this round). Disclosure (owner decision, kept): the Stabilizer/MM fund plus the treasury and incentives at-once parts put ~28.7% of supply liquid in project wallets at launch next to a 15% pool, with no on-chain constraint, so any price floor is a promise, not code; suggest publishing the wallet address next to the pool link. $YOLO demand: optional reward currency (priced by the highest 2h sample, floor at the opening price), sweetener bonuses (capped at $300 unless swarm-confirmed), the $1.50 trophy burn; protocol fees are taken in the task's currency and are not used to buy YOLO. Project funding: 10% of accepted rewards, the author fee, the LP fee share, the treasury vesting. No mismatch between docs/TOKENOMICS.md and the cited code.

**Reproduction**

Sum of docs/TOKENOMICS.md:8-16; cross-check with docs/LAUNCH-TASK.md:165-166 and docs/AUDIT-INTERNAL.md:281-283; vesting/splitter tests pass in the 393.

### 22. Info: 21 COVERAGE: HiddenList, TaskVault, YoloSweetener, DisputeDesk settlement, docs and landing read in full; forge: 393 passed, 0 failed, 5 skipped (fork); 7 scratch files, specialist proofs fail as clai

`contracts/src/HiddenList.sol:27`

```
contract HiddenList {
```

Read in full this round: contracts/src/HiddenList.sol, TaskVault.sol (topUp, announceWithdraw, withdrawContribution, timers), YoloSweetener.sol (sweeten/announce/withdraw/payout), desks/DisputeDesk.sol:500-600 (settlement and winners), DareTrophy.sol:100-112, VaultParams.sol launch values; docs/MECHANICS.md roles table and bond section, docs/LAUNCH-TASK.md order and gas tables, docs/AUDIT-INTERNAL.md round-2 rows, landing/index.html links and script tags, landing/main.js constants and receipts. Relied on the specialists' concordant readings plus targeted grep for: CaseDesk, Desk pricing, VoteLib/BallotChecker, ParamsRegistry, ProtocolDirectory, oracles, token/*, helpers, interfaces, script/LaunchPlan. Not reached line by line: OracleAttestation, NftGate, DeskApi, UniswapV3SwapAdapter, TextRules, SwarmQuestion, lib/. Forge: forge build (via-IR, 4m29s cold); forge test --no-match-path 'test/scratch/*' -> 62 suites, 393 passed, 0 failed, 5 skipped (fork, no FORK_URL); forge build --sizes (item 15); scratch (contracts/test/scratch/, not kept): the 4 specialist proofs -> 8 tests FAIL on c680e49 ('1 != 10'); Judge_FixedSelfContest.t.sol (proof against a patched copy HiddenListFixed.sol with the one-line fix) -> 3 PASS; Judge_HiddenListAsymmetry.t.sol -> 3 PASS asserting the current behaviours of items 1 and 7b. No fork, no ffi, no static analyzer. No file outside test/scratch/ was modified.

**Reproduction**

Commands from contracts/: forge build; forge test --no-match-path 'test/scratch/*'; forge build --sizes; forge test --match-path 'test/scratch/*'; cp of src/HiddenList.sol with line 154 patched into test/scratch/HiddenListFixed.sol for the fix check only.

---

Judge's submission `a7387a54a4f4179a756fc2338d6e2eb36e0d11326198470887d316b85d3c506d`, accepted on the IdentityMD network. Acceptance means the report met the job's checks;
it is not a guarantee that the code has no other defects.
