{"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":"885d5cb6-af12-41ef-94de-82d17d27bbe4","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"fc2c47a2077162dae61f731d8abc875a907acacfd76683483d41b93f41fd7584","dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"ea5db8ea23a93d7611f9ab749d6255ad6596ee76a985c1261d43e63612dfc3cf","skillId":"adapt-contract-project","tools":[]},"key":"adapt_contract_project","kind":"code","role":"implement","skillHash":"ea5db8ea23a93d7611f9ab749d6255ad6596ee76a985c1261d43e63612dfc3cf","skillId":"adapt-contract-project","state":"accepted"},{"acceptedSubmissionHash":"33c84f5a01ff36a2efb94bd48c7e3c653ac43a4c37f6cbca5a3313a25124bc06","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"0bae603243a1e355c289de3bcac8088095089636504f8a06074bd2b42e38ddc5","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"9c2d8a1d9d0e384f1b80d6083fefc80ac160fa01206798658554183d4105075c","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"b72293c3366fff3f0b93151bca72cc6cbd189dad31d1ccd1d34dd483f2165a36","skillId":"audit-imported-code","subagents":4,"tools":[]},"key":"audit_imported_code","kind":"code","role":"review","skillHash":"b72293c3366fff3f0b93151bca72cc6cbd189dad31d1ccd1d34dd483f2165a36","skillId":"audit-imported-code","state":"accepted"},{"acceptedSubmissionHash":"9721a4796e2e35e21049654047639d5ebcbeaba2e2c7f7b9313e616f975c064a","dependsOn":["adapt_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"b89279670f5a00f5d26286edee00c97dd158b997031350a01cafe6e0329ad227","skillId":"audit-judge","subagents":4,"tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"b89279670f5a00f5d26286edee00c97dd158b997031350a01cafe6e0329ad227","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"140dd13befee6f9beb6feb54fc6b53cc9e3fd1b54f486482e10b363602881908","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"0b790d77976dacadb0c9aff53144c105337a8bfb216c7f5d8906201ff86253e9","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"dc773e947db50c44350b21367753e81b5300c81e093a3053e84532fe0e83edc4","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"fb55df38ff17832e5592550dd6a0eadeb29bcd6688def52870249e7f61bccc21","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"606842ccca0e7c5d3017a54684625438eab9b351eb51dcf0cf88c43d1ea41464","skillId":"write-foundry-tests","subagents":4,"tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"606842ccca0e7c5d3017a54684625438eab9b351eb51dcf0cf88c43d1ea41464","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Launch IMDuel (IMDUEL) from launch.json exactly as committed: the IMDuelHook Uniswap v4 hook with the nine constructor arguments listed there (the engine is the ChessEngineV5 already deployed at 0xc21788f704a8b9456f7391a01d8a3d95be215a27; the IMD / native ETH route pool is hookless, so the hook takes no route-hook argument), the standard launch token src/IMDuelToken.sol (named IMDuel, symbol IMDUEL, 1,000,000,000 fixed, minted to the factory; launch.json's token.contract), and its pool paired with IMD at fee 12500 and tick spacing 60. The hook takes 0.5% of every swap of its pools (0.4% to that pool's reward pools, 0.1% converted into IMDUEL, half kept for rewards and half burned), up to 25% more in the main duel's first 300 seconds, all of it to its reward pools (duels opened later don't have it). It has no owner, admin, pause or upgrade path and no backend signer; getHookPermissions() returns exactly the permissions in launch.json. Every swap bids for its team's next chess move, which a later swap plays through the engine. Do not change any contract source, constructor argument or compiler setting: the project builds with forge build and its tests pass with forge test, including launches shaped like the factory's in both token orders. The hook's conversion batches price against the pool's prices at the start of its three latest sampled blocks and run at most once per block per pool; a reward payout whose recipient touches the PoolManager's deltas is refused and left owed (withdrawTo lets the owed party collect it elsewhere); the constructor does not check the engine's code, so the hook builds on a chain where the engine has none; the token contract is in src/.","parentJobId":null,"planHash":"3070b37eab20e6a89416f7d3186248484052ec5252d616c135618d6132c7e3a9","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"885d5cb6-af12-41ef-94de-82d17d27bbe4","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2666-launch-imduel-imduel-launch-json"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52267","feedbackHash":"4e7ef50b8271b0b95b57d6b8d328511481743f6d58e57c597163666400f03c5b","nodeKey":"adapt_contract_project","submissionHash":"fc2c47a2077162dae61f731d8abc875a907acacfd76683483d41b93f41fd7584","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52642","feedbackHash":"4700aa744b575f780f17600cf0b4ff289311e0fa523d4563e4a57f5bf9f10b88","nodeKey":"adapt_contract_project","submissionHash":"eea25d482b3080b8e6745acc2ffa11ad229fe1d054eab2cbbd3bddd880b5de05","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51747","feedbackHash":"c455239a3e88b60fb299afd3db97fc670f98f447805764a74cbb456a002f1ba0","nodeKey":"adapt_contract_project","submissionHash":"eb9b141484d0276f6cd9785f470f181d3e29f5a9fc701c60a2b2d67c6c924b54","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52268","feedbackHash":"f074a0338cea62b64e05aaf72560bde13ecf961a999e34111e0bd70a146a4d9d","nodeKey":"adapt_contract_project","submissionHash":"0e764952d268d69124256424fd903bc1ae19aeac183df6e0ec1280675bacaaec","tag1":"verification:checks","tag2":"acceptance-v2","value":0},{"agentId":"54232","feedbackHash":"d883ede4e06e71f0db1a9cec0e40c246e6ac4a5ebff25f9122794b7a77e29743","nodeKey":"audit_economics","submissionHash":"33c84f5a01ff36a2efb94bd48c7e3c653ac43a4c37f6cbca5a3313a25124bc06","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"53934","feedbackHash":"36fc429cadc5d7d24e7f6353273f54208532ac921a672d97f699691e5fdbcc90","nodeKey":"audit_flow","submissionHash":"0bae603243a1e355c289de3bcac8088095089636504f8a06074bd2b42e38ddc5","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52165","feedbackHash":"6bb8580fb31e52cb7f3ac87dd89d0936c205aeccd3c07d5882ceaa86c9a844bb","nodeKey":"audit_imported_code","submissionHash":"9c2d8a1d9d0e384f1b80d6083fefc80ac160fa01206798658554183d4105075c","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51018","feedbackHash":"676e8e782c3e6b9e868689f6be3aee4fa7986f82a0fa29752b4651d263cdad5b","nodeKey":"audit_judge","submissionHash":"eb9f8b277e7d9128f0889bcc5ad8845a405338bd1dab194919e73d2420a24d33","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52174","feedbackHash":"e71038e95b5a348295675b8e2164cd0f1a0149ffb14d39df2c0f4d0ce2cbe833","nodeKey":"audit_judge","submissionHash":"9721a4796e2e35e21049654047639d5ebcbeaba2e2c7f7b9313e616f975c064a","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51318","feedbackHash":"9cd8ef65112689f59b02836438928523e386ca57a60f3f31ed3dede78b768999","nodeKey":"audit_judge","submissionHash":"7c51bf89f5535b44c5f86583f26fcbabed09088db7e59b771d87cbc6486c2f4f","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51251","feedbackHash":"2e69853d7346ae1b51f6c6ba53f2bee6e75ee8e70a75a4337521e8f066ddb723","nodeKey":"audit_math","submissionHash":"140dd13befee6f9beb6feb54fc6b53cc9e3fd1b54f486482e10b363602881908","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52617","feedbackHash":"8fac5c24d70fc5a7dc859e7603d830795ac06045db9a49cc729108ec7de867b3","nodeKey":"audit_permissions","submissionHash":"0b790d77976dacadb0c9aff53144c105337a8bfb216c7f5d8906201ff86253e9","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52480","feedbackHash":"9b13f2c71d4fea0288a78d157691a9124227ba2b0fdc25627e947e480ce38eed","nodeKey":"manifest","submissionHash":"dc773e947db50c44350b21367753e81b5300c81e093a3053e84532fe0e83edc4","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52264","feedbackHash":"3b3bd8535622aa1b6f14550314bf5a6996f0a20753d3b1f873fb18f2a0d77dc5","nodeKey":"manifest","submissionHash":"5c8b6612b65404ecd259a404b38c7b12d2d906c3fa75b8c1502ded3aebed26f0","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52182","feedbackHash":"099fdc8238eb827c2972a39eb739e8f5642bcc1079cc97690de1ceac956dc1c2","nodeKey":"manifest","submissionHash":"636730a611bebfe067408453c3efe792d2563a26b43ff327bbad36043f62b621","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51128","feedbackHash":"1871808566e227527ca0d3c32b50ea9560625f8d15c5f4665130e8ccb2eb1f99","nodeKey":"write_foundry_tests","submissionHash":"ebb56f2943a82450e44eebe264bb52d40ec28e7b117282a3304f08a6b6aee63e","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52609","feedbackHash":"7402b31ce4cf8de4d71a9b8fc5c4dbbee66ab5f727c1bd23c6157f67818beb75","nodeKey":"write_foundry_tests","submissionHash":"c75591c1cbc79bc9e01fcff945548c619bac1bea2d4aa041648aa401075d5ca7","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52188","feedbackHash":"8da0fbfb2e1a69db24e8eba61d39a1e9e5e7037fc8898f27c1e633af2ba39e23","nodeKey":"write_foundry_tests","submissionHash":"fb55df38ff17832e5592550dd6a0eadeb29bcd6688def52870249e7f61bccc21","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"82a0d94cf97b9210441a762355053b64a067ae1a6aeb7166477a12389f405847","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"76df9026b98e4277","findings":[{"citation":"resolved","description":"Trust gap (access x economics). When hookData is not 20 bytes or a clean 32-byte address, _bidOwner falls back to the PoolManager's swap `sender`, which is the router contract (line 663). _payBounty then pays the move's bounty to that address with manager.take (_pay/payOut, lines 381-404). On a router whose token balance is public, the bounty is effectively paid to whoever takes it first. Uniswap's Universal Router is one: its own V4 swap action forwards the caller's hookData, which is empty by default, and any caller can run its SWEEP command to take the router's whole balance of a token. In a router that accepts ETH from the PoolManager, owed ETH can also be pushed there by anyone with withdraw(router, c), because withdraw has no caller check. The payout is not made in the bidder's own transaction. It happens inside the later trade that resolves the move (_play -> _resolve -> _payBounty), so the trader whose swap plays the move can put a SWEEP after the swap in the same Universal Router execute() call and take the previous bidder's bounty atomically. The bidder paid for the move with a real buy and earned the capture, check or mate bounty, but a third party receives it. The adaptation chose the swap sender instead of tx.origin and says integrations should pass the recipient. It does not cover that the default path on the most common v4 router sends every bounty to the next trader. Proposed fix (a design decision for the author): only pay a bounty to an explicit hookData recipient. A bid without one still plays at its strength, but its bounty stays in the chest, just as an explicit zero recipient already opts out (lines 831, 659-661). Alternatively, credit owed[sender] without pushing it, and have withdraw only send owed funds when the owed party calls it. Either way, no shared router balance ever holds a reward.","line":657,"path":"src/IMDuelHook.sol","reproduction":"State: the main duel is open, and the bidder's team's war chest holds 10 IMDUEL-side tokens (fundChest(pool, true, 10e18)). The position lets the mover's next move take a queen (bounty 270 bps), as in test/BidOwner.t.sol::forcedCapture. 1) Alice buys through the Universal Router (or any router with a public sweep). The router passes empty hookData, so bid().owner == router (test_missingOwnerUsesSwapSenderNotBundler asserts exactly this for PoolSwapTest). 2) After turnBlocks blocks, Mallory sends one Universal Router execute(): a V4 swap in the same pool, then SWEEP(currency1, mallory, 0). Mallory's swap resolves Alice's move, and _payBounty calls manager.take(currency1, router, 0.27e18). test/BidOwner.t.sol::test_missingOwnerPaysTheSwapSender shows the 0.27 ether landing in the router's balance. The SWEEP then moves it to Mallory. Expected: Alice, the bid's owner, receives 0.27e18 or can claim it. Actual: Alice gets nothing, and Mallory, the trader who played the move, gets 0.27e18. If the payout fails instead (for example, a router that refuses ETH), owed[router] is stuck, because a router cannot call withdrawTo.","severity":"medium","snippet":"    function _bidOwner(address sender, bytes calldata hookData) internal pure returns (address) {\n        if (hookData.length == 20) return address(bytes20(hookData));\n        if (hookData.length == 32) {\n            uint256 value = uint256(bytes32(hookData));\n            if (value <= type(uint160).max) return address(uint160(value));\n        }\n        return sender;","title":"A bid with no hookData is owned by the swap router: on a shared router (Uniswap Universal Router) the trader whose swap plays the move sweeps the bounty"}],"hash":"0b790d77976dacadb0c9aff53144c105337a8bfb216c7f5d8906201ff86253e9","nodeId":"6e08e002-d0a2-4d91-acb6-72df3d5ed18c","outcome":"completed","summary":"I found one medium-severity issue in the permissions area (Access Control, Trust Gap, Asymmetry) and no high or critical ones. The findings are in `.imd-findings.json`, with a coverage row for all 14 entry points plus three invariant rows. I changed no files; the findings file is git-ignored.\n\n**Finding 1 (medium): when no reward recipient is passed, the swap router owns the bid, so on the Uniswap Universal Router whoever plays the move can take the bounty** (`src/IMDuelHook.sol:657`)\n- If a swap's hookData isn't a 20-byte or clean 32-byte address, `_bidOwner` makes the swap's `sender` the bid owner. That is the router contract.\n- The bounty isn't paid in the bidder's own transaction. It's paid during the later trade that plays the move.\n- On the Universal Router, the hookData is empty by default, and anyone can sweep the router's token balance with its SWEEP command. The trader who plays the move can add a SWEEP after their swap in the same call and take the earlier bidder's capture, check or mate bounty.\n- On a router that accepts ETH, anyone can also push owed ETH there with `withdraw(router, c)` and sweep it. On a router that refuses ETH, the owed reward is stuck, because a router can't call `withdrawTo`.\n- The existing test `test_missingOwnerPaysTheSwapSender` shows the 0.27-ether bounty landing in the router's balance (I re-ran it and it passes). The sweep step is the Universal Router's own public behaviour; I didn't test it here.\n- The fix needs a design decision: either pay bounties only to a recipient named in hookData, or keep a router-owned reward owed and release it only when the owner itself withdraws. An earlier contributor deliberately chose the router fallback over `tx.origin` and told integrators to pass the recipient. What they didn't address is that, by default, the next trader gets the bounty.\n\n**What holds:**\n- **No privileged roles:** the hook's only guards are \"PoolManager only\" and \"hook only\". There is no owner, admin, pause, upgrade path or signer, and nothing changes its settings after construction. The token has no owner, mint or blocklist.\n- **Callers and approvals:** `unlockCallback` only ever gets data the hook encoded itself. `fundChest` pulls tokens only from its own caller, so a token approval given to the hook can't be spent for anyone else.\n- **First pool:** the first pool opened becomes the main duel, and nobody can initialize it before the hook exists. The opening surcharge applies to the main duel only.\n- **Payouts:** the PoolManager-delta check on payouts and the \"left owed\" fallback keep the hook's balances consistent.\n- **Route prices:** `sampleRoute` accepts only prices proven against a real block hash, for the configured hookless route. I recomputed the route pool id and it matches the notes.\n- **Engine:** the code deployed on mainnet at 0xc21788… is byte-for-byte the `ChessEngineV5` compiled from this tree.\n- **Manifest:** the nine constructor arguments and five permissions in `launch.json` match the code, in declaration order.\n\n**Not covered:** I didn't attempt a full math or economics pass (the batch price band, the strength formula, how bounty sizes interact with the opening surcharge), and I didn't re-audit the chess engine.","treeHash":null,"usage":{"cachedInputTokens":1329234,"inputTokens":32,"model":"claude-opus-5-5","outputTokens":36419,"runtime":"claude","turns":17,"wallClockMs":489788}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"5dd3af20ad7ac0c2","findings":[{"citation":"resolved","description":"payOut (src/IMDuelHook.sol:394-399) refuses a reward whose recipient 'touches the PoolManager's deltas' by comparing manager.getNonzeroDeltaCount() before and after _send. That count only changes when some delta crosses zero. While afterSwap runs, the hook itself holds a nonzero delta in every currency it minted fee claims in this swap (-chest share, -slice; the hookDelta credit is accounted by PoolManager.swap only after afterSwap returns). A recipient paid in native ETH (or through a token with a transfer callback) can therefore call PoolManager.settleFor(hook){value: 1 wei} (or sync(token)+transfer+settleFor(hook)) from its receive(): the hook's delta moves from -fee to -fee+1, stays nonzero, the count is unchanged, payOut returns normally and Reward(paid=true) is emitted. When PoolManager.swap later credits the hook's fee, the hook is left with a +1 wei delta nobody clears and the whole swap reverts with CurrencyNotSettled. Because every swap of that pool first runs _resolve for the same open move and pays the same recipient, every trade of the duel in either direction reverts until _hashOf(closeBlock) returns 0 (8,191 blocks, about 27 hours), when the move degrades to a 0% timeout turn. The attacker can repeat it with any later top bid that yields a bounty (capture, check or mate). This is exactly the freeze that job f7f2a606's 'DebtOwner' finding was meant to close (test/Hostile.t.sol debtOwnerIsRefusedAndTradingGoesOn), reached through the hook's delta instead of the recipient's own. Affected: every duel paired with native ETH (ETH/IMD, ETH/$IMDUEL, ETH/X) and duels in callback tokens; the launch's main duel is not, since IMD (mainnet bytecode checked: no ERC-777/1363 selectors) and IMDuelToken give a recipient no execution during take(). Cost to the attacker: one top bid (a swap) plus 1 wei. Fix: do not rely on the global count. Snapshot the hook's own deltas (manager.currencyDelta(address(this), key.currency0) and currency1, or at least the payout currency and both pool currencies) before _send and refuse if any changed other than by the burn/take pair; or make rewards pull-only for contract recipients (record owed and let withdraw/withdrawTo move them outside the trade), which removes the recipient's execution inside the swap entirely.","line":394,"path":"src/IMDuelHook.sol","reproduction":"State: hook with an ETH/IMD duel (anyone may open one: afterInitialize accepts a pool whose currency1 is IMD), liquidity in it, ETH chest 10 ETH and IMD chest 100 IMD (so _smallerChest picks the ETH side and the hook mints ETH fee claims on every trade), White (ETH's team) to move in a position where its only move captures a queen. Calls: (1) swap(duel, zeroForOne=false, -1e18 IMD, hookData=abi.encodePacked(attacker)) -> attacker owns White's bid. (2) Next block: swap(duel, zeroForOne=true, -1 ETH). Inside afterSwap -> _play -> _resolve -> _payBounty -> _pay -> payOut: take(ETH, attacker, 0.27 ETH) runs attacker.receive(), which calls manager.settleFor{value: 1}(hook). Expected: the payout is refused and left owed (Unsettled), the trade succeeds and the move is played. Actual: Reward(paid=true) is emitted, PoolManager.swap then credits the hook's +chest-share hookDelta, the hook ends the unlock with a +1 wei ETH delta and the swap reverts CurrencyNotSettled. (3) Every later swap of the duel in either direction reverts the same way (test_everyLaterTradeOfTheDuelRevertsToo: 0 of 6 trades over 3 blocks succeed). Run: forge test --match-path test/scratch/PayoutSettleFor.t.sol (2 of 3 tests fail on the current code; the plain-recipient control passes).","severity":"medium","snippet":"    function payOut(Currency c, address to, uint256 amount) external {\n        _onlySelf();\n        uint256 open = manager.getNonzeroDeltaCount();\n        _send(c, to, amount);\n        if (manager.getNonzeroDeltaCount() != open) revert Unsettled();\n    }","title":"payOut's Unsettled guard counts nonzero deltas only: a recipient that credits the hook's own already-nonzero delta (settleFor) passes it, and the trade that plays its move reverts with CurrencyNotSett"},{"citation":"resolved","description":"_bidOwner falls back to `sender`, which for every PoolManager.swap made through a router (Universal Router, aggregators, the v4 test routers) is the router contract, never the trader. Every buy is a bid, so a trader who swaps through a generic router without the 20/32-byte hookData owns nothing: when its move pays, _payBounty pays the router. With a router that has no receive() (PoolSwapTest here) take() fails and the reward is recorded in owed[router], which only `withdraw(router, c)` can move, to the router itself, where it fails again: the ETH is locked in the hook for good. With a router that accepts ETH from the PoolManager (Universal Router's receive does) the ETH lands in the router and anyone can SWEEP it. Either way the trader's bounty is lost. The contract notes acknowledge the sender is 'usually a router' but treat it as acceptable; since the IMD launch pool will be traded through the standard v4 routers by default, most trades that win a move will forfeit their reward to the router. Fix options: refuse to bid (no owner) when hookData is empty and sender has code, or record the bid with owner = address(0) in that case (a 'nobody' bid that pays nobody, as timeout moves do), or require explicit hookData for a bid.","line":663,"path":"src/IMDuelHook.sol","reproduction":"State: an ETH/IMD duel on the hook with liquidity, ETH chest 10 ETH, White (ETH's team) to move with a forced queen capture. Calls: (1) PoolSwapTest.swap(duel, zeroForOne=false, -1e18, hookData=\"\") -> game(id).bids[0].owner == address(swapRouter). (2) Next block: swap(duel, zeroForOne=true, -1 ether) plays Kxe2. Expected: the trader (tx sender) is paid 2.7% of the ETH chest, or at least nobody is. Actual: Reward(owner=router); PoolSwapTest has no receive(), so owed[router][0] == 270215568216861733 wei and the router's balance is unchanged; withdraw(router, ETH) reverts (no receive) and only the router could call withdrawTo. Reproduced in test/scratch/RouterOwner.t.sol test_emptyHookDataPaysTheRouter (logs: pushed to router 0, owed to router 270215568216861733).","severity":"low","snippet":"        return sender;","title":"A swap without hookData makes the router the bid owner: its bounty is pushed to or left owed to the router contract, where the trader cannot collect it"}],"hash":"0bae603243a1e355c289de3bcac8088095089636504f8a06074bd2b42e38ddc5","nodeId":"2daafe19-7233-421e-ac6b-b59b074de53d","outcome":"completed","summary":"Review complete. The findings file is written and validated, no tracked file was changed, and the proof test fails on the current code for the stated reason.\n\n**Outcome.** Two findings, both in `src/IMDuelHook.sol`, plus seventeen coverage rows with none unreached. The build and all 171 project tests pass as the baseline.\n\n**Finding 1, medium, with a Foundry proof.** The payout guard in `payOut` compares the PoolManager's nonzero-delta count before and after sending a reward. While afterSwap runs, the hook already holds a nonzero delta in each currency it minted fee claims in, because the fee credit is accounted only after afterSwap returns. A reward recipient that gets execution during the payout, meaning native ETH or a callback token, can call `settleFor` on the hook for 1 wei. The hook's delta stays nonzero, the count is unchanged, the payout is accepted, and the swap then dies with CurrencyNotSettled. Every later trade of that duel replays the same move and reverts, in both directions, until the window's block hash expires after about 8,191 blocks. This is the same freeze a previous review closed with the DebtOwner fix, reached through the hook's delta instead of the recipient's. The main IMD/IMDUEL pool is not exposed because both tokens are plain ERC20s with no transfer callbacks, which I checked against the mainnet IMD bytecode. ETH-paired duels, which the design invites, are. The proof is test/scratch/PayoutSettleFor.t.sol: two tests fail now, the plain-recipient control passes.\n\n**Finding 2, low.** A swap with empty hookData makes the router the bid owner. The bounty is pushed to the router or, for a router without receive, left owed to it with no way to collect. Reproduced in a scratch test showing 0.27 ETH owed to the test router.\n\n**Verified as holding.** Fee deltas and the PartialFill check in both swap directions and both token orders, the transient stash and reentrancy lock, fundChest and the withdraw paths, the once-per-block batch rule and block-start sampling, the MerkleTrie and RLPReader libraries in full, the manifest's nine constructor arguments and five permissions, and that the engine, the IMD token and the IMD/ETH route pool with fee 10000 all exist on mainnet.\n\n**Not reached.** The chess engine and bitboard internals, which are outside the control-flow area and already deployed, and economic cost modelling of the documented three-block price-hold manipulation, which the notes accept as a design bound.","treeHash":null,"usage":{"cachedInputTokens":4174442,"inputTokens":770,"model":"claude-fable-5-1","outputTokens":89104,"runtime":"claude","turns":51,"wallClockMs":2104248}},{"artifacts":[],"attempt":1,"bundleHash":"316615b80e526a6c2d428d47c9e1fa52df704f07ece1b97c7604489d5512233e","device":"ff35db055462d2ad","findings":[],"hash":"0e764952d268d69124256424fd903bc1ae19aeac183df6e0ec1280675bacaaec","nodeId":"1c20b3f6-4ce8-4ba7-8145-fa1ddea830c0","outcome":"completed","summary":"The responses file is excluded by the repository's own git exclude rules, which is the harness's intended channel, so it is in place and valid JSON. The verifier's proof copy is removed and only the delivered files remain changed.\n\n**Outcome:** the failing check is repaired. The payout guard fix itself was already in the tree from the previous pass; the proof failed on the verifier only because it could not find the artifact it deploys.\n\n**What was wrong.** The reviewer's proof deploys its helper hook with `deployCodeTo(\"Proof_SyncRecipient.t.sol:PositionHook\", ...)`. The verifier runs the proof from a copy under a different file name, so no source file named `Proof_SyncRecipient.t.sol` existed and `setUp` failed before any assertion ran. That failure happened on both the starting tree and the fixed one, so the verifier could not see the fix. I reproduced it locally: the proof fails under the verifier's file name and passes under its own.\n\n**What I changed.**\n- Added `test/Proof_SyncRecipient.t.sol`. It carries the proof's `PositionHook` helper unchanged under the file name the proof looks up, plus a regression test for the finding: the ETH-paying trade plays the move, the payout to a syncing owner is refused and left owed, nothing stays synced, later ETH-paying trades go through, and the owner collects with `withdrawTo`.\n- Updated ADAPTATION.md with the reason for that file and its name.\n- Rewrote `.imd-responses.json` answering all three reviewer findings: the medium as fixed, the two accepted limitations as disputed with no change, as settled earlier.\n\n**Verification.** `forge build` succeeds. The full suite with the verifier's fuzz seed, run with the proof copied to the verifier's exact path, passed every test, and the copy was removed afterwards.\n\n| Suites | Tests | Failed |\n| --- | --- | --- |\n| 26 | 182 | 0 |\n\nNo file in src/, launch.json or foundry.toml changed in this pass. Nothing is committed; the harness's own commit step picks up the working tree.","treeHash":"3d61d87689c86c4a0aa5dc716ae4cbb9436ddc5b","usage":{"cachedInputTokens":4206556,"inputTokens":1764,"model":"claude-fable-5-1","outputTokens":45928,"runtime":"claude","turns":57,"wallClockMs":1222446}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"723b11f958c65250","findings":[{"citation":"resolved","description":"Boundary (sentinel-address branch). _bidOwner decodes a 20-byte or clean 32-byte hookData as the reward recipient without excluding address(0). The hook elsewhere uses owner == address(0) as the marker of a timeout turn: _payBounty (src/IMDuelHook.sol:831, `if (t.owner == address(0)) return;`) pays nothing for such an owner, and _resolve creates timeout bids as Trade(0, 0, address(0), 0). A swap whose hookData is abi.encode(address(0)) (what a router does when a 'recipient' field is left unset, or abi.encodePacked(address(0))) therefore creates a real top bid (size > 0, open = true, strength from its share) whose owner is the zero address: it raises the 10% bar every later bidder of that team must clear and the move is played at its strength, but when the move captures, checks or mates the chest pays nobody, and the Reward event is never emitted. The same encoding on the ordinary path (empty hookData) would have made the router the owner, so the swapper loses the bounty it paid 0.5% for. No other party loses funds; the chest simply keeps the bounty. Fix: treat a decoded zero address like missing hookData (fall back to sender), or revert.","line":661,"path":"src/IMDuelHook.sol","reproduction":"Main duel with liquidity (test/Base.sol setUpMainDuel). 1) swap(pool, whiteTeam()==1, -1e18, abi.encode(address(0))) through PoolSwapTest. Expected: owner = the swap sender (the router) as for empty hookData, or a revert. Actual: hook.game(id).bids[toMove] has open = true, size = 1e18 - fee, owner = address(0). 2) Advance a block and let the move resolve into a capture: _payBounty returns at line 831 before _takeChest/_pay, so no Reward event and no payout, although a bid with a nonzero owner in the same state would have been paid chest*bps/10000 (capped at 2x output). Verified by test/scratch/ZeroOwner.t.sol (test_zeroOwnerHookDataMakesAnUnpayableBid, test_zeroOwnerPacked): both assert bid().owner == address(0) with bid().size > 0 and pass on the current code.","severity":"low","snippet":"            if (value <= type(uint160).max) return address(uint160(value));","title":"hookData of 20 or 32 zero bytes is accepted as an explicit bid owner of address(0), which is also the 'timeout move, pay nobody' sentinel: the trade's bid is taken but its bounty is silently forfeited"},{"citation":"resolved","description":"Math precision, zero-rounding. amount * 40 / 10_000 is 0 for amount < 250 and amount * 10 / 10_000 is 0 for amount < 1000, so a trade of up to 249 wei pays no chest share and no slice, yet _play still accepts it as a bid: _beats returns true against an empty slot, so a 100-wei trade owns the move at strength 0 (Strength.fromShare rounds such a share to 0) and later bidders must pay more than 110 wei. This is the behaviour the project's own fixtures rely on (test/Base.sol playTurn trades 100 wei 'no fee'), and the value at stake (a bounty capped at 2x a sub-250-wei output) is below gas, so it is recorded as a precision note rather than a defect. The design already caps the payout at twice the trade's output, which bounds any extraction from the chest by a dust bid.","line":339,"path":"src/IMDuelHook.sol","reproduction":"Main duel with liquidity. swap(pool, true, -249, \"\"): chest(id,0)+chest(id,1) and pile(currency0) are unchanged (249*40/10000 == 0, 249*10/10000 == 0), while bid().size > 0 and bid().open == true. Verified by test/scratch/ZeroOwner.t.sol test_dustTradePaysNoFee, which passes on the current code.","severity":"info","snippet":"        uint256 toChest = chestIdx == idx ? _addChest(id, idx, amount * _chestBps(g) / 10_000) : 0;","title":"Fee truncates to zero below 250 wei of the fee-bearing side, and such a fee-free dust trade still becomes the top bid of an open move"},{"citation":"resolved","description":"Math precision, downcast. For an oneForZero batch (the hook selling currency1, i.e. a launch whose $IMDUEL address sorts below IMD) with a sampled reference above 2^160 / 1.001 (about 0.999 * MAX_SQRT_PRICE), from * 10_010 / 10_000 exceeds 2^160 and the uint160 cast wraps to a small number; the following guard `limit <= sqrtPrice` then returns (0, 0), so the conversion is skipped rather than executed at a wrong limit. Symmetrically for zeroForOne a reference within 0.1% of MIN_SQRT_PRICE yields a limit below MIN_SQRT_PRICE, which the pool rejects (PriceLimitOutOfBounds) inside the try/catch of _play, so trades are unaffected and only an external convert() call reverts. Both require the main duel or route pool to sit at a price of roughly 2^128 or 2^-128 token1 per token0 in raw units, unreachable for two 18-decimal tokens. Recorded for completeness: no funds move, and the batch resumes once the price leaves the extreme.","line":542,"path":"src/IMDuelHook.sol","reproduction":"Hypothetical state only: samples[0] holding three packed prices whose unpacked maximum is >= 1460040000000000000000000000000000000000000000000 with imduel < imd; _swapClaims computes limit = uint160(from * 10010 / 10000) which is < from, hits `limit <= sqrtPrice` and returns (0, 0). Not reproduced in a test because the pool cannot be initialised at such a price with the project's fixtures; included as a note, not a defect.","severity":"info","snippet":"        uint160 limit =\n            uint160(zeroForOne ? from * (10_000 - BATCH_SQRT_BPS) / 10_000 : from * (10_000 + BATCH_SQRT_BPS) / 10_000);","title":"Batch price limit cast to uint160 wraps when the reference sqrt price is within 0.1% of MAX_SQRT_PRICE; the batch then returns (0,0) instead of running (fail-closed, not exploitable)"}],"hash":"140dd13befee6f9beb6feb54fc6b53cc9e3fd1b54f486482e10b363602881908","nodeId":"e30f43c0-6a9d-48a5-96ed-0234cc05014d","outcome":"completed","summary":"The findings file is valid, every snippet matches its line in the tree, and the coverage record answers all 14 entry points plus six invariant rows.\n\n**Outcome.** Within the math area (Math Precision, Boundary, Numerical Gap) I found no defect that moves funds to the wrong party or breaks a guarantee. The fee, chest, bounty, conversion-batch and sample-packing arithmetic all trace clean, and the project's money, slice, strength, route, launch, bidding and token suites pass (68 tests).\n\n**What I reported** in `.imd-findings.json`:\n\n- **Low:** hookData of 20 or 32 zero bytes is decoded as an explicit owner of the zero address, which is also the hook's \"timeout move, pay nobody\" sentinel. The bid is taken and raises the bar for later bidders, but the bounty is silently forfeited. Reproduced in a scratch test.\n- **Info:** the fee rounds to zero under 250 wei on the fee-bearing side, and such a dust trade still becomes the top bid at strength 0. The project's own fixtures rely on this, and the 2x payout cap bounds it.\n- **Info:** the batch price limit's uint160 cast wraps only within 0.1% of the max sqrt price, and then fails closed (no batch) rather than trading at a wrong limit. Unreachable for two 18-decimal tokens.\n\n**What holds, with the reasoning recorded per row:** the PartialFill check for exact-input and exact-output in both token orders, the opening surcharge bound of 25.4% keeping v4's swap sign valid, the smaller-chest double mulDiv (cannot overflow since chest is below 2^128 and price below 2^128), the 56-bit sample packing and the max/min reference direction for both token orders, the Strength log curve, and the repetition-ring lane parity. The slither \"incorrect-exp\" and aderyn \"shift order\" leads are false positives.\n\n**Not reached in depth:** the Bitboard and ChessEngineV5 internals beyond the hook's decoding of the engine's 224-byte answer, and the Merkle-Patricia proof library beyond confirming it is Optimism's audited code and that the verifier bounds the sampled block to at least two blocks back.","treeHash":null,"usage":{"cachedInputTokens":1134213,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":47698,"runtime":"claude","turns":23,"wallClockMs":1055826}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"194b47a372650d59","findings":[],"hash":"2cc93b66868d747ba5f3f14acdaf3251d23a3997ded6831057038c3bffbeae4e","nodeId":"68b6ecbd-c233-46b8-a00a-56395089e7ce","outcome":"failed","summary":"This content was flagged for possible cybersecurity risk. If this seems wrong, try rephrasing your request. If you’re doing authorized security work that requires more cyber permissive safeguards, apply for Daybreak access via https://platform.openai.com/… before retrying.","treeHash":null,"usage":{"cachedInputTokens":2795264,"inputTokens":140113,"model":"gpt-6.1-sol","outputTokens":21982,"runtime":"codex","turns":4,"wallClockMs":596535}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"89eed35842d609d7","findings":[{"citation":"resolved","description":"The stated guarantee (contract notes, lines 391-393 and the task brief) is that a reward recipient who touches the PoolManager's deltas is refused and left owed, so the trade cannot be made to revert at the end of its unlock. The guard only snapshots PoolManager.getNonzeroDeltaCount() before and after _send. During afterSwap the hook itself holds a nonzero delta: _takeFee mints balance claims in beforeSwap and afterSwap (line 344), each mint accounting a negative delta to the hook, while the offsetting positive hook delta is only booked by PoolManager.swap after afterSwap returns (lib/v4-core/src/PoolManager.sol lines 221-224: `(swapDelta, hookDelta) = key.hooks.afterSwap(...); if (hookDelta != ZERO) _accountPoolBalanceDelta(key, hookDelta, address(key.hooks));`). In a duel whose chest currency is native ETH (any X/ETH pool opened on the hook; afterInitialize accepts them because ethFee is 10000) the reward is pushed with manager.take, which calls the recipient's receive() with all remaining gas while the manager is unlocked. The recipient calls manager.settleFor{value: 1}(hook) (onlyWhenUnlocked, callable by anyone, no sync needed for native). The hook's ETH delta moves from -S to -S+1 wei: still nonzero, so the count is unchanged and the guard passes, payOut succeeds, _pay records paid = true. After afterSwap returns the manager books +S to the hook, leaving it at +1 wei; the router's unlock ends with NonzeroDeltaCount != 0 and the whole swap reverts with CurrencyNotSettled. Because a queued move is only ever resolved from _play inside a swap (lines 625-626), every later swap on that pool re-runs _resolve, re-pays the same recipient, and reverts the same way: the pool cannot trade until _hashOf(g.closeBlock) returns 0 once the chain no longer serves that block hash (8,191 blocks, about a day, lines 697-701), when the move decays into a 0% timeout move that pays nobody. The attacker can repeat this with every bounty-paying move it wins, so one trade per day keeps an ETH-paired duel untradeable indefinitely, and can stop it at will (extortion leverage). There is no owner or other path to skip or cancel a move earlier. The author's Hostile.t.sol DebtOwner test covers a recipient that opens a new delta (count changes); it does not cover a recipient that changes an existing nonzero delta of the hook, which is what this bypass does. The main duel (IMDUEL/IMD, both plain ERC-20s with no transfer callback) is not exposed; every ETH-paired duel is, including ones opened by third parties. Precondition cost: the attacker needs to own a top bid on a move that pays a bounty (any capture or check; amount > 0 needs chest(team) >= 334 wei, which fundChest provides for free) and to be reachable as bid owner (20-byte hookData). Fix: snapshot the hook's own deltas on the pool's two currencies (manager.currencyDelta(address(this), key.currency0/1)) and the recipient's, and revert Unsettled if any changed; or never push native ETH to a contract recipient and always leave it owed (withdraw/withdrawTo already exist).","line":398,"path":"src/IMDuelHook.sol","reproduction":"State: an ETH-paired duel (PoolKey{currency0 = address(0), currency1 = X, fee 12500, spacing 60, hooks = IMDuelHook}) initialized after the main duel, with liquidity; chest(id, 0) funded with 1 ether via fundChest{value: 1 ether}(key, false, 1 ether). Attacker contract A has receive() { manager.sync(Currency.wrap(address(0))); manager.settleFor{value: 1}(address(hook)); } (the sync resets any currency a router left synced so the native settle path is taken; everything reverts at the end anyway). Steps: (1) A swaps X->ETH (zeroForOne = false, exact input) through a router with hookData = abi.encodePacked(address(A)), becoming the ETH team's top bid; (2) the position is such that the ETH team's engine move captures or gives check (or simply repeat step 1 over several moves until a bounty-paying move is owned; bountyBps(captured, check, mate) > 0); (3) advance past g.closeBlock; (4) any user swaps on the pool. Expected: the move is played, the bounty to A is refused with Unsettled and recorded in owed[A][0], the user's swap succeeds. Actual: payOut succeeds because getNonzeroDeltaCount is unchanged (hook delta -S -> -S+1), the swap later reverts with CurrencyNotSettled at the end of the router's unlock, and every subsequent swap on the pool reverts identically for about 8,191 blocks, until the move decays to a timeout; the attacker repeats with its next bounty-paying bid. Note: a Foundry proof could not be executed here because the test suite's via-IR compilation is OOM-killed under this sandbox's 1.5 GB memory limit (forge build of src/ alone succeeds).","severity":"medium","snippet":"        if (manager.getNonzeroDeltaCount() != open) revert Unsettled();","title":"payOut's Unsettled guard compares only the nonzero-delta count: a native-ETH reward recipient can partially settleFor(hook) during the payout, pass the guard, and make every swap that resolves its mov"},{"citation":"resolved","description":"_bidOwner returns the PoolManager's msg.sender when hookData is not a 20- or 32-byte address. Standard front-ends route through the Universal Router or PoolSwapTest-style routers and pass empty hookData, so the bid owner, and therefore the recipient of the chest payout (_payBounty -> _pay -> manager.take(c, to, amount)), is the router. Universal Router exposes sweep(), so the first MEV bot takes the bounty; routers without a receive() make an ETH payout revert and the reward stays owed to the router, where nobody can withdraw it (withdrawTo needs msg.sender == router). The economic effect is that the 0.4% chest share paid by every trader is redistributed to router sweepers rather than to traders unless every integrator adds hookData. Documented in the code comment, but it contradicts the brief's statement that every swap bids for its team and that the move's owner is paid. Fix suggestion: refuse to bid (or pay nobody) when hookData is empty and sender has code, or document that hookData is mandatory for integrators.","line":663,"path":"src/IMDuelHook.sol","reproduction":"State: main duel with liquidity, chest(id, IMDUEL team) = 100e18. User U swaps 1e18 IMD for IMDUEL through a router R with hookData = \"\". Bid event owner = R (not U). After the window closes and a later swap plays a capturing move, Reward(..., owner = R, ...) is emitted and manager.take sends the IMDUEL to R. Expected: U receives the bounty. Actual: R holds it; any caller of R.sweep (Universal Router) takes it.","severity":"low","snippet":"        return sender;","title":"Rewards default to the swap's sender, which for ordinary router swaps is the router contract: bounties are paid to the router and are sweepable by anyone"},{"citation":"resolved","description":"Strength.fromShare(paid, depth) grants the 9,000 bps cap once paid >= 1% of depth, where depth is the pool's current in-range liquidity converted at the start price (lines 673-674). Liquidity callbacks are not used, so nothing stops a trader who is also an LP (or who provides JIT liquidity) from calling modifyLiquidity to pull the in-range position, swapping a minimal amount against the remaining dust liquidity to obtain maximum strength, and re-adding the position in the same unlock. The brief's economic rule is that strength is set by the trade's share of the pool; here the share is set at zero cost by the trader. Impact is bounded by the bounty cap (2x the trade's output) but it lets a small bidder play engine moves at full strength (captures, mates) that other bidders must pay 1% of depth for, and in a duel where the attacker is the dominant LP it also makes the opposing team's bids weak relative to theirs. Fix: measure depth from a liquidity value that cannot be changed in the same block (for example the liquidity sampled at block start alongside the price in _sample) or require the bid's size itself, not its share, to set strength.","line":297,"path":"src/IMDuelHook.sol","reproduction":"State: main duel, single LP L holding essentially all in-range liquidity (depth about 1e21 in IMD at a 1:1 price). In one unlock L (a) removes 99.9% of its position so getLiquidity(id) falls to about 1e18, (b) swaps 1e16 IMD -> IMDUEL (exact input, hookData = L), (c) re-adds the position. In (b) beforeSwap reads the reduced liquidity, _trade computes depth about 1e18 and Strength.fromShare(1e16, 1e18) returns 9000 (1% share hits the cap). Expected: a 1e16 trade against the true 1e21 depth is a 0.001% share and plays at 0 bps. Actual: Bid(..., strengthBps = 9000) at no cost beyond gas. (Leaving exactly zero liquidity does not work: fromShare returns 0 for depth 0, and the swap would hit PartialFill.)","severity":"low","snippet":"        uint256 depth = manager.getLiquidity(id); // sets a bid's strength: every buy bids","title":"Bid strength is derived from in-range liquidity read at swap start, so an LP can remove liquidity, trade a tiny amount at the 90% cap, and re-add in the same transaction"},{"citation":"resolved","description":"_reference takes the most hook-favourable of the three latest sampled block-start prices and _swapClaims refuses to buy unless the current price is on the favourable side of 0.1% (sqrt) from it (line 544). When the hook sells IMD (zeroForOne when IMD is currency0) the reference is the maximum. One trade that is the last on the main pool in block B-1 and pushes the sqrt price up by more than 0.1% (selling IMDUEL) makes block B's start sample that spike; the trader reverses it as the first trade of block B. For blocks B, B+1 and B+2 the reference includes the spike, limit = 0.999 * spike > current price, and every batch returns (0, 0): the pile stays, Converted(c, 0, 0, 0) is emitted, and every trade on an IMD-sliced duel still spends gas on the no-op attempt. Repeating this every third block delays conversion (and therefore the IMDUEL chest funding and burn) indefinitely. Cost to the griefer is two trades of about 0.1% of in-range depth each, paying 0.5% hook fee plus 1.25% LP fee, so roughly 0.0035% of depth per three blocks; there is no gain, only denial. Fix: use the median or the oldest of the three samples rather than the extreme, or widen the band when the current price has reverted.","line":574,"path":"src/IMDuelHook.sol","reproduction":"State: main duel with 3 sampled blocks at sqrt price P, pile[IMD] = 1000 IMD >= minImd. Block B-1: griefer sells 0.2% of in-range IMDUEL depth, moving sqrt price to about 1.002P; this is the last main-pool trade in B-1. Block B: griefer buys the IMDUEL back first (price returns to about P); then any call to convert(IMD) or any IMD-sliced trade. Expected per the brief: the batch buys within 0.1% of recent block-start prices. Actual: _reference returns the spike sample, limit = 0.999 * 1.002P > P, line 544 returns (0, 0); the same happens in blocks B+1 and B+2.","severity":"low","snippet":"            if (i == 0 || (zeroForOne ? p > from : p < from)) from = p;","title":"A single end-of-block trade that spikes the main pool's block-start price blocks the IMD conversion batch for the next three blocks: cheap repeated griefing of the protocol slice conversion"},{"citation":"resolved","description":"For an ETH-paired duel the slice is taken in native ETH (afterInitialize sets slice = 0). _play tries convertBatch(ETH) whenever pile[ETH] >= minEth, but _swapClaims on the route requires samples[1]'s newest sample block to be exactly block.number - 1, and sampleRoute can only add a sample for block n+1 from the header of block n with n < block.number - 1 (RoutePriceVerifier.verify lines 24-41). So a batch in block B needs someone to have submitted three consecutive headers (B-4, B-3, B-2) in block B itself, before the trade, and _reference additionally needs three samples. Each submission carries an RLP header plus an eth_getProof account and storage proof. No one is paid for this, the hook holds no funds it can use to pay a keeper, and the ETH-paired duel's traders cannot trigger it through trading. The protocol's promise that 0.1% of every swap is converted into IMDUEL (half to the chest, half burned) therefore does not hold for ETH-paired duels unless a volunteer runs a same-block multicall of three proofs plus convert() for each batch of at most 10 * minEth = 0.2 ETH. The ETH is not lost (it stays in pile[0] as hook-owned claims) but it is idle indefinitely. Fix suggestion: allow the route batch to use the previous block's hash-verified header (B-1 is available through blockhash in block B) so one proof suffices, or pay the sampler a small share of the converted amount.","line":539,"path":"src/IMDuelHook.sol","reproduction":"State: X/ETH duel on the hook with liquidity, 50 trades of 1 ETH each (pile[0] = 0.05 ETH >= minEth 0.02 ETH). Trade 51 at block B with no sampleRoute calls in block B: _play calls convertBatch(ETH); _swapClaims(route) returns (0,0) at line 539, _convert emits Converted(ETH, 0, 0, 0). Expected per the brief: the slice is converted into IMDUEL in batches as trades happen. Actual: pile[0] keeps growing with every trade; only an off-chain party submitting three proofs and convert() in one block moves it.","severity":"low","snippet":"        if (!main && ((s >> 168) & 0xffffffffff) + 1 != block.number) return (0, 0);","title":"ETH slice conversion needs three consecutive route samples with the newest proven in the same block as the batch and has no keeper incentive, so the in-trade conversion of ETH piles never fires and ET"}],"hash":"33c84f5a01ff36a2efb94bd48c7e3c653ac43a4c37f6cbca5a3313a25124bc06","nodeId":"2acb5ca4-7f88-420e-aa09-3a38883c9d50","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds five findings plus coverage rows for all 14 entry points and three invariants.\n\n**What I found (economics area: Economic Security, Invariant, Flow Gap)**\n\n- **Medium, `payOut` guard bypass (src/IMDuelHook.sol:398).** The `Unsettled` check only compares the PoolManager's nonzero-delta count. During `afterSwap` the hook already carries a negative delta from its fee mints, and v4 books the offsetting hook delta only after `afterSwap` returns. A native-ETH reward recipient can call `settleFor{value: 1}(hook)` from its `receive()`, which leaves the count unchanged, so the payout \"succeeds\", the hook ends at +1 wei, and the swap reverts with `CurrencyNotSettled`. Every later swap on that duel repeats the resolve and reverts until the move decays to a timeout after about 8,191 blocks. It applies to any ETH-paired duel, not the main IMDUEL/IMD pool. The author's hostile tests cover a recipient opening a new delta, not one altering the hook's existing delta.\n- **Low, rewards default to the router** when hookData is empty, so bounties land in router contracts and are sweepable.\n- **Low, strength is manipulable in-transaction** by an LP pulling liquidity before a tiny trade and re-adding it.\n- **Low, conversion griefing:** one block-end price spike blocks IMD batches for three blocks at the griefer's own fee cost.\n- **Low, ETH-slice conversion never fires from trades:** it needs three route proofs submitted in the same block as the batch, with no keeper incentive.\n\n**What held:** the 0.5% split and claim accounting net to zero in every path, the claims equal chests plus pile plus owed, the batch price band and once-per-block rule resist profitable manipulation, bounties stay capped at twice the trade's output from the team's own chest, and the token is a plain fixed-supply ERC-20.\n\n**Limitations:** compiling the test suite is OOM-killed under this sandbox's 1.5 GB memory limit (via-IR, 119 files), so no Foundry proof file could be executed; `forge build` of `src/` alone succeeds. All findings rest on traced source and the v4-core accounting order in `lib/v4-core/src/PoolManager.sol`. The route proof verifier and the chess engine internals were outside my area and only skimmed.","treeHash":null,"usage":{"cachedInputTokens":1749191,"inputTokens":452,"model":"claude-fable-5-1","outputTokens":70394,"runtime":"claude","turns":20,"wallClockMs":2032126}},{"artifacts":[],"attempt":1,"bundleHash":"4f2b42ac4a64be8c58faa7008e5e472364c99503d7bebc2365aa42e52c11bd32","device":"c97fd6a73e3737dc","findings":[],"hash":"5c8b6612b65404ecd259a404b38c7b12d2d906c3fa75b8c1502ded3aebed26f0","nodeId":"d947757b-cd59-455c-a66d-10903a8f7978","outcome":"completed","summary":"The committed launch.json already describes the accepted build exactly, so no edit was needed. This revision round listed no findings, and every check I ran against the manifest passed.\n\n**What launch.json declares, verified against the source**\n\n- `\"kind\": \"univ4_hook\"` is the first field, with hook, token, pool and notes as the only other keys.\n- Hook contract is IMDuelHook with nine constructor arguments in declaration order: `$poolManager`, the deployed ChessEngineV5, IMD as the pair currency, the hookless IMD/ETH route pool's fee and spacing, the three conversion minimums, and turnBlocks. No route-hook, owner, factory or launch-number argument exists because the constructor takes none.\n- Permissions are afterInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta and afterSwapReturnDelta, which is exactly what getHookPermissions returns and what the IMDUEL_HOOK_FLAGS constant encodes.\n- Token is IMDuelToken, named IMDuel, symbol IMDUEL, 18 decimals, matching src/IMDuelToken.sol.\n- Pool is paired with IMD at fee 12500, tick spacing 60, initial price one to one, as the launch facts give them.\n- Notes are 3,537 characters, under the 4,000 cap, and cover the fee split, the opening surcharge, the no-owner design, the conversion batching rule, the refused-payout and withdrawTo behavior, and the unchecked engine.\n\n**How I checked it**\n\n- A shape check confirmed lowercase addresses, no stand-in addresses, numeric fee, spacing and decimals, and a string initialPrice below 2^256.\n- A scratch Foundry test deployed IMDuelHook from the manifest's own constructor arguments on a chain where the engine has no code, then compared getHookPermissions, engine, imd, turnBlocks, and the token's name, symbol, decimals and supply against the file. It passed.\n- `forge build` succeeds, and `forge test` with the verifier's fuzz seed passes all 175 repository tests, including the factory-shaped launches in both token orders.\n\nThe working tree is clean. No contract source, constructor argument, compiler setting or configuration file was touched, and the scratch test lives only in test/scratch.","treeHash":"168799c8714eb66267446f0ec44153da00155d10","usage":{"cachedInputTokens":396607,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":7020,"runtime":"claude","turns":10,"wallClockMs":201055}},{"artifacts":[],"attempt":1,"bundleHash":"528bdca5ccd20aee988261da2eb5fbe5d274c0c68a247bd5cca8d73b5ea43e80","device":"d11ea2b5e05fa7a8","findings":[],"hash":"636730a611bebfe067408453c3efe792d2563a26b43ff327bbad36043f62b621","nodeId":"d947757b-cd59-455c-a66d-10903a8f7978","outcome":"completed","summary":"The manifest is written and verified. Only launch.json changed.\n\n**What launch.json now says**\n\n- `\"kind\": \"univ4_hook\"` is the first field.\n- Hook contract IMDuelHook with its nine constructor arguments in declaration order: `$poolManager`, the deployed ChessEngineV5 address, the IMD pair address, the hookless ETH route's fee and spacing, the three batch minimums, and turnBlocks. They are identical to the project's own launch/hook.json with its engine placeholder filled in, and the engine address matches launch/deployed.json.\n- Permissions are afterInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta and afterSwapReturnDelta, exactly what getHookPermissions returns in the source.\n- Token IMDuelToken, name IMDuel, symbol IMDUEL, 18 decimals. I dropped the previous file's token constructorArgs and totalSupply entries, since neither is a schema field and supply is set by the launch policy.\n- Pool: pair 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7, fee 12500, tick spacing 60, initialPrice 79228162514264337593543950336, as the launch facts give them.\n- Notes were rewritten to describe the tree as it stands after the accepted audit fixes, at 3,537 characters. They cover the 0.5% hook fee split and burn, the 300-second opening surcharge on the main duel only, the batch pricing against the three latest sampled block-start prices and the once-per-block limit, the proof-based sampleRoute for the ETH route, the engine not being checked at construction, bid ownership from hookData or the swap sender, and payouts refused when the recipient touches the PoolManager's deltas and left owed for withdraw or withdrawTo. They also state there is no owner, admin, pause, upgrade path or signer, so no `$owner` or `$factory` argument exists.\n\n**Checks run**\n\n- forge build succeeds, with only pre-existing lint warnings.\n- forge test with the verifier's fuzz seed: 171 tests passed, 0 failed across 23 suites, including the launch-file, admission and factory-shaped launch tests in both token orders.\n- A node check confirmed the constructor arguments, permissions and pool block match launch/hook.json and that all addresses are lowercase.\n\nThe file is left as an uncommitted change in the working tree, ready for the submission step.","treeHash":"c77a218e774a5b6bedef86ce6208f59b37100d8f","usage":{"cachedInputTokens":628077,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":8520,"runtime":"claude","turns":12,"wallClockMs":367254}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"c7b1db4d25939bd5","findings":[{"citation":"resolved","description":"Kept from the tester (write_foundry_tests 2dc17a1e), reproduced with its proof on the revised code. payOut (lines 397-407) now snapshots the nonzero-delta count and the hook's own deltas in the pool's two currencies, which closes the settleFor(hook) bypass I reported last round. It still does not look at the PoolManager's transient synced currency (TransientStateLibrary.getSyncedCurrency / getSyncedReserves). A reward recipient paid in native ETH gets execution in receive() while the manager is unlocked; if it only calls manager.sync(IMD) no delta moves, so all three checks pass, _pay records paid=true and Reward(paid=true) is emitted. The token stays synced for the rest of the unlock. When the trade that played the move pays in native ETH, the router then settles with manager.settle{value: x}() without syncing first (v4-core CurrencySettler, PoolSwapTest, V4Router's native settle all do this), and PoolManager._settle reverts NonzeroNativeValue because the synced currency is IMD, not native. The whole trade reverts, and every later ETH-paying trade of the duel replays the same resolve and payout and reverts the same way until an IMD-paying trade (whose router re-syncs before settling) plays the move. The owner can repeat it for every move its bids own. The same root cause also hits an integrator that syncs and transfers its input before the swap and settles after: the recipient's re-sync resets the reserves to the already-transferred balance, so that settle() credits 0 and the trade ends CurrencyNotSettled. Exposure: ETH-paired duels on the hook (ETH/IMD, ETH/IMDUEL, ETH/X), which afterInitialize accepts from anyone, and callback-token duels; the launch's main duel (two plain ERC-20s) gives a recipient no execution during take(). No funds are lost and token-paying trades still play the move (test/PayoutGuard.t.sol test_aRecipientThatSyncsIsSettledAroundWhenTheTradePaysInTheToken), so this is a liveness defect of the guard the brief describes ('a reward payout whose recipient touches the PoolManager's deltas is refused and left owed'), medium. The author's own test comment at PayoutGuard.t.sol lines 149-152 acknowledges the ETH-paying case reverts. Fix: in payOut also snapshot manager.getSyncedCurrency() and manager.getSyncedReserves() before _send and revert Unsettled if either differs after it (the reward then stays owed and withdrawTo collects it); or stop pushing rewards to contract recipients inside the trade.","line":397,"path":"src/IMDuelHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {PoolSwapTest} from \"v4-core/src/test/PoolSwapTest.sol\";\nimport {PoolModifyLiquidityTest} from \"v4-core/src/test/PoolModifyLiquidityTest.sol\";\nimport {Currency, CurrencyLibrary} from \"v4-core/src/types/Currency.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {PoolId, PoolIdLibrary} from \"v4-core/src/types/PoolId.sol\";\nimport {SwapParams, ModifyLiquidityParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {MockERC20} from \"solmate/src/test/utils/mocks/MockERC20.sol\";\nimport {IChessEngine} from \"src/IChessEngine.sol\";\nimport {IMDuelHook, IMDUEL_HOOK_FLAGS} from \"src/IMDuelHook.sol\";\n\n/// The real hook plus one test-only setter, to start from a position where the side to move must capture a queen.\ncontract PositionHook is IMDuelHook {\n    constructor(IPoolManager m, IChessEngine e, Currency imd_)\n        IMDuelHook(m, e, imd_, 0, 0, type(uint256).max, type(uint256).max, type(uint256).max, 1)\n    {}\n\n    function setPosition(PoolId id, uint256 board, uint32 white, uint32 black) external {\n        Game storage g = games[id];\n        (g.board, g.whiteState, g.blackState, g.ply, g.quiet) = (board, white, black, 0, 0);\n    }\n}\n\n/// A move owner whose receive() only calls manager.sync(token): no delta moves, so payOut accepts the payout.\ncontract SyncingOwner {\n    IPoolManager immutable manager;\n    Currency immutable token;\n\n    constructor(IPoolManager m, Currency t) {\n        (manager, token) = (m, t);\n    }\n\n    receive() external payable {\n        manager.sync(token);\n    }\n}\n\n/// Finding: payOut's guard compares delta counts and the hook's own deltas, but not the PoolManager's synced\n/// currency. A recipient that only syncs a token leaves it synced; the router's native settle{value}() that follows\n/// in the same unlock then reverts NonzeroNativeValue, so the trade that plays the move reverts, and so does every\n/// ETH-paying trade of the duel while that move is pending (each replays the same payout).\n/// Expected: the trade goes through, the move is played and the bounty is paid or left owed.\ncontract ProofSyncRecipientTest is Test {\n    using PoolIdLibrary for PoolKey;\n    using CurrencyLibrary for Currency;\n\n    IPoolManager manager;\n    PoolSwapTest router;\n    PositionHook hook;\n    PoolKey key;\n    PoolId id;\n    MockERC20 imd;\n\n    function sq(string memory s) internal pure returns (uint256) {\n        bytes memory b = bytes(s);\n        return uint256(uint8(b[0]) - 97) + 8 * uint256(uint8(b[1]) - 49);\n    }\n\n    function put(uint256 board, string memory s, uint256 piece) internal pure returns (uint256) {\n        return board | (piece << (sq(s) << 2));\n    }\n\n    function side(string memory k) internal pure returns (uint32) {\n        return 0x80800000 | (uint32(sq(k)) << 8) | 0xff;\n    }\n\n    function swap(bool zeroForOne, uint256 amountIn, bytes memory data) internal {\n        router.swap{value: zeroForOne ? amountIn : 0}(\n            key,\n            SwapParams(\n                zeroForOne, -int256(amountIn), zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\n            ),\n            PoolSwapTest.TestSettings(false, false),\n            data\n        );\n    }\n\n    function setUp() public {\n        manager = IPoolManager(vm.deployCode(\"PoolManager.sol:PoolManager\", abi.encode(address(this))));\n        router = PoolSwapTest(vm.deployCode(\"PoolSwapTest.sol:PoolSwapTest\", abi.encode(manager)));\n        PoolModifyLiquidityTest lp = PoolModifyLiquidityTest(\n            vm.deployCode(\"PoolModifyLiquidityTest.sol:PoolModifyLiquidityTest\", abi.encode(manager))\n        );\n        IChessEngine engine = IChessEngine(vm.deployCode(\"ChessEngineV5.sol:ChessEngineV5\"));\n        imd = new MockERC20(\"IMD\", \"IMD\", 18);\n        MockERC20 imduel = new MockERC20(\"IMDUEL\", \"IMDUEL\", 18);\n        address at = address(IMDUEL_HOOK_FLAGS | (uint160(0x1D0E1) << 40));\n        deployCodeTo(\n            \"Proof_SyncRecipient.t.sol:PositionHook\", abi.encode(manager, engine, Currency.wrap(address(imd))), at\n        );\n        hook = PositionHook(payable(at));\n        // the main duel (IMD / IMDUEL) first, then a native ETH / IMD duel\n        (address a, address b) =\n            address(imd) < address(imduel) ? (address(imd), address(imduel)) : (address(imduel), address(imd));\n        manager.initialize(PoolKey(Currency.wrap(a), Currency.wrap(b), 3000, 60, IHooks(at)), 2 ** 96);\n        key = PoolKey(CurrencyLibrary.ADDRESS_ZERO, Currency.wrap(address(imd)), 3000, 60, IHooks(at));\n        id = key.toId();\n        manager.initialize(key, 2 ** 96);\n\n        imd.mint(address(this), 1e30);\n        imd.approve(address(router), type(uint256).max);\n        imd.approve(address(lp), type(uint256).max);\n        vm.deal(address(this), 1e30);\n        lp.modifyLiquidity{value: 1e27}(key, ModifyLiquidityParams(-887220, 887220, 1e24, 0), \"\");\n        hook.fundChest{value: 10 ether}(key, false, 10 ether);\n\n        // ETH's team is White in game 0 and is in check from an unprotected queen: Kxe2 is its only move\n        uint256 board;\n        board = put(board, \"e1\", 6);\n        board = put(board, \"h1\", 4);\n        board = put(board, \"e2\", 13);\n        board = put(board, \"a8\", 14);\n        board = put(board, \"a7\", 9);\n        hook.setPosition(id, board, side(\"e1\"), side(\"a8\"));\n    }\n\n    function test_anEthPayingTradeThatPaysASyncingOwnerGoesThrough() public {\n        SyncingOwner r = new SyncingOwner(manager, Currency.wrap(address(imd)));\n        swap(false, 1e18, abi.encodePacked(address(r))); // IMD in: buys ETH, bids for White's move for r\n        vm.roll(block.number + 1);\n        vm.warp(block.timestamp + 12);\n        // ETH in: the first trade after the window plays White's move and pays r its bounty. Today it reverts\n        // NonzeroNativeValue: r's sync(IMD) is still in place when the router settles ETH with value.\n        swap(true, 1e18, \"\");\n        assertEq(hook.game(id).ply, 1, \"the move is played\");\n        assertGt(address(r).balance + hook.owed(address(r), 0), 0, \"the bounty is paid or owed\");\n    }\n\n    receive() external payable {}\n}","reproduction":"State: native ETH / IMD duel on the hook (currency0 = ETH, currency1 = IMD, fee 3000, spacing 60) initialized after the main duel, full-range liquidity 1e24, ETH chest funded with 10 ether via fundChest{value: 10 ether}(key, false, 10 ether); position K e1, R h1 vs Q e2, K a8, P a7 with ETH's team (White in game 0) to move, so Kxe2 is forced (queen capture, 270 bps). Recipient contract r: receive() { manager.sync(IMD); }. Calls: (1) PoolSwapTest.swap(key, zeroForOne=false, amountSpecified=-1e18, hookData=abi.encodePacked(address(r))) -> r owns White's bid. (2) vm.roll(+1), vm.warp(+12): PoolSwapTest.swap{value: 1e18}(key, zeroForOne=true, -1e18, hookData=''). Expected: the move is played (game(id).ply == 1) and 0.27 ETH is paid to r or left in owed[r][0]. Actual: the hook emits Reward(..., 2.7e17, paid=true), then PoolSwapTest's manager.settle{value: 1e18}() reverts NonzeroNativeValue() and the whole swap reverts; ply stays 0, and every later ETH-in swap of the duel reverts identically. Run: forge test --match-path test/scratch/Proof_SyncRecipient.t.sol -> test_anEthPayingTradeThatPaysASyncingOwnerGoesThrough fails with NonzeroNativeValue() on the current code (run here on the revised tree).","severity":"medium","snippet":"    function payOut(PoolKey calldata key, Currency c, address to, uint256 amount) external {\n        _onlySelf();\n        uint256 open = manager.getNonzeroDeltaCount();\n        int256 own0 = manager.currencyDelta(address(this), key.currency0);\n        int256 own1 = manager.currencyDelta(address(this), key.currency1);\n        _send(c, to, amount);","title":"payOut's guard ignores the PoolManager's synced currency: a move owner whose receive() only calls sync(token) is paid, and the ETH-paying trade that plays its move reverts NonzeroNativeValue, as does "},{"citation":"resolved","description":"My earlier low b3605770 (from audit_economics 6f80a7e3). The author disputes the fix, not the trace, and the trace still holds on the revised code: beforeSwap stashes manager.getLiquidity(id) at line 297, _trade (lines 683-685) turns it into a depth and Strength.fromShare(paid, depth) returns the 9,000 bps cap once paid >= 1% of depth; no liquidity callback exists to notice a position moving inside the block. I accept the author's reasoning: sampling liquidity at block start is set by the first trade of the block inside the same unlock, a liquidity callback changes getHookPermissions() and the manifest's permission list which this assignment fixes, and sizing strength by amount rather than share replaces the brief's rule. Impact is bounded by PAYOUT_CAP (a bounty is at most 2x the trade's own output) and needs a party that already owns the pool's in-range depth. Recorded in ADAPTATION.md as an accepted limitation; downgraded to info so it does not hold the launch. Nothing to fix in this round.","line":297,"path":"src/IMDuelHook.sol","reproduction":"Main duel at 1:1 with one LP L holding getLiquidity(id) = 1e21. In one unlock L: (a) modifyLiquidity(pool, -0.999e21) so getLiquidity(id) is about 1e18; (b) manager.swap(pool, zeroForOne=true, -1e16, hookData=abi.encodePacked(L)); (c) modifyLiquidity(pool, +0.999e21); settle. In (b) beforeSwap stashes liquidity about 1e18, Strength.fromShare(1e16, 1e18) returns 9000. Expected under the brief's share rule: a 1e16 trade against the true 1e21 depth plays at 0 bps. Actual: Bid(..., strengthBps = 9000). Unchanged from last round; documented as accepted.","severity":"info","snippet":"        uint256 depth = manager.getLiquidity(id); // sets a bid's strength: every buy bids","title":"Accepted limitation (settled, no change requested): bid strength is read from in-range liquidity at swap start, so the dominant LP can pull its position, trade dust at the 90% cap and re-add it in the"},{"citation":"resolved","description":"My earlier low 573a4330 (from audit_economics d024c614). The trace still holds on the revised code: _reference (lines 578-584) takes the most hook-favourable of the three latest block-start samples (the maximum when the hook sells currency0), _swapClaims sets limit = 0.999 * from and returns (0, 0) when limit >= sqrtPrice (lines 550-552), so a spike that is the last trade of block B-1 and is reversed first in block B sits in the window for three sampled blocks and each batch in them is skipped. The author keeps this on purpose: the batch must only buy within 0.1% of a price the pool held across a public block boundary, and a median would let a pusher poison two of three samples by holding a pushed price through one public block. That is a sound trade-off under the brief ('batches price against the pool's prices at the start of its three latest sampled blocks'). The grief only delays conversion (the pile stays as ERC-6909 claims), costs the griefer the 0.5% hook fee and 1.25% LP fee on two trades every third sampled block, and each attempt is bounded by CONVERT_GAS. Recorded in ADAPTATION.md; downgraded to info so it does not hold the launch.","line":582,"path":"src/IMDuelHook.sol","reproduction":"Main duel (IMD = currency0) with three sampled blocks at sqrt price P and pile[IMD] = 1000e18 >= minImd. Block B-1: griefer sells 0.2% of in-range IMDUEL depth as the last main-pool trade, sqrt price about 1.002*P. Block B: griefer buys it back as the first trade (beforeSwap samples 1.002*P as B's start price); then convert(IMD). Expected per a median rule: the batch runs near P. Actual by design: _reference returns 1.002*P, limit = 1.001*P > P, line 552 returns (0, 0), Converted(IMD, 0, 0, 0); the same in the next two sampled blocks. Unchanged from last round; documented as accepted.","severity":"info","snippet":"        for (uint256 i; i < 3; i++) {\n            uint256 p = _unpack((s >> (i * SAMPLE_BITS)) & ((1 << SAMPLE_BITS) - 1));\n            if (i == 0 || (zeroForOne ? p > from : p < from)) from = p;\n        }","title":"Accepted limitation (settled, no change requested): one end-of-block spike of the main pool's block-start price, reversed next block, makes the IMD conversion batch return (0, 0) for the next three sa"}],"hash":"7c51bf89f5535b44c5f86583f26fcbabed09088db7e59b771d87cbc6486c2f4f","nodeId":"b8c57dea-cd28-402c-925e-162dd981f86e","outcome":"completed","summary":"The review is written to `.imd-findings.json` and the working tree is unchanged apart from that file.\n\n**Settled from last round**\n\n- **payOut settleFor bypass (medium): fixed.** The guard now snapshots the hook's own deltas in both pool currencies beside the nonzero-delta count. The author's four Hostile tests and the PayoutGuard fuzz test exercise exactly my scenario, and the full suite passes at 201 of 201.\n- **Router-owned bids (medium): fixed.** A trade without a 20-byte or clean 32-byte recipient now bids for nobody and its move pays no bounty. My attached proof passes on the revised code, and the nobody owner flows cleanly through resolve, apply and payout.\n- **Two disputed lows (liquidity-at-swap-start strength, end-of-block spike delaying conversion):** the traces still hold, but the author's reasons not to change them are sound within the fixed permission set. Both are documented in ADAPTATION.md. I kept them as info notes so they no longer hold the launch.\n\n**One open finding, medium, with proof**\n\nThe tester's new finding reproduces: `payOut` checks the delta count and the hook's own deltas but not the PoolManager's synced currency. A reward recipient whose receive() only calls `sync(IMD)` is paid, then the ETH-paying trade that played its move reverts with `NonzeroNativeValue` when the router settles native ETH. Every later ETH-paying trade of that duel reverts the same way until a token-paying trade plays the move. The author's own PayoutGuard test comment acknowledges this case. Only ETH-paired and callback-token duels are exposed, not the main duel, and no funds are lost. The suggested fix is to snapshot the synced currency and reserves in payOut and refuse the payout if either changed.\n\n**Coverage:** all 14 entry points answered, two marked `finding` (afterSwap and payOut, both pointing at finding 1), the rest `holds`, plus rows recording the four settled items and the manifest check.","treeHash":null,"usage":{"cachedInputTokens":858630,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":18271,"runtime":"claude","turns":20,"wallClockMs":486782}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"b4f6137e7c93a2b3","findings":[{"citation":"resolved","description":"Kept from the tester (write_foundry_tests af7d7190), reproduced on the revised code. payOut (lines 400-414) now compares the nonzero-delta count, the hook's own deltas in the pool's two currencies and the PoolManager's synced currency and reserves around _send. None of these moves when the recipient settles 1 wei for a third address whose delta is already nonzero, because that delta never crosses zero: manager.settleFor{value: 1}(router) from the recipient's receive() leaves the count, the hook's deltas (the router is not the hook) and the synced state (native settle touches neither) unchanged. The swap's own router has such a delta while afterSwap runs whenever it paid its input before calling swap (v4 allows it: settle{value} or sync/transfer/settle before the swap; PoolManager accounts the swap's delta to the router only after afterSwap returns) or when it is in the middle of a multi-hop route through the payout currency (the first hop's output is an open positive delta during the second hop's afterSwap). The payout is then accepted, Reward(paid=true) is emitted, and the router ends its unlock 1 wei in credit; a router that settles and takes exact amounts (PrepaidRouter in test/PayoutGuard.t.sol, or any Universal Router route that does not TAKE the intermediate currency) reverts CurrencyNotSettled. The brief's guarantee, 'a reward payout whose recipient touches the PoolManager's deltas is refused and left owed', does not hold for that recipient. Impact is bounded: the owner can revert only trades through routers that hold an open delta in the payout currency while the owner's move is pending, the wei it spends is refunded by the revert, and the first trade through a router with no open delta (PoolSwapTest, a Universal Router single-hop with SETTLE after SWAP) plays the move with the reward refused and owed, so the duel is not frozen and no funds are lost: targeted griefing, low. Fix that keeps the design: payOut also snapshots manager.currencyDelta(sender, key.currency0 / key.currency1) for the swap's sender (afterSwap receives it; pass it through _play/_pay) and reverts Unsettled if either changed; in a trade only the hook and the router can hold open deltas, so this closes the remaining path. Alternatively refuse any in-trade payout while the router holds a nonzero delta in the payout currency (leave it owed).","line":408,"path":"src/IMDuelHook.sol","reproduction":"Fixture of test/PayoutGuard.t.sol (PayoutGuardTest.setUp): native ETH / IMD duel on the hook (currency0 = ETH, currency1 = q, fee 3000, spacing 60), full-range liquidity 1e24, ETH chest 10 ether, position K e1, R h1 vs Q e2, K a8, P a7 with ETH's team (White, game 0) to move so Kxe2 (queen capture, 270 bps) is forced; ProbeOwner r with mode 3 = receive() { sync(ETH); settleFor{value: amount}(target); }; PrepaidRouter p (test/PayoutGuard.t.sol) mode 2 = settle{value: amountIn}() before swap, then take(cout, exact output). Calls: (1) white(1e18, address(r)) -> r owns White's bid; (2) next(12); r.set(3, address(p), 1, ETH); vm.deal(p, 10 ether); p.swap(pool, zeroForOne=true, 1e18, mode=2, ''). Expected: the payout is refused (Unsettled), owed[r][0] == 0.27 ether, the trade settles and game(id).ply == 1 (what happens when r settles for PoolSwapTest, whose delta is zero: test_aRecipientSettlingForTheRouterIsRefused passes). Actual: Reward(paid=true), p ends the unlock with +1 wei ETH and the swap reverts CurrencyNotSettled(); ply stays 0; a retry 5 blocks later reverts the same way; a PoolSwapTest trade (black(1e18, address(0))) then plays the move with the reward refused and owed. Run on this tree: copy the probe below to test/scratch/Probe_SettleForRouter.t.sol and run forge test --match-path test/scratch/Probe_SettleForRouter.t.sol: test_probe_settleForAPrepaidRouter fails with CurrencyNotSettled() and test_probe_impact passes (observed here, 11 inherited fixture tests pass beside them). The probe imports the PayoutGuard fixture, so it is a reproduction, not a self-contained proof.\n\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Vm} from \"forge-std/Test.sol\";\nimport {Currency, CurrencyLibrary} from \"v4-core/src/types/Currency.sol\";\nimport {PayoutGuardTest, PrepaidRouter} from \"../PayoutGuard.t.sol\";\n\ncontract ProbeTest is PayoutGuardTest {\n    using CurrencyLibrary for Currency;\n\n    function test_probe_settleForAPrepaidRouter() public {\n        PrepaidRouter p = new PrepaidRouter(manager);\n        vm.deal(address(p), 10 ether);\n        white(1e18, address(r));\n        next(12);\n        r.set(3, address(p), 1, ETH);\n        p.swap(pool, true, 1e18, 2, \"\");\n        assertEq(st().ply, 1);\n        assertGt(hook.owed(address(r), ETH.toId()), 0, \"refused and owed\");\n    }\n}\n\ncontract ProbeImpactTest is PayoutGuardTest {\n    using CurrencyLibrary for Currency;\n\n    function test_probe_impact() public {\n        PrepaidRouter p = new PrepaidRouter(manager);\n        vm.deal(address(p), 10 ether);\n        white(1e18, address(r));\n        next(12);\n        r.set(3, address(p), 1, ETH);\n        vm.expectRevert();\n        p.swap(pool, true, 1e18, 2, \"\");\n        assertEq(st().ply, 0, \"not played: the prepaid trade reverted\");\n        vm.roll(vm.getBlockNumber() + 5);\n        vm.expectRevert();\n        p.swap(pool, true, 1e18, 2, \"\");\n        black(1e18, address(0));\n        assertEq(st().ply, 1);\n        assertGt(hook.owed(address(r), ETH.toId()), 0);\n    }\n}\n","severity":"low","snippet":"        if (\n            manager.getNonzeroDeltaCount() != open || manager.currencyDelta(address(this), key.currency0) != own0\n                || manager.currencyDelta(address(this), key.currency1) != own1\n                || Currency.unwrap(manager.getSyncedCurrency()) != Currency.unwrap(synced)\n                || manager.getSyncedReserves() != reserves\n        ) revert Unsettled();","title":"payOut's guard misses a recipient that settles for an address whose delta is already nonzero (the swap's router when it prepaid its input or is mid-route): the payout is accepted and the trade that pl"},{"citation":"resolved","description":"My earlier advisory 4444c6ea (from audit_economics 6f80a7e3), unchanged on this tree: beforeSwap stashes manager.getLiquidity(id) at line 297, _trade (lines 685-693) turns it into a depth and Strength.fromShare(paid, depth) returns the 9,000 bps cap once paid >= 1% of depth; no liquidity callback exists to notice a position moving inside the block. The author keeps this on purpose (a liquidity callback changes getHookPermissions() and the manifest's fixed permission list; sizing strength by amount replaces the brief's share rule), impact is bounded by PAYOUT_CAP (a bounty is at most 2x the trade's own output) and needs a party that already owns the pool's in-range depth. Recorded in ADAPTATION.md; info, nothing to fix.","line":297,"path":"src/IMDuelHook.sol","reproduction":"Main duel at 1:1 with one LP L holding getLiquidity(id) = 1e21. In one unlock L: (a) modifyLiquidity(pool, -0.999e21) so getLiquidity(id) is about 1e18; (b) manager.swap(pool, zeroForOne=true, -1e16, hookData=abi.encodePacked(L)); (c) modifyLiquidity(pool, +0.999e21); settle. In (b) beforeSwap stashes liquidity about 1e18, Strength.fromShare(1e16, 1e18) returns 9000. Expected under a pure share rule: a 1e16 trade against the true 1e21 depth plays at 0 bps. Actual: Bid(..., strengthBps = 9000). Accepted.","severity":"info","snippet":"        uint256 depth = manager.getLiquidity(id); // sets a bid's strength: every buy bids","title":"Accepted limitation (settled, no change requested): bid strength is read from in-range liquidity at swap start, so the dominant LP can pull its position, trade dust at the 90% cap and re-add it in the"},{"citation":"resolved","description":"My earlier advisory 8ae96ff6 (from audit_economics d024c614), unchanged on this tree: _reference (lines 585-591) takes the most hook-favourable of the three latest block-start samples, _swapClaims sets limit = 0.999 * from and returns (0, 0) when limit >= sqrtPrice (lines 557-559), so a spike that is the last trade of block B-1 and is reversed first in block B sits in the window for three sampled blocks and each batch in them is skipped. The author keeps it by design (a batch must only buy within 0.1% of a price the pool held across a public block boundary; a median would let a pusher poison two of three samples). The grief only delays conversion, the pile stays as ERC-6909 claims, and each attempt costs the griefer two fee-bearing trades. Recorded in ADAPTATION.md; info, nothing to fix.","line":589,"path":"src/IMDuelHook.sol","reproduction":"Main duel (IMD = currency0) with three sampled blocks at sqrt price P and pile[IMD] = 1000e18 >= minImd. Block B-1: griefer sells 0.2% of in-range IMDUEL depth as the last main-pool trade, sqrt price about 1.002*P. Block B: griefer buys it back as the first trade (beforeSwap samples 1.002*P as B's start price); then convert(IMD). Expected per a median rule: the batch runs near P. Actual by design: _reference returns 1.002*P, limit = 1.001*P > P, line 559 returns (0, 0), Converted(IMD, 0, 0, 0); the same in the next two sampled blocks. Accepted.","severity":"info","snippet":"            if (i == 0 || (zeroForOne ? p > from : p < from)) from = p;","title":"Accepted limitation (settled, no change requested): one end-of-block spike of the main pool's block-start price, reversed next block, makes the IMD conversion batch return (0, 0) for the next three sa"}],"hash":"9721a4796e2e35e21049654047639d5ebcbeaba2e2c7f7b9313e616f975c064a","nodeId":"b8c57dea-cd28-402c-925e-162dd981f86e","outcome":"completed","summary":"The review is written to `.imd-findings.json` and the scratch tests are removed. The working tree is clean apart from the findings file.\n\n**Outcome of the round**\n\n- **My earlier medium (payOut ignored the synced currency) is fixed.** The author's dispute of the proof was right: it failed in its own setup because of the artifact name. With the harness etched from its own creation code, as the author described, the proof passes on the revised code, and the new Hostile and PayoutGuard sync tests pass too. Dropped.\n- **One low finding kept, from the tester.** The guard still accepts a recipient that settles 1 wei for an address whose delta is already nonzero. The swap's router has one when it prepaid its input or is mid-route through the payout currency. The trade that plays the move then reverts with CurrencyNotSettled. I reproduced it with the tester's probe on the PayoutGuard fixture. No funds are lost and the next plain trade plays the move with the reward owed, so it stays low. The suggested fix snapshots the swap sender's deltas in the pool's two currencies as well.\n- **Specialist findings dropped as already fixed.** The settleFor(hook) bypass and the router-owned bid both describe the tree before this revision. payOut now compares the hook's own deltas, and a bid without hookData is nobody's. BidOwner and Hostile tests confirm both.\n- **Other specialist leads dropped.** Zero-address hookData now matches the documented \"nobody\" design. The dust-fee and uint160 notes are not defects. The ETH route needing a keeper is stated in the manifest notes as the intended behaviour.\n- **Two info rows carried over** as accepted limitations: strength read from swap-start liquidity, and the end-of-block price spike delaying IMD batches.\n\n**Verification on this tree**\n\n| Check | Result |\n|---|---|\n| forge build | passes |\n| forge test | 210 passed, 0 failed |\n| Hook creation / runtime size | 28,102 / 21,322 bytes |\n| Manifest | 9 constructor args in order, permissions match, fee 12500, notes 3,537 chars |\n\nCoverage answers all 14 entry points: 12 hold, afterSwap and payOut carry the low finding.","treeHash":null,"usage":{"cachedInputTokens":813807,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":31834,"runtime":"claude","turns":23,"wallClockMs":563066}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"e12f98dda6acc55f","findings":[{"citation":"resolved","description":"The hook prices a conversion batch against the most favourable of the pool's prices at the start of its three latest sampled blocks. For the main duel this holds, because every swap of that pool runs beforeSwap, which samples slot0 before the swap moves it, so the first observation in a block is the block-start price. The IMD / native ETH route pool is hookless: the hook only observes it inside _swapClaims (lines 496-498), which runs when an ETH batch is attempted, and convert(address(0)) lets anyone attempt one at any moment. The 'block start' sample of the route is therefore whatever the route's price is at the instant the attacker's own transaction calls convert, after the attacker has moved it and before the attacker moves it back. Three such transactions in three consecutive blocks give three samples at the pushed price; the honest sample taken by a trade's own batch attempt is rotated out (the samples word keeps only three). In the third block the batch executes immediately in the same call, within 0.1% of the pushed reference, and buys IMD from a narrow IMD band the attacker placed just under the pushed price. The attacker never holds the pushed price across a block boundary, so arbitrage cannot touch it, contrary to the note that 'a pushed price would have to be held through two public blocks'. Loss is the ETH protocol slice (up to one batch, 10 * minEth = 0.2 ETH on the launch configuration, per block while an ETH pile lasts), profitable whenever the batch times the price deviation exceeds the LP fees of pushing the route there and back, i.e. whenever the IMD/ETH pool is thin relative to the batch. Preconditions: some ETH-paired duel has traded (anyone may open one against any token) so pile[0] >= minEth, and no honest ETH batch samples the route between the attacker's three blocks. Who loses: the protocol slice (the main duel's IMDUEL chest and the burn). Fix direction: do not let a caller-triggered batch both record the newest route sample and execute on it; require the three route samples to come from three distinct blocks none of which is the batch's own, bound the route price by the main duel's own IMD price as a second reference, or drop the ETH route.","line":498,"path":"src/IMDuelHook.sol","reproduction":"Setup (test/scratch/RouteSample.t.sol): hook with imd=IMD, ethFee=3000, ethSpacing=60, minEth=0.1e18 (batch 1 ETH), engine with no code; main duel IMD/IMDUEL with 1e24 liquidity; hookless ETH/IMD route at 1:1 with only 1e19 liquidity (about 10 ETH deep); ETH/X duel on the hook with 1e24 liquidity. 1) An honest 1,000 ETH buy in the ETH/X duel piles 1 ETH of slice; its own batch attempt takes the route's first sample at the honest price (Converted spent 0). 2) Attacker, in each of three consecutive blocks, in one transaction: swap ETH->IMD on the route to sqrtPrice 0.7x (about 4.3 ETH in), call hook.convert(address(0)), swap IMD->ETH back to the honest price. In the third block it also adds 1e22 of IMD-only liquidity in the 60-tick band just under the pushed tick before convert and removes it after. Expected: the route's price at the start of every block was the honest one (asserted), so no batch should execute 30% away from it. Actual: the third convert emits Converted(ETH, spent 1e18, bought 0.4867e18 IMDUEL, ...): the hook sold its whole 1 ETH pile at the pushed price; the attacker ends 0.446 ETH-equivalent richer (ETH + IMD at the honest 1:1 price) after six route swaps' fees. forge test --match-path test/scratch/RouteSample.t.sol fails with 'a batch priced against block-start prices cannot be sold a pushed price: 446010277962027612 > 0'.","severity":"medium","snippet":"        uint256 s = _sample(pool, sqrtPrice);","title":"IMD/ETH route pool is sampled at batch time inside the caller's own transaction, so an ETH batch can be sold a pushed price (the stated three-block-start guarantee does not hold for the route)"},{"citation":"resolved","description":"A bid's owner, who is paid the move's bounty from the team chest, is the address in hookData when it is exactly 20 bytes, else tx.origin. Any swap whose router does not forward a 20-byte hookData credits whoever signed the outer transaction. For an ERC-4337 smart account that is the bundler's EOA; for a relayed or sponsored transaction it is the relayer; for a router that abi.encodes the user address (32 bytes) it is still tx.origin. The trader paid the swap and its 0.5% hook fee but the reward, up to 2x the trade's output, is paid to a third party. This is the documented default ('the signing wallet') and the author's tests assert it (test/Launch.t.sol:80), so it is reported as a design risk for the adapter to decide on, not as a bypass; identifying users by tx.origin is the pattern the uniswap-v4-security reference forbids. Fix direction: pay bounties only to an owner named in hookData, else to the swap's sender argument (the router, which can withdrawTo), or leave the move unowned (a timeout move) when no owner is named.","line":613,"path":"src/IMDuelHook.sol","reproduction":"State: main duel open with liquidity. Call PoolSwapTest.swap (or any router) buying the team to move with hookData = \"\" from a contract wallet W in a transaction sent by EOA B (tx.origin = B). Then hook.game(id).bids[team].owner == B, not W (test/Launch.t.sol:80 asserts bid().owner == tx.origin for a swap with empty hookData). When the move resolves with a capture, Reward(..., owner = B, ...) is emitted and B's balance of the team token grows; W, which paid the swap and the fee, receives nothing.","severity":"low","snippet":"        address owner = hookData.length == 20 ? address(bytes20(hookData)) : tx.origin;","title":"Move ownership defaults to tx.origin when hookData is not exactly 20 bytes: bounties of swaps relayed through smart accounts, bundlers or aggregator relayers go to the relayer"},{"citation":"resolved","description":"Read in full: src/IMDuelHook.sol (every external and public state-changing function: afterInitialize, beforeSwap, afterSwap, payOut, withdraw, withdrawTo, fundChest, convert, unlockCallback, convertBatch, and the internal fee, chest, conversion, sampling, bidding, resolve and payout paths), src/IMDuelToken.sol, src/Strength.sol, src/Rules.sol, src/IChessEngine.sol, launch.json, launch/hook.json, launch/engine.json, launch/deployed.json, test/Base.sol, test/Harness.sol, test/V4Fixture.sol, test/Tokens.sol, test/Slice.t.sol, test/Launch.t.sol, test/Hostile.t.sol, and the pinned protected tests and references. Checked: forge build succeeds; the hook's runtime is 19,442 bytes (under 24,576); forge test passes 142 tests including Launch.t.sol's factory-shaped launches in both token orders; getHookPermissions matches launch.json's five permissions and IMDUEL_HOOK_FLAGS; the nine constructorArgs match the constructor's declaration order and types; the route pool id in the notes (0xb07d64...) is the keccak of PoolKey(ETH, IMD, 10000, 200, no hook); no owner, pause, upgrade, delegatecall, selfdestruct or signer in the hook or token; fee claims are minted, never taken from the manager's balance during a swap; chests + pile + owed equals the hook's ERC-6909 claims on every path read; callbacks refuse callers other than the PoolManager; the factory's zero-liquidity fee collect runs no hook callback; a payout recipient that leaves a delta is refused and left owed; the main duel's block-start sampling cannot be produced inside a caller's transaction (every price change of that pool goes through beforeSwap or the hook's own pre-sampled batch). Not reached within budget: src/Bitboard.sol, src/Chess.sol and src/ChessEngineV5.sol (chess move generation, legality, repetition keys and engine search, about 1,600 lines relied on through Rules.Played and trusted by the hook by design as the already deployed engine's output), and the protected OwnerChecks helper referenced by Owners.protected.t.sol, which is not in the tree. No helpers were used; the twelve specialty passes were made by a single reviewer.","line":43,"path":"src/IMDuelHook.sol","reproduction":"Not a defect: the coverage statement the acceptance criteria require.","severity":"info","snippet":"contract IMDuelHook is IUnlockCallback {","title":"Coverage: contracts read and not reached in this review"}],"hash":"9c2d8a1d9d0e384f1b80d6083fefc80ac160fa01206798658554183d4105075c","nodeId":"75db21b6-4ea6-4d3a-a304-85652efdc78e","outcome":"completed","summary":"Review complete. No tracked file was changed; the findings are in `.imd-findings.json` and the proof lives under `test/scratch/`.\n\n**Outcome**\n\nThe hook and token build, the hook's runtime is well under the size limit, and all 142 project tests pass, including the factory-shaped launches in both token orders. The manifest's nine constructor arguments and five permissions match the code, and the route pool id in the notes matches its key. I found one substantiated medium defect, one low design risk, and recorded coverage.\n\n**Findings written**\n\n- **Medium, with proof.** The IMD/ETH route pool is hookless, so the hook only samples its price inside a batch attempt, and anyone can trigger one with `convert`. All three \"block start\" samples can therefore be produced inside the attacker's own transactions. The scratch test pushes the route price down, calls convert, and restores it, once per block for three blocks, with a narrow IMD band under the pushed price in the third. The hook sells its whole 1 ETH pile at about half the honest rate and the attacker nets roughly 0.45 ETH after fees, while the route's price at the start of every block was honest. The main duel is not affected, since every price change there passes through beforeSwap first. Loss is bounded per block by the batch cap and needs a thin route pool and an ETH-paired duel with volume.\n- **Low.** When hookData is not exactly 20 bytes the bounty owner is tx.origin, so swaps relayed through smart accounts, bundlers or relayers pay the reward to the relayer. The author's own tests assert this default, so it is flagged for the adapter as a design decision.\n- **Info.** Coverage statement: the hook, token, Strength, Rules, the engine interface, the launch files and the key test fixtures were read in full. Bitboard, Chess and ChessEngineV5 were not reached in the budget and are trusted as the already deployed engine's output. The protected OwnerChecks helper is not in the tree.\n\n**Checked and found sound**\n\nFees are minted as claims, never taken from the manager's balance during a swap. Chests plus pile plus owed balance the hook's claims on every path. Callbacks refuse non-manager callers. A payout recipient that leaves a delta is refused and left owed. The factory's zero-liquidity fee collect runs no hook callback. No owner, pause, upgrade, delegatecall, selfdestruct or signer exists in either contract.","treeHash":null,"usage":{"cachedInputTokens":4154805,"inputTokens":738,"model":"claude-fable-5-1","outputTokens":71611,"runtime":"claude","turns":43,"wallClockMs":1083308}},{"artifacts":[],"attempt":2,"bundleHash":"5164ccd3b820ff3a397242af2b0674f584889ca005f3af09e951c81b10022ec4","device":"8e25456c4ae69acf","findings":[],"hash":"c75591c1cbc79bc9e01fcff945548c619bac1bea2d4aa041648aa401075d5ca7","nodeId":"68b6ecbd-c233-46b8-a00a-56395089e7ce","outcome":"completed","summary":"I added two test files under `test/`. With the verifier's fuzz seed, the full suite passes: 190 tests across 25 suites, 0 failed, 0 skipped. I found no defect I could reproduce, so I wrote no `.imd-findings.json`. No contract source or configuration file was touched.\n\nThe project already had 171 passing tests covering a lot: callers other than the PoolManager being refused, bad constructor arguments, the engine having no code, the permissions in `launch.json`, both token orders, route proofs, payouts that touch the deltas, and two invariant suites. So I aimed the new tests at gaps.\n\n**`test/FeeSchedule.t.sol`** (16 tests)\n- **Launch surcharge:** a fuzz test over age (0–2000 s) and trade size checks the main duel's chest share is exactly 0.4% plus 25%·(300−age)/300. It also checks that the trader pays exactly its order, the 0.1% slice goes to the pile, and the hook holds exactly chest plus pile. Fixed tests pin the edges: 25.4% at second 0, 0.48% at second 299, plain 0.4% from second 300. A second fuzz test checks the share never rises with age.\n- **Exact output:** a fuzz test checks the trader gets exactly the amount asked for, 0.4% of that goes to the chest, and the 0.1% slice is charged on top of the input. An exact-output order stopped by its price limit is refused whole, with nothing paid and no fee kept.\n- **Refused chest funding:** no allowance, not enough balance, an ETH value that doesn't match the amount (or ETH sent for the token side), and funding zero.\n- **Owed rewards:**\n  - A payout to a contract that refuses ETH stays owed to it alone.\n  - A third party's `withdrawTo` moves nothing.\n  - Retrying `withdraw` to the refusing owner reverts and the amount stays owed.\n  - The owner can redirect it with `withdrawTo`, only once.\n  - Withdrawing when nothing is owed, or converting an empty pile, does nothing at all.\n- **Main-duel conversion:** however often `convert` is called in one block, or a trade happens in that block, only one batch runs, and it spends at most 10 minimums. The next block runs the next batch. The IMDUEL bought is split exactly: half burned to `0x…dEaD`, the rest to the main duel's IMDUEL chest, and the books still balance.\n\n**`test/TokenInvariant.t.sol`**\n- An invariant suite drives random transfers, approvals and `transferFrom` calls on `src/IMDuelToken.sol` across four holders, including calls that should fail. Each failing call must fail with its specific error (zero address, not enough balance, not enough allowance). The suite stops on any unexpected revert, so assertions inside the handler can't be silently skipped.\n- The invariants are: the supply stays fixed at 1,000,000,000, the holders' balances always add up to the total, nothing sits at the zero address, and every allowance equals what was approved minus what was spent.\n- Two more tests check that transfers from the zero address and transfers above the balance are refused.\n\nRun counts are set inside the test files, since `foundry.toml` is off limits. I haven't committed the two files.","treeHash":"a3304b62b2b3423fe6dbfa431f04c9e257f46ed7","usage":{"cachedInputTokens":1715206,"inputTokens":40,"model":"claude-opus-5-5","outputTokens":23918,"runtime":"claude","turns":23,"wallClockMs":429045}},{"artifacts":[],"attempt":1,"bundleHash":"ee500f6502925a68c0cbf8286681531439f95ebca4eaa3f99b73282474ce7271","device":"b0b4e7bbc84f9d80","findings":[],"hash":"dc773e947db50c44350b21367753e81b5300c81e093a3053e84532fe0e83edc4","nodeId":"d947757b-cd59-455c-a66d-10903a8f7978","outcome":"completed","summary":"launch.json is complete and verified. No changes were needed this round: the committed manifest already describes the accepted tree, and no findings were attached to the revision.\n\nWhat I verified in the working tree:\n\n- **Constructor arguments.** The hook's constructor takes nine parameters in this order: manager, engine, imd, ethFee, ethSpacing, minImd, minEth, minImduel, turnBlocks. The manifest lists \"$poolManager\", the ChessEngineV5 address, the IMD address, then 10000, 200, 10 IMD, 0.02 ETH, 1,000,000 IMDUEL and 3, all as strings, addresses lowercase. There is no route-hook argument, matching the hookless IMD / ETH pool. The hook has no owner or factory parameter, so no placeholder for either is written.\n- **Permissions.** getHookPermissions in `src/IMDuelHook.sol` enables exactly afterInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta and afterSwapReturnDelta, which is what the manifest declares. afterInitialize satisfies the launch's initialize-callback requirement.\n- **Token and pool.** token.contract is IMDuelToken, named IMDuel, symbol IMDUEL, 18 decimals, matching `src/IMDuelToken.sol`. The pool block is IMD at the given address, fee 12500, tick spacing 60, initial price one to one.\n- **Schema shape.** The first key is \"kind\": \"univ4_hook\", only schema keys are present, and notes are one string of 3,537 characters, under the 4,000 cap. The notes cover the fee split, the 300-second opening surcharge, the batched conversion pricing and once-per-block limit, the refused payout and withdrawTo path, and the unchecked engine code.\n\nChecks run:\n\n- The root manifest's hook, pool and permission blocks are byte-identical to launch/hook.json with the engine placeholder filled in. The repository's LaunchFiles test deploys the hook from that file and asserts the permission flags and every argument land correctly. It passes.\n- forge build succeeds, and forge test with the verifier's fuzz seed passes all 180 tests across 24 suites, with no failures.\n- git status is clean, so no file other than the manifest exists as a change.","treeHash":"a41dbb31b2ef1b296ace6cbcb5c8bda6fe7c831d","usage":{"cachedInputTokens":300927,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":5148,"runtime":"claude","turns":8,"wallClockMs":327799}},{"artifacts":[],"attempt":1,"bundleHash":"6d95461ed75a38019a78923e04855a1a0a81df5ca5c257ed07987131adf594ba","device":"e15fc098eeb318a3","findings":[],"hash":"eb9b141484d0276f6cd9785f470f181d3e29f5a9fc701c60a2b2d67c6c924b54","nodeId":"1c20b3f6-4ce8-4ba7-8145-fa1ddea830c0","outcome":"completed","summary":"Fixed both reproduced audit findings while preserving the manifest, constructor arguments, token and compiler settings.\n\nValidation passed: `forge build`, 171 Foundry tests with the supplied seed, and 5 helper tests.\n\nETH conversions now require authenticated price-proof publication. Every change and operational detail is documented in [ADAPTATION.md](/root/.identitymd/work/885d5cb6-af12-41ef-94de-82d17d27bbe4/1c20b3f6-4ce8-4ba7-8145-fa1ddea830c0/ADAPTATION.md).","treeHash":"a22952637e2b6fa4e07279bad77950189080ae74","usage":{"cachedInputTokens":6143232,"inputTokens":168190,"model":"gpt-6-astra","outputTokens":59973,"runtime":"codex","turns":8,"wallClockMs":3294451}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"30a6c1a419ef4f9c","findings":[{"citation":"resolved","description":"Merged from audit_economics (ecf425a1) and audit_flow (a78f33e5): same root cause, both reproduced. payOut (lines 394-399) snapshots manager.getNonzeroDeltaCount() around _send and refuses the payout only if the count changed. The count moves only when some delta crosses zero. While afterSwap runs, the hook itself already holds a nonzero delta in every currency it minted fee claims in during this swap (beforeSwap line 344 mints the chest share; the offsetting hookDelta is accounted by PoolManager.swap only after afterSwap returns, lib/v4-core/src/PoolManager.sol lines 221-224). A recipient paid in native ETH gets execution in receive() while the manager is unlocked and calls manager.sync(ETH); manager.settleFor{value: 1}(hook): the hook's ETH delta goes from -chestShare to -chestShare+1, stays nonzero, the count is unchanged, payOut returns and Reward(paid=true) is emitted. After afterSwap returns PoolManager credits the hook its +chestShare, leaving +1 wei, and the router's unlock ends with CurrencyNotSettled. Because a queued move is only resolved from _play inside a swap (lines 625-626) and the same bid is replayed each time, every later swap of that duel in either direction re-pays the same recipient and reverts the same way until _hashOf(closeBlock) returns 0 (8,191 blocks, about 27 hours), when the move degrades to a timeout turn. The attacker repeats with any later bounty-paying top bid (capture, check or mate), so one trade per day keeps an ETH-paired duel untradeable indefinitely; there is no owner or other path to skip the move. This breaks the stated guarantee ('a reward payout whose recipient touches the PoolManager's deltas is refused and left owed') and the fix for the earlier DebtOwner finding (test/Hostile.t.sol debtOwnerIsRefusedAndTradingGoesOn covers a recipient that opens its own delta, not one that moves the hook's existing delta). Exposure: every duel paired with native ETH (ETH/IMD, ETH/$IMDUEL, ETH/X, which afterInitialize accepts from anyone) and duels in tokens with transfer callbacks. The launch's main duel (IMDUEL/IMD, both plain ERC-20s) is not exposed, so medium rather than high: no funds are lost, but third-party duels on the hook can be frozen at will and their chests/LP fee flow stalled. Fix: snapshot the hook's own deltas in the pool's two currencies (manager.currencyDelta(address(this), key.currency0 / currency1)) and the payout currency before _send and revert Unsettled if any differs from the expected value afterwards; or never push to a contract recipient and leave it owed (withdraw/withdrawTo already exist).","line":398,"path":"src/IMDuelHook.sol","reproduction":"State: ETH/IMD duel on the hook (currency0 = native ETH, currency1 = IMD, fee 3000, spacing 60), opened after the main duel, full-range liquidity 1000e18; chests funded via fundChest: 10 ETH for team 0 and 100 IMD for team 1 (so _smallerChest picks ETH and the chest share is minted in ETH in beforeSwap); White (ETH's team in game 0) to move with K e1, R h1 vs Q e2, K a8, P a7 (Kxe2 is the only legal move, captures a queen, 270 bps). Attacker contract A: receive() { manager.sync(address(0)); manager.settleFor{value: 1}(hook); }, funded with 1 ETH. Calls: (1) swapRouter.swap(duel, zeroForOne=false, amountSpecified=-1e18, hookData=abi.encodePacked(A)) -> game(id).bids[0].owner == A. (2) next block: swapRouter.swap{value: 1 ether}(duel, zeroForOne=true, -1 ether, hookData=''). Expected: the move is played (ply == 1), the payout to A is refused with Unsettled, owed[A][0] == chest*270/10000 (about 0.27 ETH), the swap succeeds and later swaps in both directions succeed. Actual: the swap reverts with CurrencyNotSettled() (payOut passed: hook ETH delta -0.004e18 -> -0.004e18+1, count unchanged; PoolManager then credits +0.004e18 leaving +1 wei), and every later swap of the duel reverts identically. A plain EOA recipient under the same setup is paid and the swap succeeds (control test). Run: forge test --match-path test/scratch/PayoutSettleFor.t.sol -> test_plainOwnerIsPaidAndTheMoveIsPlayed passes, test_settleForOwnerIsRefusedAndTradingGoesOn fails with CurrencyNotSettled().","severity":"medium","snippet":"        if (manager.getNonzeroDeltaCount() != open) revert Unsettled();","title":"payOut's Unsettled guard compares only the nonzero-delta count: a native-ETH (or callback-token) reward recipient can settleFor(hook) 1 wei during the payout, pass the guard, and make the trade that p"},{"citation":"resolved","description":"Merged from audit_permissions (848725a6, medium), audit_economics (35b38704, low) and audit_flow (e72f3542, low): one root cause. _bidOwner (lines 657-664) falls back to the PoolManager swap `sender` when hookData is not a 20-byte or clean 32-byte address. For every swap made through a router (Uniswap's Universal Router, aggregators, v4's test routers) that sender is the router contract, never the trader. Every buy is a bid (line 639/642), and when the move pays, _payBounty -> _pay -> payOut pushes the chest bounty to the router with manager.take (line 403). Universal Router's V4 swap actions forward the caller's hookData, empty by default, and its SWEEP command lets any caller take the router's balance, so the trader whose later swap plays the move can append a SWEEP in the same execute() and take the previous bidder's bounty atomically. A router without receive() (PoolSwapTest here) makes an ETH payout fail, so the reward is recorded in owed[router], which only withdraw(router, c) (fails the same way) or the router's own withdrawTo (it never calls it) can move: locked in the hook permanently. Either way the party that paid for the move and earned the bounty does not get it, and the 0.4% chest share paid by every trader is redistributed to router sweepers unless every integrator adds hookData. The code comment and ADAPTATION.md treat the sender fallback as acceptable, but the launch brief says every swap bids for its team and the move's owner is paid; the default path on the most common v4 router violates that, so this is funds paid to the wrong party under the common condition, medium. Fix (author's design choice): record owner = address(0) (a nobody bid that plays but pays nobody, as timeout moves and an explicit zero recipient already do) when hookData carries no recipient, or refuse the bid, or record owed[sender] without pushing so no shared router balance ever holds a reward; document that hookData is required to earn bounties.","line":663,"path":"src/IMDuelHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {PoolSwapTest} from \"v4-core/src/test/PoolSwapTest.sol\";\nimport {PoolModifyLiquidityTest} from \"v4-core/src/test/PoolModifyLiquidityTest.sol\";\nimport {Currency, CurrencyLibrary} from \"v4-core/src/types/Currency.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {PoolId, PoolIdLibrary} from \"v4-core/src/types/PoolId.sol\";\nimport {SwapParams, ModifyLiquidityParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {TransientStateLibrary} from \"v4-core/src/libraries/TransientStateLibrary.sol\";\nimport {MockERC20} from \"solmate/src/test/utils/mocks/MockERC20.sol\";\nimport {IMDuelHook, IMDUEL_HOOK_FLAGS} from \"src/IMDuelHook.sol\";\n\n/// A swap without hookData is owned by the PoolManager's swap sender, which through any router is the router, so\n/// the move's bounty is pushed to or left owed to the router rather than the trader who paid for the move.\ncontract RouterOwnerTest is Test {\n    using PoolIdLibrary for PoolKey;\n    using TransientStateLibrary for IPoolManager;\n\n    uint160 constant SQRT_PRICE_1_1 = 79228162514264337593543950336;\n    uint256 constant NEVER = type(uint256).max;\n\n    IPoolManager manager;\n    PoolSwapTest swapRouter;\n    PoolModifyLiquidityTest lpRouter;\n    MockERC20 imd;\n    MockERC20 imduel;\n    IMDuelHook hook;\n    PoolKey duel; // native ETH / IMD, opened on the hook after the main duel\n    PoolId id;\n\n    function setUp() public {\n        manager = IPoolManager(vm.deployCode(\"PoolManager.sol:PoolManager\", abi.encode(address(this))));\n        swapRouter = PoolSwapTest(vm.deployCode(\"PoolSwapTest.sol:PoolSwapTest\", abi.encode(manager)));\n        lpRouter = PoolModifyLiquidityTest(vm.deployCode(\"PoolModifyLiquidityTest.sol:PoolModifyLiquidityTest\", abi.encode(manager)));\n        imd = new MockERC20(\"IMD\", \"IMD\", 18);\n        imduel = new MockERC20(\"IMDUEL\", \"IMDUEL\", 18);\n        imd.mint(address(this), 1e30);\n        imduel.mint(address(this), 1e30);\n        imd.approve(address(swapRouter), type(uint256).max);\n        imd.approve(address(lpRouter), type(uint256).max);\n        imduel.approve(address(lpRouter), type(uint256).max);\n        vm.deal(address(this), 1e30);\n\n        address engine = vm.deployCode(\"ChessEngineV5.sol:ChessEngineV5\");\n        address at = address(IMDUEL_HOOK_FLAGS | (uint160(0x1D0E1) << 40));\n        // the launch's constructor arguments except: no ETH route needed here, batches never, one-block turns\n        deployCodeTo(\n            \"IMDuelHook.sol:IMDuelHook\",\n            abi.encode(manager, engine, Currency.wrap(address(imd)), uint24(0), uint24(0), NEVER, NEVER, NEVER, uint256(1)),\n            at\n        );\n        hook = IMDuelHook(at);\n\n        // the main duel first (it fixes $IMDUEL), then an ETH / IMD duel, which afterInitialize accepts\n        (Currency a, Currency b) = address(imd) < address(imduel)\n            ? (Currency.wrap(address(imd)), Currency.wrap(address(imduel)))\n            : (Currency.wrap(address(imduel)), Currency.wrap(address(imd)));\n        PoolKey memory main = PoolKey(a, b, 3000, 60, IHooks(address(hook)));\n        manager.initialize(main, SQRT_PRICE_1_1);\n        lpRouter.modifyLiquidity(main, ModifyLiquidityParams(-887220, 887220, 1_000e18, 0), \"\");\n\n        duel = PoolKey(CurrencyLibrary.ADDRESS_ZERO, Currency.wrap(address(imd)), 3000, 60, IHooks(address(hook)));\n        id = duel.toId();\n        manager.initialize(duel, SQRT_PRICE_1_1);\n        lpRouter.modifyLiquidity{value: 2_000 ether}(duel, ModifyLiquidityParams(-887220, 887220, 1_000e18, 0), \"\");\n\n        // chests: 10 ETH for ETH's team, 100 IMD for IMD's, so the fee is taken on the ETH side (the smaller chest)\n        hook.fundChest{value: 10 ether}(duel, false, 10 ether);\n        imd.approve(address(hook), type(uint256).max);\n        hook.fundChest(duel, true, 100e18);\n\n        // White (ETH's team in game 0) is in check from an unprotected queen next to its king: Kxe2 is its only move\n        uint256 board;\n        board |= uint256(6) << (sq(4, 0) << 2); // white king e1\n        board |= uint256(4) << (sq(7, 0) << 2); // white rook h1\n        board |= uint256(13) << (sq(4, 1) << 2); // black queen e2\n        board |= uint256(14) << (sq(0, 7) << 2); // black king a8\n        board |= uint256(9) << (sq(0, 6) << 2); // black pawn a7\n        setPosition(board, side(sq(4, 0)), side(sq(0, 7)));\n    }\n\n    function sq(uint256 file, uint256 rank) internal pure returns (uint256) {\n        return file + 8 * rank;\n    }\n\n    /// Side state with the king on `k`, castling gone, no en passant (as the suite's Chessboard.side).\n    function side(uint256 k) internal pure returns (uint32) {\n        return 0x80800000 | (uint32(k) << 8) | 0xff;\n    }\n\n    /// Writes the position into games[id] (storage slot 0 mapping): whiteState and blackState sit at byte offsets\n    /// 10 and 14 of the struct's first word, board is its seventh word. Verified through game(id) below.\n    function setPosition(uint256 board, uint32 white, uint32 black) internal {\n        bytes32 base = keccak256(abi.encode(id, uint256(0)));\n        uint256 w = uint256(vm.load(address(hook), base));\n        w &= ~(uint256(0xffffffffffffffff) << 80);\n        w |= (uint256(white) << 80) | (uint256(black) << 112);\n        vm.store(address(hook), base, bytes32(w));\n        vm.store(address(hook), bytes32(uint256(base) + 6), bytes32(board));\n        IMDuelHook.Game memory g = hook.game(id);\n        assertEq(g.board, board, \"board set\");\n        assertEq(g.whiteState, white, \"white state set\");\n        assertEq(g.blackState, black, \"black state set\");\n        assertEq(g.ply, 0, \"White to move\");\n        assertTrue(g.live);\n    }\n\n    /// `owner` buys ETH with 1 IMD (White's bid for Kxe2); the next block's ETH buy plays the move and pays `owner`.\n    function bidAndResolve(address owner) internal {\n        swapRouter.swap(\n            duel,\n            SwapParams(false, -1e18, TickMath.MAX_SQRT_PRICE - 1),\n            PoolSwapTest.TestSettings(false, false),\n            abi.encodePacked(owner)\n        );\n        IMDuelHook.Game memory g = hook.game(id);\n        assertEq(g.bids[0].owner, owner, \"owner holds White's bid\");\n        vm.warp(vm.getBlockTimestamp() + 12);\n        vm.roll(vm.getBlockNumber() + 1);\n        swapRouter.swap{value: 1 ether}(\n            duel, SwapParams(true, -1 ether, TickMath.MIN_SQRT_PRICE + 1), PoolSwapTest.TestSettings(false, false), \"\"\n        );\n    }\n\n    /// FAILS on the current code: PoolSwapTest (no receive()) ends up owed the ETH bounty of a move the test contract\n    /// paid for, and nothing in the hook lets anyone but the router move it. Passes once a bid without an explicit\n    /// recipient no longer names the router as its owner (no bid, a nobody bid, or the real trader).\n    function test_emptyHookDataNeverLeavesTheBountyWithTheRouter() public {\n        swapRouter.swap(\n            duel, SwapParams(false, -1e18, TickMath.MAX_SQRT_PRICE - 1), PoolSwapTest.TestSettings(false, false), \"\"\n        );\n        vm.warp(vm.getBlockTimestamp() + 12);\n        vm.roll(vm.getBlockNumber() + 1);\n        uint256 routerBefore = address(swapRouter).balance;\n        swapRouter.swap{value: 1 ether}(\n            duel, SwapParams(true, -1 ether, TickMath.MIN_SQRT_PRICE + 1), PoolSwapTest.TestSettings(false, false), \"\"\n        );\n        assertEq(hook.game(id).ply, 1, \"the move is played\");\n        assertEq(hook.owed(address(swapRouter), 0), 0, \"nothing owed to the router\");\n        assertEq(address(swapRouter).balance, routerBefore, \"nothing pushed to the router\");\n        assertTrue(hook.game(id).moveOwner != address(swapRouter), \"the router is not the move's owner\");\n    }\n\n    receive() external payable {}\n}","reproduction":"State: ETH/IMD duel on the hook with liquidity, ETH chest 10 ETH, White (ETH's team) to move with the forced queen capture Kxe2 (position as in test/Hostile.t.sol queenForcedOn). Calls: (1) PoolSwapTest.swap(duel, zeroForOne=false, -1e18 IMD, hookData='') from trader T -> game(id).bids[0].owner == address(swapRouter) (test/BidOwner.t.sol test_missingOwnerUsesSwapSenderNotBundler asserts the same on the main duel). (2) next block: any swap zeroForOne=true, -1 ether. Expected: T (or nobody) is paid 2.7% of the ETH chest; nothing is owed to or held by the router. Actual: Reward(owner = router); PoolSwapTest has no receive(), so owed[router][0] == 270215461292230554 wei and the router's balance is unchanged; withdraw(router, ETH) reverts and only the router could call withdrawTo. With an ERC-20 bounty (main duel) the router receives the tokens outright: test/BidOwner.t.sol test_missingOwnerPaysTheSwapSender shows 0.27e18 landing on the router. Run: forge test --match-path test/scratch/RouterOwner.t.sol -> test_emptyHookDataNeverLeavesTheBountyWithTheRouter fails: 'nothing owed to the router: 270215461292230554 != 0'.","severity":"medium","snippet":"        return sender;","title":"A bid without hookData is owned by the swap router: the trader's bounty is pushed to or left owed to the router contract, where it is sweepable by anyone (Universal Router) or stuck for good (routers "},{"citation":"resolved","description":"From audit_economics (6f80a7e3), reproduced by trace. beforeSwap reads the pool's current in-range liquidity (line 297), stashes it, and _trade (lines 673-675) converts it to a depth in the paid token; Strength.fromShare(paid, depth) returns the 9,000 bps cap once paid >= 1% of depth. The hook uses no liquidity callbacks, so a contract holding the dominant in-range position can, inside one PoolManager unlock, modifyLiquidity to remove 99.9% of it, swap a minimal amount (hookData = itself) against the remaining dust, and re-add the position before settling. The bid is recorded at full strength although its real share of the pool is negligible; the cheapest honest bidder for the same strength must pay 1% of true depth. Impact is bounded: the bounty is capped at 2x the trade's own output (PAYOUT_CAP), so the manipulator cannot drain a chest, but it plays engine-strength moves (captures, mates, and the opposing side's bids are weak relative to its own) for gas only, and the brief's rule that strength is set by the trade's share of the pool does not hold. Fix: measure depth from a value that cannot be changed inside the same block (sample liquidity alongside the price in _sample at block start) or set strength from the bid's size rather than its share.","line":297,"path":"src/IMDuelHook.sol","reproduction":"State: main duel at 1:1, one LP L holding essentially all in-range liquidity, getLiquidity(id) = 1e21. In one unlock L: (a) modifyLiquidity(pool, -0.999e21) so getLiquidity(id) is about 1e18; (b) manager.swap(pool, zeroForOne=true, -1e16, hookData=abi.encodePacked(L)); (c) modifyLiquidity(pool, +0.999e21); settles all deltas. In (b) beforeSwap stashes liquidity about 1e18, _trade computes depth about 1e18 and Strength.fromShare(1e16, 1e18): paid*FLOOR = 1e21 vs depth 1e18, log2 gap about 9.97*256, s = 2551*903/256 > 9000 -> 9000. Expected: a 1e16 trade against the true 1e21 depth is a 0.001% share and plays at 0 bps (fromShare(1e16, 1e21): up <= down -> 0). Actual: Bid(..., strengthBps = 9000) and the move is played at the engine's full strength. Leaving exactly 0 liquidity does not work (fromShare returns 0 for depth 0 and the swap hits PartialFill), 0.1% left does.","severity":"low","snippet":"        uint256 depth = manager.getLiquidity(id); // sets a bid's strength: every buy bids","title":"Bid strength is derived from in-range liquidity read at swap start, so an LP can pull its position, trade a small amount at the 90% strength cap and re-add it in the same unlock"},{"citation":"resolved","description":"From audit_economics (d024c614), reproduced by trace. _reference (lines 570-576) takes the most hook-favourable of the three latest sampled block-start prices: when the hook sells IMD as currency0 (zeroForOne) the maximum. _swapClaims then sets limit = 0.999 * from and returns (0, 0) if limit >= sqrtPrice (lines 542-544). A trader who is last in the main pool in block B-1 and pushes the sqrt price up by more than 0.1% (selling IMDUEL) makes block B's start sample (taken in beforeSwap of the first trade of B, line 296) that spike, then reverses it as the first trade of block B. For the next three sampled blocks the reference includes the spike, limit = 0.999 * spike > current price, every batch returns (0, 0), Converted(IMD, 0, 0, 0) is emitted and the pile keeps waiting while every IMD-sliced trade still spends CONVERT_GAS on the attempt. Repeating every third sampled block delays the conversion (the IMDUEL chest funding and burn) indefinitely. Cost: two trades of about 0.1% of in-range depth each, paying the 0.5% hook fee and 1.25% LP fee, with no gain, so low: denial of a protocol flow with ongoing cost to the griefer and no loss of funds (the pile stays as claims). The 'most favourable of three' rule is a deliberate defence against selling into a pushed price; a median of the three, or widening the band once the current price has reverted, keeps that defence while bounding the delay.","line":574,"path":"src/IMDuelHook.sol","reproduction":"State: main duel (IMD = currency0) with three sampled blocks at sqrt price P and pile[IMD] = 1000e18 >= minImd (10e18). Block B-1: griefer sells 0.2% of in-range IMDUEL depth as the last main-pool trade, moving sqrt price to about 1.002*P. Block B: griefer buys the IMDUEL back as the first trade (beforeSwap samples 1.002*P as B's start price, then the price returns to about P); then any IMD-sliced trade or convert(IMD). Expected per the brief: the batch buys within 0.1% of the recent block-start prices, i.e. it runs at about P. Actual: _reference returns 1.002*P, limit = 0.999*1.002*P = 1.001*P > P, line 544 returns (0, 0), Converted(IMD, 0, 0, 0); the same in the next two sampled blocks until the spike leaves the three-sample window.","severity":"low","snippet":"            if (i == 0 || (zeroForOne ? p > from : p < from)) from = p;","title":"One end-of-block trade that spikes the main pool's block-start price, reversed at the start of the next block, blocks the IMD conversion batch for the next three sampled blocks: cheap repeatable grief"}],"hash":"eb9f8b277e7d9128f0889bcc5ad8845a405338bd1dab194919e73d2420a24d33","nodeId":"b8c57dea-cd28-402c-925e-162dd981f86e","outcome":"completed","summary":"Review complete. The findings file is at `.imd-findings.json` with four findings and a full coverage record, and both proofs fail on the current code.\n\n**What I kept, after reproducing each against the code**\n\n1. **Medium, payOut's delta guard is bypassable.** The two medium reports from the economics and flow specialists are one root cause, merged. The guard compares only the nonzero-delta count, and during afterSwap the hook already holds a nonzero delta from the fee claims it minted. A recipient paid in native ETH can call settleFor on the hook for 1 wei inside its receive, the count stays the same, the payout is accepted, and the trade then reverts with CurrencyNotSettled. Every later trade of that duel replays the same move and reverts for about 8,191 blocks. I confirmed it with `test/scratch/PayoutSettleFor.t.sol`: the plain-recipient control passes, the settleFor recipient fails with CurrencyNotSettled. Kept at medium because the main IMDUEL/IMD duel is not exposed, only ETH-paired and callback-token duels anyone can open on the hook.\n\n2. **Medium, bids without hookData belong to the router.** Three specialist reports merged into one. Through any router the swap sender is the router, so the bounty is pushed to it or left owed to it. With a router that has no receive the ETH is stuck permanently. With the Universal Router anyone can sweep it. The in-tree BidOwner test already shows the router being paid. My proof `test/scratch/RouterOwner.t.sol` fails with 0.27 ETH owed to the router.\n\n3. **Low, strength depth is manipulable in one unlock.** An LP can pull liquidity, swap a dust amount at the 90% cap, and re-add. Bounded by the 2x payout cap.\n\n4. **Low, a reversed end-of-block price spike holds the IMD conversion batch shut for three sampled blocks.** Griefing only, at ongoing cost to the griefer.\n\n**What I dropped**\n\n- Zero-address hookData opting out of the bounty: the author documents it as an intentional opt-out, no third party loses.\n- Dust-trade fee truncation and the uint160 price-limit wrap: fail-closed, no funds move, unreachable prices.\n- ETH-route conversion needing a keeper: a documented limitation, the ETH simply waits as claims.\n\n**Coverage.** All 14 entry points answered, plus one manifest row. The launch.json arguments match the constructor in order, the permissions match getHookPermissions, and the four project suites I reran pass with the committed seed. Nothing in the Mindset pass surfaced beyond what the specialists had found.","treeHash":null,"usage":{"cachedInputTokens":1359537,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":44474,"runtime":"claude","turns":21,"wallClockMs":866489}},{"artifacts":[],"attempt":1,"bundleHash":"593422ce9ad08c7022a34b6e9bfa35441a293a91f66ae3597cdae83ebf925320","device":"48cf2e19b6c54386","findings":[{"description":"payOut refuses a reward whose recipient changes the nonzero-delta count or the hook's own deltas in the pool's two currencies. It does not look at the PoolManager's synced currency (TransientStateLibrary.getSyncedCurrency / getSyncedReserves). A recipient whose receive() calls only manager.sync(token) moves no delta, so payOut accepts the payout (Reward paid=true), but the token stays synced for the rest of the unlock. In a native ETH duel, when the trade that plays the move pays in ETH, the router then calls manager.settle{value: x}() (v4-core CurrencySettler, PoolSwapTest; native settles never sync first) and the PoolManager reverts NonzeroNativeValue because the synced currency is the token. The whole trade reverts, and every later ETH-paying trade of that duel replays the same resolve and payout and reverts the same way until a trade paying in the other currency (whose router re-syncs before settling) plays the move. The owner can repeat this for every move its bid owns, so for those turns every buy of the duel's token with ETH reverts (and gas estimation for it fails). Nothing is lost and the duel is not frozen for token-paying trades (covered in test/PayoutGuard.t.sol, test_aRecipientThatSyncsIsSettledAroundWhenTheTradePaysInTheToken), so this is a liveness defect of the guard the brief describes ('a reward payout whose recipient touches the PoolManager's deltas is refused and left owed'), not a loss of funds. Suggested fix: in payOut also snapshot manager.getSyncedCurrency() and manager.getSyncedReserves() before _send and revert Unsettled if either differs after it, so the payout is refused and left owed (withdrawTo already lets the owner collect it).","line":397,"path":"src/IMDuelHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {PoolSwapTest} from \"v4-core/src/test/PoolSwapTest.sol\";\nimport {PoolModifyLiquidityTest} from \"v4-core/src/test/PoolModifyLiquidityTest.sol\";\nimport {Currency, CurrencyLibrary} from \"v4-core/src/types/Currency.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {PoolId, PoolIdLibrary} from \"v4-core/src/types/PoolId.sol\";\nimport {SwapParams, ModifyLiquidityParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {MockERC20} from \"solmate/src/test/utils/mocks/MockERC20.sol\";\nimport {IChessEngine} from \"src/IChessEngine.sol\";\nimport {IMDuelHook, IMDUEL_HOOK_FLAGS} from \"src/IMDuelHook.sol\";\n\n/// The real hook plus one test-only setter, to start from a position where the side to move must capture a queen.\ncontract PositionHook is IMDuelHook {\n    constructor(IPoolManager m, IChessEngine e, Currency imd_)\n        IMDuelHook(m, e, imd_, 0, 0, type(uint256).max, type(uint256).max, type(uint256).max, 1)\n    {}\n\n    function setPosition(PoolId id, uint256 board, uint32 white, uint32 black) external {\n        Game storage g = games[id];\n        (g.board, g.whiteState, g.blackState, g.ply, g.quiet) = (board, white, black, 0, 0);\n    }\n}\n\n/// A move owner whose receive() only calls manager.sync(token): no delta moves, so payOut accepts the payout.\ncontract SyncingOwner {\n    IPoolManager immutable manager;\n    Currency immutable token;\n\n    constructor(IPoolManager m, Currency t) {\n        (manager, token) = (m, t);\n    }\n\n    receive() external payable {\n        manager.sync(token);\n    }\n}\n\n/// Finding: payOut's guard compares delta counts and the hook's own deltas, but not the PoolManager's synced\n/// currency. A recipient that only syncs a token leaves it synced; the router's native settle{value}() that follows\n/// in the same unlock then reverts NonzeroNativeValue, so the trade that plays the move reverts, and so does every\n/// ETH-paying trade of the duel while that move is pending (each replays the same payout).\n/// Expected: the trade goes through, the move is played and the bounty is paid or left owed.\ncontract ProofSyncRecipientTest is Test {\n    using PoolIdLibrary for PoolKey;\n    using CurrencyLibrary for Currency;\n\n    IPoolManager manager;\n    PoolSwapTest router;\n    PositionHook hook;\n    PoolKey key;\n    PoolId id;\n    MockERC20 imd;\n\n    function sq(string memory s) internal pure returns (uint256) {\n        bytes memory b = bytes(s);\n        return uint256(uint8(b[0]) - 97) + 8 * uint256(uint8(b[1]) - 49);\n    }\n\n    function put(uint256 board, string memory s, uint256 piece) internal pure returns (uint256) {\n        return board | (piece << (sq(s) << 2));\n    }\n\n    function side(string memory k) internal pure returns (uint32) {\n        return 0x80800000 | (uint32(sq(k)) << 8) | 0xff;\n    }\n\n    function swap(bool zeroForOne, uint256 amountIn, bytes memory data) internal {\n        router.swap{value: zeroForOne ? amountIn : 0}(\n            key,\n            SwapParams(\n                zeroForOne, -int256(amountIn), zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\n            ),\n            PoolSwapTest.TestSettings(false, false),\n            data\n        );\n    }\n\n    function setUp() public {\n        manager = IPoolManager(vm.deployCode(\"PoolManager.sol:PoolManager\", abi.encode(address(this))));\n        router = PoolSwapTest(vm.deployCode(\"PoolSwapTest.sol:PoolSwapTest\", abi.encode(manager)));\n        PoolModifyLiquidityTest lp = PoolModifyLiquidityTest(\n            vm.deployCode(\"PoolModifyLiquidityTest.sol:PoolModifyLiquidityTest\", abi.encode(manager))\n        );\n        IChessEngine engine = IChessEngine(vm.deployCode(\"ChessEngineV5.sol:ChessEngineV5\"));\n        imd = new MockERC20(\"IMD\", \"IMD\", 18);\n        MockERC20 imduel = new MockERC20(\"IMDUEL\", \"IMDUEL\", 18);\n        address at = address(IMDUEL_HOOK_FLAGS | (uint160(0x1D0E1) << 40));\n        deployCodeTo(\n            \"Proof_SyncRecipient.t.sol:PositionHook\", abi.encode(manager, engine, Currency.wrap(address(imd))), at\n        );\n        hook = PositionHook(payable(at));\n        // the main duel (IMD / IMDUEL) first, then a native ETH / IMD duel\n        (address a, address b) =\n            address(imd) < address(imduel) ? (address(imd), address(imduel)) : (address(imduel), address(imd));\n        manager.initialize(PoolKey(Currency.wrap(a), Currency.wrap(b), 3000, 60, IHooks(at)), 2 ** 96);\n        key = PoolKey(CurrencyLibrary.ADDRESS_ZERO, Currency.wrap(address(imd)), 3000, 60, IHooks(at));\n        id = key.toId();\n        manager.initialize(key, 2 ** 96);\n\n        imd.mint(address(this), 1e30);\n        imd.approve(address(router), type(uint256).max);\n        imd.approve(address(lp), type(uint256).max);\n        vm.deal(address(this), 1e30);\n        lp.modifyLiquidity{value: 1e27}(key, ModifyLiquidityParams(-887220, 887220, 1e24, 0), \"\");\n        hook.fundChest{value: 10 ether}(key, false, 10 ether);\n\n        // ETH's team is White in game 0 and is in check from an unprotected queen: Kxe2 is its only move\n        uint256 board;\n        board = put(board, \"e1\", 6);\n        board = put(board, \"h1\", 4);\n        board = put(board, \"e2\", 13);\n        board = put(board, \"a8\", 14);\n        board = put(board, \"a7\", 9);\n        hook.setPosition(id, board, side(\"e1\"), side(\"a8\"));\n    }\n\n    function test_anEthPayingTradeThatPaysASyncingOwnerGoesThrough() public {\n        SyncingOwner r = new SyncingOwner(manager, Currency.wrap(address(imd)));\n        swap(false, 1e18, abi.encodePacked(address(r))); // IMD in: buys ETH, bids for White's move for r\n        vm.roll(block.number + 1);\n        vm.warp(block.timestamp + 12);\n        // ETH in: the first trade after the window plays White's move and pays r its bounty. Today it reverts\n        // NonzeroNativeValue: r's sync(IMD) is still in place when the router settles ETH with value.\n        swap(true, 1e18, \"\");\n        assertEq(hook.game(id).ply, 1, \"the move is played\");\n        assertGt(address(r).balance + hook.owed(address(r), 0), 0, \"the bounty is paid or owed\");\n    }\n\n    receive() external payable {}\n}","reproduction":"Native ETH / IMD duel on the hook (ETH currency0), ETH's team to move with a forced capture (Kxe2, bounty 0.27 ETH from a 10 ETH chest). Bid with an IMD-in trade whose hookData is the packed address of a contract whose receive() calls manager.sync(IMD). Next block, an ETH-in exact-input swap of 1 ETH through PoolSwapTest with empty hookData. Expected: the swap goes through, ply becomes 1, and the 0.27 ETH bounty is paid or left owed to the contract. Actual: the hook emits Reward(..., amount 2.7e17, paid: true), then PoolSwapTest's manager.settle{value: 1e18}() reverts NonzeroNativeValue() and the whole swap reverts; ply stays 0. Run: forge test --match-path test/scratch/Proof_SyncRecipient.t.sol","severity":"medium","title":"Payout guard misses the PoolManager's synced currency: a move owner that only calls sync() makes every ETH-paying trade of a native ETH duel revert while its move is pending"}],"hash":"ebb56f2943a82450e44eebe264bb52d40ec28e7b117282a3304f08a6b6aee63e","nodeId":"68b6ecbd-c233-46b8-a00a-56395089e7ce","outcome":"completed","summary":"I added one test file and found one defect, which I've reported rather than asserted. `forge build` succeeds, and `forge test` passes with the verifier's fuzz seed: **201 tests in 26 suites, none failing or skipped** (`test/scratch` excluded).\n\n**New tests: `test/PayoutGuard.t.sol` (7 tests).** They run on a native ETH / IMD duel where the side to move must capture a queen, so the bounty is paid inside the trade that plays the move. They check the rule that a payout is refused if the recipient touches the PoolManager's deltas:\n- **Not refused:** a plain recipient, and one that only reads deltas, are paid during the trade.\n- **Refused:** a recipient that settles 1 wei for the router is refused. The reward stays owed and nothing it did sticks.\n- **Fuzzed:** settling any amount for the hook is refused, and the trade still settles.\n- **Owed rewards only reach their owner:**\n  - `withdraw` fails, and the reward stays owed, while the owner still touches deltas.\n  - A stranger's `withdrawTo` moves nothing.\n  - The owner's `withdrawTo` collects it elsewhere, once.\n  - Anyone's `withdraw` delivers it to the owner once the owner accepts payment.\n- After each case, trading continues both ways and the hook's holdings equal its reward pools plus what it owes.\n\n**Finding (medium), in `.imd-findings.json` with a proof test.**\n- **The defect:** the payout check in `payOut` (`src/IMDuelHook.sol:397`) compares the open-delta count and the hook's own deltas, but not which currency the PoolManager has \"synced\". A recipient whose `receive()` only calls `manager.sync(IMD)` changes no delta, so the hook pays it (`Reward … paid: true`).\n- **The effect:** that synced currency stays set for the rest of the trade. When the trade pays in ETH, the router's `settle{value}` then reverts with `NonzeroNativeValue`, and the whole trade reverts. Every ETH-paying trade of that duel reverts the same way until a trade paying in the other token plays the move. The owner can repeat this for every move its bid wins.\n- **What it doesn't do:** no funds are lost, and trades paying in the token still go through; one test checks that.\n- **The proof:** it is self-contained and fails on the current code for exactly this reason. It passes once the check also compares the synced currency and reserves, which is the fix suggested in the finding. It deploys its own hook subclass by the artifact name `Proof_SyncRecipient.t.sol:PositionHook`, so the file must keep that name.\n\n`.imd-findings.json` doesn't appear in `git status` because the repo's `.gitignore` excludes it. It is in the repository root, where the task asked for it. The protected suites in `.imd/reads` need toolchain helpers that aren't in this repo, so they weren't run locally.","treeHash":"5148b3a9b76c20545b02caad649f9c07d01e3c65","usage":{"cachedInputTokens":1840970,"inputTokens":48,"model":"claude-opus-5-5","outputTokens":28183,"runtime":"claude","turns":27,"wallClockMs":630633}},{"artifacts":[],"attempt":1,"bundleHash":"6633560cea3816990ccedb6df57662be7ac4d8dcae920c29f4caca2c3fa7484e","device":"d73af718d55ba788","findings":[],"hash":"eea25d482b3080b8e6745acc2ffa11ad229fe1d054eab2cbbd3bddd880b5de05","nodeId":"1c20b3f6-4ce8-4ba7-8145-fa1ddea830c0","outcome":"completed","summary":"Both reopened findings reproduced on the accepted tree and are fixed; the two advisory lows are answered as disputed with reasons; the build and the full suite pass with the verifier's seed.\n\n**Fixed, finding 68f05aa8 (payout guard bypassed by settleFor).** Reproduced: a recipient that calls settleFor on the hook for 1 wei during its ETH payout passed the count-only guard and the trade reverted with CurrencyNotSettled, as did every later trade of the duel. In `src/IMDuelHook.sol`, payOut now takes the pool key and snapshots the hook's own deltas in both pool currencies beside the nonzero-delta count. Any difference after the send reverts Unsettled and the reward stays owed. Four new tests in test/Hostile.t.sol cover a native ETH duel and a callback-token duel, settling in the payout currency and in the slice currency. All four fail when the own-delta check is removed.\n\n**Fixed, finding 5edd7f4d (router owns bids without hookData).** Reproduced with the reviewer's proof. A trade that names no recipient now bids and plays for nobody: it still bids for its team's move, and its move pays no bounty, as timeout moves already did. The swap sender is no longer used. Updated tests in BidOwner, Game and Launch assert the router is never paid or owed. The invariant suite gets SP-07, which checks every Reward event names a recipient a trade gave. The proof passes on the result.\n\n**Disputed, no change, finding b3605770 (LP pulls liquidity inside one unlock).** The trace holds, but a block-start liquidity sample is set by the same unlock that pulls the position, and the only robust fix is a liquidity callback, which changes the permission set the task fixes. Impact is bounded by the 2x payout cap. Documented as an accepted limitation.\n\n**Disputed, no change, finding 573a4330 (spike delays the IMD batch).** The most-favourable-of-three rule is the designed defence against selling the pile into a pushed price. A median would weaken it. The grief delays conversion only, at an ongoing fee cost to the griefer.\n\nResults:\n\n| Check | Result |\n|---|---|\n| forge build | passes |\n| forge test with the verifier's seed | 176 tests pass, plus the proof |\n| Python offline tests | 5 pass |\n\nFiles changed: src/IMDuelHook.sol, ADAPTATION.md, and seven test files. The responses file at the repository root answers all seven finding ids, including the three imported audit findings from the previous round. launch.json, foundry.toml, the token and the chess sources are untouched.","treeHash":"3195b2ee49b71ac5999337bd93f0fc4e5e961118","usage":{"cachedInputTokens":2991079,"inputTokens":802,"model":"claude-fable-5-1","outputTokens":47919,"runtime":"claude","turns":63,"wallClockMs":1355243}},{"artifacts":[],"attempt":1,"bundleHash":"2372621743eff485ffdde385a2cc160c884337cecaecb1362b9e914c38262d12","device":"d4b53d656a3fbe07","findings":[{"description":"payOut refuses a payout when the recipient changes the nonzero-delta count, the hook's own deltas in the pool's two currencies, or the synced currency or reserves. It does not catch a recipient that moves some other address's delta that is already nonzero, because that delta never crosses zero. A router that settles its input before calling swap (v4 allows it, and the guard's own comment names a router that syncs and transfers before the swap) has a nonzero delta in the input currency while afterSwap runs. Multi-hop routes through the payout currency do too. The move owner's receive() calls manager.settleFor{value: 1}(router). The count, the hook's deltas and the synced state all stay the same, so the payout is accepted and reported as paid. The router then has 1 wei of unexpected credit, and a router that settles and takes exact amounts ends its unlock with CurrencyNotSettled, so the trade that plays the move reverts. The owner can do this to every such trade at no cost, because the reverted wei is refunded. It stops when a trade through a router with no open delta in that currency (for example PoolSwapTest) plays the move: the payout is then refused and left owed, as it should be. Impact: targeted griefing of prepaid-input and multi-hop trades that would play the owner's move. No funds are lost and the duel is not frozen. A fix that keeps the design: also snapshot and compare manager.currencyDelta(msg.sender of the swap, i.e. the `sender` afterSwap receives, payout currency), or refuse any payout made while `sender` has a nonzero delta in the payout currency.","line":400,"path":"src/IMDuelHook.sol","reproduction":"Fixture of test/PayoutGuard.t.sol: a native ETH / IMD duel on the hook, ETH's team is White, the forced capture Kxe2, a 10 ETH chest. ProbeOwner r bids for White's move (white(1e18, r)). In the next block r.set(3, router, 1, ETH), so receive() runs sync(ETH) and then settleFor{value:1}(router). PrepaidRouter (test/PayoutGuard.t.sol) mode 2 then pays 1 ETH with settle{value} before swap(ETH->IMD, exact in 1e18), and settles and takes exactly what the swap returned. Expected: the payout is refused, the reward is left owed and the trade settles (what happens when the same recipient settles for PoolSwapTest, whose delta is zero). Actual: the swap reverts CurrencyNotSettled(), and it reverts again on retry in a later block. A PoolSwapTest trade then plays the move with the reward owed. The probe below, placed in test/scratch/ beside test/PayoutGuard.t.sol, shows it: test_probe_settleForAPrepaidRouter fails with CurrencyNotSettled() and test_probe_impact passes. It imports the PayoutGuard fixture (the forced-capture position needs the test harness's setPosition), so it is a reproduction, not a self-contained proof.\n\n// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Vm} from \"forge-std/Test.sol\";\nimport {Currency, CurrencyLibrary} from \"v4-core/src/types/Currency.sol\";\nimport {PayoutGuardTest, PrepaidRouter} from \"../PayoutGuard.t.sol\";\n\n/// A router that settled native ETH before the swap has a nonzero ETH delta while afterSwap runs; a recipient that\n/// settles 1 wei for it moves that delta without crossing zero. Expected: refused and owed, the trade settles.\ncontract ProbeTest is PayoutGuardTest {\n    using CurrencyLibrary for Currency;\n\n    function test_probe_settleForAPrepaidRouter() public {\n        PrepaidRouter p = new PrepaidRouter(manager);\n        vm.deal(address(p), 10 ether);\n        white(1e18, address(r));\n        next(12);\n        r.set(3, address(p), 1, ETH);\n        p.swap(pool, true, 1e18, 2, \"\"); // expected to settle; reverts CurrencyNotSettled if the payout went through\n        assertEq(st().ply, 1);\n        assertGt(hook.owed(address(r), ETH.toId()), 0, \"refused and owed\");\n    }\n}\n\ncontract ProbeImpactTest is PayoutGuardTest {\n    using CurrencyLibrary for Currency;\n\n    function test_probe_impact() public {\n        PrepaidRouter p = new PrepaidRouter(manager);\n        vm.deal(address(p), 10 ether);\n        white(1e18, address(r));\n        next(12);\n        r.set(3, address(p), 1, ETH);\n        vm.expectRevert();\n        p.swap(pool, true, 1e18, 2, \"\");\n        assertEq(st().ply, 0, \"not played: the prepaid trade reverted\");\n        vm.roll(vm.getBlockNumber() + 5);\n        vm.expectRevert();\n        p.swap(pool, true, 1e18, 2, \"\");\n        black(1e18, address(0)); // a router with no open delta plays it; r is refused\n        assertEq(st().ply, 1);\n        assertGt(hook.owed(address(r), ETH.toId()), 0);\n    }\n}\n","severity":"low","title":"Payout guard misses a recipient that settles for a router that already has an open delta in the payout currency"}],"hash":"fb55df38ff17832e5592550dd6a0eadeb29bcd6688def52870249e7f61bccc21","nodeId":"68b6ecbd-c233-46b8-a00a-56395089e7ce","outcome":"completed","summary":"I've added tests for the hook's new payout check, and they found one more way past it, which I reported in `.imd-findings.json` instead of writing a test that accepts it. `forge build` passes, and `forge test` with the verifier's seed passes all 210 tests in 27 suites (206 before).\n\n**What I changed** (only `test/PayoutGuard.t.sol`, building on my accepted version):\n- **New test router, `PrepaidRouter`.** It pays for its trade before swapping, either by transferring its token first or by sending ETH up front. It then pays and collects exactly the amounts the swap reports, so anything an outsider changes in its balance with the PoolManager leaves it unbalanced and the trade fails.\n- **Four new tests:**\n  - A reward recipient that syncs a token is now refused when the trade pays in ETH. The trade goes through, the reward stays owed, nothing is left synced, trading continues both ways, and `withdraw` collects the reward later. Before this round's hook change, that trade reverted.\n  - A recipient that re-syncs the router's input token after the router has transferred it is refused by the new reserves check. The router gets its output and pays exactly its input.\n  - Plain recipients are still paid during the trade through both router styles.\n- I removed my old comment saying the ETH-paying sync case was reported as a defect, because the hook now handles it.\n\n**Finding (low), at `src/IMDuelHook.sol:400` (`payOut`):** a reward recipient can still make some trades fail. Its `receive()` sends the PoolManager 1 wei of ETH on behalf of a router that is already owed ETH by the PoolManager at that point. That happens with a router that pays before swapping, or a multi-step trade that passes through the payout currency. None of the hook's three checks notice, so the payout goes through. The router is left 1 wei over, and the trade that would play the recipient's move reverts with `CurrencyNotSettled`, again on every retry.\n\nThe damage is limited: no money is lost, and the next trade through an ordinary router plays the move and leaves the reward owed, so the duel isn't stuck. The finding suggests a fix: also compare the trader's balance with the PoolManager in the payout currency.\n\nThe finding has no standalone proof file. Reaching a reward needs the forced-capture position, which only the test harness's `setPosition` can set up, and proof files can't import test files. Instead the finding includes a reproduction that uses my test file's setup: the trade fails with `CurrencyNotSettled()`, and a follow-up check confirms an ordinary trade then plays the move. A proof is only required for critical or high findings, so this should be acceptable for a low one.\n\nNothing is committed; the changes are in the working tree.","treeHash":"3cb9a084fa2bc93562b03c8e89f882c0b0ec5589","usage":{"cachedInputTokens":982276,"inputTokens":33,"model":"claude-opus-5-5","outputTokens":21147,"runtime":"claude","turns":19,"wallClockMs":580036}},{"artifacts":[],"attempt":2,"bundleHash":"2351dd2fcc5269d75145d1c9edb502b40aaf7b03698b39b94a9936477ffdad68","device":"3baa2091028c32c1","findings":[],"hash":"fc2c47a2077162dae61f731d8abc875a907acacfd76683483d41b93f41fd7584","nodeId":"1c20b3f6-4ce8-4ba7-8145-fa1ddea830c0","outcome":"completed","summary":"The revision is complete. The sync-recipient defect is fixed and tested, and the reviewer's proof is answered \"disputed\" because it cannot run under the name the verifier gives it.\n\n**What I found.** The reported defect reproduces on the accepted tree. In an ETH / IMD duel, a move owner whose `receive()` only calls `sync(IMD)` moved no delta, so the payout was accepted and IMD stayed synced. The ETH-paying trade that played the move then reverted with `NonzeroNativeValue` in its router's native settle, and every later ETH-paying trade of the duel reverted the same way.\n\n**What I changed.**\n- `src/IMDuelHook.sol`: the guard in `payOut` now also snapshots the PoolManager's synced currency and synced reserves before the send and reverts `Unsettled` if either differs after it. The reward then stays owed and is collected with `withdraw` or `withdrawTo`. The reserves check also covers a router that syncs and transfers its input before the swap. Syncing ETH when nothing is synced changes neither value and is still paid.\n- `test/Tokens.sol` and `test/Hostile.t.sol`: a `SyncingOwner` fixture and four tests, covering the ETH duel, a callback-token duel syncing either currency, and the harmless ETH sync. Without the fix they fail with `NonzeroNativeValue` or an unpaid, unowed reward.\n- `test/Proof_SyncRecipient.t.sol`: the reviewer's exact scenario on the real hook through `PoolSwapTest`, including a second ETH-paying trade and the withdrawal.\n- `test/HookGas.t.sol`: the moving-trade budget rises from 560,000 to 570,000, since the four extra transient reads cost that trade about 5,600 gas.\n- `ADAPTATION.md` and `.imd-responses.json` record all of this and answer the two advisory findings as accepted limitations with no change.\n\n**Why the proof is disputed.** It deploys its harness with `deployCodeTo` under its original file name, but the verifier runs it as `Proof_28dc1ba64f1c.t.sol`. Forge resolves that name against compiled source files, a `.t.sol` file is compiled only when it matches the run's own filter, and the disk fallback needs an fs permission in the protected `foundry.toml`. I confirmed the renamed copy fails identically in `setUp` with the fix in place, with a full build cached, and with a contract-name filter. The response suggests building the harness from `type(PositionHook).creationCode` or inheriting `IMDuelHarness` so it runs as written.\n\n**Verification.** `forge build` succeeds. With the verifier's fuzz seed, 181 tests pass across 25 suites with no failures, and the 5 Python tests pass. Hook runtime is 21,322 bytes, under the 23,500 budget. No `.imd-blocked.json` was written and `src/IMDuelToken.sol`, `launch.json` and `foundry.toml` are untouched.","treeHash":"a5b1b26aafa5f76e8f69c595a1ddd99a2f033f23","usage":{"cachedInputTokens":2518032,"inputTokens":770,"model":"claude-fable-5-1","outputTokens":56079,"runtime":"claude","turns":54,"wallClockMs":1159065}}],"verification":[{"checks":[{"durationMs":88935,"exitCode":0,"name":"build","output":"Compiling 120 files with Solc 0.8.26\nSolc 0.8.26 finished in 88.80s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:22:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                      ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:38:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                                      ^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:109:57:\n    |\n109 |             uint64 d = f >= r ? DIAG >> (8 * (f - r)) : DIAG << (8 * (r - f));\n    |                                                         ^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:110:65:\n    |\n110 |             uint64 a = f + r <= 7 ? ANTI >> (8 * (7 - f - r)) : ANTI << (8 * (f + r - 7));\n    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:38:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:69:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                                                     ^^^^^^^^^^^^^^^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:73:18:\n   |\n73 |         returns (uint16, Rules.Played memory)\n   |                  ^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:73:26:\n   |\n73 |         returns (uint16, Rules.Played memory)\n   |                          ^^^^^^^^^^^^^^^^^^^\n\nWarning (2072): Unused local variable.\n  --> test/Game.t.sol:90:9:\n   |\n90 |         IMDuelHook.Game memory g = st();\n   |         ^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n  --> test/LaunchFiles.t.sol:70:5:\n   |\n70 |     function test_engineLaunchFileDeploysTheEngineWithoutArguments() public {\n   |     ^ (Relevant source part starts here and spans across multiple lines).\n\n","passed":true},{"durationMs":3823,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Size.t.sol:SizeTest\n[PASS] test_deployedCodeFitsTheBudgets() (gas: 1255177)\nLogs:\n  hook: 21314\n  engine v5: 23339\n  route price verifier: 5551\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 915.61µs (794.73µs CPU time)\n\nRan 4 tests for test/Token.t.sol:TokenTest\n[PASS] test_allowances() (gas: 347653)\n[PASS] test_fixedSupplyToTheDeployer() (gas: 39384)\n[PASS] test_nothingPrivileged() (gas: 192691)\n[PASS] test_transfers() (gas: 188240)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 945.42µs (406.42µs CPU time)\n\nRan 3 tests for test/LaunchFiles.t.sol:LaunchFilesTest\n[PASS] test_engineLaunchFileDeploysTheEngineWithoutArguments() (gas: 7834)\n[PASS] test_ethereumLaunchFile() (gas: 1334918)\n[PASS] test_sepoliaLaunchFile() (gas: 1329938)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.70ms (1.59ms CPU time)\n\nRan 1 test for test/Proof_SyncRecipient.t.sol:SyncRecipientProofTest\n[PASS] test_anEthPayingTradeThatPaysASyncingOwnerGoesThrough() (gas: 1175592)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.77ms (2.76ms CPU time)\n\nRan 5 tests for test/Teams.t.sol:TeamsTest\n[PASS] test_aDynamicFeePoolPlaysWithAZeroLpFee() (gas: 2916166)\n[PASS] test_nativeEthIsATeamLikeAnyToken() (gas: 2329765)\n[PASS] test_poolsOnOneHookKeepSeparateGamesAndChests() (gas: 3392679)\n[PASS] test_sliceSide() (gas: 1821742)\n[PASS] test_twoPlainTokensCannotDuel() (gas: 1593745)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 9.86ms (8.68ms CPU time)\n\nRan 7 tests for test/Draws.t.sol:DrawsTest\n[PASS] test_checkmate() (gas: 240028)\n[PASS] test_moveCap() (gas: 240069)\n[PASS] test_ordinaryMoveDoesNotEndTheGame() (gas: 180017)\n[PASS] test_quietMoveLimit() (gas: 246287)\n[PASS] test_stalemate() (gas: 237841)\n[PASS] test_thirdRepetition() (gas: 964226)\n[PASS] test_tooFewPiecesToMate() (gas: 231666)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 9.86ms (3.92ms CPU time)\n\nRan 12 tests for test/Bidding.t.sol:BiddingTest\n[PASS] test_aBountyIsPaidFromTheMoversChest() (gas: 1369411)\n[PASS] test_aBuyOfTheOtherTeamBidsForItsNextMove() (gas: 1952666)\n[PASS] test_aBuyOfTheOtherTeamTimesTheSilentSideOutAndBids() (gas: 1222504)\n[PASS] test_aClosedWindowTakesNoBidsForItsMove() (gas: 1062884)\n[PASS] test_aMoveIsOpenForTurnBlocksBlocks() (gas: 1040466)\n[PASS] test_aNewTopMustPayMoreThanTenPercentMore() (gas: 974355)\n[PASS] test_aNextMoveBidCarriesIntoTheNextGame() (gas: 1467024)\n[PASS] test_aWindowWithoutBidsBecomesTheFirstBidsBlock() (gas: 1283690)\n[PASS] test_bidsCountAcrossTheWholeWindow() (gas: 1511214)\n[PASS] test_theMoveDependsOnlyOnTheWindowsLastBlock() (gas: 1621426)\n[PASS] test_theMoversOwnBuyAfterItsWindowBidsForItsNextMove() (gas: 1077593)\n[PASS] test_thePreviewIsTheMoveThatIsPlayed() (gas: 1222868)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 15.78ms (24.62ms CPU time)\n\nRan 6 tests for test/RouteSample.t.sol:RouteSampleTest\n[PASS] test_authenticatedBlockStartsRejectTheSameAttack() (gas: 3422402)\n[PASS] test_callerSamplesCannotSellTheEthPileAtAPushedPrice() (gas: 1710709)\nLogs:\n  route attacker profit at the honest price: -87002115995172580\n\n[PASS] test_duplicateSampleCannotRotateOutHonestHistory() (gas: 493037)\n[PASS] test_gapRequiresThreeConsecutiveSamplesAgain() (gas: 1855898)\n[PASS] test_honestBatchRunsAndStopsAtTheHistoricalPriceBand() (gas: 1175548)\n[PASS] test_staleRoutePricesWaitWithoutLosingClaims() (gas: 1298197)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 23.14ms (27.56ms CPU time)\n\nRan 8 tests for test/BidOwner.t.sol:BidOwnerTest\n[PASS] testFuzz_otherDataLengthsNeverCreditTheBundlerOrTheRouter(bytes) (runs: 256, μ: 383291, ~: 383288)\n[PASS] test_aHookWithoutAnEthRouteRejectsRouteProofs() (gas: 32534)\n[PASS] test_abiEncodedOwnerCreditsTheWallet() (gas: 384567)\n[PASS] test_abiEncodedOwnerReceivesTheCaptureBounty() (gas: 1128568)\n[PASS] test_dirtyAbiPaddingIsNobody() (gas: 382698)\n[PASS] test_missingOwnerIsNobodyNotTheRouterNorTheBundler() (gas: 415565)\n[PASS] test_missingOwnerPlaysButPaysNobody() (gas: 1151657)\n[PASS] test_packedOwnerCreditsTheWallet() (gas: 384686)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 61.12ms (58.06ms CPU time)\n\nRan 2 tests for test/LaunchAdmission.t.sol:LaunchAdmissionTest\n[PASS] test_blankChainTokenAboveImd() (gas: 26244349)\n[PASS] test_blankChainTokenBelowImd() (gas: 26173304)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 67.17ms (99.76ms CPU time)\n\nRan 3 tests for test/Launch.t.sol:LaunchTest\n[PASS] test_launchPairedWithEth() (gas: 23117220)\n[PASS] test_launchTokenAboveImd() (gas: 24067102)\nLogs:\n  first buy (opens game 0, bids for White): 366422\n\n[PASS] test_launchTokenBelowImd() (gas: 23992486)\nLogs:\n  first buy (opens game 0, bids for White): 361672\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 69.54ms (151.92ms CPU time)\n\nRan 13 tests for test/RoutePriceVerifier.t.sol:RoutePriceVerifierTest\n[PASS] testFuzz_authenticatesSlot0AndItsBlock(uint160,uint96) (runs: 256, μ: 200948, ~: 200266)\nLogs:\n  Bound result 2964422583387036479856633470515551576907\n\n[PASS] test_changedAccountProofIsRefused() (gas: 100298)\n[PASS] test_changedHeaderIsNotCanonical() (gas: 80800)\n[PASS] test_changedStorageProofIsRefused() (gas: 158269)\n[PASS] test_clientGeneratedPoolManagerProof() (gas: 241704)\n[PASS] test_emptyProofsAreRefused() (gas: 195098)\n[PASS] test_extraProofNodesAreRefused() (gas: 195848)\n[PASS] test_outOfRangePriceIsRefused() (gas: 192143)\n[PASS] test_theBatchBlockCannotSupplyASample() (gas: 80854)\n[PASS] test_uninitializedPoolPriceIsRefused() (gas: 180136)\n[PASS] test_unknownOrExpiredHeaderIsRefused() (gas: 81188)\n[PASS] test_wrongManagerIsRefused() (gas: 108609)\n[PASS] test_wrongPoolSlotIsRefused() (gas: 165128)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 73.26ms (76.61ms CPU time)\n\nRan 14 tests for test/Slice.t.sol:SliceTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5993365)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3973553)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3064481)\n[PASS] test_anEthBatchIsCapped() (gas: 5253758)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3203346)\n[PASS] test_anyoneCanConvert() (gas: 462007)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656457)\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4254448)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245420)\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764048)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487334)\n[PASS] test_noRouteNoDuel() (gas: 1030789)\n[PASS] test_oneEthBatchPerBlock() (gas: 5779111)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187164)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 75.59ms (43.66ms CPU time)\n\nRan 19 tests for test/Hostile.t.sol:HostileTest\n[PASS] test_aCallbackTokenOwnerLeavingADebtCannotFreezeTheDuel() (gas: 5286295)\n[PASS] test_aCallbackTokenOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 5622073)\n[PASS] test_aCallbackTokenOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 5641831)\n[PASS] test_aCallbackTokenOwnerSyncingTheOtherCurrencyCannotFreezeTheDuel() (gas: 5446646)\n[PASS] test_aDeadEngineLeavesTheTurnQueuedAndTradingGoesOn() (gas: 3557668)\n[PASS] test_aGasBurningEngineCostsOneExpensiveTradeNotTheGame() (gas: 9729914)\n[PASS] test_aTokenReturningNothingFundsAChest() (gas: 2188366)\n[PASS] test_anEthOwnerLeavingADebtCannotFreezeTheDuel() (gas: 4204502)\n[PASS] test_anEthOwnerResyncingAPrepaidInputCannotFreezeTheDuel() (gas: 4360090)\n[PASS] test_anEthOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 4491589)\n[PASS] test_anEthOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 4551486)\n[PASS] test_anEthOwnerSyncingNativeWhenNothingIsSyncedIsPaid() (gas: 3446709)\n[PASS] test_anEthOwnerSyncingTheOtherCurrencyCannotFreezeTheDuel() (gas: 4463091)\n[PASS] test_anOwnerThatCannotReceiveWithdrawsElsewhere() (gas: 3563141)\n[PASS] test_blockedRecipientStaysOwedAndTradingContinues() (gas: 4194594)\n[PASS] test_brokenEngineFallsBackToALegalMove() (gas: 5153141)\n[PASS] test_feeOnTransferTokenFundsOnlyWhatArrives() (gas: 2734236)\n[PASS] test_reenteringTokenCannotBreakAPayout() (gas: 4736721)\n[PASS] test_starvingTheEngineNeverPlaysTheFallback() (gas: 25153239)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 76.53ms (122.04ms CPU time)\n\nRan 8 tests for test/Strength.t.sol:StrengthTest\n[PASS] testFuzz_boundedAndMonotonic(uint256,uint256,uint256) (runs: 256, μ: 9090, ~: 8663)\nLogs:\n  Bound result 4608\n\n[PASS] testFuzz_depthMonotonic(uint256,uint256,uint256) (runs: 256, μ: 11990, ~: 12149)\nLogs:\n  Bound result 4608\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129635345\n\n[PASS] testFuzz_shiftKeepsTheShare(uint256,uint256,uint8) (runs: 256, μ: 14615, ~: 15266)\nLogs:\n  Bound result 10\n  Bound result 18\n  Bound result 4608\n\n[PASS] testFuzz_tenfoldIsAboutThirtyPoints(uint128,uint128) (runs: 256, μ: 13837, ~: 13724)\nLogs:\n  Bound result 50082718581281235812356958881899351066\n  Bound result 49615596080000199375330363197764209\n\n[PASS] testFuzz_unscalablePaidFollowsTheCurve(uint256,uint256) (runs: 256, μ: 13955, ~: 14208)\nLogs:\n  Bound result 115790931316423822261616749298837820974191451965793907633817189432073050508959\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913097900000\n\n[PASS] test_decidedPoints() (gas: 15577)\n[PASS] test_hugePaymentsDoNotRevert() (gas: 12854)\n[PASS] test_zeroInputs() (gas: 4493)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 101.13ms (160.05ms CPU time)\n\nRan 5 tests for test/HookGas.t.sol:HookGasTest\n[PASS] test_conversionGas() (gas: 3645312)\nLogs:\n  plain trade: 207740\n  trade that converts a batch: 271602\n\n[PASS] test_hookShareOfAMovingTrade() (gas: 29179885)\nLogs:\n  hook share of a moving trade: average: 280029\n  hook share of a moving trade: max: 333564\n\n[PASS] test_middlegameGas() (gas: 9538614)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 191975\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 562836\n\n[PASS] test_openingGas() (gas: 4542201)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 192693\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 508018\n\n[PASS] test_runningGameGas() (gas: 54808371)\nLogs:\n  running game, moving trades measured: 50\n  running game, moving trade average: 454070\n  running game, moving trade max: 554296\n\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 101.14ms (161.68ms CPU time)\n\nRan 3 tests for test/GasEstimate.t.sol:GasEstimateTest\n[PASS] test_atTheEstimateADueBatchConverts() (gas: 4998592)\nLogs:\n  smallest gas limit that converts: 310375\n\n[PASS] test_atTheEstimateTheBountyIsPaidNotOwed() (gas: 4619214)\nLogs:\n  smallest gas limit that plays and pays: 417418\n\n[PASS] test_noGasLimitPlaysTheMoveAndLeavesItsBountyOwed() (gas: 19294934)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 186.79ms (208.48ms CPU time)\n\nRan 2 tests for test/FactoryLaunch.t.sol:FactoryLaunchTest\n[PASS] test_factoryLaunchWithRealTokenAboveImd() (gas: 20633904)\n[PASS] test_factoryLaunchWithRealTokenBelowImd() (gas: 20598769)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 215.70ms (36.96ms CPU time)\n\nRan 17 tests for test/CrossBlock.t.sol:CrossBlockTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5993860)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3973571)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3064506)\n[PASS] test_anEthBatchIsCapped() (gas: 5253874)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3203420)\n[PASS] test_anyoneCanConvert() (gas: 462029)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656485)\n[PASS] test_crossBlockViaConvert() (gas: 5557988)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390298682200\n\n[PASS] test_crossBlockViaFirstTrade() (gas: 5720092)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390291136661\n\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4254485)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245468)\n[PASS] test_ethRouteSameBlockPush() (gas: 5231826)\nLogs:\n  ETH pile before (1e18): 500000000000000000\n  ETH the batch sold (1e18): 0\n  IMD it got (1e18): 0\n  IMD it was worth at the home price (1e18): 0\n  attacker profit (ETH at the home price, 1e18): -29770639430607030\n\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764062)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487316)\n[PASS] test_noRouteNoDuel() (gas: 1030839)\n[PASS] test_oneEthBatchPerBlock() (gas: 5779205)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187178)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 215.77ms (78.28ms CPU time)\n\nRan 1 test for test/Predict.t.sol:PredictTest\n[PASS] test_aBotCannotPickItsOwnCapturingMoves() (gas: 289907162)\nLogs:\n  bot moves: 0\n  other moves: 74\n  captures among other moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 550.60ms (550.38ms CPU time)\n\nRan 23 tests for test/Game.t.sol:GameTest\n[PASS] testFuzz_swapsNeverRevert(uint256) (runs: 48, μ: 9528375, ~: 9571339)\n[PASS] test_aGamePlaysOutThroughSwaps() (gas: 134592260)\nLogs:\n  moves played: 187\n  bids (incl. outbid ones): 238\n  most gas for a swap: 685293\n\n[PASS] test_aSmallerTradeInTheBlockDoesNotTakeTheTurn() (gas: 686491)\n[PASS] test_aTimeoutTurnCanBeTakenInItsBlock() (gas: 1304210)\n[PASS] test_aTradeBidsForTheTurn() (gas: 483494)\n[PASS] test_aTurnClosesWithItsBlock() (gas: 965170)\n[PASS] test_anExpiredTurnBecomesATimeoutTurn() (gas: 1410536)\n[PASS] test_anOldTurnIsPlayedWithTheHistoryContractsHash() (gas: 1754873)\n[PASS] test_anyTradeSizeBids() (gas: 450095)\n[PASS] test_lateTradeStillBidsNormally() (gas: 429729)\n[PASS] test_onlyThePoolManagerAndTheHookCanCallIn() (gas: 171212)\n[PASS] test_ownerDefaultsToNobody() (gas: 449725)\n[PASS] test_playedPositionsEnterTheRepetitionMemory() (gas: 915368)\n[PASS] test_rejectsBadConfig() (gas: 9247)\n[PASS] test_silentSidePlaysAtZeroStrength() (gas: 1045239)\n[PASS] test_strengthFollowsTheShareOfThePool() (gas: 6374541)\n[PASS] test_teamsSwapColoursEveryGame() (gas: 926880)\n[PASS] test_theBiggestTradeInTheBlockOwnsTheTurn() (gas: 782415)\n[PASS] test_theFirstPoolOpenedIsTheMainDuel() (gas: 179471)\n[PASS] test_theMainDuelMustPairImd() (gas: 1468317)\n[PASS] test_theNextBlocksTradePlaysTheTurn() (gas: 1038495)\n[PASS] test_theOtherTeamCannotBidOnThisTurn() (gas: 412278)\n[PASS] test_whoPlaysTheTurnAndWhenDoesNotChangeTheMove() (gas: 1534194)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 735.65ms (956.67ms CPU time)\n\nRan 21 tests for test/Money.t.sol:MoneyTest\n[PASS] testFuzz_heldEqualsOwed(uint256) (runs: 48, μ: 10642435, ~: 10708756)\n[PASS] test_aFullChestTakesNoMore() (gas: 1059339)\n[PASS] test_aTradeStoppedByItsPriceLimitIsRefused() (gas: 582903)\n[PASS] test_blackCaptureIsPaidInItsTeamToken() (gas: 786439)\n[PASS] test_bountyTable() (gas: 44705)\n[PASS] test_checkmatePaysAQuarterOfTheChest() (gas: 766283)\n[PASS] test_chestShareGoesToTheChestWorthLess() (gas: 638456)\n[PASS] test_chestsStayAboutEqualInValue() (gas: 5511474)\n[PASS] test_exactOutputPaysTheFeeOnTheInput() (gas: 384979)\n[PASS] test_exactOutputWithTheChestShareInTheOutput() (gas: 780877)\n[PASS] test_failedPayoutStaysOwedAndTheTradeGoesThrough() (gas: 1219882)\n[PASS] test_feesCannotBeWithdrawnByAnyone() (gas: 449988)\n[PASS] test_firstTradeFundsChest0AndTheSlice() (gas: 363816)\n[PASS] test_fundChestNeedsTheRightValue() (gas: 48803)\n[PASS] test_fundChestOnlyForHookPools() (gas: 41868)\n[PASS] test_fundChestWithEth() (gas: 252918)\n[PASS] test_fundChestWithTokens() (gas: 193816)\n[PASS] test_openingSurchargeOnlyInTheMainDuel() (gas: 3469491)\n[PASS] test_payoutIsCappedAtTwiceTheTrade() (gas: 833795)\n[PASS] test_quietMovePaysNothing() (gas: 616203)\n[PASS] test_whiteCaptureIsPaidInItsTeamTokenFromItsOwnChest() (gas: 860107)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 852.29ms (871.14ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_boardHasBothKingsAndPlyIsBounded\n[PASS] invariant_heldEqualsOwed\n InvariantTest invariants (runs: 24, calls: 2400, reverts: 0)\n\n╭----------+----------------+-------+---------+----------╮\n| Contract | Selector       | Calls | Reverts | Discards |\n+========================================================+\n| Handler  | convert        | 301   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | fund           | 305   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | longPause      | 311   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | nextBlock      | 313   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | trade          | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeAgain     | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeNextBlock | 312   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | withdrawReward | 304   | 0       | 0        |\n╰----------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3\n  Bound result 19999999000000004916\n  Bound result 19999999000001000001\n  Bound result 19999999000000008426\n  Bound result 19999999000000000554\n  Bound result 47\n  Bound result 18046385208871800035\n  Bound result 2842\n  Bound result 19999999007306351798\n  Bound result 100000000000000000\n  Bound result 31865262234567526\n  Bound result 49999999999950000\n  Bound result 19999999000000002642\n  Bound result 19999999000000003077\n  Bound result 5000\n  Bound result 46\n  Bound result 25333134452981760\n  Bound result 19999999000000000091\n  Bound result 18446744073709551615\n  Bound result 36\n  Bound result 4022291159582972627\n  Bound result 6\n  Bound result 700\n  Bound result 19999999000000200001\n  Bound result 23\n  Bound result 386549022720\n  Bound result 1100\n  Bound result 19999999000000000019\n  Bound result 1221339734816628308\n  Bound result 68\n  Bound result 115\n  Bound result 120\n  Bound result 102\n  Bound result 77\n  Bound result 120\n  Bound result 19999999000000001199\n  Bound result 19999999000000007744\n  Bound result 55\n  Bound result 1000000000003\n  Bound result 4413685840969725471\n  Bound result 1251\n  Bound result 112\n  Bound result 19999999000000000031\n  Bound result 19999999000000000904\n  Bound result 19999999000000003945\n  Bound result 15672660589582542013\n  Bound result 20396340581799926600\n  Bound result 13117\n  Bound result 19999999000000000321\n  Bound result 19999999000000004444\n  Bound result 12566133904435631808\n  Bound result 15386197539150620393\n  Bound result 2\n  Bound result 53\n  Bound result 19988888200295853627\n  Bound result 19999999000000003501\n  Bound result 25333134452981760\n  Bound result 4999999999995\n  Bound result 5060859166916801136\n  Bound result 100000\n  Bound result 13\n  swaps: 33\n  moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.09s (1.09s CPU time)\n\nRan 1 test for test/InvariantDuels.t.sol:InvariantDuelsTest\n[PASS]\nInvariantDuelsTest invariants:\n[PASS] invariant_everyBoardIsSound\n[PASS] invariant_everyCurrencyBalances\n[PASS] invariant_rightsMatchTheBoard\n[PASS] invariant_turnsAreWellFormed\n InvariantDuelsTest invariants (runs: 24, calls: 2880, reverts: 0)\n\n╭--------------+-------------------+-------+---------+----------╮\n| Contract     | Selector          | Calls | Reverts | Discards |\n+===============================================================+\n| DuelsHandler | addLiquidity      | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convert           | 220   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convertNextBlock  | 200   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | fund              | 241   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | longPause         | 221   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | nextBlock         | 215   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | pushThenNextBlock | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | removeLiquidity   | 253   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | roundTrip         | 251   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | trade             | 232   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeAgain        | 198   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeNextBlock    | 223   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | withdrawReward    | 210   | 0       | 0        |\n╰--------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 23330\n  Bound result 2565653098084874863\n  Bound result 12\n  Bound result 100000000\n  Bound result 72624976668147840\n  Bound result 19999999999000000742\n  Bound result 12\n  Bound result 19999999999000000049\n  Bound result 4485934425771658475\n  Bound result 80\n  Bound result 12\n  Bound result 19999999999000000053\n  Bound result 12\n  Bound result 19999999999000011560\n  Bound result 19999999999000057006\n  Bound result 300\n  Bound result 19999999999000017405\n  Bound result 19999999999000230001\n  Bound result 12\n  Bound result 19999999999000000064\n  Bound result 4000000000000000\n  Bound result 25333134452981760\n  Bound result 133294633079153423663\n  Bound result 12\n  Bound result 19999999999000000500\n  Bound result 12\n  Bound result 19999999999000009507\n  Bound result 12\n  Bound result 19999999999000016732\n  Bound result 12\n  Bound result 19999999999000006653\n  Bound result 180000030637492410715\n  Bound result 12\n  Bound result 4999999999000017108\n  Bound result 5505625188992501493\n  Bound result 12\n  Bound result 63236437681363320635\n  Bound result 12\n  Bound result 180000000000000009384\n  Bound result 12\n  Bound result 2336288999682689270\n  Bound result 52\n  Bound result 19999999999000014187\n  Bound result 12\n  Bound result 19999999999000018838\n  Bound result 180000000000000800001\n  Bound result 12\n  Bound result 4999999999000007636\n  Bound result 6017502607293588406\n  Bound result 1715335412152105462\n  Bound result 9999999899999990000000\n  Bound result 1192833703378608720\n  Bound result 17715909253537394009\n  Bound result 12\n  Bound result 401362994990120206\n  Bound result 19999999999000015256\n  Bound result 900000001999999998\n  Bound result 805801794754002874\n  Bound result 4999999999000000601\n  Bound result 180000000000000007269\n  Bound result 12\n  Bound result 180000000000000043129\n  Bound result 12\n  Bound result 12\n  Bound result 4999999999000000377\n  Bound result 12\n  Bound result 12\n  Bound result 100000000000000000000\n  Bound result 19999999999000001742\n  Bound result 12\n  Bound result 49\n  Bound result 4999999999001966081\n  Bound result 12\n  Bound result 16354846689735429006\n  Bound result 19999999999000004020\n  Bound result 19999999999000065281\n  Bound result 12\n  Bound result 17830126982352984981\n  Bound result 20\n  Bound result 13591338940416412890\n  Bound result 120\n  Bound result 27612829230727045392\n  Bound result 12\n  Bound result 40\n  Bound result 1000178988994931336848\n  Bound result 180000000000000887271\n  Bound result 12\n  Bound result 21931300337370667194\n  Bound result 189000000000000000001\n  Bound result 12\n  Bound result 112\n  Bound result 180000000000000024115\n  Bound result 12\n  Bound result 180000000000000010000\n  Bound result 12\n  Bound result 46649393329207593530\n  Bound result 12\n  Bound result 3000\n  Bound result 32680281041836782903\n  Bound result 11100177888994921347271\n  Bound result 19999999999000000996\n  Bound result 1812930352692670847190\n  Bound result 19999999999000002541\n  Bound result 2036944743885723607\n  Bound result 19999999999000094628\n  swaps: 43\n  moves: 25\n  Bound result 12\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.64s (3.63s CPU time)\n\nRan 24 test suites in 3.64s (8.18s CPU time): 180 tests passed, 0 failed, 0 skipped (180 total tests)\n","passed":true},{"durationMs":90,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDuelHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMDuelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDuelHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDuelHook.convert(address)\",\"IMDuelHook.convertBatch(address)\",\"IMDuelHook.fundChest((address,address,uint24,int24,address),bool,uint256)\",\"IMDuelHook.payOut((address,address,uint24,int24,address),address,address,uint256)\",\"IMDuelHook.sampleRoute(bytes,bytes[],bytes[])\",\"IMDuelHook.unlockCallback(bytes)\",\"IMDuelHook.withdraw(address,address)\",\"IMDuelHook.withdrawTo(address,address)\",\"IMDuelToken.approve(address,uint256)\",\"IMDuelToken.transfer(address,uint256)\",\"IMDuelToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":112,\"LICENSE\":16,\"foundry.toml\":29,\"launch.json\":39,\"launch/deployed.json\":12,\"launch/engine.json\":10,\"launch/hook.json\":39,\"launch/hook.sepolia.json\":39,\"script/LaunchPolicy.sol\":44,\"script/route_proof.py\":135,\"src/Bitboard.sol\":719,\"src/Chess.sol\":34,\"src/ChessEngineV5.sol\":852,\"src/IChessEngine.sol\":29,\"src/IMDuelHook.sol\":974,\"src/IMDuelToken.sol\":62,\"src/RoutePriceVerifier.sol\":50,\"src/Rules.sol\":45,\"src/Strength.sol\":47,\"src/proof/Bytes.sol\":144,\"src/proof/LICENSE\":22,\"src/proof/MerkleTrie.sol\":216,\"src/proof/PROVENANCE.md\":12,\"src/proof/RLPErrors.sol\":20,\"src/proof/RLPReader.sol\":249,\"test/Base.sol\":211,\"test/BidOwner.t.sol\":118,\"test/Bidding.t.sol\":224,\"test/Chessboard.sol\":79,\"test/CrossBlock.t.sol\":195,\"test/Draws.t.sol\":90,\"test/FactoryLaunch.t.sol\":173,\"test/Game.t.sol\":348,\"test/GasEstimate.t.sol\":120,\"test/Harness.sol\":83,\"test/HookGas.t.sol\":161,\"test/Hostile.t.sol\":559,\"test/Invariant.t.sol\":150,\"test/InvariantDuels.t.sol\":529,\"test/Launch.t.sol\":159,\"test/LaunchAdmission.t.sol\":167,\"test/LaunchFiles.t.sol\":75,\"test/Money.t.sol\":353,\"test/Predict.t.sol\":102,\"test/Proof_SyncRecipient.t.sol\":157,\"test/RoutePriceVerifier.t.sol\":135,\"test/RouteProof.sol\":69,\"test/RouteSample.t.sol\":168,\"test/Size.t.sol\":36,\"test/Slice.t.sol\":392,\"test/Strength.t.sol\":85,\"test/Teams.t.sol\":87,\"test/Token.t.sol\":82,\"test/Tokens.sol\":249,\"test/V4Fixture.sol\":100,\"test/data/chess.json\":1,\"test/data/conversion-validation.json\":770,\"test/data/conversion.json\":98,\"test/data/endings.json\":1,\"test/data/legal.json\":1,\"test/data/random.json\":1,\"test/data/route-proof.json\":16,\"test/test_route_proof.py\":72},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":21264,"exitCode":0,"name":"slither","output":"[high/medium] incorrect-exp at src/IMDuelHook.sol:256: IMDuelHook.teamOf(uint8,uint32,bool) (src/IMDuelHook.sol#256-258) has bitwise-xor operator ^ instead of the exponentiation operator **: \n[high/medium] incorrect-exp at src/IMDuelHook.sol:256: IMDuelHook.teamOf(uint8,uint32,bool) (src/IMDuelHook.sol#256-258) has bitwise-xor operator ^ instead of the exponentiation operator **: \n[medium/high] incorrect-equality at src/IMDuelHook.sol:585: IMDuelHook._reference(uint256,bool) (src/IMDuelHook.sol#585-591) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMDuelHook.sol:574: IMDuelHook._sampleAt(uint256,uint160,uint256) (src/IMDuelHook.sol#574-580) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMDuelHook.sol:335: IMDuelHook._takeFee(PoolId,PoolKey,uint256,uint256,uint256,IMDuelHook.Game) (src/IMDuelHook.sol#335-347) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:511: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#511-534):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:305: Reentrancy in IMDuelHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDuelHook.sol#305-330):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:546: Reentrancy in IMDuelHook._swapClaims(PoolKey,Currency,uint256,bool) (src/IMDuelHook.sol#546-567):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:511: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#511-534):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:629: Reentrancy in IMDuelHook._play(PoolId,PoolKey,IMDuelHook.Game,bool,BalanceDelta,address,uint160,uint256) (src/IMDuelHook.sol#629-668):\n[medium/medium] uninitialized-local at src/Bitboard.sol:643: Bitboard._targets(Bitboard.Pos,Bitboard.Gen,uint8,bool).ep (src/Bitboard.sol#643) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:480: Bitboard._anyLegal(Bitboard.Pos,bool).scratch (src/Bitboard.sol#480) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:382: IMDuelHook._pay(PoolId,IMDuelHook.Game,PoolKey,Currency,address,uint256).paid (src/IMDuelHook.sol#382) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:596: IMDuelHook._pack(uint160).e (src/IMDuelHook.sol#596) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:425: Bitboard.isLegal(Bitboard.Pos,uint16,bool).scratch (src/Bitboard.sol#425) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:497: ChessEngineV5._queening(ChessEngineV5.Ctx,Bitboard.Pos,uint64,uint64,uint64,uint64,uint64).a (src/ChessEngineV5.sol#497) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:182: ChessEngineV5._choose(ChessEngineV5.Ctx,uint256,uint256,uint64[8],uint32,uint16).seen (src/ChessEngineV5.sol#182) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:400: Bitboard._legalTargets(Bitboard.Pos,Bitboard.Gen,Bitboard.Pos,uint8,bool).ep (src/Bitboard.sol#400) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:236: Bitboard.pieceAttacks(Bitboard.Pos,uint64,uint64,bool).a (src/Bitboard.sol#236) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:203: ChessEngineV5._choose(ChessEngineV5.Ctx,uint256,uint256,uint64[8],uint32,uint16).refined (src/ChessEngineV5.sol#203) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:580: Bitboard.mateInOne(Bitboard.Pos,bool,uint256).scratch (src/Bitboard.sol#580) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:883: IMDuelHook._repeated(PoolId,IMDuelHook.Game).seen (src/IMDuelHook.sol#883) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:414: ChessEngineV5._placement(ChessEngineV5.Ctx,uint8,uint8,uint8,uint8,bool,bool).near (src/ChessEngineV5.sol#414) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:276: ChessEngineV5._context(uint256,uint32,uint32,bool,bool).nonPawn (src/ChessEngineV5.sol#276) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:816: IMDuelHook._apply(PoolId,IMDuelHook.Game,uint16,Rules.Played,IMDuelHook.Trade).result (src/IMDuelHook.sol#816) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:377: ChessEngineV5._cheap(ChessEngineV5.Ctx,uint16).squeeze (src/ChessEngineV5.sol#377) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:820: IMDuelHook._apply(PoolId,IMDuelHook.Game,uint16,Rules.Played,IMDuelHook.Trade).reason (src/IMDuelHook.sol#820) is a local variable never initialized\n[medium/medium] unused-return at src/IMDuelHook.sol:433: IMDuelHook._withdraw(address,Currency,address) (src/IMDuelHook.sol#433-439) ignores return value by manager.unlock(abi.encode(uint8(0),abi.encode(to,c,amount))) (src/IMDuelHook.sol#437)\n[medium/medium] unused-return at src/IMDuelHook.sol:546: IMDuelHook._swapClaims(PoolKey,Currency,uint256,bool) (src/IMDuelHook.sol#546-567) ignores return value by (sqrtPrice,None,None,None) = manager.getSlot0(key.toId()) (src/IMDuelHook.sol#551)\n[medium/medium] unused-return at src/IMDuelHook.sol:288: IMDuelHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/IMDuelHook.sol#288-303) ignores return value by (sqrtPrice,None,None,None) = manager.getSlot0(id) (src/IMDuelHook.sol#295)\n[medium/medium] unused-return at src/IMDuelHook.sol:454: IMDuelHook.convert(Currency) (src/IMDuelHook.sol#454-456) ignores return value by manager.unlock(abi.encode(uint8(2),abi.encode(address(0),c,uint256(0)))) (src/IMDuelHook.sol#455)\n[low/high] shadowing-local at src/ChessEngineV5.sol:817: ChessEngineV5._mop(ChessEngineV5.Ctx,uint8,uint8,uint8,bool,bool).at (src/ChessEngineV5.sol#705) shadows:\n[low/medium] missing-zero-check at src/IMDuelHook.sol:476: IMDuelHook.unlockCallback(bytes).who (src/IMDuelHook.sol#476) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:335: Reentrancy in IMDuelHook._takeFee(PoolId,PoolKey,uint256,uint256,uint256,IMDuelHook.Game) (src/IMDuelHook.sol#335-347):\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:511: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#511-534):\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:443: Reentrancy in IMDuelHook.fundChest(PoolKey,bool,uint256) (src/IMDuelHook.sol#443-451):\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:381: Reentrancy in IMDuelHook._pay(PoolId,IMDuelHook.Game,PoolKey,Currency,address,uint256) (src/IMDuelHook.sol#381-389):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:629: Reentrancy in IMDuelHook._play(PoolId,PoolKey,IMDuelHook.Game,bool,BalanceDelta,address,uint160,uint256) (src/IMDuelHook.sol#629-668):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:381: Reentrancy in IMDuelHook._pay(PoolId,IMDuelHook.Game,PoolKey,Currency,address,uint256) (src/IMDuelHook.sol#381-389):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:443: Reentrancy in IMDuelHook.fundChest(PoolKey,bool,uint256) (src/IMDuelHook.sol#443-451):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:511: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#511-534):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:433: Reentrancy in IMDuelHook._withdraw(address,Currency,address) (src/IMDuelHook.sol#433-439):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:305: Reentrancy in IMDuelHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDuelHook.sol#305-330):\n[low/medium] return-bomb at src/IMDuelHook.sol:780: IMDuelHook._engine(uint256,bytes) (src/IMDuelHook.sol#780-786) tries to limit the gas of an external call that controls implicit decoding\n[low/medium] timestamp at src/IMDuelHook.sol:443: IMDuelHook.fundChest(PoolKey,bool,uint256) (src/IMDuelHook.sol#443-451) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:335: IMDuelHook._takeFee(PoolId,PoolKey,uint256,uint256,uint256,IMDuelHook.Game) (src/IMDuelHook.sol#335-347) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:351: IMDuelHook._chestBps(IMDuelHook.Game) (src/IMDuelHook.sol#351-354) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:511: IMDuelHook._convert(Currency) (src/IMDuelHook.sol#511-534) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:365: IMDuelHook._addChest(PoolId,uint256,uint256) (src/IMDuelHook.sol#365-372) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:629: IMDuelHook._play(PoolId,PoolKey,IMDuelHook.Game,bool,BalanceDelta,address,uint160,uint256) (src/IMDuelHook.sol#629-668) uses timestamp for comparisons","passed":true},{"durationMs":977,"exitCode":0,"name":"aderyn","output":"[high] contract-locks-ether at src/IMDuelHook.sol:44: Contract locks Ether without a withdraw function\n[high] incorrect-caret-operator at src/IMDuelHook.sol:257: Incorrect use of caret operator\n[high] incorrect-shift-order at src/proof/RLPReader.sol:164: Incorrect Assembly Shift Parameter Order (3 places)\n[high] reentrancy-state-change at src/IMDuelHook.sol:465: Reentrancy: State change after external call\n[high] unsafe-casting at src/Bitboard.sol:247: Unsafe Casting of integers (2 places)\n[low] empty-require-revert at src/ChessEngineV5.sol:157: Empty `require()` / `revert()` Statement\n[low] internal-function-used-once at src/Bitboard.sol:199: Internal Function Used Only Once (13 places)\n[low] large-numeric-literal at src/ChessEngineV5.sol:34: Large Numeric Literal (27 places)\n[low] literal-instead-of-constant at src/Bitboard.sol:35: Literal Instead of Constant (424 places)\n[low] missing-inheritance at src/IMDuelToken.sol:7: Missing Inheritance\n[low] push-zero-opcode at src/Bitboard.sol:2: PUSH0 Opcode (13 places)\n[low] require-revert-in-loop at src/proof/MerkleTrie.sol:73: Loop Contains `require`/`revert` (3 places)\n[low] state-change-without-event at src/IMDuelHook.sol:288: State Change Without Event\n[low] unchecked-return at src/IMDuelHook.sol:437: Unchecked Return (3 places)\n[low] uninitialized-local-variable at src/ChessEngineV5.sol:164: Uninitialized Local Variable (4 places)\n[low] unsafe-erc20-operation at src/IMDuelHook.sol:492: Unsafe ERC20 Operation\n[low] unspecific-solidity-pragma at src/ChessEngineV5.sol:2: Unspecific Solidity Pragma (6 places)\n[low] unused-state-variable at src/IMDuelHook.sol:76: Unused State Variable (2 places)","passed":true},{"durationMs":39512,"exitCode":1,"name":"proof 28dc1ba64f1c","output":"Compiling 96 files with Solc 0.8.26\nSolc 0.8.26 finished in 20.64s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:22:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                      ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:38:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                                      ^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:109:57:\n    |\n109 |             uint64 d = f >= r ? DIAG >> (8 * (f - r)) : DIAG << (8 * (r - f));\n    |                                                         ^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:110:65:\n    |\n110 |             uint64 a = f + r <= 7 ? ANTI >> (8 * (7 - f - r)) : ANTI << (8 * (f + r - 7));\n    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:38:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:69:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                                                     ^^^^^^^^^^^^^^^^^^^^\n\n\nRan 1 test for test/imd-proof-8ef41986/Proof_28dc1ba64f1c.t.sol:ProofSyncRecipientTest\n[FAIL: vm.getCode: the path out/Proof_SyncRecipient.t.sol/PositionHook.json is not allowed to be accessed for read operations] setUp() (gas: 0)\nSuite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 1.15ms (0.00ns CPU time)\n\nRan 1 test suite in 3.24ms (1.15ms CPU time): 0 tests passed, 1 failed, 0 skipped (1 total tests)\n\nFailing tests:\nEncountered 1 failing test in test/imd-proof-8ef41986/Proof_28dc1ba64f1c.t.sol:ProofSyncRecipientTest\n[FAIL: vm.getCode: the path out/Proof_SyncRecipient.t.sol/PositionHook.json is not allowed to be accessed for read operations] setUp() (gas: 0)\n\nEncountered a total of 1 failing tests, 0 tests succeeded\n\nTip: Run `forge test --rerun` to retry only the 1 failed test\nTip: Run `forge test --debug --match-test <TEST_NAME>` to inspect one failing test in the debugger\n","passed":false}],"detail":"the proof for \"payOut's guard ignores the PoolManager's synced currency: a move owner whose receive() only calls sync(token) is paid, and the ETH-paying trade that plays its move reverts NonzeroNativeValue, as does \" failed on the code it was reported against and still fails on this revision. Make it pass, or answer the finding \"disputed\" if the test itself is wrong.","evaluation":"checks","profile":"foundry","status":"rejected","submissionHash":"0e764952d268d69124256424fd903bc1ae19aeac183df6e0ec1280675bacaaec","verifiedTreeHash":"3d61d87689c86c4a0aa5dc716ae4cbb9436ddc5b","verifierVersion":"0.1.0+d372f99e"},{"checks":[{"durationMs":78895,"exitCode":0,"name":"build","output":"Compiling 119 files with Solc 0.8.26\nSolc 0.8.26 finished in 78.78s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:22:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                      ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:38:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                                      ^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:109:57:\n    |\n109 |             uint64 d = f >= r ? DIAG >> (8 * (f - r)) : DIAG << (8 * (r - f));\n    |                                                         ^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:110:65:\n    |\n110 |             uint64 a = f + r <= 7 ? ANTI >> (8 * (7 - f - r)) : ANTI << (8 * (f + r - 7));\n    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:38:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:69:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                                                     ^^^^^^^^^^^^^^^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:71:18:\n   |\n71 |         returns (uint16, Rules.Played memory)\n   |                  ^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:71:26:\n   |\n71 |         returns (uint16, Rules.Played memory)\n   |                          ^^^^^^^^^^^^^^^^^^^\n\nWarning (2072): Unused local variable.\n  --> test/Game.t.sol:90:9:\n   |\n90 |         IMDuelHook.Game memory g = st();\n   |         ^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n  --> test/LaunchFiles.t.sol:70:5:\n   |\n70 |     function test_engineLaunchFileDeploysTheEngineWithoutArguments() public {\n   |     ^ (Relevant source part starts here and spans across multiple lines).\n\n","passed":true},{"durationMs":3621,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Size.t.sol:SizeTest\n[PASS] test_deployedCodeFitsTheBudgets() (gas: 1253863)\nLogs:\n  hook: 20963\n  engine v5: 23339\n  route price verifier: 5551\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 742.25µs (643.96µs CPU time)\n\nRan 4 tests for test/Token.t.sol:TokenTest\n[PASS] test_allowances() (gas: 347653)\n[PASS] test_fixedSupplyToTheDeployer() (gas: 39384)\n[PASS] test_nothingPrivileged() (gas: 192691)\n[PASS] test_transfers() (gas: 188240)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 959.25µs (426.61µs CPU time)\n\nRan 3 tests for test/LaunchFiles.t.sol:LaunchFilesTest\n[PASS] test_engineLaunchFileDeploysTheEngineWithoutArguments() (gas: 7834)\n[PASS] test_ethereumLaunchFile() (gas: 1333515)\n[PASS] test_sepoliaLaunchFile() (gas: 1328569)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 3.01ms (2.39ms CPU time)\n\nRan 7 tests for test/Draws.t.sol:DrawsTest\n[PASS] test_checkmate() (gas: 240028)\n[PASS] test_moveCap() (gas: 240069)\n[PASS] test_ordinaryMoveDoesNotEndTheGame() (gas: 180017)\n[PASS] test_quietMoveLimit() (gas: 246287)\n[PASS] test_stalemate() (gas: 237841)\n[PASS] test_thirdRepetition() (gas: 964226)\n[PASS] test_tooFewPiecesToMate() (gas: 231666)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 4.22ms (2.84ms CPU time)\n\nRan 5 tests for test/Teams.t.sol:TeamsTest\n[PASS] test_aDynamicFeePoolPlaysWithAZeroLpFee() (gas: 2914515)\n[PASS] test_nativeEthIsATeamLikeAnyToken() (gas: 2328114)\n[PASS] test_poolsOnOneHookKeepSeparateGamesAndChests() (gas: 3391028)\n[PASS] test_sliceSide() (gas: 1820091)\n[PASS] test_twoPlainTokensCannotDuel() (gas: 1592088)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 7.89ms (8.52ms CPU time)\n\nRan 6 tests for test/RouteSample.t.sol:RouteSampleTest\n[PASS] test_authenticatedBlockStartsRejectTheSameAttack() (gas: 3422402)\n[PASS] test_callerSamplesCannotSellTheEthPileAtAPushedPrice() (gas: 1710709)\nLogs:\n  route attacker profit at the honest price: -87002115995172580\n\n[PASS] test_duplicateSampleCannotRotateOutHonestHistory() (gas: 493037)\n[PASS] test_gapRequiresThreeConsecutiveSamplesAgain() (gas: 1855898)\n[PASS] test_honestBatchRunsAndStopsAtTheHistoricalPriceBand() (gas: 1175548)\n[PASS] test_staleRoutePricesWaitWithoutLosingClaims() (gas: 1298197)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 10.80ms (16.90ms CPU time)\n\nRan 2 tests for test/FactoryLaunch.t.sol:FactoryLaunchTest\n[PASS] test_factoryLaunchWithRealTokenAboveImd() (gas: 36869239)\n[PASS] test_factoryLaunchWithRealTokenBelowImd() (gas: 36831558)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 27.40ms (51.59ms CPU time)\n\nRan 17 tests for test/CrossBlock.t.sol:CrossBlockTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5992198)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3971909)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3062844)\n[PASS] test_anEthBatchIsCapped() (gas: 5252475)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3201835)\n[PASS] test_anyoneCanConvert() (gas: 462029)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656485)\n[PASS] test_crossBlockViaConvert() (gas: 5556326)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390298682200\n\n[PASS] test_crossBlockViaFirstTrade() (gas: 5718430)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390291136661\n\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4252876)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245468)\n[PASS] test_ethRouteSameBlockPush() (gas: 5230164)\nLogs:\n  ETH pile before (1e18): 500000000000000000\n  ETH the batch sold (1e18): 0\n  IMD it got (1e18): 0\n  IMD it was worth at the home price (1e18): 0\n  attacker profit (ETH at the home price, 1e18): -29770639430607030\n\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764062)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487316)\n[PASS] test_noRouteNoDuel() (gas: 1030839)\n[PASS] test_oneEthBatchPerBlock() (gas: 5777542)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187178)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 30.62ms (54.51ms CPU time)\n\nRan 13 tests for test/RoutePriceVerifier.t.sol:RoutePriceVerifierTest\n[PASS] testFuzz_authenticatesSlot0AndItsBlock(uint160,uint96) (runs: 256, μ: 200948, ~: 200425)\nLogs:\n  Bound result 2964422583387036479856633470515551576907\n\n[PASS] test_changedAccountProofIsRefused() (gas: 100311)\n[PASS] test_changedHeaderIsNotCanonical() (gas: 80813)\n[PASS] test_changedStorageProofIsRefused() (gas: 158336)\n[PASS] test_clientGeneratedPoolManagerProof() (gas: 241704)\n[PASS] test_emptyProofsAreRefused() (gas: 195111)\n[PASS] test_extraProofNodesAreRefused() (gas: 195861)\n[PASS] test_outOfRangePriceIsRefused() (gas: 192156)\n[PASS] test_theBatchBlockCannotSupplyASample() (gas: 80841)\n[PASS] test_uninitializedPoolPriceIsRefused() (gas: 180149)\n[PASS] test_unknownOrExpiredHeaderIsRefused() (gas: 81201)\n[PASS] test_wrongManagerIsRefused() (gas: 108622)\n[PASS] test_wrongPoolSlotIsRefused() (gas: 165141)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 41.90ms (45.82ms CPU time)\n\nRan 8 tests for test/Strength.t.sol:StrengthTest\n[PASS] testFuzz_boundedAndMonotonic(uint256,uint256,uint256) (runs: 256, μ: 9098, ~: 8663)\nLogs:\n  Bound result 115792089237316195423570985008687907394007562470899364720687254562697241447318\n\n[PASS] testFuzz_depthMonotonic(uint256,uint256,uint256) (runs: 256, μ: 12020, ~: 12425)\nLogs:\n  Bound result 4608\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129635345\n\n[PASS] testFuzz_shiftKeepsTheShare(uint256,uint256,uint8) (runs: 256, μ: 14655, ~: 15266)\nLogs:\n  Bound result 10\n  Bound result 18\n  Bound result 4608\n\n[PASS] testFuzz_tenfoldIsAboutThirtyPoints(uint128,uint128) (runs: 256, μ: 13840, ~: 13724)\nLogs:\n  Bound result 340282366920938463463374607431767441456\n  Bound result 336879543251729078913183354370646567\n\n[PASS] testFuzz_unscalablePaidFollowsTheCurve(uint256,uint256) (runs: 256, μ: 13953, ~: 14208)\nLogs:\n  Bound result 115790931316423822261616749298837820974191451965793907633817189433447440043359\n  Bound result 115792089237316195423570985008687907853269984665640564039457446568959657720000\n\n[PASS] test_decidedPoints() (gas: 15577)\n[PASS] test_hugePaymentsDoNotRevert() (gas: 12854)\n[PASS] test_zeroInputs() (gas: 4493)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 41.97ms (103.54ms CPU time)\n\nRan 2 tests for test/LaunchAdmission.t.sol:LaunchAdmissionTest\n[PASS] test_blankChainTokenAboveImd() (gas: 22753347)\n[PASS] test_blankChainTokenBelowImd() (gas: 22682302)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 43.72ms (66.54ms CPU time)\n\nRan 14 tests for test/Slice.t.sol:SliceTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5991703)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3971891)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3062819)\n[PASS] test_anEthBatchIsCapped() (gas: 5252359)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3201761)\n[PASS] test_anyoneCanConvert() (gas: 462007)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656457)\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4252839)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245420)\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764048)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487334)\n[PASS] test_noRouteNoDuel() (gas: 1030789)\n[PASS] test_oneEthBatchPerBlock() (gas: 5777448)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187164)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 51.69ms (41.05ms CPU time)\n\nRan 8 tests for test/BidOwner.t.sol:BidOwnerTest\n[PASS] testFuzz_otherDataLengthsNeverCreditTheBundlerOrTheRouter(bytes) (runs: 256, μ: 383292, ~: 383288)\n[PASS] test_aHookWithoutAnEthRouteRejectsRouteProofs() (gas: 32534)\n[PASS] test_abiEncodedOwnerCreditsTheWallet() (gas: 384567)\n[PASS] test_abiEncodedOwnerReceivesTheCaptureBounty() (gas: 1122975)\n[PASS] test_dirtyAbiPaddingIsNobody() (gas: 382698)\n[PASS] test_missingOwnerIsNobodyNotTheRouterNorTheBundler() (gas: 415565)\n[PASS] test_missingOwnerPlaysButPaysNobody() (gas: 1151657)\n[PASS] test_packedOwnerCreditsTheWallet() (gas: 384686)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 51.83ms (51.69ms CPU time)\n\nRan 3 tests for test/Launch.t.sol:LaunchTest\n[PASS] test_launchPairedWithEth() (gas: 23114011)\n[PASS] test_launchTokenAboveImd() (gas: 24063809)\nLogs:\n  first buy (opens game 0, bids for White): 366422\n\n[PASS] test_launchTokenBelowImd() (gas: 23989193)\nLogs:\n  first buy (opens game 0, bids for White): 361672\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 60.52ms (155.29ms CPU time)\n\nRan 12 tests for test/Bidding.t.sol:BiddingTest\n[PASS] test_aBountyIsPaidFromTheMoversChest() (gas: 1363818)\n[PASS] test_aBuyOfTheOtherTeamBidsForItsNextMove() (gas: 1952666)\n[PASS] test_aBuyOfTheOtherTeamTimesTheSilentSideOutAndBids() (gas: 1222504)\n[PASS] test_aClosedWindowTakesNoBidsForItsMove() (gas: 1062884)\n[PASS] test_aMoveIsOpenForTurnBlocksBlocks() (gas: 1040466)\n[PASS] test_aNewTopMustPayMoreThanTenPercentMore() (gas: 974355)\n[PASS] test_aNextMoveBidCarriesIntoTheNextGame() (gas: 1467024)\n[PASS] test_aWindowWithoutBidsBecomesTheFirstBidsBlock() (gas: 1283690)\n[PASS] test_bidsCountAcrossTheWholeWindow() (gas: 1511214)\n[PASS] test_theMoveDependsOnlyOnTheWindowsLastBlock() (gas: 1621426)\n[PASS] test_theMoversOwnBuyAfterItsWindowBidsForItsNextMove() (gas: 1077593)\n[PASS] test_thePreviewIsTheMoveThatIsPlayed() (gas: 1222868)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 83.51ms (18.26ms CPU time)\n\nRan 15 tests for test/Hostile.t.sol:HostileTest\n[PASS] test_aCallbackTokenOwnerLeavingADebtCannotFreezeTheDuel() (gas: 5281617)\n[PASS] test_aCallbackTokenOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 5617407)\n[PASS] test_aCallbackTokenOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 5637162)\n[PASS] test_aDeadEngineLeavesTheTurnQueuedAndTradingGoesOn() (gas: 3556171)\n[PASS] test_aGasBurningEngineCostsOneExpensiveTradeNotTheGame() (gas: 9728512)\n[PASS] test_aTokenReturningNothingFundsAChest() (gas: 2186643)\n[PASS] test_anEthOwnerLeavingADebtCannotFreezeTheDuel() (gas: 4200022)\n[PASS] test_anEthOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 4487093)\n[PASS] test_anEthOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 4546952)\n[PASS] test_anOwnerThatCannotReceiveWithdrawsElsewhere() (gas: 3558858)\n[PASS] test_blockedRecipientStaysOwedAndTradingContinues() (gas: 4190135)\n[PASS] test_brokenEngineFallsBackToALegalMove() (gas: 5151439)\n[PASS] test_feeOnTransferTokenFundsOnlyWhatArrives() (gas: 2732535)\n[PASS] test_reenteringTokenCannotBreakAPayout() (gas: 4729548)\n[PASS] test_starvingTheEngineNeverPlaysTheFallback() (gas: 25149892)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 83.57ms (110.69ms CPU time)\n\nRan 5 tests for test/HookGas.t.sol:HookGasTest\n[PASS] test_conversionGas() (gas: 3643661)\nLogs:\n  plain trade: 207740\n  trade that converts a batch: 271602\n\n[PASS] test_hookShareOfAMovingTrade() (gas: 29142695)\nLogs:\n  hook share of a moving trade: average: 279291\n  hook share of a moving trade: max: 327966\n\n[PASS] test_middlegameGas() (gas: 9519488)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 191975\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 557243\n\n[PASS] test_openingGas() (gas: 4540681)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 192689\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 508014\n\n[PASS] test_runningGameGas() (gas: 54703750)\nLogs:\n  running game, moving trades measured: 50\n  running game, moving trade average: 452500\n  running game, moving trade max: 548698\n\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 95.73ms (140.73ms CPU time)\n\nRan 3 tests for test/GasEstimate.t.sol:GasEstimateTest\n[PASS] test_atTheEstimateADueBatchConverts() (gas: 4996933)\nLogs:\n  smallest gas limit that converts: 310375\n\n[PASS] test_atTheEstimateTheBountyIsPaidNotOwed() (gas: 4601044)\nLogs:\n  smallest gas limit that plays and pays: 411632\n\n[PASS] test_noGasLimitPlaysTheMoveAndLeavesItsBountyOwed() (gas: 18529107)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 133.92ms (151.29ms CPU time)\n\nRan 1 test for test/Predict.t.sol:PredictTest\n[PASS] test_aBotCannotPickItsOwnCapturingMoves() (gas: 289899607)\nLogs:\n  bot moves: 0\n  other moves: 74\n  captures among other moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 495.82ms (495.62ms CPU time)\n\nRan 23 tests for test/Game.t.sol:GameTest\n[PASS] testFuzz_swapsNeverRevert(uint256) (runs: 48, μ: 9606859, ~: 9583723)\n[PASS] test_aGamePlaysOutThroughSwaps() (gas: 134592260)\nLogs:\n  moves played: 187\n  bids (incl. outbid ones): 238\n  most gas for a swap: 685293\n\n[PASS] test_aSmallerTradeInTheBlockDoesNotTakeTheTurn() (gas: 686491)\n[PASS] test_aTimeoutTurnCanBeTakenInItsBlock() (gas: 1304210)\n[PASS] test_aTradeBidsForTheTurn() (gas: 483494)\n[PASS] test_aTurnClosesWithItsBlock() (gas: 965170)\n[PASS] test_anExpiredTurnBecomesATimeoutTurn() (gas: 1410536)\n[PASS] test_anOldTurnIsPlayedWithTheHistoryContractsHash() (gas: 1754873)\n[PASS] test_anyTradeSizeBids() (gas: 450095)\n[PASS] test_lateTradeStillBidsNormally() (gas: 429729)\n[PASS] test_onlyThePoolManagerAndTheHookCanCallIn() (gas: 171212)\n[PASS] test_ownerDefaultsToNobody() (gas: 449725)\n[PASS] test_playedPositionsEnterTheRepetitionMemory() (gas: 915368)\n[PASS] test_rejectsBadConfig() (gas: 9247)\n[PASS] test_silentSidePlaysAtZeroStrength() (gas: 1045239)\n[PASS] test_strengthFollowsTheShareOfThePool() (gas: 6374541)\n[PASS] test_teamsSwapColoursEveryGame() (gas: 926880)\n[PASS] test_theBiggestTradeInTheBlockOwnsTheTurn() (gas: 782415)\n[PASS] test_theFirstPoolOpenedIsTheMainDuel() (gas: 179471)\n[PASS] test_theMainDuelMustPairImd() (gas: 1466664)\n[PASS] test_theNextBlocksTradePlaysTheTurn() (gas: 1038495)\n[PASS] test_theOtherTeamCannotBidOnThisTurn() (gas: 412278)\n[PASS] test_whoPlaysTheTurnAndWhenDoesNotChangeTheMove() (gas: 1534194)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 742.22ms (950.51ms CPU time)\n\nRan 21 tests for test/Money.t.sol:MoneyTest\n[PASS] testFuzz_heldEqualsOwed(uint256) (runs: 48, μ: 10726217, ~: 10741382)\n[PASS] test_aFullChestTakesNoMore() (gas: 1059339)\n[PASS] test_aTradeStoppedByItsPriceLimitIsRefused() (gas: 582903)\n[PASS] test_blackCaptureIsPaidInItsTeamToken() (gas: 780846)\n[PASS] test_bountyTable() (gas: 44705)\n[PASS] test_checkmatePaysAQuarterOfTheChest() (gas: 760690)\n[PASS] test_chestShareGoesToTheChestWorthLess() (gas: 638456)\n[PASS] test_chestsStayAboutEqualInValue() (gas: 5511474)\n[PASS] test_exactOutputPaysTheFeeOnTheInput() (gas: 384979)\n[PASS] test_exactOutputWithTheChestShareInTheOutput() (gas: 780877)\n[PASS] test_failedPayoutStaysOwedAndTheTradeGoesThrough() (gas: 1217146)\n[PASS] test_feesCannotBeWithdrawnByAnyone() (gas: 449988)\n[PASS] test_firstTradeFundsChest0AndTheSlice() (gas: 363816)\n[PASS] test_fundChestNeedsTheRightValue() (gas: 48803)\n[PASS] test_fundChestOnlyForHookPools() (gas: 41868)\n[PASS] test_fundChestWithEth() (gas: 252918)\n[PASS] test_fundChestWithTokens() (gas: 193816)\n[PASS] test_openingSurchargeOnlyInTheMainDuel() (gas: 3469491)\n[PASS] test_payoutIsCappedAtTwiceTheTrade() (gas: 828202)\n[PASS] test_quietMovePaysNothing() (gas: 616203)\n[PASS] test_whiteCaptureIsPaidInItsTeamTokenFromItsOwnChest() (gas: 854514)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 801.68ms (820.01ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_boardHasBothKingsAndPlyIsBounded\n[PASS] invariant_heldEqualsOwed\n InvariantTest invariants (runs: 24, calls: 2400, reverts: 0)\n\n╭----------+----------------+-------+---------+----------╮\n| Contract | Selector       | Calls | Reverts | Discards |\n+========================================================+\n| Handler  | convert        | 301   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | fund           | 305   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | longPause      | 311   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | nextBlock      | 313   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | trade          | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeAgain     | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeNextBlock | 312   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | withdrawReward | 304   | 0       | 0        |\n╰----------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 53\n  Bound result 19999999000000000357\n  Bound result 19999999000001000001\n  Bound result 19999999000000020458\n  Bound result 19999999000000000755\n  Bound result 112\n  Bound result 18046385208871800035\n  Bound result 2754\n  Bound result 19999999007306351798\n  Bound result 11\n  Bound result 31865262234567526\n  Bound result 19999999000000887221\n  Bound result 463181382011195625\n  Bound result 19999999000000003151\n  Bound result 5000\n  Bound result 100\n  Bound result 7037082190885724022\n  Bound result 19999999386547056641\n  Bound result 18446744073709551615\n  Bound result 76\n  Bound result 4022291159582972627\n  Bound result 6\n  Bound result 8538\n  Bound result 19999999000000020001\n  Bound result 41\n  Bound result 386549022720\n  Bound result 796\n  Bound result 19999999000000000015\n  Bound result 1221339734816628308\n  Bound result 70\n  Bound result 104\n  Bound result 90\n  Bound result 81\n  Bound result 18\n  Bound result 120\n  Bound result 2719019064705274351\n  Bound result 19999999000000007454\n  Bound result 55\n  Bound result 1000000000003\n  Bound result 4413685840969725471\n  Bound result 1251\n  Bound result 17\n  Bound result 19999999000000000031\n  Bound result 19999999000000000904\n  Bound result 19999999000000000727\n  Bound result 15672660589582542013\n  Bound result 20396340581799926600\n  Bound result 13156\n  Bound result 19999999000000000321\n  Bound result 19999999000000004184\n  Bound result 12566133904435631808\n  Bound result 15386197539150620393\n  Bound result 2\n  Bound result 30\n  Bound result 8911052841178915334\n  Bound result 19999999000000003822\n  Bound result 7037082190885724022\n  Bound result 4999999999995\n  Bound result 5060859166916801136\n  Bound result 100000\n  Bound result 45\n  swaps: 33\n  moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.00s (1.00s CPU time)\n\nRan 1 test for test/InvariantDuels.t.sol:InvariantDuelsTest\n[PASS]\nInvariantDuelsTest invariants:\n[PASS] invariant_everyBoardIsSound\n[PASS] invariant_everyCurrencyBalances\n[PASS] invariant_rightsMatchTheBoard\n[PASS] invariant_turnsAreWellFormed\n InvariantDuelsTest invariants (runs: 24, calls: 2880, reverts: 0)\n\n╭--------------+-------------------+-------+---------+----------╮\n| Contract     | Selector          | Calls | Reverts | Discards |\n+===============================================================+\n| DuelsHandler | addLiquidity      | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convert           | 220   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convertNextBlock  | 200   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | fund              | 241   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | longPause         | 221   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | nextBlock         | 215   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | pushThenNextBlock | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | removeLiquidity   | 253   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | roundTrip         | 251   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | trade             | 232   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeAgain        | 198   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeNextBlock    | 223   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | withdrawReward    | 210   | 0       | 0        |\n╰--------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 23181\n  Bound result 2565653098084874863\n  Bound result 12\n  Bound result 100000000\n  Bound result 72624976668147840\n  Bound result 4076725131\n  Bound result 12\n  Bound result 19999999999000000049\n  Bound result 4485934425771658475\n  Bound result 120\n  Bound result 12\n  Bound result 4503599627370496\n  Bound result 12\n  Bound result 19999999999000011559\n  Bound result 499999999500000000\n  Bound result 7000\n  Bound result 19999999999000017411\n  Bound result 19999999999000200001\n  Bound result 12\n  Bound result 19999999999000000016\n  Bound result 19999999999000106693\n  Bound result 25333134452981760\n  Bound result 133294633079153423663\n  Bound result 12\n  Bound result 2296800487608934368\n  Bound result 12\n  Bound result 19999999999000010437\n  Bound result 12\n  Bound result 19999999999000014838\n  Bound result 12\n  Bound result 19999999999000006727\n  Bound result 180000000000000004462\n  Bound result 12\n  Bound result 4999999999000017032\n  Bound result 5505625188992501493\n  Bound result 12\n  Bound result 180000000000000000310\n  Bound result 12\n  Bound result 180000000000000009233\n  Bound result 12\n  Bound result 14673026436775943699\n  Bound result 52\n  Bound result 19999999999000014175\n  Bound result 12\n  Bound result 19999999999000018288\n  Bound result 180000000000000800001\n  Bound result 12\n  Bound result 6017502607293588406\n  Bound result 9257010020096093371786\n  Bound result 9999999899999990000000\n  Bound result 1649057649081008781253\n  Bound result 17715909253537394009\n  Bound result 12\n  Bound result 600000000000000000000\n  Bound result 19999999999000015200\n  Bound result 5000000000000000000\n  Bound result 805801794754002874\n  Bound result 4999999999000000601\n  Bound result 180000000000000009832\n  Bound result 12\n  Bound result 180000000000000043129\n  Bound result 12\n  Bound result 12\n  Bound result 3935650426\n  Bound result 12\n  Bound result 12\n  Bound result 100000000000000000000\n  Bound result 19999999999000000733\n  Bound result 12\n  Bound result 32\n  Bound result 4999999999001966081\n  Bound result 12\n  Bound result 458424615607726111\n  Bound result 19999999999000022394\n  Bound result 19999999999000065281\n  Bound result 12\n  Bound result 17830126982352984981\n  Bound result 90\n  Bound result 9000000000000000000\n  Bound result 11690505567452632168\n  Bound result 120\n  Bound result 27612829230727045392\n  Bound result 12\n  Bound result 80\n  Bound result 4999999999000012719\n  Bound result 1000178988994931336848\n  Bound result 180000000000000887271\n  Bound result 12\n  Bound result 21931300337370667194\n  Bound result 184000000000000000001\n  Bound result 12\n  Bound result 112\n  Bound result 180000000000000023900\n  Bound result 12\n  Bound result 180000000000000995001\n  Bound result 12\n  Bound result 46649393329207593530\n  Bound result 12\n  Bound result 2155872511\n  Bound result 32680281041836782903\n  Bound result 1100177988994931344681\n  Bound result 19999999999000000996\n  Bound result 1812930352692670847190\n  Bound result 4000000000000000\n  Bound result 3398176614991109133\n  Bound result 19999999999000094628\n  swaps: 43\n  moves: 21\n  Bound result 12\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.46s (3.46s CPU time)\n\nRan 23 test suites in 3.47s (7.28s CPU time): 175 tests passed, 0 failed, 0 skipped (175 total tests)\n","passed":true},{"durationMs":68,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDuelHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMDuelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDuelHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDuelHook.convert(address)\",\"IMDuelHook.convertBatch(address)\",\"IMDuelHook.fundChest((address,address,uint24,int24,address),bool,uint256)\",\"IMDuelHook.payOut((address,address,uint24,int24,address),address,address,uint256)\",\"IMDuelHook.sampleRoute(bytes,bytes[],bytes[])\",\"IMDuelHook.unlockCallback(bytes)\",\"IMDuelHook.withdraw(address,address)\",\"IMDuelHook.withdrawTo(address,address)\",\"IMDuelToken.approve(address,uint256)\",\"IMDuelToken.transfer(address,uint256)\",\"IMDuelToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":102,\"LICENSE\":16,\"foundry.toml\":29,\"launch.json\":37,\"launch/deployed.json\":12,\"launch/engine.json\":10,\"launch/hook.json\":39,\"launch/hook.sepolia.json\":39,\"script/LaunchPolicy.sol\":44,\"script/route_proof.py\":135,\"src/Bitboard.sol\":719,\"src/Chess.sol\":34,\"src/ChessEngineV5.sol\":852,\"src/IChessEngine.sol\":29,\"src/IMDuelHook.sol\":967,\"src/IMDuelToken.sol\":62,\"src/RoutePriceVerifier.sol\":50,\"src/Rules.sol\":45,\"src/Strength.sol\":47,\"src/proof/Bytes.sol\":144,\"src/proof/LICENSE\":22,\"src/proof/MerkleTrie.sol\":216,\"src/proof/PROVENANCE.md\":12,\"src/proof/RLPErrors.sol\":20,\"src/proof/RLPReader.sol\":249,\"test/Base.sol\":211,\"test/BidOwner.t.sol\":118,\"test/Bidding.t.sol\":224,\"test/Chessboard.sol\":79,\"test/CrossBlock.t.sol\":195,\"test/Draws.t.sol\":90,\"test/FactoryLaunch.t.sol\":173,\"test/Game.t.sol\":348,\"test/GasEstimate.t.sol\":120,\"test/Harness.sol\":83,\"test/HookGas.t.sol\":151,\"test/Hostile.t.sol\":461,\"test/Invariant.t.sol\":150,\"test/InvariantDuels.t.sol\":529,\"test/Launch.t.sol\":159,\"test/LaunchAdmission.t.sol\":167,\"test/LaunchFiles.t.sol\":75,\"test/Money.t.sol\":353,\"test/Predict.t.sol\":102,\"test/RoutePriceVerifier.t.sol\":135,\"test/RouteProof.sol\":69,\"test/RouteSample.t.sol\":168,\"test/Size.t.sol\":36,\"test/Slice.t.sol\":392,\"test/Strength.t.sol\":85,\"test/Teams.t.sol\":87,\"test/Token.t.sol\":82,\"test/Tokens.sol\":172,\"test/V4Fixture.sol\":100,\"test/data/chess.json\":1,\"test/data/conversion-validation.json\":770,\"test/data/conversion.json\":98,\"test/data/endings.json\":1,\"test/data/legal.json\":1,\"test/data/random.json\":1,\"test/data/route-proof.json\":16,\"test/test_route_proof.py\":72},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"5c8b6612b65404ecd259a404b38c7b12d2d906c3fa75b8c1502ded3aebed26f0","verifiedTreeHash":"168799c8714eb66267446f0ec44153da00155d10","verifierVersion":"0.1.0+d372f99e"},{"checks":[{"durationMs":80398,"exitCode":0,"name":"build","output":"Compiling 119 files with Solc 0.8.26\nSolc 0.8.26 finished in 80.27s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:22:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                      ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:38:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                                      ^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:109:57:\n    |\n109 |             uint64 d = f >= r ? DIAG >> (8 * (f - r)) : DIAG << (8 * (r - f));\n    |                                                         ^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:110:65:\n    |\n110 |             uint64 a = f + r <= 7 ? ANTI >> (8 * (7 - f - r)) : ANTI << (8 * (f + r - 7));\n    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:38:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:69:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                                                     ^^^^^^^^^^^^^^^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:62:18:\n   |\n62 |         returns (uint16, Rules.Played memory)\n   |                  ^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:62:26:\n   |\n62 |         returns (uint16, Rules.Played memory)\n   |                          ^^^^^^^^^^^^^^^^^^^\n\nWarning (2072): Unused local variable.\n  --> test/Game.t.sol:87:9:\n   |\n87 |         IMDuelHook.Game memory g = st();\n   |         ^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n  --> test/LaunchFiles.t.sol:70:5:\n   |\n70 |     function test_engineLaunchFileDeploysTheEngineWithoutArguments() public {\n   |     ^ (Relevant source part starts here and spans across multiple lines).\n\n","passed":true},{"durationMs":3748,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 7 tests for test/Draws.t.sol:DrawsTest\n[PASS] test_checkmate() (gas: 240001)\n[PASS] test_moveCap() (gas: 240042)\n[PASS] test_ordinaryMoveDoesNotEndTheGame() (gas: 179990)\n[PASS] test_quietMoveLimit() (gas: 246260)\n[PASS] test_stalemate() (gas: 237814)\n[PASS] test_thirdRepetition() (gas: 964010)\n[PASS] test_tooFewPiecesToMate() (gas: 231639)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 5.07ms (3.30ms CPU time)\n\nRan 1 test for test/Size.t.sol:SizeTest\n[PASS] test_deployedCodeFitsTheBudgets() (gas: 1252630)\nLogs:\n  hook: 20620\n  engine v5: 23339\n  route price verifier: 5551\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 6.58ms (6.45ms CPU time)\n\nRan 3 tests for test/LaunchFiles.t.sol:LaunchFilesTest\n[PASS] test_engineLaunchFileDeploysTheEngineWithoutArguments() (gas: 7834)\n[PASS] test_ethereumLaunchFile() (gas: 1332241)\n[PASS] test_sepoliaLaunchFile() (gas: 1327328)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 7.33ms (6.74ms CPU time)\n\nRan 5 tests for test/Teams.t.sol:TeamsTest\n[PASS] test_aDynamicFeePoolPlaysWithAZeroLpFee() (gas: 2913516)\n[PASS] test_nativeEthIsATeamLikeAnyToken() (gas: 2327096)\n[PASS] test_poolsOnOneHookKeepSeparateGamesAndChests() (gas: 3404887)\n[PASS] test_sliceSide() (gas: 1819047)\n[PASS] test_twoPlainTokensCannotDuel() (gas: 1591040)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 7.33ms (6.60ms CPU time)\n\nRan 12 tests for test/Bidding.t.sol:BiddingTest\n[PASS] test_aBountyIsPaidFromTheMoversChest() (gas: 1357258)\n[PASS] test_aBuyOfTheOtherTeamBidsForItsNextMove() (gas: 1952736)\n[PASS] test_aBuyOfTheOtherTeamTimesTheSilentSideOutAndBids() (gas: 1222539)\n[PASS] test_aClosedWindowTakesNoBidsForItsMove() (gas: 1062919)\n[PASS] test_aMoveIsOpenForTurnBlocksBlocks() (gas: 1040501)\n[PASS] test_aNewTopMustPayMoreThanTenPercentMore() (gas: 974403)\n[PASS] test_aNextMoveBidCarriesIntoTheNextGame() (gas: 1467032)\n[PASS] test_aWindowWithoutBidsBecomesTheFirstBidsBlock() (gas: 1283751)\n[PASS] test_bidsCountAcrossTheWholeWindow() (gas: 1511281)\n[PASS] test_theMoveDependsOnlyOnTheWindowsLastBlock() (gas: 1621480)\n[PASS] test_theMoversOwnBuyAfterItsWindowBidsForItsNextMove() (gas: 1077628)\n[PASS] test_thePreviewIsTheMoveThatIsPlayed() (gas: 1222903)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 26.47ms (25.16ms CPU time)\n\nRan 17 tests for test/CrossBlock.t.sol:CrossBlockTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 6015280)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3994805)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3061845)\n[PASS] test_anEthBatchIsCapped() (gas: 5275537)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3200810)\n[PASS] test_anyoneCanConvert() (gas: 462085)\n[PASS] test_booksBalanceAcrossPools() (gas: 8475081)\n[PASS] test_crossBlockViaConvert() (gas: 5551997)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390298682200\n\n[PASS] test_crossBlockViaFirstTrade() (gas: 5714127)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390291136661\n\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4251851)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245524)\n[PASS] test_ethRouteSameBlockPush() (gas: 5229243)\nLogs:\n  ETH pile before (1e18): 500000000000000000\n  ETH the batch sold (1e18): 0\n  IMD it got (1e18): 0\n  IMD it was worth at the home price (1e18): 0\n  attacker profit (ETH at the home price, 1e18): -29770639430607030\n\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764118)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487372)\n[PASS] test_noRouteNoDuel() (gas: 1030839)\n[PASS] test_oneEthBatchPerBlock() (gas: 5800437)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187260)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 33.19ms (69.96ms CPU time)\n\nRan 2 tests for test/FactoryLaunch.t.sol:FactoryLaunchTest\n[PASS] test_factoryLaunchWithRealTokenAboveImd() (gas: 15562391)\n[PASS] test_factoryLaunchWithRealTokenBelowImd() (gas: 15528327)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 40.54ms (35.48ms CPU time)\n\nRan 4 tests for test/Token.t.sol:TokenTest\n[PASS] test_allowances() (gas: 347653)\n[PASS] test_fixedSupplyToTheDeployer() (gas: 39384)\n[PASS] test_nothingPrivileged() (gas: 192691)\n[PASS] test_transfers() (gas: 188240)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 45.83ms (793.23µs CPU time)\n\nRan 14 tests for test/Slice.t.sol:SliceTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 6014785)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3994787)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3061820)\n[PASS] test_anEthBatchIsCapped() (gas: 5275421)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3200736)\n[PASS] test_anyoneCanConvert() (gas: 462063)\n[PASS] test_booksBalanceAcrossPools() (gas: 8475053)\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4251814)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245476)\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764104)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487390)\n[PASS] test_noRouteNoDuel() (gas: 1030789)\n[PASS] test_oneEthBatchPerBlock() (gas: 5800343)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187246)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 52.92ms (57.91ms CPU time)\n\nRan 13 tests for test/RoutePriceVerifier.t.sol:RoutePriceVerifierTest\n[PASS] testFuzz_authenticatesSlot0AndItsBlock(uint160,uint96) (runs: 256, μ: 200948, ~: 200425)\nLogs:\n  Bound result 7385035263781213683944894616182233\n\n[PASS] test_changedAccountProofIsRefused() (gas: 100311)\n[PASS] test_changedHeaderIsNotCanonical() (gas: 80813)\n[PASS] test_changedStorageProofIsRefused() (gas: 158336)\n[PASS] test_clientGeneratedPoolManagerProof() (gas: 241704)\n[PASS] test_emptyProofsAreRefused() (gas: 195111)\n[PASS] test_extraProofNodesAreRefused() (gas: 195861)\n[PASS] test_outOfRangePriceIsRefused() (gas: 192156)\n[PASS] test_theBatchBlockCannotSupplyASample() (gas: 80841)\n[PASS] test_uninitializedPoolPriceIsRefused() (gas: 180149)\n[PASS] test_unknownOrExpiredHeaderIsRefused() (gas: 81201)\n[PASS] test_wrongManagerIsRefused() (gas: 108622)\n[PASS] test_wrongPoolSlotIsRefused() (gas: 165141)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 52.89ms (60.61ms CPU time)\n\nRan 2 tests for test/LaunchAdmission.t.sol:LaunchAdmissionTest\n[PASS] test_blankChainTokenAboveImd() (gas: 25012847)\n[PASS] test_blankChainTokenBelowImd() (gas: 24941802)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 54.49ms (98.39ms CPU time)\n\nRan 8 tests for test/BidOwner.t.sol:BidOwnerTest\n[PASS] testFuzz_otherDataLengthsNeverCreditTheBundler(bytes) (runs: 256, μ: 385548, ~: 385542)\n[PASS] test_aHookWithoutAnEthRouteRejectsRouteProofs() (gas: 32512)\n[PASS] test_abiEncodedOwnerCreditsTheWallet() (gas: 384610)\n[PASS] test_abiEncodedOwnerReceivesTheCaptureBounty() (gas: 1116469)\n[PASS] test_dirtyAbiPaddingFallsBackToSender() (gas: 384788)\n[PASS] test_missingOwnerPaysTheSwapSender() (gas: 1133080)\n[PASS] test_missingOwnerUsesSwapSenderNotBundler() (gas: 419516)\n[PASS] test_packedOwnerCreditsTheWallet() (gas: 384689)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 56.57ms (57.97ms CPU time)\n\nRan 3 tests for test/Launch.t.sol:LaunchTest\n[PASS] test_launchPairedWithEth() (gas: 22438868)\n[PASS] test_launchTokenAboveImd() (gas: 24832045)\nLogs:\n  first buy (opens game 0, bids for White): 366448\n\n[PASS] test_launchTokenBelowImd() (gas: 24212457)\nLogs:\n  first buy (opens game 0, bids for White): 361698\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 69.23ms (168.48ms CPU time)\n\nRan 6 tests for test/RouteSample.t.sol:RouteSampleTest\n[PASS] test_authenticatedBlockStartsRejectTheSameAttack() (gas: 3422402)\n[PASS] test_callerSamplesCannotSellTheEthPileAtAPushedPrice() (gas: 1710709)\nLogs:\n  route attacker profit at the honest price: -87002115995172580\n\n[PASS] test_duplicateSampleCannotRotateOutHonestHistory() (gas: 493037)\n[PASS] test_gapRequiresThreeConsecutiveSamplesAgain() (gas: 1855928)\n[PASS] test_honestBatchRunsAndStopsAtTheHistoricalPriceBand() (gas: 1175578)\n[PASS] test_staleRoutePricesWaitWithoutLosingClaims() (gas: 1298227)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 74.74ms (27.42ms CPU time)\n\nRan 11 tests for test/Hostile.t.sol:HostileTest\n[PASS] test_aCallbackTokenOwnerLeavingADebtCannotFreezeTheDuel() (gas: 5276059)\n[PASS] test_aDeadEngineLeavesTheTurnQueuedAndTradingGoesOn() (gas: 3555280)\n[PASS] test_aGasBurningEngineCostsOneExpensiveTradeNotTheGame() (gas: 9727669)\n[PASS] test_aTokenReturningNothingFundsAChest() (gas: 2185559)\n[PASS] test_anEthOwnerLeavingADebtCannotFreezeTheDuel() (gas: 4171134)\n[PASS] test_anOwnerThatCannotReceiveWithdrawsElsewhere() (gas: 3554214)\n[PASS] test_blockedRecipientStaysOwedAndTradingContinues() (gas: 4185544)\n[PASS] test_brokenEngineFallsBackToALegalMove() (gas: 5150392)\n[PASS] test_feeOnTransferTokenFundsOnlyWhatArrives() (gas: 2731451)\n[PASS] test_reenteringTokenCannotBreakAPayout() (gas: 4721771)\n[PASS] test_starvingTheEngineNeverPlaysTheFallback() (gas: 25149078)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 74.73ms (80.89ms CPU time)\n\nRan 8 tests for test/Strength.t.sol:StrengthTest\n[PASS] testFuzz_boundedAndMonotonic(uint256,uint256,uint256) (runs: 256, μ: 9098, ~: 8663)\nLogs:\n  Bound result 4608\n\n[PASS] testFuzz_depthMonotonic(uint256,uint256,uint256) (runs: 256, μ: 12020, ~: 12425)\nLogs:\n  Bound result 4608\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129635345\n\n[PASS] testFuzz_shiftKeepsTheShare(uint256,uint256,uint8) (runs: 256, μ: 14655, ~: 15266)\nLogs:\n  Bound result 1\n  Bound result 13774059\n  Bound result 84743910324299725283116389454424367006300368779282074797\n\n[PASS] testFuzz_tenfoldIsAboutThirtyPoints(uint128,uint128) (runs: 256, μ: 13840, ~: 13724)\nLogs:\n  Bound result 340282366920938463463374607431767441456\n  Bound result 336879543251729078913183354370646567\n\n[PASS] testFuzz_unscalablePaidFollowsTheCurve(uint256,uint256) (runs: 256, μ: 13953, ~: 14208)\nLogs:\n  Bound result 115790931316423822261616749298837820974191451965793907633817189433447440043359\n  Bound result 115792089237316195423570985008687907853269984665640564039457446568959657720000\n\n[PASS] test_decidedPoints() (gas: 15577)\n[PASS] test_hugePaymentsDoNotRevert() (gas: 12854)\n[PASS] test_zeroInputs() (gas: 4493)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 106.49ms (39.08ms CPU time)\n\nRan 5 tests for test/HookGas.t.sol:HookGasTest\n[PASS] test_conversionGas() (gas: 3656290)\nLogs:\n  plain trade: 207766\n  trade that converts a batch: 271658\n\n[PASS] test_hookShareOfAMovingTrade() (gas: 29104655)\nLogs:\n  hook share of a moving trade: average: 278442\n  hook share of a moving trade: max: 321393\n\n[PASS] test_middlegameGas() (gas: 9501378)\nLogs:\n  first bid in a new pool: 270627\n  bidding trade: 191991\n  outbidding trade: 208087\n  plain trade: 182668\n  next block: play the turn and bid: 550662\n\n[PASS] test_openingGas() (gas: 4539846)\nLogs:\n  first bid in a new pool: 270627\n  bidding trade: 192702\n  outbidding trade: 208087\n  plain trade: 182668\n  next block: play the turn and bid: 508031\n\n[PASS] test_runningGameGas() (gas: 54586984)\nLogs:\n  running game, moving trades measured: 50\n  running game, moving trade average: 450671\n  running game, moving trade max: 542125\n\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 113.58ms (154.28ms CPU time)\n\nRan 3 tests for test/GasEstimate.t.sol:GasEstimateTest\n[PASS] test_atTheEstimateADueBatchConverts() (gas: 5009784)\nLogs:\n  smallest gas limit that converts: 310375\n\n[PASS] test_atTheEstimateTheBountyIsPaidNotOwed() (gas: 4997823)\nLogs:\n  smallest gas limit that plays and pays: 404882\n\n[PASS] test_noGasLimitPlaysTheMoveAndLeavesItsBountyOwed() (gas: 18563180)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 155.73ms (178.30ms CPU time)\n\nRan 23 tests for test/Game.t.sol:GameTest\n[PASS] testFuzz_swapsNeverRevert(uint256) (runs: 48, μ: 9661448, ~: 9658868)\n[PASS] test_aGamePlaysOutThroughSwaps() (gas: 57294244)\nLogs:\n  moves played: 79\n  bids (incl. outbid ones): 101\n  most gas for a swap: 1125299\n\n[PASS] test_aSmallerTradeInTheBlockDoesNotTakeTheTurn() (gas: 686523)\n[PASS] test_aTimeoutTurnCanBeTakenInItsBlock() (gas: 1304281)\n[PASS] test_aTradeBidsForTheTurn() (gas: 483510)\n[PASS] test_aTurnClosesWithItsBlock() (gas: 965205)\n[PASS] test_anExpiredTurnBecomesATimeoutTurn() (gas: 1413397)\n[PASS] test_anOldTurnIsPlayedWithTheHistoryContractsHash() (gas: 1754927)\n[PASS] test_anyTradeSizeBids() (gas: 450089)\n[PASS] test_lateTradeStillBidsNormally() (gas: 429723)\n[PASS] test_onlyThePoolManagerAndTheHookCanCallIn() (gas: 169413)\n[PASS] test_ownerDefaultsToSwapSender() (gas: 418165)\n[PASS] test_playedPositionsEnterTheRepetitionMemory() (gas: 915403)\n[PASS] test_rejectsBadConfig() (gas: 9247)\n[PASS] test_silentSidePlaysAtZeroStrength() (gas: 1048084)\n[PASS] test_strengthFollowsTheShareOfThePool() (gas: 6374901)\n[PASS] test_teamsSwapColoursEveryGame() (gas: 926885)\n[PASS] test_theBiggestTradeInTheBlockOwnsTheTurn() (gas: 782425)\n[PASS] test_theFirstPoolOpenedIsTheMainDuel() (gas: 179471)\n[PASS] test_theMainDuelMustPairImd() (gas: 1465618)\n[PASS] test_theNextBlocksTradePlaysTheTurn() (gas: 1038530)\n[PASS] test_theOtherTeamCannotBidOnThisTurn() (gas: 412294)\n[PASS] test_whoPlaysTheTurnAndWhenDoesNotChangeTheMove() (gas: 1534248)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 728.31ms (846.71ms CPU time)\n\nRan 21 tests for test/Money.t.sol:MoneyTest\n[PASS] testFuzz_heldEqualsOwed(uint256) (runs: 48, μ: 10720442, ~: 10797515)\n[PASS] test_aFullChestTakesNoMore() (gas: 1059275)\n[PASS] test_aTradeStoppedByItsPriceLimitIsRefused() (gas: 582949)\n[PASS] test_blackCaptureIsPaidInItsTeamToken() (gas: 774230)\n[PASS] test_bountyTable() (gas: 44837)\n[PASS] test_checkmatePaysAQuarterOfTheChest() (gas: 754080)\n[PASS] test_chestShareGoesToTheChestWorthLess() (gas: 638508)\n[PASS] test_chestsStayAboutEqualInValue() (gas: 5512254)\n[PASS] test_exactOutputPaysTheFeeOnTheInput() (gas: 385005)\n[PASS] test_exactOutputWithTheChestShareInTheOutput() (gas: 790954)\n[PASS] test_failedPayoutStaysOwedAndTheTradeGoesThrough() (gas: 1213589)\n[PASS] test_feesCannotBeWithdrawnByAnyone() (gas: 450014)\n[PASS] test_firstTradeFundsChest0AndTheSlice() (gas: 363842)\n[PASS] test_fundChestNeedsTheRightValue() (gas: 48758)\n[PASS] test_fundChestOnlyForHookPools() (gas: 41896)\n[PASS] test_fundChestWithEth() (gas: 252873)\n[PASS] test_fundChestWithTokens() (gas: 193846)\n[PASS] test_openingSurchargeOnlyInTheMainDuel() (gas: 3489323)\n[PASS] test_payoutIsCappedAtTwiceTheTrade() (gas: 821586)\n[PASS] test_quietMovePaysNothing() (gas: 616187)\n[PASS] test_whiteCaptureIsPaidInItsTeamTokenFromItsOwnChest() (gas: 847898)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 895.61ms (915.70ms CPU time)\n\nRan 1 test for test/Predict.t.sol:PredictTest\n[PASS] test_aBotCannotPickItsOwnCapturingMoves() (gas: 603211199)\nLogs:\n  bot moves: 0\n  other moves: 150\n  captures among other moves: 25\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.11s (1.11s CPU time)\n\nRan 1 test for test/Invariant.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_boardHasBothKingsAndPlyIsBounded\n[PASS] invariant_heldEqualsOwed\n InvariantTest invariants (runs: 24, calls: 2400, reverts: 0)\n\n╭----------+----------------+-------+---------+----------╮\n| Contract | Selector       | Calls | Reverts | Discards |\n+========================================================+\n| Handler  | convert        | 301   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | fund           | 305   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | longPause      | 311   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | nextBlock      | 313   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | trade          | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeAgain     | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeNextBlock | 312   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | withdrawReward | 304   | 0       | 0        |\n╰----------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3\n  Bound result 19999999000000004867\n  Bound result 19999999000001000001\n  Bound result 19999999000000008654\n  Bound result 19999999000000000581\n  Bound result 91\n  Bound result 18046385208871800035\n  Bound result 2908\n  Bound result 19999999007306351798\n  Bound result 11\n  Bound result 31865262234567526\n  Bound result 19999999000000887221\n  Bound result 19999999000000003146\n  Bound result 19999999000000002182\n  Bound result 5000\n  Bound result 102\n  Bound result 7037082190885724022\n  Bound result 19999999386547056641\n  Bound result 18446744073709551615\n  Bound result 90\n  Bound result 4022291159582972627\n  Bound result 6\n  Bound result 4113140427\n  Bound result 19999999000000020001\n  Bound result 79\n  Bound result 386549022720\n  Bound result 1205\n  Bound result 19999999000000000015\n  Bound result 1221339734816628308\n  Bound result 35\n  Bound result 83\n  Bound result 90\n  Bound result 79\n  Bound result 40\n  Bound result 120\n  Bound result 19999999000000001148\n  Bound result 19999999000000007280\n  Bound result 55\n  Bound result 1000000000003\n  Bound result 4413685840969725471\n  Bound result 1251\n  Bound result 119\n  Bound result 19999999000000000031\n  Bound result 19999999000000000904\n  Bound result 19999999000000003380\n  Bound result 15672660589582542013\n  Bound result 20396340581799926600\n  Bound result 13006\n  Bound result 19999999000000000321\n  Bound result 19999999000000004445\n  Bound result 12566133904435631808\n  Bound result 15386197539150620393\n  Bound result 2\n  Bound result 14\n  Bound result 19999999000000002240\n  Bound result 19999999000000003282\n  Bound result 7037082190885724022\n  Bound result 4999999999995\n  Bound result 5060859166916801136\n  Bound result 100000\n  Bound result 20\n  swaps: 33\n  moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.11s (1.10s CPU time)\n\nRan 1 test for test/InvariantDuels.t.sol:InvariantDuelsTest\n[PASS]\nInvariantDuelsTest invariants:\n[PASS] invariant_everyBoardIsSound\n[PASS] invariant_everyCurrencyBalances\n[PASS] invariant_rightsMatchTheBoard\n[PASS] invariant_turnsAreWellFormed\n InvariantDuelsTest invariants (runs: 24, calls: 2880, reverts: 0)\n\n╭--------------+-------------------+-------+---------+----------╮\n| Contract     | Selector          | Calls | Reverts | Discards |\n+===============================================================+\n| DuelsHandler | addLiquidity      | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convert           | 220   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convertNextBlock  | 200   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | fund              | 241   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | longPause         | 221   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | nextBlock         | 215   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | pushThenNextBlock | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | removeLiquidity   | 253   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | roundTrip         | 251   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | trade             | 232   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeAgain        | 198   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeNextBlock    | 223   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | withdrawReward    | 210   | 0       | 0        |\n╰--------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 23537\n  Bound result 2565653098084874863\n  Bound result 12\n  Bound result 100000000\n  Bound result 72624976668147840\n  Bound result 3057007858\n  Bound result 12\n  Bound result 19999999999000000049\n  Bound result 4485934425771658475\n  Bound result 120\n  Bound result 12\n  Bound result 4503599627370496\n  Bound result 12\n  Bound result 19999999999000010777\n  Bound result 499999999500000000\n  Bound result 7000\n  Bound result 19999999999000017653\n  Bound result 19999999999000200001\n  Bound result 12\n  Bound result 19999999999000000016\n  Bound result 19999999999000106693\n  Bound result 25333134452981760\n  Bound result 133294633079153423663\n  Bound result 12\n  Bound result 19999999999000000901\n  Bound result 12\n  Bound result 19999999999000009496\n  Bound result 12\n  Bound result 19999999999000014703\n  Bound result 12\n  Bound result 19999999999000006767\n  Bound result 180000000000000005449\n  Bound result 12\n  Bound result 5505625188992501493\n  Bound result 12\n  Bound result 135878278616570359945\n  Bound result 12\n  Bound result 180000000000000020167\n  Bound result 12\n  Bound result 13206156172284423533\n  Bound result 52\n  Bound result 19999999999000015473\n  Bound result 12\n  Bound result 19999999999000017460\n  Bound result 180000000000000800001\n  Bound result 12\n  Bound result 4999999999000008226\n  Bound result 6017502607293588406\n  Bound result 29218547819496286143\n  Bound result 9999999899999990000000\n  Bound result 17715909253537394009\n  Bound result 12\n  Bound result 19999999999000014964\n  Bound result 5000000000000000000\n  Bound result 805801794754002874\n  Bound result 4999999999000000601\n  Bound result 180000000000000006508\n  Bound result 12\n  Bound result 180000000000000043129\n  Bound result 12\n  Bound result 12\n  Bound result 1725540769\n  Bound result 12\n  Bound result 12\n  Bound result 100000000000000000000\n  Bound result 19999999999000001150\n  Bound result 12\n  Bound result 25\n  Bound result 4999999999001966081\n  Bound result 12\n  Bound result 753286538007973485\n  Bound result 19999999999000004107\n  Bound result 19999999999000065281\n  Bound result 12\n  Bound result 17830126982352984981\n  Bound result 99\n  Bound result 9000000000000000000\n  Bound result 14236200221733302947\n  Bound result 120\n  Bound result 27612829230727045392\n  Bound result 12\n  Bound result 80\n  Bound result 4999999999107477463\n  Bound result 1000178988994931336848\n  Bound result 180000000000000887271\n  Bound result 12\n  Bound result 21931300337370667194\n  Bound result 184000000000000000001\n  Bound result 12\n  Bound result 112\n  Bound result 180000000000000026302\n  Bound result 12\n  Bound result 180000000000000995001\n  Bound result 12\n  Bound result 46649393329207593530\n  Bound result 12\n  Bound result 2155872511\n  Bound result 32680281041836782903\n  Bound result 11100177888994921402130\n  Bound result 19999999999000000996\n  Bound result 1812930352692670847190\n  Bound result 4000000000000000\n  Bound result 19999999999000094628\n  swaps: 43\n  moves: 27\n  Bound result 12\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.56s (3.55s CPU time)\n\nRan 23 test suites in 3.57s (8.39s CPU time): 171 tests passed, 0 failed, 0 skipped (171 total tests)\n","passed":true},{"durationMs":78,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDuelHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMDuelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDuelHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDuelHook.convert(address)\",\"IMDuelHook.convertBatch(address)\",\"IMDuelHook.fundChest((address,address,uint24,int24,address),bool,uint256)\",\"IMDuelHook.payOut(address,address,uint256)\",\"IMDuelHook.sampleRoute(bytes,bytes[],bytes[])\",\"IMDuelHook.unlockCallback(bytes)\",\"IMDuelHook.withdraw(address,address)\",\"IMDuelHook.withdrawTo(address,address)\",\"IMDuelToken.approve(address,uint256)\",\"IMDuelToken.transfer(address,uint256)\",\"IMDuelToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":73,\"LICENSE\":16,\"foundry.toml\":29,\"launch.json\":37,\"launch/deployed.json\":12,\"launch/engine.json\":10,\"launch/hook.json\":39,\"launch/hook.sepolia.json\":39,\"script/LaunchPolicy.sol\":44,\"script/route_proof.py\":135,\"src/Bitboard.sol\":719,\"src/Chess.sol\":34,\"src/ChessEngineV5.sol\":852,\"src/IChessEngine.sol\":29,\"src/IMDuelHook.sol\":957,\"src/IMDuelToken.sol\":62,\"src/RoutePriceVerifier.sol\":50,\"src/Rules.sol\":45,\"src/Strength.sol\":47,\"src/proof/Bytes.sol\":144,\"src/proof/LICENSE\":22,\"src/proof/MerkleTrie.sol\":216,\"src/proof/PROVENANCE.md\":12,\"src/proof/RLPErrors.sol\":20,\"src/proof/RLPReader.sol\":249,\"test/Base.sol\":211,\"test/BidOwner.t.sol\":101,\"test/Bidding.t.sol\":224,\"test/Chessboard.sol\":79,\"test/CrossBlock.t.sol\":195,\"test/Draws.t.sol\":90,\"test/FactoryLaunch.t.sol\":173,\"test/Game.t.sol\":345,\"test/GasEstimate.t.sol\":120,\"test/Harness.sol\":83,\"test/HookGas.t.sol\":151,\"test/Hostile.t.sol\":359,\"test/Invariant.t.sol\":150,\"test/InvariantDuels.t.sol\":519,\"test/Launch.t.sol\":158,\"test/LaunchAdmission.t.sol\":159,\"test/LaunchFiles.t.sol\":75,\"test/Money.t.sol\":353,\"test/Predict.t.sol\":102,\"test/RoutePriceVerifier.t.sol\":135,\"test/RouteProof.sol\":69,\"test/RouteSample.t.sol\":168,\"test/Size.t.sol\":36,\"test/Slice.t.sol\":392,\"test/Strength.t.sol\":85,\"test/Teams.t.sol\":87,\"test/Token.t.sol\":82,\"test/Tokens.sol\":137,\"test/V4Fixture.sol\":100,\"test/data/chess.json\":1,\"test/data/conversion-validation.json\":770,\"test/data/conversion.json\":98,\"test/data/endings.json\":1,\"test/data/legal.json\":1,\"test/data/random.json\":1,\"test/data/route-proof.json\":16,\"test/test_route_proof.py\":72},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"636730a611bebfe067408453c3efe792d2563a26b43ff327bbad36043f62b621","verifiedTreeHash":"c77a218e774a5b6bedef86ce6208f59b37100d8f","verifierVersion":"0.1.0+d372f99e"},{"checks":[{"durationMs":96613,"exitCode":0,"name":"build","output":"Compiling 121 files with Solc 0.8.26\nSolc 0.8.26 finished in 96.47s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:22:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                      ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:38:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                                      ^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:109:57:\n    |\n109 |             uint64 d = f >= r ? DIAG >> (8 * (f - r)) : DIAG << (8 * (r - f));\n    |                                                         ^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:110:65:\n    |\n110 |             uint64 a = f + r <= 7 ? ANTI >> (8 * (7 - f - r)) : ANTI << (8 * (f + r - 7));\n    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:38:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:69:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                                                     ^^^^^^^^^^^^^^^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:62:18:\n   |\n62 |         returns (uint16, Rules.Played memory)\n   |                  ^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:62:26:\n   |\n62 |         returns (uint16, Rules.Played memory)\n   |                          ^^^^^^^^^^^^^^^^^^^\n\nWarning (2072): Unused local variable.\n  --> test/Game.t.sol:87:9:\n   |\n87 |         IMDuelHook.Game memory g = st();\n   |         ^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n  --> test/LaunchFiles.t.sol:70:5:\n   |\n70 |     function test_engineLaunchFileDeploysTheEngineWithoutArguments() public {\n   |     ^ (Relevant source part starts here and spans across multiple lines).\n\n","passed":true},{"durationMs":3844,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Size.t.sol:SizeTest\n[PASS] test_deployedCodeFitsTheBudgets() (gas: 1252630)\nLogs:\n  hook: 20620\n  engine v5: 23339\n  route price verifier: 5551\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 951.66µs (843.37µs CPU time)\n\nRan 4 tests for test/Token.t.sol:TokenTest\n[PASS] test_allowances() (gas: 347653)\n[PASS] test_fixedSupplyToTheDeployer() (gas: 39384)\n[PASS] test_nothingPrivileged() (gas: 192691)\n[PASS] test_transfers() (gas: 188240)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.09ms (316.71µs CPU time)\n\nRan 7 tests for test/Draws.t.sol:DrawsTest\n[PASS] test_checkmate() (gas: 240001)\n[PASS] test_moveCap() (gas: 240042)\n[PASS] test_ordinaryMoveDoesNotEndTheGame() (gas: 179990)\n[PASS] test_quietMoveLimit() (gas: 246260)\n[PASS] test_stalemate() (gas: 237814)\n[PASS] test_thirdRepetition() (gas: 964010)\n[PASS] test_tooFewPiecesToMate() (gas: 231639)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 6.43ms (4.20ms CPU time)\n\nRan 12 tests for test/Bidding.t.sol:BiddingTest\n[PASS] test_aBountyIsPaidFromTheMoversChest() (gas: 1357258)\n[PASS] test_aBuyOfTheOtherTeamBidsForItsNextMove() (gas: 1952736)\n[PASS] test_aBuyOfTheOtherTeamTimesTheSilentSideOutAndBids() (gas: 1222539)\n[PASS] test_aClosedWindowTakesNoBidsForItsMove() (gas: 1062919)\n[PASS] test_aMoveIsOpenForTurnBlocksBlocks() (gas: 1040501)\n[PASS] test_aNewTopMustPayMoreThanTenPercentMore() (gas: 974403)\n[PASS] test_aNextMoveBidCarriesIntoTheNextGame() (gas: 1467032)\n[PASS] test_aWindowWithoutBidsBecomesTheFirstBidsBlock() (gas: 1283751)\n[PASS] test_bidsCountAcrossTheWholeWindow() (gas: 1511281)\n[PASS] test_theMoveDependsOnlyOnTheWindowsLastBlock() (gas: 1621480)\n[PASS] test_theMoversOwnBuyAfterItsWindowBidsForItsNextMove() (gas: 1077628)\n[PASS] test_thePreviewIsTheMoveThatIsPlayed() (gas: 1222903)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 26.61ms (24.28ms CPU time)\n\nRan 6 tests for test/RouteSample.t.sol:RouteSampleTest\n[PASS] test_authenticatedBlockStartsRejectTheSameAttack() (gas: 3422402)\n[PASS] test_callerSamplesCannotSellTheEthPileAtAPushedPrice() (gas: 1710709)\nLogs:\n  route attacker profit at the honest price: -87002115995172580\n\n[PASS] test_duplicateSampleCannotRotateOutHonestHistory() (gas: 493037)\n[PASS] test_gapRequiresThreeConsecutiveSamplesAgain() (gas: 1855928)\n[PASS] test_honestBatchRunsAndStopsAtTheHistoricalPriceBand() (gas: 1175578)\n[PASS] test_staleRoutePricesWaitWithoutLosingClaims() (gas: 1298227)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 27.93ms (23.95ms CPU time)\n\nRan 17 tests for test/CrossBlock.t.sol:CrossBlockTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 6015280)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3994805)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3061845)\n[PASS] test_anEthBatchIsCapped() (gas: 5275537)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3200810)\n[PASS] test_anyoneCanConvert() (gas: 462085)\n[PASS] test_booksBalanceAcrossPools() (gas: 8475081)\n[PASS] test_crossBlockViaConvert() (gas: 5551997)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390298682200\n\n[PASS] test_crossBlockViaFirstTrade() (gas: 5714127)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390291136661\n\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4251851)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245524)\n[PASS] test_ethRouteSameBlockPush() (gas: 5229243)\nLogs:\n  ETH pile before (1e18): 500000000000000000\n  ETH the batch sold (1e18): 0\n  IMD it got (1e18): 0\n  IMD it was worth at the home price (1e18): 0\n  attacker profit (ETH at the home price, 1e18): -29770639430607030\n\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764118)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487372)\n[PASS] test_noRouteNoDuel() (gas: 1030839)\n[PASS] test_oneEthBatchPerBlock() (gas: 5800437)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187260)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 30.28ms (64.80ms CPU time)\n\nRan 8 tests for test/Strength.t.sol:StrengthTest\n[PASS] testFuzz_boundedAndMonotonic(uint256,uint256,uint256) (runs: 256, μ: 9045, ~: 8663)\nLogs:\n  Bound result 4608\n\n[PASS] testFuzz_depthMonotonic(uint256,uint256,uint256) (runs: 256, μ: 12024, ~: 12425)\nLogs:\n  Bound result 4608\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129635519\n\n[PASS] testFuzz_shiftKeepsTheShare(uint256,uint256,uint8) (runs: 256, μ: 14663, ~: 15266)\nLogs:\n  Bound result 10\n  Bound result 80\n  Bound result 4608\n\n[PASS] testFuzz_tenfoldIsAboutThirtyPoints(uint128,uint128) (runs: 256, μ: 13836, ~: 13724)\nLogs:\n  Bound result 600000000000000000000\n  Bound result 594000000000065536\n\n[PASS] testFuzz_unscalablePaidFollowsTheCurve(uint256,uint256) (runs: 256, μ: 13980, ~: 14208)\nLogs:\n  Bound result 115790931316423822261616749298837820974191451965793907643040561468927826316639\n  Bound result 115792089237316195423570985008687907853269984665639641702553898530332329700000\n\n[PASS] test_decidedPoints() (gas: 15577)\n[PASS] test_hugePaymentsDoNotRevert() (gas: 12854)\n[PASS] test_zeroInputs() (gas: 4493)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 58.40ms (41.29ms CPU time)\n\nRan 3 tests for test/LaunchFiles.t.sol:LaunchFilesTest\n[PASS] test_engineLaunchFileDeploysTheEngineWithoutArguments() (gas: 7834)\n[PASS] test_ethereumLaunchFile() (gas: 1332241)\n[PASS] test_sepoliaLaunchFile() (gas: 1327328)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 72.30ms (2.01ms CPU time)\n\nRan 13 tests for test/RoutePriceVerifier.t.sol:RoutePriceVerifierTest\n[PASS] testFuzz_authenticatesSlot0AndItsBlock(uint160,uint96) (runs: 256, μ: 200948, ~: 200266)\nLogs:\n  Bound result 7385035263781213683944894616182233\n\n[PASS] test_changedAccountProofIsRefused() (gas: 100298)\n[PASS] test_changedHeaderIsNotCanonical() (gas: 80800)\n[PASS] test_changedStorageProofIsRefused() (gas: 158269)\n[PASS] test_clientGeneratedPoolManagerProof() (gas: 241704)\n[PASS] test_emptyProofsAreRefused() (gas: 195098)\n[PASS] test_extraProofNodesAreRefused() (gas: 195848)\n[PASS] test_outOfRangePriceIsRefused() (gas: 192143)\n[PASS] test_theBatchBlockCannotSupplyASample() (gas: 80854)\n[PASS] test_uninitializedPoolPriceIsRefused() (gas: 180136)\n[PASS] test_unknownOrExpiredHeaderIsRefused() (gas: 81188)\n[PASS] test_wrongManagerIsRefused() (gas: 108609)\n[PASS] test_wrongPoolSlotIsRefused() (gas: 165128)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 74.80ms (79.61ms CPU time)\n\nRan 2 tests for test/LaunchAdmission.t.sol:LaunchAdmissionTest\n[PASS] test_blankChainTokenAboveImd() (gas: 25012847)\n[PASS] test_blankChainTokenBelowImd() (gas: 24941802)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 76.19ms (117.01ms CPU time)\n\nRan 2 tests for test/FactoryLaunch.t.sol:FactoryLaunchTest\n[PASS] test_factoryLaunchWithRealTokenAboveImd() (gas: 15562391)\n[PASS] test_factoryLaunchWithRealTokenBelowImd() (gas: 15528327)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 81.41ms (24.61ms CPU time)\n\nRan 3 tests for test/Launch.t.sol:LaunchTest\n[PASS] test_launchPairedWithEth() (gas: 22438868)\n[PASS] test_launchTokenAboveImd() (gas: 24832045)\nLogs:\n  first buy (opens game 0, bids for White): 366448\n\n[PASS] test_launchTokenBelowImd() (gas: 24212457)\nLogs:\n  first buy (opens game 0, bids for White): 361698\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 81.36ms (139.48ms CPU time)\n\nRan 14 tests for test/Slice.t.sol:SliceTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 6014785)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3994787)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3061820)\n[PASS] test_anEthBatchIsCapped() (gas: 5275421)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3200736)\n[PASS] test_anyoneCanConvert() (gas: 462063)\n[PASS] test_booksBalanceAcrossPools() (gas: 8475053)\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4251814)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245476)\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764104)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487390)\n[PASS] test_noRouteNoDuel() (gas: 1030789)\n[PASS] test_oneEthBatchPerBlock() (gas: 5800343)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187246)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 126.83ms (61.68ms CPU time)\n\nRan 8 tests for test/BidOwner.t.sol:BidOwnerTest\n[PASS] testFuzz_otherDataLengthsNeverCreditTheBundler(bytes) (runs: 256, μ: 385547, ~: 385542)\n[PASS] test_aHookWithoutAnEthRouteRejectsRouteProofs() (gas: 32512)\n[PASS] test_abiEncodedOwnerCreditsTheWallet() (gas: 384610)\n[PASS] test_abiEncodedOwnerReceivesTheCaptureBounty() (gas: 1116469)\n[PASS] test_dirtyAbiPaddingFallsBackToSender() (gas: 384788)\n[PASS] test_missingOwnerPaysTheSwapSender() (gas: 1133080)\n[PASS] test_missingOwnerUsesSwapSenderNotBundler() (gas: 419516)\n[PASS] test_packedOwnerCreditsTheWallet() (gas: 384689)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 139.38ms (141.66ms CPU time)\n\nRan 5 tests for test/Teams.t.sol:TeamsTest\n[PASS] test_aDynamicFeePoolPlaysWithAZeroLpFee() (gas: 2913516)\n[PASS] test_nativeEthIsATeamLikeAnyToken() (gas: 2327096)\n[PASS] test_poolsOnOneHookKeepSeparateGamesAndChests() (gas: 3404887)\n[PASS] test_sliceSide() (gas: 1819047)\n[PASS] test_twoPlainTokensCannotDuel() (gas: 1591040)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 139.26ms (13.81ms CPU time)\n\nRan 3 tests for test/GasEstimate.t.sol:GasEstimateTest\n[PASS] test_atTheEstimateADueBatchConverts() (gas: 5009784)\nLogs:\n  smallest gas limit that converts: 310375\n\n[PASS] test_atTheEstimateTheBountyIsPaidNotOwed() (gas: 4997823)\nLogs:\n  smallest gas limit that plays and pays: 404882\n\n[PASS] test_noGasLimitPlaysTheMoveAndLeavesItsBountyOwed() (gas: 18563180)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 155.23ms (155.03ms CPU time)\n\nRan 5 tests for test/HookGas.t.sol:HookGasTest\n[PASS] test_conversionGas() (gas: 3656290)\nLogs:\n  plain trade: 207766\n  trade that converts a batch: 271658\n\n[PASS] test_hookShareOfAMovingTrade() (gas: 29104655)\nLogs:\n  hook share of a moving trade: average: 278442\n  hook share of a moving trade: max: 321393\n\n[PASS] test_middlegameGas() (gas: 9501378)\nLogs:\n  first bid in a new pool: 270627\n  bidding trade: 191991\n  outbidding trade: 208087\n  plain trade: 182668\n  next block: play the turn and bid: 550662\n\n[PASS] test_openingGas() (gas: 4539846)\nLogs:\n  first bid in a new pool: 270627\n  bidding trade: 192702\n  outbidding trade: 208087\n  plain trade: 182668\n  next block: play the turn and bid: 508031\n\n[PASS] test_runningGameGas() (gas: 54586984)\nLogs:\n  running game, moving trades measured: 50\n  running game, moving trade average: 450671\n  running game, moving trade max: 542125\n\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 155.83ms (188.11ms CPU time)\n\nRan 11 tests for test/Hostile.t.sol:HostileTest\n[PASS] test_aCallbackTokenOwnerLeavingADebtCannotFreezeTheDuel() (gas: 5276059)\n[PASS] test_aDeadEngineLeavesTheTurnQueuedAndTradingGoesOn() (gas: 3555280)\n[PASS] test_aGasBurningEngineCostsOneExpensiveTradeNotTheGame() (gas: 9727669)\n[PASS] test_aTokenReturningNothingFundsAChest() (gas: 2185559)\n[PASS] test_anEthOwnerLeavingADebtCannotFreezeTheDuel() (gas: 4171134)\n[PASS] test_anOwnerThatCannotReceiveWithdrawsElsewhere() (gas: 3554214)\n[PASS] test_blockedRecipientStaysOwedAndTradingContinues() (gas: 4185544)\n[PASS] test_brokenEngineFallsBackToALegalMove() (gas: 5150392)\n[PASS] test_feeOnTransferTokenFundsOnlyWhatArrives() (gas: 2731451)\n[PASS] test_reenteringTokenCannotBreakAPayout() (gas: 4721771)\n[PASS] test_starvingTheEngineNeverPlaysTheFallback() (gas: 25149078)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 155.86ms (88.63ms CPU time)\n\nRan 16 tests for test/FeeSchedule.t.sol:FeeScheduleTest\n[PASS] testFuzz_exactOutputDeliversTheOrderAndTakesHalfAPercent(uint256) (runs: 256, μ: 950943, ~: 951460)\nLogs:\n  Bound result 499999999999000100000\n\n[PASS] testFuzz_openingChestShareFollowsTheSchedule(uint256,uint256) (runs: 512, μ: 464555, ~: 464703)\nLogs:\n  Bound result 1716\n  Bound result 999999999999999999998\n\n[PASS] testFuzz_surchargeNeverRises(uint256,uint256) (runs: 256, μ: 1072019, ~: 1072083)\nLogs:\n  Bound result 222\n  Bound result 394\n\n[PASS] test_conversionSplitsTheImduelHalfKeptHalfBurned() (gas: 3719497)\n[PASS] test_convertWithAnEmptyPileDoesNothing() (gas: 123769)\n[PASS] test_exactOutputStoppedByItsPriceLimitIsRefused() (gas: 215750)\n[PASS] test_fundChestBeyondBalanceIsRefused() (gas: 121423)\n[PASS] test_fundChestOfZeroAddsNothing() (gas: 98316)\n[PASS] test_fundChestWithTheWrongEthValueIsRefused() (gas: 344804)\n[PASS] test_fundChestWithoutAllowanceIsRefused() (gas: 111168)\n[PASS] test_lastSurchargeSecond() (gas: 470088)\n[PASS] test_mainDuelConvertsAtMostOnceAndTenMinimumsPerBlock() (gas: 4156018)\n[PASS] test_openingEdges() (gas: 469710)\n[PASS] test_owedRewardOnlyEverReachesItsOwner() (gas: 1176868)\n[PASS] test_surchargeEndsAtThreeHundredSeconds() (gas: 469601)\n[PASS] test_withdrawWithNothingOwedDoesNothing() (gas: 90029)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 156.02ms (351.78ms CPU time)\n\nRan 23 tests for test/Game.t.sol:GameTest\n[PASS] testFuzz_swapsNeverRevert(uint256) (runs: 48, μ: 9683299, ~: 9680346)\n[PASS] test_aGamePlaysOutThroughSwaps() (gas: 57294244)\nLogs:\n  moves played: 79\n  bids (incl. outbid ones): 101\n  most gas for a swap: 1125299\n\n[PASS] test_aSmallerTradeInTheBlockDoesNotTakeTheTurn() (gas: 686523)\n[PASS] test_aTimeoutTurnCanBeTakenInItsBlock() (gas: 1304281)\n[PASS] test_aTradeBidsForTheTurn() (gas: 483510)\n[PASS] test_aTurnClosesWithItsBlock() (gas: 965205)\n[PASS] test_anExpiredTurnBecomesATimeoutTurn() (gas: 1413397)\n[PASS] test_anOldTurnIsPlayedWithTheHistoryContractsHash() (gas: 1754927)\n[PASS] test_anyTradeSizeBids() (gas: 450089)\n[PASS] test_lateTradeStillBidsNormally() (gas: 429723)\n[PASS] test_onlyThePoolManagerAndTheHookCanCallIn() (gas: 169413)\n[PASS] test_ownerDefaultsToSwapSender() (gas: 418165)\n[PASS] test_playedPositionsEnterTheRepetitionMemory() (gas: 915403)\n[PASS] test_rejectsBadConfig() (gas: 9247)\n[PASS] test_silentSidePlaysAtZeroStrength() (gas: 1048084)\n[PASS] test_strengthFollowsTheShareOfThePool() (gas: 6374901)\n[PASS] test_teamsSwapColoursEveryGame() (gas: 926885)\n[PASS] test_theBiggestTradeInTheBlockOwnsTheTurn() (gas: 782425)\n[PASS] test_theFirstPoolOpenedIsTheMainDuel() (gas: 179471)\n[PASS] test_theMainDuelMustPairImd() (gas: 1465618)\n[PASS] test_theNextBlocksTradePlaysTheTurn() (gas: 1038530)\n[PASS] test_theOtherTeamCannotBidOnThisTurn() (gas: 412294)\n[PASS] test_whoPlaysTheTurnAndWhenDoesNotChangeTheMove() (gas: 1534248)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 856.58ms (1.04s CPU time)\n\nRan 21 tests for test/Money.t.sol:MoneyTest\n[PASS] testFuzz_heldEqualsOwed(uint256) (runs: 48, μ: 10634970, ~: 10724301)\n[PASS] test_aFullChestTakesNoMore() (gas: 1059275)\n[PASS] test_aTradeStoppedByItsPriceLimitIsRefused() (gas: 582949)\n[PASS] test_blackCaptureIsPaidInItsTeamToken() (gas: 774230)\n[PASS] test_bountyTable() (gas: 44837)\n[PASS] test_checkmatePaysAQuarterOfTheChest() (gas: 754080)\n[PASS] test_chestShareGoesToTheChestWorthLess() (gas: 638508)\n[PASS] test_chestsStayAboutEqualInValue() (gas: 5512254)\n[PASS] test_exactOutputPaysTheFeeOnTheInput() (gas: 385005)\n[PASS] test_exactOutputWithTheChestShareInTheOutput() (gas: 790954)\n[PASS] test_failedPayoutStaysOwedAndTheTradeGoesThrough() (gas: 1213589)\n[PASS] test_feesCannotBeWithdrawnByAnyone() (gas: 450014)\n[PASS] test_firstTradeFundsChest0AndTheSlice() (gas: 363842)\n[PASS] test_fundChestNeedsTheRightValue() (gas: 48758)\n[PASS] test_fundChestOnlyForHookPools() (gas: 41896)\n[PASS] test_fundChestWithEth() (gas: 252873)\n[PASS] test_fundChestWithTokens() (gas: 193846)\n[PASS] test_openingSurchargeOnlyInTheMainDuel() (gas: 3489323)\n[PASS] test_payoutIsCappedAtTwiceTheTrade() (gas: 821586)\n[PASS] test_quietMovePaysNothing() (gas: 616187)\n[PASS] test_whiteCaptureIsPaidInItsTeamTokenFromItsOwnChest() (gas: 847898)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 858.33ms (875.03ms CPU time)\n\nRan 1 test for test/Predict.t.sol:PredictTest\n[PASS] test_aBotCannotPickItsOwnCapturingMoves() (gas: 603211199)\nLogs:\n  bot moves: 0\n  other moves: 150\n  captures among other moves: 25\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.09s (1.09s CPU time)\n\nRan 1 test for test/Invariant.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_boardHasBothKingsAndPlyIsBounded\n[PASS] invariant_heldEqualsOwed\n InvariantTest invariants (runs: 24, calls: 2400, reverts: 0)\n\n╭----------+----------------+-------+---------+----------╮\n| Contract | Selector       | Calls | Reverts | Discards |\n+========================================================+\n| Handler  | convert        | 301   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | fund           | 305   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | longPause      | 311   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | nextBlock      | 313   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | trade          | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeAgain     | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeNextBlock | 312   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | withdrawReward | 304   | 0       | 0        |\n╰----------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3\n  Bound result 19999999000000004867\n  Bound result 500000000000000000\n  Bound result 19999999000000008654\n  Bound result 19999999000000000703\n  Bound result 106\n  Bound result 18046385208871800035\n  Bound result 2924\n  Bound result 19999999007306351798\n  Bound result 4000000000000000000\n  Bound result 31865262234567526\n  Bound result 19999999000000000014\n  Bound result 19999999000000003145\n  Bound result 19999999000000002282\n  Bound result 128\n  Bound result 98\n  Bound result 19999999386547056641\n  Bound result 7616448877154347021\n  Bound result 19999999000000000065\n  Bound result 84\n  Bound result 4022291159582972627\n  Bound result 6\n  Bound result 17001\n  Bound result 19999999000000049375\n  Bound result 81\n  Bound result 90\n  Bound result 1619\n  Bound result 19999999000000000081\n  Bound result 1221339734816628308\n  Bound result 71\n  Bound result 98\n  Bound result 30\n  Bound result 32\n  Bound result 75\n  Bound result 80\n  Bound result 19999999000000000957\n  Bound result 19999999000000007542\n  Bound result 55\n  Bound result 1000000000003\n  Bound result 19999999000000000049\n  Bound result 1251\n  Bound result 112\n  Bound result 19999999000000000021\n  Bound result 19999999000000000184\n  Bound result 19999999000000000627\n  Bound result 19304386864691798331\n  Bound result 20396340581799926600\n  Bound result 13117\n  Bound result 12054830257181156736\n  Bound result 19999999000000006514\n  Bound result 12566133904435631808\n  Bound result 15386197539150620393\n  Bound result 2\n  Bound result 87\n  Bound result 19999999000000002424\n  Bound result 19999999000000003359\n  Bound result 19999999386547056641\n  Bound result 10000000000000000\n  Bound result 5060859166916801136\n  Bound result 400\n  Bound result 84\n  swaps: 33\n  moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.13s (1.13s CPU time)\n\nRan 3 tests for test/TokenInvariant.t.sol:TokenInvariantTest\n[PASS]\nTokenInvariantTest invariants:\n[PASS] invariant_allowancesAreExactlyWhatWasApprovedLessWhatWasSpent\n[PASS] invariant_supplyIsFixedAndHeldByTheActors\n TokenInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+--------------------+-------+---------+----------╮\n| Contract     | Selector           | Calls | Reverts | Discards |\n+================================================================+\n| TokenHandler | approve            | 2093  | 0       | 0        |\n|--------------+--------------------+-------+---------+----------|\n| TokenHandler | approveZeroSpender | 1976  | 0       | 0        |\n|--------------+--------------------+-------+---------+----------|\n| TokenHandler | transfer           | 2073  | 0       | 0        |\n|--------------+--------------------+-------+---------+----------|\n| TokenHandler | transferFrom       | 2050  | 0       | 0        |\n╰--------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3\n  Bound result 9\n  Bound result 16613\n  Bound result 6\n  Bound result 6482\n  Bound result 3\n  Bound result 1265\n  Bound result 3\n  Bound result 605\n  Bound result 5149\n  Bound result 800\n  Bound result 57694\n  Bound result 1466\n  Bound result 2004\n  Bound result 1091\n  Bound result 65280\n  Bound result 2\n  Bound result 6\n  Bound result 343926200696183277375289309\n  Bound result 5669\n  Bound result 253\n  Bound result 4000\n  Bound result 10\n  Bound result 400\n  Bound result 1198\n  Bound result 2293517445\n  Bound result 223\n  Bound result 812118896355640807882601351\n  Bound result 777062408\n  Bound result 1000000000000000000000\n  Bound result 174\n  Bound result 7462\n  Bound result 3600\n  Bound result 3569\n  Bound result 121656067608018816226\n  Bound result 4288\n\n[PASS] testFuzz_transferOfMoreThanTheBalanceIsRefused(uint256) (runs: 512, μ: 55904, ~: 55857)\nLogs:\n  Bound result 137122462167341575662000267002353578582749290296\n\n[PASS] test_transferFromTheZeroAddressIsRefused() (gas: 82118)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.74s (2.76s CPU time)\n\nRan 1 test for test/InvariantDuels.t.sol:InvariantDuelsTest\n[PASS]\nInvariantDuelsTest invariants:\n[PASS] invariant_everyBoardIsSound\n[PASS] invariant_everyCurrencyBalances\n[PASS] invariant_rightsMatchTheBoard\n[PASS] invariant_turnsAreWellFormed\n InvariantDuelsTest invariants (runs: 24, calls: 2880, reverts: 0)\n\n╭--------------+-------------------+-------+---------+----------╮\n| Contract     | Selector          | Calls | Reverts | Discards |\n+===============================================================+\n| DuelsHandler | addLiquidity      | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convert           | 220   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convertNextBlock  | 200   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | fund              | 241   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | longPause         | 221   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | nextBlock         | 215   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | pushThenNextBlock | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | removeLiquidity   | 253   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | roundTrip         | 251   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | trade             | 232   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeAgain        | 198   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeNextBlock    | 223   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | withdrawReward    | 210   | 0       | 0        |\n╰--------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 23537\n  Bound result 2565653098084874863\n  Bound result 12\n  Bound result 8121606201292619776\n  Bound result 72624976668147840\n  Bound result 3057007858\n  Bound result 12\n  Bound result 19999999999000000049\n  Bound result 4485934425771658475\n  Bound result 120\n  Bound result 12\n  Bound result 562949953421312\n  Bound result 12\n  Bound result 19999999999000010908\n  Bound result 20000000000000000000\n  Bound result 5000\n  Bound result 19999999999000017645\n  Bound result 29999999970\n  Bound result 12\n  Bound result 19999999999000000064\n  Bound result 19999999999000000243\n  Bound result 386547056640\n  Bound result 133294633079153423663\n  Bound result 12\n  Bound result 19999999999000007558\n  Bound result 12\n  Bound result 19999999999000009179\n  Bound result 12\n  Bound result 1126409556\n  Bound result 12\n  Bound result 19999999999000006339\n  Bound result 180000000000000014097\n  Bound result 12\n  Bound result 4999999999000017146\n  Bound result 5505625188992501493\n  Bound result 12\n  Bound result 180000000000000001111\n  Bound result 12\n  Bound result 180000000000000009365\n  Bound result 12\n  Bound result 482270721370195085\n  Bound result 52\n  Bound result 19999999999000013491\n  Bound result 12\n  Bound result 19999999999000017873\n  Bound result 74264337593103793878\n  Bound result 12\n  Bound result 4999999999000008096\n  Bound result 6017502607293588406\n  Bound result 1500472347603705430\n  Bound result 9999999000000000000126\n  Bound result 17715909253537394009\n  Bound result 12\n  Bound result 19999999999000015277\n  Bound result 805801794754002874\n  Bound result 4999999999000002501\n  Bound result 180000000000000006941\n  Bound result 12\n  Bound result 180000000000000043129\n  Bound result 12\n  Bound result 12\n  Bound result 4999999999000004030\n  Bound result 12\n  Bound result 12\n  Bound result 10000000000000000\n  Bound result 19999999999000000651\n  Bound result 12\n  Bound result 74\n  Bound result 25332747903959040\n  Bound result 12\n  Bound result 4960186154993472837\n  Bound result 19999999999000004099\n  Bound result 19999999999000000801\n  Bound result 12\n  Bound result 17830126982352984981\n  Bound result 30\n  Bound result 1151112918199026685\n  Bound result 11071107257345517972\n  Bound result 80\n  Bound result 27612829230727045392\n  Bound result 12\n  Bound result 120\n  Bound result 1000178988994931336848\n  Bound result 191000000000000000001\n  Bound result 12\n  Bound result 21931300337370667194\n  Bound result 99999999994444444445\n  Bound result 12\n  Bound result 4503599627370496\n  Bound result 180000000000000026303\n  Bound result 12\n  Bound result 180050000000000000001\n  Bound result 12\n  Bound result 46649393329207593530\n  Bound result 12\n  Bound result 10000000000000000000\n  Bound result 32680281041836782903\n  Bound result 11000186988994931350568\n  Bound result 1000000000\n  Bound result 1812930352692670847190\n  Bound result 19999999999000000245\n  Bound result 4999999999001966081\n  Bound result 19999999999000094628\n  swaps: 43\n  moves: 21\n  Bound result 12\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.66s (3.65s CPU time)\n\nRan 25 test suites in 3.66s (11.90s CPU time): 190 tests passed, 0 failed, 0 skipped (190 total tests)\n","passed":true},{"durationMs":86,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDuelHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMDuelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDuelHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDuelHook.convert(address)\",\"IMDuelHook.convertBatch(address)\",\"IMDuelHook.fundChest((address,address,uint24,int24,address),bool,uint256)\",\"IMDuelHook.payOut(address,address,uint256)\",\"IMDuelHook.sampleRoute(bytes,bytes[],bytes[])\",\"IMDuelHook.unlockCallback(bytes)\",\"IMDuelHook.withdraw(address,address)\",\"IMDuelHook.withdrawTo(address,address)\",\"IMDuelToken.approve(address,uint256)\",\"IMDuelToken.transfer(address,uint256)\",\"IMDuelToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":73,\"LICENSE\":16,\"foundry.toml\":29,\"launch.json\":39,\"launch/deployed.json\":12,\"launch/engine.json\":10,\"launch/hook.json\":39,\"launch/hook.sepolia.json\":39,\"script/LaunchPolicy.sol\":44,\"script/route_proof.py\":135,\"src/Bitboard.sol\":719,\"src/Chess.sol\":34,\"src/ChessEngineV5.sol\":852,\"src/IChessEngine.sol\":29,\"src/IMDuelHook.sol\":957,\"src/IMDuelToken.sol\":62,\"src/RoutePriceVerifier.sol\":50,\"src/Rules.sol\":45,\"src/Strength.sol\":47,\"src/proof/Bytes.sol\":144,\"src/proof/LICENSE\":22,\"src/proof/MerkleTrie.sol\":216,\"src/proof/PROVENANCE.md\":12,\"src/proof/RLPErrors.sol\":20,\"src/proof/RLPReader.sol\":249,\"test/Base.sol\":211,\"test/BidOwner.t.sol\":101,\"test/Bidding.t.sol\":224,\"test/Chessboard.sol\":79,\"test/CrossBlock.t.sol\":195,\"test/Draws.t.sol\":90,\"test/FactoryLaunch.t.sol\":173,\"test/FeeSchedule.t.sol\":327,\"test/Game.t.sol\":345,\"test/GasEstimate.t.sol\":120,\"test/Harness.sol\":83,\"test/HookGas.t.sol\":151,\"test/Hostile.t.sol\":359,\"test/Invariant.t.sol\":150,\"test/InvariantDuels.t.sol\":519,\"test/Launch.t.sol\":158,\"test/LaunchAdmission.t.sol\":159,\"test/LaunchFiles.t.sol\":75,\"test/Money.t.sol\":353,\"test/Predict.t.sol\":102,\"test/RoutePriceVerifier.t.sol\":135,\"test/RouteProof.sol\":69,\"test/RouteSample.t.sol\":168,\"test/Size.t.sol\":36,\"test/Slice.t.sol\":392,\"test/Strength.t.sol\":85,\"test/Teams.t.sol\":87,\"test/Token.t.sol\":82,\"test/TokenInvariant.t.sol\":155,\"test/Tokens.sol\":137,\"test/V4Fixture.sol\":100,\"test/data/chess.json\":1,\"test/data/conversion-validation.json\":770,\"test/data/conversion.json\":98,\"test/data/endings.json\":1,\"test/data/legal.json\":1,\"test/data/random.json\":1,\"test/data/route-proof.json\":16,\"test/test_route_proof.py\":72},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"c75591c1cbc79bc9e01fcff945548c619bac1bea2d4aa041648aa401075d5ca7","verifiedTreeHash":"a3304b62b2b3423fe6dbfa431f04c9e257f46ed7","verifierVersion":"0.1.0+d372f99e"},{"checks":[{"durationMs":91865,"exitCode":0,"name":"build","output":"Compiling 120 files with Solc 0.8.26\nSolc 0.8.26 finished in 91.72s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:22:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                      ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:38:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                                      ^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:109:57:\n    |\n109 |             uint64 d = f >= r ? DIAG >> (8 * (f - r)) : DIAG << (8 * (r - f));\n    |                                                         ^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:110:65:\n    |\n110 |             uint64 a = f + r <= 7 ? ANTI >> (8 * (7 - f - r)) : ANTI << (8 * (f + r - 7));\n    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:38:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:69:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                                                     ^^^^^^^^^^^^^^^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:72:18:\n   |\n72 |         returns (uint16, Rules.Played memory)\n   |                  ^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:72:26:\n   |\n72 |         returns (uint16, Rules.Played memory)\n   |                          ^^^^^^^^^^^^^^^^^^^\n\nWarning (2072): Unused local variable.\n  --> test/Game.t.sol:90:9:\n   |\n90 |         IMDuelHook.Game memory g = st();\n   |         ^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n  --> test/LaunchFiles.t.sol:70:5:\n   |\n70 |     function test_engineLaunchFileDeploysTheEngineWithoutArguments() public {\n   |     ^ (Relevant source part starts here and spans across multiple lines).\n\n","passed":true},{"durationMs":3567,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/Token.t.sol:TokenTest\n[PASS] test_allowances() (gas: 347653)\n[PASS] test_fixedSupplyToTheDeployer() (gas: 39384)\n[PASS] test_nothingPrivileged() (gas: 192691)\n[PASS] test_transfers() (gas: 188240)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 823.19µs (406.19µs CPU time)\n\nRan 1 test for test/Size.t.sol:SizeTest\n[PASS] test_deployedCodeFitsTheBudgets() (gas: 1255245)\nLogs:\n  hook: 21322\n  engine v5: 23339\n  route price verifier: 5551\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.37ms (1.21ms CPU time)\n\nRan 1 test for test/Proof_SyncRecipient.t.sol:SyncRecipientTest\n[PASS] test_anEthPayingTradeThatPaysASyncingOwnerGoesThrough() (gas: 1161237)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.75ms (980.88µs CPU time)\n\nRan 3 tests for test/LaunchFiles.t.sol:LaunchFilesTest\n[PASS] test_engineLaunchFileDeploysTheEngineWithoutArguments() (gas: 7834)\n[PASS] test_ethereumLaunchFile() (gas: 1334990)\n[PASS] test_sepoliaLaunchFile() (gas: 1330008)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 3.30ms (2.73ms CPU time)\n\nRan 7 tests for test/Draws.t.sol:DrawsTest\n[PASS] test_checkmate() (gas: 240028)\n[PASS] test_moveCap() (gas: 240069)\n[PASS] test_ordinaryMoveDoesNotEndTheGame() (gas: 180017)\n[PASS] test_quietMoveLimit() (gas: 246287)\n[PASS] test_stalemate() (gas: 237841)\n[PASS] test_thirdRepetition() (gas: 964226)\n[PASS] test_tooFewPiecesToMate() (gas: 231666)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 11.60ms (8.65ms CPU time)\n\nRan 12 tests for test/Bidding.t.sol:BiddingTest\n[PASS] test_aBountyIsPaidFromTheMoversChest() (gas: 1369426)\n[PASS] test_aBuyOfTheOtherTeamBidsForItsNextMove() (gas: 1952666)\n[PASS] test_aBuyOfTheOtherTeamTimesTheSilentSideOutAndBids() (gas: 1222504)\n[PASS] test_aClosedWindowTakesNoBidsForItsMove() (gas: 1062884)\n[PASS] test_aMoveIsOpenForTurnBlocksBlocks() (gas: 1040466)\n[PASS] test_aNewTopMustPayMoreThanTenPercentMore() (gas: 974355)\n[PASS] test_aNextMoveBidCarriesIntoTheNextGame() (gas: 1467024)\n[PASS] test_aWindowWithoutBidsBecomesTheFirstBidsBlock() (gas: 1283690)\n[PASS] test_bidsCountAcrossTheWholeWindow() (gas: 1511214)\n[PASS] test_theMoveDependsOnlyOnTheWindowsLastBlock() (gas: 1621426)\n[PASS] test_theMoversOwnBuyAfterItsWindowBidsForItsNextMove() (gas: 1077593)\n[PASS] test_thePreviewIsTheMoveThatIsPlayed() (gas: 1222868)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 14.96ms (17.70ms CPU time)\n\nRan 5 tests for test/Teams.t.sol:TeamsTest\n[PASS] test_aDynamicFeePoolPlaysWithAZeroLpFee() (gas: 2916166)\n[PASS] test_nativeEthIsATeamLikeAnyToken() (gas: 2329765)\n[PASS] test_poolsOnOneHookKeepSeparateGamesAndChests() (gas: 3392679)\n[PASS] test_sliceSide() (gas: 1821742)\n[PASS] test_twoPlainTokensCannotDuel() (gas: 1593745)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 28.31ms (16.28ms CPU time)\n\nRan 13 tests for test/RoutePriceVerifier.t.sol:RoutePriceVerifierTest\n[PASS] testFuzz_authenticatesSlot0AndItsBlock(uint160,uint96) (runs: 256, μ: 200948, ~: 200266)\nLogs:\n  Bound result 2964422583387036479856633470515551576907\n\n[PASS] test_changedAccountProofIsRefused() (gas: 100298)\n[PASS] test_changedHeaderIsNotCanonical() (gas: 80800)\n[PASS] test_changedStorageProofIsRefused() (gas: 158269)\n[PASS] test_clientGeneratedPoolManagerProof() (gas: 241704)\n[PASS] test_emptyProofsAreRefused() (gas: 195098)\n[PASS] test_extraProofNodesAreRefused() (gas: 195848)\n[PASS] test_outOfRangePriceIsRefused() (gas: 192143)\n[PASS] test_theBatchBlockCannotSupplyASample() (gas: 80854)\n[PASS] test_uninitializedPoolPriceIsRefused() (gas: 180136)\n[PASS] test_unknownOrExpiredHeaderIsRefused() (gas: 81188)\n[PASS] test_wrongManagerIsRefused() (gas: 108609)\n[PASS] test_wrongPoolSlotIsRefused() (gas: 165128)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 37.45ms (43.84ms CPU time)\n\nRan 6 tests for test/RouteSample.t.sol:RouteSampleTest\n[PASS] test_authenticatedBlockStartsRejectTheSameAttack() (gas: 3422402)\n[PASS] test_callerSamplesCannotSellTheEthPileAtAPushedPrice() (gas: 1710709)\nLogs:\n  route attacker profit at the honest price: -87002115995172580\n\n[PASS] test_duplicateSampleCannotRotateOutHonestHistory() (gas: 493037)\n[PASS] test_gapRequiresThreeConsecutiveSamplesAgain() (gas: 1855898)\n[PASS] test_honestBatchRunsAndStopsAtTheHistoricalPriceBand() (gas: 1175548)\n[PASS] test_staleRoutePricesWaitWithoutLosingClaims() (gas: 1298197)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 41.32ms (23.31ms CPU time)\n\nRan 2 tests for test/FactoryLaunch.t.sol:FactoryLaunchTest\n[PASS] test_factoryLaunchWithRealTokenAboveImd() (gas: 21676728)\n[PASS] test_factoryLaunchWithRealTokenBelowImd() (gas: 21641394)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 46.34ms (48.18ms CPU time)\n\nRan 17 tests for test/CrossBlock.t.sol:CrossBlockTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5993860)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3973571)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3064506)\n[PASS] test_anEthBatchIsCapped() (gas: 5253874)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3203497)\n[PASS] test_anyoneCanConvert() (gas: 462029)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656485)\n[PASS] test_crossBlockViaConvert() (gas: 5557988)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390298682200\n\n[PASS] test_crossBlockViaFirstTrade() (gas: 5720092)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390291136661\n\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4254538)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245468)\n[PASS] test_ethRouteSameBlockPush() (gas: 5231826)\nLogs:\n  ETH pile before (1e18): 500000000000000000\n  ETH the batch sold (1e18): 0\n  IMD it got (1e18): 0\n  IMD it was worth at the home price (1e18): 0\n  attacker profit (ETH at the home price, 1e18): -29770639430607030\n\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764062)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487316)\n[PASS] test_noRouteNoDuel() (gas: 1030839)\n[PASS] test_oneEthBatchPerBlock() (gas: 5779205)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187178)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 48.52ms (72.72ms CPU time)\n\nRan 8 tests for test/BidOwner.t.sol:BidOwnerTest\n[PASS] testFuzz_otherDataLengthsNeverCreditTheBundlerOrTheRouter(bytes) (runs: 256, μ: 383290, ~: 383288)\n[PASS] test_aHookWithoutAnEthRouteRejectsRouteProofs() (gas: 32534)\n[PASS] test_abiEncodedOwnerCreditsTheWallet() (gas: 384567)\n[PASS] test_abiEncodedOwnerReceivesTheCaptureBounty() (gas: 1128583)\n[PASS] test_dirtyAbiPaddingIsNobody() (gas: 382698)\n[PASS] test_missingOwnerIsNobodyNotTheRouterNorTheBundler() (gas: 415565)\n[PASS] test_missingOwnerPlaysButPaysNobody() (gas: 1151657)\n[PASS] test_packedOwnerCreditsTheWallet() (gas: 384686)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 49.15ms (47.86ms CPU time)\n\nRan 3 tests for test/Launch.t.sol:LaunchTest\n[PASS] test_launchPairedWithEth() (gas: 23117220)\n[PASS] test_launchTokenAboveImd() (gas: 24067102)\nLogs:\n  first buy (opens game 0, bids for White): 366422\n\n[PASS] test_launchTokenBelowImd() (gas: 23992486)\nLogs:\n  first buy (opens game 0, bids for White): 361672\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 61.65ms (159.83ms CPU time)\n\nRan 14 tests for test/Slice.t.sol:SliceTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5993365)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3973553)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3064481)\n[PASS] test_anEthBatchIsCapped() (gas: 5253758)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3203346)\n[PASS] test_anyoneCanConvert() (gas: 462007)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656457)\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4254448)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245420)\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764048)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487334)\n[PASS] test_noRouteNoDuel() (gas: 1030789)\n[PASS] test_oneEthBatchPerBlock() (gas: 5779111)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187164)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 65.73ms (46.64ms CPU time)\n\nRan 19 tests for test/Hostile.t.sol:HostileTest\n[PASS] test_aCallbackTokenOwnerLeavingADebtCannotFreezeTheDuel() (gas: 5286273)\n[PASS] test_aCallbackTokenOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 5622051)\n[PASS] test_aCallbackTokenOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 5641809)\n[PASS] test_aCallbackTokenOwnerSyncingTheOtherCurrencyCannotFreezeTheDuel() (gas: 5505007)\n[PASS] test_aCallbackTokenOwnerSyncingTheTokenCannotFreezeTheDuel() (gas: 5500324)\n[PASS] test_aDeadEngineLeavesTheTurnQueuedAndTradingGoesOn() (gas: 3557668)\n[PASS] test_aGasBurningEngineCostsOneExpensiveTradeNotTheGame() (gas: 9729914)\n[PASS] test_aTokenReturningNothingFundsAChest() (gas: 2188344)\n[PASS] test_anEthOwnerLeavingADebtCannotFreezeTheDuel() (gas: 4204480)\n[PASS] test_anEthOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 4491567)\n[PASS] test_anEthOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 4551486)\n[PASS] test_anEthOwnerSyncingEthWhenNothingIsSyncedIsPaid() (gas: 3336300)\n[PASS] test_anEthOwnerSyncingTheOtherCurrencyCannotFreezeTheDuel() (gas: 4380600)\n[PASS] test_anOwnerThatCannotReceiveWithdrawsElsewhere() (gas: 3563277)\n[PASS] test_blockedRecipientStaysOwedAndTradingContinues() (gas: 4194594)\n[PASS] test_brokenEngineFallsBackToALegalMove() (gas: 5153119)\n[PASS] test_feeOnTransferTokenFundsOnlyWhatArrives() (gas: 2734236)\n[PASS] test_reenteringTokenCannotBreakAPayout() (gas: 4736699)\n[PASS] test_starvingTheEngineNeverPlaysTheFallback() (gas: 25153217)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 71.57ms (109.39ms CPU time)\n\nRan 2 tests for test/LaunchAdmission.t.sol:LaunchAdmissionTest\n[PASS] test_blankChainTokenAboveImd() (gas: 62938925)\n[PASS] test_blankChainTokenBelowImd() (gas: 62867880)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 80.21ms (123.59ms CPU time)\n\nRan 5 tests for test/HookGas.t.sol:HookGasTest\n[PASS] test_conversionGas() (gas: 3645312)\nLogs:\n  plain trade: 207740\n  trade that converts a batch: 271602\n\n[PASS] test_hookShareOfAMovingTrade() (gas: 29179960)\nLogs:\n  hook share of a moving trade: average: 280031\n  hook share of a moving trade: max: 333579\n\n[PASS] test_middlegameGas() (gas: 9538644)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 191975\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 562851\n\n[PASS] test_openingGas() (gas: 4542201)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 192693\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 508018\n\n[PASS] test_runningGameGas() (gas: 54808626)\nLogs:\n  running game, moving trades measured: 50\n  running game, moving trade average: 454075\n  running game, moving trade max: 554311\n\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 91.36ms (134.44ms CPU time)\n\nRan 8 tests for test/Strength.t.sol:StrengthTest\n[PASS] testFuzz_boundedAndMonotonic(uint256,uint256,uint256) (runs: 256, μ: 9090, ~: 8663)\nLogs:\n  Bound result 4608\n\n[PASS] testFuzz_depthMonotonic(uint256,uint256,uint256) (runs: 256, μ: 11990, ~: 12149)\nLogs:\n  Bound result 4608\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129635345\n\n[PASS] testFuzz_shiftKeepsTheShare(uint256,uint256,uint8) (runs: 256, μ: 14615, ~: 15266)\nLogs:\n  Bound result 10\n  Bound result 18\n  Bound result 4608\n\n[PASS] testFuzz_tenfoldIsAboutThirtyPoints(uint128,uint128) (runs: 256, μ: 13837, ~: 13724)\nLogs:\n  Bound result 340282366920938463463374607431767781456\n  Bound result 336879543251729078913183354370646904\n\n[PASS] testFuzz_unscalablePaidFollowsTheCurve(uint256,uint256) (runs: 256, μ: 13955, ~: 14208)\nLogs:\n  Bound result 115790931316423822261616749298837820974191451965793907633817189432073050508959\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913097900000\n\n[PASS] test_decidedPoints() (gas: 15577)\n[PASS] test_hugePaymentsDoNotRevert() (gas: 12854)\n[PASS] test_zeroInputs() (gas: 4493)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 143.01ms (54.46ms CPU time)\n\nRan 3 tests for test/GasEstimate.t.sol:GasEstimateTest\n[PASS] test_atTheEstimateADueBatchConverts() (gas: 4998592)\nLogs:\n  smallest gas limit that converts: 310375\n\n[PASS] test_atTheEstimateTheBountyIsPaidNotOwed() (gas: 4619259)\nLogs:\n  smallest gas limit that plays and pays: 417418\n\n[PASS] test_noGasLimitPlaysTheMoveAndLeavesItsBountyOwed() (gas: 19295099)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 143.07ms (152.27ms CPU time)\n\nRan 1 test for test/Predict.t.sol:PredictTest\n[PASS] test_aBotCannotPickItsOwnCapturingMoves() (gas: 289907162)\nLogs:\n  bot moves: 0\n  other moves: 74\n  captures among other moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 524.40ms (524.08ms CPU time)\n\nRan 23 tests for test/Game.t.sol:GameTest\n[PASS] testFuzz_swapsNeverRevert(uint256) (runs: 48, μ: 9533608, ~: 9539772)\n[PASS] test_aGamePlaysOutThroughSwaps() (gas: 134592260)\nLogs:\n  moves played: 187\n  bids (incl. outbid ones): 238\n  most gas for a swap: 685293\n\n[PASS] test_aSmallerTradeInTheBlockDoesNotTakeTheTurn() (gas: 686491)\n[PASS] test_aTimeoutTurnCanBeTakenInItsBlock() (gas: 1304210)\n[PASS] test_aTradeBidsForTheTurn() (gas: 483494)\n[PASS] test_aTurnClosesWithItsBlock() (gas: 965170)\n[PASS] test_anExpiredTurnBecomesATimeoutTurn() (gas: 1410536)\n[PASS] test_anOldTurnIsPlayedWithTheHistoryContractsHash() (gas: 1754873)\n[PASS] test_anyTradeSizeBids() (gas: 450095)\n[PASS] test_lateTradeStillBidsNormally() (gas: 429729)\n[PASS] test_onlyThePoolManagerAndTheHookCanCallIn() (gas: 171212)\n[PASS] test_ownerDefaultsToNobody() (gas: 449725)\n[PASS] test_playedPositionsEnterTheRepetitionMemory() (gas: 915368)\n[PASS] test_rejectsBadConfig() (gas: 9247)\n[PASS] test_silentSidePlaysAtZeroStrength() (gas: 1045239)\n[PASS] test_strengthFollowsTheShareOfThePool() (gas: 6374541)\n[PASS] test_teamsSwapColoursEveryGame() (gas: 926880)\n[PASS] test_theBiggestTradeInTheBlockOwnsTheTurn() (gas: 782415)\n[PASS] test_theFirstPoolOpenedIsTheMainDuel() (gas: 179471)\n[PASS] test_theMainDuelMustPairImd() (gas: 1468317)\n[PASS] test_theNextBlocksTradePlaysTheTurn() (gas: 1038495)\n[PASS] test_theOtherTeamCannotBidOnThisTurn() (gas: 412278)\n[PASS] test_whoPlaysTheTurnAndWhenDoesNotChangeTheMove() (gas: 1534194)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 727.18ms (945.84ms CPU time)\n\nRan 21 tests for test/Money.t.sol:MoneyTest\n[PASS] testFuzz_heldEqualsOwed(uint256) (runs: 48, μ: 10710677, ~: 10726288)\n[PASS] test_aFullChestTakesNoMore() (gas: 1059339)\n[PASS] test_aTradeStoppedByItsPriceLimitIsRefused() (gas: 582903)\n[PASS] test_blackCaptureIsPaidInItsTeamToken() (gas: 786454)\n[PASS] test_bountyTable() (gas: 44705)\n[PASS] test_checkmatePaysAQuarterOfTheChest() (gas: 766298)\n[PASS] test_chestShareGoesToTheChestWorthLess() (gas: 638456)\n[PASS] test_chestsStayAboutEqualInValue() (gas: 5511474)\n[PASS] test_exactOutputPaysTheFeeOnTheInput() (gas: 384979)\n[PASS] test_exactOutputWithTheChestShareInTheOutput() (gas: 780877)\n[PASS] test_failedPayoutStaysOwedAndTheTradeGoesThrough() (gas: 1219882)\n[PASS] test_feesCannotBeWithdrawnByAnyone() (gas: 449988)\n[PASS] test_firstTradeFundsChest0AndTheSlice() (gas: 363816)\n[PASS] test_fundChestNeedsTheRightValue() (gas: 48803)\n[PASS] test_fundChestOnlyForHookPools() (gas: 41868)\n[PASS] test_fundChestWithEth() (gas: 252918)\n[PASS] test_fundChestWithTokens() (gas: 193816)\n[PASS] test_openingSurchargeOnlyInTheMainDuel() (gas: 3469491)\n[PASS] test_payoutIsCappedAtTwiceTheTrade() (gas: 833810)\n[PASS] test_quietMovePaysNothing() (gas: 616203)\n[PASS] test_whiteCaptureIsPaidInItsTeamTokenFromItsOwnChest() (gas: 860122)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 798.33ms (820.72ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_boardHasBothKingsAndPlyIsBounded\n[PASS] invariant_heldEqualsOwed\n InvariantTest invariants (runs: 24, calls: 2400, reverts: 0)\n\n╭----------+----------------+-------+---------+----------╮\n| Contract | Selector       | Calls | Reverts | Discards |\n+========================================================+\n| Handler  | convert        | 301   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | fund           | 305   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | longPause      | 311   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | nextBlock      | 313   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | trade          | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeAgain     | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeNextBlock | 312   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | withdrawReward | 304   | 0       | 0        |\n╰----------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 40\n  Bound result 19999999000000007339\n  Bound result 19999999000001000001\n  Bound result 19999999000000008367\n  Bound result 19999999000000000581\n  Bound result 102\n  Bound result 18046385208871800035\n  Bound result 2855\n  Bound result 19999999007306351798\n  Bound result 100000000000000000\n  Bound result 31865262234567526\n  Bound result 49999999999950000\n  Bound result 19999999000000002681\n  Bound result 19999999000000003076\n  Bound result 5000\n  Bound result 42\n  Bound result 25333134452981760\n  Bound result 19999999000000000091\n  Bound result 18446744073709551615\n  Bound result 84\n  Bound result 4022291159582972627\n  Bound result 6\n  Bound result 423\n  Bound result 19999999000000200001\n  Bound result 35\n  Bound result 386549022720\n  Bound result 1100\n  Bound result 19999999000000000019\n  Bound result 1221339734816628308\n  Bound result 58\n  Bound result 115\n  Bound result 120\n  Bound result 107\n  Bound result 100\n  Bound result 120\n  Bound result 19999999000000000955\n  Bound result 19999999000000005018\n  Bound result 55\n  Bound result 1000000000003\n  Bound result 4413685840969725471\n  Bound result 1251\n  Bound result 34\n  Bound result 19999999000000000031\n  Bound result 19999999000000000904\n  Bound result 19999999000000003946\n  Bound result 15672660589582542013\n  Bound result 20396340581799926600\n  Bound result 13289\n  Bound result 19999999000000000321\n  Bound result 19999999000000004313\n  Bound result 12566133904435631808\n  Bound result 15386197539150620393\n  Bound result 2\n  Bound result 13\n  Bound result 8911052841178915333\n  Bound result 19999999000000003503\n  Bound result 25333134452981760\n  Bound result 4999999999995\n  Bound result 5060859166916801136\n  Bound result 100000\n  Bound result 86\n  swaps: 33\n  moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.08s (1.08s CPU time)\n\nRan 1 test for test/InvariantDuels.t.sol:InvariantDuelsTest\n[PASS]\nInvariantDuelsTest invariants:\n[PASS] invariant_everyBoardIsSound\n[PASS] invariant_everyCurrencyBalances\n[PASS] invariant_rightsMatchTheBoard\n[PASS] invariant_turnsAreWellFormed\n InvariantDuelsTest invariants (runs: 24, calls: 2880, reverts: 0)\n\n╭--------------+-------------------+-------+---------+----------╮\n| Contract     | Selector          | Calls | Reverts | Discards |\n+===============================================================+\n| DuelsHandler | addLiquidity      | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convert           | 220   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convertNextBlock  | 200   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | fund              | 241   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | longPause         | 221   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | nextBlock         | 215   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | pushThenNextBlock | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | removeLiquidity   | 253   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | roundTrip         | 251   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | trade             | 232   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeAgain        | 198   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeNextBlock    | 223   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | withdrawReward    | 210   | 0       | 0        |\n╰--------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 22905\n  Bound result 2565653098084874863\n  Bound result 12\n  Bound result 100000000\n  Bound result 72624976668147840\n  Bound result 4113140425\n  Bound result 12\n  Bound result 19999999999000000049\n  Bound result 4485934425771658475\n  Bound result 80\n  Bound result 12\n  Bound result 19999999999000000053\n  Bound result 12\n  Bound result 19999999999000011329\n  Bound result 19999999999000057006\n  Bound result 300\n  Bound result 19999999999000017333\n  Bound result 19999999999000230001\n  Bound result 12\n  Bound result 19999999999000000064\n  Bound result 4000000000000000\n  Bound result 25333134452981760\n  Bound result 133294633079153423663\n  Bound result 12\n  Bound result 19999999999000016320\n  Bound result 12\n  Bound result 19999999999000065474\n  Bound result 12\n  Bound result 19999999999000015212\n  Bound result 12\n  Bound result 19999999999000006709\n  Bound result 180000000000000004982\n  Bound result 12\n  Bound result 4999999999000017295\n  Bound result 5505625188992501493\n  Bound result 12\n  Bound result 180000000000000000422\n  Bound result 12\n  Bound result 180000000000000008617\n  Bound result 12\n  Bound result 18349840678526191568\n  Bound result 52\n  Bound result 19999999999000014270\n  Bound result 12\n  Bound result 19999999999000018368\n  Bound result 180000000000000800001\n  Bound result 12\n  Bound result 4999999999000007894\n  Bound result 6017502607293588406\n  Bound result 3114093074094967090166\n  Bound result 9999999899999990000000\n  Bound result 17715909253537394009\n  Bound result 12\n  Bound result 19999999999000015256\n  Bound result 900000001999999998\n  Bound result 805801794754002874\n  Bound result 4999999999000000601\n  Bound result 180000000000000007154\n  Bound result 12\n  Bound result 180000000000000043129\n  Bound result 12\n  Bound result 12\n  Bound result 2978945347\n  Bound result 12\n  Bound result 12\n  Bound result 100000000000000000000\n  Bound result 19999999999000001434\n  Bound result 12\n  Bound result 64\n  Bound result 4999999999001966081\n  Bound result 12\n  Bound result 248258377071896800\n  Bound result 19999999999000003857\n  Bound result 19999999999000065281\n  Bound result 12\n  Bound result 17830126982352984981\n  Bound result 89\n  Bound result 6301131947387847123\n  Bound result 120\n  Bound result 27612829230727045392\n  Bound result 12\n  Bound result 40\n  Bound result 4999999999000012523\n  Bound result 1000178988994931336848\n  Bound result 180000000000000887271\n  Bound result 12\n  Bound result 21931300337370667194\n  Bound result 189000000000000000001\n  Bound result 12\n  Bound result 112\n  Bound result 180000000000000024122\n  Bound result 12\n  Bound result 180000000000000010000\n  Bound result 12\n  Bound result 46649393329207593530\n  Bound result 12\n  Bound result 3000\n  Bound result 32680281041836782903\n  Bound result 11100177888994921344274\n  Bound result 19999999999000000996\n  Bound result 1812930352692670847190\n  Bound result 19999999999000002541\n  Bound result 2036944743885723607\n  Bound result 19999999999000094628\n  swaps: 43\n  moves: 23\n  Bound result 12\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.39s (3.37s CPU time)\n\nRan 24 test suites in 3.39s (7.46s CPU time): 180 tests passed, 0 failed, 0 skipped (180 total tests)\n","passed":true},{"durationMs":90,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDuelHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMDuelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDuelHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDuelHook.convert(address)\",\"IMDuelHook.convertBatch(address)\",\"IMDuelHook.fundChest((address,address,uint24,int24,address),bool,uint256)\",\"IMDuelHook.payOut((address,address,uint24,int24,address),address,address,uint256)\",\"IMDuelHook.sampleRoute(bytes,bytes[],bytes[])\",\"IMDuelHook.unlockCallback(bytes)\",\"IMDuelHook.withdraw(address,address)\",\"IMDuelHook.withdrawTo(address,address)\",\"IMDuelToken.approve(address,uint256)\",\"IMDuelToken.transfer(address,uint256)\",\"IMDuelToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":114,\"LICENSE\":16,\"foundry.toml\":29,\"launch.json\":37,\"launch/deployed.json\":12,\"launch/engine.json\":10,\"launch/hook.json\":39,\"launch/hook.sepolia.json\":39,\"script/LaunchPolicy.sol\":44,\"script/route_proof.py\":135,\"src/Bitboard.sol\":719,\"src/Chess.sol\":34,\"src/ChessEngineV5.sol\":852,\"src/IChessEngine.sol\":29,\"src/IMDuelHook.sol\":974,\"src/IMDuelToken.sol\":62,\"src/RoutePriceVerifier.sol\":50,\"src/Rules.sol\":45,\"src/Strength.sol\":47,\"src/proof/Bytes.sol\":144,\"src/proof/LICENSE\":22,\"src/proof/MerkleTrie.sol\":216,\"src/proof/PROVENANCE.md\":12,\"src/proof/RLPErrors.sol\":20,\"src/proof/RLPReader.sol\":249,\"test/Base.sol\":211,\"test/BidOwner.t.sol\":118,\"test/Bidding.t.sol\":224,\"test/Chessboard.sol\":79,\"test/CrossBlock.t.sol\":195,\"test/Draws.t.sol\":90,\"test/FactoryLaunch.t.sol\":173,\"test/Game.t.sol\":348,\"test/GasEstimate.t.sol\":120,\"test/Harness.sol\":83,\"test/HookGas.t.sol\":161,\"test/Hostile.t.sol\":535,\"test/Invariant.t.sol\":150,\"test/InvariantDuels.t.sol\":529,\"test/Launch.t.sol\":159,\"test/LaunchAdmission.t.sol\":167,\"test/LaunchFiles.t.sol\":75,\"test/Money.t.sol\":353,\"test/Predict.t.sol\":102,\"test/Proof_SyncRecipient.t.sol\":141,\"test/RoutePriceVerifier.t.sol\":135,\"test/RouteProof.sol\":69,\"test/RouteSample.t.sol\":168,\"test/Size.t.sol\":36,\"test/Slice.t.sol\":392,\"test/Strength.t.sol\":85,\"test/Teams.t.sol\":87,\"test/Token.t.sol\":82,\"test/Tokens.sol\":193,\"test/V4Fixture.sol\":100,\"test/data/chess.json\":1,\"test/data/conversion-validation.json\":770,\"test/data/conversion.json\":98,\"test/data/endings.json\":1,\"test/data/legal.json\":1,\"test/data/random.json\":1,\"test/data/route-proof.json\":16,\"test/test_route_proof.py\":72},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"dc773e947db50c44350b21367753e81b5300c81e093a3053e84532fe0e83edc4","verifiedTreeHash":"a41dbb31b2ef1b296ace6cbcb5c8bda6fe7c831d","verifierVersion":"0.1.0+d372f99e"},{"checks":[{"durationMs":81310,"exitCode":0,"name":"build","output":"Compiling 119 files with Solc 0.8.26\nSolc 0.8.26 finished in 81.17s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:22:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                      ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:38:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                                      ^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:109:57:\n    |\n109 |             uint64 d = f >= r ? DIAG >> (8 * (f - r)) : DIAG << (8 * (r - f));\n    |                                                         ^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:110:65:\n    |\n110 |             uint64 a = f + r <= 7 ? ANTI >> (8 * (7 - f - r)) : ANTI << (8 * (f + r - 7));\n    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:38:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:69:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                                                     ^^^^^^^^^^^^^^^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:62:18:\n   |\n62 |         returns (uint16, Rules.Played memory)\n   |                  ^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:62:26:\n   |\n62 |         returns (uint16, Rules.Played memory)\n   |                          ^^^^^^^^^^^^^^^^^^^\n\nWarning (2072): Unused local variable.\n  --> test/Game.t.sol:87:9:\n   |\n87 |         IMDuelHook.Game memory g = st();\n   |         ^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n  --> test/LaunchFiles.t.sol:70:5:\n   |\n70 |     function test_engineLaunchFileDeploysTheEngineWithoutArguments() public {\n   |     ^ (Relevant source part starts here and spans across multiple lines).\n\n","passed":true},{"durationMs":3610,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/LaunchFiles.t.sol:LaunchFilesTest\n[PASS] test_engineLaunchFileDeploysTheEngineWithoutArguments() (gas: 7834)\n[PASS] test_ethereumLaunchFile() (gas: 1332241)\n[PASS] test_sepoliaLaunchFile() (gas: 1327328)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.47ms (1.46ms CPU time)\n\nRan 4 tests for test/Token.t.sol:TokenTest\n[PASS] test_allowances() (gas: 347653)\n[PASS] test_fixedSupplyToTheDeployer() (gas: 39384)\n[PASS] test_nothingPrivileged() (gas: 192691)\n[PASS] test_transfers() (gas: 188240)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.56ms (1.99ms CPU time)\n\nRan 1 test for test/Size.t.sol:SizeTest\n[PASS] test_deployedCodeFitsTheBudgets() (gas: 1252630)\nLogs:\n  hook: 20620\n  engine v5: 23339\n  route price verifier: 5551\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.80ms (3.69ms CPU time)\n\nRan 7 tests for test/Draws.t.sol:DrawsTest\n[PASS] test_checkmate() (gas: 240001)\n[PASS] test_moveCap() (gas: 240042)\n[PASS] test_ordinaryMoveDoesNotEndTheGame() (gas: 179990)\n[PASS] test_quietMoveLimit() (gas: 246260)\n[PASS] test_stalemate() (gas: 237814)\n[PASS] test_thirdRepetition() (gas: 964010)\n[PASS] test_tooFewPiecesToMate() (gas: 231639)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 8.95ms (7.07ms CPU time)\n\nRan 2 tests for test/FactoryLaunch.t.sol:FactoryLaunchTest\n[PASS] test_factoryLaunchWithRealTokenAboveImd() (gas: 15562391)\n[PASS] test_factoryLaunchWithRealTokenBelowImd() (gas: 15528327)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 13.15ms (24.81ms CPU time)\n\nRan 12 tests for test/Bidding.t.sol:BiddingTest\n[PASS] test_aBountyIsPaidFromTheMoversChest() (gas: 1357258)\n[PASS] test_aBuyOfTheOtherTeamBidsForItsNextMove() (gas: 1952736)\n[PASS] test_aBuyOfTheOtherTeamTimesTheSilentSideOutAndBids() (gas: 1222539)\n[PASS] test_aClosedWindowTakesNoBidsForItsMove() (gas: 1062919)\n[PASS] test_aMoveIsOpenForTurnBlocksBlocks() (gas: 1040501)\n[PASS] test_aNewTopMustPayMoreThanTenPercentMore() (gas: 974403)\n[PASS] test_aNextMoveBidCarriesIntoTheNextGame() (gas: 1467032)\n[PASS] test_aWindowWithoutBidsBecomesTheFirstBidsBlock() (gas: 1283751)\n[PASS] test_bidsCountAcrossTheWholeWindow() (gas: 1511281)\n[PASS] test_theMoveDependsOnlyOnTheWindowsLastBlock() (gas: 1621480)\n[PASS] test_theMoversOwnBuyAfterItsWindowBidsForItsNextMove() (gas: 1077628)\n[PASS] test_thePreviewIsTheMoveThatIsPlayed() (gas: 1222903)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 14.70ms (25.22ms CPU time)\n\nRan 6 tests for test/RouteSample.t.sol:RouteSampleTest\n[PASS] test_authenticatedBlockStartsRejectTheSameAttack() (gas: 3422402)\n[PASS] test_callerSamplesCannotSellTheEthPileAtAPushedPrice() (gas: 1710709)\nLogs:\n  route attacker profit at the honest price: -87002115995172580\n\n[PASS] test_duplicateSampleCannotRotateOutHonestHistory() (gas: 493037)\n[PASS] test_gapRequiresThreeConsecutiveSamplesAgain() (gas: 1855928)\n[PASS] test_honestBatchRunsAndStopsAtTheHistoricalPriceBand() (gas: 1175578)\n[PASS] test_staleRoutePricesWaitWithoutLosingClaims() (gas: 1298227)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 18.70ms (15.73ms CPU time)\n\nRan 17 tests for test/CrossBlock.t.sol:CrossBlockTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 6015280)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3994805)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3061845)\n[PASS] test_anEthBatchIsCapped() (gas: 5275537)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3200810)\n[PASS] test_anyoneCanConvert() (gas: 462085)\n[PASS] test_booksBalanceAcrossPools() (gas: 8475081)\n[PASS] test_crossBlockViaConvert() (gas: 5551997)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390298682200\n\n[PASS] test_crossBlockViaFirstTrade() (gas: 5714127)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390291136661\n\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4251851)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245524)\n[PASS] test_ethRouteSameBlockPush() (gas: 5229243)\nLogs:\n  ETH pile before (1e18): 500000000000000000\n  ETH the batch sold (1e18): 0\n  IMD it got (1e18): 0\n  IMD it was worth at the home price (1e18): 0\n  attacker profit (ETH at the home price, 1e18): -29770639430607030\n\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764118)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487372)\n[PASS] test_noRouteNoDuel() (gas: 1030839)\n[PASS] test_oneEthBatchPerBlock() (gas: 5800437)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187260)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 34.00ms (64.66ms CPU time)\n\nRan 2 tests for test/LaunchAdmission.t.sol:LaunchAdmissionTest\n[PASS] test_blankChainTokenAboveImd() (gas: 25012847)\n[PASS] test_blankChainTokenBelowImd() (gas: 24941802)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 36.14ms (59.83ms CPU time)\n\nRan 8 tests for test/BidOwner.t.sol:BidOwnerTest\n[PASS] testFuzz_otherDataLengthsNeverCreditTheBundler(bytes) (runs: 256, μ: 385548, ~: 385542)\n[PASS] test_aHookWithoutAnEthRouteRejectsRouteProofs() (gas: 32512)\n[PASS] test_abiEncodedOwnerCreditsTheWallet() (gas: 384610)\n[PASS] test_abiEncodedOwnerReceivesTheCaptureBounty() (gas: 1116469)\n[PASS] test_dirtyAbiPaddingFallsBackToSender() (gas: 384788)\n[PASS] test_missingOwnerPaysTheSwapSender() (gas: 1133080)\n[PASS] test_missingOwnerUsesSwapSenderNotBundler() (gas: 419516)\n[PASS] test_packedOwnerCreditsTheWallet() (gas: 384689)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 36.79ms (38.77ms CPU time)\n\nRan 14 tests for test/Slice.t.sol:SliceTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 6014785)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3994787)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3061820)\n[PASS] test_anEthBatchIsCapped() (gas: 5275421)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3200736)\n[PASS] test_anyoneCanConvert() (gas: 462063)\n[PASS] test_booksBalanceAcrossPools() (gas: 8475053)\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4251814)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245476)\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764104)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487390)\n[PASS] test_noRouteNoDuel() (gas: 1030789)\n[PASS] test_oneEthBatchPerBlock() (gas: 5800343)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187246)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 41.40ms (47.93ms CPU time)\n\nRan 13 tests for test/RoutePriceVerifier.t.sol:RoutePriceVerifierTest\n[PASS] testFuzz_authenticatesSlot0AndItsBlock(uint160,uint96) (runs: 256, μ: 200948, ~: 200425)\nLogs:\n  Bound result 47419737482723328\n\n[PASS] test_changedAccountProofIsRefused() (gas: 100311)\n[PASS] test_changedHeaderIsNotCanonical() (gas: 80813)\n[PASS] test_changedStorageProofIsRefused() (gas: 158336)\n[PASS] test_clientGeneratedPoolManagerProof() (gas: 241704)\n[PASS] test_emptyProofsAreRefused() (gas: 195111)\n[PASS] test_extraProofNodesAreRefused() (gas: 195861)\n[PASS] test_outOfRangePriceIsRefused() (gas: 192156)\n[PASS] test_theBatchBlockCannotSupplyASample() (gas: 80841)\n[PASS] test_uninitializedPoolPriceIsRefused() (gas: 180149)\n[PASS] test_unknownOrExpiredHeaderIsRefused() (gas: 81201)\n[PASS] test_wrongManagerIsRefused() (gas: 108622)\n[PASS] test_wrongPoolSlotIsRefused() (gas: 165141)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 42.44ms (47.11ms CPU time)\n\nRan 3 tests for test/Launch.t.sol:LaunchTest\n[PASS] test_launchPairedWithEth() (gas: 22438868)\n[PASS] test_launchTokenAboveImd() (gas: 24832045)\nLogs:\n  first buy (opens game 0, bids for White): 366448\n\n[PASS] test_launchTokenBelowImd() (gas: 24212457)\nLogs:\n  first buy (opens game 0, bids for White): 361698\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 59.61ms (149.75ms CPU time)\n\nRan 5 tests for test/Teams.t.sol:TeamsTest\n[PASS] test_aDynamicFeePoolPlaysWithAZeroLpFee() (gas: 2913516)\n[PASS] test_nativeEthIsATeamLikeAnyToken() (gas: 2327096)\n[PASS] test_poolsOnOneHookKeepSeparateGamesAndChests() (gas: 3404887)\n[PASS] test_sliceSide() (gas: 1819047)\n[PASS] test_twoPlainTokensCannotDuel() (gas: 1591040)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 65.69ms (9.20ms CPU time)\n\nRan 11 tests for test/Hostile.t.sol:HostileTest\n[PASS] test_aCallbackTokenOwnerLeavingADebtCannotFreezeTheDuel() (gas: 5276059)\n[PASS] test_aDeadEngineLeavesTheTurnQueuedAndTradingGoesOn() (gas: 3555280)\n[PASS] test_aGasBurningEngineCostsOneExpensiveTradeNotTheGame() (gas: 9727669)\n[PASS] test_aTokenReturningNothingFundsAChest() (gas: 2185559)\n[PASS] test_anEthOwnerLeavingADebtCannotFreezeTheDuel() (gas: 4171134)\n[PASS] test_anOwnerThatCannotReceiveWithdrawsElsewhere() (gas: 3554214)\n[PASS] test_blockedRecipientStaysOwedAndTradingContinues() (gas: 4185544)\n[PASS] test_brokenEngineFallsBackToALegalMove() (gas: 5150392)\n[PASS] test_feeOnTransferTokenFundsOnlyWhatArrives() (gas: 2731451)\n[PASS] test_reenteringTokenCannotBreakAPayout() (gas: 4721771)\n[PASS] test_starvingTheEngineNeverPlaysTheFallback() (gas: 25149078)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 65.80ms (82.24ms CPU time)\n\nRan 5 tests for test/HookGas.t.sol:HookGasTest\n[PASS] test_conversionGas() (gas: 3656290)\nLogs:\n  plain trade: 207766\n  trade that converts a batch: 271658\n\n[PASS] test_hookShareOfAMovingTrade() (gas: 29104655)\nLogs:\n  hook share of a moving trade: average: 278442\n  hook share of a moving trade: max: 321393\n\n[PASS] test_middlegameGas() (gas: 9501378)\nLogs:\n  first bid in a new pool: 270627\n  bidding trade: 191991\n  outbidding trade: 208087\n  plain trade: 182668\n  next block: play the turn and bid: 550662\n\n[PASS] test_openingGas() (gas: 4539846)\nLogs:\n  first bid in a new pool: 270627\n  bidding trade: 192702\n  outbidding trade: 208087\n  plain trade: 182668\n  next block: play the turn and bid: 508031\n\n[PASS] test_runningGameGas() (gas: 54586984)\nLogs:\n  running game, moving trades measured: 50\n  running game, moving trade average: 450671\n  running game, moving trade max: 542125\n\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 96.45ms (145.38ms CPU time)\n\nRan 8 tests for test/Strength.t.sol:StrengthTest\n[PASS] testFuzz_boundedAndMonotonic(uint256,uint256,uint256) (runs: 256, μ: 9098, ~: 8663)\nLogs:\n  Bound result 84743910324299725283116389454424367006300368779282074797\n\n[PASS] testFuzz_depthMonotonic(uint256,uint256,uint256) (runs: 256, μ: 12020, ~: 12425)\nLogs:\n  Bound result 84743910324299725283116389454424367006300368779282074797\n  Bound result 115792089237316195423486241098363608127986868276186139672451283691800154846424\n\n[PASS] testFuzz_shiftKeepsTheShare(uint256,uint256,uint8) (runs: 256, μ: 14655, ~: 15266)\nLogs:\n  Bound result 10\n  Bound result 18\n  Bound result 4608\n\n[PASS] testFuzz_tenfoldIsAboutThirtyPoints(uint128,uint128) (runs: 256, μ: 13840, ~: 13724)\nLogs:\n  Bound result 340282366920938463463374607431767441456\n  Bound result 336879543251729078913183354370646567\n\n[PASS] testFuzz_unscalablePaidFollowsTheCurve(uint256,uint256) (runs: 256, μ: 13953, ~: 14208)\nLogs:\n  Bound result 115790931316423822261616749298837820974191451965793907633817189433447440043359\n  Bound result 115792089237316195423570985008687907853269984665640564039457446568959657720000\n\n[PASS] test_decidedPoints() (gas: 15577)\n[PASS] test_hugePaymentsDoNotRevert() (gas: 12854)\n[PASS] test_zeroInputs() (gas: 4493)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 129.55ms (156.03ms CPU time)\n\nRan 3 tests for test/GasEstimate.t.sol:GasEstimateTest\n[PASS] test_atTheEstimateADueBatchConverts() (gas: 5009784)\nLogs:\n  smallest gas limit that converts: 310375\n\n[PASS] test_atTheEstimateTheBountyIsPaidNotOwed() (gas: 4997823)\nLogs:\n  smallest gas limit that plays and pays: 404882\n\n[PASS] test_noGasLimitPlaysTheMoveAndLeavesItsBountyOwed() (gas: 18563180)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 129.64ms (143.52ms CPU time)\n\nRan 23 tests for test/Game.t.sol:GameTest\n[PASS] testFuzz_swapsNeverRevert(uint256) (runs: 48, μ: 9661448, ~: 9658868)\n[PASS] test_aGamePlaysOutThroughSwaps() (gas: 57294244)\nLogs:\n  moves played: 79\n  bids (incl. outbid ones): 101\n  most gas for a swap: 1125299\n\n[PASS] test_aSmallerTradeInTheBlockDoesNotTakeTheTurn() (gas: 686523)\n[PASS] test_aTimeoutTurnCanBeTakenInItsBlock() (gas: 1304281)\n[PASS] test_aTradeBidsForTheTurn() (gas: 483510)\n[PASS] test_aTurnClosesWithItsBlock() (gas: 965205)\n[PASS] test_anExpiredTurnBecomesATimeoutTurn() (gas: 1413397)\n[PASS] test_anOldTurnIsPlayedWithTheHistoryContractsHash() (gas: 1754927)\n[PASS] test_anyTradeSizeBids() (gas: 450089)\n[PASS] test_lateTradeStillBidsNormally() (gas: 429723)\n[PASS] test_onlyThePoolManagerAndTheHookCanCallIn() (gas: 169413)\n[PASS] test_ownerDefaultsToSwapSender() (gas: 418165)\n[PASS] test_playedPositionsEnterTheRepetitionMemory() (gas: 915403)\n[PASS] test_rejectsBadConfig() (gas: 9247)\n[PASS] test_silentSidePlaysAtZeroStrength() (gas: 1048084)\n[PASS] test_strengthFollowsTheShareOfThePool() (gas: 6374901)\n[PASS] test_teamsSwapColoursEveryGame() (gas: 926885)\n[PASS] test_theBiggestTradeInTheBlockOwnsTheTurn() (gas: 782425)\n[PASS] test_theFirstPoolOpenedIsTheMainDuel() (gas: 179471)\n[PASS] test_theMainDuelMustPairImd() (gas: 1465618)\n[PASS] test_theNextBlocksTradePlaysTheTurn() (gas: 1038530)\n[PASS] test_theOtherTeamCannotBidOnThisTurn() (gas: 412294)\n[PASS] test_whoPlaysTheTurnAndWhenDoesNotChangeTheMove() (gas: 1534248)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 721.32ms (838.13ms CPU time)\n\nRan 21 tests for test/Money.t.sol:MoneyTest\n[PASS] testFuzz_heldEqualsOwed(uint256) (runs: 48, μ: 10720442, ~: 10797515)\n[PASS] test_aFullChestTakesNoMore() (gas: 1059275)\n[PASS] test_aTradeStoppedByItsPriceLimitIsRefused() (gas: 582949)\n[PASS] test_blackCaptureIsPaidInItsTeamToken() (gas: 774230)\n[PASS] test_bountyTable() (gas: 44837)\n[PASS] test_checkmatePaysAQuarterOfTheChest() (gas: 754080)\n[PASS] test_chestShareGoesToTheChestWorthLess() (gas: 638508)\n[PASS] test_chestsStayAboutEqualInValue() (gas: 5512254)\n[PASS] test_exactOutputPaysTheFeeOnTheInput() (gas: 385005)\n[PASS] test_exactOutputWithTheChestShareInTheOutput() (gas: 790954)\n[PASS] test_failedPayoutStaysOwedAndTheTradeGoesThrough() (gas: 1213589)\n[PASS] test_feesCannotBeWithdrawnByAnyone() (gas: 450014)\n[PASS] test_firstTradeFundsChest0AndTheSlice() (gas: 363842)\n[PASS] test_fundChestNeedsTheRightValue() (gas: 48758)\n[PASS] test_fundChestOnlyForHookPools() (gas: 41896)\n[PASS] test_fundChestWithEth() (gas: 252873)\n[PASS] test_fundChestWithTokens() (gas: 193846)\n[PASS] test_openingSurchargeOnlyInTheMainDuel() (gas: 3489323)\n[PASS] test_payoutIsCappedAtTwiceTheTrade() (gas: 821586)\n[PASS] test_quietMovePaysNothing() (gas: 616187)\n[PASS] test_whiteCaptureIsPaidInItsTeamTokenFromItsOwnChest() (gas: 847898)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 785.42ms (801.32ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_boardHasBothKingsAndPlyIsBounded\n[PASS] invariant_heldEqualsOwed\n InvariantTest invariants (runs: 24, calls: 2400, reverts: 0)\n\n╭----------+----------------+-------+---------+----------╮\n| Contract | Selector       | Calls | Reverts | Discards |\n+========================================================+\n| Handler  | convert        | 301   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | fund           | 305   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | longPause      | 311   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | nextBlock      | 313   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | trade          | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeAgain     | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeNextBlock | 312   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | withdrawReward | 304   | 0       | 0        |\n╰----------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3\n  Bound result 19999999000000004867\n  Bound result 19999999000001000001\n  Bound result 19999999000000008654\n  Bound result 19999999000000000581\n  Bound result 91\n  Bound result 18046385208871800035\n  Bound result 2908\n  Bound result 19999999007306351798\n  Bound result 11\n  Bound result 31865262234567526\n  Bound result 19999999000000887221\n  Bound result 19999999000000003146\n  Bound result 19999999000000002182\n  Bound result 5000\n  Bound result 102\n  Bound result 7037082190885724022\n  Bound result 19999999386547056641\n  Bound result 18446744073709551615\n  Bound result 90\n  Bound result 4022291159582972627\n  Bound result 6\n  Bound result 4113140427\n  Bound result 19999999000000020001\n  Bound result 79\n  Bound result 386549022720\n  Bound result 1205\n  Bound result 19999999000000000015\n  Bound result 1221339734816628308\n  Bound result 35\n  Bound result 83\n  Bound result 90\n  Bound result 79\n  Bound result 40\n  Bound result 120\n  Bound result 19999999000000001148\n  Bound result 19999999000000007280\n  Bound result 55\n  Bound result 1000000000003\n  Bound result 4413685840969725471\n  Bound result 1251\n  Bound result 119\n  Bound result 19999999000000000031\n  Bound result 19999999000000000904\n  Bound result 19999999000000003380\n  Bound result 15672660589582542013\n  Bound result 20396340581799926600\n  Bound result 13006\n  Bound result 19999999000000000321\n  Bound result 19999999000000004445\n  Bound result 12566133904435631808\n  Bound result 15386197539150620393\n  Bound result 2\n  Bound result 14\n  Bound result 19999999000000002240\n  Bound result 19999999000000003282\n  Bound result 7037082190885724022\n  Bound result 4999999999995\n  Bound result 5060859166916801136\n  Bound result 100000\n  Bound result 20\n  swaps: 33\n  moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.00s (999.24ms CPU time)\n\nRan 1 test for test/Predict.t.sol:PredictTest\n[PASS] test_aBotCannotPickItsOwnCapturingMoves() (gas: 603211199)\nLogs:\n  bot moves: 0\n  other moves: 150\n  captures among other moves: 25\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.03s (1.03s CPU time)\n\nRan 1 test for test/InvariantDuels.t.sol:InvariantDuelsTest\n[PASS]\nInvariantDuelsTest invariants:\n[PASS] invariant_everyBoardIsSound\n[PASS] invariant_everyCurrencyBalances\n[PASS] invariant_rightsMatchTheBoard\n[PASS] invariant_turnsAreWellFormed\n InvariantDuelsTest invariants (runs: 24, calls: 2880, reverts: 0)\n\n╭--------------+-------------------+-------+---------+----------╮\n| Contract     | Selector          | Calls | Reverts | Discards |\n+===============================================================+\n| DuelsHandler | addLiquidity      | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convert           | 220   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convertNextBlock  | 200   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | fund              | 241   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | longPause         | 221   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | nextBlock         | 215   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | pushThenNextBlock | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | removeLiquidity   | 253   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | roundTrip         | 251   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | trade             | 232   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeAgain        | 198   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeNextBlock    | 223   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | withdrawReward    | 210   | 0       | 0        |\n╰--------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 23537\n  Bound result 2565653098084874863\n  Bound result 12\n  Bound result 100000000\n  Bound result 72624976668147840\n  Bound result 3057007858\n  Bound result 12\n  Bound result 19999999999000000049\n  Bound result 4485934425771658475\n  Bound result 120\n  Bound result 12\n  Bound result 4503599627370496\n  Bound result 12\n  Bound result 19999999999000010777\n  Bound result 499999999500000000\n  Bound result 7000\n  Bound result 19999999999000017653\n  Bound result 19999999999000200001\n  Bound result 12\n  Bound result 19999999999000000016\n  Bound result 19999999999000106693\n  Bound result 25333134452981760\n  Bound result 133294633079153423663\n  Bound result 12\n  Bound result 19999999999000000901\n  Bound result 12\n  Bound result 19999999999000009496\n  Bound result 12\n  Bound result 19999999999000014703\n  Bound result 12\n  Bound result 19999999999000006767\n  Bound result 180000000000000005449\n  Bound result 12\n  Bound result 5505625188992501493\n  Bound result 12\n  Bound result 135878278616570359945\n  Bound result 12\n  Bound result 180000000000000020166\n  Bound result 12\n  Bound result 18672102751851390356\n  Bound result 52\n  Bound result 19999999999000015500\n  Bound result 12\n  Bound result 19999999999000016974\n  Bound result 180000000000000800001\n  Bound result 12\n  Bound result 4999999999000000900\n  Bound result 6017502607293588406\n  Bound result 9976146240558519260795\n  Bound result 9999999899999990000000\n  Bound result 4845199830596549539788\n  Bound result 17715909253537394009\n  Bound result 12\n  Bound result 600000000000000000000\n  Bound result 19999999999000014937\n  Bound result 805801794754002874\n  Bound result 4999999999000000601\n  Bound result 20000000000000000000\n  Bound result 12\n  Bound result 180000000000000043129\n  Bound result 12\n  Bound result 12\n  Bound result 4999999999100527562\n  Bound result 12\n  Bound result 12\n  Bound result 100000000000000000000\n  Bound result 19999999999000000569\n  Bound result 12\n  Bound result 93\n  Bound result 4999999999001966081\n  Bound result 12\n  Bound result 19938013784840535176\n  Bound result 19999999999000003908\n  Bound result 19999999999000065281\n  Bound result 12\n  Bound result 17830126982352984981\n  Bound result 75\n  Bound result 9000000000000000000\n  Bound result 8422376485763105360\n  Bound result 120\n  Bound result 27612829230727045392\n  Bound result 12\n  Bound result 80\n  Bound result 1000178988994931336848\n  Bound result 180000000000000887271\n  Bound result 12\n  Bound result 21931300337370667194\n  Bound result 184000000000000000001\n  Bound result 12\n  Bound result 112\n  Bound result 180000000000000023646\n  Bound result 12\n  Bound result 180000000000000995001\n  Bound result 12\n  Bound result 46649393329207593530\n  Bound result 12\n  Bound result 2155872511\n  Bound result 32680281041836782903\n  Bound result 1100177988994931344576\n  Bound result 19999999999000000996\n  Bound result 1812930352692670847190\n  Bound result 4000000000000000\n  Bound result 3398176614991109133\n  Bound result 19999999999000094628\n  swaps: 43\n  moves: 22\n  Bound result 12\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.43s (3.43s CPU time)\n\nRan 23 test suites in 3.44s (7.77s CPU time): 171 tests passed, 0 failed, 0 skipped (171 total tests)\n","passed":true},{"durationMs":68,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDuelHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMDuelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDuelHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDuelHook.convert(address)\",\"IMDuelHook.convertBatch(address)\",\"IMDuelHook.fundChest((address,address,uint24,int24,address),bool,uint256)\",\"IMDuelHook.payOut(address,address,uint256)\",\"IMDuelHook.sampleRoute(bytes,bytes[],bytes[])\",\"IMDuelHook.unlockCallback(bytes)\",\"IMDuelHook.withdraw(address,address)\",\"IMDuelHook.withdrawTo(address,address)\",\"IMDuelToken.approve(address,uint256)\",\"IMDuelToken.transfer(address,uint256)\",\"IMDuelToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":73,\"LICENSE\":16,\"foundry.toml\":29,\"launch.json\":39,\"launch/deployed.json\":12,\"launch/engine.json\":10,\"launch/hook.json\":39,\"launch/hook.sepolia.json\":39,\"script/LaunchPolicy.sol\":44,\"script/route_proof.py\":135,\"src/Bitboard.sol\":719,\"src/Chess.sol\":34,\"src/ChessEngineV5.sol\":852,\"src/IChessEngine.sol\":29,\"src/IMDuelHook.sol\":957,\"src/IMDuelToken.sol\":62,\"src/RoutePriceVerifier.sol\":50,\"src/Rules.sol\":45,\"src/Strength.sol\":47,\"src/proof/Bytes.sol\":144,\"src/proof/LICENSE\":22,\"src/proof/MerkleTrie.sol\":216,\"src/proof/PROVENANCE.md\":12,\"src/proof/RLPErrors.sol\":20,\"src/proof/RLPReader.sol\":249,\"test/Base.sol\":211,\"test/BidOwner.t.sol\":101,\"test/Bidding.t.sol\":224,\"test/Chessboard.sol\":79,\"test/CrossBlock.t.sol\":195,\"test/Draws.t.sol\":90,\"test/FactoryLaunch.t.sol\":173,\"test/Game.t.sol\":345,\"test/GasEstimate.t.sol\":120,\"test/Harness.sol\":83,\"test/HookGas.t.sol\":151,\"test/Hostile.t.sol\":359,\"test/Invariant.t.sol\":150,\"test/InvariantDuels.t.sol\":519,\"test/Launch.t.sol\":158,\"test/LaunchAdmission.t.sol\":159,\"test/LaunchFiles.t.sol\":75,\"test/Money.t.sol\":353,\"test/Predict.t.sol\":102,\"test/RoutePriceVerifier.t.sol\":135,\"test/RouteProof.sol\":69,\"test/RouteSample.t.sol\":168,\"test/Size.t.sol\":36,\"test/Slice.t.sol\":392,\"test/Strength.t.sol\":85,\"test/Teams.t.sol\":87,\"test/Token.t.sol\":82,\"test/Tokens.sol\":137,\"test/V4Fixture.sol\":100,\"test/data/chess.json\":1,\"test/data/conversion-validation.json\":770,\"test/data/conversion.json\":98,\"test/data/endings.json\":1,\"test/data/legal.json\":1,\"test/data/random.json\":1,\"test/data/route-proof.json\":16,\"test/test_route_proof.py\":72},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":20841,"exitCode":0,"name":"slither","output":"[high/medium] incorrect-exp at src/IMDuelHook.sol:256: IMDuelHook.teamOf(uint8,uint32,bool) (src/IMDuelHook.sol#256-258) has bitwise-xor operator ^ instead of the exponentiation operator **: \n[high/medium] incorrect-exp at src/IMDuelHook.sol:256: IMDuelHook.teamOf(uint8,uint32,bool) (src/IMDuelHook.sol#256-258) has bitwise-xor operator ^ instead of the exponentiation operator **: \n[medium/high] incorrect-equality at src/IMDuelHook.sol:335: IMDuelHook._takeFee(PoolId,PoolKey,uint256,uint256,uint256,IMDuelHook.Game) (src/IMDuelHook.sol#335-347) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMDuelHook.sol:559: IMDuelHook._sampleAt(uint256,uint160,uint256) (src/IMDuelHook.sol#559-565) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMDuelHook.sol:570: IMDuelHook._reference(uint256,bool) (src/IMDuelHook.sol#570-576) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:614: Reentrancy in IMDuelHook._play(PoolId,PoolKey,IMDuelHook.Game,bool,BalanceDelta,address,uint160,uint256) (src/IMDuelHook.sol#614-653):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:305: Reentrancy in IMDuelHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDuelHook.sol#305-330):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:496: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#496-519):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:496: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#496-519):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:531: Reentrancy in IMDuelHook._swapClaims(PoolKey,Currency,uint256,bool) (src/IMDuelHook.sol#531-552):\n[medium/medium] uninitialized-local at src/Bitboard.sol:643: Bitboard._targets(Bitboard.Pos,Bitboard.Gen,uint8,bool).ep (src/Bitboard.sol#643) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:480: Bitboard._anyLegal(Bitboard.Pos,bool).scratch (src/Bitboard.sol#480) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:581: IMDuelHook._pack(uint160).e (src/IMDuelHook.sol#581) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:425: Bitboard.isLegal(Bitboard.Pos,uint16,bool).scratch (src/Bitboard.sol#425) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:497: ChessEngineV5._queening(ChessEngineV5.Ctx,Bitboard.Pos,uint64,uint64,uint64,uint64,uint64).a (src/ChessEngineV5.sol#497) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:382: IMDuelHook._pay(PoolId,IMDuelHook.Game,Currency,address,uint256).paid (src/IMDuelHook.sol#382) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:182: ChessEngineV5._choose(ChessEngineV5.Ctx,uint256,uint256,uint64[8],uint32,uint16).seen (src/ChessEngineV5.sol#182) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:400: Bitboard._legalTargets(Bitboard.Pos,Bitboard.Gen,Bitboard.Pos,uint8,bool).ep (src/Bitboard.sol#400) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:236: Bitboard.pieceAttacks(Bitboard.Pos,uint64,uint64,bool).a (src/Bitboard.sol#236) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:203: ChessEngineV5._choose(ChessEngineV5.Ctx,uint256,uint256,uint64[8],uint32,uint16).refined (src/ChessEngineV5.sol#203) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:580: Bitboard.mateInOne(Bitboard.Pos,bool,uint256).scratch (src/Bitboard.sol#580) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:866: IMDuelHook._repeated(PoolId,IMDuelHook.Game).seen (src/IMDuelHook.sol#866) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:414: ChessEngineV5._placement(ChessEngineV5.Ctx,uint8,uint8,uint8,uint8,bool,bool).near (src/ChessEngineV5.sol#414) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:276: ChessEngineV5._context(uint256,uint32,uint32,bool,bool).nonPawn (src/ChessEngineV5.sol#276) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:799: IMDuelHook._apply(PoolId,IMDuelHook.Game,uint16,Rules.Played,IMDuelHook.Trade).result (src/IMDuelHook.sol#799) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:377: ChessEngineV5._cheap(ChessEngineV5.Ctx,uint16).squeeze (src/ChessEngineV5.sol#377) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:803: IMDuelHook._apply(PoolId,IMDuelHook.Game,uint16,Rules.Played,IMDuelHook.Trade).reason (src/IMDuelHook.sol#803) is a local variable never initialized\n[medium/medium] unused-return at src/IMDuelHook.sol:418: IMDuelHook._withdraw(address,Currency,address) (src/IMDuelHook.sol#418-424) ignores return value by manager.unlock(abi.encode(uint8(0),abi.encode(to,c,amount))) (src/IMDuelHook.sol#422)\n[medium/medium] unused-return at src/IMDuelHook.sol:531: IMDuelHook._swapClaims(PoolKey,Currency,uint256,bool) (src/IMDuelHook.sol#531-552) ignores return value by (sqrtPrice,None,None,None) = manager.getSlot0(key.toId()) (src/IMDuelHook.sol#536)\n[medium/medium] unused-return at src/IMDuelHook.sol:288: IMDuelHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/IMDuelHook.sol#288-303) ignores return value by (sqrtPrice,None,None,None) = manager.getSlot0(id) (src/IMDuelHook.sol#295)\n[medium/medium] unused-return at src/IMDuelHook.sol:439: IMDuelHook.convert(Currency) (src/IMDuelHook.sol#439-441) ignores return value by manager.unlock(abi.encode(uint8(2),abi.encode(address(0),c,uint256(0)))) (src/IMDuelHook.sol#440)\n[low/high] shadowing-local at src/ChessEngineV5.sol:817: ChessEngineV5._mop(ChessEngineV5.Ctx,uint8,uint8,uint8,bool,bool).at (src/ChessEngineV5.sol#705) shadows:\n[low/medium] missing-zero-check at src/IMDuelHook.sol:461: IMDuelHook.unlockCallback(bytes).who (src/IMDuelHook.sol#461) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:496: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#496-519):\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:335: Reentrancy in IMDuelHook._takeFee(PoolId,PoolKey,uint256,uint256,uint256,IMDuelHook.Game) (src/IMDuelHook.sol#335-347):\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:381: Reentrancy in IMDuelHook._pay(PoolId,IMDuelHook.Game,Currency,address,uint256) (src/IMDuelHook.sol#381-389):\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:428: Reentrancy in IMDuelHook.fundChest(PoolKey,bool,uint256) (src/IMDuelHook.sol#428-436):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:305: Reentrancy in IMDuelHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDuelHook.sol#305-330):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:381: Reentrancy in IMDuelHook._pay(PoolId,IMDuelHook.Game,Currency,address,uint256) (src/IMDuelHook.sol#381-389):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:614: Reentrancy in IMDuelHook._play(PoolId,PoolKey,IMDuelHook.Game,bool,BalanceDelta,address,uint160,uint256) (src/IMDuelHook.sol#614-653):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:418: Reentrancy in IMDuelHook._withdraw(address,Currency,address) (src/IMDuelHook.sol#418-424):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:428: Reentrancy in IMDuelHook.fundChest(PoolKey,bool,uint256) (src/IMDuelHook.sol#428-436):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:496: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#496-519):\n[low/medium] return-bomb at src/IMDuelHook.sol:763: IMDuelHook._engine(uint256,bytes) (src/IMDuelHook.sol#763-769) tries to limit the gas of an external call that controls implicit decoding\n[low/medium] timestamp at src/IMDuelHook.sol:335: IMDuelHook._takeFee(PoolId,PoolKey,uint256,uint256,uint256,IMDuelHook.Game) (src/IMDuelHook.sol#335-347) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:614: IMDuelHook._play(PoolId,PoolKey,IMDuelHook.Game,bool,BalanceDelta,address,uint160,uint256) (src/IMDuelHook.sol#614-653) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:496: IMDuelHook._convert(Currency) (src/IMDuelHook.sol#496-519) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:351: IMDuelHook._chestBps(IMDuelHook.Game) (src/IMDuelHook.sol#351-354) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:365: IMDuelHook._addChest(PoolId,uint256,uint256) (src/IMDuelHook.sol#365-372) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:428: IMDuelHook.fundChest(PoolKey,bool,uint256) (src/IMDuelHook.sol#428-436) uses timestamp for comparisons","passed":true},{"durationMs":1052,"exitCode":0,"name":"aderyn","output":"[high] contract-locks-ether at src/IMDuelHook.sol:44: Contract locks Ether without a withdraw function\n[high] incorrect-caret-operator at src/IMDuelHook.sol:257: Incorrect use of caret operator\n[high] incorrect-shift-order at src/proof/RLPReader.sol:164: Incorrect Assembly Shift Parameter Order (3 places)\n[high] reentrancy-state-change at src/IMDuelHook.sol:450: Reentrancy: State change after external call\n[high] unsafe-casting at src/Bitboard.sol:247: Unsafe Casting of integers (2 places)\n[low] empty-require-revert at src/ChessEngineV5.sol:157: Empty `require()` / `revert()` Statement\n[low] internal-function-used-once at src/Bitboard.sol:199: Internal Function Used Only Once (13 places)\n[low] large-numeric-literal at src/ChessEngineV5.sol:34: Large Numeric Literal (27 places)\n[low] literal-instead-of-constant at src/Bitboard.sol:35: Literal Instead of Constant (424 places)\n[low] missing-inheritance at src/IMDuelToken.sol:7: Missing Inheritance\n[low] push-zero-opcode at src/Bitboard.sol:2: PUSH0 Opcode (13 places)\n[low] require-revert-in-loop at src/proof/MerkleTrie.sol:73: Loop Contains `require`/`revert` (3 places)\n[low] state-change-without-event at src/IMDuelHook.sol:288: State Change Without Event\n[low] unchecked-return at src/IMDuelHook.sol:422: Unchecked Return (3 places)\n[low] uninitialized-local-variable at src/ChessEngineV5.sol:164: Uninitialized Local Variable (4 places)\n[low] unsafe-erc20-operation at src/IMDuelHook.sol:477: Unsafe ERC20 Operation\n[low] unspecific-solidity-pragma at src/ChessEngineV5.sol:2: Unspecific Solidity Pragma (6 places)\n[low] unused-state-variable at src/IMDuelHook.sol:76: Unused State Variable (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"eb9b141484d0276f6cd9785f470f181d3e29f5a9fc701c60a2b2d67c6c924b54","verifiedTreeHash":"a22952637e2b6fa4e07279bad77950189080ae74","verifierVersion":"0.1.0+d372f99e"},{"checks":[{"durationMs":92279,"exitCode":0,"name":"build","output":"Compiling 122 files with Solc 0.8.26\nSolc 0.8.26 finished in 92.16s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:22:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                      ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:38:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                                      ^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:109:57:\n    |\n109 |             uint64 d = f >= r ? DIAG >> (8 * (f - r)) : DIAG << (8 * (r - f));\n    |                                                         ^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:110:65:\n    |\n110 |             uint64 a = f + r <= 7 ? ANTI >> (8 * (7 - f - r)) : ANTI << (8 * (f + r - 7));\n    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:38:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:69:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                                                     ^^^^^^^^^^^^^^^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:71:18:\n   |\n71 |         returns (uint16, Rules.Played memory)\n   |                  ^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:71:26:\n   |\n71 |         returns (uint16, Rules.Played memory)\n   |                          ^^^^^^^^^^^^^^^^^^^\n\nWarning (2072): Unused local variable.\n  --> test/Game.t.sol:90:9:\n   |\n90 |         IMDuelHook.Game memory g = st();\n   |         ^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n  --> test/LaunchFiles.t.sol:70:5:\n   |\n70 |     function test_engineLaunchFileDeploysTheEngineWithoutArguments() public {\n   |     ^ (Relevant source part starts here and spans across multiple lines).\n\n","passed":true},{"durationMs":3783,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/Token.t.sol:TokenTest\n[PASS] test_allowances() (gas: 347653)\n[PASS] test_fixedSupplyToTheDeployer() (gas: 39384)\n[PASS] test_nothingPrivileged() (gas: 192691)\n[PASS] test_transfers() (gas: 188240)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 987.64µs (390.91µs CPU time)\n\nRan 1 test for test/Size.t.sol:SizeTest\n[PASS] test_deployedCodeFitsTheBudgets() (gas: 1253863)\nLogs:\n  hook: 20963\n  engine v5: 23339\n  route price verifier: 5551\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.16ms (1.01ms CPU time)\n\nRan 3 tests for test/LaunchFiles.t.sol:LaunchFilesTest\n[PASS] test_engineLaunchFileDeploysTheEngineWithoutArguments() (gas: 7834)\n[PASS] test_ethereumLaunchFile() (gas: 1333515)\n[PASS] test_sepoliaLaunchFile() (gas: 1328569)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 4.05ms (3.14ms CPU time)\n\nRan 7 tests for test/Draws.t.sol:DrawsTest\n[PASS] test_checkmate() (gas: 240028)\n[PASS] test_moveCap() (gas: 240069)\n[PASS] test_ordinaryMoveDoesNotEndTheGame() (gas: 180017)\n[PASS] test_quietMoveLimit() (gas: 246287)\n[PASS] test_stalemate() (gas: 237841)\n[PASS] test_thirdRepetition() (gas: 964226)\n[PASS] test_tooFewPiecesToMate() (gas: 231666)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 4.68ms (2.89ms CPU time)\n\nRan 5 tests for test/Teams.t.sol:TeamsTest\n[PASS] test_aDynamicFeePoolPlaysWithAZeroLpFee() (gas: 2914515)\n[PASS] test_nativeEthIsATeamLikeAnyToken() (gas: 2328114)\n[PASS] test_poolsOnOneHookKeepSeparateGamesAndChests() (gas: 3391028)\n[PASS] test_sliceSide() (gas: 1820091)\n[PASS] test_twoPlainTokensCannotDuel() (gas: 1592088)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 7.46ms (6.47ms CPU time)\n\nRan 6 tests for test/RouteSample.t.sol:RouteSampleTest\n[PASS] test_authenticatedBlockStartsRejectTheSameAttack() (gas: 3422402)\n[PASS] test_callerSamplesCannotSellTheEthPileAtAPushedPrice() (gas: 1710709)\nLogs:\n  route attacker profit at the honest price: -87002115995172580\n\n[PASS] test_duplicateSampleCannotRotateOutHonestHistory() (gas: 493037)\n[PASS] test_gapRequiresThreeConsecutiveSamplesAgain() (gas: 1855898)\n[PASS] test_honestBatchRunsAndStopsAtTheHistoricalPriceBand() (gas: 1175548)\n[PASS] test_staleRoutePricesWaitWithoutLosingClaims() (gas: 1298197)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 30.90ms (24.72ms CPU time)\n\nRan 17 tests for test/CrossBlock.t.sol:CrossBlockTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5992198)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3971909)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3062844)\n[PASS] test_anEthBatchIsCapped() (gas: 5252475)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3201835)\n[PASS] test_anyoneCanConvert() (gas: 462029)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656485)\n[PASS] test_crossBlockViaConvert() (gas: 5556326)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390298682200\n\n[PASS] test_crossBlockViaFirstTrade() (gas: 5718430)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390291136661\n\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4252876)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245468)\n[PASS] test_ethRouteSameBlockPush() (gas: 5230164)\nLogs:\n  ETH pile before (1e18): 500000000000000000\n  ETH the batch sold (1e18): 0\n  IMD it got (1e18): 0\n  IMD it was worth at the home price (1e18): 0\n  attacker profit (ETH at the home price, 1e18): -29770639430607030\n\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764062)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487316)\n[PASS] test_noRouteNoDuel() (gas: 1030839)\n[PASS] test_oneEthBatchPerBlock() (gas: 5777542)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187178)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 46.44ms (57.64ms CPU time)\n\nRan 7 tests for test/PayoutGuard.t.sol:PayoutGuardTest\n[PASS] testFuzz_settlingAnyAmountForTheHookIsRefused(uint256) (runs: 24, μ: 1737816, ~: 1737931)\nLogs:\n  Bound result 887270\n\n[PASS] test_aPlainRecipientIsPaidInTheTrade() (gas: 1633097)\n[PASS] test_aRecipientSettlingForTheRouterIsRefused() (gas: 1736500)\n[PASS] test_aRecipientThatOnlyReadsDeltasIsPaid() (gas: 1707184)\n[PASS] test_aRecipientThatSyncsIsSettledAroundWhenTheTradePaysInTheToken() (gas: 1255175)\n[PASS] test_aRefusedRewardLeavesOnlyThroughItsOwnersWithdrawTo() (gas: 1305317)\n[PASS] test_anOwedRewardIsDeliveredByAnyonesWithdrawOnceTheOwnerAccepts() (gas: 1170965)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 62.58ms (70.22ms CPU time)\n\nRan 3 tests for test/Launch.t.sol:LaunchTest\n[PASS] test_launchPairedWithEth() (gas: 23114011)\n[PASS] test_launchTokenAboveImd() (gas: 24063809)\nLogs:\n  first buy (opens game 0, bids for White): 366422\n\n[PASS] test_launchTokenBelowImd() (gas: 23989193)\nLogs:\n  first buy (opens game 0, bids for White): 361672\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 71.54ms (169.11ms CPU time)\n\nRan 2 tests for test/FactoryLaunch.t.sol:FactoryLaunchTest\n[PASS] test_factoryLaunchWithRealTokenAboveImd() (gas: 36869239)\n[PASS] test_factoryLaunchWithRealTokenBelowImd() (gas: 36831558)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 74.86ms (73.25ms CPU time)\n\nRan 2 tests for test/LaunchAdmission.t.sol:LaunchAdmissionTest\n[PASS] test_blankChainTokenAboveImd() (gas: 22753347)\n[PASS] test_blankChainTokenBelowImd() (gas: 22682302)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 91.73ms (90.54ms CPU time)\n\nRan 5 tests for test/HookGas.t.sol:HookGasTest\n[PASS] test_conversionGas() (gas: 3643661)\nLogs:\n  plain trade: 207740\n  trade that converts a batch: 271602\n\n[PASS] test_hookShareOfAMovingTrade() (gas: 29142695)\nLogs:\n  hook share of a moving trade: average: 279291\n  hook share of a moving trade: max: 327966\n\n[PASS] test_middlegameGas() (gas: 9519488)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 191975\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 557243\n\n[PASS] test_openingGas() (gas: 4540681)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 192689\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 508014\n\n[PASS] test_runningGameGas() (gas: 54703750)\nLogs:\n  running game, moving trades measured: 50\n  running game, moving trade average: 452500\n  running game, moving trade max: 548698\n\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 129.84ms (135.50ms CPU time)\n\nRan 12 tests for test/Bidding.t.sol:BiddingTest\n[PASS] test_aBountyIsPaidFromTheMoversChest() (gas: 1363818)\n[PASS] test_aBuyOfTheOtherTeamBidsForItsNextMove() (gas: 1952666)\n[PASS] test_aBuyOfTheOtherTeamTimesTheSilentSideOutAndBids() (gas: 1222504)\n[PASS] test_aClosedWindowTakesNoBidsForItsMove() (gas: 1062884)\n[PASS] test_aMoveIsOpenForTurnBlocksBlocks() (gas: 1040466)\n[PASS] test_aNewTopMustPayMoreThanTenPercentMore() (gas: 974355)\n[PASS] test_aNextMoveBidCarriesIntoTheNextGame() (gas: 1467024)\n[PASS] test_aWindowWithoutBidsBecomesTheFirstBidsBlock() (gas: 1283690)\n[PASS] test_bidsCountAcrossTheWholeWindow() (gas: 1511214)\n[PASS] test_theMoveDependsOnlyOnTheWindowsLastBlock() (gas: 1621426)\n[PASS] test_theMoversOwnBuyAfterItsWindowBidsForItsNextMove() (gas: 1077593)\n[PASS] test_thePreviewIsTheMoveThatIsPlayed() (gas: 1222868)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 130.18ms (24.22ms CPU time)\n\nRan 16 tests for test/FeeSchedule.t.sol:FeeScheduleTest\n[PASS] testFuzz_exactOutputDeliversTheOrderAndTakesHalfAPercent(uint256) (runs: 256, μ: 940881, ~: 941383)\nLogs:\n  Bound result 194406498560197242511\n\n[PASS] testFuzz_openingChestShareFollowsTheSchedule(uint256,uint256) (runs: 512, μ: 464532, ~: 464677)\nLogs:\n  Bound result 1850\n  Bound result 300000000000000000000\n\n[PASS] testFuzz_surchargeNeverRises(uint256,uint256) (runs: 256, μ: 1045111, ~: 1045173)\nLogs:\n  Bound result 253\n  Bound result 378\n\n[PASS] test_conversionSplitsTheImduelHalfKeptHalfBurned() (gas: 3731069)\n[PASS] test_convertWithAnEmptyPileDoesNothing() (gas: 123769)\n[PASS] test_exactOutputStoppedByItsPriceLimitIsRefused() (gas: 215730)\n[PASS] test_fundChestBeyondBalanceIsRefused() (gas: 121468)\n[PASS] test_fundChestOfZeroAddsNothing() (gas: 98361)\n[PASS] test_fundChestWithTheWrongEthValueIsRefused() (gas: 344909)\n[PASS] test_fundChestWithoutAllowanceIsRefused() (gas: 111138)\n[PASS] test_lastSurchargeSecond() (gas: 470062)\n[PASS] test_mainDuelConvertsAtMostOnceAndTenMinimumsPerBlock() (gas: 4167534)\n[PASS] test_openingEdges() (gas: 469684)\n[PASS] test_owedRewardOnlyEverReachesItsOwner() (gas: 1180430)\n[PASS] test_surchargeEndsAtThreeHundredSeconds() (gas: 469575)\n[PASS] test_withdrawWithNothingOwedDoesNothing() (gas: 90029)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 137.43ms (317.85ms CPU time)\n\nRan 3 tests for test/GasEstimate.t.sol:GasEstimateTest\n[PASS] test_atTheEstimateADueBatchConverts() (gas: 4996933)\nLogs:\n  smallest gas limit that converts: 310375\n\n[PASS] test_atTheEstimateTheBountyIsPaidNotOwed() (gas: 4601044)\nLogs:\n  smallest gas limit that plays and pays: 411632\n\n[PASS] test_noGasLimitPlaysTheMoveAndLeavesItsBountyOwed() (gas: 18529107)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 171.68ms (177.30ms CPU time)\n\nRan 13 tests for test/RoutePriceVerifier.t.sol:RoutePriceVerifierTest\n[PASS] testFuzz_authenticatesSlot0AndItsBlock(uint160,uint96) (runs: 256, μ: 200948, ~: 200266)\nLogs:\n  Bound result 47419737482723328\n\n[PASS] test_changedAccountProofIsRefused() (gas: 100298)\n[PASS] test_changedHeaderIsNotCanonical() (gas: 80800)\n[PASS] test_changedStorageProofIsRefused() (gas: 158269)\n[PASS] test_clientGeneratedPoolManagerProof() (gas: 241704)\n[PASS] test_emptyProofsAreRefused() (gas: 195098)\n[PASS] test_extraProofNodesAreRefused() (gas: 195848)\n[PASS] test_outOfRangePriceIsRefused() (gas: 192143)\n[PASS] test_theBatchBlockCannotSupplyASample() (gas: 80854)\n[PASS] test_uninitializedPoolPriceIsRefused() (gas: 180136)\n[PASS] test_unknownOrExpiredHeaderIsRefused() (gas: 81188)\n[PASS] test_wrongManagerIsRefused() (gas: 108609)\n[PASS] test_wrongPoolSlotIsRefused() (gas: 165128)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 279.73ms (283.09ms CPU time)\n\nRan 14 tests for test/Slice.t.sol:SliceTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5991703)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3971891)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3062819)\n[PASS] test_anEthBatchIsCapped() (gas: 5252359)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3201761)\n[PASS] test_anyoneCanConvert() (gas: 462007)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656457)\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4252839)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245420)\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764048)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487334)\n[PASS] test_noRouteNoDuel() (gas: 1030789)\n[PASS] test_oneEthBatchPerBlock() (gas: 5777448)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187164)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 279.86ms (56.71ms CPU time)\n\nRan 15 tests for test/Hostile.t.sol:HostileTest\n[PASS] test_aCallbackTokenOwnerLeavingADebtCannotFreezeTheDuel() (gas: 5281617)\n[PASS] test_aCallbackTokenOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 5617407)\n[PASS] test_aCallbackTokenOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 5637162)\n[PASS] test_aDeadEngineLeavesTheTurnQueuedAndTradingGoesOn() (gas: 3556171)\n[PASS] test_aGasBurningEngineCostsOneExpensiveTradeNotTheGame() (gas: 9728512)\n[PASS] test_aTokenReturningNothingFundsAChest() (gas: 2186643)\n[PASS] test_anEthOwnerLeavingADebtCannotFreezeTheDuel() (gas: 4200022)\n[PASS] test_anEthOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 4487093)\n[PASS] test_anEthOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 4546952)\n[PASS] test_anOwnerThatCannotReceiveWithdrawsElsewhere() (gas: 3558858)\n[PASS] test_blockedRecipientStaysOwedAndTradingContinues() (gas: 4190135)\n[PASS] test_brokenEngineFallsBackToALegalMove() (gas: 5151439)\n[PASS] test_feeOnTransferTokenFundsOnlyWhatArrives() (gas: 2732535)\n[PASS] test_reenteringTokenCannotBreakAPayout() (gas: 4729548)\n[PASS] test_starvingTheEngineNeverPlaysTheFallback() (gas: 25149892)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 280.03ms (118.27ms CPU time)\n\nRan 8 tests for test/BidOwner.t.sol:BidOwnerTest\n[PASS] testFuzz_otherDataLengthsNeverCreditTheBundlerOrTheRouter(bytes) (runs: 256, μ: 383290, ~: 383300)\n[PASS] test_aHookWithoutAnEthRouteRejectsRouteProofs() (gas: 32534)\n[PASS] test_abiEncodedOwnerCreditsTheWallet() (gas: 384567)\n[PASS] test_abiEncodedOwnerReceivesTheCaptureBounty() (gas: 1122975)\n[PASS] test_dirtyAbiPaddingIsNobody() (gas: 382698)\n[PASS] test_missingOwnerIsNobodyNotTheRouterNorTheBundler() (gas: 415565)\n[PASS] test_missingOwnerPlaysButPaysNobody() (gas: 1151657)\n[PASS] test_packedOwnerCreditsTheWallet() (gas: 384686)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 280.26ms (280.50ms CPU time)\n\nRan 8 tests for test/Strength.t.sol:StrengthTest\n[PASS] testFuzz_boundedAndMonotonic(uint256,uint256,uint256) (runs: 256, μ: 9045, ~: 8663)\nLogs:\n  Bound result 84743910324299725283116389454424367006300368779282074797\n\n[PASS] testFuzz_depthMonotonic(uint256,uint256,uint256) (runs: 256, μ: 12024, ~: 12425)\nLogs:\n  Bound result 4608\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129635519\n\n[PASS] testFuzz_shiftKeepsTheShare(uint256,uint256,uint8) (runs: 256, μ: 14663, ~: 15266)\nLogs:\n  Bound result 10\n  Bound result 80\n  Bound result 4608\n\n[PASS] testFuzz_tenfoldIsAboutThirtyPoints(uint128,uint128) (runs: 256, μ: 13836, ~: 13724)\nLogs:\n  Bound result 600000000000000000000\n  Bound result 594000000000065536\n\n[PASS] testFuzz_unscalablePaidFollowsTheCurve(uint256,uint256) (runs: 256, μ: 13980, ~: 14208)\nLogs:\n  Bound result 115790931316423822261616749298837820974191451965793907643040561468927826316639\n  Bound result 115792089237316195423570985008687907853269984665639641702553898530332329700000\n\n[PASS] test_decidedPoints() (gas: 15577)\n[PASS] test_hugePaymentsDoNotRevert() (gas: 12854)\n[PASS] test_zeroInputs() (gas: 4493)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 280.20ms (298.96ms CPU time)\n\nRan 1 test for test/Predict.t.sol:PredictTest\n[PASS] test_aBotCannotPickItsOwnCapturingMoves() (gas: 289899607)\nLogs:\n  bot moves: 0\n  other moves: 74\n  captures among other moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 573.18ms (572.94ms CPU time)\n\nRan 23 tests for test/Game.t.sol:GameTest\n[PASS] testFuzz_swapsNeverRevert(uint256) (runs: 48, μ: 9632622, ~: 9664749)\n[PASS] test_aGamePlaysOutThroughSwaps() (gas: 134592260)\nLogs:\n  moves played: 187\n  bids (incl. outbid ones): 238\n  most gas for a swap: 685293\n\n[PASS] test_aSmallerTradeInTheBlockDoesNotTakeTheTurn() (gas: 686491)\n[PASS] test_aTimeoutTurnCanBeTakenInItsBlock() (gas: 1304210)\n[PASS] test_aTradeBidsForTheTurn() (gas: 483494)\n[PASS] test_aTurnClosesWithItsBlock() (gas: 965170)\n[PASS] test_anExpiredTurnBecomesATimeoutTurn() (gas: 1410536)\n[PASS] test_anOldTurnIsPlayedWithTheHistoryContractsHash() (gas: 1754873)\n[PASS] test_anyTradeSizeBids() (gas: 450095)\n[PASS] test_lateTradeStillBidsNormally() (gas: 429729)\n[PASS] test_onlyThePoolManagerAndTheHookCanCallIn() (gas: 171212)\n[PASS] test_ownerDefaultsToNobody() (gas: 449725)\n[PASS] test_playedPositionsEnterTheRepetitionMemory() (gas: 915368)\n[PASS] test_rejectsBadConfig() (gas: 9247)\n[PASS] test_silentSidePlaysAtZeroStrength() (gas: 1045239)\n[PASS] test_strengthFollowsTheShareOfThePool() (gas: 6374541)\n[PASS] test_teamsSwapColoursEveryGame() (gas: 926880)\n[PASS] test_theBiggestTradeInTheBlockOwnsTheTurn() (gas: 782415)\n[PASS] test_theFirstPoolOpenedIsTheMainDuel() (gas: 179471)\n[PASS] test_theMainDuelMustPairImd() (gas: 1466664)\n[PASS] test_theNextBlocksTradePlaysTheTurn() (gas: 1038495)\n[PASS] test_theOtherTeamCannotBidOnThisTurn() (gas: 412278)\n[PASS] test_whoPlaysTheTurnAndWhenDoesNotChangeTheMove() (gas: 1534194)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 796.28ms (1.04s CPU time)\n\nRan 21 tests for test/Money.t.sol:MoneyTest\n[PASS] testFuzz_heldEqualsOwed(uint256) (runs: 48, μ: 10635225, ~: 10699905)\n[PASS] test_aFullChestTakesNoMore() (gas: 1059339)\n[PASS] test_aTradeStoppedByItsPriceLimitIsRefused() (gas: 582903)\n[PASS] test_blackCaptureIsPaidInItsTeamToken() (gas: 780846)\n[PASS] test_bountyTable() (gas: 44705)\n[PASS] test_checkmatePaysAQuarterOfTheChest() (gas: 760690)\n[PASS] test_chestShareGoesToTheChestWorthLess() (gas: 638456)\n[PASS] test_chestsStayAboutEqualInValue() (gas: 5511474)\n[PASS] test_exactOutputPaysTheFeeOnTheInput() (gas: 384979)\n[PASS] test_exactOutputWithTheChestShareInTheOutput() (gas: 780877)\n[PASS] test_failedPayoutStaysOwedAndTheTradeGoesThrough() (gas: 1217146)\n[PASS] test_feesCannotBeWithdrawnByAnyone() (gas: 449988)\n[PASS] test_firstTradeFundsChest0AndTheSlice() (gas: 363816)\n[PASS] test_fundChestNeedsTheRightValue() (gas: 48803)\n[PASS] test_fundChestOnlyForHookPools() (gas: 41868)\n[PASS] test_fundChestWithEth() (gas: 252918)\n[PASS] test_fundChestWithTokens() (gas: 193816)\n[PASS] test_openingSurchargeOnlyInTheMainDuel() (gas: 3469491)\n[PASS] test_payoutIsCappedAtTwiceTheTrade() (gas: 828202)\n[PASS] test_quietMovePaysNothing() (gas: 616203)\n[PASS] test_whiteCaptureIsPaidInItsTeamTokenFromItsOwnChest() (gas: 854514)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 865.35ms (885.17ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_boardHasBothKingsAndPlyIsBounded\n[PASS] invariant_heldEqualsOwed\n InvariantTest invariants (runs: 24, calls: 2400, reverts: 0)\n\n╭----------+----------------+-------+---------+----------╮\n| Contract | Selector       | Calls | Reverts | Discards |\n+========================================================+\n| Handler  | convert        | 301   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | fund           | 305   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | longPause      | 311   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | nextBlock      | 313   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | trade          | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeAgain     | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeNextBlock | 312   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | withdrawReward | 304   | 0       | 0        |\n╰----------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 53\n  Bound result 19999999000000000357\n  Bound result 500000000000000000\n  Bound result 19999999000000020458\n  Bound result 19999999000000017036\n  Bound result 84\n  Bound result 18046385208871800035\n  Bound result 2809\n  Bound result 19999999007306351798\n  Bound result 4000000000000000000\n  Bound result 31865262234567526\n  Bound result 19999999000000000014\n  Bound result 463181382011195627\n  Bound result 19999999000000003128\n  Bound result 128\n  Bound result 64\n  Bound result 19999999386547056641\n  Bound result 7616448877154347021\n  Bound result 19999999000000000065\n  Bound result 40\n  Bound result 4022291159582972627\n  Bound result 6\n  Bound result 754\n  Bound result 19999999000000049375\n  Bound result 120\n  Bound result 90\n  Bound result 861\n  Bound result 19999999000000000081\n  Bound result 1221339734816628308\n  Bound result 14\n  Bound result 117\n  Bound result 30\n  Bound result 69\n  Bound result 55\n  Bound result 80\n  Bound result 7134569247168856\n  Bound result 19999999000000007667\n  Bound result 55\n  Bound result 1000000000003\n  Bound result 19999999000000000049\n  Bound result 1251\n  Bound result 47\n  Bound result 19999999000000000021\n  Bound result 19999999000000000184\n  Bound result 19999999000000001670\n  Bound result 19304386864691798331\n  Bound result 20396340581799926600\n  Bound result 13210\n  Bound result 12054830257181156736\n  Bound result 20000000000000000000\n  Bound result 12566133904435631808\n  Bound result 15386197539150620393\n  Bound result 2\n  Bound result 81\n  Bound result 9943809481308575891\n  Bound result 19999999000000004122\n  Bound result 19999999386547056641\n  Bound result 10000000000000000\n  Bound result 5060859166916801136\n  Bound result 400\n  Bound result 12\n  swaps: 33\n  moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.11s (1.11s CPU time)\n\nRan 3 tests for test/TokenInvariant.t.sol:TokenInvariantTest\n[PASS]\nTokenInvariantTest invariants:\n[PASS] invariant_allowancesAreExactlyWhatWasApprovedLessWhatWasSpent\n[PASS] invariant_supplyIsFixedAndHeldByTheActors\n TokenInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+--------------------+-------+---------+----------╮\n| Contract     | Selector           | Calls | Reverts | Discards |\n+================================================================+\n| TokenHandler | approve            | 2093  | 0       | 0        |\n|--------------+--------------------+-------+---------+----------|\n| TokenHandler | approveZeroSpender | 1976  | 0       | 0        |\n|--------------+--------------------+-------+---------+----------|\n| TokenHandler | transfer           | 2073  | 0       | 0        |\n|--------------+--------------------+-------+---------+----------|\n| TokenHandler | transferFrom       | 2050  | 0       | 0        |\n╰--------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3\n  Bound result 9\n  Bound result 16613\n  Bound result 6\n  Bound result 6482\n  Bound result 3\n  Bound result 1265\n  Bound result 3\n  Bound result 605\n  Bound result 5149\n  Bound result 800\n  Bound result 57694\n  Bound result 1466\n  Bound result 2004\n  Bound result 1091\n  Bound result 65280\n  Bound result 2\n  Bound result 6\n  Bound result 343926200696183277375289309\n  Bound result 5669\n  Bound result 253\n  Bound result 4000\n  Bound result 10\n  Bound result 400\n  Bound result 1198\n  Bound result 2293517445\n  Bound result 223\n  Bound result 812118896355640807882601351\n  Bound result 777062408\n  Bound result 1000000000000000000000\n  Bound result 174\n  Bound result 7462\n  Bound result 3600\n  Bound result 3569\n  Bound result 121656067608018816226\n  Bound result 4288\n\n[PASS] testFuzz_transferOfMoreThanTheBalanceIsRefused(uint256) (runs: 512, μ: 55904, ~: 55857)\nLogs:\n  Bound result 8195\n\n[PASS] test_transferFromTheZeroAddressIsRefused() (gas: 82118)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.64s (2.88s CPU time)\n\nRan 1 test for test/InvariantDuels.t.sol:InvariantDuelsTest\n[PASS]\nInvariantDuelsTest invariants:\n[PASS] invariant_everyBoardIsSound\n[PASS] invariant_everyCurrencyBalances\n[PASS] invariant_rightsMatchTheBoard\n[PASS] invariant_turnsAreWellFormed\n InvariantDuelsTest invariants (runs: 24, calls: 2880, reverts: 0)\n\n╭--------------+-------------------+-------+---------+----------╮\n| Contract     | Selector          | Calls | Reverts | Discards |\n+===============================================================+\n| DuelsHandler | addLiquidity      | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convert           | 220   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convertNextBlock  | 200   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | fund              | 241   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | longPause         | 221   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | nextBlock         | 215   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | pushThenNextBlock | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | removeLiquidity   | 253   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | roundTrip         | 251   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | trade             | 232   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeAgain        | 198   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeNextBlock    | 223   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | withdrawReward    | 210   | 0       | 0        |\n╰--------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 23181\n  Bound result 2565653098084874863\n  Bound result 12\n  Bound result 8121606201292619776\n  Bound result 72624976668147840\n  Bound result 4076725131\n  Bound result 12\n  Bound result 19999999999000000049\n  Bound result 4485934425771658475\n  Bound result 120\n  Bound result 12\n  Bound result 562949953421312\n  Bound result 12\n  Bound result 19999999999000011559\n  Bound result 20000000000000000000\n  Bound result 5000\n  Bound result 19999999999000017413\n  Bound result 29999999970\n  Bound result 12\n  Bound result 19999999999000000064\n  Bound result 19999999999000000243\n  Bound result 386547056640\n  Bound result 133294633079153423663\n  Bound result 12\n  Bound result 2296800487608934368\n  Bound result 12\n  Bound result 19999999999000010429\n  Bound result 12\n  Bound result 19999999999000014942\n  Bound result 12\n  Bound result 19999999999000006727\n  Bound result 180000000000000016675\n  Bound result 12\n  Bound result 4999999999000017033\n  Bound result 5505625188992501493\n  Bound result 12\n  Bound result 180000000000000002956\n  Bound result 12\n  Bound result 180000000000000009300\n  Bound result 12\n  Bound result 19858352496989593\n  Bound result 52\n  Bound result 19999999999000014176\n  Bound result 12\n  Bound result 19999999999000018304\n  Bound result 74264337593103793878\n  Bound result 12\n  Bound result 6017502607293588406\n  Bound result 440531926212502586\n  Bound result 9999999000000000000126\n  Bound result 17715909253537394009\n  Bound result 12\n  Bound result 19999999999000014990\n  Bound result 3000000000000000000\n  Bound result 805801794754002874\n  Bound result 4999999999000002501\n  Bound result 180000000000000006503\n  Bound result 12\n  Bound result 180000000000000043129\n  Bound result 12\n  Bound result 12\n  Bound result 4999999999000005065\n  Bound result 12\n  Bound result 12\n  Bound result 10000000000000000\n  Bound result 19999999999000001315\n  Bound result 12\n  Bound result 60\n  Bound result 25332747903959040\n  Bound result 12\n  Bound result 14930066797812130238\n  Bound result 19999999999000004003\n  Bound result 19999999999000000801\n  Bound result 12\n  Bound result 17830126982352984981\n  Bound result 35\n  Bound result 39999987299997460\n  Bound result 13302602898618240261\n  Bound result 80\n  Bound result 27612829230727045392\n  Bound result 12\n  Bound result 120\n  Bound result 4999999999107477464\n  Bound result 1000178988994931336848\n  Bound result 191000000000000000001\n  Bound result 12\n  Bound result 21931300337370667194\n  Bound result 99999999994444444445\n  Bound result 12\n  Bound result 4503599627370496\n  Bound result 180000000000000024030\n  Bound result 12\n  Bound result 180050000000000000001\n  Bound result 12\n  Bound result 46649393329207593530\n  Bound result 12\n  Bound result 10000000000000000000\n  Bound result 32680281041836782903\n  Bound result 1000628520921143846870\n  Bound result 1000000000\n  Bound result 1812930352692670847190\n  Bound result 19999999999000000245\n  Bound result 4999999999001966081\n  Bound result 19999999999000094628\n  swaps: 43\n  moves: 23\n  Bound result 12\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.60s (3.59s CPU time)\n\nRan 26 test suites in 3.60s (11.95s CPU time): 201 tests passed, 0 failed, 0 skipped (201 total tests)\n","passed":true},{"durationMs":125,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDuelHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMDuelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDuelHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDuelHook.convert(address)\",\"IMDuelHook.convertBatch(address)\",\"IMDuelHook.fundChest((address,address,uint24,int24,address),bool,uint256)\",\"IMDuelHook.payOut((address,address,uint24,int24,address),address,address,uint256)\",\"IMDuelHook.sampleRoute(bytes,bytes[],bytes[])\",\"IMDuelHook.unlockCallback(bytes)\",\"IMDuelHook.withdraw(address,address)\",\"IMDuelHook.withdrawTo(address,address)\",\"IMDuelToken.approve(address,uint256)\",\"IMDuelToken.transfer(address,uint256)\",\"IMDuelToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":102,\"LICENSE\":16,\"foundry.toml\":29,\"launch.json\":39,\"launch/deployed.json\":12,\"launch/engine.json\":10,\"launch/hook.json\":39,\"launch/hook.sepolia.json\":39,\"script/LaunchPolicy.sol\":44,\"script/route_proof.py\":135,\"src/Bitboard.sol\":719,\"src/Chess.sol\":34,\"src/ChessEngineV5.sol\":852,\"src/IChessEngine.sol\":29,\"src/IMDuelHook.sol\":967,\"src/IMDuelToken.sol\":62,\"src/RoutePriceVerifier.sol\":50,\"src/Rules.sol\":45,\"src/Strength.sol\":47,\"src/proof/Bytes.sol\":144,\"src/proof/LICENSE\":22,\"src/proof/MerkleTrie.sol\":216,\"src/proof/PROVENANCE.md\":12,\"src/proof/RLPErrors.sol\":20,\"src/proof/RLPReader.sol\":249,\"test/Base.sol\":211,\"test/BidOwner.t.sol\":118,\"test/Bidding.t.sol\":224,\"test/Chessboard.sol\":79,\"test/CrossBlock.t.sol\":195,\"test/Draws.t.sol\":90,\"test/FactoryLaunch.t.sol\":173,\"test/FeeSchedule.t.sol\":327,\"test/Game.t.sol\":348,\"test/GasEstimate.t.sol\":120,\"test/Harness.sol\":83,\"test/HookGas.t.sol\":151,\"test/Hostile.t.sol\":461,\"test/Invariant.t.sol\":150,\"test/InvariantDuels.t.sol\":529,\"test/Launch.t.sol\":159,\"test/LaunchAdmission.t.sol\":167,\"test/LaunchFiles.t.sol\":75,\"test/Money.t.sol\":353,\"test/PayoutGuard.t.sol\":236,\"test/Predict.t.sol\":102,\"test/RoutePriceVerifier.t.sol\":135,\"test/RouteProof.sol\":69,\"test/RouteSample.t.sol\":168,\"test/Size.t.sol\":36,\"test/Slice.t.sol\":392,\"test/Strength.t.sol\":85,\"test/Teams.t.sol\":87,\"test/Token.t.sol\":82,\"test/TokenInvariant.t.sol\":155,\"test/Tokens.sol\":172,\"test/V4Fixture.sol\":100,\"test/data/chess.json\":1,\"test/data/conversion-validation.json\":770,\"test/data/conversion.json\":98,\"test/data/endings.json\":1,\"test/data/legal.json\":1,\"test/data/random.json\":1,\"test/data/route-proof.json\":16,\"test/test_route_proof.py\":72},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"ebb56f2943a82450e44eebe264bb52d40ec28e7b117282a3304f08a6b6aee63e","verifiedTreeHash":"5148b3a9b76c20545b02caad649f9c07d01e3c65","verifierVersion":"0.1.0+d372f99e"},{"checks":[{"durationMs":78915,"exitCode":0,"name":"build","output":"Compiling 119 files with Solc 0.8.26\nSolc 0.8.26 finished in 78.79s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:22:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                      ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:38:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                                      ^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:109:57:\n    |\n109 |             uint64 d = f >= r ? DIAG >> (8 * (f - r)) : DIAG << (8 * (r - f));\n    |                                                         ^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:110:65:\n    |\n110 |             uint64 a = f + r <= 7 ? ANTI >> (8 * (7 - f - r)) : ANTI << (8 * (f + r - 7));\n    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:38:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:69:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                                                     ^^^^^^^^^^^^^^^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:71:18:\n   |\n71 |         returns (uint16, Rules.Played memory)\n   |                  ^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:71:26:\n   |\n71 |         returns (uint16, Rules.Played memory)\n   |                          ^^^^^^^^^^^^^^^^^^^\n\nWarning (2072): Unused local variable.\n  --> test/Game.t.sol:90:9:\n   |\n90 |         IMDuelHook.Game memory g = st();\n   |         ^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n  --> test/LaunchFiles.t.sol:70:5:\n   |\n70 |     function test_engineLaunchFileDeploysTheEngineWithoutArguments() public {\n   |     ^ (Relevant source part starts here and spans across multiple lines).\n\n","passed":true},{"durationMs":3654,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/Token.t.sol:TokenTest\n[PASS] test_allowances() (gas: 347653)\n[PASS] test_fixedSupplyToTheDeployer() (gas: 39384)\n[PASS] test_nothingPrivileged() (gas: 192691)\n[PASS] test_transfers() (gas: 188240)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.39ms (731.84µs CPU time)\n\nRan 1 test for test/Size.t.sol:SizeTest\n[PASS] test_deployedCodeFitsTheBudgets() (gas: 1253863)\nLogs:\n  hook: 20963\n  engine v5: 23339\n  route price verifier: 5551\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.45ms (3.29ms CPU time)\n\nRan 3 tests for test/LaunchFiles.t.sol:LaunchFilesTest\n[PASS] test_engineLaunchFileDeploysTheEngineWithoutArguments() (gas: 7834)\n[PASS] test_ethereumLaunchFile() (gas: 1333515)\n[PASS] test_sepoliaLaunchFile() (gas: 1328569)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 5.66ms (2.94ms CPU time)\n\nRan 5 tests for test/Teams.t.sol:TeamsTest\n[PASS] test_aDynamicFeePoolPlaysWithAZeroLpFee() (gas: 2914515)\n[PASS] test_nativeEthIsATeamLikeAnyToken() (gas: 2328114)\n[PASS] test_poolsOnOneHookKeepSeparateGamesAndChests() (gas: 3391028)\n[PASS] test_sliceSide() (gas: 1820091)\n[PASS] test_twoPlainTokensCannotDuel() (gas: 1592088)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 16.50ms (14.25ms CPU time)\n\nRan 7 tests for test/Draws.t.sol:DrawsTest\n[PASS] test_checkmate() (gas: 240028)\n[PASS] test_moveCap() (gas: 240069)\n[PASS] test_ordinaryMoveDoesNotEndTheGame() (gas: 180017)\n[PASS] test_quietMoveLimit() (gas: 246287)\n[PASS] test_stalemate() (gas: 237841)\n[PASS] test_thirdRepetition() (gas: 964226)\n[PASS] test_tooFewPiecesToMate() (gas: 231666)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 27.67ms (7.06ms CPU time)\n\nRan 2 tests for test/LaunchAdmission.t.sol:LaunchAdmissionTest\n[PASS] test_blankChainTokenAboveImd() (gas: 22753347)\n[PASS] test_blankChainTokenBelowImd() (gas: 22682302)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 35.26ms (63.50ms CPU time)\n\nRan 17 tests for test/CrossBlock.t.sol:CrossBlockTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5992198)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3971909)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3062844)\n[PASS] test_anEthBatchIsCapped() (gas: 5252475)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3201835)\n[PASS] test_anyoneCanConvert() (gas: 462029)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656485)\n[PASS] test_crossBlockViaConvert() (gas: 5556326)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390298682200\n\n[PASS] test_crossBlockViaFirstTrade() (gas: 5718430)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390291136661\n\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4252876)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245468)\n[PASS] test_ethRouteSameBlockPush() (gas: 5230164)\nLogs:\n  ETH pile before (1e18): 500000000000000000\n  ETH the batch sold (1e18): 0\n  IMD it got (1e18): 0\n  IMD it was worth at the home price (1e18): 0\n  attacker profit (ETH at the home price, 1e18): -29770639430607030\n\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764062)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487316)\n[PASS] test_noRouteNoDuel() (gas: 1030839)\n[PASS] test_oneEthBatchPerBlock() (gas: 5777542)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187178)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 39.82ms (68.61ms CPU time)\n\nRan 14 tests for test/Slice.t.sol:SliceTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5991703)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3971891)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3062819)\n[PASS] test_anEthBatchIsCapped() (gas: 5252359)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3201761)\n[PASS] test_anyoneCanConvert() (gas: 462007)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656457)\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4252839)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245420)\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764048)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487334)\n[PASS] test_noRouteNoDuel() (gas: 1030789)\n[PASS] test_oneEthBatchPerBlock() (gas: 5777448)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187164)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 40.68ms (53.55ms CPU time)\n\nRan 2 tests for test/FactoryLaunch.t.sol:FactoryLaunchTest\n[PASS] test_factoryLaunchWithRealTokenAboveImd() (gas: 36869239)\n[PASS] test_factoryLaunchWithRealTokenBelowImd() (gas: 36831558)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 64.41ms (95.49ms CPU time)\n\nRan 12 tests for test/Bidding.t.sol:BiddingTest\n[PASS] test_aBountyIsPaidFromTheMoversChest() (gas: 1363818)\n[PASS] test_aBuyOfTheOtherTeamBidsForItsNextMove() (gas: 1952666)\n[PASS] test_aBuyOfTheOtherTeamTimesTheSilentSideOutAndBids() (gas: 1222504)\n[PASS] test_aClosedWindowTakesNoBidsForItsMove() (gas: 1062884)\n[PASS] test_aMoveIsOpenForTurnBlocksBlocks() (gas: 1040466)\n[PASS] test_aNewTopMustPayMoreThanTenPercentMore() (gas: 974355)\n[PASS] test_aNextMoveBidCarriesIntoTheNextGame() (gas: 1467024)\n[PASS] test_aWindowWithoutBidsBecomesTheFirstBidsBlock() (gas: 1283690)\n[PASS] test_bidsCountAcrossTheWholeWindow() (gas: 1511214)\n[PASS] test_theMoveDependsOnlyOnTheWindowsLastBlock() (gas: 1621426)\n[PASS] test_theMoversOwnBuyAfterItsWindowBidsForItsNextMove() (gas: 1077593)\n[PASS] test_thePreviewIsTheMoveThatIsPlayed() (gas: 1222868)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 64.62ms (22.49ms CPU time)\n\nRan 8 tests for test/BidOwner.t.sol:BidOwnerTest\n[PASS] testFuzz_otherDataLengthsNeverCreditTheBundlerOrTheRouter(bytes) (runs: 256, μ: 383292, ~: 383288)\n[PASS] test_aHookWithoutAnEthRouteRejectsRouteProofs() (gas: 32534)\n[PASS] test_abiEncodedOwnerCreditsTheWallet() (gas: 384567)\n[PASS] test_abiEncodedOwnerReceivesTheCaptureBounty() (gas: 1122975)\n[PASS] test_dirtyAbiPaddingIsNobody() (gas: 382698)\n[PASS] test_missingOwnerIsNobodyNotTheRouterNorTheBundler() (gas: 415565)\n[PASS] test_missingOwnerPlaysButPaysNobody() (gas: 1151657)\n[PASS] test_packedOwnerCreditsTheWallet() (gas: 384686)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 78.45ms (79.39ms CPU time)\n\nRan 3 tests for test/Launch.t.sol:LaunchTest\n[PASS] test_launchPairedWithEth() (gas: 23114011)\n[PASS] test_launchTokenAboveImd() (gas: 24063809)\nLogs:\n  first buy (opens game 0, bids for White): 366422\n\n[PASS] test_launchTokenBelowImd() (gas: 23989193)\nLogs:\n  first buy (opens game 0, bids for White): 361672\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 80.80ms (232.01ms CPU time)\n\nRan 15 tests for test/Hostile.t.sol:HostileTest\n[PASS] test_aCallbackTokenOwnerLeavingADebtCannotFreezeTheDuel() (gas: 5281617)\n[PASS] test_aCallbackTokenOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 5617407)\n[PASS] test_aCallbackTokenOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 5637162)\n[PASS] test_aDeadEngineLeavesTheTurnQueuedAndTradingGoesOn() (gas: 3556171)\n[PASS] test_aGasBurningEngineCostsOneExpensiveTradeNotTheGame() (gas: 9728512)\n[PASS] test_aTokenReturningNothingFundsAChest() (gas: 2186643)\n[PASS] test_anEthOwnerLeavingADebtCannotFreezeTheDuel() (gas: 4200022)\n[PASS] test_anEthOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 4487093)\n[PASS] test_anEthOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 4546952)\n[PASS] test_anOwnerThatCannotReceiveWithdrawsElsewhere() (gas: 3558858)\n[PASS] test_blockedRecipientStaysOwedAndTradingContinues() (gas: 4190135)\n[PASS] test_brokenEngineFallsBackToALegalMove() (gas: 5151439)\n[PASS] test_feeOnTransferTokenFundsOnlyWhatArrives() (gas: 2732535)\n[PASS] test_reenteringTokenCannotBreakAPayout() (gas: 4729548)\n[PASS] test_starvingTheEngineNeverPlaysTheFallback() (gas: 25149892)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 84.01ms (116.24ms CPU time)\n\nRan 13 tests for test/RoutePriceVerifier.t.sol:RoutePriceVerifierTest\n[PASS] testFuzz_authenticatesSlot0AndItsBlock(uint160,uint96) (runs: 256, μ: 200948, ~: 200425)\nLogs:\n  Bound result 47419737482723328\n\n[PASS] test_changedAccountProofIsRefused() (gas: 100311)\n[PASS] test_changedHeaderIsNotCanonical() (gas: 80813)\n[PASS] test_changedStorageProofIsRefused() (gas: 158336)\n[PASS] test_clientGeneratedPoolManagerProof() (gas: 241704)\n[PASS] test_emptyProofsAreRefused() (gas: 195111)\n[PASS] test_extraProofNodesAreRefused() (gas: 195861)\n[PASS] test_outOfRangePriceIsRefused() (gas: 192156)\n[PASS] test_theBatchBlockCannotSupplyASample() (gas: 80841)\n[PASS] test_uninitializedPoolPriceIsRefused() (gas: 180149)\n[PASS] test_unknownOrExpiredHeaderIsRefused() (gas: 81201)\n[PASS] test_wrongManagerIsRefused() (gas: 108622)\n[PASS] test_wrongPoolSlotIsRefused() (gas: 165141)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 83.83ms (68.55ms CPU time)\n\nRan 5 tests for test/HookGas.t.sol:HookGasTest\n[PASS] test_conversionGas() (gas: 3643661)\nLogs:\n  plain trade: 207740\n  trade that converts a batch: 271602\n\n[PASS] test_hookShareOfAMovingTrade() (gas: 29142695)\nLogs:\n  hook share of a moving trade: average: 279291\n  hook share of a moving trade: max: 327966\n\n[PASS] test_middlegameGas() (gas: 9519488)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 191975\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 557243\n\n[PASS] test_openingGas() (gas: 4540681)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 192689\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 508014\n\n[PASS] test_runningGameGas() (gas: 54703750)\nLogs:\n  running game, moving trades measured: 50\n  running game, moving trade average: 452500\n  running game, moving trade max: 548698\n\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 102.43ms (151.87ms CPU time)\n\nRan 8 tests for test/Strength.t.sol:StrengthTest\n[PASS] testFuzz_boundedAndMonotonic(uint256,uint256,uint256) (runs: 256, μ: 9098, ~: 8663)\nLogs:\n  Bound result 4608\n\n[PASS] testFuzz_depthMonotonic(uint256,uint256,uint256) (runs: 256, μ: 12020, ~: 12425)\nLogs:\n  Bound result 4608\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129635345\n\n[PASS] testFuzz_shiftKeepsTheShare(uint256,uint256,uint8) (runs: 256, μ: 14655, ~: 15266)\nLogs:\n  Bound result 10\n  Bound result 18\n  Bound result 4608\n\n[PASS] testFuzz_tenfoldIsAboutThirtyPoints(uint128,uint128) (runs: 256, μ: 13840, ~: 13724)\nLogs:\n  Bound result 50082718581281235812356958881899351066\n  Bound result 49615596080000199375330363197764209\n\n[PASS] testFuzz_unscalablePaidFollowsTheCurve(uint256,uint256) (runs: 256, μ: 13953, ~: 14208)\nLogs:\n  Bound result 115790931316423822261616749298837820974191451965793907633817189433447440043359\n  Bound result 115792089237316195423570985008687907853269984665640564039457446568959657720000\n\n[PASS] test_decidedPoints() (gas: 15577)\n[PASS] test_hugePaymentsDoNotRevert() (gas: 12854)\n[PASS] test_zeroInputs() (gas: 4493)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 103.40ms (43.49ms CPU time)\n\nRan 6 tests for test/RouteSample.t.sol:RouteSampleTest\n[PASS] test_authenticatedBlockStartsRejectTheSameAttack() (gas: 3422402)\n[PASS] test_callerSamplesCannotSellTheEthPileAtAPushedPrice() (gas: 1710709)\nLogs:\n  route attacker profit at the honest price: -87002115995172580\n\n[PASS] test_duplicateSampleCannotRotateOutHonestHistory() (gas: 493037)\n[PASS] test_gapRequiresThreeConsecutiveSamplesAgain() (gas: 1855898)\n[PASS] test_honestBatchRunsAndStopsAtTheHistoricalPriceBand() (gas: 1175548)\n[PASS] test_staleRoutePricesWaitWithoutLosingClaims() (gas: 1298197)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 103.43ms (22.59ms CPU time)\n\nRan 3 tests for test/GasEstimate.t.sol:GasEstimateTest\n[PASS] test_atTheEstimateADueBatchConverts() (gas: 4996933)\nLogs:\n  smallest gas limit that converts: 310375\n\n[PASS] test_atTheEstimateTheBountyIsPaidNotOwed() (gas: 4601044)\nLogs:\n  smallest gas limit that plays and pays: 411632\n\n[PASS] test_noGasLimitPlaysTheMoveAndLeavesItsBountyOwed() (gas: 18529107)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 171.71ms (206.87ms CPU time)\n\nRan 1 test for test/Predict.t.sol:PredictTest\n[PASS] test_aBotCannotPickItsOwnCapturingMoves() (gas: 289899607)\nLogs:\n  bot moves: 0\n  other moves: 74\n  captures among other moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 576.74ms (576.56ms CPU time)\n\nRan 23 tests for test/Game.t.sol:GameTest\n[PASS] testFuzz_swapsNeverRevert(uint256) (runs: 48, μ: 9606859, ~: 9583723)\n[PASS] test_aGamePlaysOutThroughSwaps() (gas: 134592260)\nLogs:\n  moves played: 187\n  bids (incl. outbid ones): 238\n  most gas for a swap: 685293\n\n[PASS] test_aSmallerTradeInTheBlockDoesNotTakeTheTurn() (gas: 686491)\n[PASS] test_aTimeoutTurnCanBeTakenInItsBlock() (gas: 1304210)\n[PASS] test_aTradeBidsForTheTurn() (gas: 483494)\n[PASS] test_aTurnClosesWithItsBlock() (gas: 965170)\n[PASS] test_anExpiredTurnBecomesATimeoutTurn() (gas: 1410536)\n[PASS] test_anOldTurnIsPlayedWithTheHistoryContractsHash() (gas: 1754873)\n[PASS] test_anyTradeSizeBids() (gas: 450095)\n[PASS] test_lateTradeStillBidsNormally() (gas: 429729)\n[PASS] test_onlyThePoolManagerAndTheHookCanCallIn() (gas: 171212)\n[PASS] test_ownerDefaultsToNobody() (gas: 449725)\n[PASS] test_playedPositionsEnterTheRepetitionMemory() (gas: 915368)\n[PASS] test_rejectsBadConfig() (gas: 9247)\n[PASS] test_silentSidePlaysAtZeroStrength() (gas: 1045239)\n[PASS] test_strengthFollowsTheShareOfThePool() (gas: 6374541)\n[PASS] test_teamsSwapColoursEveryGame() (gas: 926880)\n[PASS] test_theBiggestTradeInTheBlockOwnsTheTurn() (gas: 782415)\n[PASS] test_theFirstPoolOpenedIsTheMainDuel() (gas: 179471)\n[PASS] test_theMainDuelMustPairImd() (gas: 1466664)\n[PASS] test_theNextBlocksTradePlaysTheTurn() (gas: 1038495)\n[PASS] test_theOtherTeamCannotBidOnThisTurn() (gas: 412278)\n[PASS] test_whoPlaysTheTurnAndWhenDoesNotChangeTheMove() (gas: 1534194)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 769.98ms (987.50ms CPU time)\n\nRan 21 tests for test/Money.t.sol:MoneyTest\n[PASS] testFuzz_heldEqualsOwed(uint256) (runs: 48, μ: 10726217, ~: 10741382)\n[PASS] test_aFullChestTakesNoMore() (gas: 1059339)\n[PASS] test_aTradeStoppedByItsPriceLimitIsRefused() (gas: 582903)\n[PASS] test_blackCaptureIsPaidInItsTeamToken() (gas: 780846)\n[PASS] test_bountyTable() (gas: 44705)\n[PASS] test_checkmatePaysAQuarterOfTheChest() (gas: 760690)\n[PASS] test_chestShareGoesToTheChestWorthLess() (gas: 638456)\n[PASS] test_chestsStayAboutEqualInValue() (gas: 5511474)\n[PASS] test_exactOutputPaysTheFeeOnTheInput() (gas: 384979)\n[PASS] test_exactOutputWithTheChestShareInTheOutput() (gas: 780877)\n[PASS] test_failedPayoutStaysOwedAndTheTradeGoesThrough() (gas: 1217146)\n[PASS] test_feesCannotBeWithdrawnByAnyone() (gas: 449988)\n[PASS] test_firstTradeFundsChest0AndTheSlice() (gas: 363816)\n[PASS] test_fundChestNeedsTheRightValue() (gas: 48803)\n[PASS] test_fundChestOnlyForHookPools() (gas: 41868)\n[PASS] test_fundChestWithEth() (gas: 252918)\n[PASS] test_fundChestWithTokens() (gas: 193816)\n[PASS] test_openingSurchargeOnlyInTheMainDuel() (gas: 3469491)\n[PASS] test_payoutIsCappedAtTwiceTheTrade() (gas: 828202)\n[PASS] test_quietMovePaysNothing() (gas: 616203)\n[PASS] test_whiteCaptureIsPaidInItsTeamTokenFromItsOwnChest() (gas: 854514)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 821.72ms (850.53ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_boardHasBothKingsAndPlyIsBounded\n[PASS] invariant_heldEqualsOwed\n InvariantTest invariants (runs: 24, calls: 2400, reverts: 0)\n\n╭----------+----------------+-------+---------+----------╮\n| Contract | Selector       | Calls | Reverts | Discards |\n+========================================================+\n| Handler  | convert        | 301   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | fund           | 305   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | longPause      | 311   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | nextBlock      | 313   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | trade          | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeAgain     | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeNextBlock | 312   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | withdrawReward | 304   | 0       | 0        |\n╰----------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 53\n  Bound result 19999999000000000357\n  Bound result 19999999000001000001\n  Bound result 19999999000000020458\n  Bound result 19999999000000000755\n  Bound result 112\n  Bound result 18046385208871800035\n  Bound result 2754\n  Bound result 19999999007306351798\n  Bound result 11\n  Bound result 31865262234567526\n  Bound result 19999999000000887221\n  Bound result 463181382011195625\n  Bound result 19999999000000003151\n  Bound result 5000\n  Bound result 100\n  Bound result 7037082190885724022\n  Bound result 19999999386547056641\n  Bound result 18446744073709551615\n  Bound result 76\n  Bound result 4022291159582972627\n  Bound result 6\n  Bound result 8538\n  Bound result 19999999000000020001\n  Bound result 41\n  Bound result 386549022720\n  Bound result 796\n  Bound result 19999999000000000015\n  Bound result 1221339734816628308\n  Bound result 70\n  Bound result 104\n  Bound result 90\n  Bound result 81\n  Bound result 18\n  Bound result 120\n  Bound result 2719019064705274351\n  Bound result 19999999000000007454\n  Bound result 55\n  Bound result 1000000000003\n  Bound result 4413685840969725471\n  Bound result 1251\n  Bound result 17\n  Bound result 19999999000000000031\n  Bound result 19999999000000000904\n  Bound result 19999999000000000727\n  Bound result 15672660589582542013\n  Bound result 20396340581799926600\n  Bound result 13156\n  Bound result 19999999000000000321\n  Bound result 19999999000000004184\n  Bound result 12566133904435631808\n  Bound result 15386197539150620393\n  Bound result 2\n  Bound result 30\n  Bound result 8911052841178915334\n  Bound result 19999999000000003822\n  Bound result 7037082190885724022\n  Bound result 4999999999995\n  Bound result 5060859166916801136\n  Bound result 100000\n  Bound result 45\n  swaps: 33\n  moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.10s (1.10s CPU time)\n\nRan 1 test for test/InvariantDuels.t.sol:InvariantDuelsTest\n[PASS]\nInvariantDuelsTest invariants:\n[PASS] invariant_everyBoardIsSound\n[PASS] invariant_everyCurrencyBalances\n[PASS] invariant_rightsMatchTheBoard\n[PASS] invariant_turnsAreWellFormed\n InvariantDuelsTest invariants (runs: 24, calls: 2880, reverts: 0)\n\n╭--------------+-------------------+-------+---------+----------╮\n| Contract     | Selector          | Calls | Reverts | Discards |\n+===============================================================+\n| DuelsHandler | addLiquidity      | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convert           | 220   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convertNextBlock  | 200   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | fund              | 241   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | longPause         | 221   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | nextBlock         | 215   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | pushThenNextBlock | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | removeLiquidity   | 253   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | roundTrip         | 251   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | trade             | 232   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeAgain        | 198   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeNextBlock    | 223   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | withdrawReward    | 210   | 0       | 0        |\n╰--------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 23181\n  Bound result 2565653098084874863\n  Bound result 12\n  Bound result 100000000\n  Bound result 72624976668147840\n  Bound result 4076725131\n  Bound result 12\n  Bound result 19999999999000000049\n  Bound result 4485934425771658475\n  Bound result 120\n  Bound result 12\n  Bound result 4503599627370496\n  Bound result 12\n  Bound result 19999999999000011559\n  Bound result 499999999500000000\n  Bound result 7000\n  Bound result 19999999999000017411\n  Bound result 19999999999000200001\n  Bound result 12\n  Bound result 19999999999000000016\n  Bound result 19999999999000106693\n  Bound result 25333134452981760\n  Bound result 133294633079153423663\n  Bound result 12\n  Bound result 2296800487608934368\n  Bound result 12\n  Bound result 19999999999000010437\n  Bound result 12\n  Bound result 19999999999000014838\n  Bound result 12\n  Bound result 19999999999000006727\n  Bound result 180000000000000004462\n  Bound result 12\n  Bound result 4999999999000017032\n  Bound result 5505625188992501493\n  Bound result 12\n  Bound result 180000000000000000310\n  Bound result 12\n  Bound result 180000000000000009233\n  Bound result 12\n  Bound result 190113537283163612\n  Bound result 52\n  Bound result 19999999999000014187\n  Bound result 12\n  Bound result 19999999999006906997\n  Bound result 180000000000000800001\n  Bound result 12\n  Bound result 4999999999000000501\n  Bound result 6017502607293588406\n  Bound result 9999999757171428133383\n  Bound result 9999999899999990000000\n  Bound result 237231844378604801142\n  Bound result 17715909253537394009\n  Bound result 12\n  Bound result 600000000000000000000\n  Bound result 19999999999000015106\n  Bound result 805801794754002874\n  Bound result 4999999999000000601\n  Bound result 180000000000000006941\n  Bound result 12\n  Bound result 180000000000000043129\n  Bound result 12\n  Bound result 12\n  Bound result 4999999999000001690\n  Bound result 12\n  Bound result 12\n  Bound result 100000000000000000000\n  Bound result 19999999999000001433\n  Bound result 12\n  Bound result 56\n  Bound result 4999999999001966081\n  Bound result 12\n  Bound result 9967844527072848824\n  Bound result 19999999999000004005\n  Bound result 19999999999000065281\n  Bound result 12\n  Bound result 17830126982352984981\n  Bound result 29\n  Bound result 18337441071504203334\n  Bound result 120\n  Bound result 27612829230727045392\n  Bound result 12\n  Bound result 80\n  Bound result 1000178988994931336848\n  Bound result 180000000000000887271\n  Bound result 12\n  Bound result 21931300337370667194\n  Bound result 184000000000000000001\n  Bound result 12\n  Bound result 112\n  Bound result 180000000000000026315\n  Bound result 12\n  Bound result 180000000000000995001\n  Bound result 12\n  Bound result 46649393329207593530\n  Bound result 12\n  Bound result 2155872511\n  Bound result 32680281041836782903\n  Bound result 11100177746166359477658\n  Bound result 19999999999000000996\n  Bound result 1812930352692670847190\n  Bound result 4000000000000000\n  Bound result 3398176614991109133\n  Bound result 19999999999000094628\n  swaps: 43\n  moves: 24\n  Bound result 12\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.44s (3.43s CPU time)\n\nRan 23 test suites in 3.44s (7.82s CPU time): 175 tests passed, 0 failed, 0 skipped (175 total tests)\n","passed":true},{"durationMs":94,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDuelHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMDuelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDuelHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDuelHook.convert(address)\",\"IMDuelHook.convertBatch(address)\",\"IMDuelHook.fundChest((address,address,uint24,int24,address),bool,uint256)\",\"IMDuelHook.payOut((address,address,uint24,int24,address),address,address,uint256)\",\"IMDuelHook.sampleRoute(bytes,bytes[],bytes[])\",\"IMDuelHook.unlockCallback(bytes)\",\"IMDuelHook.withdraw(address,address)\",\"IMDuelHook.withdrawTo(address,address)\",\"IMDuelToken.approve(address,uint256)\",\"IMDuelToken.transfer(address,uint256)\",\"IMDuelToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":102,\"LICENSE\":16,\"foundry.toml\":29,\"launch.json\":39,\"launch/deployed.json\":12,\"launch/engine.json\":10,\"launch/hook.json\":39,\"launch/hook.sepolia.json\":39,\"script/LaunchPolicy.sol\":44,\"script/route_proof.py\":135,\"src/Bitboard.sol\":719,\"src/Chess.sol\":34,\"src/ChessEngineV5.sol\":852,\"src/IChessEngine.sol\":29,\"src/IMDuelHook.sol\":967,\"src/IMDuelToken.sol\":62,\"src/RoutePriceVerifier.sol\":50,\"src/Rules.sol\":45,\"src/Strength.sol\":47,\"src/proof/Bytes.sol\":144,\"src/proof/LICENSE\":22,\"src/proof/MerkleTrie.sol\":216,\"src/proof/PROVENANCE.md\":12,\"src/proof/RLPErrors.sol\":20,\"src/proof/RLPReader.sol\":249,\"test/Base.sol\":211,\"test/BidOwner.t.sol\":118,\"test/Bidding.t.sol\":224,\"test/Chessboard.sol\":79,\"test/CrossBlock.t.sol\":195,\"test/Draws.t.sol\":90,\"test/FactoryLaunch.t.sol\":173,\"test/Game.t.sol\":348,\"test/GasEstimate.t.sol\":120,\"test/Harness.sol\":83,\"test/HookGas.t.sol\":151,\"test/Hostile.t.sol\":461,\"test/Invariant.t.sol\":150,\"test/InvariantDuels.t.sol\":529,\"test/Launch.t.sol\":159,\"test/LaunchAdmission.t.sol\":167,\"test/LaunchFiles.t.sol\":75,\"test/Money.t.sol\":353,\"test/Predict.t.sol\":102,\"test/RoutePriceVerifier.t.sol\":135,\"test/RouteProof.sol\":69,\"test/RouteSample.t.sol\":168,\"test/Size.t.sol\":36,\"test/Slice.t.sol\":392,\"test/Strength.t.sol\":85,\"test/Teams.t.sol\":87,\"test/Token.t.sol\":82,\"test/Tokens.sol\":172,\"test/V4Fixture.sol\":100,\"test/data/chess.json\":1,\"test/data/conversion-validation.json\":770,\"test/data/conversion.json\":98,\"test/data/endings.json\":1,\"test/data/legal.json\":1,\"test/data/random.json\":1,\"test/data/route-proof.json\":16,\"test/test_route_proof.py\":72},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":20816,"exitCode":0,"name":"slither","output":"[high/medium] incorrect-exp at src/IMDuelHook.sol:256: IMDuelHook.teamOf(uint8,uint32,bool) (src/IMDuelHook.sol#256-258) has bitwise-xor operator ^ instead of the exponentiation operator **: \n[high/medium] incorrect-exp at src/IMDuelHook.sol:256: IMDuelHook.teamOf(uint8,uint32,bool) (src/IMDuelHook.sol#256-258) has bitwise-xor operator ^ instead of the exponentiation operator **: \n[medium/high] incorrect-equality at src/IMDuelHook.sol:567: IMDuelHook._sampleAt(uint256,uint160,uint256) (src/IMDuelHook.sol#567-573) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMDuelHook.sol:578: IMDuelHook._reference(uint256,bool) (src/IMDuelHook.sol#578-584) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMDuelHook.sol:335: IMDuelHook._takeFee(PoolId,PoolKey,uint256,uint256,uint256,IMDuelHook.Game) (src/IMDuelHook.sol#335-347) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:622: Reentrancy in IMDuelHook._play(PoolId,PoolKey,IMDuelHook.Game,bool,BalanceDelta,address,uint160,uint256) (src/IMDuelHook.sol#622-661):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:305: Reentrancy in IMDuelHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDuelHook.sol#305-330):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:504: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#504-527):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:539: Reentrancy in IMDuelHook._swapClaims(PoolKey,Currency,uint256,bool) (src/IMDuelHook.sol#539-560):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:504: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#504-527):\n[medium/medium] uninitialized-local at src/Bitboard.sol:643: Bitboard._targets(Bitboard.Pos,Bitboard.Gen,uint8,bool).ep (src/Bitboard.sol#643) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:480: Bitboard._anyLegal(Bitboard.Pos,bool).scratch (src/Bitboard.sol#480) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:382: IMDuelHook._pay(PoolId,IMDuelHook.Game,PoolKey,Currency,address,uint256).paid (src/IMDuelHook.sol#382) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:589: IMDuelHook._pack(uint160).e (src/IMDuelHook.sol#589) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:425: Bitboard.isLegal(Bitboard.Pos,uint16,bool).scratch (src/Bitboard.sol#425) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:497: ChessEngineV5._queening(ChessEngineV5.Ctx,Bitboard.Pos,uint64,uint64,uint64,uint64,uint64).a (src/ChessEngineV5.sol#497) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:182: ChessEngineV5._choose(ChessEngineV5.Ctx,uint256,uint256,uint64[8],uint32,uint16).seen (src/ChessEngineV5.sol#182) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:400: Bitboard._legalTargets(Bitboard.Pos,Bitboard.Gen,Bitboard.Pos,uint8,bool).ep (src/Bitboard.sol#400) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:236: Bitboard.pieceAttacks(Bitboard.Pos,uint64,uint64,bool).a (src/Bitboard.sol#236) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:203: ChessEngineV5._choose(ChessEngineV5.Ctx,uint256,uint256,uint64[8],uint32,uint16).refined (src/ChessEngineV5.sol#203) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:580: Bitboard.mateInOne(Bitboard.Pos,bool,uint256).scratch (src/Bitboard.sol#580) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:876: IMDuelHook._repeated(PoolId,IMDuelHook.Game).seen (src/IMDuelHook.sol#876) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:414: ChessEngineV5._placement(ChessEngineV5.Ctx,uint8,uint8,uint8,uint8,bool,bool).near (src/ChessEngineV5.sol#414) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:276: ChessEngineV5._context(uint256,uint32,uint32,bool,bool).nonPawn (src/ChessEngineV5.sol#276) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:809: IMDuelHook._apply(PoolId,IMDuelHook.Game,uint16,Rules.Played,IMDuelHook.Trade).result (src/IMDuelHook.sol#809) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:377: ChessEngineV5._cheap(ChessEngineV5.Ctx,uint16).squeeze (src/ChessEngineV5.sol#377) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:813: IMDuelHook._apply(PoolId,IMDuelHook.Game,uint16,Rules.Played,IMDuelHook.Trade).reason (src/IMDuelHook.sol#813) is a local variable never initialized\n[medium/medium] unused-return at src/IMDuelHook.sol:426: IMDuelHook._withdraw(address,Currency,address) (src/IMDuelHook.sol#426-432) ignores return value by manager.unlock(abi.encode(uint8(0),abi.encode(to,c,amount))) (src/IMDuelHook.sol#430)\n[medium/medium] unused-return at src/IMDuelHook.sol:288: IMDuelHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/IMDuelHook.sol#288-303) ignores return value by (sqrtPrice,None,None,None) = manager.getSlot0(id) (src/IMDuelHook.sol#295)\n[medium/medium] unused-return at src/IMDuelHook.sol:539: IMDuelHook._swapClaims(PoolKey,Currency,uint256,bool) (src/IMDuelHook.sol#539-560) ignores return value by (sqrtPrice,None,None,None) = manager.getSlot0(key.toId()) (src/IMDuelHook.sol#544)\n[medium/medium] unused-return at src/IMDuelHook.sol:447: IMDuelHook.convert(Currency) (src/IMDuelHook.sol#447-449) ignores return value by manager.unlock(abi.encode(uint8(2),abi.encode(address(0),c,uint256(0)))) (src/IMDuelHook.sol#448)\n[low/high] shadowing-local at src/ChessEngineV5.sol:817: ChessEngineV5._mop(ChessEngineV5.Ctx,uint8,uint8,uint8,bool,bool).at (src/ChessEngineV5.sol#705) shadows:\n[low/medium] missing-zero-check at src/IMDuelHook.sol:469: IMDuelHook.unlockCallback(bytes).who (src/IMDuelHook.sol#469) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:335: Reentrancy in IMDuelHook._takeFee(PoolId,PoolKey,uint256,uint256,uint256,IMDuelHook.Game) (src/IMDuelHook.sol#335-347):\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:504: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#504-527):\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:436: Reentrancy in IMDuelHook.fundChest(PoolKey,bool,uint256) (src/IMDuelHook.sol#436-444):\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:381: Reentrancy in IMDuelHook._pay(PoolId,IMDuelHook.Game,PoolKey,Currency,address,uint256) (src/IMDuelHook.sol#381-389):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:436: Reentrancy in IMDuelHook.fundChest(PoolKey,bool,uint256) (src/IMDuelHook.sol#436-444):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:381: Reentrancy in IMDuelHook._pay(PoolId,IMDuelHook.Game,PoolKey,Currency,address,uint256) (src/IMDuelHook.sol#381-389):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:305: Reentrancy in IMDuelHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDuelHook.sol#305-330):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:622: Reentrancy in IMDuelHook._play(PoolId,PoolKey,IMDuelHook.Game,bool,BalanceDelta,address,uint160,uint256) (src/IMDuelHook.sol#622-661):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:504: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#504-527):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:426: Reentrancy in IMDuelHook._withdraw(address,Currency,address) (src/IMDuelHook.sol#426-432):\n[low/medium] return-bomb at src/IMDuelHook.sol:773: IMDuelHook._engine(uint256,bytes) (src/IMDuelHook.sol#773-779) tries to limit the gas of an external call that controls implicit decoding\n[low/medium] timestamp at src/IMDuelHook.sol:335: IMDuelHook._takeFee(PoolId,PoolKey,uint256,uint256,uint256,IMDuelHook.Game) (src/IMDuelHook.sol#335-347) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:622: IMDuelHook._play(PoolId,PoolKey,IMDuelHook.Game,bool,BalanceDelta,address,uint160,uint256) (src/IMDuelHook.sol#622-661) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:351: IMDuelHook._chestBps(IMDuelHook.Game) (src/IMDuelHook.sol#351-354) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:504: IMDuelHook._convert(Currency) (src/IMDuelHook.sol#504-527) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:365: IMDuelHook._addChest(PoolId,uint256,uint256) (src/IMDuelHook.sol#365-372) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:436: IMDuelHook.fundChest(PoolKey,bool,uint256) (src/IMDuelHook.sol#436-444) uses timestamp for comparisons","passed":true},{"durationMs":1053,"exitCode":0,"name":"aderyn","output":"[high] contract-locks-ether at src/IMDuelHook.sol:44: Contract locks Ether without a withdraw function\n[high] incorrect-caret-operator at src/IMDuelHook.sol:257: Incorrect use of caret operator\n[high] incorrect-shift-order at src/proof/RLPReader.sol:164: Incorrect Assembly Shift Parameter Order (3 places)\n[high] reentrancy-state-change at src/IMDuelHook.sol:458: Reentrancy: State change after external call\n[high] unsafe-casting at src/Bitboard.sol:247: Unsafe Casting of integers (2 places)\n[low] empty-require-revert at src/ChessEngineV5.sol:157: Empty `require()` / `revert()` Statement\n[low] internal-function-used-once at src/Bitboard.sol:199: Internal Function Used Only Once (13 places)\n[low] large-numeric-literal at src/ChessEngineV5.sol:34: Large Numeric Literal (27 places)\n[low] literal-instead-of-constant at src/Bitboard.sol:35: Literal Instead of Constant (424 places)\n[low] missing-inheritance at src/IMDuelToken.sol:7: Missing Inheritance\n[low] push-zero-opcode at src/Bitboard.sol:2: PUSH0 Opcode (13 places)\n[low] require-revert-in-loop at src/proof/MerkleTrie.sol:73: Loop Contains `require`/`revert` (3 places)\n[low] state-change-without-event at src/IMDuelHook.sol:288: State Change Without Event\n[low] unchecked-return at src/IMDuelHook.sol:430: Unchecked Return (3 places)\n[low] uninitialized-local-variable at src/ChessEngineV5.sol:164: Uninitialized Local Variable (4 places)\n[low] unsafe-erc20-operation at src/IMDuelHook.sol:485: Unsafe ERC20 Operation\n[low] unspecific-solidity-pragma at src/ChessEngineV5.sol:2: Unspecific Solidity Pragma (6 places)\n[low] unused-state-variable at src/IMDuelHook.sol:76: Unused State Variable (2 places)","passed":true},{"durationMs":18129,"exitCode":0,"name":"proof 5edd7f4d325d","output":"Compiling 96 files with Solc 0.8.26\nSolc 0.8.26 finished in 17.39s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:22:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                      ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:38:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                                      ^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:109:57:\n    |\n109 |             uint64 d = f >= r ? DIAG >> (8 * (f - r)) : DIAG << (8 * (r - f));\n    |                                                         ^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:110:65:\n    |\n110 |             uint64 a = f + r <= 7 ? ANTI >> (8 * (7 - f - r)) : ANTI << (8 * (f + r - 7));\n    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:38:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:69:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                                                     ^^^^^^^^^^^^^^^^^^^^\n\n\nRan 1 test for test/imd-proof-d29e3f36/Proof_5edd7f4d325d.t.sol:RouterOwnerTest\n[PASS] test_emptyHookDataNeverLeavesTheBountyWithTheRouter() (gas: 704330)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.22ms (720.39µs CPU time)\n\nRan 1 test suite in 2.93ms (2.22ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"eea25d482b3080b8e6745acc2ffa11ad229fe1d054eab2cbbd3bddd880b5de05","verifiedTreeHash":"3195b2ee49b71ac5999337bd93f0fc4e5e961118","verifierVersion":"0.1.0+d372f99e"},{"checks":[{"durationMs":96288,"exitCode":0,"name":"build","output":"Compiling 123 files with Solc 0.8.26\nSolc 0.8.26 finished in 96.13s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:22:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                      ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:38:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                                      ^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:109:57:\n    |\n109 |             uint64 d = f >= r ? DIAG >> (8 * (f - r)) : DIAG << (8 * (r - f));\n    |                                                         ^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:110:65:\n    |\n110 |             uint64 a = f + r <= 7 ? ANTI >> (8 * (7 - f - r)) : ANTI << (8 * (f + r - 7));\n    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:38:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:69:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                                                     ^^^^^^^^^^^^^^^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:72:18:\n   |\n72 |         returns (uint16, Rules.Played memory)\n   |                  ^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:72:26:\n   |\n72 |         returns (uint16, Rules.Played memory)\n   |                          ^^^^^^^^^^^^^^^^^^^\n\nWarning (2072): Unused local variable.\n  --> test/Game.t.sol:90:9:\n   |\n90 |         IMDuelHook.Game memory g = st();\n   |         ^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n  --> test/LaunchFiles.t.sol:70:5:\n   |\n70 |     function test_engineLaunchFileDeploysTheEngineWithoutArguments() public {\n   |     ^ (Relevant source part starts here and spans across multiple lines).\n\n","passed":true},{"durationMs":3819,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/Token.t.sol:TokenTest\n[PASS] test_allowances() (gas: 347653)\n[PASS] test_fixedSupplyToTheDeployer() (gas: 39384)\n[PASS] test_nothingPrivileged() (gas: 192691)\n[PASS] test_transfers() (gas: 188240)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.07ms (498.85µs CPU time)\n\nRan 1 test for test/Size.t.sol:SizeTest\n[PASS] test_deployedCodeFitsTheBudgets() (gas: 1255245)\nLogs:\n  hook: 21322\n  engine v5: 23339\n  route price verifier: 5551\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.32ms (3.18ms CPU time)\n\nRan 3 tests for test/LaunchFiles.t.sol:LaunchFilesTest\n[PASS] test_engineLaunchFileDeploysTheEngineWithoutArguments() (gas: 7834)\n[PASS] test_ethereumLaunchFile() (gas: 1334990)\n[PASS] test_sepoliaLaunchFile() (gas: 1330008)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 4.49ms (1.24ms CPU time)\n\nRan 1 test for test/Proof_SyncRecipient.t.sol:SyncRecipientTest\n[PASS] test_anEthPayingTradeThatPaysASyncingOwnerGoesThrough() (gas: 1161237)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.90ms (1.11ms CPU time)\n\nRan 7 tests for test/Draws.t.sol:DrawsTest\n[PASS] test_checkmate() (gas: 240028)\n[PASS] test_moveCap() (gas: 240069)\n[PASS] test_ordinaryMoveDoesNotEndTheGame() (gas: 180017)\n[PASS] test_quietMoveLimit() (gas: 246287)\n[PASS] test_stalemate() (gas: 237841)\n[PASS] test_thirdRepetition() (gas: 964226)\n[PASS] test_tooFewPiecesToMate() (gas: 231666)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 7.31ms (4.75ms CPU time)\n\nRan 5 tests for test/Teams.t.sol:TeamsTest\n[PASS] test_aDynamicFeePoolPlaysWithAZeroLpFee() (gas: 2916166)\n[PASS] test_nativeEthIsATeamLikeAnyToken() (gas: 2329765)\n[PASS] test_poolsOnOneHookKeepSeparateGamesAndChests() (gas: 3392679)\n[PASS] test_sliceSide() (gas: 1821742)\n[PASS] test_twoPlainTokensCannotDuel() (gas: 1593745)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 13.53ms (18.37ms CPU time)\n\nRan 8 tests for test/Strength.t.sol:StrengthTest\n[PASS] testFuzz_boundedAndMonotonic(uint256,uint256,uint256) (runs: 256, μ: 9072, ~: 8663)\nLogs:\n  Bound result 4608\n\n[PASS] testFuzz_depthMonotonic(uint256,uint256,uint256) (runs: 256, μ: 12018, ~: 12425)\nLogs:\n  Bound result 4608\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129635519\n\n[PASS] testFuzz_shiftKeepsTheShare(uint256,uint256,uint8) (runs: 256, μ: 14666, ~: 15266)\nLogs:\n  Bound result 26\n  Bound result 459262422194879674572042097913192910163574\n  Bound result 138475253271768467977021970956\n\n[PASS] testFuzz_tenfoldIsAboutThirtyPoints(uint128,uint128) (runs: 256, μ: 13833, ~: 13724)\nLogs:\n  Bound result 340282366920938463463374607431767211492\n  Bound result 336879543251729078828740861357515074\n\n[PASS] testFuzz_unscalablePaidFollowsTheCurve(uint256,uint256) (runs: 256, μ: 13980, ~: 14208)\nLogs:\n  Bound result 115790931316423822261616749298837820974191451965793907643040561468927826316639\n  Bound result 115792089237316195423570985008687907853269984665639641702553898530332329700000\n\n[PASS] test_decidedPoints() (gas: 15577)\n[PASS] test_hugePaymentsDoNotRevert() (gas: 12854)\n[PASS] test_zeroInputs() (gas: 4493)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 30.07ms (44.37ms CPU time)\n\nRan 11 tests for test/PayoutGuard.t.sol:PayoutGuardTest\n[PASS] testFuzz_settlingAnyAmountForTheHookIsRefused(uint256) (runs: 24, μ: 1740671, ~: 1740788)\nLogs:\n  Bound result 887270\n\n[PASS] test_aPlainRecipientIsPaidInTheTrade() (gas: 1638804)\n[PASS] test_aPlainRecipientIsPaidInsideARouterThatPrepaysByTransfer() (gas: 1825587)\n[PASS] test_aPlainRecipientIsPaidInsideARouterThatPrepaysEth() (gas: 2140587)\n[PASS] test_aRecipientSettlingForTheRouterIsRefused() (gas: 1739357)\n[PASS] test_aRecipientThatOnlyReadsDeltasIsPaid() (gas: 1712869)\n[PASS] test_aRecipientThatSyncsATokenIsRefusedWhenTheTradePaysInEth() (gas: 1841967)\n[PASS] test_aRecipientThatSyncsIsSettledAroundWhenTheTradePaysInTheToken() (gas: 1281561)\n[PASS] test_aRefusedRewardLeavesOnlyThroughItsOwnersWithdrawTo() (gas: 1308174)\n[PASS] test_aResyncAfterTheRoutersTransferIsRefused() (gas: 2001570)\n[PASS] test_anOwedRewardIsDeliveredByAnyonesWithdrawOnceTheOwnerAccepts() (gas: 1173892)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 89.50ms (109.60ms CPU time)\n\nRan 2 tests for test/LaunchAdmission.t.sol:LaunchAdmissionTest\n[PASS] test_blankChainTokenAboveImd() (gas: 62938925)\n[PASS] test_blankChainTokenBelowImd() (gas: 62867880)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 101.65ms (168.07ms CPU time)\n\nRan 8 tests for test/BidOwner.t.sol:BidOwnerTest\n[PASS] testFuzz_otherDataLengthsNeverCreditTheBundlerOrTheRouter(bytes) (runs: 256, μ: 383284, ~: 383282)\n[PASS] test_aHookWithoutAnEthRouteRejectsRouteProofs() (gas: 32534)\n[PASS] test_abiEncodedOwnerCreditsTheWallet() (gas: 384567)\n[PASS] test_abiEncodedOwnerReceivesTheCaptureBounty() (gas: 1128583)\n[PASS] test_dirtyAbiPaddingIsNobody() (gas: 382698)\n[PASS] test_missingOwnerIsNobodyNotTheRouterNorTheBundler() (gas: 415565)\n[PASS] test_missingOwnerPlaysButPaysNobody() (gas: 1151657)\n[PASS] test_packedOwnerCreditsTheWallet() (gas: 384686)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 107.80ms (105.40ms CPU time)\n\nRan 12 tests for test/Bidding.t.sol:BiddingTest\n[PASS] test_aBountyIsPaidFromTheMoversChest() (gas: 1369426)\n[PASS] test_aBuyOfTheOtherTeamBidsForItsNextMove() (gas: 1952666)\n[PASS] test_aBuyOfTheOtherTeamTimesTheSilentSideOutAndBids() (gas: 1222504)\n[PASS] test_aClosedWindowTakesNoBidsForItsMove() (gas: 1062884)\n[PASS] test_aMoveIsOpenForTurnBlocksBlocks() (gas: 1040466)\n[PASS] test_aNewTopMustPayMoreThanTenPercentMore() (gas: 974355)\n[PASS] test_aNextMoveBidCarriesIntoTheNextGame() (gas: 1467024)\n[PASS] test_aWindowWithoutBidsBecomesTheFirstBidsBlock() (gas: 1283690)\n[PASS] test_bidsCountAcrossTheWholeWindow() (gas: 1511214)\n[PASS] test_theMoveDependsOnlyOnTheWindowsLastBlock() (gas: 1621426)\n[PASS] test_theMoversOwnBuyAfterItsWindowBidsForItsNextMove() (gas: 1077593)\n[PASS] test_thePreviewIsTheMoveThatIsPlayed() (gas: 1222868)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 110.89ms (29.96ms CPU time)\n\nRan 3 tests for test/Launch.t.sol:LaunchTest\n[PASS] test_launchPairedWithEth() (gas: 23117220)\n[PASS] test_launchTokenAboveImd() (gas: 24067102)\nLogs:\n  first buy (opens game 0, bids for White): 366422\n\n[PASS] test_launchTokenBelowImd() (gas: 23992486)\nLogs:\n  first buy (opens game 0, bids for White): 361672\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 117.40ms (258.61ms CPU time)\n\nRan 2 tests for test/FactoryLaunch.t.sol:FactoryLaunchTest\n[PASS] test_factoryLaunchWithRealTokenAboveImd() (gas: 21676728)\n[PASS] test_factoryLaunchWithRealTokenBelowImd() (gas: 21641394)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 128.03ms (55.95ms CPU time)\n\nRan 13 tests for test/RoutePriceVerifier.t.sol:RoutePriceVerifierTest\n[PASS] testFuzz_authenticatesSlot0AndItsBlock(uint160,uint96) (runs: 256, μ: 200949, ~: 200425)\nLogs:\n  Bound result 2964422583387036479856633470515551576907\n\n[PASS] test_changedAccountProofIsRefused() (gas: 100311)\n[PASS] test_changedHeaderIsNotCanonical() (gas: 80813)\n[PASS] test_changedStorageProofIsRefused() (gas: 158336)\n[PASS] test_clientGeneratedPoolManagerProof() (gas: 241704)\n[PASS] test_emptyProofsAreRefused() (gas: 195111)\n[PASS] test_extraProofNodesAreRefused() (gas: 195861)\n[PASS] test_outOfRangePriceIsRefused() (gas: 192156)\n[PASS] test_theBatchBlockCannotSupplyASample() (gas: 80841)\n[PASS] test_uninitializedPoolPriceIsRefused() (gas: 180149)\n[PASS] test_unknownOrExpiredHeaderIsRefused() (gas: 81201)\n[PASS] test_wrongManagerIsRefused() (gas: 108622)\n[PASS] test_wrongPoolSlotIsRefused() (gas: 165141)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 128.20ms (133.91ms CPU time)\n\nRan 6 tests for test/RouteSample.t.sol:RouteSampleTest\n[PASS] test_authenticatedBlockStartsRejectTheSameAttack() (gas: 3422402)\n[PASS] test_callerSamplesCannotSellTheEthPileAtAPushedPrice() (gas: 1710709)\nLogs:\n  route attacker profit at the honest price: -87002115995172580\n\n[PASS] test_duplicateSampleCannotRotateOutHonestHistory() (gas: 493037)\n[PASS] test_gapRequiresThreeConsecutiveSamplesAgain() (gas: 1855898)\n[PASS] test_honestBatchRunsAndStopsAtTheHistoricalPriceBand() (gas: 1175548)\n[PASS] test_staleRoutePricesWaitWithoutLosingClaims() (gas: 1298197)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 134.83ms (27.82ms CPU time)\n\nRan 14 tests for test/Slice.t.sol:SliceTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5993365)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3973553)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3064481)\n[PASS] test_anEthBatchIsCapped() (gas: 5253758)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3203346)\n[PASS] test_anyoneCanConvert() (gas: 462007)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656457)\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4254448)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245420)\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764048)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487334)\n[PASS] test_noRouteNoDuel() (gas: 1030789)\n[PASS] test_oneEthBatchPerBlock() (gas: 5779111)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187164)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 138.52ms (78.99ms CPU time)\n\nRan 5 tests for test/HookGas.t.sol:HookGasTest\n[PASS] test_conversionGas() (gas: 3645312)\nLogs:\n  plain trade: 207740\n  trade that converts a batch: 271602\n\n[PASS] test_hookShareOfAMovingTrade() (gas: 29179960)\nLogs:\n  hook share of a moving trade: average: 280031\n  hook share of a moving trade: max: 333579\n\n[PASS] test_middlegameGas() (gas: 9538644)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 191975\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 562851\n\n[PASS] test_openingGas() (gas: 4542201)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 192693\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 508018\n\n[PASS] test_runningGameGas() (gas: 54808626)\nLogs:\n  running game, moving trades measured: 50\n  running game, moving trade average: 454075\n  running game, moving trade max: 554311\n\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 154.73ms (144.66ms CPU time)\n\nRan 19 tests for test/Hostile.t.sol:HostileTest\n[PASS] test_aCallbackTokenOwnerLeavingADebtCannotFreezeTheDuel() (gas: 5286273)\n[PASS] test_aCallbackTokenOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 5622051)\n[PASS] test_aCallbackTokenOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 5641809)\n[PASS] test_aCallbackTokenOwnerSyncingTheOtherCurrencyCannotFreezeTheDuel() (gas: 5505007)\n[PASS] test_aCallbackTokenOwnerSyncingTheTokenCannotFreezeTheDuel() (gas: 5500324)\n[PASS] test_aDeadEngineLeavesTheTurnQueuedAndTradingGoesOn() (gas: 3557668)\n[PASS] test_aGasBurningEngineCostsOneExpensiveTradeNotTheGame() (gas: 9729914)\n[PASS] test_aTokenReturningNothingFundsAChest() (gas: 2188344)\n[PASS] test_anEthOwnerLeavingADebtCannotFreezeTheDuel() (gas: 4204480)\n[PASS] test_anEthOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 4491567)\n[PASS] test_anEthOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 4551486)\n[PASS] test_anEthOwnerSyncingEthWhenNothingIsSyncedIsPaid() (gas: 3336300)\n[PASS] test_anEthOwnerSyncingTheOtherCurrencyCannotFreezeTheDuel() (gas: 4380600)\n[PASS] test_anOwnerThatCannotReceiveWithdrawsElsewhere() (gas: 3563277)\n[PASS] test_blockedRecipientStaysOwedAndTradingContinues() (gas: 4194594)\n[PASS] test_brokenEngineFallsBackToALegalMove() (gas: 5153119)\n[PASS] test_feeOnTransferTokenFundsOnlyWhatArrives() (gas: 2734236)\n[PASS] test_reenteringTokenCannotBreakAPayout() (gas: 4736699)\n[PASS] test_starvingTheEngineNeverPlaysTheFallback() (gas: 25153217)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 229.00ms (200.05ms CPU time)\n\nRan 17 tests for test/CrossBlock.t.sol:CrossBlockTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5993860)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3973571)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3064506)\n[PASS] test_anEthBatchIsCapped() (gas: 5253874)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3203497)\n[PASS] test_anyoneCanConvert() (gas: 462029)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656485)\n[PASS] test_crossBlockViaConvert() (gas: 5557988)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390298682200\n\n[PASS] test_crossBlockViaFirstTrade() (gas: 5720092)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390291136661\n\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4254538)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245468)\n[PASS] test_ethRouteSameBlockPush() (gas: 5231826)\nLogs:\n  ETH pile before (1e18): 500000000000000000\n  ETH the batch sold (1e18): 0\n  IMD it got (1e18): 0\n  IMD it was worth at the home price (1e18): 0\n  attacker profit (ETH at the home price, 1e18): -29770639430607030\n\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764062)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487316)\n[PASS] test_noRouteNoDuel() (gas: 1030839)\n[PASS] test_oneEthBatchPerBlock() (gas: 5779205)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187178)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 231.15ms (66.24ms CPU time)\n\nRan 16 tests for test/FeeSchedule.t.sol:FeeScheduleTest\n[PASS] testFuzz_exactOutputDeliversTheOrderAndTakesHalfAPercent(uint256) (runs: 256, μ: 940762, ~: 941381)\nLogs:\n  Bound result 268085210440097680199\n\n[PASS] testFuzz_openingChestShareFollowsTheSchedule(uint256,uint256) (runs: 512, μ: 464538, ~: 464677)\nLogs:\n  Bound result 1716\n  Bound result 999999999999999999998\n\n[PASS] testFuzz_surchargeNeverRises(uint256,uint256) (runs: 256, μ: 1045112, ~: 1045173)\nLogs:\n  Bound result 327\n  Bound result 382\n\n[PASS] test_conversionSplitsTheImduelHalfKeptHalfBurned() (gas: 3732728)\n[PASS] test_convertWithAnEmptyPileDoesNothing() (gas: 123769)\n[PASS] test_exactOutputStoppedByItsPriceLimitIsRefused() (gas: 215730)\n[PASS] test_fundChestBeyondBalanceIsRefused() (gas: 121468)\n[PASS] test_fundChestOfZeroAddsNothing() (gas: 98361)\n[PASS] test_fundChestWithTheWrongEthValueIsRefused() (gas: 344909)\n[PASS] test_fundChestWithoutAllowanceIsRefused() (gas: 111138)\n[PASS] test_lastSurchargeSecond() (gas: 470062)\n[PASS] test_mainDuelConvertsAtMostOnceAndTenMinimumsPerBlock() (gas: 4169193)\n[PASS] test_openingEdges() (gas: 469684)\n[PASS] test_owedRewardOnlyEverReachesItsOwner() (gas: 1183166)\n[PASS] test_surchargeEndsAtThreeHundredSeconds() (gas: 469575)\n[PASS] test_withdrawWithNothingOwedDoesNothing() (gas: 90029)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 231.35ms (433.69ms CPU time)\n\nRan 3 tests for test/GasEstimate.t.sol:GasEstimateTest\n[PASS] test_atTheEstimateADueBatchConverts() (gas: 4998592)\nLogs:\n  smallest gas limit that converts: 310375\n\n[PASS] test_atTheEstimateTheBountyIsPaidNotOwed() (gas: 4619259)\nLogs:\n  smallest gas limit that plays and pays: 417418\n\n[PASS] test_noGasLimitPlaysTheMoveAndLeavesItsBountyOwed() (gas: 19295099)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 264.77ms (271.22ms CPU time)\n\nRan 1 test for test/Predict.t.sol:PredictTest\n[PASS] test_aBotCannotPickItsOwnCapturingMoves() (gas: 289907162)\nLogs:\n  bot moves: 0\n  other moves: 74\n  captures among other moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 530.27ms (530.11ms CPU time)\n\nRan 21 tests for test/Money.t.sol:MoneyTest\n[PASS] testFuzz_heldEqualsOwed(uint256) (runs: 48, μ: 10681132, ~: 10763435)\n[PASS] test_aFullChestTakesNoMore() (gas: 1059339)\n[PASS] test_aTradeStoppedByItsPriceLimitIsRefused() (gas: 582903)\n[PASS] test_blackCaptureIsPaidInItsTeamToken() (gas: 786454)\n[PASS] test_bountyTable() (gas: 44705)\n[PASS] test_checkmatePaysAQuarterOfTheChest() (gas: 766298)\n[PASS] test_chestShareGoesToTheChestWorthLess() (gas: 638456)\n[PASS] test_chestsStayAboutEqualInValue() (gas: 5511474)\n[PASS] test_exactOutputPaysTheFeeOnTheInput() (gas: 384979)\n[PASS] test_exactOutputWithTheChestShareInTheOutput() (gas: 780877)\n[PASS] test_failedPayoutStaysOwedAndTheTradeGoesThrough() (gas: 1219882)\n[PASS] test_feesCannotBeWithdrawnByAnyone() (gas: 449988)\n[PASS] test_firstTradeFundsChest0AndTheSlice() (gas: 363816)\n[PASS] test_fundChestNeedsTheRightValue() (gas: 48803)\n[PASS] test_fundChestOnlyForHookPools() (gas: 41868)\n[PASS] test_fundChestWithEth() (gas: 252918)\n[PASS] test_fundChestWithTokens() (gas: 193816)\n[PASS] test_openingSurchargeOnlyInTheMainDuel() (gas: 3469491)\n[PASS] test_payoutIsCappedAtTwiceTheTrade() (gas: 833810)\n[PASS] test_quietMovePaysNothing() (gas: 616203)\n[PASS] test_whiteCaptureIsPaidInItsTeamTokenFromItsOwnChest() (gas: 860122)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 824.99ms (841.79ms CPU time)\n\nRan 23 tests for test/Game.t.sol:GameTest\n[PASS] testFuzz_swapsNeverRevert(uint256) (runs: 48, μ: 9608301, ~: 9622657)\n[PASS] test_aGamePlaysOutThroughSwaps() (gas: 134592260)\nLogs:\n  moves played: 187\n  bids (incl. outbid ones): 238\n  most gas for a swap: 685293\n\n[PASS] test_aSmallerTradeInTheBlockDoesNotTakeTheTurn() (gas: 686491)\n[PASS] test_aTimeoutTurnCanBeTakenInItsBlock() (gas: 1304210)\n[PASS] test_aTradeBidsForTheTurn() (gas: 483494)\n[PASS] test_aTurnClosesWithItsBlock() (gas: 965170)\n[PASS] test_anExpiredTurnBecomesATimeoutTurn() (gas: 1410536)\n[PASS] test_anOldTurnIsPlayedWithTheHistoryContractsHash() (gas: 1754873)\n[PASS] test_anyTradeSizeBids() (gas: 450095)\n[PASS] test_lateTradeStillBidsNormally() (gas: 429729)\n[PASS] test_onlyThePoolManagerAndTheHookCanCallIn() (gas: 171212)\n[PASS] test_ownerDefaultsToNobody() (gas: 449725)\n[PASS] test_playedPositionsEnterTheRepetitionMemory() (gas: 915368)\n[PASS] test_rejectsBadConfig() (gas: 9247)\n[PASS] test_silentSidePlaysAtZeroStrength() (gas: 1045239)\n[PASS] test_strengthFollowsTheShareOfThePool() (gas: 6374541)\n[PASS] test_teamsSwapColoursEveryGame() (gas: 926880)\n[PASS] test_theBiggestTradeInTheBlockOwnsTheTurn() (gas: 782415)\n[PASS] test_theFirstPoolOpenedIsTheMainDuel() (gas: 179471)\n[PASS] test_theMainDuelMustPairImd() (gas: 1468317)\n[PASS] test_theNextBlocksTradePlaysTheTurn() (gas: 1038495)\n[PASS] test_theOtherTeamCannotBidOnThisTurn() (gas: 412278)\n[PASS] test_whoPlaysTheTurnAndWhenDoesNotChangeTheMove() (gas: 1534194)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 891.17ms (1.13s CPU time)\n\nRan 1 test for test/Invariant.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_boardHasBothKingsAndPlyIsBounded\n[PASS] invariant_heldEqualsOwed\n InvariantTest invariants (runs: 24, calls: 2400, reverts: 0)\n\n╭----------+----------------+-------+---------+----------╮\n| Contract | Selector       | Calls | Reverts | Discards |\n+========================================================+\n| Handler  | convert        | 301   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | fund           | 305   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | longPause      | 311   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | nextBlock      | 313   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | trade          | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeAgain     | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeNextBlock | 312   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | withdrawReward | 304   | 0       | 0        |\n╰----------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 40\n  Bound result 19999999000000007339\n  Bound result 19999999000012648431\n  Bound result 19999999000000008367\n  Bound result 19999999000000000581\n  Bound result 102\n  Bound result 18046385208871800035\n  Bound result 2460\n  Bound result 19999999007306351798\n  Bound result 9000000000000000000\n  Bound result 31865262234567526\n  Bound result 270000000000000000\n  Bound result 19999999000000002681\n  Bound result 19999999000000003076\n  Bound result 128\n  Bound result 100\n  Bound result 19999999386547056641\n  Bound result 7616448877154347021\n  Bound result 19999999000000000065\n  Bound result 49\n  Bound result 4022291159582972627\n  Bound result 6\n  Bound result 424\n  Bound result 14999999999985\n  Bound result 35\n  Bound result 25332747903959040\n  Bound result 1101\n  Bound result 19999999000000000081\n  Bound result 1221339734816628308\n  Bound result 59\n  Bound result 98\n  Bound result 30\n  Bound result 9\n  Bound result 101\n  Bound result 80\n  Bound result 19999999000000004153\n  Bound result 19999999000000007541\n  Bound result 55\n  Bound result 1000000000003\n  Bound result 19999999000000000049\n  Bound result 1251\n  Bound result 32\n  Bound result 19999999000000000011\n  Bound result 19999999000000000184\n  Bound result 19999999000000003531\n  Bound result 19304386864691798331\n  Bound result 20396340581799926600\n  Bound result 13287\n  Bound result 12054830257181156736\n  Bound result 1000000000001\n  Bound result 12566133904435631808\n  Bound result 15386197539150620393\n  Bound result 2\n  Bound result 80\n  Bound result 19999999000000002423\n  Bound result 19999999000000003280\n  Bound result 19999999386547056641\n  Bound result 10000000000000000\n  Bound result 5060859166916801136\n  Bound result 400\n  Bound result 85\n  swaps: 33\n  moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.27s (1.27s CPU time)\n\nRan 3 tests for test/TokenInvariant.t.sol:TokenInvariantTest\n[PASS]\nTokenInvariantTest invariants:\n[PASS] invariant_allowancesAreExactlyWhatWasApprovedLessWhatWasSpent\n[PASS] invariant_supplyIsFixedAndHeldByTheActors\n TokenInvariantTest invariants (runs: 128, calls: 8192, reverts: 0)\n\n╭--------------+--------------------+-------+---------+----------╮\n| Contract     | Selector           | Calls | Reverts | Discards |\n+================================================================+\n| TokenHandler | approve            | 2093  | 0       | 0        |\n|--------------+--------------------+-------+---------+----------|\n| TokenHandler | approveZeroSpender | 1976  | 0       | 0        |\n|--------------+--------------------+-------+---------+----------|\n| TokenHandler | transfer           | 2073  | 0       | 0        |\n|--------------+--------------------+-------+---------+----------|\n| TokenHandler | transferFrom       | 2050  | 0       | 0        |\n╰--------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3\n  Bound result 9\n  Bound result 16613\n  Bound result 6\n  Bound result 732\n  Bound result 3\n  Bound result 6\n  Bound result 3\n  Bound result 7\n  Bound result 12\n  Bound result 800\n  Bound result 57005\n  Bound result 545\n  Bound result 503\n  Bound result 333\n  Bound result 12\n  Bound result 1\n  Bound result 2\n  Bound result 131108336506390989984163337\n  Bound result 5670\n  Bound result 253\n  Bound result 4000\n  Bound result 10\n  Bound result 400\n  Bound result 529918236770399344205256927\n  Bound result 2293517445\n  Bound result 3\n  Bound result 901108312137550284369919888\n  Bound result 1094998567\n  Bound result 1000000000000000000000\n  Bound result 372\n  Bound result 7296\n  Bound result 49374\n  Bound result 3570\n  Bound result 982766869279416013057\n  Bound result 242\n\n[PASS] testFuzz_transferOfMoreThanTheBalanceIsRefused(uint256) (runs: 512, μ: 55904, ~: 55857)\nLogs:\n  Bound result 137122462167341575662000267002353578582749290296\n\n[PASS] test_transferFromTheZeroAddressIsRefused() (gas: 82118)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.78s (2.80s CPU time)\n\nRan 1 test for test/InvariantDuels.t.sol:InvariantDuelsTest\n[PASS]\nInvariantDuelsTest invariants:\n[PASS] invariant_everyBoardIsSound\n[PASS] invariant_everyCurrencyBalances\n[PASS] invariant_rightsMatchTheBoard\n[PASS] invariant_turnsAreWellFormed\n InvariantDuelsTest invariants (runs: 24, calls: 2880, reverts: 0)\n\n╭--------------+-------------------+-------+---------+----------╮\n| Contract     | Selector          | Calls | Reverts | Discards |\n+===============================================================+\n| DuelsHandler | addLiquidity      | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convert           | 220   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convertNextBlock  | 200   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | fund              | 241   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | longPause         | 221   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | nextBlock         | 215   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | pushThenNextBlock | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | removeLiquidity   | 253   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | roundTrip         | 251   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | trade             | 232   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeAgain        | 198   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeNextBlock    | 223   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | withdrawReward    | 210   | 0       | 0        |\n╰--------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 22905\n  Bound result 2565653098084874863\n  Bound result 12\n  Bound result 8121606201292619776\n  Bound result 19999999999000000005\n  Bound result 4113140427\n  Bound result 12\n  Bound result 19999999999000000049\n  Bound result 4485934425771658475\n  Bound result 120\n  Bound result 12\n  Bound result 19999999999000000050\n  Bound result 12\n  Bound result 19999999999000011292\n  Bound result 20000000000000000000\n  Bound result 8500\n  Bound result 19999999999000017333\n  Bound result 29999999970\n  Bound result 12\n  Bound result 19999999999000000064\n  Bound result 19999999999000000245\n  Bound result 19999999999000000091\n  Bound result 133294633079153423663\n  Bound result 12\n  Bound result 19999999999000007636\n  Bound result 12\n  Bound result 19999999999000008803\n  Bound result 12\n  Bound result 19999999999000014838\n  Bound result 12\n  Bound result 19999999999000006708\n  Bound result 180000000000000004808\n  Bound result 12\n  Bound result 4999999999000017294\n  Bound result 5505625188992501493\n  Bound result 12\n  Bound result 180000000000000000561\n  Bound result 12\n  Bound result 180000000000000008618\n  Bound result 12\n  Bound result 10470634972575724452\n  Bound result 52\n  Bound result 19999999999000014176\n  Bound result 12\n  Bound result 32405229419848556\n  Bound result 161484149837762997770\n  Bound result 12\n  Bound result 4999999999000007895\n  Bound result 6017502607293588406\n  Bound result 5235317486287862\n  Bound result 9999999000000000000126\n  Bound result 3920614805610362\n  Bound result 17715909253537394009\n  Bound result 12\n  Bound result 1273608279538289\n  Bound result 19999999999000015596\n  Bound result 4999999999000001801\n  Bound result 805801794754002874\n  Bound result 3398176614991109133\n  Bound result 180000000000000008339\n  Bound result 12\n  Bound result 180000000000000043129\n  Bound result 12\n  Bound result 12\n  Bound result 4999999999483266409\n  Bound result 12\n  Bound result 12\n  Bound result 10000000000000000\n  Bound result 19999999999000001900\n  Bound result 12\n  Bound result 112\n  Bound result 2011611609432741847\n  Bound result 12\n  Bound result 6670104912549950418\n  Bound result 19999999999000004049\n  Bound result 19999999999000000801\n  Bound result 12\n  Bound result 17830126982352984981\n  Bound result 63\n  Bound result 41094401139211\n  Bound result 513854677739483187\n  Bound result 39\n  Bound result 27612829230727045392\n  Bound result 12\n  Bound result 120\n  Bound result 1000178988994931336848\n  Bound result 191000000000000000001\n  Bound result 12\n  Bound result 21931300337370667194\n  Bound result 99999999994444444445\n  Bound result 12\n  Bound result 4503599627370496\n  Bound result 180000000000000026606\n  Bound result 12\n  Bound result 180000000000000000996\n  Bound result 12\n  Bound result 46649393329207593530\n  Bound result 12\n  Bound result 10000000000000000000\n  Bound result 32680281041836782903\n  Bound result 1000187988994931347281\n  Bound result 254000000000000000\n  Bound result 1812930352692670847190\n  Bound result 19999999999000106693\n  Bound result 4999999999001966081\n  Bound result 19999999999000094628\n  swaps: 43\n  moves: 23\n  Bound result 12\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.62s (3.61s CPU time)\n\nRan 27 test suites in 3.62s (12.15s CPU time): 210 tests passed, 0 failed, 0 skipped (210 total tests)\n","passed":true},{"durationMs":114,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDuelHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMDuelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDuelHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDuelHook.convert(address)\",\"IMDuelHook.convertBatch(address)\",\"IMDuelHook.fundChest((address,address,uint24,int24,address),bool,uint256)\",\"IMDuelHook.payOut((address,address,uint24,int24,address),address,address,uint256)\",\"IMDuelHook.sampleRoute(bytes,bytes[],bytes[])\",\"IMDuelHook.unlockCallback(bytes)\",\"IMDuelHook.withdraw(address,address)\",\"IMDuelHook.withdrawTo(address,address)\",\"IMDuelToken.approve(address,uint256)\",\"IMDuelToken.transfer(address,uint256)\",\"IMDuelToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":114,\"LICENSE\":16,\"foundry.toml\":29,\"launch.json\":39,\"launch/deployed.json\":12,\"launch/engine.json\":10,\"launch/hook.json\":39,\"launch/hook.sepolia.json\":39,\"script/LaunchPolicy.sol\":44,\"script/route_proof.py\":135,\"src/Bitboard.sol\":719,\"src/Chess.sol\":34,\"src/ChessEngineV5.sol\":852,\"src/IChessEngine.sol\":29,\"src/IMDuelHook.sol\":974,\"src/IMDuelToken.sol\":62,\"src/RoutePriceVerifier.sol\":50,\"src/Rules.sol\":45,\"src/Strength.sol\":47,\"src/proof/Bytes.sol\":144,\"src/proof/LICENSE\":22,\"src/proof/MerkleTrie.sol\":216,\"src/proof/PROVENANCE.md\":12,\"src/proof/RLPErrors.sol\":20,\"src/proof/RLPReader.sol\":249,\"test/Base.sol\":211,\"test/BidOwner.t.sol\":118,\"test/Bidding.t.sol\":224,\"test/Chessboard.sol\":79,\"test/CrossBlock.t.sol\":195,\"test/Draws.t.sol\":90,\"test/FactoryLaunch.t.sol\":173,\"test/FeeSchedule.t.sol\":327,\"test/Game.t.sol\":348,\"test/GasEstimate.t.sol\":120,\"test/Harness.sol\":83,\"test/HookGas.t.sol\":161,\"test/Hostile.t.sol\":535,\"test/Invariant.t.sol\":150,\"test/InvariantDuels.t.sol\":529,\"test/Launch.t.sol\":159,\"test/LaunchAdmission.t.sol\":167,\"test/LaunchFiles.t.sol\":75,\"test/Money.t.sol\":353,\"test/PayoutGuard.t.sol\":375,\"test/Predict.t.sol\":102,\"test/Proof_SyncRecipient.t.sol\":141,\"test/RoutePriceVerifier.t.sol\":135,\"test/RouteProof.sol\":69,\"test/RouteSample.t.sol\":168,\"test/Size.t.sol\":36,\"test/Slice.t.sol\":392,\"test/Strength.t.sol\":85,\"test/Teams.t.sol\":87,\"test/Token.t.sol\":82,\"test/TokenInvariant.t.sol\":155,\"test/Tokens.sol\":193,\"test/V4Fixture.sol\":100,\"test/data/chess.json\":1,\"test/data/conversion-validation.json\":770,\"test/data/conversion.json\":98,\"test/data/endings.json\":1,\"test/data/legal.json\":1,\"test/data/random.json\":1,\"test/data/route-proof.json\":16,\"test/test_route_proof.py\":72},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"fb55df38ff17832e5592550dd6a0eadeb29bcd6688def52870249e7f61bccc21","verifiedTreeHash":"3cb9a084fa2bc93562b03c8e89f882c0b0ec5589","verifierVersion":"0.1.0+d372f99e"},{"checks":[{"durationMs":81759,"exitCode":0,"name":"build","output":"Compiling 120 files with Solc 0.8.26\nSolc 0.8.26 finished in 81.63s\nCompiler run successful with warnings:\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:22:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                      ^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:108:38:\n    |\n108 |             rook = ((FILE_A << f) | (RANK_1 << (8 * r))) ^ bit;\n    |                                      ^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:109:57:\n    |\n109 |             uint64 d = f >= r ? DIAG >> (8 * (f - r)) : DIAG << (8 * (r - f));\n    |                                                         ^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/Bitboard.sol:110:65:\n    |\n110 |             uint64 a = f + r <= 7 ? ANTI >> (8 * (7 - f - r)) : ANTI << (8 * (f + r - 7));\n    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:38:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (3149): The result type of the shift operation is equal to the type of the first operand (uint64) ignoring the (larger) type of the second operand (uint256) which might be unexpected. Silence this warning by either converting the first or the second operand to the type of the other.\n   --> src/ChessEngineV5.sol:537:69:\n    |\n537 |             uint64 ahead = c.white ? ~uint64(0) << ((r + 1) * 8) : (uint64(1) << (r * 8)) - 1;\n    |                                                                     ^^^^^^^^^^^^^^^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:72:18:\n   |\n72 |         returns (uint16, Rules.Played memory)\n   |                  ^^^^^^\n\nWarning (6321): Unnamed return variable can remain unassigned. Add an explicit return with value to all non-reverting code paths or name the variable.\n  --> test/Hostile.t.sol:72:26:\n   |\n72 |         returns (uint16, Rules.Played memory)\n   |                          ^^^^^^^^^^^^^^^^^^^\n\nWarning (2072): Unused local variable.\n  --> test/Game.t.sol:90:9:\n   |\n90 |         IMDuelHook.Game memory g = st();\n   |         ^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2018): Function state mutability can be restricted to view\n  --> test/LaunchFiles.t.sol:70:5:\n   |\n70 |     function test_engineLaunchFileDeploysTheEngineWithoutArguments() public {\n   |     ^ (Relevant source part starts here and spans across multiple lines).\n\n","passed":true},{"durationMs":3497,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Size.t.sol:SizeTest\n[PASS] test_deployedCodeFitsTheBudgets() (gas: 1255245)\nLogs:\n  hook: 21322\n  engine v5: 23339\n  route price verifier: 5551\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.24ms (1.12ms CPU time)\n\nRan 4 tests for test/Token.t.sol:TokenTest\n[PASS] test_allowances() (gas: 347653)\n[PASS] test_fixedSupplyToTheDeployer() (gas: 39384)\n[PASS] test_nothingPrivileged() (gas: 192691)\n[PASS] test_transfers() (gas: 188240)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.33ms (581.95µs CPU time)\n\nRan 3 tests for test/LaunchFiles.t.sol:LaunchFilesTest\n[PASS] test_engineLaunchFileDeploysTheEngineWithoutArguments() (gas: 7834)\n[PASS] test_ethereumLaunchFile() (gas: 1334990)\n[PASS] test_sepoliaLaunchFile() (gas: 1330008)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 3.11ms (2.21ms CPU time)\n\nRan 1 test for test/Proof_SyncRecipient.t.sol:SyncRecipientTest\n[PASS] test_anEthPayingTradeThatPaysASyncingOwnerGoesThrough() (gas: 1161237)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.21ms (2.11ms CPU time)\n\nRan 5 tests for test/Teams.t.sol:TeamsTest\n[PASS] test_aDynamicFeePoolPlaysWithAZeroLpFee() (gas: 2916166)\n[PASS] test_nativeEthIsATeamLikeAnyToken() (gas: 2329765)\n[PASS] test_poolsOnOneHookKeepSeparateGamesAndChests() (gas: 3392679)\n[PASS] test_sliceSide() (gas: 1821742)\n[PASS] test_twoPlainTokensCannotDuel() (gas: 1593745)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 9.94ms (8.84ms CPU time)\n\nRan 7 tests for test/Draws.t.sol:DrawsTest\n[PASS] test_checkmate() (gas: 240028)\n[PASS] test_moveCap() (gas: 240069)\n[PASS] test_ordinaryMoveDoesNotEndTheGame() (gas: 180017)\n[PASS] test_quietMoveLimit() (gas: 246287)\n[PASS] test_stalemate() (gas: 237841)\n[PASS] test_thirdRepetition() (gas: 964226)\n[PASS] test_tooFewPiecesToMate() (gas: 231666)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 10.66ms (5.16ms CPU time)\n\nRan 2 tests for test/FactoryLaunch.t.sol:FactoryLaunchTest\n[PASS] test_factoryLaunchWithRealTokenAboveImd() (gas: 21676728)\n[PASS] test_factoryLaunchWithRealTokenBelowImd() (gas: 21641394)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 46.38ms (74.38ms CPU time)\n\nRan 8 tests for test/BidOwner.t.sol:BidOwnerTest\n[PASS] testFuzz_otherDataLengthsNeverCreditTheBundlerOrTheRouter(bytes) (runs: 256, μ: 383290, ~: 383288)\n[PASS] test_aHookWithoutAnEthRouteRejectsRouteProofs() (gas: 32534)\n[PASS] test_abiEncodedOwnerCreditsTheWallet() (gas: 384567)\n[PASS] test_abiEncodedOwnerReceivesTheCaptureBounty() (gas: 1128583)\n[PASS] test_dirtyAbiPaddingIsNobody() (gas: 382698)\n[PASS] test_missingOwnerIsNobodyNotTheRouterNorTheBundler() (gas: 415565)\n[PASS] test_missingOwnerPlaysButPaysNobody() (gas: 1151657)\n[PASS] test_packedOwnerCreditsTheWallet() (gas: 384686)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 47.83ms (50.91ms CPU time)\n\nRan 2 tests for test/LaunchAdmission.t.sol:LaunchAdmissionTest\n[PASS] test_blankChainTokenAboveImd() (gas: 62938925)\n[PASS] test_blankChainTokenBelowImd() (gas: 62867880)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 64.79ms (117.49ms CPU time)\n\nRan 12 tests for test/Bidding.t.sol:BiddingTest\n[PASS] test_aBountyIsPaidFromTheMoversChest() (gas: 1369426)\n[PASS] test_aBuyOfTheOtherTeamBidsForItsNextMove() (gas: 1952666)\n[PASS] test_aBuyOfTheOtherTeamTimesTheSilentSideOutAndBids() (gas: 1222504)\n[PASS] test_aClosedWindowTakesNoBidsForItsMove() (gas: 1062884)\n[PASS] test_aMoveIsOpenForTurnBlocksBlocks() (gas: 1040466)\n[PASS] test_aNewTopMustPayMoreThanTenPercentMore() (gas: 974355)\n[PASS] test_aNextMoveBidCarriesIntoTheNextGame() (gas: 1467024)\n[PASS] test_aWindowWithoutBidsBecomesTheFirstBidsBlock() (gas: 1283690)\n[PASS] test_bidsCountAcrossTheWholeWindow() (gas: 1511214)\n[PASS] test_theMoveDependsOnlyOnTheWindowsLastBlock() (gas: 1621426)\n[PASS] test_theMoversOwnBuyAfterItsWindowBidsForItsNextMove() (gas: 1077593)\n[PASS] test_thePreviewIsTheMoveThatIsPlayed() (gas: 1222868)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 66.68ms (27.10ms CPU time)\n\nRan 13 tests for test/RoutePriceVerifier.t.sol:RoutePriceVerifierTest\n[PASS] testFuzz_authenticatesSlot0AndItsBlock(uint160,uint96) (runs: 256, μ: 200948, ~: 200266)\nLogs:\n  Bound result 2964422583387036479856633470515551576907\n\n[PASS] test_changedAccountProofIsRefused() (gas: 100298)\n[PASS] test_changedHeaderIsNotCanonical() (gas: 80800)\n[PASS] test_changedStorageProofIsRefused() (gas: 158269)\n[PASS] test_clientGeneratedPoolManagerProof() (gas: 241704)\n[PASS] test_emptyProofsAreRefused() (gas: 195098)\n[PASS] test_extraProofNodesAreRefused() (gas: 195848)\n[PASS] test_outOfRangePriceIsRefused() (gas: 192143)\n[PASS] test_theBatchBlockCannotSupplyASample() (gas: 80854)\n[PASS] test_uninitializedPoolPriceIsRefused() (gas: 180136)\n[PASS] test_unknownOrExpiredHeaderIsRefused() (gas: 81188)\n[PASS] test_wrongManagerIsRefused() (gas: 108609)\n[PASS] test_wrongPoolSlotIsRefused() (gas: 165128)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 67.57ms (72.51ms CPU time)\n\nRan 3 tests for test/Launch.t.sol:LaunchTest\n[PASS] test_launchPairedWithEth() (gas: 23117220)\n[PASS] test_launchTokenAboveImd() (gas: 24067102)\nLogs:\n  first buy (opens game 0, bids for White): 366422\n\n[PASS] test_launchTokenBelowImd() (gas: 23992486)\nLogs:\n  first buy (opens game 0, bids for White): 361672\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 82.67ms (145.99ms CPU time)\n\nRan 8 tests for test/Strength.t.sol:StrengthTest\n[PASS] testFuzz_boundedAndMonotonic(uint256,uint256,uint256) (runs: 256, μ: 9090, ~: 8663)\nLogs:\n  Bound result 4608\n\n[PASS] testFuzz_depthMonotonic(uint256,uint256,uint256) (runs: 256, μ: 11990, ~: 12149)\nLogs:\n  Bound result 4608\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129635345\n\n[PASS] testFuzz_shiftKeepsTheShare(uint256,uint256,uint8) (runs: 256, μ: 14615, ~: 15266)\nLogs:\n  Bound result 10\n  Bound result 18\n  Bound result 4608\n\n[PASS] testFuzz_tenfoldIsAboutThirtyPoints(uint128,uint128) (runs: 256, μ: 13837, ~: 13724)\nLogs:\n  Bound result 50082718581281235812356958881899351066\n  Bound result 49615596080000199375330363197764209\n\n[PASS] testFuzz_unscalablePaidFollowsTheCurve(uint256,uint256) (runs: 256, μ: 13955, ~: 14208)\nLogs:\n  Bound result 115790931316427069117065304187595536131692059120702541412637166716107108937898\n  Bound result 115791918693118036427940688095549633652654453930919576576213642020245741097412\n\n[PASS] test_decidedPoints() (gas: 15577)\n[PASS] test_hugePaymentsDoNotRevert() (gas: 12854)\n[PASS] test_zeroInputs() (gas: 4493)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 82.77ms (52.20ms CPU time)\n\nRan 19 tests for test/Hostile.t.sol:HostileTest\n[PASS] test_aCallbackTokenOwnerLeavingADebtCannotFreezeTheDuel() (gas: 5286273)\n[PASS] test_aCallbackTokenOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 5622051)\n[PASS] test_aCallbackTokenOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 5641809)\n[PASS] test_aCallbackTokenOwnerSyncingTheOtherCurrencyCannotFreezeTheDuel() (gas: 5505007)\n[PASS] test_aCallbackTokenOwnerSyncingTheTokenCannotFreezeTheDuel() (gas: 5500324)\n[PASS] test_aDeadEngineLeavesTheTurnQueuedAndTradingGoesOn() (gas: 3557668)\n[PASS] test_aGasBurningEngineCostsOneExpensiveTradeNotTheGame() (gas: 9729914)\n[PASS] test_aTokenReturningNothingFundsAChest() (gas: 2188344)\n[PASS] test_anEthOwnerLeavingADebtCannotFreezeTheDuel() (gas: 4204480)\n[PASS] test_anEthOwnerSettlingForTheHookCannotFreezeTheDuel() (gas: 4491567)\n[PASS] test_anEthOwnerSettlingTheOtherCurrencyForTheHookCannotFreezeTheDuel() (gas: 4551486)\n[PASS] test_anEthOwnerSyncingEthWhenNothingIsSyncedIsPaid() (gas: 3336300)\n[PASS] test_anEthOwnerSyncingTheOtherCurrencyCannotFreezeTheDuel() (gas: 4380600)\n[PASS] test_anOwnerThatCannotReceiveWithdrawsElsewhere() (gas: 3563277)\n[PASS] test_blockedRecipientStaysOwedAndTradingContinues() (gas: 4194594)\n[PASS] test_brokenEngineFallsBackToALegalMove() (gas: 5153119)\n[PASS] test_feeOnTransferTokenFundsOnlyWhatArrives() (gas: 2734236)\n[PASS] test_reenteringTokenCannotBreakAPayout() (gas: 4736699)\n[PASS] test_starvingTheEngineNeverPlaysTheFallback() (gas: 25153217)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 82.78ms (139.20ms CPU time)\n\nRan 14 tests for test/Slice.t.sol:SliceTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5993365)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3973553)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3064481)\n[PASS] test_anEthBatchIsCapped() (gas: 5253758)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3203346)\n[PASS] test_anyoneCanConvert() (gas: 462007)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656457)\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4254448)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245420)\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764048)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487334)\n[PASS] test_noRouteNoDuel() (gas: 1030789)\n[PASS] test_oneEthBatchPerBlock() (gas: 5779111)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187164)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 92.84ms (67.56ms CPU time)\n\nRan 6 tests for test/RouteSample.t.sol:RouteSampleTest\n[PASS] test_authenticatedBlockStartsRejectTheSameAttack() (gas: 3422402)\n[PASS] test_callerSamplesCannotSellTheEthPileAtAPushedPrice() (gas: 1710709)\nLogs:\n  route attacker profit at the honest price: -87002115995172580\n\n[PASS] test_duplicateSampleCannotRotateOutHonestHistory() (gas: 493037)\n[PASS] test_gapRequiresThreeConsecutiveSamplesAgain() (gas: 1855898)\n[PASS] test_honestBatchRunsAndStopsAtTheHistoricalPriceBand() (gas: 1175548)\n[PASS] test_staleRoutePricesWaitWithoutLosingClaims() (gas: 1298197)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 92.90ms (24.06ms CPU time)\n\nRan 5 tests for test/HookGas.t.sol:HookGasTest\n[PASS] test_conversionGas() (gas: 3645312)\nLogs:\n  plain trade: 207740\n  trade that converts a batch: 271602\n\n[PASS] test_hookShareOfAMovingTrade() (gas: 29179960)\nLogs:\n  hook share of a moving trade: average: 280031\n  hook share of a moving trade: max: 333579\n\n[PASS] test_middlegameGas() (gas: 9538644)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 191975\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 562851\n\n[PASS] test_openingGas() (gas: 4542201)\nLogs:\n  first bid in a new pool: 270611\n  bidding trade: 192693\n  outbidding trade: 208071\n  plain trade: 182652\n  next block: play the turn and bid: 508018\n\n[PASS] test_runningGameGas() (gas: 54808626)\nLogs:\n  running game, moving trades measured: 50\n  running game, moving trade average: 454075\n  running game, moving trade max: 554311\n\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 92.88ms (142.04ms CPU time)\n\nRan 17 tests for test/CrossBlock.t.sol:CrossBlockTest\n[PASS] test_aBatchCannotBeSoldAtAPushedPrice() (gas: 5993860)\nLogs:\n  attacker profit (IMD, 1e18): -36380852262017773\n\n[PASS] test_aBatchStopsAtItsPriceLimit() (gas: 3973571)\n[PASS] test_aFailedConversionNeverBreaksATrade() (gas: 3064506)\n[PASS] test_anEthBatchIsCapped() (gas: 5253874)\n[PASS] test_anEthBatchWithAnEmptyRouteWaits() (gas: 3203497)\n[PASS] test_anyoneCanConvert() (gas: 462029)\n[PASS] test_booksBalanceAcrossPools() (gas: 8656485)\n[PASS] test_crossBlockViaConvert() (gas: 5557988)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390298682200\n\n[PASS] test_crossBlockViaFirstTrade() (gas: 5720092)\nLogs:\n  pile before (IMD, 1e18): 5002505331713439797\n  IMD pile spent in block N (1e18): 0\n  attacker profit (IMD at the home price, 1e18): -36513390291136661\n\n[PASS] test_ethBatchKeepsTheImdTheMainDuelCouldNotTake() (gas: 4254538)\n[PASS] test_ethDuelsGoThroughTheImdEthPool() (gas: 2245468)\n[PASS] test_ethRouteSameBlockPush() (gas: 5231826)\nLogs:\n  ETH pile before (1e18): 500000000000000000\n  ETH the batch sold (1e18): 0\n  IMD it got (1e18): 0\n  IMD it was worth at the home price (1e18): 0\n  attacker profit (ETH at the home price, 1e18): -29770639430607030\n\n[PASS] test_imduelDuelsNeedNoSwap() (gas: 764062)\n[PASS] test_mainDuelSliceBuysImduelHalfToTheChestHalfBurned() (gas: 487316)\n[PASS] test_noRouteNoDuel() (gas: 1030839)\n[PASS] test_oneEthBatchPerBlock() (gas: 5779205)\n[PASS] test_otherDuelsFeedTheMainChest() (gas: 1187178)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 92.91ms (68.85ms CPU time)\n\nRan 3 tests for test/GasEstimate.t.sol:GasEstimateTest\n[PASS] test_atTheEstimateADueBatchConverts() (gas: 4998592)\nLogs:\n  smallest gas limit that converts: 310375\n\n[PASS] test_atTheEstimateTheBountyIsPaidNotOwed() (gas: 4619259)\nLogs:\n  smallest gas limit that plays and pays: 417418\n\n[PASS] test_noGasLimitPlaysTheMoveAndLeavesItsBountyOwed() (gas: 19295099)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 140.61ms (166.21ms CPU time)\n\nRan 1 test for test/Predict.t.sol:PredictTest\n[PASS] test_aBotCannotPickItsOwnCapturingMoves() (gas: 289907162)\nLogs:\n  bot moves: 0\n  other moves: 74\n  captures among other moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 507.23ms (507.06ms CPU time)\n\nRan 23 tests for test/Game.t.sol:GameTest\n[PASS] testFuzz_swapsNeverRevert(uint256) (runs: 48, μ: 9533608, ~: 9539772)\n[PASS] test_aGamePlaysOutThroughSwaps() (gas: 134592260)\nLogs:\n  moves played: 187\n  bids (incl. outbid ones): 238\n  most gas for a swap: 685293\n\n[PASS] test_aSmallerTradeInTheBlockDoesNotTakeTheTurn() (gas: 686491)\n[PASS] test_aTimeoutTurnCanBeTakenInItsBlock() (gas: 1304210)\n[PASS] test_aTradeBidsForTheTurn() (gas: 483494)\n[PASS] test_aTurnClosesWithItsBlock() (gas: 965170)\n[PASS] test_anExpiredTurnBecomesATimeoutTurn() (gas: 1410536)\n[PASS] test_anOldTurnIsPlayedWithTheHistoryContractsHash() (gas: 1754873)\n[PASS] test_anyTradeSizeBids() (gas: 450095)\n[PASS] test_lateTradeStillBidsNormally() (gas: 429729)\n[PASS] test_onlyThePoolManagerAndTheHookCanCallIn() (gas: 171212)\n[PASS] test_ownerDefaultsToNobody() (gas: 449725)\n[PASS] test_playedPositionsEnterTheRepetitionMemory() (gas: 915368)\n[PASS] test_rejectsBadConfig() (gas: 9247)\n[PASS] test_silentSidePlaysAtZeroStrength() (gas: 1045239)\n[PASS] test_strengthFollowsTheShareOfThePool() (gas: 6374541)\n[PASS] test_teamsSwapColoursEveryGame() (gas: 926880)\n[PASS] test_theBiggestTradeInTheBlockOwnsTheTurn() (gas: 782415)\n[PASS] test_theFirstPoolOpenedIsTheMainDuel() (gas: 179471)\n[PASS] test_theMainDuelMustPairImd() (gas: 1468317)\n[PASS] test_theNextBlocksTradePlaysTheTurn() (gas: 1038495)\n[PASS] test_theOtherTeamCannotBidOnThisTurn() (gas: 412278)\n[PASS] test_whoPlaysTheTurnAndWhenDoesNotChangeTheMove() (gas: 1534194)\nSuite result: ok. 23 passed; 0 failed; 0 skipped; finished in 708.35ms (944.51ms CPU time)\n\nRan 21 tests for test/Money.t.sol:MoneyTest\n[PASS] testFuzz_heldEqualsOwed(uint256) (runs: 48, μ: 10710677, ~: 10726288)\n[PASS] test_aFullChestTakesNoMore() (gas: 1059339)\n[PASS] test_aTradeStoppedByItsPriceLimitIsRefused() (gas: 582903)\n[PASS] test_blackCaptureIsPaidInItsTeamToken() (gas: 786454)\n[PASS] test_bountyTable() (gas: 44705)\n[PASS] test_checkmatePaysAQuarterOfTheChest() (gas: 766298)\n[PASS] test_chestShareGoesToTheChestWorthLess() (gas: 638456)\n[PASS] test_chestsStayAboutEqualInValue() (gas: 5511474)\n[PASS] test_exactOutputPaysTheFeeOnTheInput() (gas: 384979)\n[PASS] test_exactOutputWithTheChestShareInTheOutput() (gas: 780877)\n[PASS] test_failedPayoutStaysOwedAndTheTradeGoesThrough() (gas: 1219882)\n[PASS] test_feesCannotBeWithdrawnByAnyone() (gas: 449988)\n[PASS] test_firstTradeFundsChest0AndTheSlice() (gas: 363816)\n[PASS] test_fundChestNeedsTheRightValue() (gas: 48803)\n[PASS] test_fundChestOnlyForHookPools() (gas: 41868)\n[PASS] test_fundChestWithEth() (gas: 252918)\n[PASS] test_fundChestWithTokens() (gas: 193816)\n[PASS] test_openingSurchargeOnlyInTheMainDuel() (gas: 3469491)\n[PASS] test_payoutIsCappedAtTwiceTheTrade() (gas: 833810)\n[PASS] test_quietMovePaysNothing() (gas: 616203)\n[PASS] test_whiteCaptureIsPaidInItsTeamTokenFromItsOwnChest() (gas: 860122)\nSuite result: ok. 21 passed; 0 failed; 0 skipped; finished in 773.26ms (792.59ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:InvariantTest\n[PASS]\nInvariantTest invariants:\n[PASS] invariant_boardHasBothKingsAndPlyIsBounded\n[PASS] invariant_heldEqualsOwed\n InvariantTest invariants (runs: 24, calls: 2400, reverts: 0)\n\n╭----------+----------------+-------+---------+----------╮\n| Contract | Selector       | Calls | Reverts | Discards |\n+========================================================+\n| Handler  | convert        | 301   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | fund           | 305   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | longPause      | 311   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | nextBlock      | 313   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | trade          | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeAgain     | 277   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | tradeNextBlock | 312   | 0       | 0        |\n|----------+----------------+-------+---------+----------|\n| Handler  | withdrawReward | 304   | 0       | 0        |\n╰----------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 40\n  Bound result 19999999000000007339\n  Bound result 19999999000001000001\n  Bound result 19999999000000008367\n  Bound result 19999999000000000581\n  Bound result 102\n  Bound result 18046385208871800035\n  Bound result 2855\n  Bound result 19999999007306351798\n  Bound result 100000000000000000\n  Bound result 31865262234567526\n  Bound result 49999999999950000\n  Bound result 19999999000000002681\n  Bound result 19999999000000003076\n  Bound result 5000\n  Bound result 42\n  Bound result 25333134452981760\n  Bound result 19999999000000000091\n  Bound result 18446744073709551615\n  Bound result 84\n  Bound result 4022291159582972627\n  Bound result 6\n  Bound result 423\n  Bound result 19999999000000200001\n  Bound result 35\n  Bound result 386549022720\n  Bound result 1100\n  Bound result 19999999000000000019\n  Bound result 1221339734816628308\n  Bound result 58\n  Bound result 115\n  Bound result 120\n  Bound result 107\n  Bound result 100\n  Bound result 120\n  Bound result 19999999000000000955\n  Bound result 19999999000000005018\n  Bound result 55\n  Bound result 1000000000003\n  Bound result 4413685840969725471\n  Bound result 1251\n  Bound result 34\n  Bound result 19999999000000000031\n  Bound result 19999999000000000904\n  Bound result 19999999000000003946\n  Bound result 15672660589582542013\n  Bound result 20396340581799926600\n  Bound result 13289\n  Bound result 19999999000000000321\n  Bound result 19999999000000004313\n  Bound result 12566133904435631808\n  Bound result 15386197539150620393\n  Bound result 2\n  Bound result 13\n  Bound result 8911052841178915333\n  Bound result 19999999000000003503\n  Bound result 25333134452981760\n  Bound result 4999999999995\n  Bound result 5060859166916801136\n  Bound result 100000\n  Bound result 86\n  swaps: 33\n  moves: 17\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.04s (1.03s CPU time)\n\nRan 1 test for test/InvariantDuels.t.sol:InvariantDuelsTest\n[PASS]\nInvariantDuelsTest invariants:\n[PASS] invariant_everyBoardIsSound\n[PASS] invariant_everyCurrencyBalances\n[PASS] invariant_rightsMatchTheBoard\n[PASS] invariant_turnsAreWellFormed\n InvariantDuelsTest invariants (runs: 24, calls: 2880, reverts: 0)\n\n╭--------------+-------------------+-------+---------+----------╮\n| Contract     | Selector          | Calls | Reverts | Discards |\n+===============================================================+\n| DuelsHandler | addLiquidity      | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convert           | 220   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | convertNextBlock  | 200   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | fund              | 241   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | longPause         | 221   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | nextBlock         | 215   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | pushThenNextBlock | 208   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | removeLiquidity   | 253   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | roundTrip         | 251   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | trade             | 232   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeAgain        | 198   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | tradeNextBlock    | 223   | 0       | 0        |\n|--------------+-------------------+-------+---------+----------|\n| DuelsHandler | withdrawReward    | 210   | 0       | 0        |\n╰--------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 22905\n  Bound result 2565653098084874863\n  Bound result 12\n  Bound result 100000000\n  Bound result 72624976668147840\n  Bound result 4113140425\n  Bound result 12\n  Bound result 19999999999000000049\n  Bound result 4485934425771658475\n  Bound result 80\n  Bound result 12\n  Bound result 19999999999000000053\n  Bound result 12\n  Bound result 19999999999000011329\n  Bound result 19999999999000057006\n  Bound result 300\n  Bound result 19999999999000017333\n  Bound result 19999999999000230001\n  Bound result 12\n  Bound result 19999999999000000064\n  Bound result 4000000000000000\n  Bound result 25333134452981760\n  Bound result 133294633079153423663\n  Bound result 12\n  Bound result 19999999999000016320\n  Bound result 12\n  Bound result 19999999999000065474\n  Bound result 12\n  Bound result 19999999999000015212\n  Bound result 12\n  Bound result 19999999999000006709\n  Bound result 180000000000000004982\n  Bound result 12\n  Bound result 4999999999000017295\n  Bound result 5505625188992501493\n  Bound result 12\n  Bound result 180000000000000000420\n  Bound result 12\n  Bound result 180000000000000008618\n  Bound result 12\n  Bound result 18349840678526191566\n  Bound result 52\n  Bound result 19999999999000014066\n  Bound result 12\n  Bound result 19999999999000018427\n  Bound result 180000000000000800001\n  Bound result 12\n  Bound result 4999999999000007895\n  Bound result 6017502607293588406\n  Bound result 340769609819090518684\n  Bound result 9999999899999990000000\n  Bound result 237231844378604801142\n  Bound result 17715909253537394009\n  Bound result 12\n  Bound result 500000000000000000000\n  Bound result 19999999999000015724\n  Bound result 900000001999999998\n  Bound result 805801794754002874\n  Bound result 4999999999000000601\n  Bound result 180000000000000008104\n  Bound result 12\n  Bound result 180000000000000043129\n  Bound result 12\n  Bound result 12\n  Bound result 4999999999483266410\n  Bound result 12\n  Bound result 12\n  Bound result 100000000000000000000\n  Bound result 19999999999373836275\n  Bound result 12\n  Bound result 104\n  Bound result 4999999999001966081\n  Bound result 12\n  Bound result 19999999996000002\n  Bound result 19999999999000004049\n  Bound result 19999999999000065281\n  Bound result 12\n  Bound result 17830126982352984981\n  Bound result 94\n  Bound result 14581426942671790513\n  Bound result 120\n  Bound result 27612829230727045392\n  Bound result 12\n  Bound result 40\n  Bound result 1135331256181516065\n  Bound result 1000178988994931336848\n  Bound result 180000000000000887271\n  Bound result 12\n  Bound result 21931300337370667194\n  Bound result 189000000000000000001\n  Bound result 12\n  Bound result 112\n  Bound result 180000000000000024306\n  Bound result 12\n  Bound result 180000000000000010000\n  Bound result 12\n  Bound result 46649393329207593530\n  Bound result 12\n  Bound result 3000\n  Bound result 32680281041836782903\n  Bound result 1100177988994931344355\n  Bound result 19999999999000000996\n  Bound result 1812930352692670847190\n  Bound result 19999999999000002541\n  Bound result 2036944743885723607\n  Bound result 19999999999000094628\n  swaps: 43\n  moves: 25\n  Bound result 12\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n  Bound result 1000000000000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.33s (3.32s CPU time)\n\nRan 24 test suites in 3.33s (7.44s CPU time): 180 tests passed, 0 failed, 0 skipped (180 total tests)\n","passed":true},{"durationMs":83,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDuelHook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMDuelHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMDuelHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMDuelHook.convert(address)\",\"IMDuelHook.convertBatch(address)\",\"IMDuelHook.fundChest((address,address,uint24,int24,address),bool,uint256)\",\"IMDuelHook.payOut((address,address,uint24,int24,address),address,address,uint256)\",\"IMDuelHook.sampleRoute(bytes,bytes[],bytes[])\",\"IMDuelHook.unlockCallback(bytes)\",\"IMDuelHook.withdraw(address,address)\",\"IMDuelHook.withdrawTo(address,address)\",\"IMDuelToken.approve(address,uint256)\",\"IMDuelToken.transfer(address,uint256)\",\"IMDuelToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":114,\"LICENSE\":16,\"foundry.toml\":29,\"launch.json\":39,\"launch/deployed.json\":12,\"launch/engine.json\":10,\"launch/hook.json\":39,\"launch/hook.sepolia.json\":39,\"script/LaunchPolicy.sol\":44,\"script/route_proof.py\":135,\"src/Bitboard.sol\":719,\"src/Chess.sol\":34,\"src/ChessEngineV5.sol\":852,\"src/IChessEngine.sol\":29,\"src/IMDuelHook.sol\":974,\"src/IMDuelToken.sol\":62,\"src/RoutePriceVerifier.sol\":50,\"src/Rules.sol\":45,\"src/Strength.sol\":47,\"src/proof/Bytes.sol\":144,\"src/proof/LICENSE\":22,\"src/proof/MerkleTrie.sol\":216,\"src/proof/PROVENANCE.md\":12,\"src/proof/RLPErrors.sol\":20,\"src/proof/RLPReader.sol\":249,\"test/Base.sol\":211,\"test/BidOwner.t.sol\":118,\"test/Bidding.t.sol\":224,\"test/Chessboard.sol\":79,\"test/CrossBlock.t.sol\":195,\"test/Draws.t.sol\":90,\"test/FactoryLaunch.t.sol\":173,\"test/Game.t.sol\":348,\"test/GasEstimate.t.sol\":120,\"test/Harness.sol\":83,\"test/HookGas.t.sol\":161,\"test/Hostile.t.sol\":535,\"test/Invariant.t.sol\":150,\"test/InvariantDuels.t.sol\":529,\"test/Launch.t.sol\":159,\"test/LaunchAdmission.t.sol\":167,\"test/LaunchFiles.t.sol\":75,\"test/Money.t.sol\":353,\"test/Predict.t.sol\":102,\"test/Proof_SyncRecipient.t.sol\":141,\"test/RoutePriceVerifier.t.sol\":135,\"test/RouteProof.sol\":69,\"test/RouteSample.t.sol\":168,\"test/Size.t.sol\":36,\"test/Slice.t.sol\":392,\"test/Strength.t.sol\":85,\"test/Teams.t.sol\":87,\"test/Token.t.sol\":82,\"test/Tokens.sol\":193,\"test/V4Fixture.sol\":100,\"test/data/chess.json\":1,\"test/data/conversion-validation.json\":770,\"test/data/conversion.json\":98,\"test/data/endings.json\":1,\"test/data/legal.json\":1,\"test/data/random.json\":1,\"test/data/route-proof.json\":16,\"test/test_route_proof.py\":72},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":20471,"exitCode":0,"name":"slither","output":"[high/medium] incorrect-exp at src/IMDuelHook.sol:256: IMDuelHook.teamOf(uint8,uint32,bool) (src/IMDuelHook.sol#256-258) has bitwise-xor operator ^ instead of the exponentiation operator **: \n[high/medium] incorrect-exp at src/IMDuelHook.sol:256: IMDuelHook.teamOf(uint8,uint32,bool) (src/IMDuelHook.sol#256-258) has bitwise-xor operator ^ instead of the exponentiation operator **: \n[medium/high] incorrect-equality at src/IMDuelHook.sol:585: IMDuelHook._reference(uint256,bool) (src/IMDuelHook.sol#585-591) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMDuelHook.sol:574: IMDuelHook._sampleAt(uint256,uint160,uint256) (src/IMDuelHook.sol#574-580) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMDuelHook.sol:335: IMDuelHook._takeFee(PoolId,PoolKey,uint256,uint256,uint256,IMDuelHook.Game) (src/IMDuelHook.sol#335-347) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:511: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#511-534):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:305: Reentrancy in IMDuelHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDuelHook.sol#305-330):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:546: Reentrancy in IMDuelHook._swapClaims(PoolKey,Currency,uint256,bool) (src/IMDuelHook.sol#546-567):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:511: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#511-534):\n[medium/medium] reentrancy-no-eth at src/IMDuelHook.sol:629: Reentrancy in IMDuelHook._play(PoolId,PoolKey,IMDuelHook.Game,bool,BalanceDelta,address,uint160,uint256) (src/IMDuelHook.sol#629-668):\n[medium/medium] uninitialized-local at src/Bitboard.sol:643: Bitboard._targets(Bitboard.Pos,Bitboard.Gen,uint8,bool).ep (src/Bitboard.sol#643) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:480: Bitboard._anyLegal(Bitboard.Pos,bool).scratch (src/Bitboard.sol#480) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:382: IMDuelHook._pay(PoolId,IMDuelHook.Game,PoolKey,Currency,address,uint256).paid (src/IMDuelHook.sol#382) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:596: IMDuelHook._pack(uint160).e (src/IMDuelHook.sol#596) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:425: Bitboard.isLegal(Bitboard.Pos,uint16,bool).scratch (src/Bitboard.sol#425) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:497: ChessEngineV5._queening(ChessEngineV5.Ctx,Bitboard.Pos,uint64,uint64,uint64,uint64,uint64).a (src/ChessEngineV5.sol#497) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:182: ChessEngineV5._choose(ChessEngineV5.Ctx,uint256,uint256,uint64[8],uint32,uint16).seen (src/ChessEngineV5.sol#182) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:400: Bitboard._legalTargets(Bitboard.Pos,Bitboard.Gen,Bitboard.Pos,uint8,bool).ep (src/Bitboard.sol#400) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:236: Bitboard.pieceAttacks(Bitboard.Pos,uint64,uint64,bool).a (src/Bitboard.sol#236) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:203: ChessEngineV5._choose(ChessEngineV5.Ctx,uint256,uint256,uint64[8],uint32,uint16).refined (src/ChessEngineV5.sol#203) is a local variable never initialized\n[medium/medium] uninitialized-local at src/Bitboard.sol:580: Bitboard.mateInOne(Bitboard.Pos,bool,uint256).scratch (src/Bitboard.sol#580) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:883: IMDuelHook._repeated(PoolId,IMDuelHook.Game).seen (src/IMDuelHook.sol#883) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:414: ChessEngineV5._placement(ChessEngineV5.Ctx,uint8,uint8,uint8,uint8,bool,bool).near (src/ChessEngineV5.sol#414) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:276: ChessEngineV5._context(uint256,uint32,uint32,bool,bool).nonPawn (src/ChessEngineV5.sol#276) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:816: IMDuelHook._apply(PoolId,IMDuelHook.Game,uint16,Rules.Played,IMDuelHook.Trade).result (src/IMDuelHook.sol#816) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ChessEngineV5.sol:377: ChessEngineV5._cheap(ChessEngineV5.Ctx,uint16).squeeze (src/ChessEngineV5.sol#377) is a local variable never initialized\n[medium/medium] uninitialized-local at src/IMDuelHook.sol:820: IMDuelHook._apply(PoolId,IMDuelHook.Game,uint16,Rules.Played,IMDuelHook.Trade).reason (src/IMDuelHook.sol#820) is a local variable never initialized\n[medium/medium] unused-return at src/IMDuelHook.sol:433: IMDuelHook._withdraw(address,Currency,address) (src/IMDuelHook.sol#433-439) ignores return value by manager.unlock(abi.encode(uint8(0),abi.encode(to,c,amount))) (src/IMDuelHook.sol#437)\n[medium/medium] unused-return at src/IMDuelHook.sol:546: IMDuelHook._swapClaims(PoolKey,Currency,uint256,bool) (src/IMDuelHook.sol#546-567) ignores return value by (sqrtPrice,None,None,None) = manager.getSlot0(key.toId()) (src/IMDuelHook.sol#551)\n[medium/medium] unused-return at src/IMDuelHook.sol:288: IMDuelHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/IMDuelHook.sol#288-303) ignores return value by (sqrtPrice,None,None,None) = manager.getSlot0(id) (src/IMDuelHook.sol#295)\n[medium/medium] unused-return at src/IMDuelHook.sol:454: IMDuelHook.convert(Currency) (src/IMDuelHook.sol#454-456) ignores return value by manager.unlock(abi.encode(uint8(2),abi.encode(address(0),c,uint256(0)))) (src/IMDuelHook.sol#455)\n[low/high] shadowing-local at src/ChessEngineV5.sol:817: ChessEngineV5._mop(ChessEngineV5.Ctx,uint8,uint8,uint8,bool,bool).at (src/ChessEngineV5.sol#705) shadows:\n[low/medium] missing-zero-check at src/IMDuelHook.sol:476: IMDuelHook.unlockCallback(bytes).who (src/IMDuelHook.sol#476) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:335: Reentrancy in IMDuelHook._takeFee(PoolId,PoolKey,uint256,uint256,uint256,IMDuelHook.Game) (src/IMDuelHook.sol#335-347):\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:511: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#511-534):\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:443: Reentrancy in IMDuelHook.fundChest(PoolKey,bool,uint256) (src/IMDuelHook.sol#443-451):\n[low/medium] reentrancy-benign at src/IMDuelHook.sol:381: Reentrancy in IMDuelHook._pay(PoolId,IMDuelHook.Game,PoolKey,Currency,address,uint256) (src/IMDuelHook.sol#381-389):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:629: Reentrancy in IMDuelHook._play(PoolId,PoolKey,IMDuelHook.Game,bool,BalanceDelta,address,uint160,uint256) (src/IMDuelHook.sol#629-668):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:381: Reentrancy in IMDuelHook._pay(PoolId,IMDuelHook.Game,PoolKey,Currency,address,uint256) (src/IMDuelHook.sol#381-389):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:443: Reentrancy in IMDuelHook.fundChest(PoolKey,bool,uint256) (src/IMDuelHook.sol#443-451):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:511: Reentrancy in IMDuelHook._convert(Currency) (src/IMDuelHook.sol#511-534):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:433: Reentrancy in IMDuelHook._withdraw(address,Currency,address) (src/IMDuelHook.sol#433-439):\n[low/medium] reentrancy-events at src/IMDuelHook.sol:305: Reentrancy in IMDuelHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/IMDuelHook.sol#305-330):\n[low/medium] return-bomb at src/IMDuelHook.sol:780: IMDuelHook._engine(uint256,bytes) (src/IMDuelHook.sol#780-786) tries to limit the gas of an external call that controls implicit decoding\n[low/medium] timestamp at src/IMDuelHook.sol:443: IMDuelHook.fundChest(PoolKey,bool,uint256) (src/IMDuelHook.sol#443-451) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:335: IMDuelHook._takeFee(PoolId,PoolKey,uint256,uint256,uint256,IMDuelHook.Game) (src/IMDuelHook.sol#335-347) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:351: IMDuelHook._chestBps(IMDuelHook.Game) (src/IMDuelHook.sol#351-354) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:511: IMDuelHook._convert(Currency) (src/IMDuelHook.sol#511-534) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:365: IMDuelHook._addChest(PoolId,uint256,uint256) (src/IMDuelHook.sol#365-372) uses timestamp for comparisons\n[low/medium] timestamp at src/IMDuelHook.sol:629: IMDuelHook._play(PoolId,PoolKey,IMDuelHook.Game,bool,BalanceDelta,address,uint160,uint256) (src/IMDuelHook.sol#629-668) uses timestamp for comparisons","passed":true},{"durationMs":948,"exitCode":0,"name":"aderyn","output":"[high] contract-locks-ether at src/IMDuelHook.sol:44: Contract locks Ether without a withdraw function\n[high] incorrect-caret-operator at src/IMDuelHook.sol:257: Incorrect use of caret operator\n[high] incorrect-shift-order at src/proof/RLPReader.sol:164: Incorrect Assembly Shift Parameter Order (3 places)\n[high] reentrancy-state-change at src/IMDuelHook.sol:465: Reentrancy: State change after external call\n[high] unsafe-casting at src/Bitboard.sol:247: Unsafe Casting of integers (2 places)\n[low] empty-require-revert at src/ChessEngineV5.sol:157: Empty `require()` / `revert()` Statement\n[low] internal-function-used-once at src/Bitboard.sol:199: Internal Function Used Only Once (13 places)\n[low] large-numeric-literal at src/ChessEngineV5.sol:34: Large Numeric Literal (27 places)\n[low] literal-instead-of-constant at src/Bitboard.sol:35: Literal Instead of Constant (424 places)\n[low] missing-inheritance at src/IMDuelToken.sol:7: Missing Inheritance\n[low] push-zero-opcode at src/Bitboard.sol:2: PUSH0 Opcode (13 places)\n[low] require-revert-in-loop at src/proof/MerkleTrie.sol:73: Loop Contains `require`/`revert` (3 places)\n[low] state-change-without-event at src/IMDuelHook.sol:288: State Change Without Event\n[low] unchecked-return at src/IMDuelHook.sol:437: Unchecked Return (3 places)\n[low] uninitialized-local-variable at src/ChessEngineV5.sol:164: Uninitialized Local Variable (4 places)\n[low] unsafe-erc20-operation at src/IMDuelHook.sol:492: Unsafe ERC20 Operation\n[low] unspecific-solidity-pragma at src/ChessEngineV5.sol:2: Unspecific Solidity Pragma (6 places)\n[low] unused-state-variable at src/IMDuelHook.sol:76: Unused State Variable (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"fc2c47a2077162dae61f731d8abc875a907acacfd76683483d41b93f41fd7584","verifiedTreeHash":"a5b1b26aafa5f76e8f69c595a1ddd99a2f033f23","verifierVersion":"0.1.0+d372f99e"}]}