{"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":"1929103c-cbed-4f97-9439-bb0bb3e40edb","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"ef386ded787f31a39ca3c0c85169c52a9552c2f2fa173cb415cad5a20b567b17","dependsOn":["audit_imported_code"],"execution":{"mustProduce":["src/LaunchToken.sol"],"network":false,"profile":"foundry","requires":[],"skillHash":"a2cea02e8b33e91eeb784e0e5eaeb571cd62cd69e768d335d5834e24b3aa46e5","skillId":"adapt-contract-project","tools":[]},"key":"adapt_contract_project","kind":"code","role":"implement","skillHash":"a2cea02e8b33e91eeb784e0e5eaeb571cd62cd69e768d335d5834e24b3aa46e5","skillId":"adapt-contract-project","state":"accepted"},{"acceptedSubmissionHash":"058c3a3243dbafe37a6ee9923379ed088dea5fe5aaa5275b78141059ed53f915","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"2c4b5759cdd67f37373e328c5948af909e56d94e7a18bd60ff6ef1c99ea1fead","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"a79b4f30e36f7f687d33dca9fef6f4e7db76f04a635839a829ad0ac35e5804d6","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","tools":[]},"key":"audit_imported_code","kind":"code","role":"review","skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","state":"accepted"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"waiting"},{"acceptedSubmissionHash":"1293c3c3a2d8370af01f0258bd457afa90bbff44335daf7be45225bda32897c4","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"a622d5e9611768bd6f4a8d6dfb94414f71d01a35b6985a992f600d2858ea8266","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":null,"dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"failed"},{"acceptedSubmissionHash":"51d68709da66a1c9776674422921c6f1c6cdfb021970a410c9ddde692c079d21","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Deploy TONOFHACKATHONS ($HACK) from this repository as already built and audited (IMD launch 1032, job e1bcd0e4). It is a univ4_hook launch on Ethereum mainnet, paired with IMD 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7, exactly as launch.json and docs/launch-request.json state. The previous run passed build, tests, the audit panel and the judge, and failed only because that request was paired with ETH. Do not redesign or add features. Change code only to fix a real finding from the import audit or panel, keeping the pool paired with IMD and launch.json as is.","parentJobId":null,"planHash":"8fa9166bf3822dbc4800a9ee6f81c26f5f016f6ba09283db9006ce4a16613b8a","previousHash":"a9da8bbaa2047744b579533a7511bfadb335e9d5a08b4ba631416020f53d3250","projectId":"1929103c-cbed-4f97-9439-bb0bb3e40edb","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1044-deploy-tonofhackathons-hack-repository"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51347","feedbackHash":"d17c79f4d9b021682abee8acf3d500b8ec9827ce8369664ed9f8b56e1d6ffb16","nodeKey":"audit_economics","submissionHash":"058c3a3243dbafe37a6ee9923379ed088dea5fe5aaa5275b78141059ed53f915","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51216","feedbackHash":"16d0600ab5bbe8d4d12d59ddcd874b35829136adcffd3595eb95c49c7756376f","nodeKey":"audit_flow","submissionHash":"2c4b5759cdd67f37373e328c5948af909e56d94e7a18bd60ff6ef1c99ea1fead","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52180","feedbackHash":"55ac5afc1b31a5a904fe13cce616cf9a8fa228ec73e7c7ad38dd9a847aae402b","nodeKey":"audit_math","submissionHash":"1293c3c3a2d8370af01f0258bd457afa90bbff44335daf7be45225bda32897c4","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51083","feedbackHash":"ff01429ac8f4be231fe399056eb41c638085dca2a21d083efad2e8def7141617","nodeKey":"audit_permissions","submissionHash":"a622d5e9611768bd6f4a8d6dfb94414f71d01a35b6985a992f600d2858ea8266","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52184","feedbackHash":"509f1c6419179b12f48ed09f1ab6d3112916288665b8d72f8988cd84fa8adffd","nodeKey":"write_foundry_tests","submissionHash":"51d68709da66a1c9776674422921c6f1c6cdfb021970a410c9ddde692c079d21","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"5d8bc9fde156203031fb1e05b79d8f1428dee15a077953dee7efdc7920b7921f","state":"blocked","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"af9a875696459139","findings":[{"citation":"resolved","description":"Trust gap (economics x asymmetry) between the two prize paths. The 70% stream is vested over 28 days and, per README, every unpaid amount is reclaimable to the pot once the owner's 48-hour denial takes effect (claim/recycleDeniedStream -> _recycle). The 10% purchase path is final the moment submitResult runs: configureBuy (lines 399-410) lets the payout pick any initialized IMD pool for its registered token with any positive minRateX96, and _buy spends the machine's IMD into that pool and takes the output to the payout. An entrant who is the sole liquidity provider of that pool therefore receives the allocation's IMD into their own position (plus the pool's LP fee), withdraws it in the same block, and that part of the prize can no longer be recycled if a denial is later queued. README documents that judges should reject fake pools and that the purchase delivers tokens to the payout, but it does not state that the denial clawback (described as 'all unpaid amounts in its existing streams') never covers the purchased 10%, nor that the vest can be bypassed for that share. No code change is required to keep the agreed design; this is a documentation/trust-assumption gap. If the requester wants the clawback to cover 80% as the README implies, the only in-design options are to document the limit or to require the purchase pool to be the launch pool (a scope decision, not made here).","line":531,"path":"src/HackathonMachine.sol","reproduction":"Fixture: MachineFixture (local PoolManager, IMD mock, HACK, hook at a 0x20cc address, question pinned). 1) alice deploys ERC20 T (10,000,000e18), initializes pool {IMD, T, fee 100, tickSpacing 1, hooks 0} at sqrtPriceX96 2^96 and adds token-only liquidity 1,000,000e18 on the side the IMD buy will consume (ticks [1,60000] when IMD is currency1, [-60000,-1] when IMD is currency0); her IMD balance is unchanged by this. 2) register(..., payout=alice, token=T); alice calls configureBuy(id, thatKey, 1). 3) fundRound(990e18) so the closed pot is 1,000e18; warp past round close; submitResult with a valid attestation naming id. Actual: ResultSettled with reward 10e18, streamed 693e18, bought 99e18; alice then calls modifyLiquidity(-1,000,000e18) and her IMD balance rises by 98,999,999,999,999,999,998 wei (the whole 99e18 allocation minus 2 wei rounding) immediately. 4) owner queueDeny(alice); warp 48h; recycleDeniedStream(id) returns exactly 693e18 to the pot. Expected per README's clawback description ('all unpaid amounts' after denial) and the 28-day vest: the 99e18 purchase share is neither vested nor reclaimable; 792e18 would have been reclaimable had the same entrant left token = address(0). Verified with test/scratch/Probe.t.sol::test_probe_selfPoolRealizesAllocationImmediatelyAndEscapesDeny (passes on current code with those exact numbers).","severity":"low","snippet":"        poolManager.take(Currency.wrap(e.token), e.payout, uint256(uint128(output)));","title":"Winner can route the 10% purchase allocation into a self-owned pool, realizing it instantly as IMD and placing it outside the 28-day vest and the deny clawback"},{"citation":"resolved","description":"Economic griefing vector, within the documented fallback design but attributed in README only to hostile hooks/tokens, liquidity removal by the LP, or the submitter's timing. The purchase subcall is capped at BUY_GAS = 500,000. A v4 swap pays roughly 20-30k gas per initialized tick it crosses, so in a thin pool the purchase crosses many ticks and an unrelated address can initialize enough one-tick positions along the buy's path to push the subcall over 500k. The subcall then reverts out of gas, BuySkipped is emitted, and the whole allocation is streamed instead of bought. No funds are lost (the winner still receives the IMD over 28 days), so this is informational: the winner's project token loses its buyback and the attacker gains nothing beyond denying it. Documenting that third parties can trigger fallback cheaply, or raising BUY_GAS, are the only in-design responses; no change is required.","line":453,"path":"src/HackathonMachine.sol","reproduction":"Fixture: MachineFixture. 1) deploy prize ERC20, initialize pool {IMD, prize, fee 3000, tickSpacing 1, hooks 0} at 2^96, add liquidity 2,000e18 over ticks [-60000, 60000]. 2) register alice with token = prize; alice configureBuy(id, pool, 1); fundRound(990e18); close round. Baseline (no spam): submitResult succeeds with streamed 693e18, submitResult costs 470,272 gas and the buy moves the pool from tick 0 to tick 963. 3) bob (unrelated) calls modifyLiquidity 25 times with liquidityDelta 1e6 on [i, i+1] for i = 1..25 in the direction the buy moves (or [-i-1, -i] when IMD is currency0); measured cost 4,101,975 gas total. 4) submitResult with the same attestation. Actual: BuySkipped(id) is emitted, stream total is 792e18, alice receives 0 prize tokens. Expected per README's description of fallback triggers: fallback only from the hostile hook/token, LP withdrawal or submitter-timing cases; a non-LP, non-submitter third party is not listed. Verified with test/scratch/Probe.t.sol::test_probe_tickSpamForcesFallback and ::test_probe_tickSpamBaselineSucceeds.","severity":"info","snippet":"            try this.buyWinnerToken{gas: BUY_GAS}(id, buyAmount) {","title":"Any third party can force the winner's token purchase into stream fallback for about 4.1M gas by spamming one-tick positions in a thin prize pool"}],"hash":"058c3a3243dbafe37a6ee9923379ed088dea5fe5aaa5275b78141059ed53f915","nodeId":"e1ca7fd0-3f48-477b-8309-98a650af2044","outcome":"completed","summary":"The review is complete. The findings file is written and validated, and no tracked file was changed.\n\n**Outcome.** The economics of HackathonMachine hold up under the Economic Security, Invariant and Flow Gap checks. I found no loss-of-funds defect. I report one low finding and one informational observation, both reproduced locally with scratch tests that pass on the current code with the numbers quoted.\n\n**Findings written to `.imd-findings.json`:**\n\n- **Low, `src/HackathonMachine.sol:531`.** The winner can point the 10% purchase at a pool where they are the only liquidity provider. The allocation then lands in their own position as IMD in the same block. A 1,000 IMD pot yields a 693 IMD stream and a 99 IMD purchase, and the winner withdrew 98.999… IMD immediately. A later owner denial recycled only the 693 IMD stream. The README describes the clawback as covering all unpaid stream amounts and does not say the purchased share is exempt from both the vest and the clawback. This is a documentation and trust-assumption gap, not a code change within the agreed design.\n- **Info, `src/HackathonMachine.sol:453`.** In a thin prize pool, an unrelated address can force the gas-capped purchase into stream fallback by adding 25 one-tick positions along the buy's path for about 4.1M gas. The winner still receives the IMD, so nothing is lost. The README attributes fallback to hostile hooks, hostile tokens, liquidity removal or submitter timing, not to third parties.\n\n**Coverage.** All 34 listed entry points have a row, plus three invariant rows. Every row is `holds` except configureBuy and submitResult, which reference the two findings. I traced the four swap fee modes in both currency orderings against the vendored v4 Hooks and Pool libraries, the balance equation through every writer, settlement conservation, rollover, streams, denial recycling, and the unlock callback paths.\n\n**Static-analysis leads.** None reproduced as a defect. The strict-equality and uninitialized-local lines are benign, and the state change after the purchase call at line 453 is protected by the shared reentrancy guard, the manager-only callback and the calldata-hash binding.\n\n**Not reached or not verifiable here.** Signature and EIP-712 domain details were only skimmed as outside my area. The production factory's initialization route, the resolved `$owner`, the mainnet IMD token's behavior and the opening-price currency ordering cannot be checked without the deployer or a fork, and the repository already documents them as open deployment items.","treeHash":null,"usage":{"cachedInputTokens":3436560,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":97471,"runtime":"claude","turns":55,"wallClockMs":2129123}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"c722c2e9ac9aa084","findings":[{"citation":"resolved","description":"submitResult pays the submitter max(pot/100, 5 IMD) whenever pot >= 5 IMD. The jump from 1% to a flat 5 IMD is a step, not a floor on the winner's share, so the winner's stream falls as the pot grows across 5 IMD. With pot = 4.99 IMD the winner streams 3.95208 IMD (submitter 0.0499); with pot = exactly 5 IMD the submitter takes all 5 IMD and the stream is created with total = 0; the winner only regains the 4.99 IMD payout once pot >= 9.94 IMD (0.8 * (pot - 5) >= 3.95208 with no token purchase; 10.65 IMD on the 70% basis if a purchase executes). The whole range pot in [5, 9.94) IMD pays the winner less than a 4.99 IMD pot. Reachable because setEntryFee accepts 1 IMD (line 594) and fundRound accepts any amount, so a settleable round can hold exactly 5e18. The manifest describes the minimum as 'when affordable'; this reports the concrete boundary where 'affordable' leaves the winner nothing. Boundary x invariant seam: the invariant 'winner share grows with the pot' breaks at the step. Minimal fix that keeps the 1%-with-5-IMD-minimum design: bound the minimum by a share of the pot, e.g. reward = min(5 ether, pot / 2) when pot / 100 < 5 ether, which is monotone and passes the proof.","line":440,"path":"src/HackathonMachine.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {HackToken} from \"src/HackToken.sol\";\nimport {HackathonMachine} from \"src/HackathonMachine.sol\";\nimport {OracleAttestation} from \"src/OracleAttestation.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\n\ncontract ScratchIMD is ERC20 {\n    constructor() ERC20(\"IdentityMD\", \"IMD\") {\n        _mint(msg.sender, 1_000_000 ether);\n    }\n}\n\n/// @dev Reproduces the non-monotonic winner payout around the 5 IMD submitter minimum in submitResult.\ncontract MinRewardBoundaryTest is Test {\n    uint256 internal constant SIGNER_KEY = 0xA77E57;\n    bytes32 internal constant QUESTION = keccak256(\"fixed question document\");\n    HackathonMachine internal machine;\n    ScratchIMD internal imd;\n    address internal owner = makeAddr(\"owner\");\n    address internal alice = makeAddr(\"alice\");\n    address internal keeper = makeAddr(\"keeper\");\n\n    function setUp() public {\n        vm.chainId(1);\n        vm.warp(345600 + 3000 * 7 days + 1 days);\n        imd = new ScratchIMD();\n        HackToken hack = new HackToken();\n        PoolManager manager = new PoolManager(address(this));\n        address hookAddress = address(uint160(0x100000) | 0x20cc);\n        deployCodeTo(\n            \"HackathonMachine.sol:HackathonMachine\",\n            abi.encode(IPoolManager(address(manager)), owner, address(this), address(hack), address(imd), vm.addr(SIGNER_KEY)),\n            hookAddress\n        );\n        machine = HackathonMachine(hookAddress);\n        imd.approve(address(machine), type(uint256).max);\n        vm.startPrank(owner);\n        machine.setQuestionHash(QUESTION);\n        machine.setEntryFee(1 ether);\n        vm.stopPrank();\n    }\n\n    function settleWithPot(uint256 pot) internal returns (uint256 streamedTotal, uint256 keeperReward) {\n        uint256 id = machine.register(\"Builder\", \"https://github.com/b/r\", \"\", alice, address(0));\n        if (pot > 1 ether) machine.fundRound(pot - 1 ether);\n        assertEq(machine.totalPot(), pot);\n        vm.warp(machine.roundClose(machine.currentRound()) + 1 hours);\n        OracleAttestation.Attestation memory a = OracleAttestation.Attestation({\n            requestId: keccak256(abi.encode(\"request\", id)),\n            chainId: 1,\n            questionHash: QUESTION,\n            answerType: 2,\n            answer: abi.encode(bytes32(id)),\n            figure: 0,\n            fromBlock: 100,\n            toBlock: 101,\n            blockHash: keccak256(\"block\"),\n            panelJobId: keccak256(\"panel\"),\n            panelSize: 7,\n            quorum: 5,\n            agreed: 5,\n            issuedAt: uint64(block.timestamp),\n            expiresAt: uint64(block.timestamp + 6 days)\n        });\n        (uint8 v, bytes32 r, bytes32 s) = vm.sign(SIGNER_KEY, machine.attestationDigest(a));\n        uint256 keeperBefore = imd.balanceOf(keeper);\n        vm.prank(keeper);\n        machine.submitResult(a, abi.encodePacked(r, s, v));\n        (,,, uint256 total,) = machine.streams(id);\n        return (total, imd.balanceOf(keeper) - keeperBefore);\n    }\n\n    /// A larger pot must never yield a smaller winner stream. On the current code a 4.99 IMD pot\n    /// streams 3.9521 IMD to the winner, a 5.00 IMD pot streams 0 (the submitter takes all 5 IMD),\n    /// and a 9.00 IMD pot streams 3.2 IMD.\n    function test_winnerPayoutIsMonotonicInPotSize() public {\n        uint256 snap = vm.snapshotState();\n        (uint256 below, uint256 rewardBelow) = settleWithPot(5 ether - 1e16);\n        vm.revertToState(snap);\n        snap = vm.snapshotState();\n        (uint256 at, uint256 rewardAt) = settleWithPot(5 ether);\n        vm.revertToState(snap);\n        (uint256 above,) = settleWithPot(9 ether);\n        emit log_named_uint(\"streamed at 4.99 IMD\", below);\n        emit log_named_uint(\"submitter reward at 4.99 IMD\", rewardBelow);\n        emit log_named_uint(\"streamed at 5.00 IMD\", at);\n        emit log_named_uint(\"submitter reward at 5.00 IMD\", rewardAt);\n        emit log_named_uint(\"streamed at 9.00 IMD\", above);\n        assertGe(at, below, \"winner stream fell when the pot grew from 4.99 to 5.00 IMD\");\n        assertGe(above, below, \"winner stream fell when the pot grew from 4.99 to 9.00 IMD\");\n    }\n}","reproduction":"Owner calls setEntryFee(1 ether). Alice registers (pot 1 IMD); anyone calls fundRound(4 ether) so totalPot() == 5e18. Close the round, submit a valid attestation naming Alice's entry. Actual: keeper receives 5e18 IMD, streams[id].total == 0, remaining == 0, carried == 0. Expected: a 5 IMD pot should not pay the winner less than a 4.99 IMD pot (3.9521e18 streamed). Same sequence with fundRound(3.99 ether) gives streamed 3,952,080,000,000,000,000 and reward 49,900,000,000,000,000; with fundRound(8 ether) streamed is 3.2e18, still below the 4.99 IMD case.","severity":"low","snippet":"        if (pot >= 5 ether && reward < 5 ether) reward = 5 ether;","title":"Winner payout is non-monotonic at the 5 IMD submitter minimum: a 5 IMD pot streams nothing to the winner"},{"citation":"resolved","description":"The hook's fee is defined as 2% of IMD on every trade. On the two IMD-specified paths _specifiedFee makes the fee 2% of the user's gross: exact-input takes amount*200/10000 of the budget, exact-output grosses up with (amount*200+9799)/9800. On the IMD-unspecified path afterSwap instead takes 2% of |imdDelta|, where imdDelta is the pool's IMD leg. When the user buys an exact HACK amount and pays IMD, imdDelta is the pool input and the user pays imdDelta + fee, so the fee is 2/102 = 1.9608% of what the buyer actually pays, versus 2.0000% for the same purchase routed as exact input. Measured on the fixture pool (1,000,000 liquidity, price 1): buying the HACK that 100 IMD exact-input delivers costs 100 IMD with a 2.0 IMD fee, while the identical HACK amount via exact output costs 99.96 IMD with a 1.96 IMD fee. Every buyer can route exact-output to pay 0.04% less of trade size, and the pot under-collects by 2% of its fee on that flow. Precision x invariant seam: the 'fixed 2% IMD fee' invariant holds on three of four modes and drifts on the fourth. The sell-side unspecified case (exact-input HACK, IMD out) is consistent: 2% of the gross output. Fix that preserves the design: when imdDelta < 0 (user pays IMD) use the gross-up (amount * FEE_BPS + 9799) / 9800 so fee/(amount+fee) == 2%; keep amount*FEE_BPS/10_000 when imdDelta > 0.","line":238,"path":"src/HackathonMachine.sol","reproduction":"seed() the launch pool at sqrtPrice 2^96 with 1,000,000e18 liquidity on [-60000, 60000]. Call 1: exact-input buy, amountSpecified = -100e18 IMD: feeClaims == 2,000,000,000,000,000,000 and HACK received H. Revert state. Call 2: exact-output buy of the same H HACK: router pays 99,960,000,000,000,000,000 IMD and feeClaims == 1,960,000,000,000,000,000, i.e. 196 bps of the total paid. Expected: both routes to the same HACK amount charge 2.00% of the IMD the buyer pays (2.0 IMD here). Also at the small end, an exact-input IMD budget of 49 wei pays fee 49*200/10000 == 0 while an exact-output IMD request of 1 wei pays fee ceil(200/9800) == 1 wei (50%); both are dust and favour the pot on the round-up side.","severity":"low","snippet":"        uint256 fee = amount * FEE_BPS / 10_000;","title":"afterSwap charges 2% of the pool input, not of the buyer's gross, on exact-output HACK purchases (effective 1.96%)"},{"citation":"resolved","description":"sqrtPriceX96 = 125270724187523965593206900 gives price token1/token0 = (sqrtP/2^96)^2 = 2.5e-6. That equals 2,500 IMD for the 1e9 HACK supply only when HACK is currency0 and IMD (0xd34a99bc...) is currency1, i.e. only when the CREATE2 HACK address is numerically below 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7. A HACK address above it (about 17.5% of uniformly random addresses, 1 - 0xd34a/0x10000) makes IMD currency0, and the same number then means 2.5e-6 HACK per IMD: 1 HACK = 400,000 IMD, a 4e14 IMD opening cap, 1.6e11 times the request. beforeInitialize (line 198) accepts either ordering, and test_invertedOrderingAcceptedAtManifestPrice shows the inverted pool initialising at this price. This is already recorded in docs/security-review.md as a deployment risk, and the evm-project reference states the deployer derives the effective opening price from the policy's 2,500 IMD cap rather than this field under current policies, so no code change is proposed; it is reported so the judge has the concrete number and condition. The deployer's derived price must be computed per the actual currency ordering of the deployed HACK address.","line":31,"path":"launch.json","reproduction":"Deploy HackToken to any address > 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7 (e.g. 0xffff...fff8 as in test/Revision.t.sol:255), initialise the IMD/HACK pool with sqrtPriceX96 125270724187523965593206900. Actual: getSlot0 price == 125270724187523965593206900 meaning 400,000 IMD per HACK. Expected for the request: 2.5e-6 IMD per HACK, which with IMD as currency0 is sqrtPriceX96 = sqrt(400000) * 2^96 = 50,110,290,000,000,000,000,000,000,000,000 (approx 5.011e31).","severity":"info","snippet":"    \"initialPrice\": \"125270724187523965593206900\"","title":"Manifest initialPrice encodes the 2,500 IMD cap only if HACK sorts as currency0; with HACK above 0xd34a... it means 1 HACK = 400,000 IMD"}],"hash":"1293c3c3a2d8370af01f0258bd457afa90bbff44335daf7be45225bda32897c4","nodeId":"1ec27575-56e5-40a7-b484-983a2c341531","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds the result. No tracked files were changed. The scratch tests live under `test/scratch/`, which git ignores.\n\n**What I covered.** I read the Senior Auditor's Mindset and the Math Precision, Boundary and Numerical Gap guides, then traced every division, cast, rounding and boundary in the hook fee paths, the settlement split, the vesting stream, the deny recycling and the round accounting. I also traced the Uniswap v4 delta plumbing in the vendored Hooks and Pool libraries to confirm the full-fill equality check and the claim minting net to zero. The existing suite of 102 tests passes. I probed the mainnet IMD token over a public RPC: it has code, reports symbol IMD with 18 decimals, and its selectors match a LayerZero OFT, so the exact-amount accounting in register and fundRound is sound.\n\n**Findings, ranked.**\n\n- **Low, with a failing proof.** The submitter reward steps from 1% to a flat 5 IMD at a pot of exactly 5 IMD, so the winner's stream is not monotone in pot size. A 4.99 IMD pot streams 3.95 IMD to the winner. A 5.00 IMD pot streams nothing, since the submitter takes all of it. The winner only recovers the 4.99 IMD payout once the pot reaches 9.94 IMD. This is reachable because the entry fee may be set as low as 1 IMD. The author's tests already assert this edge as intended, so the judge may treat it as a specified behaviour; the proof asserts monotonicity and fails on the current code.\n- **Low.** On exact-output HACK purchases the hook charges 2% of the pool's IMD input rather than 2% of what the buyer pays, so the effective fee is 1.96%. On the fixture pool, the same HACK amount costs 100 IMD with a 2.00 IMD fee by exact input and 99.96 IMD with a 1.96 IMD fee by exact output. The other three swap modes charge exactly 2% of gross. A gross-up on that one branch fixes it without changing the design.\n- **Info.** The manifest's initialPrice encodes the 2,500 IMD cap only if the deployed HACK address sorts below the IMD address. Otherwise the same number means 1 HACK = 400,000 IMD. The author already documents this and the reference says the deployer derives the opening price from policy, so I report it only for the record with the concrete number.\n\n**Coverage.** All 34 listed entry points have a row, plus two invariant rows. Two rows carry findings, the rest hold, none are unreached. Nothing in my area turned up a loss-of-funds defect: the fee delta math, the partial-fill rejection, the settlement conservation and the vesting arithmetic all check out with concrete values.","treeHash":null,"usage":{"cachedInputTokens":1293549,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":50049,"runtime":"claude","turns":28,"wallClockMs":1102941}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fa2b7fcb5f53535a","findings":[],"hash":"2c4b5759cdd67f37373e328c5948af909e56d94e7a18bd60ff6ef1c99ea1fead","nodeId":"d191a680-7a94-4549-a59f-bbe67e2bd717","outcome":"completed","summary":"The review is complete. The findings file is in place at the repository root, and no source or configuration file was changed.\n\n**Outcome: no reproducible defect in the control flow.** The findings list is empty and the coverage record answers all 34 listed entry points plus two invariants, every row `holds` with a one-line reason.\n\n**What I traced, against the Execution Trace, Periphery and First Principles guides:**\n\n- **Hook fee path** in `src/HackathonMachine.sol` against the vendored v4 dispatch in `lib/v4-core/src/libraries/Hooks.sol`. The specified-IMD fee is minted as an ERC6909 claim in beforeSwap and credited back through the hook delta, so the hook nets zero and the claim is always backed. The unspecified path charges 2% of executed IMD. Partial specified fills revert whole, as documented. Own swaps are skipped by v4 and compensated by the explicit own-fee in the buy.\n- **Settlement.** The manager-unlocked guard, replay keys, window checks, eligibility recheck, pot split and stream write all sit in the right order. The 500,000-gas buy sub-call is fixed regardless of the outer gas, so a keeper cannot starve it into fallback.\n- **Buy sub-call periphery.** A hostile prize-pool hook that reenters the machine, nests a launch-pool swap, or injects credit via `settleFor` ends in a revert of the sub-call and a stream fallback with no loss. Only the machine can take its own positive deltas, and claims cannot be burned without allowance.\n- **Streams and denial.** Claimed is monotonic, recycle sets total to claimed, and the denied state is permanent after recycle, so the post-recycle normal claim path is unreachable and cannot underflow.\n\n**Evidence I ran.** The existing 102-test suite passes. Six scratch probes under test/scratch/ also pass, covering the settleFor gift, hook reentry, keeper gas starvation over a range of limits, a 10 IMD pot, the deny/recycle/removeDeny claim sequence, and the exact-output gross-up on a real PoolManager. The static-analysis leads (strict equalities, uninitialized locals, reentrancy-after-call) were each checked and are either view-only, intentional defaults, or covered by the reentrancy guard.\n\n**Documented residuals I did not report as findings**, because the code behaves as the README and docs/security-review.md state: price ordering is not validated at initialization, the entrant's floor is the only buy price protection, deny is a delayed censorship power, the one-shot question pin has no correction path, and initialization requires the factory to call the manager directly.\n\n**Not reachable here.** Behaviour of the real IMD token and attester contract on mainnet, and the production deployer's initialization route, cannot be verified from this tree.","treeHash":null,"usage":{"cachedInputTokens":1744766,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":59947,"runtime":"claude","turns":34,"wallClockMs":1021040}},{"artifacts":[],"attempt":1,"bundleHash":"255f8cfc2cae5e258b9041118f96dbe6a4d6f00e9495ef1378813d8eb767680c","device":"2bb6789ad7b554e8","findings":[],"hash":"51d68709da66a1c9776674422921c6f1c6cdfb021970a410c9ddde692c079d21","nodeId":"4b4caa64-0eb9-494a-84f2-90bbc1268aa3","outcome":"completed","summary":"Added failure-path tests for oracle signatures, claim transfers, reentrancy, and ETH-pair rejection. Strengthened custody invariants with vesting and rollback checks.\n\nOffline `forge build` and `forge test` passed: **116 tests, zero failures or skips**.\n\nOnly `test/` changed. IMD pairing and `launch.json` remain unchanged. No new defects found.","treeHash":"57a0950cef7a69b9f05bd80fa817a367ef35a8fa","usage":{"cachedInputTokens":1345536,"inputTokens":110145,"model":"gpt-6-astra","outputTokens":13874,"runtime":"codex","turns":7,"wallClockMs":544216}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"dc7103e773ea194a","findings":[{"citation":"resolved","description":"Access x asymmetry seam. The README and launch.json notes state that the launch payer is the permanently excluded payout and the one-shot question pinner, but the contract has a single owner_ argument that fills both roles, and the reference says $owner resolves from policy. docs/policy-snapshot.json names hookAdmin = 0xcecc29b037f5064fcdf45a5c318f132ef76aa551 (an EOA on mainnet, no code). If the deployer resolves $owner to that policy admin while a different wallet pays for the launch, the _eligible exclusion at line 393 targets the wrong address: the actual payer can register, win and stream prizes, and setQuestionHash (line 587) can only be called by the policy admin, so settlement is blocked until that admin acts. No permission bypass exists in the code; this is a configuration/trust gap that cannot be resolved from the repository and was previously dispositioned by the imported audit as 'not established'. Evidence needed: the deployer's resolved $owner for launch 1032 compared with the paying wallet. No code change is proposed; keeping the ABI as instructed means the service must resolve $owner to the paying wallet.","line":161,"path":"src/HackathonMachine.sol","reproduction":"State: deploy HackathonMachine with owner_ = 0xcecc29b037f5064fcdf45a5c318f132ef76aa551 and a distinct payer P. Calls: P.register(name, repo, ref, P, 0) succeeds (expected per README: Ineligible); P.setQuestionHash(h) reverts Unauthorized; after a signed result for P's entry, submitResult reverts InvalidResult until 0xcecc... calls setQuestionHash, after which P receives a 792 IMD stream from a 1000 IMD pot. Existing test test/ImportedAudit.t.sol::test_ownerArgumentAloneControlsExclusionAndQuestionPin reproduces this exactly.","severity":"info","snippet":"        payingWallet = owner_; // This launch names no separate owner: $owner resolves to the paying wallet.","title":"Trust gap: owner and payingWallet are one constructor argument; if the service resolves $owner to the policy hookAdmin instead of the paying wallet, the payer stays prize-eligible and only the policy "},{"citation":"resolved","description":"register() passes an unbounded maxEntryFee into _register, which reads the live entryFee (line 337) and pulls it from msg.sender. The owner may change entryFee in [1, 100] IMD at any time. A registrant who approved an open allowance and calls the five-argument register can be sandwiched by setEntryFee(100 ether) and loses up to 90 IMD more than expected. The overcharge goes to the pot (openPot/liquidBalance), not to the owner, and the owner is excluded from prizes, so there is no direct extraction; this is a documented admin power (README section on fee consent) with registerWithMaxFee as the user-side guard. Reported as a trust assumption for the record, not as a defect requiring a code change.","line":313,"path":"src/HackathonMachine.sol","reproduction":"State: entryFee = 10 IMD, registrant R approved type(uint256).max to the machine. Calls: owner.setEntryFee(100 ether); R.register('n','https://x','',R,0). Actual: R's IMD balance drops by 100 IMD, totalPot = 100 IMD, owner balance unchanged. Expected by a user who read 'entry fee 10 IMD': 10 IMD. Verified with a scratch test (test_entryFeeSandwichOnPlainRegister) on this tree; the same call via registerWithMaxFee(..., 10 ether) reverts EntryFeeExceedsMaximum before any transfer.","severity":"info","snippet":"        return _register(name, repoUrl, imdRef, payout, token, type(uint256).max);","title":"Trust gap (access x economics): owner can front-run a plain register() with setEntryFee and charge a registrant with an open allowance up to 100 IMD instead of 10; registerWithMaxFee is the documented"},{"citation":"resolved","description":"submitResult reads winnerBps, minPanelSize and minAgreement live at settlement rather than snapshotting them at round close. The owner can therefore lower the winner's share of an already-closed pot (from 70% to 50% of the remainder) or raise panel floors above the panel size of an attestation already issued for the closed round, causing it to revert InvalidResult and the pot to roll over if no replacement attestation arrives before the next close. The difference stays in the pot and the owner cannot withdraw or win, so there is no extraction path and no unprivileged amplifier; the README documents both ('Settings other than delays apply when the relevant operation executes, including the share and panel floors at settlement'; 'increasing quorum can delay settlement'). Recorded as a privileged-power trust assumption, not a defect.","line":442,"path":"src/HackathonMachine.sol","reproduction":"State: entry id for round R, pot 1000 IMD (10 fee + 990 funding), round closed, winnerBps 7000. Calls: owner.setWinnerBps(5000); keeper.submitResult(valid attestation for id). Actual: streams[id].total = 594 IMD (495 streamed + 99 unbought) instead of 792 IMD; openPot = 396 IMD; owner balance 0. Verified with a scratch test (test_winnerBpsChangeAfterCloseAppliesToClosedRound). Panel variant: owner.setPanelFloors(8, 5) then submitResult with panelSize 7 reverts InvalidResult.","severity":"info","snippet":"        uint256 streamed = remaining * winnerBps / 10_000;","title":"Trust gap (access x asymmetry): setWinnerBps and setPanelFloors apply at settlement, so the owner can change a closed round's stream share or invalidate an already-issued attestation after entries wer"}],"hash":"a622d5e9611768bd6f4a8d6dfb94414f71d01a35b6985a992f600d2858ea8266","nodeId":"1a718e46-15d7-4cff-a17f-174c48f11724","outcome":"completed","summary":"The review is complete. The findings file is written at `.imd-findings.json`, the scratch tests are removed, and no source or configuration file was changed.\n\n**Outcome: no permission bypass found.** I traced every state-changing entry point from the Access Control, Trust Gap and Asymmetry guides and found the permission model tight. The suite of 102 existing tests passes on this tree. All three items I report are informational trust assumptions, each with a concrete state and a reproduction I ran, and none calls for a code change.\n\n**What I report**\n\n- **Owner and paying wallet share one constructor argument.** The README promises the payer is excluded from prizes and holds the one-shot question pin, but the reference says `$owner` resolves from policy, and the policy snapshot names hookAdmin 0xcecc29b0… (a plain EOA on mainnet). If the service resolves `$owner` to that admin while a different wallet pays, the actual payer stays eligible and only the policy admin can pin the question. The existing imported-audit test reproduces this; the deployer's resolved `$owner` is the evidence still needed.\n- **Plain `register` accepts any live fee.** The owner can sandwich it with a fee raise to 100 IMD. The overcharge lands in the pot, not with the owner. Already documented, with `registerWithMaxFee` as the guard.\n- **Winner share and panel floors are read live at settlement.** The owner can shrink a closed round's stream from 792 to 594 IMD out of 1000, or invalidate an already-issued attestation by raising the floors. Funds stay in the pot and roll forward. Already documented.\n\n**What holds, in depth**\n\n- Manager-only callbacks are bound to the one initialized pool, fees are minted only as the hook's own ERC6909 claims, and the hook's own buy charges the same 2% explicitly where v4 skips its callbacks.\n- The unlock path requires manager caller, entered guard and an exact calldata hash set in the same call. The self-call buy boundary rejects outside callers.\n- Entry updates and buy configuration are payout-only and frozen at close. Owner and keepers cannot change route, recipient or floor.\n- Deny, signer and domain-version changes are owner-only with public delays, and the deny generation asymmetry (pending cancel preserves streams, active removal revokes them) matches the documented intent.\n- Mainnet reads confirm the constructor inputs: the IMD address is an 18-decimal OFT-style ERC20 with no pause selector, and the attester is an EOA.\n\n**Coverage.** All 34 listed entry points have a row, plus three invariant rows. Three admin setters and the two result and registration paths are marked `finding` pointing at the informational items; everything else is `holds`. Nothing is `unreached`.","treeHash":null,"usage":{"cachedInputTokens":1472707,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":46882,"runtime":"claude","turns":36,"wallClockMs":904008}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"229c8cb0d9067fbe","findings":[{"citation":"resolved","description":"The constructor has no separate paying-wallet argument: the second constructor argument is used both as `owner` (setQuestionHash once, setEntryFee, setWinnerBps, setPanelFloors, pauseEntries, signer/domain rotation, deny list) and as `payingWallet`, the address `_eligible` permanently excludes from registering or winning. launch.json passes `$owner` for that slot and the launch references say `$owner` resolves from the launch policy, not from the request. The policy snapshot committed in this repository (docs/policy-snapshot.json, policy 34) names `owners.hookAdmin = 0xcecc29b037f5064fcdf45a5c318f132ef76aa551` (an EOA on mainnet as of this review; the same address is also the policy's token/treasury/lpPosition owner), while the README and the code comment assume `$owner` is the wallet that paid for the launch. If those differ: (1) the real paying wallet can register and win prizes, voiding the documented exclusion; (2) every owner power sits with 0xcecc..., and in particular `setQuestionHash` must be called once by that address before any `submitResult` can succeed, so if 0xcecc... is not operated by the requester the machine cannot settle any round and every pot rolls forever (there is no withdrawal path). This could not be verified from the repository: the resolution of `$owner` happens in the launch service. Suggested handling: confirm what `$owner` resolves to for this launch before deployment; if it is not the paying wallet, either have the service resolve it to the paying wallet or accept and document that the exclusion does not apply and that 0xcecc... must perform the question pin. A code-level fix (a distinct `payingWallet_` constructor argument) would change the constructor ABI and launch.json, which this task keeps as is, so it is not recommended here.","line":161,"path":"src/HackathonMachine.sol","reproduction":"State: deploy HackathonMachine with owner_ = 0xcecc29b037f5064fcdf45a5c318f132ef76aa551 (the policy hookAdmin) while the wallet that paid for the launch is W != 0xcecc.... Calls: (a) anyone calls register(\"Payer project\", \"https://github.com/payer/repo\", \"\", W, address(0)) -> succeeds and entries[id].payout == W, and a later attestation naming that id settles with lastWonRound[W] set and a stream paying W; expected per README: Ineligible because the paying wallet is excluded. (b) W calls setQuestionHash(h) -> reverts Unauthorized; only 0xcecc... can pin, and until it does submitResult reverts InvalidResult (questionHash == 0). Verified locally with a scratch test deploying the hook with owner_ = 0xcecc... and registering payout W.","severity":"low","snippet":"        payingWallet = owner_; // This launch names no separate owner: $owner resolves to the paying wallet.","title":"payingWallet is taken from $owner; if $owner resolves to the policy hookAdmin the paying wallet is not excluded and owner powers (including the one-shot questionHash pin) belong to another address"},{"citation":"resolved","description":"`_recycle` moves `total - claimed` of a denied winner's stream back into the open pot. `claimed` only grows when someone calls `claim`, so the amount recycled includes IMD that had already vested but was not yet claimed. Because `queueDeny` is owner-only and delayed 48 hours with a public event, and because `claim` is permissionless (anyone can push vested funds to the winner during the delay), this is an intentional owner power rather than a permission bypass, and it is described in the README. It is recorded here because it is the one place where IMD that the contract's own vesting schedule already attributes to a winner can be redirected. A design-preserving softening, if the requester wants it, is to pay `vested - claimed` to the payout inside `_recycle` before recycling only the unvested remainder; that keeps the deny list and its delay unchanged. No change is required for correctness.","line":563,"path":"src/HackathonMachine.sol","reproduction":"State: a 792 IMD stream for payout P starts at t0 (pot 1000 IMD, no token). The owner calls queueDeny(P) at t0 + 7 days; isDenied(P) becomes true at t0 + 9 days. Nobody calls claim(id) before that. At t0 + 9 days, vested = 792 * 9 / 28 = 254.57 IMD and claimed = 0. Call: claim(id) (anyone) -> returns 0, emits StreamRecycled(id, 792e18), openPot += 792e18, streamLiability -= 792e18; P receives nothing, including the 254.57 IMD already vested. If P had claimed at t0 + 7 days it would have received 198 IMD and 594 IMD would be recycled instead.","severity":"info","snippet":"        uint256 unpaid = s.total - s.claimed;","title":"Deny list recycles vested-but-unclaimed prize IMD, not only unvested amounts (intentional per README; documented as a trust assumption)"},{"citation":"resolved","description":"`_isPair` accepts IMD on either side of the key. launch.json's initialPrice 125270724187523965593206900 (sqrtPriceX96) encodes 1e9 HACK = 2,500 IMD only when HACK is currency0, i.e. when the CREATE2-derived HACK address is numerically below 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7. With the opposite ordering the same number prices 1 HACK at about 400,000 IMD, and the hook cannot tell. The launch reference states the deployer derives the effective opening price from the policy's 2,500 IMD cap for the actual pair, in which case this is moot, but that derivation could not be verified from the repository. No code change is recommended: enforcing an ordering in `beforeInitialize` would make the launch transaction revert whenever the mined token address sorts above IMD. The deployer should confirm the predicted token address, the resulting currency order and the opening price before broadcasting (test/Revision.t.sol:254 shows the hook accepting the inverted key at the manifest price).","line":198,"path":"src/HackathonMachine.sol","reproduction":"State: HACK deployed at an address > 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7, so key = (currency0 = IMD, currency1 = HACK, 12500, 60, hook). Call: factory -> PoolManager.initialize(key, 125270724187523965593206900). Result: beforeInitialize returns its selector, initialized = true, the pool opens at price(HACK per IMD) = 2.5e-6, i.e. 1 HACK = 400,000 IMD instead of 1e9 HACK = 2,500 IMD. Expected: either the price for that ordering (sqrtPriceX96 = floor(2^96 * sqrt(400000)) = 50108289675009586237282760313921) or a revert.","severity":"info","snippet":"            initialized || sender != factory || address(key.hooks) != address(this) || !_isPair(key, launchToken)","title":"beforeInitialize accepts either currency ordering, so an opening price derived for HACK-as-currency0 would be silently inverted if the deployed HACK address sorts above IMD and the manifest price is u"},{"citation":"resolved","description":"Every fixture passes the same `owner` address as both the owner and the excluded paying wallet, so the suite cannot observe the finding above. The purchase tests cover empty, partial, false-returning and reentering prize tokens and hookless pools, but no test puts a hook with beforeSwap/afterSwap flags on the entrant's pool, and none exhausts the 500,000 gas purchase budget. These edges were checked during this review with a scratch suite (not kept): a hook that re-enters claim/fundRound/redeemFeeClaims, swaps on the launch pool, calls PoolManager.take(IMD, attacker, 50 IMD) or loops forever, and a token whose transfer burns about 1M gas. All attempts were refused by the reentrancy guard or ended in the documented stream fallback with accounting intact (stream totals 693.8316 IMD when the purchase still succeeded, 792.9504 IMD when it fell back, for a 1001.2 IMD pot). Adding those cases to the kept suite would stop them from regressing silently.","line":57,"path":"test/MachineFixture.sol","reproduction":"Edge 1: deploy with owner_ = A and register payout = B where B paid for the launch; the kept suite has no such case. Edge 2: PoolKey(prize, IMD, 3000, 60, hook with flags 0x00c0) whose beforeSwap calls PoolManager.take(IMD, attacker, 50e18) during submitResult; expected and observed: BuySkipped, stream += 10% allocation, attacker balance unchanged. Edge 3: prize token whose transfer loops 1,000,000 iterations; expected and observed: BuySkipped and stream of 792 IMD for a 1000 IMD pot.","severity":"info","snippet":"                IPoolManager(address(manager)), owner, address(this), address(hack), address(imd), vm.addr(SIGNER_KEY)","title":"Test suite never exercises owner != paying wallet, a hostile hook on the entrant's purchase pool, or a gas-exhausting prize token"},{"citation":"resolved","description":"Read in full: src/HackToken.sol, src/HackathonMachine.sol, src/OracleAttestation.sol, src/HookFlags.sol (unused by production code), every file under test/, launch.json, docs/*, and the vendored v4-core paths the hook depends on (PoolManager, Hooks, Pool.swap, BeforeSwapDelta, TransientStateLibrary, StateLibrary, Lock, CurrencyReserves, ERC6909Claims, SafeCast). Reached over the network (public RPC, read-only): the IMD token at 0xd34a99bc...e263b7 is a 22,990-byte non-proxy LayerZero OFT ERC20 (name Identity.md, 18 decimals, about 4.138M supply, no pause/blacklist/fee selectors in its dispatcher), the attester 0x5598aa...2982 and the policy owner 0xcecc...a551 are EOAs, and the PoolManager at 0x000000000004444c5dc75cB358380D2e3dE08A90 contains the transient Lock/CurrencyReserves slot constants the hook reads, so isUnlocked and getSlot0 work against the deployed manager. Not reachable: the launch factory and its initialize path (beforeInitialize requires PoolManager.initialize to be called with msg.sender == $factory; a factory that initializes through periphery would make the launch revert, safely), the resolution of $poolManager/$owner/$factory, and the oracle's canonical questionHash. Compiled artifacts: hook runtime 23,221 bytes, token 1,502 bytes, no DELEGATECALL/CALLCODE/SELFDESTRUCT; full suite passes (90 tests including both invariant campaigns). No minting, proxy, initializer, holder-balance pause or token blacklist exists; pauseEntries only stops new registrations. Privileged powers, all bounded and documented, that holders must trust: the attester key (an EOA; a compromise settles pots to an attacker's entry for up to 7 days because rotation is delayed and there is no emergency stop; the owner can limit loss to the keeper reward, 48 hours of vesting and the 10% purchase by queueing a deny), the owner's ability to stall settlement by raising panel floors or to censor a payout after 48 hours, first-valid-attestation-wins settlement when independently purchased panels disagree, and keeper timing of the winner-token purchase within the entrant's own floor.","line":23,"path":"src/HackathonMachine.sol","reproduction":"Not a defect; see the reachability list in the description. Concrete states checked: 90 kept tests pass; scratch probes (hostile hook modes 1-4, gas-burning token, owner != payer) behave as described in the other entries.","severity":"info","snippet":"contract HackathonMachine is OracleAttestationConsumer, ReentrancyGuard {","title":"Review coverage, external contracts reached, and the privileged powers this launch relies on"}],"hash":"a79b4f30e36f7f687d33dca9fef6f4e7db76f04a635839a829ad0ac35e5804d6","nodeId":"2c471d5f-27e5-4d3e-9bd5-b7b1bb8a742f","outcome":"completed","summary":"The findings file is in place at the repository root (the harness's own exclude rule keeps it out of git, which is expected), and the scratch tests live only under the ignored `test/scratch/`. The review is complete.\n\n## Outcome\n\nNo critical, high or medium defect was found. The `.imd-findings.json` file holds one low finding and four informational entries. The full kept suite passes on the code as it is (90 tests, including both invariant campaigns), the hook runtime is 23,221 bytes, the token 1,502 bytes, and neither contains DELEGATECALL, CALLCODE or SELFDESTRUCT.\n\n**Contracts read in full:** `src/HackToken.sol`, `src/HackathonMachine.sol`, `src/OracleAttestation.sol`, `src/HookFlags.sol` (unused by production code), every test under `test/`, `launch.json`, `docs/*`, and the vendored v4-core paths the hook depends on (PoolManager, Hooks, Pool.swap, BeforeSwapDelta, transient-state and state libraries, Lock, CurrencyReserves, ERC6909Claims, SafeCast).\n\n**Reached over the network (read-only public RPC):** the mainnet IMD token is a non-proxy LayerZero OFT ERC20 with 18 decimals and no pause, blacklist or fee selectors; the attester and the policy owner are EOAs; the deployed PoolManager contains the transient slot constants the hook reads, so its unlock check and `getSlot0` work against mainnet.\n\n**Not reachable:** the launch factory and how it calls `initialize` (the hook requires the factory itself to be `msg.sender`, otherwise the launch reverts safely), the resolution of the `$poolManager`, `$owner` and `$factory` placeholders, and the oracle's canonical question hash.\n\n## Findings\n\n- **Low. `payingWallet` is taken from `$owner`** (`src/HackathonMachine.sol:161`). The repository's own policy snapshot names `hookAdmin = 0xcecc…a551` while the README assumes `$owner` is the paying wallet. If they differ, the paying wallet can register and win, and every owner power sits with `0xcecc…`, including the one-shot question pin without which no round can ever settle. The resolution could not be verified here. No code change is recommended because a separate constructor argument would alter the manifest.\n- **Info. Deny list recycles vested-but-unclaimed prize IMD**, not only unvested amounts. Intentional per the README and delayed 48 hours, so it is recorded as a trust assumption with a design-preserving softening if the requester wants one.\n- **Info. `beforeInitialize` accepts either currency ordering**, so the manifest's opening price is only right when the mined HACK address sorts below IMD. The deployer should confirm the predicted ordering and price; enforcing it on-chain would make the launch revert in the other case.\n- **Info. Untested edges:** owner distinct from paying wallet, a hostile hook on the entrant's purchase pool, and a gas-exhausting prize token. I checked all three with scratch tests. Every hostile attempt was refused by the reentrancy guard or ended in the documented stream fallback with accounting intact.\n- **Info. Coverage and privileged powers** holders must trust: the attester key (an EOA, with a 7-day rotation delay and no emergency stop), the owner's ability to stall settlement through panel floors or censor a payout after 48 hours, first-valid-attestation-wins settlement, and keeper timing of the winner purchase within the entrant's floor.\n\n## What the adapter should do\n\nNothing in the Solidity needs changing for this launch. Before deployment the service should confirm what `$owner` resolves to, and that the factory calls `PoolManager.initialize` directly with the HACK/IMD key at fee 12500 and spacing 60, since the hook refuses anything else.","treeHash":null,"usage":{"cachedInputTokens":5327051,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":98312,"runtime":"claude","turns":53,"wallClockMs":1771076}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"73d25b5e0857cef5","findings":[],"hash":"e2197064438e69c4aa0c65429dcea1fed30aa4e7ceab2f30ebc8bba2acdf84e0","nodeId":"68a0c5d4-e3a6-4b17-9461-b58caafcb2c0","outcome":"failed","summary":"needs input: The brief requires preserving the accepted univ4_hook IMD/HACK launch and also requires an evm_project manifest paired only with native ETH at fee 500, 3000, or 10000. These requirements conflict. The existing launch.json and docs/launch-request.json select univ4_hook and IMD (0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7). HackathonMachine.beforeInitialize requires that IMD/HACK pair, fee 12500, tickSpacing 60, and HackathonMachine itself as the pool hook. The required evm_project format instead supplies an initialization-only guard and does not support the accepted $poolManager and $factory references. No manifest-only change can satisfy both specifications while preserving the accepted implementation. The requester must resolve the launch kind, pairing, and fee conflict. — Which launch specification is authoritative for this assignment: the accepted univ4_hook IMD/HACK launch with fee 12500, or the incompatible evm_project ETH-only requirements?","treeHash":null,"usage":{"cachedInputTokens":909696,"inputTokens":48604,"model":"gpt-6-astra","outputTokens":6550,"runtime":"codex","turns":6,"wallClockMs":374122}},{"artifacts":[],"attempt":1,"bundleHash":"676db28c5a76c53b80d8ced32b671d23834b22937247f14c965ab35cf694328c","device":"42dc54b22317eb81","findings":[],"hash":"ef386ded787f31a39ca3c0c85169c52a9552c2f2fa173cb415cad5a20b567b17","nodeId":"fe312a02-60d9-4a5a-86e4-a4a4d0909933","outcome":"completed","summary":"Added `src/LaunchToken.sol` using the existing `HackToken` implementation; creation and runtime bytecode match exactly. Added regression tests and updated `ADAPTATION.md`.\n\nIMD pairing, `launch.json`, existing contracts, configuration, and dependencies remain unchanged.\n\n`forge build` passed. `forge test`: **102 passed, 0 failed, 0 skipped**.","treeHash":"c859a0c8c3b1e205bc2aa951dadf9dded02bfbeb","usage":{"cachedInputTokens":2653184,"inputTokens":144518,"model":"gpt-6-astra","outputTokens":19987,"runtime":"codex","turns":11,"wallClockMs":1213814}}],"verification":[{"checks":[{"durationMs":95229,"exitCode":0,"name":"build","output":"Compiling 120 files with Solc 0.8.26\nSolc 0.8.26 finished in 95.02s\nCompiler run successful!\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/HackathonMachine.sol:194:9\n    │\n194 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/HackathonMachine.sol:219:73\n    │\n219 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(_asInt128(fee), 0), 0);\n    │                                                                         ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/HackathonMachine.sol:210:9\n    │\n210 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/HackathonMachine.sol:225:9\n    │\n225 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:233:56\n    │\n233 │             int256 expected = params.amountSpecified + int256(_specifiedFee(params.amountSpecified));\n    │                                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/HackathonMachine.sol:237:41\n    │\n237 │         uint256 amount = imdDelta < 0 ? uint256(-int256(imdDelta)) : uint256(int256(imdDelta));\n    │                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:237:70\n    │\n237 │         uint256 amount = imdDelta < 0 ? uint256(-int256(imdDelta)) : uint256(int256(imdDelta));\n    │                                                                      ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:245:26\n    │\n245 │         uint256 amount = uint256(specified < 0 ? -specified : specified);\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:246:22\n    │\n246 │         if (amount > uint256(uint128(type(int128).max))) revert InvalidInput();\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:246:30\n    │\n246 │         if (amount > uint256(uint128(type(int128).max))) revert InvalidInput();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/HackathonMachine.sol:266:9\n    │\n266 │         emit FeeCollected(accountingRound, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/HackathonMachine.sol:270:16\n    │\n270 │         return block.timestamp < MONDAY_EPOCH ? 0 : uint64((block.timestamp - MONDAY_EPOCH) / WEEK);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:270:53\n    │\n270 │         return block.timestamp < MONDAY_EPOCH ? 0 : uint64((block.timestamp - MONDAY_EPOCH) / WEEK);\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[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/HackathonMachine.sol:459:33\n    │\n459 │         streamed += buyAmount - bought;\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/HackathonMachine.sol:456:17\n    │\n456 │                 emit BuySkipped(id);\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/HackathonMachine.sol:462:9\n    │\n462 │         emit ResultSettled(round, id, a.requestId, msg.sender, reward, streamed, bought, carried);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:460:40\n    │\n460 │         streams[id] = Stream(e.payout, uint64(block.timestamp), denyGeneration[e.payout], streamed, 0);\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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/HackathonMachine.sol:497:13\n    │\n497 │             emit ClaimsRedeemed(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/HackathonMachine.sol:525:13\n    │\n525 │             emit FeeCollected(accountingRound, ownFee);\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/HackathonMachine.sol:532:9\n    │\n532 │         emit TokenBought(id, spend, uint256(uint128(output)));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:518:66\n    │\n518 │             poolManager.swap(key, SwapParams(zeroForOne, -int256(uint256(uint128(_asInt128(spend)))), limit), \"\");\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:518:74\n    │\n518 │             poolManager.swap(key, SwapParams(zeroForOne, -int256(uint256(uint128(_asInt128(spend)))), limit), \"\");\n    │                                                                          ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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/HackathonMachine.sol:521:31\n    │\n521 │         if (int256(input) != -int256(spend) || output <= 0 || uint256(uint128(output)) < minimum) revert Slippage();\n    │                               ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/HackathonMachine.sol:521:63\n    │\n521 │         if (int256(input) != -int256(spend) || output <= 0 || uint256(uint128(output)) < minimum) revert Slippage();\n    │                                                               ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:521:71\n    │\n521 │         if (int256(input) != -int256(spend) || output <= 0 || uint256(uint128(output)) < minimum) revert Slippage();\n    │                                                                       ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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/HackathonMachine.sol:531:60\n    │\n531 │         poolManager.take(Currency.wrap(e.token), e.payout, uint256(uint128(output)));\n    │                                                            ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:531:68\n    │\n531 │         poolManager.take(Currency.wrap(e.token), e.payout, uint256(uint128(output)));\n    │                                                                    ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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/HackathonMachine.sol:532:37\n    │\n532 │         emit TokenBought(id, spend, uint256(uint128(output)));\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:532:45\n    │\n532 │         emit TokenBought(id, spend, uint256(uint128(output)));\n    │                                             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/HackathonMachine.sol:543:26\n    │\n543 │         uint256 vested = elapsed >= STREAM_DURATION ? s.total : FullMath.mulDiv(s.total, elapsed, STREAM_DURATION);\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/HackathonMachine.sol:548:9\n    │\n548 │         emit Claimed(id, s.payout, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:577:22\n    │\n577 │         if (amount > uint256(uint128(type(int128).max))) revert InvalidInput();\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:577:30\n    │\n577 │         if (amount > uint256(uint128(type(int128).max))) revert InvalidInput();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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/HackathonMachine.sol:578:16\n    │\n578 │         return int128(uint128(amount));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' 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/HackathonMachine.sol:578:23\n    │\n578 │         return int128(uint128(amount));\n    │                       ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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/HackathonMachine.sol:620:25\n    │\n620 │         signerReadyAt = uint64(block.timestamp + ORACLE_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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/HackathonMachine.sol:632:13\n    │\n632 │         if (block.timestamp < signerReadyAt) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:642:26\n    │\n642 │         versionReadyAt = uint64(block.timestamp + ORACLE_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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/HackathonMachine.sol:654:13\n    │\n654 │         if (block.timestamp < versionReadyAt) revert TooEarly();\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/HackathonMachine.sol:693:39\n    │\n693 │         return denyAt[payout] != 0 && block.timestamp >= denyAt[payout];\n    │                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:698:26\n    │\n698 │         denyAt[payout] = uint64(block.timestamp + DENY_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[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.revertToState`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/AdversarialMachine.t.sol:254:20\n    │\n254 │         assertTrue(vm.revertToState(snapshot));\n    │                    ────────────────────────── `vm.revertToState` 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.revertToState`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/AdversarialMachine.t.sol:254:20\n    │\n254 │         assertTrue(vm.revertToState(snapshot));\n    │                    ────────────────────────── `vm.revertToState` 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/AdversarialMachine.t.sol:302:13\n    │\n302 │             vm.warp(start + 28 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/AdversarialMachine.t.sol:302:13\n    │\n302 │             vm.warp(start + 28 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/AdversarialMachine.t.sol:341:9\n    │\n341 │         vm.warp(vm.getBlockTimestamp() + 7 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/AdversarialMachine.t.sol:341:9\n    │\n341 │         vm.warp(vm.getBlockTimestamp() + 7 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Attestation.t.sol:87:26\n   │\n87 │         uint256 queued = block.timestamp;\n   │                          ━━━━━━━━━━━━━━━\n   ‡\n90 │         vm.warp(queued + elapsed);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Attestation.t.sol:114:9\n    │\n114 │         vm.warp(block.timestamp + 7 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Attestation.t.sol:114:9\n    │\n114 │         vm.warp(block.timestamp + 7 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Attestation.t.sol:114:17\n    │\n114 │         vm.warp(block.timestamp + 7 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Attestation.t.sol:120:17\n    │\n120 │         vm.warp(block.timestamp + 6 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Attestation.t.sol:135:9\n    │\n135 │         vm.warp(block.timestamp + 7 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Attestation.t.sol:135:9\n    │\n135 │         vm.warp(block.timestamp + 7 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Attestation.t.sol:135:17\n    │\n135 │         vm.warp(block.timestamp + 7 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Attestation.t.sol:141:17\n    │\n141 │         vm.warp(block.timestamp + 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Attestation.t.sol:163:17\n    │\n163 │         vm.warp(block.timestamp + 7 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n 98 │         vm.warp(machine.roundClose(machine.currentRound()) + 1 hours);\n    │         ───────────────────────────────────────────────────────────── `vm.warp` changes this environment here\n    ‡\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n 98 │         vm.warp(machine.roundClose(machine.currentRound()) + 1 hours);\n    │         ───────────────────────────────────────────────────────────── `vm.warp` changes this environment here\n    ‡\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Machine.t.sol:231:17\n    │\n231 │         vm.warp(block.timestamp + 2 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Revision.t.sol:22:9\n    │\n 22 │         vm.warp(block.timestamp + 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Revision.t.sol:22:9\n    │\n 22 │         vm.warp(block.timestamp + 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Revision.t.sol:22:17\n   │\n22 │         vm.warp(block.timestamp + 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":5892,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Token.t.sol:TokenTest\n[PASS] test_fixedSupplyMetadataAndTransfer() (gas: 234376)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 436.48µs (331.30µs CPU time)\n\nRan 2 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] test_factoryReceivesFixedSupplyAndTransfersExactAmount() (gas: 305379)\n[PASS] test_runtimeMatchesExistingManifestToken() (gas: 6854)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 492.72µs (565.34µs CPU time)\n\nRan 1 test for test/LaunchPurchase.t.sol:LaunchPurchaseTest\n[PASS] test_winnerTokenPurchaseExecutesForOrdinaryPot() (gas: 1005203)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.46ms (893.99µs CPU time)\n\nRan 9 tests for test/ImportedAudit.t.sol:ImportedAuditTest\n[PASS] test_effectiveDenyRecyclesVestedButUnclaimedAmounts() (gas: 958675)\n[PASS] test_gasExhaustingPrizeTokenFallsBack() (gas: 2519808)\n[PASS] test_gasExhaustingPurchaseHookFallsBack() (gas: 2932290)\n[PASS] test_ownerArgumentAloneControlsExclusionAndQuestionPin() (gas: 1387525)\n[PASS] test_purchaseHookCannotReenterAfterSwap() (gas: 2640446)\n[PASS] test_purchaseHookCannotReenterBeforeSwap() (gas: 2640324)\n[PASS] test_purchaseHookCannotStealIMDAfterSwap() (gas: 2660816)\n[PASS] test_purchaseHookCannotStealIMDBeforeSwap() (gas: 2660666)\n[PASS] test_purchaseHookCannotSwapOnLaunchPool() (gas: 3041569)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 37.66ms (12.31ms CPU time)\n\nRan 6 tests for test/ContractSigner.t.sol:ContractSignerTest\n[PASS] test_contractApprovalBindsBothDigestAndSignatureBytes() (gas: 1187457)\n[PASS] test_contractApprovalCannotBypassPanelPolicy() (gas: 1169153)\n[PASS] test_contractSignerAcceptsItsOwnSignatureFormatAndStillPreventsReplay() (gas: 971558)\n[PASS] test_domainRotationInvalidatesContractApprovalUntilNewDigestIsApproved() (gas: 1290355)\n[PASS] test_rejectedRevertingAndMalformedResponsesRollBackAndCanRetry() (gas: 2334196)\n[PASS] test_revocationIsCheckedAtSubmissionAndDoesNotConsumeRequest() (gas: 1188368)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 39.16ms (3.20ms CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] test_acceptsExactProtocolSignature() (gas: 23346)\n[PASS] test_digestMatchesTheProtocol() (gas: 16335)\n[PASS] test_freshVectorKeySignatureSettlesBytes32Entry() (gas: 649612)\n[PASS] test_submitSelectorUsesAllFifteenCanonicalFields() (gas: 3450)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 50.88ms (1.16ms CPU time)\n\nRan 7 tests for test/Attestation.t.sol:AttestationTest\n[PASS] testFuzz_queuedSignerHasNoEffectBeforeSevenDays(uint32) (runs: 256, μ: 192125, ~: 192058)\nLogs:\n  Bound result 3\n\n[PASS] test_domainVersionChangesOnlyAfterSevenDaysAndCanCancel() (gas: 609940)\n[PASS] test_forgedTamperedWrongDomainAndExpiredFail() (gas: 732263)\n[PASS] test_oldKeyAndOldDomainCannotSettleAfterRotation() (gas: 972422)\n[PASS] test_signedButWrongQuestionTypePanelChainTimeOrEntryFail() (gas: 1655446)\n[PASS] test_signerCancellationRequeueAndSignatures() (gas: 779858)\n[PASS] test_unsetQuestionFailsAndCanOnlyBePinnedOnce() (gas: 660674)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 53.82ms (59.72ms CPU time)\n\nRan 1 test for test/FactoryDeployment.t.sol:FactoryDeploymentTest\n[PASS] test_factoryExecutesUnchangedConstructorsAndPreservesSupply() (gas: 35883630)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 84.38ms (82.48ms CPU time)\n\nRan 21 tests for test/Machine.t.sol:MachineTest\n[PASS] testFuzz_rewardMinimumAndAllocationConserve(uint96,uint16) (runs: 256, μ: 798736, ~: 798769)\nLogs:\n  Bound result 8104\n  Bound result 7946\n\n[PASS] test_adminCapsAndNoWithdrawal() (gas: 258967)\n[PASS] test_buyTokenAndFixedRecipient() (gas: 1484307)\n[PASS] test_denyDelayClaimsAndUnpaidRecycling() (gas: 1036957)\n[PASS] test_donationsDoNotIncreaseInternalPot() (gas: 90339)\n[PASS] test_entrySponsorAndWeekBoundary() (gas: 913913)\n[PASS] test_entryValidationAndCallerOnlyTransfers() (gas: 806766)\n[PASS] test_falseReturnAndReentrancyCannotChangeAccounting() (gas: 1431299)\n[PASS] test_falseReturnWinnerTokenFallsBackToStream() (gas: 2187588)\n[PASS] test_manySkippedWeeksRollWithoutLoops() (gas: 741620)\n[PASS] test_missingPoolConfigurationAddsBuyToStream() (gas: 751384)\n[PASS] test_pauseBlocksOnlyEntries() (gas: 879225)\n[PASS] test_pendingDenyCanBeCancelledAndDeniedWinnerCannotSettle() (gas: 544858)\n[PASS] test_poolConfigCannotBeChangedAfterCloseOrByOthers() (gas: 819153)\n[PASS] test_removingActiveDenyDoesNotRestoreUnpaidStream() (gas: 814109)\n[PASS] test_rolloverWholeAndStaleRoundCannotSettle() (gas: 1099730)\n[PASS] test_roundAndRequestCannotSettleTwice() (gas: 936927)\n[PASS] test_settlementStreamingKeeperAndCarry() (gas: 1292447)\n[PASS] test_slippageFailureAddsBuyToStream() (gas: 1417370)\n[PASS] test_smallPotRewardIsOnePercentBelowFive() (gas: 677898)\n[PASS] test_winnerCooldownFourRoundsAndEarlyEntryCannotBypass() (gas: 1444013)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 115.47ms (124.33ms CPU time)\n\nRan 5 tests for test/ClaimFailure.t.sol:ClaimFailureTest\n[PASS] testFuzz_failedClaimPreservesDebtAndRetryPaysExactlyOnce(uint32) (runs: 1000, μ: 741844, ~: 730154)\nLogs:\n  Bound result 2853111\n\n[PASS] test_claimAtStartOneSecondAndExactMaturity() (gas: 438160)\n[PASS] test_transferCallbackCannotClaimSameStreamTwice() (gas: 399721)\n[PASS] test_unknownClaimAndLiveStreamRecyclingCannotChangeExistingDebt() (gas: 187614)\n[PASS] test_zeroDueAndDeniedRecyclingDoNotDependOnWorkingTokenTransfers() (gas: 380786)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 121.14ms (119.81ms CPU time)\n\nRan 12 tests for test/Revision.t.sol:RevisionTest\n[PASS] test_anyValidAttestationSettlesFirstComeFirstServed() (gas: 994388)\n[PASS] test_cappedRegistrationRejectsFeeIncreaseAndAcceptsBudget() (gas: 471880)\n[PASS] test_denyEffectiveBetweenCloseAndSubmitRollsRound() (gas: 627826)\n[PASS] test_invertedOrderingAcceptedAtManifestPrice() (gas: 483091)\n[PASS] test_nonOwnerCannotRaceSignerExecution() (gas: 817365)\n[PASS] test_nonOwnerCannotRaceVersionExecution() (gas: 762863)\n[PASS] test_onlyPayoutCanUpdateBeforeCloseAndMustReconfigureBuy() (gas: 1196996)\n[PASS] test_payoutCanRepairFrontRunRegistration() (gas: 833994)\n[PASS] test_submitInsideUnlockRevertsWithoutConsumingResult() (gas: 1575529)\n[PASS] test_submitterSandwichWithinFloor() (gas: 1707241)\nLogs:\n  HACK to winner after front-running buy: 49791227766071856375\n\n[PASS] test_updatedEntryUsesRegistrationValidation() (gas: 580438)\n[PASS] test_wrongPinnedHashCannotBeCorrected() (gas: 493709)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 125.56ms (7.11ms CPU time)\n\nRan 9 tests for test/Hook.t.sol:HookTest\n[PASS] testFuzz_fourSwapModesCollectOnlyIMD(bool,bool,uint96) (runs: 256, μ: 909493, ~: 911234)\nLogs:\n  Bound result 1000000000000000003\n\n[PASS] test_everyCallbackAuthenticatesManager() (gas: 176764)\n[PASS] test_freshManagerWithOnlyHackLiquidityCanBuy() (gas: 827775)\n[PASS] test_initializationAndPermissions() (gas: 145175)\n[PASS] test_nativeETHPairCannotInitializeOrReplaceIMDPool() (gas: 337882)\n[PASS] test_onlyFactoryCanInitializeOnlyLaunchPair() (gas: 240487)\n[PASS] test_partialSellChargesExecutedOutput() (gas: 755886)\n[PASS] test_partialSpecifiedIMDSwapRevertsWithoutTakingFee() (gas: 869287)\n[PASS] test_unknownPoolAndForgedHookDataCannotStealApprovals() (gas: 743512)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 125.64ms (128.05ms CPU time)\n\nRan 9 tests for test/Hook.t.sol:ReverseOrderHookTest\n[PASS] testFuzz_fourSwapModesCollectOnlyIMD(bool,bool,uint96) (runs: 256, μ: 908755, ~: 906971)\nLogs:\n  Bound result 999000000000000012282\n\n[PASS] test_everyCallbackAuthenticatesManager() (gas: 176080)\n[PASS] test_freshManagerWithOnlyHackLiquidityCanBuy() (gas: 838781)\n[PASS] test_initializationAndPermissions() (gas: 144893)\n[PASS] test_nativeETHPairCannotInitializeOrReplaceIMDPool() (gas: 337600)\n[PASS] test_onlyFactoryCanInitializeOnlyLaunchPair() (gas: 239467)\n[PASS] test_partialSellChargesExecutedOutput() (gas: 749983)\n[PASS] test_partialSpecifiedIMDSwapRevertsWithoutTakingFee() (gas: 868512)\n[PASS] test_unknownPoolAndForgedHookDataCannotStealApprovals() (gas: 748113)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 125.63ms (111.35ms CPU time)\n\nRan 7 tests for test/PrizeBuyAdversarial.t.sol:PrizeBuyAdversarialTest\n[PASS] test_emptyPoolFallsBackWithoutSpendingAnyIMD() (gas: 1308935)\n[PASS] test_impossibleOutputFloorRollsBackTheSwapAndItsTransfers() (gas: 1360854)\n[PASS] test_invalidBuyConfigurationsCannotReplaceTheEntrantsCommitment() (gas: 976661)\n[PASS] test_liquidityRemovedAfterConfigurationStillAllowsSettlement() (gas: 1646714)\n[PASS] test_partialBuyRevertsAllPoolEffectsBeforeFallingBack() (gas: 2239172)\n[PASS] test_prizeTokenCallbackCannotReenterOrClaimDuringSettlement() (gas: 2385453)\n[PASS] test_successfulExternalPoolBuySpendsOwnFundsAndPaysOnlyWinner() (gas: 1429129)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 125.68ms (8.80ms CPU time)\n\nRan 14 tests for test/AdversarialMachine.t.sol:AdversarialMachineTest\n[PASS] testFuzz_claimCadenceCannotCreateOrLosePrize(uint96,uint32,uint32) (runs: 1000, μ: 1114128, ~: 1116436)\nLogs:\n  Bound result 263251972275856686527580\n  Bound result 839079\n  Bound result 1248753\n\n[PASS] testFuzz_everySignedFieldIsAuthenticated(uint8) (runs: 1000, μ: 952533, ~: 956275)\nLogs:\n  Bound result 7\n\n[PASS] test_adminRejectsInvalidOracleAndDenyConfiguration() (gas: 276028)\n[PASS] test_denyRevokesOnlyOldStreamsAndCannotReviveThemAfterRemoval() (gas: 1769859)\n[PASS] test_everyOwnerOperationRefusesAnUnrelatedCaller() (gas: 378583)\n[PASS] test_expiryBoundaryAndFutureTimestampTolerance() (gas: 1010864)\n[PASS] test_failedEntryAndSponsorTransfersRollBackRoundAdvance() (gas: 944897)\n[PASS] test_failedFeeRedemptionPreservesClaimsAndCanBeRetried() (gas: 1109172)\n[PASS] test_failedKeeperPaymentDoesNotConsumeRequestOrSettleRound() (gas: 1200246)\n[PASS] test_invalidEntriesAndZeroFundingLeaveNoGhostState() (gas: 292892)\n[PASS] test_keeperRewardEdgesConserveEvenWhenTheMinimumConsumesThePot() (gas: 5135727)\n[PASS] test_maximumMetadataIsMeasuredInBytesAndEntryPullsOnlyFromCaller() (gas: 668381)\n[PASS] test_signatureCannotCrossConsumersEvenWithIdenticalEntriesAndQuestion() (gas: 1267296)\n[PASS] test_signedInvalidBlockWindowAndOversizePanelAreRejectedAtomically() (gas: 1060332)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 125.88ms (243.93ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:MachineInvariantTest\n[PASS]\nMachineInvariantTest invariants:\n[PASS] invariant_assetsEqualPotsAndUnpaidStreams\n[PASS] invariant_eachRoundSettlesAtMostOnceAndToItsWinner\n[PASS] invariant_forgedOrStaleAttestationsNeverSettle\n[PASS] invariant_onlyWinnersPoolsKeepersAndCarryReceivePot\n[PASS] invariant_queuedSignerHasNoEffectUntilExecutionAfterDelay\n MachineInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭----------------+--------------------------+-------+---------+----------╮\n| Contract       | Selector                 | Calls | Reverts | Discards |\n+========================================================================+\n| MachineHandler | advance                  | 779   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | claim                    | 843   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | executeSigner            | 808   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | fund                     | 860   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | queueSigner              | 852   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | redeem                   | 795   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | register                 | 834   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | rejectInvalidAttestation | 833   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | settle                   | 769   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | trade                    | 819   | 0       | 0        |\n╰----------------+--------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 9607\n  Bound result 9000000000000004187\n  Bound result 22901\n  Bound result 14097\n  Bound result 1024\n  Bound result 18\n  Bound result 41866593122510136\n  Bound result 9000000000000001558\n  Bound result 3016\n  Bound result 400848\n  Bound result 468512\n  Bound result 12670\n  Bound result 9000000000000004234\n  Bound result 10921\n  Bound result 2204\n  Bound result 6492975655154201\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.41s (1.41s CPU time)\n\nRan 3 tests for test/TokenProperties.t.sol:TokenPropertiesTest\n[PASS] invariant_fixedSupplyBalancesAndAllowancesFollowERC20Operations() (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------------+--------------+-------+---------+----------╮\n| Contract             | Selector     | Calls | Reverts | Discards |\n+==================================================================+\n| TokenTransferHandler | approve      | 4070  | 0       | 0        |\n|----------------------+--------------+-------+---------+----------|\n| TokenTransferHandler | overdraft    | 4164  | 0       | 0        |\n|----------------------+--------------+-------+---------+----------|\n| TokenTransferHandler | transfer     | 4062  | 0       | 0        |\n|----------------------+--------------+-------+---------+----------|\n| TokenTransferHandler | transferFrom | 4088  | 0       | 0        |\n╰----------------------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 1183\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 27\n  Bound result 649\n  Bound result 0\n  Bound result 0\n  Bound result 355\n  Bound result 0\n  Bound result 0\n  Bound result 3470\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 276\n  Bound result 0\n  Bound result 2626\n  Bound result 0\n  Bound result 506\n  Bound result 147\n  Bound result 0\n  Bound result 774\n  Bound result 0\n  Bound result 3886\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 0\n  Bound result 0\n\n[PASS] test_transferFailureRestoresTheAllowanceItWouldHaveSpent() (gas: 135850)\n[PASS] test_zeroTransferSelfTransferAndInfiniteApproval() (gas: 240814)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.30s (2.30s CPU time)\n\nRan 4 tests for test/CustodyInvariant.t.sol:CustodyInvariantTest\n[PASS]\nCustodyInvariantTest invariants:\n[PASS] invariant_domainVersionChangesOnlyOnAnExecutedMatureQueue\n[PASS] invariant_eachSettlementAndStreamRetainsItsOriginalRecipientAndLiability\n[PASS] invariant_internalAccountingExcludesUnsolicitedDonationsAndMatchesRealAssets\n[PASS] invariant_receivedFundsEqualAllRemainingObligationsAndActualPermittedOutflows\n CustodyInvariantTest invariants (runs: 256, calls: 20480, reverts: 0)\n\n╭----------------+---------------+-------+---------+----------╮\n| Contract       | Selector      | Calls | Reverts | Discards |\n+=============================================================+\n| CustodyHandler | advance       | 1402  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | claim         | 1449  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | configure     | 1484  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | deny          | 1483  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | donate        | 1471  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | failedClaim   | 1442  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | fund          | 1470  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | recycle       | 1457  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | redeem        | 1433  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | registerEntry | 1453  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | rejectResult  | 1561  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | rotateVersion | 1443  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | settle        | 1498  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | trade         | 1434  | 0       | 0        |\n╰----------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 990000000000000000000\n  Bound result 604800\n  Bound result 604800\n  Bound result 10\n  Bound result 5013\n  Bound result 1055239860\n  Bound result 5130\n  Bound result 8302\n  Bound result 43558686302652\n  Bound result 8381\n  Bound result 9387\n  Bound result 65534\n  Bound result 2926\n  Bound result 22399\n  Bound result 4090335564\n  Bound result 606819708534369914926\n  Bound result 127\n  Bound result 8801\n  Bound result 11530\n  Bound result 4264337981761946634\n  Bound result 6754825645\n  Bound result 502063099171439\n  Bound result 50960812170793582233\n  Bound result 4015\n  Bound result 19840\n  Bound result 15456\n  Bound result 2975529\n  Bound result 20084\n  Bound result 14269\n  Bound result 101\n  Bound result 14592\n  Bound result 110222020629\n  Bound result 3000\n  Bound result 64\n  Bound result 512\n  Bound result 20331\n  Bound result 1380\n\n[PASS] test_handlerChecksVestingAndFailedClaimsAcrossMultipleStreams() (gas: 1788150)\nLogs:\n  Bound result 990000000000000000000\n  Bound result 604800\n  Bound result 604800\n  Bound result 86400\n  Bound result 172800\n  Bound result 86400\n\n[PASS] test_handlerExercisesDenyPauseDonationsAndVersionExecution() (gas: 1680423)\nLogs:\n  Bound result 990000000000000000000\n  Bound result 604800\n  Bound result 604800\n  Bound result 100000000000000000000\n  Bound result 490\n  Bound result 172800\n  Bound result 604800\n\n[PASS] test_handlerVersionRequeueCancellationAndExactDelay() (gas: 1189014)\nLogs:\n  Bound result 990000000000000000000\n  Bound result 604800\n  Bound result 604800\n  Bound result 518400\n  Bound result 86400\n  Bound result 604800\n  Bound result 604799\n  Bound result 1\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 5.79s (5.79s CPU time)\n\nRan 18 test suites in 5.79s (10.64s CPU time): 116 tests passed, 0 failed, 0 skipped (116 total tests)\n","passed":true},{"durationMs":60,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"HackToken.approve(address,uint256)\",\"HackToken.transfer(address,uint256)\",\"HackToken.transferFrom(address,address,uint256)\",\"HackathonMachine.advanceRound()\",\"HackathonMachine.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"HackathonMachine.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"HackathonMachine.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"HackathonMachine.buyWinnerToken(uint256,uint256)\",\"HackathonMachine.cancelDomainVersion()\",\"HackathonMachine.cancelSigner()\",\"HackathonMachine.claim(uint256)\",\"HackathonMachine.configureBuy(uint256,(address,address,uint24,int24,address),uint256)\",\"HackathonMachine.executeDomainVersion()\",\"HackathonMachine.executeSigner()\",\"HackathonMachine.fundRound(uint256)\",\"HackathonMachine.pauseEntries(bool)\",\"HackathonMachine.queueDeny(address)\",\"HackathonMachine.queueDomainVersion(string)\",\"HackathonMachine.queueSigner(address)\",\"HackathonMachine.recycleDeniedStream(uint256)\",\"HackathonMachine.redeemFeeClaims()\",\"HackathonMachine.register(string,string,string,address,address)\",\"HackathonMachine.registerWithMaxFee(string,string,string,address,address,uint256)\",\"HackathonMachine.removeDeny(address)\",\"HackathonMachine.setEntryFee(uint256)\",\"HackathonMachine.setPanelFloors(uint16,uint16)\",\"HackathonMachine.setQuestionHash(bytes32)\",\"HackathonMachine.setWinnerBps(uint16)\",\"HackathonMachine.submitResult((bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HackathonMachine.unlockCallback(bytes)\",\"HackathonMachine.updateEntry(uint256,string,string,string,address)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":131,\"LICENSE\":25,\"README.md\":130,\"docs/dependencies.json\":26,\"docs/heartbeat.json\":35,\"docs/launch-request.json\":8,\"docs/policy-snapshot.json\":44,\"docs/schedule.json\":10,\"docs/security-review.md\":38,\"foundry.toml\":21,\"launch.json\":34,\"remappings.txt\":4,\"src/HackToken.sol\":11,\"src/HackathonMachine.sol\":709,\"src/HookFlags.sol\":28,\"src/LaunchToken.sol\":10,\"src/OracleAttestation.sol\":221,\"test/AdversarialMachine.t.sol\":368,\"test/Attestation.t.sol\":182,\"test/ClaimFailure.t.sol\":124,\"test/ContractSigner.t.sol\":180,\"test/CustodyInvariant.t.sol\":548,\"test/FactoryDeployment.t.sol\":82,\"test/Hook.t.sol\":207,\"test/ImportedAudit.t.sol\":315,\"test/Invariant.t.sol\":264,\"test/LaunchPurchase.t.sol\":115,\"test/LaunchToken.t.sol\":38,\"test/Machine.t.sol\":373,\"test/MachineFixture.sol\":138,\"test/OracleConformance.t.sol\":120,\"test/PrizeBuyAdversarial.t.sol\":196,\"test/Revision.t.sol\":270,\"test/TESTING.md\":42,\"test/Token.t.sol\":31,\"test/TokenProperties.t.sol\":147,\"test/mocks/MockERC20.sol\":46,\"tools/render_heartbeat.py\":42,\"tools/validate.py\":61},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"51d68709da66a1c9776674422921c6f1c6cdfb021970a410c9ddde692c079d21","verifiedTreeHash":"57a0950cef7a69b9f05bd80fa817a367ef35a8fa","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":83362,"exitCode":0,"name":"build","output":"Compiling 118 files with Solc 0.8.26\nSolc 0.8.26 finished in 83.16s\nCompiler run successful!\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/HackathonMachine.sol:194:9\n    │\n194 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/HackathonMachine.sol:219:73\n    │\n219 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(_asInt128(fee), 0), 0);\n    │                                                                         ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/HackathonMachine.sol:210:9\n    │\n210 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/HackathonMachine.sol:225:9\n    │\n225 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:233:56\n    │\n233 │             int256 expected = params.amountSpecified + int256(_specifiedFee(params.amountSpecified));\n    │                                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/HackathonMachine.sol:237:41\n    │\n237 │         uint256 amount = imdDelta < 0 ? uint256(-int256(imdDelta)) : uint256(int256(imdDelta));\n    │                                         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:237:70\n    │\n237 │         uint256 amount = imdDelta < 0 ? uint256(-int256(imdDelta)) : uint256(int256(imdDelta));\n    │                                                                      ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:245:26\n    │\n245 │         uint256 amount = uint256(specified < 0 ? -specified : specified);\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:246:22\n    │\n246 │         if (amount > uint256(uint128(type(int128).max))) revert InvalidInput();\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:246:30\n    │\n246 │         if (amount > uint256(uint128(type(int128).max))) revert InvalidInput();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/HackathonMachine.sol:266:9\n    │\n266 │         emit FeeCollected(accountingRound, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/HackathonMachine.sol:270:16\n    │\n270 │         return block.timestamp < MONDAY_EPOCH ? 0 : uint64((block.timestamp - MONDAY_EPOCH) / WEEK);\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:270:53\n    │\n270 │         return block.timestamp < MONDAY_EPOCH ? 0 : uint64((block.timestamp - MONDAY_EPOCH) / WEEK);\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[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/HackathonMachine.sol:459:33\n    │\n459 │         streamed += buyAmount - bought;\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/HackathonMachine.sol:456:17\n    │\n456 │                 emit BuySkipped(id);\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/HackathonMachine.sol:462:9\n    │\n462 │         emit ResultSettled(round, id, a.requestId, msg.sender, reward, streamed, bought, carried);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:460:40\n    │\n460 │         streams[id] = Stream(e.payout, uint64(block.timestamp), denyGeneration[e.payout], streamed, 0);\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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/HackathonMachine.sol:497:13\n    │\n497 │             emit ClaimsRedeemed(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/HackathonMachine.sol:525:13\n    │\n525 │             emit FeeCollected(accountingRound, ownFee);\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/HackathonMachine.sol:532:9\n    │\n532 │         emit TokenBought(id, spend, uint256(uint128(output)));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:518:66\n    │\n518 │             poolManager.swap(key, SwapParams(zeroForOne, -int256(uint256(uint128(_asInt128(spend)))), limit), \"\");\n    │                                                                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:518:74\n    │\n518 │             poolManager.swap(key, SwapParams(zeroForOne, -int256(uint256(uint128(_asInt128(spend)))), limit), \"\");\n    │                                                                          ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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/HackathonMachine.sol:521:31\n    │\n521 │         if (int256(input) != -int256(spend) || output <= 0 || uint256(uint128(output)) < minimum) revert Slippage();\n    │                               ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/HackathonMachine.sol:521:63\n    │\n521 │         if (int256(input) != -int256(spend) || output <= 0 || uint256(uint128(output)) < minimum) revert Slippage();\n    │                                                               ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:521:71\n    │\n521 │         if (int256(input) != -int256(spend) || output <= 0 || uint256(uint128(output)) < minimum) revert Slippage();\n    │                                                                       ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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/HackathonMachine.sol:531:60\n    │\n531 │         poolManager.take(Currency.wrap(e.token), e.payout, uint256(uint128(output)));\n    │                                                            ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:531:68\n    │\n531 │         poolManager.take(Currency.wrap(e.token), e.payout, uint256(uint128(output)));\n    │                                                                    ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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/HackathonMachine.sol:532:37\n    │\n532 │         emit TokenBought(id, spend, uint256(uint128(output)));\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:532:45\n    │\n532 │         emit TokenBought(id, spend, uint256(uint128(output)));\n    │                                             ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/HackathonMachine.sol:543:26\n    │\n543 │         uint256 vested = elapsed >= STREAM_DURATION ? s.total : FullMath.mulDiv(s.total, elapsed, STREAM_DURATION);\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/HackathonMachine.sol:548:9\n    │\n548 │         emit Claimed(id, s.payout, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:577:22\n    │\n577 │         if (amount > uint256(uint128(type(int128).max))) revert InvalidInput();\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/HackathonMachine.sol:577:30\n    │\n577 │         if (amount > uint256(uint128(type(int128).max))) revert InvalidInput();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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/HackathonMachine.sol:578:16\n    │\n578 │         return int128(uint128(amount));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' 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/HackathonMachine.sol:578:23\n    │\n578 │         return int128(uint128(amount));\n    │                       ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' 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/HackathonMachine.sol:620:25\n    │\n620 │         signerReadyAt = uint64(block.timestamp + ORACLE_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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/HackathonMachine.sol:632:13\n    │\n632 │         if (block.timestamp < signerReadyAt) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:642:26\n    │\n642 │         versionReadyAt = uint64(block.timestamp + ORACLE_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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/HackathonMachine.sol:654:13\n    │\n654 │         if (block.timestamp < versionReadyAt) revert TooEarly();\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/HackathonMachine.sol:693:39\n    │\n693 │         return denyAt[payout] != 0 && block.timestamp >= denyAt[payout];\n    │                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/HackathonMachine.sol:698:26\n    │\n698 │         denyAt[payout] = uint64(block.timestamp + DENY_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[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.revertToState`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/AdversarialMachine.t.sol:254:20\n    │\n254 │         assertTrue(vm.revertToState(snapshot));\n    │                    ────────────────────────── `vm.revertToState` 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.revertToState`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/AdversarialMachine.t.sol:254:20\n    │\n254 │         assertTrue(vm.revertToState(snapshot));\n    │                    ────────────────────────── `vm.revertToState` 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/AdversarialMachine.t.sol:302:13\n    │\n302 │             vm.warp(start + 28 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/AdversarialMachine.t.sol:302:13\n    │\n302 │             vm.warp(start + 28 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/AdversarialMachine.t.sol:341:9\n    │\n341 │         vm.warp(vm.getBlockTimestamp() + 7 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/AdversarialMachine.t.sol:341:9\n    │\n341 │         vm.warp(vm.getBlockTimestamp() + 7 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Attestation.t.sol:87:26\n   │\n87 │         uint256 queued = block.timestamp;\n   │                          ━━━━━━━━━━━━━━━\n   ‡\n90 │         vm.warp(queued + elapsed);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Attestation.t.sol:114:9\n    │\n114 │         vm.warp(block.timestamp + 7 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Attestation.t.sol:114:9\n    │\n114 │         vm.warp(block.timestamp + 7 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Attestation.t.sol:114:17\n    │\n114 │         vm.warp(block.timestamp + 7 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Attestation.t.sol:120:17\n    │\n120 │         vm.warp(block.timestamp + 6 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Attestation.t.sol:135:9\n    │\n135 │         vm.warp(block.timestamp + 7 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Attestation.t.sol:135:9\n    │\n135 │         vm.warp(block.timestamp + 7 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Attestation.t.sol:135:17\n    │\n135 │         vm.warp(block.timestamp + 7 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Attestation.t.sol:141:17\n    │\n141 │         vm.warp(block.timestamp + 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Attestation.t.sol:163:17\n    │\n163 │         vm.warp(block.timestamp + 7 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n 98 │         vm.warp(machine.roundClose(machine.currentRound()) + 1 hours);\n    │         ───────────────────────────────────────────────────────────── `vm.warp` changes this environment here\n    ‡\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n 98 │         vm.warp(machine.roundClose(machine.currentRound()) + 1 hours);\n    │         ───────────────────────────────────────────────────────────── `vm.warp` changes this environment here\n    ‡\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Machine.t.sol:231:17\n    │\n231 │         vm.warp(block.timestamp + 2 days);\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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:116:30\n    │\n116 │             issuedAt: uint64(block.timestamp),\n    │                              ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Revision.t.sol:22:9\n    │\n 22 │         vm.warp(block.timestamp + 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/MachineFixture.sol:117:31\n    │\n117 │             expiresAt: uint64(block.timestamp + 6 days)\n    │                               ━━━━━━━━━━━━━━━\n    │\n    ⸬  test/Revision.t.sol:22:9\n    │\n 22 │         vm.warp(block.timestamp + 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\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Revision.t.sol:22:17\n   │\n22 │         vm.warp(block.timestamp + 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":5063,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Token.t.sol:TokenTest\n[PASS] test_fixedSupplyMetadataAndTransfer() (gas: 234376)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 494.91µs (365.77µs CPU time)\n\nRan 2 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] test_factoryReceivesFixedSupplyAndTransfersExactAmount() (gas: 305379)\n[PASS] test_runtimeMatchesExistingManifestToken() (gas: 6854)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 496.66µs (629.70µs CPU time)\n\nRan 1 test for test/LaunchPurchase.t.sol:LaunchPurchaseTest\n[PASS] test_winnerTokenPurchaseExecutesForOrdinaryPot() (gas: 1005203)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.03ms (576.24µs CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] test_acceptsExactProtocolSignature() (gas: 23346)\n[PASS] test_digestMatchesTheProtocol() (gas: 16335)\n[PASS] test_freshVectorKeySignatureSettlesBytes32Entry() (gas: 649612)\n[PASS] test_submitSelectorUsesAllFifteenCanonicalFields() (gas: 3450)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 4.17ms (1.57ms CPU time)\n\nRan 7 tests for test/Attestation.t.sol:AttestationTest\n[PASS] testFuzz_queuedSignerHasNoEffectBeforeSevenDays(uint32) (runs: 256, μ: 192134, ~: 192058)\nLogs:\n  Bound result 63\n\n[PASS] test_domainVersionChangesOnlyAfterSevenDaysAndCanCancel() (gas: 609940)\n[PASS] test_forgedTamperedWrongDomainAndExpiredFail() (gas: 732263)\n[PASS] test_oldKeyAndOldDomainCannotSettleAfterRotation() (gas: 972422)\n[PASS] test_signedButWrongQuestionTypePanelChainTimeOrEntryFail() (gas: 1655446)\n[PASS] test_signerCancellationRequeueAndSignatures() (gas: 779858)\n[PASS] test_unsetQuestionFailsAndCanOnlyBePinnedOnce() (gas: 660674)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 18.56ms (22.05ms CPU time)\n\nRan 9 tests for test/ImportedAudit.t.sol:ImportedAuditTest\n[PASS] test_effectiveDenyRecyclesVestedButUnclaimedAmounts() (gas: 958675)\n[PASS] test_gasExhaustingPrizeTokenFallsBack() (gas: 2519808)\n[PASS] test_gasExhaustingPurchaseHookFallsBack() (gas: 2932290)\n[PASS] test_ownerArgumentAloneControlsExclusionAndQuestionPin() (gas: 1387525)\n[PASS] test_purchaseHookCannotReenterAfterSwap() (gas: 2640446)\n[PASS] test_purchaseHookCannotReenterBeforeSwap() (gas: 2640324)\n[PASS] test_purchaseHookCannotStealIMDAfterSwap() (gas: 2660816)\n[PASS] test_purchaseHookCannotStealIMDBeforeSwap() (gas: 2660666)\n[PASS] test_purchaseHookCannotSwapOnLaunchPool() (gas: 3041569)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 80.28ms (16.75ms CPU time)\n\nRan 1 test for test/FactoryDeployment.t.sol:FactoryDeploymentTest\n[PASS] test_factoryExecutesUnchangedConstructorsAndPreservesSupply() (gas: 35883630)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 85.88ms (84.61ms CPU time)\n\nRan 8 tests for test/Hook.t.sol:HookTest\n[PASS] testFuzz_fourSwapModesCollectOnlyIMD(bool,bool,uint96) (runs: 256, μ: 909571, ~: 911504)\nLogs:\n  Bound result 999278315616700823565\n\n[PASS] test_everyCallbackAuthenticatesManager() (gas: 176764)\n[PASS] test_freshManagerWithOnlyHackLiquidityCanBuy() (gas: 827775)\n[PASS] test_initializationAndPermissions() (gas: 145175)\n[PASS] test_onlyFactoryCanInitializeOnlyLaunchPair() (gas: 240465)\n[PASS] test_partialSellChargesExecutedOutput() (gas: 755864)\n[PASS] test_partialSpecifiedIMDSwapRevertsWithoutTakingFee() (gas: 869287)\n[PASS] test_unknownPoolAndForgedHookDataCannotStealApprovals() (gas: 743490)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 90.70ms (92.09ms CPU time)\n\nRan 7 tests for test/PrizeBuyAdversarial.t.sol:PrizeBuyAdversarialTest\n[PASS] test_emptyPoolFallsBackWithoutSpendingAnyIMD() (gas: 1308935)\n[PASS] test_impossibleOutputFloorRollsBackTheSwapAndItsTransfers() (gas: 1360854)\n[PASS] test_invalidBuyConfigurationsCannotReplaceTheEntrantsCommitment() (gas: 976661)\n[PASS] test_liquidityRemovedAfterConfigurationStillAllowsSettlement() (gas: 1646714)\n[PASS] test_partialBuyRevertsAllPoolEffectsBeforeFallingBack() (gas: 2239172)\n[PASS] test_prizeTokenCallbackCannotReenterOrClaimDuringSettlement() (gas: 2385453)\n[PASS] test_successfulExternalPoolBuySpendsOwnFundsAndPaysOnlyWinner() (gas: 1429129)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 95.07ms (8.58ms CPU time)\n\nRan 21 tests for test/Machine.t.sol:MachineTest\n[PASS] testFuzz_rewardMinimumAndAllocationConserve(uint96,uint16) (runs: 256, μ: 798774, ~: 798769)\nLogs:\n  Bound result 8007\n  Bound result 1263648860\n\n[PASS] test_adminCapsAndNoWithdrawal() (gas: 258967)\n[PASS] test_buyTokenAndFixedRecipient() (gas: 1484307)\n[PASS] test_denyDelayClaimsAndUnpaidRecycling() (gas: 1036957)\n[PASS] test_donationsDoNotIncreaseInternalPot() (gas: 90339)\n[PASS] test_entrySponsorAndWeekBoundary() (gas: 913913)\n[PASS] test_entryValidationAndCallerOnlyTransfers() (gas: 806766)\n[PASS] test_falseReturnAndReentrancyCannotChangeAccounting() (gas: 1431299)\n[PASS] test_falseReturnWinnerTokenFallsBackToStream() (gas: 2187588)\n[PASS] test_manySkippedWeeksRollWithoutLoops() (gas: 741620)\n[PASS] test_missingPoolConfigurationAddsBuyToStream() (gas: 751384)\n[PASS] test_pauseBlocksOnlyEntries() (gas: 879225)\n[PASS] test_pendingDenyCanBeCancelledAndDeniedWinnerCannotSettle() (gas: 544858)\n[PASS] test_poolConfigCannotBeChangedAfterCloseOrByOthers() (gas: 819153)\n[PASS] test_removingActiveDenyDoesNotRestoreUnpaidStream() (gas: 814109)\n[PASS] test_rolloverWholeAndStaleRoundCannotSettle() (gas: 1099730)\n[PASS] test_roundAndRequestCannotSettleTwice() (gas: 936927)\n[PASS] test_settlementStreamingKeeperAndCarry() (gas: 1292447)\n[PASS] test_slippageFailureAddsBuyToStream() (gas: 1417370)\n[PASS] test_smallPotRewardIsOnePercentBelowFive() (gas: 677898)\n[PASS] test_winnerCooldownFourRoundsAndEarlyEntryCannotBypass() (gas: 1444013)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 95.23ms (75.32ms CPU time)\n\nRan 8 tests for test/Hook.t.sol:ReverseOrderHookTest\n[PASS] testFuzz_fourSwapModesCollectOnlyIMD(bool,bool,uint96) (runs: 256, μ: 908560, ~: 906951)\nLogs:\n  Bound result 999000000002563714305\n\n[PASS] test_everyCallbackAuthenticatesManager() (gas: 176080)\n[PASS] test_freshManagerWithOnlyHackLiquidityCanBuy() (gas: 838781)\n[PASS] test_initializationAndPermissions() (gas: 144893)\n[PASS] test_onlyFactoryCanInitializeOnlyLaunchPair() (gas: 239445)\n[PASS] test_partialSellChargesExecutedOutput() (gas: 749961)\n[PASS] test_partialSpecifiedIMDSwapRevertsWithoutTakingFee() (gas: 868512)\n[PASS] test_unknownPoolAndForgedHookDataCannotStealApprovals() (gas: 748091)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 98.16ms (98.94ms CPU time)\n\nRan 12 tests for test/Revision.t.sol:RevisionTest\n[PASS] test_anyValidAttestationSettlesFirstComeFirstServed() (gas: 994388)\n[PASS] test_cappedRegistrationRejectsFeeIncreaseAndAcceptsBudget() (gas: 471880)\n[PASS] test_denyEffectiveBetweenCloseAndSubmitRollsRound() (gas: 627826)\n[PASS] test_invertedOrderingAcceptedAtManifestPrice() (gas: 483091)\n[PASS] test_nonOwnerCannotRaceSignerExecution() (gas: 817365)\n[PASS] test_nonOwnerCannotRaceVersionExecution() (gas: 762863)\n[PASS] test_onlyPayoutCanUpdateBeforeCloseAndMustReconfigureBuy() (gas: 1196996)\n[PASS] test_payoutCanRepairFrontRunRegistration() (gas: 833994)\n[PASS] test_submitInsideUnlockRevertsWithoutConsumingResult() (gas: 1575529)\n[PASS] test_submitterSandwichWithinFloor() (gas: 1707241)\nLogs:\n  HACK to winner after front-running buy: 49791227766071856375\n\n[PASS] test_updatedEntryUsesRegistrationValidation() (gas: 580438)\n[PASS] test_wrongPinnedHashCannotBeCorrected() (gas: 493709)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 98.49ms (9.00ms CPU time)\n\nRan 14 tests for test/AdversarialMachine.t.sol:AdversarialMachineTest\n[PASS] testFuzz_claimCadenceCannotCreateOrLosePrize(uint96,uint32,uint32) (runs: 1000, μ: 1113687, ~: 1116436)\nLogs:\n  Bound result 8051\n  Bound result 2\n  Bound result 240\n\n[PASS] testFuzz_everySignedFieldIsAuthenticated(uint8) (runs: 1000, μ: 952170, ~: 956275)\nLogs:\n  Bound result 8\n\n[PASS] test_adminRejectsInvalidOracleAndDenyConfiguration() (gas: 276028)\n[PASS] test_denyRevokesOnlyOldStreamsAndCannotReviveThemAfterRemoval() (gas: 1769859)\n[PASS] test_everyOwnerOperationRefusesAnUnrelatedCaller() (gas: 378583)\n[PASS] test_expiryBoundaryAndFutureTimestampTolerance() (gas: 1010864)\n[PASS] test_failedEntryAndSponsorTransfersRollBackRoundAdvance() (gas: 944897)\n[PASS] test_failedFeeRedemptionPreservesClaimsAndCanBeRetried() (gas: 1109172)\n[PASS] test_failedKeeperPaymentDoesNotConsumeRequestOrSettleRound() (gas: 1200246)\n[PASS] test_invalidEntriesAndZeroFundingLeaveNoGhostState() (gas: 292892)\n[PASS] test_keeperRewardEdgesConserveEvenWhenTheMinimumConsumesThePot() (gas: 5135727)\n[PASS] test_maximumMetadataIsMeasuredInBytesAndEntryPullsOnlyFromCaller() (gas: 668381)\n[PASS] test_signatureCannotCrossConsumersEvenWithIdenticalEntriesAndQuestion() (gas: 1267296)\n[PASS] test_signedInvalidBlockWindowAndOversizePanelAreRejectedAtomically() (gas: 1060332)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 98.62ms (201.15ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:MachineInvariantTest\n[PASS]\nMachineInvariantTest invariants:\n[PASS] invariant_assetsEqualPotsAndUnpaidStreams\n[PASS] invariant_eachRoundSettlesAtMostOnceAndToItsWinner\n[PASS] invariant_forgedOrStaleAttestationsNeverSettle\n[PASS] invariant_onlyWinnersPoolsKeepersAndCarryReceivePot\n[PASS] invariant_queuedSignerHasNoEffectUntilExecutionAfterDelay\n MachineInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭----------------+--------------------------+-------+---------+----------╮\n| Contract       | Selector                 | Calls | Reverts | Discards |\n+========================================================================+\n| MachineHandler | advance                  | 804   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | claim                    | 796   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | executeSigner            | 844   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | fund                     | 772   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | queueSigner              | 873   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | redeem                   | 859   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | register                 | 754   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | rejectInvalidAttestation | 788   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | settle                   | 836   | 0       | 0        |\n|----------------+--------------------------+-------+---------+----------|\n| MachineHandler | trade                    | 866   | 0       | 0        |\n╰----------------+--------------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 9000000000001030954\n  Bound result 9000000001517015002\n  Bound result 123456\n  Bound result 3201303218\n  Bound result 237\n  Bound result 9000000000000015063\n  Bound result 5790\n  Bound result 48\n  Bound result 16384\n  Bound result 658618\n  Bound result 9000000000000001526\n  Bound result 9000000000000019774\n  Bound result 1363486403\n  Bound result 9000000000000020872\n  Bound result 514264337593543950333\n  Bound result 8887421013046764604\n  Bound result 12305\n  Bound result 1022583892\n  Bound result 103872\n  Bound result 10923\n  Bound result 24576\n  Bound result 8000\n  Bound result 12500\n  Bound result 420\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.36s (1.36s CPU time)\n\nRan 3 tests for test/TokenProperties.t.sol:TokenPropertiesTest\n[PASS] invariant_fixedSupplyBalancesAndAllowancesFollowERC20Operations() (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------------+--------------+-------+---------+----------╮\n| Contract             | Selector     | Calls | Reverts | Discards |\n+==================================================================+\n| TokenTransferHandler | approve      | 4091  | 0       | 0        |\n|----------------------+--------------+-------+---------+----------|\n| TokenTransferHandler | overdraft    | 4095  | 0       | 0        |\n|----------------------+--------------+-------+---------+----------|\n| TokenTransferHandler | transfer     | 4072  | 0       | 0        |\n|----------------------+--------------+-------+---------+----------|\n| TokenTransferHandler | transferFrom | 4126  | 0       | 0        |\n╰----------------------+--------------+-------+---------+----------╯\n\nLogs:\n  Bound result 328\n  Bound result 202\n  Bound result 2002\n  Bound result 0\n  Bound result 0\n  Bound result 190968\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 758\n  Bound result 493\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 7254\n  Bound result 1405\n  Bound result 26\n  Bound result 47891\n  Bound result 0\n  Bound result 2249\n  Bound result 1533\n  Bound result 314\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 5166\n  Bound result 228\n  Bound result 1059\n  Bound result 1080\n  Bound result 52990\n  Bound result 4300\n  Bound result 6358\n  Bound result 0\n  Bound result 8356\n  Bound result 5073\n  Bound result 453\n  Bound result 999999999999999999999848498\n  Bound result 4857\n  Bound result 0\n\n[PASS] test_transferFailureRestoresTheAllowanceItWouldHaveSpent() (gas: 135850)\n[PASS] test_zeroTransferSelfTransferAndInfiniteApproval() (gas: 240814)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.21s (2.21s CPU time)\n\nRan 3 tests for test/CustodyInvariant.t.sol:CustodyInvariantTest\n[PASS]\nCustodyInvariantTest invariants:\n[PASS] invariant_domainVersionChangesOnlyOnAnExecutedMatureQueue\n[PASS] invariant_eachSettlementAndStreamRetainsItsOriginalRecipientAndLiability\n[PASS] invariant_internalAccountingExcludesUnsolicitedDonationsAndMatchesRealAssets\n[PASS] invariant_receivedFundsEqualAllRemainingObligationsAndActualPermittedOutflows\n CustodyInvariantTest invariants (runs: 256, calls: 20480, reverts: 0)\n\n╭----------------+---------------+-------+---------+----------╮\n| Contract       | Selector      | Calls | Reverts | Discards |\n+=============================================================+\n| CustodyHandler | advance       | 1552  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | claim         | 1559  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | configure     | 1568  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | deny          | 1575  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | donate        | 1527  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | fund          | 1630  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | recycle       | 1593  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | redeem        | 1631  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | registerEntry | 1607  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | rejectResult  | 1536  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | rotateVersion | 1493  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | settle        | 1596  | 0       | 0        |\n|----------------+---------------+-------+---------+----------|\n| CustodyHandler | trade         | 1613  | 0       | 0        |\n╰----------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 990000000000000000000\n  Bound result 604800\n  Bound result 604800\n  Bound result 17\n  Bound result 8791\n  Bound result 44\n  Bound result 19644939068787388474\n  Bound result 8097\n  Bound result 100\n  Bound result 17914\n  Bound result 2167\n  Bound result 113549766\n  Bound result 1075469243\n  Bound result 1359\n  Bound result 1103294676404703240\n  Bound result 13080\n  Bound result 3169\n  Bound result 7695\n  Bound result 20093\n  Bound result 12244\n  Bound result 14076\n  Bound result 5946\n  Bound result 5276\n  Bound result 2034284841709\n  Bound result 100\n  Bound result 22766\n\n[PASS] test_handlerExercisesDenyPauseDonationsAndVersionExecution() (gas: 1617267)\nLogs:\n  Bound result 990000000000000000000\n  Bound result 604800\n  Bound result 604800\n  Bound result 100000000000000000000\n  Bound result 490\n  Bound result 172800\n  Bound result 604800\n\n[PASS] test_handlerVersionRequeueCancellationAndExactDelay() (gas: 1187492)\nLogs:\n  Bound result 990000000000000000000\n  Bound result 604800\n  Bound result 604800\n  Bound result 518400\n  Bound result 86400\n  Bound result 604800\n  Bound result 604799\n  Bound result 1\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 4.97s (4.97s CPU time)\n\nRan 16 test suites in 4.97s (9.31s CPU time): 102 tests passed, 0 failed, 0 skipped (102 total tests)\n","passed":true},{"durationMs":68,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"HackToken.approve(address,uint256)\",\"HackToken.transfer(address,uint256)\",\"HackToken.transferFrom(address,address,uint256)\",\"HackathonMachine.advanceRound()\",\"HackathonMachine.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"HackathonMachine.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"HackathonMachine.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"HackathonMachine.buyWinnerToken(uint256,uint256)\",\"HackathonMachine.cancelDomainVersion()\",\"HackathonMachine.cancelSigner()\",\"HackathonMachine.claim(uint256)\",\"HackathonMachine.configureBuy(uint256,(address,address,uint24,int24,address),uint256)\",\"HackathonMachine.executeDomainVersion()\",\"HackathonMachine.executeSigner()\",\"HackathonMachine.fundRound(uint256)\",\"HackathonMachine.pauseEntries(bool)\",\"HackathonMachine.queueDeny(address)\",\"HackathonMachine.queueDomainVersion(string)\",\"HackathonMachine.queueSigner(address)\",\"HackathonMachine.recycleDeniedStream(uint256)\",\"HackathonMachine.redeemFeeClaims()\",\"HackathonMachine.register(string,string,string,address,address)\",\"HackathonMachine.registerWithMaxFee(string,string,string,address,address,uint256)\",\"HackathonMachine.removeDeny(address)\",\"HackathonMachine.setEntryFee(uint256)\",\"HackathonMachine.setPanelFloors(uint16,uint16)\",\"HackathonMachine.setQuestionHash(bytes32)\",\"HackathonMachine.setWinnerBps(uint16)\",\"HackathonMachine.submitResult((bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HackathonMachine.unlockCallback(bytes)\",\"HackathonMachine.updateEntry(uint256,string,string,string,address)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":7,\"ADAPTATION.md\":131,\"LICENSE\":25,\"README.md\":130,\"docs/dependencies.json\":26,\"docs/heartbeat.json\":35,\"docs/launch-request.json\":8,\"docs/policy-snapshot.json\":44,\"docs/schedule.json\":10,\"docs/security-review.md\":38,\"foundry.toml\":21,\"launch.json\":34,\"remappings.txt\":4,\"src/HackToken.sol\":11,\"src/HackathonMachine.sol\":709,\"src/HookFlags.sol\":28,\"src/LaunchToken.sol\":10,\"src/OracleAttestation.sol\":221,\"test/AdversarialMachine.t.sol\":368,\"test/Attestation.t.sol\":182,\"test/CustodyInvariant.t.sol\":471,\"test/FactoryDeployment.t.sol\":82,\"test/Hook.t.sol\":179,\"test/ImportedAudit.t.sol\":315,\"test/Invariant.t.sol\":264,\"test/LaunchPurchase.t.sol\":115,\"test/LaunchToken.t.sol\":38,\"test/Machine.t.sol\":373,\"test/MachineFixture.sol\":138,\"test/OracleConformance.t.sol\":120,\"test/PrizeBuyAdversarial.t.sol\":196,\"test/Revision.t.sol\":270,\"test/TESTING.md\":37,\"test/Token.t.sol\":31,\"test/TokenProperties.t.sol\":147,\"test/mocks/MockERC20.sol\":46,\"tools/render_heartbeat.py\":42,\"tools/validate.py\":61},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":6425,"exitCode":0,"name":"slither","output":"[medium/high] incorrect-equality at src/HackathonMachine.sol:281: HackathonMachine.potForRound(uint64) (src/HackathonMachine.sol#281-290) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/HackathonMachine.sol:416: HackathonMachine.submitResult(OracleAttestation.Attestation,bytes) (src/HackathonMachine.sol#416-463) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/HackathonMachine.sol:281: HackathonMachine.potForRound(uint64) (src/HackathonMachine.sol#281-290) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/HackathonMachine.sol:391: HackathonMachine._eligible(address,uint64) (src/HackathonMachine.sol#391-395) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/HackathonMachine.sol:281: HackathonMachine.potForRound(uint64) (src/HackathonMachine.sol#281-290) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/HackathonMachine.sol:570: HackathonMachine._pay(address,uint256) (src/HackathonMachine.sol#570-574) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/HackathonMachine.sol:281: HackathonMachine.potForRound(uint64) (src/HackathonMachine.sol#281-290) uses a dangerous strict equality:\n[medium/medium] uninitialized-local at src/HackathonMachine.sol:214: HackathonMachine.beforeSwap(address,PoolKey,SwapParams,bytes).fee (src/HackathonMachine.sol#214) is a local variable never initialized\n[medium/medium] uninitialized-local at src/HackathonMachine.sol:449: HackathonMachine.submitResult(OracleAttestation.Attestation,bytes).bought (src/HackathonMachine.sol#449) is a local variable never initialized\n[medium/medium] unused-return at src/HackathonMachine.sol:399: HackathonMachine.configureBuy(uint256,PoolKey,uint256) (src/HackathonMachine.sol#399-410) ignores return value by (price,None,None,None) = poolManager.getSlot0(key.toId()) (src/HackathonMachine.sol#406)\n[low/medium] reentrancy-benign at src/HackathonMachine.sol:506: Reentrancy in HackathonMachine._buy(uint256,uint256) (src/HackathonMachine.sol#506-533):\n[low/medium] reentrancy-benign at src/HackathonMachine.sol:416: Reentrancy in HackathonMachine.submitResult(OracleAttestation.Attestation,bytes) (src/HackathonMachine.sol#416-463):\n[low/medium] reentrancy-benign at src/HackathonMachine.sol:416: Reentrancy in HackathonMachine.submitResult(OracleAttestation.Attestation,bytes) (src/HackathonMachine.sol#416-463):\n[low/medium] reentrancy-events at src/HackathonMachine.sol:486: Reentrancy in HackathonMachine.unlockCallback(bytes) (src/HackathonMachine.sol#486-504):\n[low/medium] reentrancy-events at src/HackathonMachine.sol:506: Reentrancy in HackathonMachine._buy(uint256,uint256) (src/HackathonMachine.sol#506-533):\n[low/medium] reentrancy-events at src/HackathonMachine.sol:506: Reentrancy in HackathonMachine._buy(uint256,uint256) (src/HackathonMachine.sol#506-533):\n[low/medium] timestamp at src/HackathonMachine.sol:269: HackathonMachine.currentRound() (src/HackathonMachine.sol#269-271) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:328: HackathonMachine._register(string,string,string,address,address,uint256) (src/HackathonMachine.sol#328-350) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:570: HackathonMachine._pay(address,uint256) (src/HackathonMachine.sol#570-574) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:354: HackathonMachine.updateEntry(uint256,string,string,string,address) (src/HackathonMachine.sol#354-368) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:535: HackathonMachine.claim(uint256) (src/HackathonMachine.sol#535-549) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:692: HackathonMachine.isDenied(address) (src/HackathonMachine.sol#692-694) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:557: HackathonMachine._streamDenied(HackathonMachine.Stream) (src/HackathonMachine.sol#557-559) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:630: HackathonMachine.executeSigner() (src/HackathonMachine.sol#630-637) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:296: HackathonMachine._syncRounds() (src/HackathonMachine.sol#296-304) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:281: HackathonMachine.potForRound(uint64) (src/HackathonMachine.sol#281-290) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:551: HackathonMachine.recycleDeniedStream(uint256) (src/HackathonMachine.sol#551-555) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:399: HackathonMachine.configureBuy(uint256,PoolKey,uint256) (src/HackathonMachine.sol#399-410) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:652: HackathonMachine.executeDomainVersion() (src/HackathonMachine.sol#652-660) uses timestamp for comparisons\n[low/medium] timestamp at src/OracleAttestation.sol:157: OracleAttestationConsumer._verifyAttestation(OracleAttestation.Attestation,bytes) (src/OracleAttestation.sol#157-165) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:391: HackathonMachine._eligible(address,uint64) (src/HackathonMachine.sol#391-395) uses timestamp for comparisons\n[low/medium] timestamp at src/HackathonMachine.sol:416: HackathonMachine.submitResult(OracleAttestation.Attestation,bytes) (src/HackathonMachine.sol#416-463) uses timestamp for comparisons","passed":true},{"durationMs":694,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/HackathonMachine.sol:453: Reentrancy: State change after external call\n[low] centralization-risk at src/HackathonMachine.sol:587: Centralization Risk (13 places)\n[low] internal-function-used-once at src/HookFlags.sol:21: Internal Function Used Only Once (2 places)\n[low] large-numeric-literal at src/HackToken.sol:9: Large Numeric Literal (8 places)\n[low] literal-instead-of-constant at src/HackathonMachine.sol:238: Literal Instead of Constant (23 places)\n[low] local-variable-shadowing at src/HackathonMachine.sol:86: Local Variable Shadows State Variable\n[low] non-reentrant-not-first at src/HackathonMachine.sol:194: `nonReentrant` is Not the First Modifier (3 places)\n[low] state-change-without-event at src/HackathonMachine.sol:466: State Change Without Event (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"ef386ded787f31a39ca3c0c85169c52a9552c2f2fa173cb415cad5a20b567b17","verifiedTreeHash":"c859a0c8c3b1e205bc2aa951dadf9dded02bfbeb","verifierVersion":"0.1.0+ad90ce4c"}]}