{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"ace1f037-7f1f-4264-8a4b-aea442c39e87","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"ce3ade0a4baa81a9b028e804dc19053d24f1f7091950a0d449ccc5555a607f66","dependsOn":["implement_and_test"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"6b037a7b6601e883cf8a906c1520c0624817d42d8310b65c2f43679204608af3","skillId":"adversarial-review","tools":[]},"key":"adversarial_review","kind":"code","role":"review","skillHash":"6b037a7b6601e883cf8a906c1520c0624817d42d8310b65c2f43679204608af3","skillId":"adversarial-review","state":"accepted"},{"acceptedSubmissionHash":"c76f2f48a5cb99c5ba4402529992e6f8caebff9b63d0eb71314c871e19630f7b","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"faa936aa6599a58119b14e5c483676c38e88578451893fa7cfe9a39b358526d0","skillId":"implement-and-test","tools":[]},"key":"implement_and_test","kind":"code","role":"implement","skillHash":"faa936aa6599a58119b14e5c483676c38e88578451893fa7cfe9a39b358526d0","skillId":"implement-and-test","state":"accepted"}],"objective":"Build Swarm Derby Theme Day work package 3, the DerbyAuction contract and its tests, as specified in specs/WP3-auction-contract.md. Do not deploy.","parentJobId":null,"planHash":"fb8a13fc7b91cb1b21950839d617df8ba3b3985aedd29f2226935d36646f6947","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"ace1f037-7f1f-4264-8a4b-aea442c39e87","publication":{"commit":null,"deliveredAt":null,"repoUrl":null},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52232","feedbackHash":"ff8dcacf5dbf70826945e39c5e114a53e6b79e340ef3eb890079f659a20d4a81","nodeKey":"adversarial_review","submissionHash":"ce3ade0a4baa81a9b028e804dc19053d24f1f7091950a0d449ccc5555a607f66","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51745","feedbackHash":"2a5d6ac7fe5732be5b011b9c628655bae2f5e7cc95c367f19f6d83d41990a149","nodeKey":"implement_and_test","submissionHash":"c76f2f48a5cb99c5ba4402529992e6f8caebff9b63d0eb71314c871e19630f7b","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"19f8647b8084ebe3aecf0c07bdacffbb0dcaa6022cebaacba8d1ee15cb0f3cf6","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"c656125983984e6213842f62c2bb5cb250b7cf340855affefec16adebc852e20","device":"f84fad79f165f267","findings":[],"hash":"c76f2f48a5cb99c5ba4402529992e6f8caebff9b63d0eb71314c871e19630f7b","nodeId":"135cd1ba-b3b7-4008-9164-e197b77c6d7c","outcome":"completed","summary":"Blocked: the verified repair requires editing the explicitly frozen `test/SwarmDerby.t.sol`. Wrote [.imd-blocked.json](/root/.identitymd/work/ace1f037-7f1f-4264-8a4b-aea442c39e87/135cd1ba-b3b7-4008-9164-e197b77c6d7c/.imd-blocked.json). Existing deliverables remain unchanged; nothing deployed.\n\n- `forge build`: **passed**.\n- `forge test`: **72 passed, 18 failed**. All 36 auction tests pass.\n- A scratch copy changing the original fixture’s chain ID from `4663` to `31337` passes **all 54 SwarmDerby tests**. This avoids [Foundry’s ArbSys interception](https://github.com/foundry-rs/foundry/blob/v1.8.5/crates/evm/evm/src/inspectors/stack.rs).\n\nWP3 results, with the proving tests named in [DEPLOY.md’s evidence table](/root/.identitymd/work/ace1f037-7f1f-4264-8a4b-aea442c39e87/135cd1ba-b3b7-4008-9164-e197b77c6d7c/DEPLOY.md:240):\n\n| Item | Result |\n|---|---|\n| 1. Bid rejection | Pass |\n| 2. Refunds and withdrawals | Pass |\n| 3. Repeated extensions | Pass |\n| 4. Settlement and fees | Pass |\n| 5. Real swings and bonus split | Pass |\n| 6. League isolation and carry | Pass |\n| 7. Veto/reclaim restrictions | Pass |\n| 8. Conservation and constructor isolation | Pass; original regression suite remains blocked |\n\nMay I change only `vm.chainId(4663)` to `vm.chainId(31337)` in the original test fixture?\n\nYour explicit prohibition on changing that file requires this scope exception before I can apply the verified repair.","treeHash":"bf5654aaf280639beff4128de12e49832e9d6db9","usage":{"cachedInputTokens":2838912,"inputTokens":205935,"model":"gpt-6-astra","outputTokens":36929,"runtime":"codex","turns":14,"wallClockMs":1259180}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"82692ab5f7f7e54e","findings":[{"citation":"resolved","description":"bid() extends `end` to now + ANTI_SNIPE on every late bid with no upper bound (line 166), while veto() is time-locked to block.timestamp < day * 1 days and settle() requires now >= end. Two colluding wallets alternating minimum (+5%) bids every 299 s from 17:59:59 UTC the day before push the end past 00:00 UTC of the theme day after 73 extensions; each earlier bid is refunded, so the only capital at stake is the final bid (about 70 IMD from a 2 IMD start, less if the honest lead was higher). Once settled, the owner's veto reverts BidClosed, so an offensive theme cannot be stopped, and the theme is not even settled by the time the build job expects it (CLOSE_OFFSET's 6 h buffer is gone). No funds are lost: payBonus/reclaim still work. The implementation follows the spec literally; the spec's two rules conflict, so this needs a scope decision: e.g. cap the extended end at day*86400 - some margin, or let veto run until the bonus is paid (dayClosed) rather than until the theme day starts.","line":196,"path":"src/DerbyAuction.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {DerbyAuction, ISwarmDerby} from \"src/DerbyAuction.sol\";\nimport {IERC20} from \"src/SwarmDerby.sol\";\n\ncontract ScratchToken {\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n\n    function mint(address to, uint256 amount) external { balanceOf[to] += amount; }\n    function approve(address to, uint256 amount) external returns (bool) { allowance[msg.sender][to] = amount; return true; }\n    function transfer(address to, uint256 amount) external returns (bool) {\n        balanceOf[msg.sender] -= amount; balanceOf[to] += amount; return true;\n    }\n    function transferFrom(address from, address to, uint256 amount) external returns (bool) {\n        allowance[from][msg.sender] -= amount; balanceOf[from] -= amount; balanceOf[to] += amount; return true;\n    }\n}\n\ncontract ScratchDerby {\n    function currentDay() external view returns (uint256) { return block.timestamp / 1 days; }\n    function dayClosed(uint8, uint256) external pure returns (bool) { return false; }\n    function board(uint8, uint256) external pure returns (address[] memory a, uint256[] memory b) {}\n}\n\ncontract AntiSnipeVetoTest is Test {\n    ScratchToken imd;\n    DerbyAuction sale;\n    address alice = makeAddr(\"alice\");\n    address bob = makeAddr(\"bob\");\n    uint256 constant DAY = 20_400;\n\n    function setUp() public {\n        imd = new ScratchToken();\n        sale = new DerbyAuction(address(this), IERC20(address(imd)), ISwarmDerby(address(new ScratchDerby())), makeAddr(\"studio\"), 0);\n    }\n\n    function _answers() internal pure returns (DerbyAuction.Answers memory) {\n        return DerbyAuction.Answers(\"offensive creature\", 0, 0, 0, \"offensive title\", \"\");\n    }\n\n    function _bid(address who, uint256 amount) internal {\n        imd.mint(who, amount);\n        vm.startPrank(who);\n        imd.approve(address(sale), amount);\n        sale.bid(DAY, amount, _answers());\n        vm.stopPrank();\n    }\n\n    /// Two colluding wallets alternate minimum bids every 299 s, starting one second before the\n    /// scheduled 18:00 UTC close. Each bid extends the end by ANTI_SNIPE; after 73 extensions the\n    /// end is past the theme day's 00:00 UTC. The last bid is about 70 IMD; every earlier bid was\n    /// refunded. Expected: the owner can still veto the settled, unpaid theme before it is built\n    /// and paid. Actual: veto(DAY) reverts BidClosed because block.timestamp >= DAY * 1 days.\n    function test_ownerCanVetoSettledThemeAfterAntiSnipeExtensions() public {\n        uint256 end = (DAY - 1) * 1 days + 64800;\n        vm.warp(end - 1);\n        _bid(alice, 2 ether);\n        uint256 amount;\n        for (uint256 i; i < 73; ++i) {\n            (,, uint256 cur,,,,) = sale.auction(DAY);\n            vm.warp(cur - 1);\n            amount = sale.minNextBid(DAY);\n            _bid(i % 2 == 0 ? bob : alice, amount);\n        }\n        assertLt(amount, 80 ether, \"capital needed stays small\");\n        (,, uint256 finalEnd,,,,) = sale.auction(DAY);\n        vm.warp(finalEnd);\n        sale.settle(DAY);\n        (address leader,,, bool settled, bool vetoed, bool paid,) = sale.auction(DAY);\n        assertTrue(settled && !paid && !vetoed && leader != address(0));\n        sale.veto(DAY);\n        (,,,, vetoed,,) = sale.auction(DAY);\n        assertTrue(vetoed, \"owner must be able to veto an unpaid, unbuilt theme\");\n    }\n}","reproduction":"Fresh DerbyAuction (fee 0). warp to end(DAY)-1 = (DAY-1)*86400+64799; alice bids 2e18. Repeat 73 times: warp to auction(DAY).end - 1; bob/alice alternately bid minNextBid(DAY). Now auction(DAY).end > DAY*86400 and the last bid is < 80e18. warp to that end; settle(DAY) succeeds (settled, leader set, bonus > 0, unpaid). Expected: owner veto(DAY) succeeds and refunds the winner. Actual: veto(DAY) reverts BidClosed() because block.timestamp >= DAY*86400. Proof file test/scratch/AntiSnipeVeto.t.sol fails with [FAIL: BidClosed()].","severity":"medium","snippet":"if (block.timestamp >= day * 1 days) revert BidClosed();","title":"Anti-snipe extensions can push the auction end past the theme day, making veto unreachable and erasing the 6 h build buffer"},{"citation":"resolved","description":"This is the specified behaviour (reclaim 'only if unpaid'), recorded as a trust/scope observation, not a defect. From (day+1)*86400 + 7 days + 1 onward both calls pass _checkRefundable. The winner (or anyone) can call reclaim(day) and take the whole bonus minus carryIn back even though the arcade top 3 earned it and dayClosed(0, day) has been true for a week; a subsequent payBonus(day) reverts WrongStatus. The 0.5% tip makes an unpaid week unlikely but not impossible (e.g. a token outage or no keeper).","line":243,"path":"src/DerbyAuction.sol","reproduction":"settle(D) with winner W and bonus B, carryIn 0; real derby with a non-empty board for day D; warp to (D+1)*86400 + 7 days + 1 with dayClosed(0,D) true. W calls reclaim(D): W receives B, auction paid=true. payBonus(D) then reverts WrongStatus; the top 3 receive nothing. Compare: calling payBonus(D) first in the same block pays the board and reclaim reverts.","severity":"info","snippet":"if (block.timestamp <= (day + 1) * 1 days + RECLAIM_AFTER) revert TooEarly();","title":"After the 7-day grace period reclaim and payBonus are simultaneously live; whoever lands first decides who gets the bonus"},{"citation":"resolved","description":"Mirrors SwarmDerby's _send as the spec asks and is owner-recoverable via setStudio, so an observation only. With buildFee = 1e18 and a studio the token refuses (reverting, returning false, or returning malformed data) settle(day) reverts TransferFailed and the auction stays open-but-ended: nobody can bid, veto, pay or reclaim until the owner changes studio. Launch uses buildFee 0, where no transfer call is made.","line":188,"path":"src/DerbyAuction.sol","reproduction":"setBuildFee(1e18); alice bids 2e18 for DAY; warp to end(DAY); make token.transfer(studio, 1e18) revert. settle(DAY) reverts TransferFailed; auction(DAY).settled stays false. setStudio(carol) then settle(DAY) succeeds (existing test test_failedStudioPaymentLeavesAuctionUnsettled shows this).","severity":"info","snippet":"_send(studio, fee);","title":"settle() reverts while buildFee > 0 and the studio address cannot receive IMD"},{"citation":"resolved","description":"_end(day) reverts for day < 2, so the public view auction(0) / auction(1) revert rather than returning an empty record. openDay() returns 1 when block.timestamp < CLOSE_OFFSET while bid(1, ...) reverts BadDay. Unreachable on any real chain (timestamps are far past 1970-01-01 18:00 UTC); a UI iterating from day 0 would hit it.","line":140,"path":"src/DerbyAuction.sol","reproduction":"Call auction(1) on any deployment: expected an all-zero tuple; actual revert BadDay(). Call openDay() at block.timestamp = 1000: returns 1; bid(1, 2e18, validAnswers) reverts BadDay().","severity":"info","snippet":"return (a.leader, a.amount, _end(day), a.settled, a.vetoed, a.paid, a.bonus);","title":"auction(day) view reverts BadDay for day 0 and 1 instead of returning defaults; openDay() can return 1, which bid() rejects"},{"citation":"resolved","description":"The anti-snipe test extends 6 times (about 30 minutes) and the fuzz warps to end-1 at most once per bid with at most 5 bids per day, so the maximum reachable extension is about 25 minutes, far short of the 6 h gap between the scheduled close and the theme day. Finding 1 is therefore invisible to the suite. The suite also never checks settle() or veto() when auction(day).end >= day*86400, nor that the theme is settled before 00:00 UTC of its day (the 6 h buffer the spec names).","line":445,"path":"test/DerbyAuction.t.sol","reproduction":"Run forge test --match-path test/scratch/AntiSnipeVeto.t.sol: the scenario (73 consecutive late bids, then settle and veto) fails on the current code and is not exercised by any existing test.","severity":"info","snippet":"function test_antiSnipeExtendsRepeatedlyAndKeepsOtherDaysIndependent() public {","title":"Tests never drive an auction across the theme-day boundary (anti-snipe chain long enough to cross 00:00 UTC) nor the veto/settle interaction after it"}],"hash":"ce3ade0a4baa81a9b028e804dc19053d24f1f7091950a0d449ccc5555a607f66","nodeId":"81b8827f-c1dd-4efd-8ee8-6cd53a3cf193","outcome":"completed","summary":"## Scope\nFiles read: `src/DerbyAuction.sol` (356 lines, every function), `test/DerbyAuction.t.sol` (1019 lines), `specs/WP3-auction-contract.md`, and the parts of `src/SwarmDerby.sol` the auction depends on (`swing`, `finalize`, `expire`, `board`, `dayClosed`, `settleNextDay`, ownership, token helpers). Full suite run: 90 tests pass (36 auction, 54 derby). Findings are in `.imd-findings.json` with a coverage row for each of the 22 listed entry points plus two invariants.\n\n**Severity counts:** 0 Critical · 0 High · 1 Medium · 0 Low · 4 Info\n\n### [M-1] Anti-snipe extensions can push the auction end past the theme day, so the owner's veto becomes unreachable\n**Location:** `src/DerbyAuction.sol:196` (`veto`), interacting with `src/DerbyAuction.sol:166` (`bid`)\n**Root cause:** Every late bid sets `end = now + 300` with no cap, while `veto` is locked to `now < day * 86400` and `settle` requires `now >= end`.\n**Reproduction:** Two colluding wallets alternate minimum (+5%) bids every 299 s starting at 17:59:59 UTC the day before. After 73 extensions the end is past 00:00 UTC of the theme day. Every earlier bid was refunded, so the capital at stake is only the last bid, under 80 IMD from a 2 IMD start. After `settle(DAY)`, `veto(DAY)` reverts `BidClosed()`.\n**Expected vs actual:** expected the owner can veto an unpaid, unbuilt theme; actual the veto path is dead and the theme is not even settled when the 6 h build buffer was supposed to start. No funds are lost. The proof `test/scratch/AntiSnipeVeto.t.sol` fails on the current code with `BidClosed()`.\n**Scope note:** the code follows the spec literally. The two spec rules conflict, so fixing it is a scope decision: cap extensions short of `day * 86400`, or let veto run until the bonus is paid.\n\n### Info observations (not defects, spec-conformant, each with a concrete state)\n- **Reclaim vs payBonus race** after `(day+1)*86400 + 7 days`: whoever lands first decides whether the winner or the arcade top 3 get the bonus. Spec says \"only if unpaid\", so by design.\n- **Studio refusing IMD blocks settle** when `buildFee > 0`. Owner-recoverable via `setStudio`; launch fee is 0.\n- **`auction(0)` and `auction(1)` revert `BadDay`** instead of returning defaults, and `openDay()` can return 1 while `bid(1)` rejects it. Unreachable on a real chain.\n- **Test gap:** no test extends an auction more than about 30 minutes, so the suite cannot see M-1.\n\n## What I checked and found correct\n- Fund accounting: traced every transition of the invariant `balance == unsettled leads + unpaid bonuses + carry + refunds` through bid, settle, veto, payBonus (including failed sends and dust), reclaim and withdrawRefund. `carryIn` keeps carried money out of veto and reclaim refunds.\n- Reentrancy and tokens: every value-moving function is `nonReentrant` with checks-effects-interactions. `_pull` rejects fee-on-transfer and false or malformed returns by balance diff. `_trySend` treats reverts, `false`, malformed data and a codeless token as failed sends and credits refunds without blocking bids.\n- Anti-snipe boundary and 5% round-up match the spec exactly.\n- `payBonus` reads `board(0, day)` of the live SwarmDerby only once `dayClosed(0, day)` is true. I traced in SwarmDerby that dingers are credited to the commit day and that no swing of that day can still reveal a hash past the window, so the board is final when paid. Agent league scores never reach the auction.\n- `_bps` is an exact floor with no overflowing intermediate. Slither's weak-PRNG and strict-equality leads were false positives after reading the code.","treeHash":null,"usage":{"cachedInputTokens":1035783,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":27939,"runtime":"claude","turns":22,"wallClockMs":528241}}],"verification":[{"checks":[{"durationMs":24333,"exitCode":0,"name":"build","output":"Compiling 24 files with Solc 0.8.26\nSolc 0.8.26 finished in 24.20s\nCompiler run successful!\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/SwarmDerby.sol:436:48\n    │\n436 │         if (i >= _days[league].length) return (false, false, 0, 0, 0);\n    │                                                ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[boolean-cst]: misuse of a boolean constant\n    ╭▸ src/SwarmDerby.sol:436:55\n    │\n436 │         if (i >= _days[league].length) return (false, false, 0, 0, 0);\n    │                                                       ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/boolean-cst\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/DerbyOdds.sol:67:16\n   │\n67 │         feet = uint16(lo + (d % span));\n   │                ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint16' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[missing-events-arithmetic]: `singlePrice` is changed without an event but is used in arithmetic\n    ╭▸ src/SwarmDerby.sol:507:9\n    │\n507 │         singlePrice = single;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `packPrice` is changed without an event but is used in arithmetic\n    ╭▸ src/SwarmDerby.sol:508:9\n    │\n508 │         packPrice = pack;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-access-control]: `owner` is changed without an event but is used for access control\n    ╭▸ src/DerbyAuction.sol:279:9\n    │\n279 │         owner = msg.sender;\n    │         ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `pendingOwner` is changed without an event but is used for access control\n    ╭▸ src/DerbyAuction.sol:280:9\n    │\n280 │         pendingOwner = address(0);\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `owner` is changed without an event but is used for access control\n    ╭▸ src/SwarmDerby.sol:528:9\n    │\n528 │         owner = msg.sender;\n    │         ━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-events-access-control]: `pendingOwner` is changed without an event but is used for access control\n    ╭▸ src/SwarmDerby.sol:529:9\n    │\n529 │         pendingOwner = address(0);\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DerbyAuction.sol:130:13\n    │\n130 │         if (block.timestamp < CLOSE_OFFSET) return 1;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_entered` is updated\n    ╭▸ src/DerbyAuction.sol:331:13\n    │\n331 │             address(imd).call(abi.encodeCall(IERC20.transferFrom, (from, address(this), amount)));\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_entered` is updated\n    ╭▸ src/DerbyAuction.sol:344:40\n    │\n344 │         (bool ok, bytes memory data) = address(imd).call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DerbyAuction.sol:159:29\n    │\n159 │         if (sale.settled || block.timestamp < (day - 2) * 1 days + CLOSE_OFFSET || block.timestamp >= end) {\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DerbyAuction.sol:159:84\n    │\n159 │         if (sale.settled || block.timestamp < (day - 2) * 1 days + CLOSE_OFFSET || block.timestamp >= end) {\n    │                                                                                    ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DerbyAuction.sol:166:13\n    │\n166 │         if (end - block.timestamp < ANTI_SNIPE) end = block.timestamp + ANTI_SNIPE;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DerbyAuction.sol:323:13\n    │\n323 │             emit RefundCredited(bidder, amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DerbyAuction.sol:174:9\n    │\n174 │         emit Bid(day, msg.sender, amount, end, a.creature, a.vibe, a.stadium, a.weather, a.title, a.shoutout);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/DerbyAuction.sol:190:47\n    │\n190 │         emit Settled(day, a.leader, a.amount, fee, a.bonus);\n    │                                               ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DerbyAuction.sol:180:13\n    │\n180 │         if (block.timestamp < _end(day)) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DerbyAuction.sol:190:9\n    │\n190 │         emit Settled(day, a.leader, a.amount, fee, a.bonus);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/DerbyAuction.sol:193:51\n    │\n193 │     function veto(uint256 day) external onlyOwner nonReentrant {\n    │                                                   ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DerbyAuction.sol:196:13\n    │\n196 │         if (block.timestamp >= day * 1 days) revert BidClosed();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DerbyAuction.sol:200:9\n    │\n200 │         emit Vetoed(day, a.leader, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `carry` is updated\n    ╭▸ src/DerbyAuction.sol:344:40\n    │\n344 │         (bool ok, bytes memory data) = address(imd).call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/DerbyAuction.sol:228:27\n    │\n228 │         _send(msg.sender, tip);\n    │                           ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/DerbyAuction.sol:226:35\n    │\n226 │         uint256 carried = bonus - paid;\n    │                                   ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/DerbyAuction.sol:344:40\n    │\n344 │         (bool ok, bytes memory data) = address(imd).call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DerbyAuction.sol:236:9\n    │\n236 │         emit BonusPaid(day, winners, amounts, msg.sender, tip, carried);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/DerbyAuction.sol:210:37\n    │\n210 │         (address[] memory board,) = derby.board(0, day);\n    │                                     ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/DerbyAuction.sol:243:13\n    │\n243 │         if (block.timestamp <= (day + 1) * 1 days + RECLAIM_AFTER) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DerbyAuction.sol:247:9\n    │\n247 │         emit Reclaimed(day, a.leader, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DerbyAuction.sol:255:9\n    │\n255 │         emit RefundWithdrawn(msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmDerby.sol:212:9\n    │\n212 │         emit TurnsBought(player, league, count, cost, burned);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmDerby.sol:296:91\n    │\n296 │             swings[swingId] = Swing(player, league, 0, velo, Status.Final, 0, bytes32(0), uint32(day));\n    │                                                                                           ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmDerby.sol:302:25\n    │\n302 │         uint64 target = uint64(ARB_SYS.arbBlockNumber() + REVEAL_DELAY);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SwarmDerby.sol:305:98\n    │\n305 │         swings[swingId] = Swing(player, league, quality, velo, Status.Committed, target, commit, uint32(day));\n    │                                                                                                  ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint32' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `vault` is updated\n    ╭▸ src/SwarmDerby.sol:564:40\n    │\n564 │         (bool ok, bytes memory data) = address(imd).call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SwarmDerby.sol:323:13\n    │\n323 │         if (bh == bytes32(0)) {\n    │             ━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmDerby.sol:339:13\n    │\n339 │             emit GrandSlam(swingId, s.player, s.league, feet, payout);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmDerby.sol:341:9\n    │\n341 │         emit SwingResolved(swingId, s.player, tier, feet);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/SwarmDerby.sol:441:51\n    │\n441 │         if (_board[league][day].length > 0) tip = (amount * PAYOUT_BPS / 10_000) * SETTLE_TIP_BPS / 10_000;\n    │                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[reentrancy-no-eth]: external call can be reentered before `rollover` is updated\n    ╭▸ src/SwarmDerby.sol:564:40\n    │\n564 │         (bool ok, bytes memory data) = address(imd).call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/SwarmDerby.sol:465:19\n    │\n465 │             tip = (distributable * SETTLE_TIP_BPS) / 10_000;\n    │                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SwarmDerby.sol:477:27\n    │\n477 │         _send(msg.sender, tip);\n    │                           ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SwarmDerby.sol:475:37\n    │\n475 │         rollover[league] = amount - paid;\n    │                                     ━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/SwarmDerby.sol:564:40\n    │\n564 │         (bool ok, bytes memory data) = address(imd).call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SwarmDerby.sol:485:9\n    │\n485 │         emit DaySettled(league, day, winners, amounts, msg.sender, tip, rollover[league]);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/DerbyAuction.sol:271:32\n    │\n271 │     function transferOwnership(address to) external onlyOwner {\n    │                                ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/DerbyAuction.sol:311:91\n    │\n311 │             if (c < 0x20 || c > 0x7e || c == 0x3c || c == 0x3e || c == 0x22 || c == 0x5c) revert BadAnswers();\n    │                                                                                           ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/DerbyAuction.sol:317:16\n    │\n317 │         return amount / 10_000 * bps + (amount % 10_000) * bps / 10_000;\n    │                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/SwarmDerby.sol:519:32\n    │\n519 │     function transferOwnership(address to) external onlyOwner {\n    │                                ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/SwarmDerby.t.sol:103:18\n    │\n103 │         vm.warp((block.timestamp / 1 days + 1) * 1 days + 1);\n    │         ─────────━━━━━━━━━━━━━━━──────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":458,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 54 tests for test/SwarmDerby.t.sol:SwarmDerbyTest\n[PASS] testFuzz_boardsAreTopTen(uint256) (runs: 256, μ: 5055191, ~: 5051056)\n[PASS] testFuzz_settlementConserves(uint256) (runs: 256, μ: 5555605, ~: 5579686)\n[PASS] test_agentLeagueHasNoCap() (gas: 2695596)\n[PASS] test_arcadeCapIsTwentyPerDay() (gas: 2523316)\n[PASS] test_arcadeKeepsLongestAgentKeepsTotal() (gas: 298076)\n[PASS] test_badLeagueRejected() (gas: 32903)\n[PASS] test_boardResetsEachDay() (gas: 216345)\n[PASS] test_buyPackSplitsPerLeague() (gas: 518853)\n[PASS] test_capCountsSessionSwingsForThePlayer() (gas: 2274997)\n[PASS] test_commitBoundToPlayer() (gas: 588030)\n[PASS] test_constructorRejectsZeroAddressesAndFreeTurns() (gas: 1145480)\n[PASS] test_contactNeedsCommit() (gas: 334529)\n[PASS] test_eachDaySettlesOnceInOrder() (gas: 1443282)\n[PASS] test_emptyDayRollsOverWithoutTip() (gas: 1103450)\n[PASS] test_expireUnrevealed() (gas: 613957)\n[PASS] test_finalizeTooEarly() (gas: 503290)\n[PASS] test_finalizeUpdatesArcadeBoard() (gas: 629256)\n[PASS] test_fullSwingMatchesOdds() (gas: 522204)\n[PASS] test_lateFinalizeScoresOnCommitDay() (gas: 664435)\n[PASS] test_lateRevealIsFoul() (gas: 516676)\n[PASS] test_leagueTurnsAreSeparate() (gas: 295691)\n[PASS] test_leaveSessionFreesTheKey() (gas: 205604)\n[PASS] test_nextSettlementBeforeAnything() (gas: 11875)\n[PASS] test_oddsMonotoneInQuality() (gas: 316953)\n[PASS] test_ownerIsConstructorArg() (gas: 2913055)\n[PASS] test_ownerOnlyReachesOps() (gas: 418230)\n[PASS] test_ownershipIsTwoStep() (gas: 169425)\n[PASS] test_ownershipMoveCanBeCancelled() (gas: 113732)\n[PASS] test_parityWithBrowser() (gas: 63207)\n[PASS] test_priceFloor() (gas: 100369)\n[PASS] test_qualityAndVeloBounded() (gas: 317033)\n[PASS] test_revealAtWindowEdgeStillCounts() (gas: 615287)\n[PASS] test_sessionBuysForThePlayer() (gas: 483847)\n[PASS] test_sessionCannotBeClaimedTwice() (gas: 179047)\n[PASS] test_sessionChainsRejected() (gas: 376408)\n[PASS] test_sessionConsentIsSingleUse() (gas: 313939)\n[PASS] test_sessionNeedsTheKeysConsent() (gas: 161377)\n[PASS] test_sessionRevokeAndRotate() (gas: 300545)\n[PASS] test_sessionSwingsForPlayer() (gas: 671328)\n[PASS] test_settleAgentLeague() (gas: 633371)\n[PASS] test_settlePaysOnlyThatDaysPot() (gas: 1162741)\n[PASS] test_settlePaysTheDaysTopThree() (gas: 1224382)\n[PASS] test_settleWaitsForTheDayToEnd() (gas: 927961)\n[PASS] test_settleWaitsForTheLastReveal() (gas: 687692)\n[PASS] test_singleTurnPrice() (gas: 285720)\n[PASS] test_slamPaysTenPercentOfItsLeagueVault() (gas: 4962665)\n[PASS] test_swingStoresVelo() (gas: 449101)\n[PASS] test_tokenWithoutCodeRefusesPurchases() (gas: 3276413)\n[PASS] test_underPowerLineNoBombOrSlam() (gas: 29184887)\n[PASS] test_unpayableSlamKeepsPrizeAndHomer() (gas: 4747467)\n[PASS] test_unpayableWinnerRollsOver() (gas: 997516)\n[PASS] test_whiffCostsTurnNoRoll() (gas: 404595)\n[PASS] test_wrongSaltRejected() (gas: 498546)\n[PASS] test_zeroCountPurchaseReverts() (gas: 77131)\nSuite result: ok. 54 passed; 0 failed; 0 skipped; finished in 352.23ms (423.96ms CPU time)\n\nRan 36 tests for test/DerbyAuction.t.sol:DerbyAuctionTest\n[PASS] testFuzz_conservationAcrossAuctionSequences(uint256) (runs: 256, μ: 13357904, ~: 13275266)\n[PASS] test_adminPermissionsFeeCapAndTwoStepOwnership() (gas: 536509)\n[PASS] test_agentScoresAndOtherDaysNeverAffectArcadeBonus() (gas: 3416444)\n[PASS] test_answerBoundaryValuesStoredAndBidEmitted() (gas: 660457)\n[PASS] test_answerLengthsAndEnums() (gas: 533340)\n[PASS] test_antiSnipeExtendsRepeatedlyAndKeepsOtherDaysIndependent() (gas: 1962213)\n[PASS] test_bidMinimumAndIncrementRoundUp() (gas: 768819)\n[PASS] test_blockedReclaimIsCreditedAndCarryIsReusable() (gas: 1896033)\n[PASS] test_constructorRejectsZeroAddressesAndExcessFee() (gas: 11214)\n[PASS] test_constructorStoresArgumentsWithoutAnyDependencyCalls() (gas: 394377)\n[PASS] test_emptyArcadeWithAgentPlayersCarriesEverythingWithoutTip() (gas: 1919663)\n[PASS] test_emptyAuctionSettlesWithoutTakingCarry() (gas: 829123)\n[PASS] test_everyForbiddenByteRejectedInEveryString() (gas: 30597389)\n[PASS] test_failedOrShortTokenPullRollsBackBidAndAnswers() (gas: 1642681)\n[PASS] test_failedPrizeAndRoundingDustCarryWithoutBlockingOthers() (gas: 2675792)\n[PASS] test_failedRefundAccumulatesAndWithdrawsOnlyOnce() (gas: 1323794)\n[PASS] test_failedStudioPaymentLeavesAuctionUnsettled() (gas: 754394)\n[PASS] test_failedTipRevertsAndAnotherCallerCanPay() (gas: 1614942)\n[PASS] test_falseAndMalformedRefundsDoNotBlockBids() (gas: 1139230)\n[PASS] test_fullNewBidMustBeFundedBeforeSelfRefund() (gas: 559319)\n[PASS] test_maximumBidSettlesAndPaysWithoutIntermediateOverflow() (gas: 2667169)\n[PASS] test_noReturnTokenBidsRefundsFeesWithdrawalsAndBonus() (gas: 2143841)\n[PASS] test_onePlayerCarriesUnfilledSharesIntoNextAuction() (gas: 1886794)\n[PASS] test_openDayAndBidTimeBoundaries() (gas: 657470)\n[PASS] test_outbidAndSelfRaiseRefundPreviousBid() (gas: 819574)\n[PASS] test_realSwingsPayOnlyAfterArcadeDayClosedAndOnlyTopThree() (gas: 3524526)\n[PASS] test_reclaimGraceBoundaryReturnsOwnNetBidNotCarryToWinner() (gas: 1529082)\n[PASS] test_reclaimRejectsUnsettledEmptyAndAlreadyPaidAuctions() (gas: 804176)\n[PASS] test_reentrantBonusAndReclaimCannotPayTwice() (gas: 2240261)\n[PASS] test_reentrantTokenCannotBidOrWithdrawDuringTransfers() (gas: 1346585)\n[PASS] test_settleOneIMDFeeAndSettingsApplyOnlyAtSettlement() (gas: 666978)\n[PASS] test_settleZeroFeeExactlyAndOnlyOnce() (gas: 646946)\n[PASS] test_twoPlayersCarryUnfilledShareIntoNextAuction() (gas: 2478132)\n[PASS] test_vetoOwnerOnlyBeforeThemeDayReturnsOwnNetBidNotCarry() (gas: 1490725)\n[PASS] test_vetoRejectsUnsettledEmptyAndThemeDayBoundary() (gas: 704553)\n[PASS] test_zeroFeeVetoRefundsFullBidAndBlockedWinnerGetsCredit() (gas: 716250)\nSuite result: ok. 36 passed; 0 failed; 0 skipped; finished in 378.95ms (429.70ms CPU time)\n\nRan 2 test suites in 379.85ms (731.19ms CPU time): 90 tests passed, 0 failed, 0 skipped (90 total tests)\n","passed":true},{"durationMs":39,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DerbyAuction.acceptOwnership()\",\"DerbyAuction.bid(uint256,uint256,(string,uint8,uint8,uint8,string,string))\",\"DerbyAuction.payBonus(uint256)\",\"DerbyAuction.reclaim(uint256)\",\"DerbyAuction.setBuildFee(uint256)\",\"DerbyAuction.setStudio(address)\",\"DerbyAuction.settle(uint256)\",\"DerbyAuction.transferOwnership(address)\",\"DerbyAuction.veto(uint256)\",\"DerbyAuction.withdrawRefund()\",\"SwarmDerby.acceptOwnership()\",\"SwarmDerby.buyPacks(uint8,uint256)\",\"SwarmDerby.buyTurns(uint8,uint256)\",\"SwarmDerby.expire(uint256)\",\"SwarmDerby.finalize(uint256,bytes32)\",\"SwarmDerby.leaveSession()\",\"SwarmDerby.setPrices(uint256,uint256)\",\"SwarmDerby.setSession(address,bytes)\",\"SwarmDerby.settleNextDay(uint8)\",\"SwarmDerby.swing(uint8,uint8,uint8,bytes32)\",\"SwarmDerby.transferOwnership(address)\",\"SwarmDerby.withdrawOps(address,uint256)\"],\"files\":{\".gitignore\":5,\"DEPLOY.md\":251,\"HANDOFF.md\":144,\"LICENSE\":21,\"README.md\":15,\"e2e/Mocks.sol\":27,\"e2e/README.md\":23,\"e2e/board.py\":47,\"e2e/leagues.py\":45,\"e2e/play.py\":63,\"e2e/recover.py\":23,\"e2e/recover_unmined.py\":28,\"e2e/settle.py\":55,\"e2e/setup.py\":30,\"foundry.toml\":9,\"imd-check.mjs\":40,\"specs/README.md\":69,\"specs/WP1-theme-packs.md\":142,\"specs/WP2-daily-conditions.md\":17,\"specs/WP3-auction-contract.md\":127,\"specs/WP4-auction-ui.md\":87,\"specs/WP5-theme-build-job.md\":110,\"specs/WP6-operator-runbook.md\":55,\"src/DerbyAuction.sol\":356,\"src/DerbyOdds.sol\":69,\"src/SwarmDerby.sol\":567,\"test/DerbyAuction.t.sol\":1019,\"test/SwarmDerby.t.sol\":862,\"web/derby-odds.js\":48},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":2623,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/DerbyAuction.sol:316: DerbyAuction._bps(uint256,uint256) (src/DerbyAuction.sol#316-318) uses a weak PRNG: \"amount / 10_000 * bps + (amount % 10_000) * bps / 10_000 (src/DerbyAuction.sol#317)\"\n[high/medium] reentrancy-balance at src/DerbyAuction.sol:327: Reentrancy in DerbyAuction._pull(address,uint256) (src/DerbyAuction.sol#327-335):\n[high/high] uninitialized-state at src/SwarmDerby.sol:370: SwarmDerby._board (src/SwarmDerby.sol#112) is never initialized. It is used in:\n[high/high] uninitialized-state at src/SwarmDerby.sol:429: SwarmDerby._days (src/SwarmDerby.sol#121) is never initialized. It is used in:\n[medium/medium] divide-before-multiply at src/DerbyAuction.sol:316: DerbyAuction._bps(uint256,uint256) (src/DerbyAuction.sol#316-318) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/SwarmDerby.sol:448: SwarmDerby.settleNextDay(uint8) (src/SwarmDerby.sol#448-486) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/SwarmDerby.sol:429: SwarmDerby.nextSettlement(uint8) (src/SwarmDerby.sol#429-442) performs a multiplication on the result of a division:\n[medium/medium] divide-before-multiply at src/SwarmDerby.sol:448: SwarmDerby.settleNextDay(uint8) (src/SwarmDerby.sol#448-486) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/DerbyAuction.sol:341: DerbyAuction._trySend(address,uint256) (src/DerbyAuction.sol#341-346) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DerbyAuction.sol:348: DerbyAuction._accepted(bytes) (src/DerbyAuction.sol#348-355) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DerbyAuction.sol:283: DerbyAuction._end(uint256) (src/DerbyAuction.sol#283-287) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DerbyAuction.sol:348: DerbyAuction._accepted(bytes) (src/DerbyAuction.sol#348-355) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/SwarmDerby.sol:448: Reentrancy in SwarmDerby.settleNextDay(uint8) (src/SwarmDerby.sol#448-486):\n[medium/medium] reentrancy-no-eth at src/DerbyAuction.sol:203: Reentrancy in DerbyAuction.payBonus(uint256) (src/DerbyAuction.sol#203-237):\n[medium/medium] uninitialized-local at src/SwarmDerby.sol:462: SwarmDerby.settleNextDay(uint8).paid (src/SwarmDerby.sol#462) is a local variable never initialized\n[medium/medium] uninitialized-local at src/DerbyAuction.sol:182: DerbyAuction.settle(uint256).fee (src/DerbyAuction.sol#182) is a local variable never initialized\n[medium/medium] uninitialized-local at src/DerbyAuction.sol:215: DerbyAuction.payBonus(uint256).paid (src/DerbyAuction.sol#215) is a local variable never initialized\n[medium/medium] uninitialized-local at src/SwarmDerby.sol:319: SwarmDerby.finalize(uint256,bytes32).bh (src/SwarmDerby.sol#319) is a local variable never initialized\n[medium/medium] uninitialized-local at src/DerbyAuction.sol:214: DerbyAuction.payBonus(uint256).tip (src/DerbyAuction.sol#214) is a local variable never initialized\n[medium/medium] uninitialized-local at src/SwarmDerby.sol:461: SwarmDerby.settleNextDay(uint8).tip (src/SwarmDerby.sol#461) is a local variable never initialized\n[medium/medium] unused-return at src/DerbyAuction.sol:203: DerbyAuction.payBonus(uint256) (src/DerbyAuction.sol#203-237) ignores return value by (board,None) = derby.board(0,day) (src/DerbyAuction.sol#210)\n[low/medium] missing-zero-check at src/SwarmDerby.sol:519: SwarmDerby.transferOwnership(address).to (src/SwarmDerby.sol#519) lacks a zero-check on :\n[low/medium] missing-zero-check at src/DerbyAuction.sol:271: DerbyAuction.transferOwnership(address).to (src/DerbyAuction.sol#271) lacks a zero-check on :\n[low/medium] reentrancy-events at src/SwarmDerby.sol:448: Reentrancy in SwarmDerby.settleNextDay(uint8) (src/SwarmDerby.sol#448-486):\n[low/medium] reentrancy-events at src/SwarmDerby.sol:311: Reentrancy in SwarmDerby.finalize(uint256,bytes32) (src/SwarmDerby.sol#311-342):\n[low/medium] reentrancy-events at src/SwarmDerby.sol:195: Reentrancy in SwarmDerby._buy(uint8,uint256,uint256) (src/SwarmDerby.sol#195-213):\n[low/medium] timestamp at src/DerbyAuction.sol:177: DerbyAuction.settle(uint256) (src/DerbyAuction.sol#177-191) uses timestamp for comparisons\n[low/medium] timestamp at src/DerbyAuction.sol:240: DerbyAuction.reclaim(uint256) (src/DerbyAuction.sol#240-248) uses timestamp for comparisons\n[low/medium] timestamp at src/DerbyAuction.sol:348: DerbyAuction._accepted(bytes) (src/DerbyAuction.sol#348-355) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmDerby.sol:419: SwarmDerby.dayClosed(uint8,uint256) (src/SwarmDerby.sol#419-421) uses timestamp for comparisons\n[low/medium] timestamp at src/DerbyAuction.sol:156: DerbyAuction.bid(uint256,uint256,DerbyAuction.Answers) (src/DerbyAuction.sol#156-175) uses timestamp for comparisons\n[low/medium] timestamp at src/DerbyAuction.sol:148: DerbyAuction.minNextBid(uint256) (src/DerbyAuction.sol#148-154) uses timestamp for comparisons\n[low/medium] timestamp at src/DerbyAuction.sol:327: DerbyAuction._pull(address,uint256) (src/DerbyAuction.sol#327-335) uses timestamp for comparisons\n[low/medium] timestamp at src/DerbyAuction.sol:341: DerbyAuction._trySend(address,uint256) (src/DerbyAuction.sol#341-346) uses timestamp for comparisons\n[low/medium] timestamp at src/SwarmDerby.sol:496: SwarmDerby._markDay(uint8,uint256) (src/SwarmDerby.sol#496-499) uses timestamp for comparisons\n[low/medium] timestamp at src/DerbyAuction.sol:283: DerbyAuction._end(uint256) (src/DerbyAuction.sol#283-287) uses timestamp for comparisons\n[low/medium] timestamp at src/DerbyAuction.sol:128: DerbyAuction.openDay() (src/DerbyAuction.sol#128-132) uses timestamp for comparisons\n[low/medium] timestamp at src/DerbyAuction.sol:193: DerbyAuction.veto(uint256) (src/DerbyAuction.sol#193-201) uses timestamp for comparisons","passed":true},{"durationMs":330,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/DerbyAuction.sol:206: Reentrancy: State change after external call (6 places)\n[low] centralization-risk at src/DerbyAuction.sol:193: Centralization Risk (7 places)\n[low] costly-loop at src/DerbyAuction.sol:229: Costly operations inside loop (3 places)\n[low] division-before-multiplication at src/DerbyAuction.sol:317: Incorrect Order of Division and Multiplication\n[low] ecrecover at src/SwarmDerby.sol:546: `ecrecover` Signature Malleability\n[low] internal-function-used-once at src/DerbyOdds.sol:32: Internal Function Used Only Once\n[low] large-numeric-literal at src/DerbyAuction.sol:151: Large Numeric Literal (15 places)\n[low] literal-instead-of-constant at src/DerbyAuction.sol:151: Literal Instead of Constant (46 places)\n[low] missing-inheritance at src/SwarmDerby.sol:43: Missing Inheritance\n[low] non-reentrant-not-first at src/DerbyAuction.sol:193: `nonReentrant` is Not the First Modifier\n[low] require-revert-in-loop at src/DerbyAuction.sol:309: Loop Contains `require`/`revert`\n[low] state-change-without-event at src/SwarmDerby.sol:505: State Change Without Event (2 places)\n[low] state-no-address-check at src/DerbyAuction.sol:272: Address State Variable Set Without Checks (2 places)\n[low] uninitialized-local-variable at src/DerbyAuction.sol:309: Uninitialized Local Variable (2 places)\n[low] unsafe-erc20-operation at src/DerbyAuction.sol:331: Unsafe ERC20 Operation (4 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"c76f2f48a5cb99c5ba4402529992e6f8caebff9b63d0eb71314c871e19630f7b","verifiedTreeHash":"bf5654aaf280639beff4128de12e49832e9d6db9","verifierVersion":"0.1.0+ad90ce4c"}]}