{"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":"e42d648c-704c-4822-9b9d-25660d83dd66","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"1174023706157d97fbd2b4a826721e55fe816c87683859ec08799f26dcab105e","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"0649f78a411b6f9abd4c344c70dd395742cd3ea5b7f0fbf388ba4b063d3654d4","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"614956d96a764807a6b80195c4572b60541bc413fc3a885880e99cfffcec8c22","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","tools":[]},"key":"audit_imported_code","kind":"code","role":"review","skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","state":"accepted"},{"acceptedSubmissionHash":"3c20f70ccb3ace0196ec6c0aad66b76d53d2d4ee2f57ba9649e6a422bf93816a","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"3d9e78db08eb679ec269e1d3e3db2e3f93c1b7be30c03040289366b38001d998","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"af66c7a7c21d7d649887cd7cc19c6fa010aa8b1c4cffc7865ed7d102ce4ca394","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"888d7f8553e676ab3f95d6650389f4a8685d431765cfae0ed2e0954eee1f7ab5","dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"6b8d2c2ebd3301cff2be8edb1e1cc6ba65c858c02fe7247be81722b969842265","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"6b8d2c2ebd3301cff2be8edb1e1cc6ba65c858c02fe7247be81722b969842265","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"639eb3525a57b1bf9bd98e85f3352515abecea0a95798d44940785d5d24352b6","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"837811e791469239c165ae41d8d3778b7748497ff8bbf452a69fcc1cd2056b78","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Launch IMD Money Back ($MONEYBACK) on Robinhood Chain, paired with IMD, from this repository exactly as it is at the pinned commit. The contracts were written, tested and reviewed by the swarm (jobs 57052175 and cb7160d0) and the pinned commit already contains the fee-base fix and two-phase manifest produced in launch job d6360666 (build revision 1, all checks passed). TWO-PHASE DEPLOYMENT IS AUTHORIZED: the factory deploys only MoneyBackToken and MoneyBackHook (the univ4_hook schema deploys nothing else); RoundPayout and MoneyBackRouter are deployed afterwards by the requester with script/DeployAuxiliaries.s.sol. Do not treat their absence from the launch as a finding. Do not change fee parameters: hook fee 4.25% of the IMD leg on every swap in both directions, sell-only surcharge 2000 bps decaying to 0 over 1800 s, sweep() permissionless to the immutable payout which is the paying wallet ($owner). Keep the fees in IMD; never sell them for ETH. Deploy MoneyBackToken and MoneyBackHook (flags 0x20CC); hook payout = the paying wallet. Nobody can change anything after deployment except RoundPayout via its owner. Supply: 10% to the swarm seats (factory rule); of the requester 90%, poolBps 9000 (the maximum) seeds the pool single-sided and the remaining 10% of it is sent to the dead address, so 81% of supply is in the pool, 9% is burned and the team holds nothing. LOGO: the token mark is one open umbrella, canopy and hooked handle, in warm yellow (#FFC83D), centred and large, on a flat navy background (#17356B) that fills the whole square. Nothing else: no text, no rain, no frame, no gradient. The project is Money Back Mutual, a fun insurance-themed memecoin; website https://imdmoneyback.xyz; X https://x.com/IMDmoneyback.","parentJobId":null,"planHash":"d7beb9647c3e4828f192c74b3d6cac32166be0135b57859be2e18aca7952eead","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"e42d648c-704c-4822-9b9d-25660d83dd66","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1316-src-moneybacktoken-sol-src-moneybackhook"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51557","feedbackHash":"a1e413e34d986e60c23466a36df1f527c22355db154a06283eac04fbc2145f9f","nodeKey":"audit_economics","submissionHash":"1174023706157d97fbd2b4a826721e55fe816c87683859ec08799f26dcab105e","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52262","feedbackHash":"cd95cf4e1be1baad5e21f012a3cca27a111afad4d3bcc2e9b5a3edcb13e65c73","nodeKey":"audit_flow","submissionHash":"0649f78a411b6f9abd4c344c70dd395742cd3ea5b7f0fbf388ba4b063d3654d4","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52207","feedbackHash":"c982a03478510f4616ca8b627fc9cf8e551ed7dacee4ad2a39722dae3dbe9a58","nodeKey":"audit_imported_code","submissionHash":"614956d96a764807a6b80195c4572b60541bc413fc3a885880e99cfffcec8c22","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52193","feedbackHash":"6227a781922ca9a3793ffcfb9f32d1dba9f6b5f616cc651bae717510875c9ad2","nodeKey":"audit_judge","submissionHash":"3c20f70ccb3ace0196ec6c0aad66b76d53d2d4ee2f57ba9649e6a422bf93816a","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52165","feedbackHash":"9f1d0e91907a56e711f712ca92485cc39e37c24353fed14b1bfd71169e7f9b24","nodeKey":"audit_math","submissionHash":"3d9e78db08eb679ec269e1d3e3db2e3f93c1b7be30c03040289366b38001d998","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51423","feedbackHash":"7808be73d44a6dc85fefa093e7b14466b1a02424d9794ec920e50576ddae0a45","nodeKey":"audit_permissions","submissionHash":"af66c7a7c21d7d649887cd7cc19c6fa010aa8b1c4cffc7865ed7d102ce4ca394","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52468","feedbackHash":"f1bdda7dbc4d24988a1f0ba11d44d7f61ba17baa31d0f70f1b41f4dcf87a1389","nodeKey":"build_contract_project","submissionHash":"888d7f8553e676ab3f95d6650389f4a8685d431765cfae0ed2e0954eee1f7ab5","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51300","feedbackHash":"323d84f090e51af985d9d63a0f716b8f9c27e6cf20e935451b39ef73362da5a5","nodeKey":"manifest","submissionHash":"639eb3525a57b1bf9bd98e85f3352515abecea0a95798d44940785d5d24352b6","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"50904","feedbackHash":"33bf91dc148f41036425c6a09620700a2ffcc6ee92650692a13b7a8daaa0af6a","nodeKey":"write_foundry_tests","submissionHash":"837811e791469239c165ae41d8d3778b7748497ff8bbf452a69fcc1cd2056b78","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"c9a1dd02755d2c1f263298a60e4e2f560cccf8490cac4223f174446ae7c3cd7b","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"28e346843ec15530","findings":[],"hash":"0649f78a411b6f9abd4c344c70dd395742cd3ea5b7f0fbf388ba4b063d3654d4","nodeId":"aa96dcb1-f7cf-4d2b-b148-0e74a70e1611","outcome":"completed","summary":"The review is complete. The findings file is written and the tree contains no other changes.\n\n**Outcome: no substantiated defect in the Execution Trace, Periphery or First Principles area.** The file `.imd-findings.json` holds an empty findings list and a coverage record with a row for every one of the 24 listed entry points plus five invariant rows, all marked `holds`.\n\n**What I verified, beyond reading:**\n\n- **Hook swap paths on the real v4-core PoolManager.** The delivered suite already covers all four swap forms, partial-fill rollback, decay boundaries and both currency orders. I added scratch tests for paths it did not touch: a swapper paying input and hook fee entirely with ERC-6909 claims followed by a sweep that still delivers real IMD, two swaps of different forms inside one unlock, and a sweep attempted from inside another unlock. All passed, and the scratch file has been removed.\n- **Vendored v4-core honesty.** A whitespace-insensitive diff against the upstream checkout at the pinned commit shows only formatting differences and the documented solmate import-path change, so the integration evidence rests on real pool math.\n- **Router settlement.** The sync, transfer, settle sequence, the digest-gated callback, the quote revert-and-catch, and the refund paths net every delta to zero and keep user funds attributable to the caller.\n- **Manifest and brief.** Constructor argument order, permissions, fee tier, tick spacing, lowercase paired currency, token metadata and notes length all match the implementation. The PNG and SVG logo match the brief exactly.\n- **RoundPayout and token** were traced outside my area with no severe defect found. Owner powers there are the documented trust assumption.\n\n**Items I deliberately did not report:** the unbound-hook initialization window and the single-sided LP surcharge bypass are already recorded imported findings answered by design, and the sub-wei rounding on dust-sized exact-output sells stays inside the contract's own stated two-wei bound.","treeHash":null,"usage":{"cachedInputTokens":2360754,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":55281,"runtime":"claude","turns":31,"wallClockMs":772219}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"559cfaaab2c0d013","findings":[{"citation":"resolved","description":"Invariant guide, 'capacity constraints / bypass cap enforcement on secondary paths'. The contract's own invariant (header lines 27-31 and the NatSpec at 209-214) says failed legs are 'held in failed(roundId, to) until retried or written off' and that 'a batch that starts always has enough to pay every leg'. The balance check in payRound (line 233) and in retryFailed (line 314, `_requireBalance(token, needed)`) only compares against the raw token balance; nothing tracks the sum of outstanding `failed[roundId][to]` debts. A second round whose sum fits in balance-including-debts passes the check and transfers the tokens that were being 'held' for the earlier failed recipient; `sweep()` (line 198) does the same with no check at all. The debt stays recorded, `failedCount` stays non-zero, and `retryFailed` for that recipient reverts `InsufficientBalance(owed, 0)` until the owner tops up. Only the owner can trigger it, and the owner can always fund more, so this is a bookkeeping gap for the documented retry workflow rather than a fund-loss path: no holder loses tokens, and `sum(Paid) <= sum(received)` still holds. Reported because the header states a stronger guarantee than the code enforces; an operator who relies on `failed()` as a funded liability will pay a round that silently consumes it.","line":233,"path":"src/RoundPayout.sol","reproduction":"State: RoundPayout owned by O, token T (any ERC-20 whose transfer reverts for recipient A, e.g. a blacklist token), RoundPayout holds 100 T. Calls by O: (1) payRound(1, T, [A, B], [60, 40], 0, 0, 0) -> A's leg fails and is recorded failed(1,A)=60, B is paid 40, round 1 marked paid, contract balance 60 (the amount the header says is 'held' for A). (2) payRound(2, T, [B], [60], 0, 0, 0) -> `_requireBalance(T, 60)` passes against the 60 held for A; B is paid 60; balance 0. (3) A is unblocked; retryFailed(1, [A]) -> reverts InsufficientBalance(60, 0); failed(1,A) is still 60 and rounds(1).failedCount is still 1. Expected per the stated invariant: step (2) refuses (or reserves) the 60 owed to A, so step (3) pays A. Actual: step (2) succeeds and step (3) reverts. Verified with a scratch Foundry test against src/RoundPayout.sol as it is (test passes, i.e. the behaviour above is what the code does).","severity":"low","snippet":"        _requireBalance(token, _sum(amounts));","title":"RoundPayout does not reserve failed-leg debts: a later payRound or sweep spends them and retryFailed then reverts InsufficientBalance"},{"citation":"resolved","description":"Economic Security guide, 'legitimate features turned against the protocol' and Flow Gap seam periphery x first principles. The brief's economics ('4.25% of the IMD leg on every swap in both directions' and a 20% sell surcharge decaying over 1800 s) are enforced only by MoneyBackHook, and the hook is consulted only for the single PoolKey it binds in beforeInitialize (fee 12500, spacing 60, hooks = this). MoneyBackToken is a plain ERC-20 with no transfer hook (src/MoneyBackToken.sol line 12). PoolManager.initialize is permissionless, so any address can initialize a second MONEYBACK/IMD pool with hooks = address(0) (or any other hook) and any fee tier; swaps and liquidity there never touch this hook, so no base fee, no surcharge and nothing to sweep. Sells of the 10% swarm allocation during the surcharge window can therefore route through such a pool paying only its LP fee, and the paying wallet collects nothing on that volume. This is inherent to a hook-only fee design with a plain token and is partly acknowledged in README 'Scope of the surcharge' (which names LP conversion, OTC and 'other pools'); the previous round reported the LP-conversion variant (e1b55a37, low). Recorded here, with a reproduction against the real PoolManager, so the requester weighs it explicitly: it bounds the surcharge's economic effect to whatever liquidity sits in the hooked pool, and no change within the fixed flags 0x20CC can close it (closing it would need a token-level transfer restriction, which the brief does not ask for). No code change is proposed.","line":473,"path":"src/MoneyBackHook.sol","reproduction":"State: real v4 PoolManager, MoneyBackToken, MoneyBackHook deployed at a 0x20CC address and bound to {MONEYBACK, IMD, 12500, 60, hook} at t = initializedAt (currentSurchargeBps() == 2000). Calls: (1) from any stranger: PoolManager.initialize({MONEYBACK, IMD, fee 3000, tickSpacing 60, hooks 0x0}, 2^96) -> succeeds (the hook is not called; AlreadyBound protects only keys whose hooks field is this hook). (2) any LP: modifyLiquidity on that hookless key, e.g. 1e24 liquidity over ticks [-600, 600]. (3) a seller: swap on the hookless key, exactInput 100e18 MONEYBACK, widest price limit -> receives about 99.7e18 IMD (only the 0.3% LP fee). Expected under the brief: 24.25% of the IMD leg (about 24.2e18 IMD) accrues to hook.pending() for sweep() to the paying wallet. Actual: hook.pending() == 0 and the seller keeps the full output. Verified with a scratch Foundry test using test/vendor/v4-core PoolManager: received > 99e18 IMD and hook.pending() == 0 while hook.currentSurchargeBps() == 2000.","severity":"info","snippet":"    function _onlyBoundPool(PoolKey calldata key) private view {\n        if (initializedAt == 0 || keccak256(abi.encode(key)) != PoolId.unwrap(poolId)) revert NotBoundPool();\n    }","title":"Hook fee and sell surcharge apply only to swaps in the one bound pool; anyone can open a hookless MONEYBACK/IMD pool and trade there fee-free inside the 1800 s window"}],"hash":"1174023706157d97fbd2b4a826721e55fe816c87683859ec08799f26dcab105e","nodeId":"85af1b23-249b-425b-b4a8-f8dc90045192","outcome":"completed","summary":"The review is complete and `.imd-findings.json` is written with both findings and a full coverage record. The working tree is clean apart from that file.\n\n**Outcome: no critical, high or medium defect in the economics.** The hook's fee accounting is correct in all four swap forms against the real vendored v4 PoolManager, the claims-based fee custody is fully backed, sweep only ever pays the immutable paying wallet, and the router nets its IMD and ETH deltas to zero on every path. Build and the existing 239 tests pass.\n\nTwo substantiated findings, both reproduced with scratch tests against the code as it is:\n\n- **Low, RoundPayout.** Failed-leg debts are not reserved. After a leg fails, a later `payRound` or `sweep` passes the balance check against the tokens the header says are \"held\" for retry. The retry then reverts with insufficient balance. Only the owner can trigger it and no holder loses funds, so it is a bookkeeping gap against the contract's own stated invariant, not a loss path.\n- **Info, MoneyBackHook.** The fee and surcharge apply only to the one bound pool. Anyone can initialize a hookless MONEYBACK/IMD pool and sell there during the surcharge window paying only that pool's LP fee. This is inherent to a hook-only fee on a plain token, is partly acknowledged in the README, and cannot be closed within the fixed flags. Recorded so the requester weighs it explicitly.\n\nCoverage: all 24 listed entry points have a row. Twenty-two hold, two point at finding 1. I added four invariant rows and one honest `unreached` note. The one thing I could not verify from this tree is which opening price the deployer actually uses, since the manifest carries the 2^96 policy literal while the README says the launch service derives the 2,500 IMD valuation price. The hook accepts either.","treeHash":null,"usage":{"cachedInputTokens":1462053,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":38813,"runtime":"claude","turns":30,"wallClockMs":574149}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"5d667e4b0751bcb5","findings":[{"citation":"resolved","description":"_payLegs (and retryFailed) treat any transfer call that returns true as a transferred leg. A standard ERC-20 transfer whose recipient is the sender (to[i] == address(this)) returns true and moves nothing: MoneyBackToken._update subtracts and re-adds the same balance. The same balance is therefore counted as paid again in every later round, _requireBalance passes each time because the balance never decreases, and the header invariant 'sum of all Paid amounts of a token <= sum of everything ever received in that token' (line 33) is false. Only the trusted owner can trigger it and no tokens leave, so this is a bookkeeping defect for the off-chain engine that reconciles against Paid/RoundPaid, not a loss. Merged from audit_math (single report). Minimal fix: in payRound (and the retry precheck) revert when to[i] == address(this).","line":256,"path":"src/RoundPayout.sol","reproduction":"State: RoundPayout(owner=O); O approves and calls fund(MONEYBACK, 100e18). Calls by O: payRound(1, MONEYBACK, [address(payout)], [100e18], 0, 0, 0) -> returns 100e18, emits Paid(1, payout, 100e18), rounds(1).totalPaid == 100e18, balanceOf(payout) still 100e18. payRound(2, MONEYBACK, [address(payout)], [100e18], 0, 0, 0) -> also returns 100e18. Expected: second round refused or totalPaid reflects tokens actually distributed. Actual: rounds(1).totalPaid + rounds(2).totalPaid == 200e18 while the contract only ever received 100e18 and still holds all of it. Reproduced with a scratch Foundry test (test_selfRecipientDoubleCountsPaid) on this tree: both calls succeed and the final balance is 100e18.","severity":"low","snippet":"            (bool ok, bytes memory reason) = _tryTransfer(token, recipient, amount);","title":"RoundPayout: a leg paid to the RoundPayout contract itself counts as Paid without any token leaving, so the Paid ledger over-reports and the documented conservation invariant breaks"},{"citation":"resolved","description":"The header (lines 27-31) says failed legs are 'held in failed(roundId, to) until retried or written off'. The balance check in payRound (line 233) and in retryFailed (line 314) compares only against the raw token balance; nothing tracks the sum of outstanding failed[roundId][to] debts, and sweep() (line 198) has no check at all. A later round whose sum fits the balance including those debts passes the check and transfers the tokens recorded as owed to the earlier failed recipient; the debt stays recorded, failedCount stays non-zero, and retryFailed reverts InsufficientBalance until more funding arrives. Owner-only, no holder loses tokens and sum(Paid) <= sum(received) still holds, so this is an operational gap in the documented retry workflow. Merged from audit_economics (single report). Minimal fix: keep a per-token reserved total of outstanding failed debts and check balance - reserved in payRound and sweep, or document that failed debts are not reserved.","line":233,"path":"src/RoundPayout.sol","reproduction":"State: RoundPayout(owner=O), token T whose transfer reverts for recipient A (blacklist-style), RoundPayout holds 100 T. Calls by O: (1) payRound(1, T, [A, B], [60, 40], 0, 0, 0) -> A's leg fails, failed(1,A)=60, B paid 40, balance 60. (2) payRound(2, T, [B], [60], 0, 0, 0) -> _requireBalance(T, 60) passes against the 60 recorded as owed to A; B paid 60; balance 0. (3) A unblocked; retryFailed(1, [A]) -> reverts InsufficientBalance(60, 0); failed(1,A) still 60, rounds(1).failedCount still 1. Expected per the stated invariant: step (2) refuses or reserves the 60 owed to A so step (3) pays A. Actual: step (2) succeeds and step (3) reverts. Reproduced with a scratch Foundry test (test_failedDebtNotReserved) on this tree.","severity":"low","snippet":"        _requireBalance(token, _sum(amounts));","title":"RoundPayout does not reserve failed-leg debts: a later payRound or sweep spends the balance held for a failed recipient and retryFailed then reverts InsufficientBalance until the owner tops up"},{"citation":"resolved","description":"MoneyBackToken is a plain ERC-20 with no transfer hook, and the hook is consulted only for the PoolKey it binds in beforeInitialize. PoolManager.initialize is permissionless, so anyone can open a second MONEYBACK/IMD pool with hooks = address(0) at any fee tier; swaps there never touch this hook. Flags 0x20CC carry no liquidity callbacks, so a holder can also post MONEYBACK-only liquidity in the bound pool, let buyers trade through it and withdraw IMD without a sell. Both are the same root cause: the brief's economics are a fee on swaps through this pool, not a token-level tax. The README ('Scope of the surcharge') and docs/LAUNCH_REVIEW.md item 1 already document it and the suite tests the LP variant (test_liquidityConversionIsOutsideSwapSurcharge). Merged from audit_economics (hookless pool) and the earlier-round finding e1b55a37 (LP conversion). Closing it would need token-level transfer restrictions or liquidity callbacks, both outside the fixed flags and fee parameters the brief forbids changing; no code change is proposed. Recorded so the requester weighs the bound on the surcharge's effect explicitly.","line":474,"path":"src/MoneyBackHook.sol","reproduction":"State: real v4 PoolManager, MoneyBackToken, MoneyBackHook at a 0x20CC address bound to {MONEYBACK, IMD, 12500, 60, hook} at t = initializedAt (currentSurchargeBps() == 2000). Calls: (1) any stranger: PoolManager.initialize({MONEYBACK, IMD, fee 3000, tickSpacing 60, hooks 0x0}, 2^96) -> succeeds, the hook is not called. (2) any LP: modifyLiquidity on that key, 1e24 liquidity over ticks [-600, 600]. (3) a seller: exactInput 100e18 MONEYBACK with the widest price limit -> receives more than 99e18 IMD (only the 0.3% LP fee). Expected under the brief: about 24.25% of the IMD leg accrues to hook.pending(). Actual: hook.pending() == 0 and currentSurchargeBps() == 2000. Reproduced with a scratch Foundry test (test_hooklessPoolBypassesFee) against test/vendor/v4-core PoolManager on this tree.","severity":"info","snippet":"        if (initializedAt == 0 || keccak256(abi.encode(key)) != PoolId.unwrap(poolId)) revert NotBoundPool();","title":"Hook fee and sell surcharge apply only to swaps through the one bound pool: a hookless MONEYBACK/IMD pool, a single-sided LP position or an OTC transfer moves MONEYBACK into IMD fee-free inside the 18"},{"citation":"resolved","description":"The sender argument is unused and there is no $factory constructor argument, so the only guard on who opens the pool is that the call comes from the PoolManager. The key is fully constrained (fee 12500, spacing 60, {MONEYBACK, IMD}, this hook); the only things an outsider could choose are sqrtPriceX96 and the moment the 1800 s surcharge clock starts. Under the launch flow the factory deploys the hook and calls PoolManager.initialize in the same transaction, so no unbound-hook state exists on chain (a revert in initialize reverts the hook deployment too). The repository documents this (docs/LAUNCH_REVIEW.md item 3, README 'Deployment sequence') and tests it. Adding $factory would change the constructor signature, mined address and manifest. Kept from audit_permissions as the one residual operational rule: never deploy MoneyBackHook without initializing its pool in the same transaction, and never redeploy it. No code change requested.","line":360,"path":"src/MoneyBackHook.sol","reproduction":"State: MoneyBackHook deployed at a 0x20CC address with PoolManager.initialize not yet called (does not occur under the launch flow). Input: any EOA calls PoolManager.initialize({MONEYBACK, IMD, 12500, 60, hook}, TickMath.MIN_SQRT_PRICE + 1). Actual: hook.initializedAt is set, poolId bound, surcharge clock started, pool open at the attacker's price; the factory's later initialize reverts AlreadyBound. Expected under a $factory-gated design: a WrongSender revert. Reproduced by running the tree's own test_unboundHookIsBoundByWhoeverInitializesFirst and test_boundPoolRefusesAnySecondInitialize (both pass, i.e. the behaviour above is what the code does).","severity":"info","snippet":"    function beforeInitialize(address, PoolKey calldata key, uint160) external returns (bytes4) {","title":"beforeInitialize ignores the initializing sender: an unbound hook is bound by whoever initializes first; closed only by the factory's atomic deploy-and-initialize"}],"hash":"3c20f70ccb3ace0196ec6c0aad66b76d53d2d4ee2f57ba9649e6a422bf93816a","nodeId":"8e5f234b-6566-4917-9dd3-06fee665595a","outcome":"completed","summary":"The review is complete. The findings file is written at the repository root and the working tree is otherwise unchanged (the scratch tests were removed after running).\n\n**Outcome: no blocking defects.** The launch code deploys under the factory model and behaves as the brief specifies. All four specialist findings reproduced against the code, none rose above low, and my own pass found nothing they missed.\n\n**What I verified**\n\n- The full existing suite passes, 253 tests across 10 suites, including the real vendored v4-core PoolManager suites in both currency orders.\n- The hook's fee accounting matches v4-core's actual delta flow: `afterSwap` receives the pool-only delta, hook deltas are credited after the callback, and the inverted fee for IMD-specified swaps cannot trip the manager's exceeds-swap-amount check.\n- The manifest matches the implementation: three constructor arguments in order, permissions equal to the declared flags 0x20CC, fee 12500 accepted by `beforeInitialize`, paired currency equal to the hook's IMD constant, notes under the 4,000-character limit.\n- Contract sizes are well inside the limits, and an opcode scan of all four compiled runtimes finds no DELEGATECALL, CALLCODE or SELFDESTRUCT, so the protected hook and token floors pass.\n- The logo is a single yellow umbrella with a hooked handle on a flat navy square, 1024 by 1024, nothing else.\n\n**Findings kept, with reproductions run**\n\n1. Low. RoundPayout counts a leg paid to itself as Paid without tokens leaving, breaking the stated conservation invariant. Owner-only bookkeeping.\n2. Low. RoundPayout does not reserve failed-leg debts, so a later round or sweep can spend them and `retryFailed` reverts until the owner tops up. Owner-only, no loss.\n3. Info. Fees apply only to swaps through the bound pool. A hookless pool, a single-sided LP position or an OTC transfer bypasses the surcharge. Merged the hookless-pool and LP-conversion reports into one root cause. Documented in the repo and inherent to the fixed flags.\n4. Info. `beforeInitialize` ignores the sender, so an unbound hook could be bound by anyone. Closed by the factory's atomic deploy-and-initialize.\n\n**Coverage.** All 24 entry points are answered, plus three invariant rows. One row is marked unreached: the manifest's initial price is parity while the docs say the deployer derives the opening price from the 2,500 IMD policy valuation, and that deployer behaviour cannot be checked from this tree.","treeHash":null,"usage":{"cachedInputTokens":2059019,"inputTokens":546,"model":"claude-fable-5-1","outputTokens":39849,"runtime":"claude","turns":28,"wallClockMs":618169}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"e12f98dda6acc55f","findings":[{"citation":"resolved","description":"Boundary x invariant seam. The contract header states the invariant 'sum of all Paid amounts of a token <= sum of everything ever received in that token' and that totalPaid 'counts only legs that actually transferred'. _payLegs (and retryFailed) treat any transfer call that returns true as a transferred leg. A standard ERC-20 transfer whose recipient is the sender (to[i] == address(this)) returns true and moves nothing, so the same balance is counted as paid again in every later round. The up-front _requireBalance check passes each time because the balance never decreases. Nobody loses funds (the owner is trusted and the tokens stay in the contract), but the on-chain ledger (rounds(id).totalPaid, Paid events, RoundPaid.totalPaid) that the off-chain engine reconciles against over-reports distributions without bound, and the stated invariant is false. Minimal fix: in payRound (and the retry precheck) revert when to[i] == address(this); optionally also when to[i] == token.","line":256,"path":"src/RoundPayout.sol","reproduction":"Deploy RoundPayout(owner); fund it with 100e18 IMD (fund(token, 100e18)). Call payRound(1, token, [address(payout)], [100e18], 0x0, 0, 0): returns 100e18, emits Paid(1, payout, 100e18), rounds(1).totalPaid == 100e18, but token.balanceOf(payout) is still 100e18. Call payRound(2, token, [address(payout)], [100e18], 0x0, 0, 0): also succeeds and returns 100e18. Now rounds(1).totalPaid + rounds(2).totalPaid == 200e18 while the contract has only ever received 100e18 and still holds all of it. Expected: the second round (or both) to be refused, or totalPaid to reflect tokens actually distributed. Verified with a scratch Foundry test (test_selfRecipientDoubleCountsPaid) against the tree as it is.","severity":"low","snippet":"            (bool ok, bytes memory reason) = _tryTransfer(token, recipient, amount);\n            if (ok) {\n                anySucceeded = true;\n                totalPaid += amount;\n                emit Paid(roundId, recipient, amount);","title":"RoundPayout: a leg whose recipient is the RoundPayout contract itself counts as paid without any token leaving, breaking the documented Paid-conservation invariant"}],"hash":"3d9e78db08eb679ec269e1d3e3db2e3f93c1b7be30c03040289366b38001d998","nodeId":"1f55ace0-0926-4651-b2d3-741698a18d0b","outcome":"completed","summary":"The review is complete. The findings file is written at `.imd-findings.json` with one finding and 29 coverage rows, and the scratch tests are removed.\n\n**Outcome: the fee math holds.** I traced every division, cast and inversion in the hook and router against the Math Precision, Boundary and Numerical Gap guides, then exercised them on the real vendored v4 PoolManager in both currency orders. Nothing in my area reaches high or critical severity, so no proof file was needed.\n\n**What I verified empirically**\n\n- Tiny amounts in all four swap forms, at every surcharge boundary (0, 1, 899, 1799, 1800, 5000 s): the inverted buy fee stays within 1 wei and the inverted sell fee within 2 wei of the floored pool-leg fee, and sells never under-charge the gross leg.\n- A protocol fee enabled on the pool does not disturb the full-fill check for specified-IMD swaps.\n- Repeated trade and sweep cycles deliver exactly the claim balance to the immutable payout.\n- Int128 guards, the sign extraction from packed deltas, the surcharge formula, and the router's dust subtractions cannot underflow or overflow for any reachable input.\n\n**The one finding (low)**\n\nRoundPayout counts a leg as paid whenever the transfer returns true. A recipient equal to the RoundPayout contract itself satisfies that without moving tokens, so the same balance is reported as distributed in every later round. The reproduction in the file funds 100 IMD and pays it \"twice\" across two rounds while the contract still holds all 100. No funds are lost, but the documented conservation invariant and the ledger the off-chain engine reconciles against are wrong. The suggested fix is to reject a recipient equal to the contract address.\n\n**Not reported as defects**\n\n- Fee truncation to zero below 24 wei of IMD leg is real but worth less than the gas to exploit.\n- The manifest's parity initial price versus the 2,500 IMD valuation is already documented as resolved by the deployer's derivation in the launch review.","treeHash":null,"usage":{"cachedInputTokens":1796860,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":37141,"runtime":"claude","turns":28,"wallClockMs":496230}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f009de0ce52c7b3a","findings":[{"citation":"resolved","description":"The hook charges its 4.25% base fee and the decaying sell surcharge only inside beforeSwap/afterSwap. getHookPermissions enables no liquidity callbacks, so modifyLiquidity on the bound pool never touches the hook. During the 1800 s anti-dump window any holder (including the 10% swarm-seat recipients) can place MONEYBACK as a single-sided range just above the current price, wait for buyers to trade through it, and remove the position as pure IMD. That is economically a sell at the surcharge-era price, yet it pays no base fee and no surcharge; only the buyers who filled it paid the hook. The README (\"Scope of the surcharge\") and docs/REVISION.md acknowledge this limit, and the brief fixes the flags at 0x20CC, so this is reported for the requester's and adapter's awareness rather than as a change request: closing it would require beforeAddLiquidity/beforeRemoveLiquidity permissions and a different mined address. Verified against the vendored real v4-core PoolManager (test/vendor), both currency orders are covered by the repository's own test_liquidityConversionIsOutsideSwapSurcharge.","line":303,"path":"src/MoneyBackHook.sol","reproduction":"Real PoolManager, pool initialized at sqrtPrice 2^96 (tick 0) at time T0, factory-style single-sided seed of 1e24 liquidity in [0,600] (MONEYBACK = currency0 case). (1) Holder calls modifyLiquidity(key, {tickLower 60, tickUpper 120, liquidityDelta 1e24}) at T0: pool pulls 2,986,382,804,598,881,661,260 wei MONEYBACK and 0 IMD; hook.pending() unchanged. (2) An unrelated buyer swaps exact-input 12,000e18 IMD (zeroForOne=false): hook accrues 489,208,633,093,525,179,856 wei IMD from the BUYER (12000e18*425/10425). (3) Holder calls modifyLiquidity with liquidityDelta -1e24 at T0+1 s: receives 3,051,524,259,317,710,955,470 wei IMD and 0 MONEYBACK; hook.pending() is still exactly the buyer's 489.2e18; currentSurchargeBps() is still 2000. Expected under the brief's guarantee: converting 2,986e18 MONEYBACK into IMD inside the window carries 24.25% of the IMD leg (about 740e18 IMD on this leg: an exact-input sell of the same tokens at T0 is charged floor(P*425/1e4)+floor(P*2000/1e4)). Actual: 0 hook fee and 0 surcharge on the holder's conversion.","severity":"low","snippet":"            beforeAddLiquidity: false,","title":"Sell surcharge and base fee are bypassed by converting MONEYBACK to IMD through a single-sided LP position (flags 0x20CC carry no liquidity callbacks)"},{"citation":"resolved","description":"pool.initialPrice is the sqrtPriceX96 the deployer verifies against the signed manifest and initializes the pool with. 79228162514264337593543950336 is exactly 2^96, i.e. 1 MONEYBACK = 1 IMD regardless of currency ordering, which values the 1e9 supply at 1e9 IMD. README.md (\"Opening valuation policy: 2,500 IMD for the whole supply\", \"0.0000025 IMD per MONEYBACK\") and the manifest notes claim the deployer will instead derive the opening price from a 2,500 IMD valuation and ignore this field. The notes are not deployment authority and the reference states initialPrice is a real pool field, so if the deployer honours the manifest the pool opens 400,000x above the documented valuation with 81% of supply offered single-sided at >= 1 IMD each; if it does not, the manifest misdescribes the pool that gets created. The adapter should pick one and make the manifest and docs agree. For a 2,500 IMD whole-supply valuation (2.5e-6 IMD per MONEYBACK) the sqrtPriceX96 is about 125,269,000,000,000,000,000,000,000 (1.2527e26) when MONEYBACK is currency0, or about 50,107,000,000,000,000,000,000,000,000,000 (5.0107e31) when MONEYBACK is currency1; the correct one depends on the mined token address ordering against IMD 0x5f7b...7127. Not a code defect; verified only that the manifest value produces parity trades.","line":28,"path":"launch.json","reproduction":"Real PoolManager, MoneyBackToken deployed, MoneyBackHook deployed at a 0x20CC address, PoolManager.initialize({MONEYBACK, IMD, 12500, 60, hook}, 79228162514264337593543950336) succeeds (PoolBound, tick 0), single-sided seed of MONEYBACK above the price. An exact-input buy of 1e18 IMD returns 947,241,308,968,064,646 wei MONEYBACK (1 IMD buys about 0.947 MONEYBACK after the 4.25% hook fee and 1.25% LP fee). Under the README's stated valuation the same 1 IMD should buy on the order of 3.8e23 wei MONEYBACK (about 380,000 tokens before fees). Expected: manifest and documentation describe the same opening price; actual: they differ by a factor of about 4e5.","severity":"low","snippet":"    \"initialPrice\": \"79228162514264337593543950336\"","title":"launch.json initialPrice is parity (2^96) while README and notes state a 2,500 IMD whole-supply opening valuation; one of the two is wrong for the deployer"},{"citation":"resolved","description":"beforeInitialize correctly restricts msg.sender to the PoolManager, binds exactly one pool, and validates currencies, fee 12500, tickSpacing 60 and the hook address, but it ignores the `sender` argument and the sqrtPriceX96. Whoever calls PoolManager.initialize first with the correct key binds the pool at a price of their choosing and starts the 1800 s surcharge clock. In the IMD launch flow the factory deploys the hook and calls initialize in the same transaction, so there is no window; this only matters if the hook were ever deployed (or redeployed at the mined address) without immediately initializing. Taking the factory as a `$factory` constructor argument and checking `sender == factory` would close it at the cost of one more constructor argument in launch.json. No change is required for this launch; recorded so the adapter does not split deployment and initialization across transactions.","line":360,"path":"src/MoneyBackHook.sol","reproduction":"Deploy MoneyBackHook(manager, token, payout) at a 0x20CC address in transaction 1 and do not initialize. In transaction 2 any EOA calls PoolManager.initialize({token, IMD, 12500, 60, hook}, 4295128740) (MIN_SQRT_PRICE+1). Actual: beforeInitialize returns its selector, PoolBound is emitted with the attacker's price and initializedAt = now; the factory's later initialize for the same key reverts in the PoolManager with PoolAlreadyInitialized, and any other key reverts in the hook with AlreadyBound. Expected (if a factory check existed): the EOA's call reverts and only the factory can open the pool.","severity":"info","snippet":"    function beforeInitialize(address, PoolKey calldata key, uint160) external returns (bytes4) {","title":"beforeInitialize does not check the initializing sender; the hook relies on the factory deploying and initializing in one transaction"},{"citation":"resolved","description":"READ IN FULL at the pinned commit: src/MoneyBackHook.sol, src/MoneyBackToken.sol, src/RoundPayout.sol, src/MoneyBackRouter.sol, script/DeployAuxiliaries.s.sol, launch.json, foundry.toml, README.md, docs/REVISION.md, docs/network-observations.json, docs/requester-share.json, the delivered tests (MoneyBackHook.t.sol, MoneyBackRealV4.t.sol, MoneyBackRouter.t.sol, RoundPayout.t.sol, MoneyBackToken.t.sol, DeployAuxiliaries.t.sol) and the vendored v4-core PoolManager/Hooks the real-v4 tests use. The two protected floor tests and both skill references were read and the code checked against them. CHECKS: forge test passes 231/231 locally; a PUSH-aware scan of the four compiled runtimes (6478/1575/6979/5566 bytes, all under EIP-170) finds no DELEGATECALL, CALLCODE or SELFDESTRUCT; no proxy, initializer, mint-after-construction, pause, blacklist or balance-seizure path exists in the token or hook; the hook has no owner or setter and sweep() can only move IMD to the immutable payout; every hook callback and the unlock callback refuse callers other than the PoolManager; launch.json constructorArgs ($poolManager,$token,$owner) match the constructor in declaration order, permissions match getHookPermissions (0x20CC), pool.fee 12500 is the only fee beforeInitialize accepts, pairedCurrency is lowercase, notes are 3355 chars. Against the real vendored PoolManager I additionally verified: all four swap forms settle with hook and swapper deltas netting to zero and claims backed by IMD the swapper settled; a protocol fee set on the pool does not break the full-fill (PartialFill) check in either IMD-specified form; a fresh single-sided pool with no IMD in the manager can take its first buy and later sweep; the inverse-fee rounding stays within the documented 1/2 wei; swap amounts of 24 wei or less pay zero fee (dust, not exploitable at any gas price). RoundPayout's owner powers (choose recipients, sweep while paused, pause, renounce) are the ones the brief authorises and are reported as design, not defects. NOT REACHED: the live IMD token bytecode on Robinhood Chain (only its keccak from docs/network-observations.json; fee-on-transfer, pause or blacklist behaviour of IMD would affect sweep() and the router but cannot be ruled out offline), the live PoolManager and public ETH/IMD pool (router constants match the pinned read-only observation at block 85912608 only), and the launch factory and MerkleDistributor sources that implement the 10%/81%/9% split and single-sided seeding. No network was used; no file outside .imd-findings.json and the removed test/scratch was created or changed.","line":194,"path":"src/MoneyBackHook.sol","reproduction":"Not a defect. Commands run from the repository root: `forge test` (231 passed, 0 failed, 0 skipped); `forge build --sizes`; scratch tests under test/scratch (removed) against test/vendor/v4-core PoolManager with the pool initialized at 2^96, fee 12500, tickSpacing 60, protocol fee 1000|1000<<12 for the protocol-fee case.","severity":"info","snippet":"contract MoneyBackHook is IHooks, IUnlockCallback {","title":"Audit scope: contracts read, checks performed, and what could not be reached"}],"hash":"614956d96a764807a6b80195c4572b60541bc413fc3a885880e99cfffcec8c22","nodeId":"8f4347bc-3f53-43fc-88b7-0bb6ef2f17c2","outcome":"completed","summary":"The review is complete. The findings file is written and validated, the scratch directory is removed, and the working tree holds no other changes.\n\n**Outcome: no critical, high or medium defects in the four contracts as they arrived.** The hook's fee accounting holds against the real vendored v4 PoolManager in all four swap forms, including with a protocol fee set, on a fresh single-sided pool with no IMD in the manager, and at the rounding edges. All callbacks refuse non-PoolManager callers, nothing in the token or hook can mint, pause, freeze, seize or upgrade, the compiled runtimes contain no DELEGATECALL, CALLCODE or SELFDESTRUCT, and the manifest's constructor arguments, permissions, fee tier and paired currency agree with the code. The project's own 231 tests pass.\n\nWhat I recorded in `.imd-findings.json`:\n\n- **Low: surcharge bypass via single-sided LP.** With flags 0x20CC the hook never sees liquidity changes, so a holder can park MONEYBACK just above the price, let buyers fill it, and withdraw IMD during the 30-minute window paying zero base fee and zero surcharge. Reproduced with concrete amounts on the real PoolManager. The README already acknowledges it and the brief fixes the flags, so this is for awareness rather than a change request.\n- **Low: opening price contradiction.** The manifest's initialPrice is exactly 2^96 (parity, 1e9 IMD whole-supply valuation), while the README and notes claim a 2,500 IMD valuation that the deployer will derive instead. One of the two is wrong for the deployer. The adapter should settle which, and I gave the sqrtPriceX96 for the documented valuation in both currency orderings.\n- **Info: beforeInitialize ignores the initializing sender.** Safe in the factory's atomic deploy-and-initialize flow, noted so deployment is never split across transactions.\n- **Info: scope statement.** Lists every contract, test and document read, the checks run, and what could not be reached offline: the live IMD token bytecode, the live PoolManager and public ETH/IMD pool, and the factory and MerkleDistributor sources that implement the supply split.\n\nNothing further is pending on my side. The adapter's only decision points are the opening-price inconsistency and whether the requester accepts the documented LP bypass of the surcharge.","treeHash":null,"usage":{"cachedInputTokens":2236810,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":47770,"runtime":"claude","turns":29,"wallClockMs":717280}},{"artifacts":[],"attempt":1,"bundleHash":"4c14cffe28926bb3abe7c06fc867fc57d47f446b07b96e517088e4c5258b4063","device":"05778e691c371384","findings":[],"hash":"639eb3525a57b1bf9bd98e85f3352515abecea0a95798d44940785d5d24352b6","nodeId":"38641080-bd38-4e1b-bd91-18a70d562169","outcome":"completed","summary":"Updated only [launch.json](/root/.identitymd/work/e42d648c-704c-4822-9b9d-25660d83dd66/38641080-bd38-4e1b-bd91-18a70d562169/launch.json) to document the authorized two-phase deployment, preserving constructor arguments, permissions, pool settings, and fees.\n\nChecked schema-reference fields and compiled ABI consistency. Notes are 3,151/4,000 characters.\n\n`forge build` succeeded; all 239 tests passed.","treeHash":"94dfc553f0db650f9d38389cd0e965375091659b","usage":{"cachedInputTokens":367744,"inputTokens":58710,"model":"gpt-6-astra","outputTokens":5117,"runtime":"codex","turns":3,"wallClockMs":179474}},{"artifacts":[],"attempt":1,"bundleHash":"58958fcc57683cd7254b9f7c26b94dfbd85ec2d78269a5e3f16a93d12aa98bf7","device":"c4f696e22e7a36f7","findings":[],"hash":"837811e791469239c165ae41d8d3778b7748497ff8bbf452a69fcc1cd2056b78","nodeId":"c1135a15-5a55-4f02-afb7-eddc59cd4eff","outcome":"completed","summary":"Added real Uniswap v4 stateful invariants, token balance/allowance ledger tests, and failure/recovery coverage. Existing invariants now reject unexpected handler reverts.\n\nValidation: `forge build` passed; all **266 tests passed**, with zero failures or skips.\n\nAll changes are under `test/`. No new production defect was reproduced.","treeHash":"f69d1f3483dbe90286ce117b07d5c2825825ce03","usage":{"cachedInputTokens":5342464,"inputTokens":173636,"model":"gpt-6-astra","outputTokens":27482,"runtime":"codex","turns":10,"wallClockMs":899806}},{"artifacts":[],"attempt":1,"bundleHash":"4f060c70b208b050c4ebada1ec652561aa9346446b08bc9c568e374b67077ea3","device":"c9743cd9807c4c3a","findings":[],"hash":"888d7f8553e676ab3f95d6650389f4a8685d431765cfae0ed2e0954eee1f7ab5","nodeId":"2a8075a5-f660-4888-842c-9a02e5189620","outcome":"completed","summary":"The work is done and verified. Summary:\n\n**Outcome.** The pinned contracts launch as they are: no production source, script, `foundry.toml` or `launch.json` changed. I reproduced all four imported audit findings against the real vendored v4-core PoolManager, added eight regression tests, and wrote the launch documentation. Build, tests and format check all pass with the pinned compiler.\n\n| Check | Result |\n| --- | --- |\n| `forge build` | exit 0 |\n| `forge test` | 239 passed, 0 failed, 0 skipped |\n| `forge fmt --check` | exit 0 |\n\n**Findings and dispositions** (full detail in `docs/LAUNCH_REVIEW.md`):\n\n- **LP bypass of the surcharge (low):** reproduces; the existing real-v4 test already demonstrates it in both currency orders. Left unchanged because the brief fixes the flags at 0x20CC and forbids fee changes. Documented as a limit of a swap fee.\n- **Manifest parity price vs. 2,500 IMD valuation (low):** reproduces as a documentation gap only. The launch rules require the literal in `pool.initialPrice` and have the deployer derive the opening price. I computed both candidate sqrtPriceX96 values (one per sort order), put them in the README, and added tests that open the pool at each derived price, seed 81% single-sided with no IMD in the manager, make a first buy at the documented valuation, charge the fee as an IMD claim and sweep it.\n- **beforeInitialize ignores the initializing sender (info):** reproduces. Left unchanged because fixing it means a new constructor argument, a re-mined address and a changed manifest, against the instruction to launch the pinned code; the factory's single deploy-and-initialize transaction leaves no window. A new test pins both the window and that a bound hook refuses every later initialize.\n- **Audit scope (info):** nothing to fix; the unreached live-chain items are restated.\n\n**Also done.** Confirmed the logo matches the brief (1024 square, one yellow umbrella on flat navy). Fixed a README link to a response file that is not in the tree, and refreshed the deployment-parameter and operational-responsibility sections.\n\n**Open items for the launch, not for this repository:** the deployer must initialize in the same transaction as the hook deployment and record the opening price actually used; the requester must then run the auxiliary script for RoundPayout and the router and fund RoundPayout by hand. Slither runs only on the verifier.","treeHash":"c4728bdba8721c24ec8ba7dfef1602a06821299e","usage":{"cachedInputTokens":3055479,"inputTokens":834,"model":"claude-fable-5-1","outputTokens":27986,"runtime":"claude","turns":35,"wallClockMs":413135}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"56e50117311155be","findings":[{"citation":"resolved","description":"Access-control note, not a defect in the launch as specified. The sender argument is ignored and there is no $factory constructor argument, so the only guard on who opens the pool is that the call comes from the PoolManager. The key is fully constrained (fee 12500, spacing 60, {MONEYBACK, IMD}, this hook), so the only thing an outsider could choose is sqrtPriceX96 and the moment the 1800 s surcharge clock starts. In this launch the factory deploys the hook and calls PoolManager.initialize in the same transaction, so no unbound-hook state ever exists on chain (a revert in initialize also reverts the hook deployment). The repository already documents this (docs/LAUNCH_REVIEW.md item 3, README 'Deployment sequence') and tests it (test_unboundHookIsBoundByWhoeverInitializesFirst). Recorded so the judge has the residual condition in one place: the operational rule 'never deploy MoneyBackHook without initializing its pool in the same transaction' is the only thing that keeps this closed. No code change is requested; adding $factory would change the constructor signature, the mined address and the manifest, which the brief forbids.","line":360,"path":"src/MoneyBackHook.sol","reproduction":"State: MoneyBackHook deployed at a 0x20CC address but PoolManager.initialize not yet called (does not occur under the launch flow). Input: any EOA calls PoolManager.initialize({MONEYBACK, IMD, 12500, 60, hook}, TickMath.MIN_SQRT_PRICE + 1). Actual: hook.initializedAt is set, poolId bound, surcharge clock started, pool opened at the attacker's price; the factory's later initialize reverts AlreadyBound. Expected under a $factory-gated design: WrongSender revert. Under the actual atomic factory transaction the precondition is unreachable, hence info.","severity":"info","snippet":"    function beforeInitialize(address, PoolKey calldata key, uint160) external returns (bytes4) {\n        _onlyPoolManager();\n        if (initializedAt != 0) revert AlreadyBound();","title":"beforeInitialize accepts any initializing sender; an unbound hook is bound by whoever initializes first (closed only by the factory's atomic deploy+initialize)"}],"hash":"af66c7a7c21d7d649887cd7cc19c6fa010aa8b1c4cffc7865ed7d102ce4ca394","nodeId":"fb12302e-2187-44d5-9ff1-6c9f29962843","outcome":"completed","summary":"The review is complete. The findings file is written and the working tree is unchanged apart from it.\n\n**Result: no defects found in the assigned area (Access Control, Trust Gap, Asymmetry).** The file `.imd-findings.json` holds one info-level note and a coverage row for all 24 entry points plus four invariant rows.\n\nWhat I checked and how:\n\n- **Permission model.** Every state-changing function of the four contracts was mapped to its guard. The hook's three callbacks and its unlock callback are PoolManager-only, the swap callbacks also require the bound key, and the unlock callback additionally requires a sweep in flight. The router's callback runs only for the exact data it just passed to unlock. RoundPayout's privileged functions are all owner-gated behind a two-step handoff. The token has no privileged surface.\n- **Scratch probes against the real vendored PoolManager.** A third party cannot transfer or burn the hook's IMD claims, direct callbacks from a non-manager are refused, a random EOA's sweep delivers the full claim balance to the payout wallet, and a second key with a different fee tier cannot bind the hook. All passed, so those \"holds\" rows are backed by execution, not reading alone. The scratch file was removed afterwards.\n- **Asymmetry.** beforeSwap and afterSwap use the same \"IMD specified\" predicate and the same fee inversion, so the PartialFill check cannot disagree with what beforeSwap charged. Router buy and sell mirror each other on lock, deadline, slippage and refunds. RoundPayout's retry and write-off paths clear debt and the counter symmetrically. The one asymmetry I saw, writeOffFailed lacking the pause check, is owner-only bookkeeping and not a defect.\n- **Trust gap.** Fees can only ever reach the immutable payout. Fee avoidance through hookless pools or single-sided liquidity is inherent to the fixed 0x20CC flags and is already documented in the README and launch review. The manifest's constructor arguments, permissions, fee tier and paired currency all match the implementation.\n\n**The single info note** records that beforeInitialize does not check the initializing sender. With the key fully constrained, an outsider could only choose the opening price and start the surcharge clock, and only if the hook were ever left deployed but unbound. The factory's atomic deploy-and-initialize makes that state unreachable, the repository already documents and tests it, and fixing it would change the constructor and mined address the brief forbids. It is recorded for completeness, not as a request for change.\n\nExisting suite: 239 tests pass on the pinned tree.","treeHash":null,"usage":{"cachedInputTokens":1601027,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":34950,"runtime":"claude","turns":30,"wallClockMs":566669}}],"verification":[{"checks":[{"durationMs":4875,"exitCode":0,"name":"build","output":"Compiling 57 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.72s\nCompiler run successful!\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/RoundPayout.sol:62:32\n   │\n62 │     function transferOwnership(address newOwner) external onlyOwner {\n   │                                ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:388:24\n    │\n388 │         r = abi.decode(poolManager.unlock(data), (Result));\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:421:22\n    │\n421 │         (bool ok,) = to.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/MoneyBackRouter.sol:204:13\n    │\n204 │         if (block.timestamp > deadline) revert Expired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/MoneyBackRouter.sol:212:9\n    │\n212 │         emit BoughtWithEth(msg.sender, r.amountIn, r.imdMid, tokensOut);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:229:14\n    │\n229 │         if (!IERC20(token).transferFrom(msg.sender, address(this), tokens)) revert TransferFromFailed();\n    │              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:235:29\n    │\n235 │         if (unused != 0 && !IERC20(token).transfer(msg.sender, unused)) revert TransferFailed();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/MoneyBackRouter.sol:225:13\n    │\n225 │         if (block.timestamp > deadline) revert Expired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/MoneyBackRouter.sol:237:9\n    │\n237 │         emit SoldForEth(msg.sender, r.amountIn, r.imdMid, ethOut);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:227:22\n    │\n227 │         if (tokens > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:227:30\n    │\n227 │         if (tokens > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:399:13\n    │\n399 │         try poolManager.unlock(data) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/RoundPayout.sol:370:40\n    │\n370 │         (bool ok, bytes memory data) = token.call(abi.encodeCall(IERC20.transferFrom, (from, to, amount)));\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:194:9\n    │\n194 │         emit Funded(token, msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/RoundPayout.sol:364:40\n    │\n364 │         (bool ok, bytes memory data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/RoundPayout.sol:198:82\n    │\n198 │     function sweep(address token, address to, uint256 amount) external onlyOwner nonReentrant {\n    │                                                                                  ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:201:9\n    │\n201 │         emit Swept(token, to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:307:22\n    │\n307 │         r.amountIn = uint256(-a0);\n    │                      ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:308:26\n    │\n308 │         uint256 imdOut = uint256(a1);\n    │                          ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/MoneyBackRouter.sol:313:13\n    │\n313 │             poolManager.settle{value: r.amountIn}();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:321:20\n    │\n321 │         r.imdMid = uint256(-imdDelta);\n    │                    ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:322:23\n    │\n322 │         r.amountOut = uint256(tokenDelta);\n    │                       ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:338:22\n    │\n338 │         r.amountIn = uint256(-tokenDelta);\n    │                      ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/MoneyBackRouter.sol:342:13\n    │\n342 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:344:26\n    │\n344 │         uint256 imdOut = uint256(imdDelta);\n    │                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:349:20\n    │\n349 │         r.imdMid = uint256(-a1);\n    │                    ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:350:23\n    │\n350 │         r.amountOut = uint256(a0);\n    │                       ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:361:24\n    │\n361 │         if (amountIn > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:361:32\n    │\n361 │         if (amountIn > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                                ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:366:35\n    │\n366 │                 amountSpecified: -int256(amountIn),\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/RoundPayout.sol:358:22\n    │\n358 │         (ok, data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:404:40\n    │\n404 │             if (reason.length == 36 && bytes4(reason) == QuoteResult.selector) {\n    │                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/MoneyBackRouter.sol:421:22\n    │\n421 │         (bool ok,) = to.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/RoundPayout.sol:224:40\n    │\n224 │     ) external onlyOwner whenNotPaused nonReentrant returns (uint256 totalPaid) {\n    │                                        ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:244:9\n    │\n244 │         emit RoundPaid(roundId, token, ledgerHash, twapCloseX96, totalEligibleLoss, totalPaid, n);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/RoundPayout.sol:265:14\n    │\n265 │         if (!anySucceeded) revert AllTransfersFailed(roundId);\n    │              ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/RoundPayout.sol:358:22\n    │\n358 │         (ok, data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:260:17\n    │\n260 │                 emit Paid(roundId, recipient, amount);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:274:9\n    │\n274 │         emit PayFailed(roundId, recipient, amount, reason);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/MoneyBackHook.sol:350:13\n    │\n350 │         if (elapsed >= SURCHARGE_DURATION) return 0;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/MoneyBackHook.sol:382:27\n    │\n382 │         _imdIsCurrency0 = imdIsCurrency0;\n    │                           ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-no-eth]: external call can be reentered before `failed` is updated\n    ╭▸ src/RoundPayout.sol:358:22\n    │\n358 │         (ok, data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/RoundPayout.sol:312:13\n    │\n312 │             needed += owed;\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/RoundPayout.sol:298:9\n    │\n298 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:326:17\n    │\n326 │                 emit Paid(roundId, recipient, amount);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:328:17\n    │\n328 │                 emit PayFailed(roundId, recipient, amount, reason);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/RoundPayout.sol:311:28\n    │\n311 │             if (owed == 0) revert NothingFailed(roundId, to[i]);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/RoundPayout.sol:319:30\n    │\n319 │             if (amount == 0) revert NothingFailed(roundId, recipient);\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:403:42\n    │\n403 │         uint256 specified = exactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:403:77\n    │\n403 │         uint256 specified = exactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/MoneyBackHook.sol:490:9\n    │\n490 │         emit FeeAccrued(isSell, baseFee, surcharge, imdLeg);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/RoundPayout.sol:334:77\n    │\n334 │     function writeOffFailed(uint256 roundId, address to) external onlyOwner nonReentrant {\n    │                                                                             ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:423:46\n    │\n423 │             uint256 specified = exactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:423:81\n    │\n423 │             uint256 specified = exactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:428:39\n    │\n428 │             if (imdDelta != (isSell ? int256(expectedLeg) : -int256(expectedLeg))) revert PartialFill();\n    │                                       ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:428:62\n    │\n428 │             if (imdDelta != (isSell ? int256(expectedLeg) : -int256(expectedLeg))) revert PartialFill();\n    │                                                              ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:432:41\n    │\n432 │         uint256 imdLeg = imdDelta < 0 ? uint256(-imdDelta) : uint256(imdDelta);\n    │                                         ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:432:62\n    │\n432 │         uint256 imdLeg = imdDelta < 0 ? uint256(-imdDelta) : uint256(imdDelta);\n    │                                                              ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackHook.sol:447:9\n    │\n447 │         poolManager.unlock(\"\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/MoneyBackHook.sol:447:9\n    │\n447 │         poolManager.unlock(\"\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/MoneyBackHook.sol:461:9\n    │\n461 │         emit Swept(amount, payout);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:509:25\n    │\n509 │         if (specified > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:509:33\n    │\n509 │         if (specified > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:516:22\n    │\n516 │         if (imdLeg > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:516:30\n    │\n516 │         if (imdLeg > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:522:17\n    │\n522 │         if (x > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:522:25\n    │\n522 │         if (x > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:523:16\n    │\n523 │         return int128(uint128(x));\n    │                ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:523:23\n    │\n523 │         return int128(uint128(x));\n    │                       ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n     ╭▸ test/MoneyBackRouter.t.sol:1132:24\n     │\n1132 │         uint256 now_ = block.timestamp;\n     │                        ━━━━━━━━━━━━━━━\n1133 │         vm.warp(ts);\n     │         ─────────── `vm.warp` changes this environment here\n     │\n     ├ help: capture it with `vm.getBlockTimestamp()` instead\n     ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n     ╭▸ test/MoneyBackRouter.t.sol:1179:53\n     │\n1179 │         uint256 bought = _buyAs(ALICE, 10 ether, 0, block.timestamp);\n     │                                                     ━━━━━━━━━━━━━━━\n1180 │         vm.warp(T0 + 3600);\n     │         ────────────────── `vm.warp` changes this environment here\n     │\n     ├ help: capture it with `vm.getBlockTimestamp()` instead\n     ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n     ╭▸ test/MoneyBackRouter.t.sol:1538:49\n     │\n1538 │         assertEq(_sellAs(ALICE, tokens, quoted, block.timestamp), quoted, \"sell delivers exactly the quote\");\n     │                                                 ━━━━━━━━━━━━━━━\n1539 │         vm.warp(T0 + 1800);\n     │         ────────────────── `vm.warp` changes this environment here\n     │\n     ├ help: capture it with `vm.getBlockTimestamp()` instead\n     ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":958,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 6 tests for test/DeployAuxiliaries.t.sol:DeployAuxiliariesTest\n[PASS] test_deploysBothAuxiliariesWithPayingWalletOwner() (gas: 2849445)\n[PASS] test_uninitializedHookRefused() (gas: 71375001)\n[PASS] test_unresolvedAddressesRefused() (gas: 61999)\n[PASS] test_wrongChainRefused() (gas: 35325)\n[PASS] test_wrongManagerRefused() (gas: 41055)\n[PASS] test_wrongWalletRefused() (gas: 44951506)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 140.61ms (91.43ms CPU time)\n\nRan 19 tests for test/MoneyBackRealV4.t.sol:MoneyBackRealV4Token1Test\n[PASS] testFuzz_equivalentSwapForms(uint96,uint32,bool) (runs: 256, μ: 421805, ~: 415403)\n[PASS] testFuzz_fullFillSpecifiedFee(uint96,uint32,bool) (runs: 256, μ: 206467, ~: 201123)\n[PASS] test_boundPoolRefusesAnySecondInitialize() (gas: 90976)\n[PASS] test_equivalentBuysAndSellsAtDecayBoundaries() (gas: 3633684)\n[PASS] test_exactInputBuyExhaustedLiquidityReverts() (gas: 602557)\n[PASS] test_exactInputBuyTightLimitRevertsWithoutOvercharge() (gas: 223378)\n[PASS] test_exactOutputSellExhaustedLiquidityReverts() (gas: 587084)\n[PASS] test_exactOutputSellTightLimitRevertsWithoutTakingIMD() (gas: 218104)\n[PASS] test_freshSingleSidedPoolCanBuyAndSweepWithNoIMDReserve() (gas: 30394831)\n[PASS] test_fullFillAllFourCasesAndSweep() (gas: 2290933)\n[PASS] test_liquidityConversionIsOutsideSwapSurcharge() (gas: 581036)\n[PASS] test_partialExactOutputBuyChargesActualIMD() (gas: 572169)\n[PASS] test_partialUnspecifiedIMDStillChargesActualFill() (gas: 584246)\n[PASS] test_policyOpeningPriceSellSurchargeApplies() (gas: 30487180)\n[PASS] test_policyOpeningPriceSingleSidedSeedFirstBuyAndSweep() (gas: 30443225)\n[PASS] test_routerTradesAgainstRealManagerAndRefundsPartialSell() (gas: 2049089)\n[PASS] test_routerUnfillableBuyAndQuoteRevertAtomically() (gas: 2016550)\n[PASS] test_singleSidedPoolRejectsUnfillableExactOutputSell() (gas: 30678298)\n[PASS] test_unboundHookIsBoundByWhoeverInitializesFirst() (gas: 29900986)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 179.76ms (346.93ms CPU time)\n\nRan 19 tests for test/MoneyBackRealV4.t.sol:MoneyBackRealV4Token0Test\n[PASS] testFuzz_equivalentSwapForms(uint96,uint32,bool) (runs: 256, μ: 421677, ~: 403759)\n[PASS] testFuzz_fullFillSpecifiedFee(uint96,uint32,bool) (runs: 256, μ: 205546, ~: 194678)\n[PASS] test_boundPoolRefusesAnySecondInitialize() (gas: 90976)\n[PASS] test_equivalentBuysAndSellsAtDecayBoundaries() (gas: 3633631)\n[PASS] test_exactInputBuyExhaustedLiquidityReverts() (gas: 595832)\n[PASS] test_exactInputBuyTightLimitRevertsWithoutOvercharge() (gas: 217215)\n[PASS] test_exactOutputSellExhaustedLiquidityReverts() (gas: 593689)\n[PASS] test_exactOutputSellTightLimitRevertsWithoutTakingIMD() (gas: 224147)\n[PASS] test_freshSingleSidedPoolCanBuyAndSweepWithNoIMDReserve() (gas: 92916044)\n[PASS] test_fullFillAllFourCasesAndSweep() (gas: 2289965)\n[PASS] test_liquidityConversionIsOutsideSwapSurcharge() (gas: 567599)\n[PASS] test_partialExactOutputBuyChargesActualIMD() (gas: 565545)\n[PASS] test_partialUnspecifiedIMDStillChargesActualFill() (gas: 590956)\n[PASS] test_policyOpeningPriceSellSurchargeApplies() (gas: 93047666)\n[PASS] test_policyOpeningPriceSingleSidedSeedFirstBuyAndSweep() (gas: 92999921)\n[PASS] test_routerTradesAgainstRealManagerAndRefundsPartialSell() (gas: 2050421)\n[PASS] test_routerUnfillableBuyAndQuoteRevertAtomically() (gas: 1988954)\n[PASS] test_singleSidedPoolRejectsUnfillableExactOutputSell() (gas: 93359140)\n[PASS] test_unboundHookIsBoundByWhoeverInitializesFirst() (gas: 92442671)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 183.48ms (655.90ms CPU time)\n\nRan 27 tests for test/MoneyBackToken.t.sol:MoneyBackTokenTest\n[PASS]\nMoneyBackTokenTest invariants:\n[PASS] invariant_balancesSumToSupply\n[PASS] invariant_totalSupplyNeverChanges\n[PASS] invariant_zeroAddressHoldsNothing\n MoneyBackTokenTest invariants (runs: 128, calls: 4096, reverts: 0)\n\n╭--------------+--------------+-------+---------+----------╮\n| Contract     | Selector     | Calls | Reverts | Discards |\n+==========================================================+\n| TokenHandler | approve      | 1346  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| TokenHandler | transfer     | 1369  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| TokenHandler | transferFrom | 1381  | 0       | 0        |\n╰--------------+--------------+-------+---------+----------╯\n\n[PASS] testFuzz_allowanceAccounting(uint256,uint256) (runs: 512, μ: 117288, ~: 134030)\n[PASS] testFuzz_transferAboveBalanceAlwaysReverts(uint256) (runs: 512, μ: 40842, ~: 40840)\n[PASS] testFuzz_transferConservesSupplyAndBalances(address,uint256) (runs: 512, μ: 92678, ~: 93559)\n[PASS] test_approveSetsAllowanceAndEmits() (gas: 97003)\n[PASS] test_approveZeroSpenderReverts() (gas: 30352)\n[PASS] test_constructorMintsWholeSupplyToDeployer() (gas: 17042)\n[PASS] test_deployerIsMsgSenderNotTxOrigin() (gas: 188189)\n[PASS] test_metadata() (gas: 17630)\n[PASS] test_noPrivilegedSurface() (gas: 165545)\n[PASS] test_rejectsEth() (gas: 32817)\n[PASS] test_supplyIsExactlyOneBillionWithEighteenDecimals() (gas: 13317)\n[PASS] test_transferFromDoesNotEmitApprovalOnSpend() (gas: 115689)\n[PASS] test_transferFromEmptyAccountRevertsEvenForOneWei() (gas: 33309)\n[PASS] test_transferFromExactAllowanceLeavesZero() (gas: 144349)\n[PASS] test_transferFromInfiniteAllowanceIsNotDecremented() (gas: 112829)\n[PASS] test_transferFromRevertsWhenAllowanceBelowAmount() (gas: 80374)\n[PASS] test_transferFromRevertsWhenBalanceBelowAmountEvenWithAllowance() (gas: 86141)\n[PASS] test_transferFromRevertsWithoutAllowance() (gas: 33778)\n[PASS] test_transferFromSpendsAllowance() (gas: 125597)\n[PASS] test_transferFromToZeroAddressReverts() (gas: 83503)\n[PASS] test_transferMovesBalanceAndEmits() (gas: 87232)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 40643)\n[PASS] test_transferToSelfKeepsBalance() (gas: 42924)\n[PASS] test_transferToZeroAddressReverts() (gas: 30391)\n[PASS] test_transferWholeBalanceLeavesZero() (gas: 70165)\n[PASS] test_transferZeroAmountSucceeds() (gas: 37089)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 332.27ms (489.78ms CPU time)\n\nRan 60 tests for test/MoneyBackHook.t.sol:MoneyBackHookTest\n[PASS]\nMoneyBackHookTest invariants:\n[PASS] invariant_hookNeverHoldsMoneybackClaims\n[PASS] invariant_lpFeeNeverOverriddenAndManagerLocked\n[PASS] invariant_pendingEqualsAccruedMinusSwept\n[PASS] invariant_sweepPaysOnlyPayout\n MoneyBackHookTest invariants (runs: 64, calls: 2560, reverts: 0)\n\n╭-------------+----------+-------+---------+----------╮\n| Contract    | Selector | Calls | Reverts | Discards |\n+=====================================================+\n| HookHandler | swap     | 840   | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | sweep    | 847   | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | warp     | 873   | 0       | 0        |\n╰-------------+----------+-------+---------+----------╯\n\n[PASS] testFuzz_feeMathsAllCases(uint8,uint256,uint32) (runs: 1000, μ: 173137, ~: 175196)\n[PASS] testFuzz_sequenceAccumulatesPending(uint256,uint256,uint256,uint32) (runs: 256, μ: 326936, ~: 330090)\n[PASS] testFuzz_specifiedOneWeiUnderfillAlwaysRefused(uint96,uint32,bool) (runs: 256, μ: 65800, ~: 65734)\n[PASS] testFuzz_surchargeFormula(uint32) (runs: 1000, μ: 18986, ~: 18699)\n[PASS] testFuzz_surchargeIsMonotoneNonIncreasing(uint32,uint32) (runs: 512, μ: 21741, ~: 21807)\n[PASS] testFuzz_tinyAmountsRespectRoundingBounds(uint8,uint256,uint32) (runs: 512, μ: 125119, ~: 116104)\n[PASS] test_acceptanceSellCostAtT0AndAfterDecay() (gas: 309530)\n[PASS] test_afterSwapWithZeroPoolDeltaAccruesNothing() (gas: 64200)\n[PASS] test_allCasesWithImdAsCurrency1() (gas: 36104180)\n[PASS] test_buysNeverSurchargedAtAnyTime() (gas: 504482)\n[PASS] test_callbacksRejectNonPoolManager() (gas: 159864)\n[PASS] test_case1_exactInputBuy_t0() (gas: 175005)\n[PASS] test_case1_exactInputBuy_t1800() (gas: 174616)\n[PASS] test_case1_exactInputBuy_t1day() (gas: 174635)\n[PASS] test_case1_exactInputBuy_t900() (gas: 175037)\n[PASS] test_case2_exactOutputBuy_t0() (gas: 172835)\n[PASS] test_case2_exactOutputBuy_t1800() (gas: 172381)\n[PASS] test_case2_exactOutputBuy_t1day() (gas: 172312)\n[PASS] test_case2_exactOutputBuy_t900() (gas: 172778)\n[PASS] test_case3_exactInputSell_t0() (gas: 173663)\n[PASS] test_case3_exactInputSell_t1800() (gas: 172962)\n[PASS] test_case3_exactInputSell_t1day() (gas: 173003)\n[PASS] test_case3_exactInputSell_t900() (gas: 173682)\n[PASS] test_case4_exactOutputSell_t0() (gas: 175682)\n[PASS] test_case4_exactOutputSell_t1800() (gas: 174600)\n[PASS] test_case4_exactOutputSell_t1day() (gas: 174575)\n[PASS] test_case4_exactOutputSell_t900() (gas: 175715)\n[PASS] test_constructorRejectsImdAsLaunchToken() (gas: 63997)\n[PASS] test_constructorRejectsWrongFlagBits() (gas: 115010453)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 182731)\n[PASS] test_constructorStoresImmutablesAndConstants() (gas: 58919)\n[PASS] test_deploymentAddressFlagBitsMatchPermissions() (gas: 18488)\n[PASS] test_emptySweepSucceedsAndEmitsZero() (gas: 75461)\n[PASS] test_exactOutputGrossLegOverflowRevertsBeforeMint() (gas: 107257)\n[PASS] test_feeAccruedEventsPerCase() (gas: 410040)\n[PASS] test_fixedCallbacksOnlyChargeTheirOwnCases() (gas: 165507)\n[PASS] test_hookNeverMintsMoneybackClaimsEvenIfGiftedImdClaims() (gas: 295089)\n[PASS] test_hookRejectsEthAndHasNoAdminSurface() (gas: 169036)\n[PASS] test_hugeSpecifiedAmountRevertsAmountOverflow() (gas: 98731)\n[PASS] test_initializeAcceptsEitherCurrencyOrder() (gas: 46362180)\n[PASS] test_initializeBindsPoolAndRecordsTimestamp() (gas: 58346)\n[PASS] test_initializeEmitsPoolBound() (gas: 46367154)\n[PASS] test_initializeRejectsSecondPool() (gas: 74581)\n[PASS] test_initializeRejectsWrongKeys() (gas: 47724457)\n[PASS] test_specifiedFeesAtInt128Boundary() (gas: 208889)\n[PASS] test_specifiedZeroFeeUnderfillRefused() (gas: 53628)\n[PASS] test_surchargeAt1799IsStillChargedOnSells() (gas: 136266)\n[PASS] test_surchargeAt1800IsZeroOnSells() (gas: 135401)\n[PASS] test_surchargeBoundaries() (gas: 83038)\n[PASS] test_swapBeforeInitializationReverts() (gas: 46393621)\n[PASS] test_swapRejectsWrongPoolKey() (gas: 260875)\n[PASS] test_sweepAccumulatesAcrossRounds() (gas: 454150)\n[PASS] test_sweepCannotBeNestedInsideAnotherUnlock() (gas: 774909)\n[PASS] test_sweepReentrancyFromPayoutTransferIsRejected() (gas: 760265)\n[PASS] test_sweepRevertsCleanlyIfTakeFails() (gas: 453897)\n[PASS] test_sweepTakesAllImdClaimsToPayout() (gas: 452883)\n[PASS] test_unlockCallbackOutsideSweepIsRejected() (gas: 167674)\n[PASS] test_viewsBeforeInitialization() (gas: 46220834)\n[PASS] test_zeroAmountSwapAccruesNothing() (gas: 205472)\nSuite result: ok. 60 passed; 0 failed; 0 skipped; finished in 423.73ms (1.39s CPU time)\n\nRan 56 tests for test/RoundPayout.t.sol:RoundPayoutTest\n[PASS]\nRoundPayoutTest invariants:\n[PASS] invariant_balanceEqualsFundedMinusPaidMinusSwept\n[PASS] invariant_ownerNeverChangesWithoutHandoff\n[PASS] invariant_roundRecordsMatchLedger\n[PASS] invariant_sumPaidNeverExceedsFunded\n RoundPayoutTest invariants (runs: 96, calls: 3840, reverts: 27)\n\n╭---------------+---------------+-------+---------+----------╮\n| Contract      | Selector      | Calls | Reverts | Discards |\n+============================================================+\n| PayoutHandler | fund          | 424   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | pay           | 411   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | replay        | 463   | 27      | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | retry         | 404   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | setBlocked    | 448   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | strangerCalls | 415   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | sweep         | 408   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | togglePause   | 447   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | writeOff      | 420   | 0       | 0        |\n╰---------------+---------------+-------+---------+----------╯\n\n[PASS] testFuzz_batchSizeBoundary(uint16) (runs: 256, μ: 2181188, ~: 1481830)\n[PASS] testFuzz_payRoundAccounting(uint256,uint256,uint256,uint256,uint8) (runs: 512, μ: 304951, ~: 231347)\n[PASS] test_allDuplicateFailuresStillRevert() (gas: 226537)\n[PASS] test_allTransfersFailedReverts() (gas: 349732)\n[PASS] test_constructorRejectsZeroOwner() (gas: 4100)\n[PASS] test_constructorSetsOwner() (gas: 22667)\n[PASS] test_duplicateFailedRecipientAccumulates() (gas: 387635)\n[PASS] test_duplicateFailedRecipientRetryClearsWholeDebtAndCount() (gas: 683078)\n[PASS] test_duplicateFailedRecipientWriteOffClearsWholeDebtAndCount() (gas: 500642)\n[PASS] test_fundAcceptsNoReturnToken() (gas: 476949)\n[PASS] test_fundPullsTokensFromAnyone() (gas: 174918)\n[PASS] test_fundRejectsEoaToken() (gas: 38646)\n[PASS] test_fundRejectsFalseReturningToken() (gas: 196919)\n[PASS] test_fundWithoutApprovalReverts() (gas: 96318)\n[PASS] test_fundZeroAmountSucceeds() (gas: 47919)\n[PASS] test_ownable2StepHandoff() (gas: 285110)\n[PASS] test_partialFailureIsRecordedAndBatchContinues() (gas: 430837)\n[PASS] test_partialFailureSilentRevertAndFalseReturn() (gas: 454037)\n[PASS] test_pauseTwiceAndUnpauseWhenRunningRevert() (gas: 108689)\n[PASS] test_payRoundBatchTooLarge() (gas: 283154)\n[PASS] test_payRoundBlockedWhilePaused() (gas: 388431)\n[PASS] test_payRoundEmptyBatch() (gas: 50414)\n[PASS] test_payRoundExactBalanceSucceeds() (gas: 329414)\n[PASS] test_payRoundIdempotent() (gas: 668322)\n[PASS] test_payRoundInsufficientBalanceUpFront() (gas: 127472)\n[PASS] test_payRoundIsTokenAgnosticPerRound() (gas: 821078)\n[PASS] test_payRoundLengthMismatch() (gas: 47260)\n[PASS] test_payRoundMaxBatchOf500() (gas: 17388182)\n[PASS] test_payRoundPaysEveryLegAndRecords() (gas: 404660)\n[PASS] test_payRoundRoundIdZeroIsOrdinary() (gas: 287441)\n[PASS] test_payRoundSumOverflowReverts() (gas: 48383)\n[PASS] test_payRoundWithEoaTokenReverts() (gas: 47866)\n[PASS] test_payRoundWithNoReturnToken() (gas: 539387)\n[PASS] test_payRoundZeroAmountLegIsPaid() (gas: 162387)\n[PASS] test_payRoundZeroToken() (gas: 44994)\n[PASS] test_privilegedFunctionsRejectNonOwner() (gas: 165748)\n[PASS] test_reentrantTokenCannotReenterFundOrSweepDuringPayRound() (gas: 840049)\n[PASS] test_reentrantTokenCannotReenterPayRound() (gas: 699804)\n[PASS] test_renounceOwnershipLocksEverything() (gas: 254602)\n[PASS] test_retryFailedInsufficientBalanceAfterSweep() (gas: 525680)\n[PASS] test_retryFailedPaysAndClears() (gas: 574104)\n[PASS] test_retryFailedRevertsForUnpaidRound() (gas: 43015)\n[PASS] test_retryFailedShapeChecks() (gas: 806977)\n[PASS] test_retryFailedStillFailingKeepsRecord() (gas: 467231)\n[PASS] test_singleLegFailureIsAllTransfersFailed() (gas: 188077)\n[PASS] test_sweepAboveBalanceReverts() (gas: 116001)\n[PASS] test_sweepByOwnerMovesTokens() (gas: 155282)\n[PASS] test_sweepRejectsShortReturnData() (gas: 174911)\n[PASS] test_sweepRejectsZeroRecipient() (gas: 101034)\n[PASS] test_sweepWorksWhilePaused() (gas: 185829)\n[PASS] test_transferOwnershipOnlyOwnerAndCancelable() (gas: 142657)\n[PASS] test_writeOffFailedClearsWithoutPaying() (gas: 517696)\n[PASS] test_writeOffFailedNothingOwedReverts() (gas: 40337)\n[PASS] test_zeroAmountFailuresCreateNoOutstandingRecipient() (gas: 342693)\n[PASS] test_zeroAmountSuccessPreventsAllFailedRevert() (gas: 352531)\nSuite result: ok. 56 passed; 0 failed; 0 skipped; finished in 539.02ms (765.48ms CPU time)\n\nRan 52 tests for test/MoneyBackRouter.t.sol:MoneyBackRouterTest\n[PASS]\nMoneyBackRouterTest invariants:\n[PASS] invariant_bindingsNeverChange\n[PASS] invariant_hookPendingEqualsRouterFeesMinusSwept\n[PASS] invariant_routerHoldsNothing\n[PASS] invariant_valueIsConserved\n MoneyBackRouterTest invariants (runs: 64, calls: 2560, reverts: 0)\n\n╭---------------+----------+-------+---------+----------╮\n| Contract      | Selector | Calls | Reverts | Discards |\n+=======================================================+\n| RouterHandler | buy      | 505   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| RouterHandler | quote    | 518   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| RouterHandler | sell     | 506   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| RouterHandler | sweep    | 533   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| RouterHandler | warp     | 498   | 0       | 0        |\n╰---------------+----------+-------+---------+----------╯\n\n[PASS] testFuzz_buyMatchesFormulaAndLeavesNothing(uint256,uint32,uint256,uint256) (runs: 600, μ: 1299605, ~: 1343789)\n[PASS] testFuzz_sellMatchesFormulaAndLeavesNothing(uint256,uint32,uint256,uint256) (runs: 600, μ: 1488957, ~: 1525758)\n[PASS] test_acceptanceRouterBuyPaysHookExactlyLikeDirectImdBuy() (gas: 1743717)\n[PASS] test_beforePoolBoundEverythingReverts() (gas: 10489440)\n[PASS] test_buyAboveInt128RevertsAmountOverflow() (gas: 1027153)\n[PASS] test_buyAndSellRevertOnDeadline() (gas: 1396894)\n[PASS] test_buyHappyPath() (gas: 754133)\n[PASS] test_buyRefundsEthDustWhenPublicPoolFillsPartially() (gas: 738812)\n[PASS] test_buyRejectsPartialLaunchFillAndRollsBackBothLegs() (gas: 770020)\n[PASS] test_buyRejectsPartialLaunchFillEvenWithPartialPublicFill() (gas: 816982)\n[PASS] test_buyRevertsOnMinOut() (gas: 2020115)\n[PASS] test_buySwapLegsAreExactInputWithWidestLimitsAndRouterAsSender() (gas: 633160)\n[PASS] test_buyThenSellRoundTrip() (gas: 1302853)\n[PASS] test_buyTwiceAndByTwoUsers() (gas: 1752780)\n[PASS] test_buyWorksWithImdAsCurrency1() (gas: 9937027)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 1005804)\n[PASS] test_constructorStoresImmutablesFromHook() (gas: 39385)\n[PASS] test_contractWithoutReceiveCanBuyFullFillOnly() (gas: 2498756)\n[PASS] test_deadlineIsCheckedBeforeAmount() (gas: 65550)\n[PASS] test_directTokenGiftsRemainUnrecoverable() (gas: 2374002)\n[PASS] test_feesFromRouterTradesAreSweepableToPayout() (gas: 1465812)\n[PASS] test_forcedEthGoesToNextTrader() (gas: 549206)\n[PASS] test_guardResetsAfterReverts() (gas: 2106373)\n[PASS] test_launchPoolKeyComesFromHook() (gas: 24076)\n[PASS] test_managerThatNeverCallsBackIsRejected() (gas: 320438)\n[PASS] test_publicPoolKeyIsHardcoded() (gas: 7323)\n[PASS] test_quoteBubblesUpForeignReverts() (gas: 15859555)\n[PASS] test_quoteBuyMatchesBuyAndChangesNothing() (gas: 3113722)\n[PASS] test_quoteCannotBeCalledWhileLocked() (gas: 839997)\n[PASS] test_quoteSellMatchesSellAndChangesNothing() (gas: 3301026)\n[PASS] test_quoteZeroReturnsZeroWithoutTouchingTheManager() (gas: 61476)\n[PASS] test_receiveRejectsEthFromAnyoneButTheManager() (gas: 775245)\n[PASS] test_reentrancyFromBuyDustRefundIsBlocked() (gas: 5521303)\n[PASS] test_reentrancyFromSellProceedsIsBlocked() (gas: 5539511)\n[PASS] test_routerHasNoAdminSurface() (gas: 163696)\n[PASS] test_sellAfterSurchargeDecayPaysOnlyBaseFee() (gas: 856182)\n[PASS] test_sellHappyPathAtT0WithSurcharge() (gas: 855788)\n[PASS] test_sellPrefundsRouterThenSettlesAndRefundsFromItsOwnBalance() (gas: 827408)\n[PASS] test_sellPullsExactlyTokensAndKeepsNoAllowance() (gas: 694656)\n[PASS] test_sellRefundsBothDusts() (gas: 925165)\n[PASS] test_sellRefundsImdDustWhenPublicPoolFillsPartially() (gas: 877077)\n[PASS] test_sellRefundsTokenDustWhenOurPoolFillsPartially() (gas: 863321)\n[PASS] test_sellRevertsOnMinOut() (gas: 1570902)\n[PASS] test_sellSwapLegsAreExactInputWithWidestLimits() (gas: 745732)\n[PASS] test_sellWhereSecondLegYieldsNothingRefundsNothingAndReverts() (gas: 942502)\n[PASS] test_sellWithoutAllowanceOrBalanceReverts() (gas: 334704)\n[PASS] test_tamperedCallbackDataIsRejected() (gas: 1062246)\n[PASS] test_tinyBuyThatYieldsNothingReverts() (gas: 2158204)\n[PASS] test_unlockCallbackRejectsManagerWithNoCallInFlight() (gas: 120933)\n[PASS] test_unlockCallbackRejectsNonManager() (gas: 59848)\n[PASS] test_zeroValueBuyAndZeroTokenSellRevert() (gas: 237280)\nSuite result: ok. 52 passed; 0 failed; 0 skipped; finished in 851.84ms (1.06s CPU time)\n\nRan 7 test suites in 856.23ms (2.65s CPU time): 239 tests passed, 0 failed, 0 skipped (239 total tests)\n","passed":true},{"durationMs":65,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"MoneyBackHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"MoneyBackHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"MoneyBackHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"MoneyBackHook.sweep()\",\"MoneyBackHook.unlockCallback(bytes)\",\"MoneyBackRouter.buyWithEth(uint256,uint256)\",\"MoneyBackRouter.quoteBuy(uint256)\",\"MoneyBackRouter.quoteSell(uint256)\",\"MoneyBackRouter.receive()\",\"MoneyBackRouter.sellForEth(uint256,uint256,uint256)\",\"MoneyBackRouter.unlockCallback(bytes)\",\"MoneyBackToken.approve(address,uint256)\",\"MoneyBackToken.transfer(address,uint256)\",\"MoneyBackToken.transferFrom(address,address,uint256)\",\"RoundPayout.acceptOwnership()\",\"RoundPayout.fund(address,uint256)\",\"RoundPayout.pause()\",\"RoundPayout.payRound(uint256,address,address[],uint256[],bytes32,uint256,uint256)\",\"RoundPayout.renounceOwnership()\",\"RoundPayout.retryFailed(uint256,address[])\",\"RoundPayout.sweep(address,address,uint256)\",\"RoundPayout.transferOwnership(address)\",\"RoundPayout.unpause()\",\"RoundPayout.writeOffFailed(uint256,address)\"],\"files\":{\"README.md\":138,\"assets/moneyback.png\":39,\"assets/moneyback.svg\":8,\"docs/AUDIT_RESPONSES.md\":99,\"docs/LAUNCH_REVIEW.md\":71,\"docs/REVISION.md\":78,\"docs/network-observations.json\":36,\"docs/requester-share.json\":4,\"foundry.toml\":13,\"launch.json\":31,\"script/DeployAuxiliaries.s.sol\":64,\"src/MoneyBackHook.sol\":547,\"src/MoneyBackRouter.sol\":445,\"src/MoneyBackToken.sol\":153,\"src/RoundPayout.sol\":383,\"test/DeployAuxiliaries.t.sol\":73,\"test/MoneyBackHook.t.sol\":1360,\"test/MoneyBackRealV4.t.sol\":568,\"test/MoneyBackRouter.t.sol\":1860,\"test/MoneyBackToken.t.sol\":372,\"test/RoundPayout.t.sol\":1318,\"test/vendor/README.md\":8,\"test/vendor/solmate/LICENSE\":661,\"test/vendor/solmate/src/auth/Owned.sol\":44,\"test/vendor/v4-core/licenses/BUSL_LICENSE\":63,\"test/vendor/v4-core/licenses/MIT_LICENSE\":7,\"test/vendor/v4-core/src/ERC6909.sol\":90,\"test/vendor/v4-core/src/ERC6909Claims.sol\":23,\"test/vendor/v4-core/src/Extsload.sol\":64,\"test/vendor/v4-core/src/Exttload.sol\":40,\"test/vendor/v4-core/src/NoDelegateCall.sol\":33,\"test/vendor/v4-core/src/PoolManager.sol\":394,\"test/vendor/v4-core/src/ProtocolFees.sol\":71,\"test/vendor/v4-core/src/interfaces/IExtsload.sol\":21,\"test/vendor/v4-core/src/interfaces/IExttload.sol\":15,\"test/vendor/v4-core/src/interfaces/IHooks.sol\":152,\"test/vendor/v4-core/src/interfaces/IPoolManager.sol\":217,\"test/vendor/v4-core/src/interfaces/IProtocolFees.sol\":52,\"test/vendor/v4-core/src/interfaces/callback/IUnlockCallback.sol\":10,\"test/vendor/v4-core/src/interfaces/external/IERC20Minimal.sol\":48,\"test/vendor/v4-core/src/interfaces/external/IERC6909Claims.sol\":66,\"test/vendor/v4-core/src/libraries/BitMath.sol\":49,\"test/vendor/v4-core/src/libraries/CurrencyDelta.sol\":42,\"test/vendor/v4-core/src/libraries/CurrencyReserves.sol\":39,\"test/vendor/v4-core/src/libraries/CustomRevert.sol\":120,\"test/vendor/v4-core/src/libraries/FixedPoint128.sol\":8,\"test/vendor/v4-core/src/libraries/FixedPoint96.sol\":10,\"test/vendor/v4-core/src/libraries/FullMath.sol\":117,\"test/vendor/v4-core/src/libraries/Hooks.sol\":344,\"test/vendor/v4-core/src/libraries/LPFeeLibrary.sol\":79,\"test/vendor/v4-core/src/libraries/LiquidityMath.sol\":20,\"test/vendor/v4-core/src/libraries/Lock.sol\":28,\"test/vendor/v4-core/src/libraries/NonzeroDeltaCount.sol\":35,\"test/vendor/v4-core/src/libraries/ParseBytes.sol\":29,\"test/vendor/v4-core/src/libraries/Pool.sol\":620,\"test/vendor/v4-core/src/libraries/Position.sol\":103,\"test/vendor/v4-core/src/libraries/ProtocolFeeLibrary.sol\":47,\"test/vendor/v4-core/src/libraries/SafeCast.sol\":60,\"test/vendor/v4-core/src/libraries/SqrtPriceMath.sol\":292,\"test/vendor/v4-core/src/libraries/StateLibrary.sol\":345,\"test/vendor/v4-core/src/libraries/SwapMath.sol\":108,\"test/vendor/v4-core/src/libraries/TickBitmap.sol\":121,\"test/vendor/v4-core/src/libraries/TickMath.sol\":238,\"test/vendor/v4-core/src/libraries/UnsafeMath.sol\":29,\"test/vendor/v4-core/src/types/BalanceDelta.sol\":72,\"test/vendor/v4-core/src/types/BeforeSwapDelta.sol\":38,\"test/vendor/v4-core/src/types/Currency.sol\":118,\"test/vendor/v4-core/src/types/PoolId.sol\":17,\"test/vendor/v4-core/src/types/PoolKey.sol\":22,\"test/vendor/v4-core/src/types/PoolOperation.sol\":26,\"test/vendor/v4-core/src/types/Slot0.sol\":95},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"639eb3525a57b1bf9bd98e85f3352515abecea0a95798d44940785d5d24352b6","verifiedTreeHash":"94dfc553f0db650f9d38389cd0e965375091659b","verifierVersion":"0.1.0+0e08a464"},{"checks":[{"durationMs":5607,"exitCode":0,"name":"build","output":"Compiling 59 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.47s\nCompiler run successful!\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/MoneyBackHook.sol:350:13\n    │\n350 │         if (elapsed >= SURCHARGE_DURATION) return 0;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/MoneyBackHook.sol:382:27\n    │\n382 │         _imdIsCurrency0 = imdIsCurrency0;\n    │                           ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/RoundPayout.sol:62:32\n   │\n62 │     function transferOwnership(address newOwner) external onlyOwner {\n   │                                ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:403:42\n    │\n403 │         uint256 specified = exactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:403:77\n    │\n403 │         uint256 specified = exactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/MoneyBackHook.sol:490:9\n    │\n490 │         emit FeeAccrued(isSell, baseFee, surcharge, imdLeg);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:423:46\n    │\n423 │             uint256 specified = exactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:423:81\n    │\n423 │             uint256 specified = exactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:428:39\n    │\n428 │             if (imdDelta != (isSell ? int256(expectedLeg) : -int256(expectedLeg))) revert PartialFill();\n    │                                       ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:428:62\n    │\n428 │             if (imdDelta != (isSell ? int256(expectedLeg) : -int256(expectedLeg))) revert PartialFill();\n    │                                                              ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:432:41\n    │\n432 │         uint256 imdLeg = imdDelta < 0 ? uint256(-imdDelta) : uint256(imdDelta);\n    │                                         ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:432:62\n    │\n432 │         uint256 imdLeg = imdDelta < 0 ? uint256(-imdDelta) : uint256(imdDelta);\n    │                                                              ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackHook.sol:447:9\n    │\n447 │         poolManager.unlock(\"\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/MoneyBackHook.sol:447:9\n    │\n447 │         poolManager.unlock(\"\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/MoneyBackHook.sol:461:9\n    │\n461 │         emit Swept(amount, payout);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:509:25\n    │\n509 │         if (specified > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:509:33\n    │\n509 │         if (specified > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:516:22\n    │\n516 │         if (imdLeg > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:516:30\n    │\n516 │         if (imdLeg > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:522:17\n    │\n522 │         if (x > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:522:25\n    │\n522 │         if (x > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:523:16\n    │\n523 │         return int128(uint128(x));\n    │                ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:523:23\n    │\n523 │         return int128(uint128(x));\n    │                       ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/RoundPayout.sol:370:40\n    │\n370 │         (bool ok, bytes memory data) = token.call(abi.encodeCall(IERC20.transferFrom, (from, to, amount)));\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:194:9\n    │\n194 │         emit Funded(token, msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/RoundPayout.sol:364:40\n    │\n364 │         (bool ok, bytes memory data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/RoundPayout.sol:198:82\n    │\n198 │     function sweep(address token, address to, uint256 amount) external onlyOwner nonReentrant {\n    │                                                                                  ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:201:9\n    │\n201 │         emit Swept(token, to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/RoundPayout.sol:358:22\n    │\n358 │         (ok, data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/RoundPayout.sol:224:40\n    │\n224 │     ) external onlyOwner whenNotPaused nonReentrant returns (uint256 totalPaid) {\n    │                                        ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:244:9\n    │\n244 │         emit RoundPaid(roundId, token, ledgerHash, twapCloseX96, totalEligibleLoss, totalPaid, n);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/RoundPayout.sol:265:14\n    │\n265 │         if (!anySucceeded) revert AllTransfersFailed(roundId);\n    │              ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/RoundPayout.sol:358:22\n    │\n358 │         (ok, data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:260:17\n    │\n260 │                 emit Paid(roundId, recipient, amount);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:274:9\n    │\n274 │         emit PayFailed(roundId, recipient, amount, reason);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `failed` is updated\n    ╭▸ src/RoundPayout.sol:358:22\n    │\n358 │         (ok, data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/RoundPayout.sol:312:13\n    │\n312 │             needed += owed;\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/RoundPayout.sol:298:9\n    │\n298 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:326:17\n    │\n326 │                 emit Paid(roundId, recipient, amount);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:328:17\n    │\n328 │                 emit PayFailed(roundId, recipient, amount, reason);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/RoundPayout.sol:311:28\n    │\n311 │             if (owed == 0) revert NothingFailed(roundId, to[i]);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/RoundPayout.sol:319:30\n    │\n319 │             if (amount == 0) revert NothingFailed(roundId, recipient);\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/RoundPayout.sol:334:77\n    │\n334 │     function writeOffFailed(uint256 roundId, address to) external onlyOwner nonReentrant {\n    │                                                                             ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:388:24\n    │\n388 │         r = abi.decode(poolManager.unlock(data), (Result));\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:421:22\n    │\n421 │         (bool ok,) = to.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/MoneyBackRouter.sol:204:13\n    │\n204 │         if (block.timestamp > deadline) revert Expired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/MoneyBackRouter.sol:212:9\n    │\n212 │         emit BoughtWithEth(msg.sender, r.amountIn, r.imdMid, tokensOut);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:229:14\n    │\n229 │         if (!IERC20(token).transferFrom(msg.sender, address(this), tokens)) revert TransferFromFailed();\n    │              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:235:29\n    │\n235 │         if (unused != 0 && !IERC20(token).transfer(msg.sender, unused)) revert TransferFailed();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/MoneyBackRouter.sol:225:13\n    │\n225 │         if (block.timestamp > deadline) revert Expired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/MoneyBackRouter.sol:237:9\n    │\n237 │         emit SoldForEth(msg.sender, r.amountIn, r.imdMid, ethOut);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:227:22\n    │\n227 │         if (tokens > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:227:30\n    │\n227 │         if (tokens > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:399:13\n    │\n399 │         try poolManager.unlock(data) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:307:22\n    │\n307 │         r.amountIn = uint256(-a0);\n    │                      ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:308:26\n    │\n308 │         uint256 imdOut = uint256(a1);\n    │                          ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/MoneyBackRouter.sol:313:13\n    │\n313 │             poolManager.settle{value: r.amountIn}();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:321:20\n    │\n321 │         r.imdMid = uint256(-imdDelta);\n    │                    ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:322:23\n    │\n322 │         r.amountOut = uint256(tokenDelta);\n    │                       ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:338:22\n    │\n338 │         r.amountIn = uint256(-tokenDelta);\n    │                      ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/MoneyBackRouter.sol:342:13\n    │\n342 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:344:26\n    │\n344 │         uint256 imdOut = uint256(imdDelta);\n    │                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:349:20\n    │\n349 │         r.imdMid = uint256(-a1);\n    │                    ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:350:23\n    │\n350 │         r.amountOut = uint256(a0);\n    │                       ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:361:24\n    │\n361 │         if (amountIn > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:361:32\n    │\n361 │         if (amountIn > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                                ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:366:35\n    │\n366 │                 amountSpecified: -int256(amountIn),\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:404:40\n    │\n404 │             if (reason.length == 36 && bytes4(reason) == QuoteResult.selector) {\n    │                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/MoneyBackRouter.sol:421:22\n    │\n421 │         (bool ok,) = to.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n     ╭▸ test/MoneyBackRouter.t.sol:1132:24\n     │\n1132 │         uint256 now_ = block.timestamp;\n     │                        ━━━━━━━━━━━━━━━\n1133 │         vm.warp(ts);\n     │         ─────────── `vm.warp` changes this environment here\n     │\n     ├ help: capture it with `vm.getBlockTimestamp()` instead\n     ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n     ╭▸ test/MoneyBackRouter.t.sol:1179:53\n     │\n1179 │         uint256 bought = _buyAs(ALICE, 10 ether, 0, block.timestamp);\n     │                                                     ━━━━━━━━━━━━━━━\n1180 │         vm.warp(T0 + 3600);\n     │         ────────────────── `vm.warp` changes this environment here\n     │\n     ├ help: capture it with `vm.getBlockTimestamp()` instead\n     ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n     ╭▸ test/MoneyBackRouter.t.sol:1538:49\n     │\n1538 │         assertEq(_sellAs(ALICE, tokens, quoted, block.timestamp), quoted, \"sell delivers exactly the quote\");\n     │                                                 ━━━━━━━━━━━━━━━\n1539 │         vm.warp(T0 + 1800);\n     │         ────────────────── `vm.warp` changes this environment here\n     │\n     ├ help: capture it with `vm.getBlockTimestamp()` instead\n     ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":10709,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 6 tests for test/DeployAuxiliaries.t.sol:DeployAuxiliariesTest\n[PASS] test_deploysBothAuxiliariesWithPayingWalletOwner() (gas: 2849445)\n[PASS] test_uninitializedHookRefused() (gas: 71375001)\n[PASS] test_unresolvedAddressesRefused() (gas: 61999)\n[PASS] test_wrongChainRefused() (gas: 35325)\n[PASS] test_wrongManagerRefused() (gas: 41055)\n[PASS] test_wrongWalletRefused() (gas: 44951506)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 509.20ms (87.94ms CPU time)\n\nRan 19 tests for test/MoneyBackRealV4.t.sol:MoneyBackRealV4Token0Test\n[PASS] testFuzz_equivalentSwapForms(uint96,uint32,bool) (runs: 256, μ: 421125, ~: 403745)\n[PASS] testFuzz_fullFillSpecifiedFee(uint96,uint32,bool) (runs: 256, μ: 205208, ~: 194671)\n[PASS] test_boundPoolRefusesAnySecondInitialize() (gas: 90976)\n[PASS] test_equivalentBuysAndSellsAtDecayBoundaries() (gas: 3633631)\n[PASS] test_exactInputBuyExhaustedLiquidityReverts() (gas: 595832)\n[PASS] test_exactInputBuyTightLimitRevertsWithoutOvercharge() (gas: 217215)\n[PASS] test_exactOutputSellExhaustedLiquidityReverts() (gas: 593689)\n[PASS] test_exactOutputSellTightLimitRevertsWithoutTakingIMD() (gas: 224147)\n[PASS] test_freshSingleSidedPoolCanBuyAndSweepWithNoIMDReserve() (gas: 92916044)\n[PASS] test_fullFillAllFourCasesAndSweep() (gas: 2289965)\n[PASS] test_liquidityConversionIsOutsideSwapSurcharge() (gas: 567599)\n[PASS] test_partialExactOutputBuyChargesActualIMD() (gas: 565545)\n[PASS] test_partialUnspecifiedIMDStillChargesActualFill() (gas: 590956)\n[PASS] test_policyOpeningPriceSellSurchargeApplies() (gas: 93047666)\n[PASS] test_policyOpeningPriceSingleSidedSeedFirstBuyAndSweep() (gas: 92999921)\n[PASS] test_routerTradesAgainstRealManagerAndRefundsPartialSell() (gas: 2050421)\n[PASS] test_routerUnfillableBuyAndQuoteRevertAtomically() (gas: 1988954)\n[PASS] test_singleSidedPoolRejectsUnfillableExactOutputSell() (gas: 93359140)\n[PASS] test_unboundHookIsBoundByWhoeverInitializesFirst() (gas: 92442671)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 509.10ms (523.54ms CPU time)\n\nRan 60 tests for test/MoneyBackHook.t.sol:MoneyBackHookTest\n[PASS]\nMoneyBackHookTest invariants:\n[PASS] invariant_hookNeverHoldsMoneybackClaims\n[PASS] invariant_lpFeeNeverOverriddenAndManagerLocked\n[PASS] invariant_pendingEqualsAccruedMinusSwept\n[PASS] invariant_sweepPaysOnlyPayout\n MoneyBackHookTest invariants (runs: 64, calls: 2560, reverts: 0)\n\n╭-------------+----------+-------+---------+----------╮\n| Contract    | Selector | Calls | Reverts | Discards |\n+=====================================================+\n| HookHandler | swap     | 849   | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | sweep    | 864   | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | warp     | 847   | 0       | 0        |\n╰-------------+----------+-------+---------+----------╯\n\n[PASS] testFuzz_feeMathsAllCases(uint8,uint256,uint32) (runs: 1000, μ: 172694, ~: 175196)\n[PASS] testFuzz_sequenceAccumulatesPending(uint256,uint256,uint256,uint32) (runs: 256, μ: 328050, ~: 330090)\n[PASS] testFuzz_specifiedOneWeiUnderfillAlwaysRefused(uint96,uint32,bool) (runs: 256, μ: 65802, ~: 65734)\n[PASS] testFuzz_surchargeFormula(uint32) (runs: 1000, μ: 18984, ~: 18699)\n[PASS] testFuzz_surchargeIsMonotoneNonIncreasing(uint32,uint32) (runs: 512, μ: 21750, ~: 21807)\n[PASS] testFuzz_tinyAmountsRespectRoundingBounds(uint8,uint256,uint32) (runs: 512, μ: 123988, ~: 116104)\n[PASS] test_acceptanceSellCostAtT0AndAfterDecay() (gas: 309530)\n[PASS] test_afterSwapWithZeroPoolDeltaAccruesNothing() (gas: 64200)\n[PASS] test_allCasesWithImdAsCurrency1() (gas: 36104180)\n[PASS] test_buysNeverSurchargedAtAnyTime() (gas: 504482)\n[PASS] test_callbacksRejectNonPoolManager() (gas: 159864)\n[PASS] test_case1_exactInputBuy_t0() (gas: 175005)\n[PASS] test_case1_exactInputBuy_t1800() (gas: 174616)\n[PASS] test_case1_exactInputBuy_t1day() (gas: 174635)\n[PASS] test_case1_exactInputBuy_t900() (gas: 175037)\n[PASS] test_case2_exactOutputBuy_t0() (gas: 172835)\n[PASS] test_case2_exactOutputBuy_t1800() (gas: 172381)\n[PASS] test_case2_exactOutputBuy_t1day() (gas: 172312)\n[PASS] test_case2_exactOutputBuy_t900() (gas: 172778)\n[PASS] test_case3_exactInputSell_t0() (gas: 173663)\n[PASS] test_case3_exactInputSell_t1800() (gas: 172962)\n[PASS] test_case3_exactInputSell_t1day() (gas: 173003)\n[PASS] test_case3_exactInputSell_t900() (gas: 173682)\n[PASS] test_case4_exactOutputSell_t0() (gas: 175682)\n[PASS] test_case4_exactOutputSell_t1800() (gas: 174600)\n[PASS] test_case4_exactOutputSell_t1day() (gas: 174575)\n[PASS] test_case4_exactOutputSell_t900() (gas: 175715)\n[PASS] test_constructorRejectsImdAsLaunchToken() (gas: 63997)\n[PASS] test_constructorRejectsWrongFlagBits() (gas: 115010453)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 182731)\n[PASS] test_constructorStoresImmutablesAndConstants() (gas: 58919)\n[PASS] test_deploymentAddressFlagBitsMatchPermissions() (gas: 18488)\n[PASS] test_emptySweepSucceedsAndEmitsZero() (gas: 75461)\n[PASS] test_exactOutputGrossLegOverflowRevertsBeforeMint() (gas: 107257)\n[PASS] test_feeAccruedEventsPerCase() (gas: 410040)\n[PASS] test_fixedCallbacksOnlyChargeTheirOwnCases() (gas: 165507)\n[PASS] test_hookNeverMintsMoneybackClaimsEvenIfGiftedImdClaims() (gas: 295089)\n[PASS] test_hookRejectsEthAndHasNoAdminSurface() (gas: 169036)\n[PASS] test_hugeSpecifiedAmountRevertsAmountOverflow() (gas: 98731)\n[PASS] test_initializeAcceptsEitherCurrencyOrder() (gas: 46362180)\n[PASS] test_initializeBindsPoolAndRecordsTimestamp() (gas: 58346)\n[PASS] test_initializeEmitsPoolBound() (gas: 46367154)\n[PASS] test_initializeRejectsSecondPool() (gas: 74581)\n[PASS] test_initializeRejectsWrongKeys() (gas: 47724457)\n[PASS] test_specifiedFeesAtInt128Boundary() (gas: 208889)\n[PASS] test_specifiedZeroFeeUnderfillRefused() (gas: 53628)\n[PASS] test_surchargeAt1799IsStillChargedOnSells() (gas: 136266)\n[PASS] test_surchargeAt1800IsZeroOnSells() (gas: 135401)\n[PASS] test_surchargeBoundaries() (gas: 83038)\n[PASS] test_swapBeforeInitializationReverts() (gas: 46393621)\n[PASS] test_swapRejectsWrongPoolKey() (gas: 260875)\n[PASS] test_sweepAccumulatesAcrossRounds() (gas: 454150)\n[PASS] test_sweepCannotBeNestedInsideAnotherUnlock() (gas: 774909)\n[PASS] test_sweepReentrancyFromPayoutTransferIsRejected() (gas: 760265)\n[PASS] test_sweepRevertsCleanlyIfTakeFails() (gas: 453897)\n[PASS] test_sweepTakesAllImdClaimsToPayout() (gas: 452883)\n[PASS] test_unlockCallbackOutsideSweepIsRejected() (gas: 167674)\n[PASS] test_viewsBeforeInitialization() (gas: 46220834)\n[PASS] test_zeroAmountSwapAccruesNothing() (gas: 205472)\nSuite result: ok. 60 passed; 0 failed; 0 skipped; finished in 509.25ms (1.63s CPU time)\n\nRan 27 tests for test/MoneyBackToken.t.sol:MoneyBackTokenTest\n[PASS]\nMoneyBackTokenTest invariants:\n[PASS] invariant_balancesSumToSupply\n[PASS] invariant_totalSupplyNeverChanges\n[PASS] invariant_zeroAddressHoldsNothing\n MoneyBackTokenTest invariants (runs: 128, calls: 4096, reverts: 0)\n\n╭--------------+--------------+-------+---------+----------╮\n| Contract     | Selector     | Calls | Reverts | Discards |\n+==========================================================+\n| TokenHandler | approve      | 1368  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| TokenHandler | transfer     | 1383  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| TokenHandler | transferFrom | 1345  | 0       | 0        |\n╰--------------+--------------+-------+---------+----------╯\n\n[PASS] testFuzz_allowanceAccounting(uint256,uint256) (runs: 512, μ: 116285, ~: 134030)\n[PASS] testFuzz_transferAboveBalanceAlwaysReverts(uint256) (runs: 512, μ: 40841, ~: 40840)\n[PASS] testFuzz_transferConservesSupplyAndBalances(address,uint256) (runs: 512, μ: 92801, ~: 93583)\n[PASS] test_approveSetsAllowanceAndEmits() (gas: 97003)\n[PASS] test_approveZeroSpenderReverts() (gas: 30352)\n[PASS] test_constructorMintsWholeSupplyToDeployer() (gas: 17042)\n[PASS] test_deployerIsMsgSenderNotTxOrigin() (gas: 188189)\n[PASS] test_metadata() (gas: 17630)\n[PASS] test_noPrivilegedSurface() (gas: 165545)\n[PASS] test_rejectsEth() (gas: 32817)\n[PASS] test_supplyIsExactlyOneBillionWithEighteenDecimals() (gas: 13317)\n[PASS] test_transferFromDoesNotEmitApprovalOnSpend() (gas: 115689)\n[PASS] test_transferFromEmptyAccountRevertsEvenForOneWei() (gas: 33309)\n[PASS] test_transferFromExactAllowanceLeavesZero() (gas: 144349)\n[PASS] test_transferFromInfiniteAllowanceIsNotDecremented() (gas: 112829)\n[PASS] test_transferFromRevertsWhenAllowanceBelowAmount() (gas: 80374)\n[PASS] test_transferFromRevertsWhenBalanceBelowAmountEvenWithAllowance() (gas: 86141)\n[PASS] test_transferFromRevertsWithoutAllowance() (gas: 33778)\n[PASS] test_transferFromSpendsAllowance() (gas: 125597)\n[PASS] test_transferFromToZeroAddressReverts() (gas: 83503)\n[PASS] test_transferMovesBalanceAndEmits() (gas: 87232)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 40643)\n[PASS] test_transferToSelfKeepsBalance() (gas: 42924)\n[PASS] test_transferToZeroAddressReverts() (gas: 30391)\n[PASS] test_transferWholeBalanceLeavesZero() (gas: 70165)\n[PASS] test_transferZeroAmountSucceeds() (gas: 37089)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 509.54ms (1.93s CPU time)\n\nRan 19 tests for test/MoneyBackRealV4.t.sol:MoneyBackRealV4Token1Test\n[PASS] testFuzz_equivalentSwapForms(uint96,uint32,bool) (runs: 256, μ: 421599, ~: 415390)\n[PASS] testFuzz_fullFillSpecifiedFee(uint96,uint32,bool) (runs: 256, μ: 206293, ~: 201117)\n[PASS] test_boundPoolRefusesAnySecondInitialize() (gas: 90976)\n[PASS] test_equivalentBuysAndSellsAtDecayBoundaries() (gas: 3633684)\n[PASS] test_exactInputBuyExhaustedLiquidityReverts() (gas: 602557)\n[PASS] test_exactInputBuyTightLimitRevertsWithoutOvercharge() (gas: 223378)\n[PASS] test_exactOutputSellExhaustedLiquidityReverts() (gas: 587084)\n[PASS] test_exactOutputSellTightLimitRevertsWithoutTakingIMD() (gas: 218104)\n[PASS] test_freshSingleSidedPoolCanBuyAndSweepWithNoIMDReserve() (gas: 30394831)\n[PASS] test_fullFillAllFourCasesAndSweep() (gas: 2290933)\n[PASS] test_liquidityConversionIsOutsideSwapSurcharge() (gas: 581036)\n[PASS] test_partialExactOutputBuyChargesActualIMD() (gas: 572169)\n[PASS] test_partialUnspecifiedIMDStillChargesActualFill() (gas: 584246)\n[PASS] test_policyOpeningPriceSellSurchargeApplies() (gas: 30487180)\n[PASS] test_policyOpeningPriceSingleSidedSeedFirstBuyAndSweep() (gas: 30443225)\n[PASS] test_routerTradesAgainstRealManagerAndRefundsPartialSell() (gas: 2049089)\n[PASS] test_routerUnfillableBuyAndQuoteRevertAtomically() (gas: 2016550)\n[PASS] test_singleSidedPoolRejectsUnfillableExactOutputSell() (gas: 30678298)\n[PASS] test_unboundHookIsBoundByWhoeverInitializesFirst() (gas: 29900986)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 512.46ms (399.83ms CPU time)\n\nRan 56 tests for test/RoundPayout.t.sol:RoundPayoutTest\n[PASS]\nRoundPayoutTest invariants:\n[PASS] invariant_balanceEqualsFundedMinusPaidMinusSwept\n[PASS] invariant_ownerNeverChangesWithoutHandoff\n[PASS] invariant_roundRecordsMatchLedger\n[PASS] invariant_sumPaidNeverExceedsFunded\n RoundPayoutTest invariants (runs: 96, calls: 3840, reverts: 0)\n\n╭---------------+---------------+-------+---------+----------╮\n| Contract      | Selector      | Calls | Reverts | Discards |\n+============================================================+\n| PayoutHandler | fund          | 421   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | pay           | 455   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | replay        | 444   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | retry         | 380   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | setBlocked    | 410   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | strangerCalls | 406   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | sweep         | 480   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | togglePause   | 424   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | writeOff      | 420   | 0       | 0        |\n╰---------------+---------------+-------+---------+----------╯\n\n[PASS] testFuzz_batchSizeBoundary(uint16) (runs: 256, μ: 2134361, ~: 1046159)\n[PASS] testFuzz_payRoundAccounting(uint256,uint256,uint256,uint256,uint8) (runs: 512, μ: 305831, ~: 231317)\n[PASS] test_allDuplicateFailuresStillRevert() (gas: 226537)\n[PASS] test_allTransfersFailedReverts() (gas: 349732)\n[PASS] test_constructorRejectsZeroOwner() (gas: 4100)\n[PASS] test_constructorSetsOwner() (gas: 22667)\n[PASS] test_duplicateFailedRecipientAccumulates() (gas: 387635)\n[PASS] test_duplicateFailedRecipientRetryClearsWholeDebtAndCount() (gas: 683078)\n[PASS] test_duplicateFailedRecipientWriteOffClearsWholeDebtAndCount() (gas: 500642)\n[PASS] test_fundAcceptsNoReturnToken() (gas: 476949)\n[PASS] test_fundPullsTokensFromAnyone() (gas: 174918)\n[PASS] test_fundRejectsEoaToken() (gas: 38646)\n[PASS] test_fundRejectsFalseReturningToken() (gas: 196919)\n[PASS] test_fundWithoutApprovalReverts() (gas: 96318)\n[PASS] test_fundZeroAmountSucceeds() (gas: 47919)\n[PASS] test_ownable2StepHandoff() (gas: 285110)\n[PASS] test_partialFailureIsRecordedAndBatchContinues() (gas: 430837)\n[PASS] test_partialFailureSilentRevertAndFalseReturn() (gas: 454037)\n[PASS] test_pauseTwiceAndUnpauseWhenRunningRevert() (gas: 108689)\n[PASS] test_payRoundBatchTooLarge() (gas: 283154)\n[PASS] test_payRoundBlockedWhilePaused() (gas: 388431)\n[PASS] test_payRoundEmptyBatch() (gas: 50414)\n[PASS] test_payRoundExactBalanceSucceeds() (gas: 329414)\n[PASS] test_payRoundIdempotent() (gas: 668322)\n[PASS] test_payRoundInsufficientBalanceUpFront() (gas: 127472)\n[PASS] test_payRoundIsTokenAgnosticPerRound() (gas: 821078)\n[PASS] test_payRoundLengthMismatch() (gas: 47260)\n[PASS] test_payRoundMaxBatchOf500() (gas: 17388182)\n[PASS] test_payRoundPaysEveryLegAndRecords() (gas: 404660)\n[PASS] test_payRoundRoundIdZeroIsOrdinary() (gas: 287441)\n[PASS] test_payRoundSumOverflowReverts() (gas: 48383)\n[PASS] test_payRoundWithEoaTokenReverts() (gas: 47866)\n[PASS] test_payRoundWithNoReturnToken() (gas: 539387)\n[PASS] test_payRoundZeroAmountLegIsPaid() (gas: 162387)\n[PASS] test_payRoundZeroToken() (gas: 44994)\n[PASS] test_privilegedFunctionsRejectNonOwner() (gas: 165748)\n[PASS] test_reentrantTokenCannotReenterFundOrSweepDuringPayRound() (gas: 840049)\n[PASS] test_reentrantTokenCannotReenterPayRound() (gas: 699804)\n[PASS] test_renounceOwnershipLocksEverything() (gas: 254602)\n[PASS] test_retryFailedInsufficientBalanceAfterSweep() (gas: 525680)\n[PASS] test_retryFailedPaysAndClears() (gas: 574104)\n[PASS] test_retryFailedRevertsForUnpaidRound() (gas: 43015)\n[PASS] test_retryFailedShapeChecks() (gas: 806977)\n[PASS] test_retryFailedStillFailingKeepsRecord() (gas: 467231)\n[PASS] test_singleLegFailureIsAllTransfersFailed() (gas: 188077)\n[PASS] test_sweepAboveBalanceReverts() (gas: 116001)\n[PASS] test_sweepByOwnerMovesTokens() (gas: 155282)\n[PASS] test_sweepRejectsShortReturnData() (gas: 174911)\n[PASS] test_sweepRejectsZeroRecipient() (gas: 101034)\n[PASS] test_sweepWorksWhilePaused() (gas: 185829)\n[PASS] test_transferOwnershipOnlyOwnerAndCancelable() (gas: 142657)\n[PASS] test_writeOffFailedClearsWithoutPaying() (gas: 517696)\n[PASS] test_writeOffFailedNothingOwedReverts() (gas: 40337)\n[PASS] test_zeroAmountFailuresCreateNoOutstandingRecipient() (gas: 342693)\n[PASS] test_zeroAmountSuccessPreventsAllFailedRevert() (gas: 352531)\nSuite result: ok. 56 passed; 0 failed; 0 skipped; finished in 678.94ms (1.42s CPU time)\n\nRan 52 tests for test/MoneyBackRouter.t.sol:MoneyBackRouterTest\n[PASS]\nMoneyBackRouterTest invariants:\n[PASS] invariant_bindingsNeverChange\n[PASS] invariant_hookPendingEqualsRouterFeesMinusSwept\n[PASS] invariant_routerHoldsNothing\n[PASS] invariant_valueIsConserved\n MoneyBackRouterTest invariants (runs: 64, calls: 2560, reverts: 0)\n\n╭---------------+----------+-------+---------+----------╮\n| Contract      | Selector | Calls | Reverts | Discards |\n+=======================================================+\n| RouterHandler | buy      | 495   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| RouterHandler | quote    | 557   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| RouterHandler | sell     | 524   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| RouterHandler | sweep    | 491   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| RouterHandler | warp     | 493   | 0       | 0        |\n╰---------------+----------+-------+---------+----------╯\n\n[PASS] testFuzz_buyMatchesFormulaAndLeavesNothing(uint256,uint32,uint256,uint256) (runs: 600, μ: 1305117, ~: 1343729)\n[PASS] testFuzz_sellMatchesFormulaAndLeavesNothing(uint256,uint32,uint256,uint256) (runs: 600, μ: 1498517, ~: 1524411)\n[PASS] test_acceptanceRouterBuyPaysHookExactlyLikeDirectImdBuy() (gas: 1743717)\n[PASS] test_beforePoolBoundEverythingReverts() (gas: 10489440)\n[PASS] test_buyAboveInt128RevertsAmountOverflow() (gas: 1027153)\n[PASS] test_buyAndSellRevertOnDeadline() (gas: 1396894)\n[PASS] test_buyHappyPath() (gas: 754133)\n[PASS] test_buyRefundsEthDustWhenPublicPoolFillsPartially() (gas: 738812)\n[PASS] test_buyRejectsPartialLaunchFillAndRollsBackBothLegs() (gas: 770020)\n[PASS] test_buyRejectsPartialLaunchFillEvenWithPartialPublicFill() (gas: 816982)\n[PASS] test_buyRevertsOnMinOut() (gas: 2020115)\n[PASS] test_buySwapLegsAreExactInputWithWidestLimitsAndRouterAsSender() (gas: 633160)\n[PASS] test_buyThenSellRoundTrip() (gas: 1302853)\n[PASS] test_buyTwiceAndByTwoUsers() (gas: 1752780)\n[PASS] test_buyWorksWithImdAsCurrency1() (gas: 9937027)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 1005804)\n[PASS] test_constructorStoresImmutablesFromHook() (gas: 39385)\n[PASS] test_contractWithoutReceiveCanBuyFullFillOnly() (gas: 2498756)\n[PASS] test_deadlineIsCheckedBeforeAmount() (gas: 65550)\n[PASS] test_directTokenGiftsRemainUnrecoverable() (gas: 2374002)\n[PASS] test_feesFromRouterTradesAreSweepableToPayout() (gas: 1465812)\n[PASS] test_forcedEthGoesToNextTrader() (gas: 549206)\n[PASS] test_guardResetsAfterReverts() (gas: 2106373)\n[PASS] test_launchPoolKeyComesFromHook() (gas: 24076)\n[PASS] test_managerThatNeverCallsBackIsRejected() (gas: 320438)\n[PASS] test_publicPoolKeyIsHardcoded() (gas: 7323)\n[PASS] test_quoteBubblesUpForeignReverts() (gas: 15859555)\n[PASS] test_quoteBuyMatchesBuyAndChangesNothing() (gas: 3113722)\n[PASS] test_quoteCannotBeCalledWhileLocked() (gas: 839997)\n[PASS] test_quoteSellMatchesSellAndChangesNothing() (gas: 3301026)\n[PASS] test_quoteZeroReturnsZeroWithoutTouchingTheManager() (gas: 61476)\n[PASS] test_receiveRejectsEthFromAnyoneButTheManager() (gas: 775245)\n[PASS] test_reentrancyFromBuyDustRefundIsBlocked() (gas: 5521303)\n[PASS] test_reentrancyFromSellProceedsIsBlocked() (gas: 5539511)\n[PASS] test_routerHasNoAdminSurface() (gas: 163696)\n[PASS] test_sellAfterSurchargeDecayPaysOnlyBaseFee() (gas: 856182)\n[PASS] test_sellHappyPathAtT0WithSurcharge() (gas: 855788)\n[PASS] test_sellPrefundsRouterThenSettlesAndRefundsFromItsOwnBalance() (gas: 827408)\n[PASS] test_sellPullsExactlyTokensAndKeepsNoAllowance() (gas: 694656)\n[PASS] test_sellRefundsBothDusts() (gas: 925165)\n[PASS] test_sellRefundsImdDustWhenPublicPoolFillsPartially() (gas: 877077)\n[PASS] test_sellRefundsTokenDustWhenOurPoolFillsPartially() (gas: 863321)\n[PASS] test_sellRevertsOnMinOut() (gas: 1570902)\n[PASS] test_sellSwapLegsAreExactInputWithWidestLimits() (gas: 745732)\n[PASS] test_sellWhereSecondLegYieldsNothingRefundsNothingAndReverts() (gas: 942502)\n[PASS] test_sellWithoutAllowanceOrBalanceReverts() (gas: 334704)\n[PASS] test_tamperedCallbackDataIsRejected() (gas: 1062246)\n[PASS] test_tinyBuyThatYieldsNothingReverts() (gas: 2158204)\n[PASS] test_unlockCallbackRejectsManagerWithNoCallInFlight() (gas: 120933)\n[PASS] test_unlockCallbackRejectsNonManager() (gas: 59848)\n[PASS] test_zeroValueBuyAndZeroTokenSellRevert() (gas: 237280)\nSuite result: ok. 52 passed; 0 failed; 0 skipped; finished in 945.62ms (1.64s CPU time)\n\nRan 2 tests for test/MoneyBackTokenLedger.t.sol:MoneyBackTokenLedgerTest\n[PASS] invariant_individualBalancesAndAllowancesMatchLedger() (runs: 256, calls: 20480, reverts: 0)\n\n╭--------------------+-------------+-------+---------+----------╮\n| Contract           | Selector    | Calls | Reverts | Discards |\n+===============================================================+\n| TokenLedgerHandler | approve     | 5136  | 0       | 0        |\n|--------------------+-------------+-------+---------+----------|\n| TokenLedgerHandler | rejectSpend | 5158  | 0       | 0        |\n|--------------------+-------------+-------+---------+----------|\n| TokenLedgerHandler | spend       | 5062  | 0       | 0        |\n|--------------------+-------------+-------+---------+----------|\n| TokenLedgerHandler | transfer    | 5124  | 0       | 0        |\n╰--------------------+-------------+-------+---------+----------╯\n\n[PASS] test_fullBalanceSelfTransferAndInfiniteAllowanceSequence() (gas: 1070868)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.24s (3.24s CPU time)\n\nRan 6 tests for test/MoneyBackStatefulV4.t.sol:MoneyBackStatefulV4Token1Test\n[PASS]\nMoneyBackStatefulV4Token1Test invariants:\n[PASS] invariant_realAssetConservationAndNoRouterCustody\n[PASS] invariant_realClaimBackingAndSettlement\n MoneyBackStatefulV4Token1Test invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------------+-------------------+-------+---------+----------╮\n| Contract           | Selector          | Calls | Reverts | Discards |\n+=====================================================================+\n| MoneyBackV4Handler | batchSwap         | 1781  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | elapse            | 1777  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | giftClaims        | 1772  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | quote             | 1798  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | rejectPartialFill | 1866  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | rejectSlippage    | 1863  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | route             | 1871  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | swap              | 1803  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | sweep             | 1853  | 0       | 0        |\n╰--------------------+-------------------+-------+---------+----------╯\n\n[PASS] testFuzz_repeatedRealRouterRoundTripsCannotCreateValue(uint96,uint16,uint8) (runs: 1000, μ: 1692899, ~: 1336743)\n[PASS] test_competingBuyInvalidatesOldQuoteAndSlippageRevertIsAtomic() (gas: 2366983)\n[PASS] test_failedSweepRestoresBurnedClaimsAndCanRetry() (gas: 1581946)\n[PASS] test_rejectedEthProceedsRollBackBothPoolsAndTokenPull() (gas: 1501429)\n[PASS] test_sequenceExercisesEveryAction() (gas: 4756892)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 10.41s (10.65s CPU time)\n\nRan 6 tests for test/MoneyBackStatefulV4.t.sol:MoneyBackStatefulV4Token0Test\n[PASS]\nMoneyBackStatefulV4Token0Test invariants:\n[PASS] invariant_realAssetConservationAndNoRouterCustody\n[PASS] invariant_realClaimBackingAndSettlement\n MoneyBackStatefulV4Token0Test invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------------+-------------------+-------+---------+----------╮\n| Contract           | Selector          | Calls | Reverts | Discards |\n+=====================================================================+\n| MoneyBackV4Handler | batchSwap         | 1781  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | elapse            | 1777  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | giftClaims        | 1772  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | quote             | 1798  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | rejectPartialFill | 1866  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | rejectSlippage    | 1863  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | route             | 1871  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | swap              | 1803  | 0       | 0        |\n|--------------------+-------------------+-------+---------+----------|\n| MoneyBackV4Handler | sweep             | 1853  | 0       | 0        |\n╰--------------------+-------------------+-------+---------+----------╯\n\n[PASS] testFuzz_repeatedRealRouterRoundTripsCannotCreateValue(uint96,uint16,uint8) (runs: 1000, μ: 1689507, ~: 1331066)\n[PASS] test_competingBuyInvalidatesOldQuoteAndSlippageRevertIsAtomic() (gas: 2343397)\n[PASS] test_failedSweepRestoresBurnedClaimsAndCanRetry() (gas: 1575862)\n[PASS] test_rejectedEthProceedsRollBackBothPoolsAndTokenPull() (gas: 1520030)\n[PASS] test_sequenceExercisesEveryAction() (gas: 4755672)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 10.60s (11.09s CPU time)\n\nRan 10 test suites in 10.61s (28.42s CPU time): 253 tests passed, 0 failed, 0 skipped (253 total tests)\n","passed":true},{"durationMs":69,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"MoneyBackHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"MoneyBackHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"MoneyBackHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"MoneyBackHook.sweep()\",\"MoneyBackHook.unlockCallback(bytes)\",\"MoneyBackRouter.buyWithEth(uint256,uint256)\",\"MoneyBackRouter.quoteBuy(uint256)\",\"MoneyBackRouter.quoteSell(uint256)\",\"MoneyBackRouter.receive()\",\"MoneyBackRouter.sellForEth(uint256,uint256,uint256)\",\"MoneyBackRouter.unlockCallback(bytes)\",\"MoneyBackToken.approve(address,uint256)\",\"MoneyBackToken.transfer(address,uint256)\",\"MoneyBackToken.transferFrom(address,address,uint256)\",\"RoundPayout.acceptOwnership()\",\"RoundPayout.fund(address,uint256)\",\"RoundPayout.pause()\",\"RoundPayout.payRound(uint256,address,address[],uint256[],bytes32,uint256,uint256)\",\"RoundPayout.renounceOwnership()\",\"RoundPayout.retryFailed(uint256,address[])\",\"RoundPayout.sweep(address,address,uint256)\",\"RoundPayout.transferOwnership(address)\",\"RoundPayout.unpause()\",\"RoundPayout.writeOffFailed(uint256,address)\"],\"files\":{\"README.md\":138,\"assets/moneyback.png\":39,\"assets/moneyback.svg\":8,\"docs/AUDIT_RESPONSES.md\":99,\"docs/LAUNCH_REVIEW.md\":71,\"docs/REVISION.md\":78,\"docs/network-observations.json\":36,\"docs/requester-share.json\":4,\"foundry.toml\":13,\"launch.json\":31,\"script/DeployAuxiliaries.s.sol\":64,\"src/MoneyBackHook.sol\":547,\"src/MoneyBackRouter.sol\":445,\"src/MoneyBackToken.sol\":153,\"src/RoundPayout.sol\":383,\"test/DeployAuxiliaries.t.sol\":73,\"test/MoneyBackHook.t.sol\":1364,\"test/MoneyBackRealV4.t.sol\":573,\"test/MoneyBackRouter.t.sol\":1864,\"test/MoneyBackStatefulV4.t.sol\":662,\"test/MoneyBackToken.t.sol\":375,\"test/MoneyBackTokenLedger.t.sol\":156,\"test/RoundPayout.t.sol\":1327,\"test/TESTING.md\":93,\"test/vendor/README.md\":8,\"test/vendor/solmate/LICENSE\":661,\"test/vendor/solmate/src/auth/Owned.sol\":44,\"test/vendor/v4-core/licenses/BUSL_LICENSE\":63,\"test/vendor/v4-core/licenses/MIT_LICENSE\":7,\"test/vendor/v4-core/src/ERC6909.sol\":90,\"test/vendor/v4-core/src/ERC6909Claims.sol\":23,\"test/vendor/v4-core/src/Extsload.sol\":64,\"test/vendor/v4-core/src/Exttload.sol\":40,\"test/vendor/v4-core/src/NoDelegateCall.sol\":33,\"test/vendor/v4-core/src/PoolManager.sol\":394,\"test/vendor/v4-core/src/ProtocolFees.sol\":71,\"test/vendor/v4-core/src/interfaces/IExtsload.sol\":21,\"test/vendor/v4-core/src/interfaces/IExttload.sol\":15,\"test/vendor/v4-core/src/interfaces/IHooks.sol\":152,\"test/vendor/v4-core/src/interfaces/IPoolManager.sol\":217,\"test/vendor/v4-core/src/interfaces/IProtocolFees.sol\":52,\"test/vendor/v4-core/src/interfaces/callback/IUnlockCallback.sol\":10,\"test/vendor/v4-core/src/interfaces/external/IERC20Minimal.sol\":48,\"test/vendor/v4-core/src/interfaces/external/IERC6909Claims.sol\":66,\"test/vendor/v4-core/src/libraries/BitMath.sol\":49,\"test/vendor/v4-core/src/libraries/CurrencyDelta.sol\":42,\"test/vendor/v4-core/src/libraries/CurrencyReserves.sol\":39,\"test/vendor/v4-core/src/libraries/CustomRevert.sol\":120,\"test/vendor/v4-core/src/libraries/FixedPoint128.sol\":8,\"test/vendor/v4-core/src/libraries/FixedPoint96.sol\":10,\"test/vendor/v4-core/src/libraries/FullMath.sol\":117,\"test/vendor/v4-core/src/libraries/Hooks.sol\":344,\"test/vendor/v4-core/src/libraries/LPFeeLibrary.sol\":79,\"test/vendor/v4-core/src/libraries/LiquidityMath.sol\":20,\"test/vendor/v4-core/src/libraries/Lock.sol\":28,\"test/vendor/v4-core/src/libraries/NonzeroDeltaCount.sol\":35,\"test/vendor/v4-core/src/libraries/ParseBytes.sol\":29,\"test/vendor/v4-core/src/libraries/Pool.sol\":620,\"test/vendor/v4-core/src/libraries/Position.sol\":103,\"test/vendor/v4-core/src/libraries/ProtocolFeeLibrary.sol\":47,\"test/vendor/v4-core/src/libraries/SafeCast.sol\":60,\"test/vendor/v4-core/src/libraries/SqrtPriceMath.sol\":292,\"test/vendor/v4-core/src/libraries/StateLibrary.sol\":345,\"test/vendor/v4-core/src/libraries/SwapMath.sol\":108,\"test/vendor/v4-core/src/libraries/TickBitmap.sol\":121,\"test/vendor/v4-core/src/libraries/TickMath.sol\":238,\"test/vendor/v4-core/src/libraries/UnsafeMath.sol\":29,\"test/vendor/v4-core/src/types/BalanceDelta.sol\":72,\"test/vendor/v4-core/src/types/BeforeSwapDelta.sol\":38,\"test/vendor/v4-core/src/types/Currency.sol\":118,\"test/vendor/v4-core/src/types/PoolId.sol\":17,\"test/vendor/v4-core/src/types/PoolKey.sol\":22,\"test/vendor/v4-core/src/types/PoolOperation.sol\":26,\"test/vendor/v4-core/src/types/Slot0.sol\":95},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"837811e791469239c165ae41d8d3778b7748497ff8bbf452a69fcc1cd2056b78","verifiedTreeHash":"f69d1f3483dbe90286ce117b07d5c2825825ce03","verifierVersion":"0.1.0+0e08a464"},{"checks":[{"durationMs":4355,"exitCode":0,"name":"build","output":"Compiling 57 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.24s\nCompiler run successful!\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/MoneyBackHook.sol:350:13\n    │\n350 │         if (elapsed >= SURCHARGE_DURATION) return 0;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/MoneyBackHook.sol:382:27\n    │\n382 │         _imdIsCurrency0 = imdIsCurrency0;\n    │                           ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/RoundPayout.sol:62:32\n   │\n62 │     function transferOwnership(address newOwner) external onlyOwner {\n   │                                ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:403:42\n    │\n403 │         uint256 specified = exactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:403:77\n    │\n403 │         uint256 specified = exactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/MoneyBackHook.sol:490:9\n    │\n490 │         emit FeeAccrued(isSell, baseFee, surcharge, imdLeg);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:423:46\n    │\n423 │             uint256 specified = exactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:423:81\n    │\n423 │             uint256 specified = exactInput ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:428:39\n    │\n428 │             if (imdDelta != (isSell ? int256(expectedLeg) : -int256(expectedLeg))) revert PartialFill();\n    │                                       ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:428:62\n    │\n428 │             if (imdDelta != (isSell ? int256(expectedLeg) : -int256(expectedLeg))) revert PartialFill();\n    │                                                              ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:432:41\n    │\n432 │         uint256 imdLeg = imdDelta < 0 ? uint256(-imdDelta) : uint256(imdDelta);\n    │                                         ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:432:62\n    │\n432 │         uint256 imdLeg = imdDelta < 0 ? uint256(-imdDelta) : uint256(imdDelta);\n    │                                                              ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackHook.sol:447:9\n    │\n447 │         poolManager.unlock(\"\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/MoneyBackHook.sol:447:9\n    │\n447 │         poolManager.unlock(\"\");\n    │         ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/MoneyBackHook.sol:461:9\n    │\n461 │         emit Swept(amount, payout);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:509:25\n    │\n509 │         if (specified > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:509:33\n    │\n509 │         if (specified > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:516:22\n    │\n516 │         if (imdLeg > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:516:30\n    │\n516 │         if (imdLeg > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:522:17\n    │\n522 │         if (x > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:522:25\n    │\n522 │         if (x > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:523:16\n    │\n523 │         return int128(uint128(x));\n    │                ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackHook.sol:523:23\n    │\n523 │         return int128(uint128(x));\n    │                       ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:388:24\n    │\n388 │         r = abi.decode(poolManager.unlock(data), (Result));\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:421:22\n    │\n421 │         (bool ok,) = to.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/MoneyBackRouter.sol:204:13\n    │\n204 │         if (block.timestamp > deadline) revert Expired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/MoneyBackRouter.sol:212:9\n    │\n212 │         emit BoughtWithEth(msg.sender, r.amountIn, r.imdMid, tokensOut);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/RoundPayout.sol:370:40\n    │\n370 │         (bool ok, bytes memory data) = token.call(abi.encodeCall(IERC20.transferFrom, (from, to, amount)));\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:229:14\n    │\n229 │         if (!IERC20(token).transferFrom(msg.sender, address(this), tokens)) revert TransferFromFailed();\n    │              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:235:29\n    │\n235 │         if (unused != 0 && !IERC20(token).transfer(msg.sender, unused)) revert TransferFailed();\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:194:9\n    │\n194 │         emit Funded(token, msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/MoneyBackRouter.sol:225:13\n    │\n225 │         if (block.timestamp > deadline) revert Expired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/MoneyBackRouter.sol:237:9\n    │\n237 │         emit SoldForEth(msg.sender, r.amountIn, r.imdMid, ethOut);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:227:22\n    │\n227 │         if (tokens > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:227:30\n    │\n227 │         if (tokens > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/MoneyBackRouter.sol:399:13\n    │\n399 │         try poolManager.unlock(data) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/RoundPayout.sol:364:40\n    │\n364 │         (bool ok, bytes memory data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/RoundPayout.sol:198:82\n    │\n198 │     function sweep(address token, address to, uint256 amount) external onlyOwner nonReentrant {\n    │                                                                                  ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:201:9\n    │\n201 │         emit Swept(token, to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/RoundPayout.sol:358:22\n    │\n358 │         (ok, data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/RoundPayout.sol:224:40\n    │\n224 │     ) external onlyOwner whenNotPaused nonReentrant returns (uint256 totalPaid) {\n    │                                        ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:244:9\n    │\n244 │         emit RoundPaid(roundId, token, ledgerHash, twapCloseX96, totalEligibleLoss, totalPaid, n);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/RoundPayout.sol:265:14\n    │\n265 │         if (!anySucceeded) revert AllTransfersFailed(roundId);\n    │              ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/RoundPayout.sol:358:22\n    │\n358 │         (ok, data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:260:17\n    │\n260 │                 emit Paid(roundId, recipient, amount);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:274:9\n    │\n274 │         emit PayFailed(roundId, recipient, amount, reason);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:307:22\n    │\n307 │         r.amountIn = uint256(-a0);\n    │                      ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:308:26\n    │\n308 │         uint256 imdOut = uint256(a1);\n    │                          ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/MoneyBackRouter.sol:313:13\n    │\n313 │             poolManager.settle{value: r.amountIn}();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:321:20\n    │\n321 │         r.imdMid = uint256(-imdDelta);\n    │                    ━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:322:23\n    │\n322 │         r.amountOut = uint256(tokenDelta);\n    │                       ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-no-eth]: external call can be reentered before `failed` is updated\n    ╭▸ src/RoundPayout.sol:358:22\n    │\n358 │         (ok, data) = token.call(abi.encodeCall(IERC20.transfer, (to, amount)));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:338:22\n    │\n338 │         r.amountIn = uint256(-tokenDelta);\n    │                      ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/MoneyBackRouter.sol:342:13\n    │\n342 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:344:26\n    │\n344 │         uint256 imdOut = uint256(imdDelta);\n    │                          ━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:349:20\n    │\n349 │         r.imdMid = uint256(-a1);\n    │                    ━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:350:23\n    │\n350 │         r.amountOut = uint256(a0);\n    │                       ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:361:24\n    │\n361 │         if (amountIn > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:361:32\n    │\n361 │         if (amountIn > uint256(uint128(type(int128).max))) revert AmountOverflow();\n    │                                ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:366:35\n    │\n366 │                 amountSpecified: -int256(amountIn),\n    │                                   ━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/RoundPayout.sol:312:13\n    │\n312 │             needed += owed;\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/RoundPayout.sol:298:9\n    │\n298 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:326:17\n    │\n326 │                 emit Paid(roundId, recipient, amount);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RoundPayout.sol:328:17\n    │\n328 │                 emit PayFailed(roundId, recipient, amount, reason);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/MoneyBackRouter.sol:404:40\n    │\n404 │             if (reason.length == 36 && bytes4(reason) == QuoteResult.selector) {\n    │                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/RoundPayout.sol:311:28\n    │\n311 │             if (owed == 0) revert NothingFailed(roundId, to[i]);\n    │                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/RoundPayout.sol:319:30\n    │\n319 │             if (amount == 0) revert NothingFailed(roundId, recipient);\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/MoneyBackRouter.sol:421:22\n    │\n421 │         (bool ok,) = to.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/RoundPayout.sol:334:77\n    │\n334 │     function writeOffFailed(uint256 roundId, address to) external onlyOwner nonReentrant {\n    │                                                                             ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n     ╭▸ test/MoneyBackRouter.t.sol:1132:24\n     │\n1132 │         uint256 now_ = block.timestamp;\n     │                        ━━━━━━━━━━━━━━━\n1133 │         vm.warp(ts);\n     │         ─────────── `vm.warp` changes this environment here\n     │\n     ├ help: capture it with `vm.getBlockTimestamp()` instead\n     ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n     ╭▸ test/MoneyBackRouter.t.sol:1179:53\n     │\n1179 │         uint256 bought = _buyAs(ALICE, 10 ether, 0, block.timestamp);\n     │                                                     ━━━━━━━━━━━━━━━\n1180 │         vm.warp(T0 + 3600);\n     │         ────────────────── `vm.warp` changes this environment here\n     │\n     ├ help: capture it with `vm.getBlockTimestamp()` instead\n     ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n     ╭▸ test/MoneyBackRouter.t.sol:1538:49\n     │\n1538 │         assertEq(_sellAs(ALICE, tokens, quoted, block.timestamp), quoted, \"sell delivers exactly the quote\");\n     │                                                 ━━━━━━━━━━━━━━━\n1539 │         vm.warp(T0 + 1800);\n     │         ────────────────── `vm.warp` changes this environment here\n     │\n     ├ help: capture it with `vm.getBlockTimestamp()` instead\n     ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":832,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 6 tests for test/DeployAuxiliaries.t.sol:DeployAuxiliariesTest\n[PASS] test_deploysBothAuxiliariesWithPayingWalletOwner() (gas: 2849445)\n[PASS] test_uninitializedHookRefused() (gas: 71375001)\n[PASS] test_unresolvedAddressesRefused() (gas: 61999)\n[PASS] test_wrongChainRefused() (gas: 35325)\n[PASS] test_wrongManagerRefused() (gas: 41055)\n[PASS] test_wrongWalletRefused() (gas: 44951506)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 134.58ms (65.38ms CPU time)\n\nRan 19 tests for test/MoneyBackRealV4.t.sol:MoneyBackRealV4Token0Test\n[PASS] testFuzz_equivalentSwapForms(uint96,uint32,bool) (runs: 256, μ: 421317, ~: 403758)\n[PASS] testFuzz_fullFillSpecifiedFee(uint96,uint32,bool) (runs: 256, μ: 205312, ~: 194677)\n[PASS] test_boundPoolRefusesAnySecondInitialize() (gas: 90976)\n[PASS] test_equivalentBuysAndSellsAtDecayBoundaries() (gas: 3633631)\n[PASS] test_exactInputBuyExhaustedLiquidityReverts() (gas: 595832)\n[PASS] test_exactInputBuyTightLimitRevertsWithoutOvercharge() (gas: 217215)\n[PASS] test_exactOutputSellExhaustedLiquidityReverts() (gas: 593689)\n[PASS] test_exactOutputSellTightLimitRevertsWithoutTakingIMD() (gas: 224147)\n[PASS] test_freshSingleSidedPoolCanBuyAndSweepWithNoIMDReserve() (gas: 92916044)\n[PASS] test_fullFillAllFourCasesAndSweep() (gas: 2289965)\n[PASS] test_liquidityConversionIsOutsideSwapSurcharge() (gas: 567599)\n[PASS] test_partialExactOutputBuyChargesActualIMD() (gas: 565545)\n[PASS] test_partialUnspecifiedIMDStillChargesActualFill() (gas: 590956)\n[PASS] test_policyOpeningPriceSellSurchargeApplies() (gas: 93047666)\n[PASS] test_policyOpeningPriceSingleSidedSeedFirstBuyAndSweep() (gas: 92999921)\n[PASS] test_routerTradesAgainstRealManagerAndRefundsPartialSell() (gas: 2050421)\n[PASS] test_routerUnfillableBuyAndQuoteRevertAtomically() (gas: 1988954)\n[PASS] test_singleSidedPoolRejectsUnfillableExactOutputSell() (gas: 93359140)\n[PASS] test_unboundHookIsBoundByWhoeverInitializesFirst() (gas: 92442671)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 147.41ms (576.63ms CPU time)\n\nRan 19 tests for test/MoneyBackRealV4.t.sol:MoneyBackRealV4Token1Test\n[PASS] testFuzz_equivalentSwapForms(uint96,uint32,bool) (runs: 256, μ: 421640, ~: 415403)\n[PASS] testFuzz_fullFillSpecifiedFee(uint96,uint32,bool) (runs: 256, μ: 206338, ~: 201123)\n[PASS] test_boundPoolRefusesAnySecondInitialize() (gas: 90976)\n[PASS] test_equivalentBuysAndSellsAtDecayBoundaries() (gas: 3633684)\n[PASS] test_exactInputBuyExhaustedLiquidityReverts() (gas: 602557)\n[PASS] test_exactInputBuyTightLimitRevertsWithoutOvercharge() (gas: 223378)\n[PASS] test_exactOutputSellExhaustedLiquidityReverts() (gas: 587084)\n[PASS] test_exactOutputSellTightLimitRevertsWithoutTakingIMD() (gas: 218104)\n[PASS] test_freshSingleSidedPoolCanBuyAndSweepWithNoIMDReserve() (gas: 30394831)\n[PASS] test_fullFillAllFourCasesAndSweep() (gas: 2290933)\n[PASS] test_liquidityConversionIsOutsideSwapSurcharge() (gas: 581036)\n[PASS] test_partialExactOutputBuyChargesActualIMD() (gas: 572169)\n[PASS] test_partialUnspecifiedIMDStillChargesActualFill() (gas: 584246)\n[PASS] test_policyOpeningPriceSellSurchargeApplies() (gas: 30487180)\n[PASS] test_policyOpeningPriceSingleSidedSeedFirstBuyAndSweep() (gas: 30443225)\n[PASS] test_routerTradesAgainstRealManagerAndRefundsPartialSell() (gas: 2049089)\n[PASS] test_routerUnfillableBuyAndQuoteRevertAtomically() (gas: 2016550)\n[PASS] test_singleSidedPoolRejectsUnfillableExactOutputSell() (gas: 30678298)\n[PASS] test_unboundHookIsBoundByWhoeverInitializesFirst() (gas: 29900986)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 156.15ms (347.78ms CPU time)\n\nRan 27 tests for test/MoneyBackToken.t.sol:MoneyBackTokenTest\n[PASS]\nMoneyBackTokenTest invariants:\n[PASS] invariant_balancesSumToSupply\n[PASS] invariant_totalSupplyNeverChanges\n[PASS] invariant_zeroAddressHoldsNothing\n MoneyBackTokenTest invariants (runs: 128, calls: 4096, reverts: 0)\n\n╭--------------+--------------+-------+---------+----------╮\n| Contract     | Selector     | Calls | Reverts | Discards |\n+==========================================================+\n| TokenHandler | approve      | 1409  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| TokenHandler | transfer     | 1346  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| TokenHandler | transferFrom | 1341  | 0       | 0        |\n╰--------------+--------------+-------+---------+----------╯\n\n[PASS] testFuzz_allowanceAccounting(uint256,uint256) (runs: 512, μ: 115534, ~: 134030)\n[PASS] testFuzz_transferAboveBalanceAlwaysReverts(uint256) (runs: 512, μ: 40842, ~: 40840)\n[PASS] testFuzz_transferConservesSupplyAndBalances(address,uint256) (runs: 512, μ: 92967, ~: 93559)\n[PASS] test_approveSetsAllowanceAndEmits() (gas: 97003)\n[PASS] test_approveZeroSpenderReverts() (gas: 30352)\n[PASS] test_constructorMintsWholeSupplyToDeployer() (gas: 17042)\n[PASS] test_deployerIsMsgSenderNotTxOrigin() (gas: 188189)\n[PASS] test_metadata() (gas: 17630)\n[PASS] test_noPrivilegedSurface() (gas: 165545)\n[PASS] test_rejectsEth() (gas: 32817)\n[PASS] test_supplyIsExactlyOneBillionWithEighteenDecimals() (gas: 13317)\n[PASS] test_transferFromDoesNotEmitApprovalOnSpend() (gas: 115689)\n[PASS] test_transferFromEmptyAccountRevertsEvenForOneWei() (gas: 33309)\n[PASS] test_transferFromExactAllowanceLeavesZero() (gas: 144349)\n[PASS] test_transferFromInfiniteAllowanceIsNotDecremented() (gas: 112829)\n[PASS] test_transferFromRevertsWhenAllowanceBelowAmount() (gas: 80374)\n[PASS] test_transferFromRevertsWhenBalanceBelowAmountEvenWithAllowance() (gas: 86141)\n[PASS] test_transferFromRevertsWithoutAllowance() (gas: 33778)\n[PASS] test_transferFromSpendsAllowance() (gas: 125597)\n[PASS] test_transferFromToZeroAddressReverts() (gas: 83503)\n[PASS] test_transferMovesBalanceAndEmits() (gas: 87232)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 40643)\n[PASS] test_transferToSelfKeepsBalance() (gas: 42924)\n[PASS] test_transferToZeroAddressReverts() (gas: 30391)\n[PASS] test_transferWholeBalanceLeavesZero() (gas: 70165)\n[PASS] test_transferZeroAmountSucceeds() (gas: 37089)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 287.71ms (628.23ms CPU time)\n\nRan 60 tests for test/MoneyBackHook.t.sol:MoneyBackHookTest\n[PASS]\nMoneyBackHookTest invariants:\n[PASS] invariant_hookNeverHoldsMoneybackClaims\n[PASS] invariant_lpFeeNeverOverriddenAndManagerLocked\n[PASS] invariant_pendingEqualsAccruedMinusSwept\n[PASS] invariant_sweepPaysOnlyPayout\n MoneyBackHookTest invariants (runs: 64, calls: 2560, reverts: 0)\n\n╭-------------+----------+-------+---------+----------╮\n| Contract    | Selector | Calls | Reverts | Discards |\n+=====================================================+\n| HookHandler | swap     | 857   | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | sweep    | 837   | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | warp     | 866   | 0       | 0        |\n╰-------------+----------+-------+---------+----------╯\n\n[PASS] testFuzz_feeMathsAllCases(uint8,uint256,uint32) (runs: 1000, μ: 172391, ~: 175196)\n[PASS] testFuzz_sequenceAccumulatesPending(uint256,uint256,uint256,uint32) (runs: 256, μ: 328128, ~: 330090)\n[PASS] testFuzz_specifiedOneWeiUnderfillAlwaysRefused(uint96,uint32,bool) (runs: 256, μ: 65754, ~: 65734)\n[PASS] testFuzz_surchargeFormula(uint32) (runs: 1000, μ: 18995, ~: 18699)\n[PASS] testFuzz_surchargeIsMonotoneNonIncreasing(uint32,uint32) (runs: 512, μ: 21741, ~: 21807)\n[PASS] testFuzz_tinyAmountsRespectRoundingBounds(uint8,uint256,uint32) (runs: 512, μ: 124914, ~: 116104)\n[PASS] test_acceptanceSellCostAtT0AndAfterDecay() (gas: 309530)\n[PASS] test_afterSwapWithZeroPoolDeltaAccruesNothing() (gas: 64200)\n[PASS] test_allCasesWithImdAsCurrency1() (gas: 36104180)\n[PASS] test_buysNeverSurchargedAtAnyTime() (gas: 504482)\n[PASS] test_callbacksRejectNonPoolManager() (gas: 159864)\n[PASS] test_case1_exactInputBuy_t0() (gas: 175005)\n[PASS] test_case1_exactInputBuy_t1800() (gas: 174616)\n[PASS] test_case1_exactInputBuy_t1day() (gas: 174635)\n[PASS] test_case1_exactInputBuy_t900() (gas: 175037)\n[PASS] test_case2_exactOutputBuy_t0() (gas: 172835)\n[PASS] test_case2_exactOutputBuy_t1800() (gas: 172381)\n[PASS] test_case2_exactOutputBuy_t1day() (gas: 172312)\n[PASS] test_case2_exactOutputBuy_t900() (gas: 172778)\n[PASS] test_case3_exactInputSell_t0() (gas: 173663)\n[PASS] test_case3_exactInputSell_t1800() (gas: 172962)\n[PASS] test_case3_exactInputSell_t1day() (gas: 173003)\n[PASS] test_case3_exactInputSell_t900() (gas: 173682)\n[PASS] test_case4_exactOutputSell_t0() (gas: 175682)\n[PASS] test_case4_exactOutputSell_t1800() (gas: 174600)\n[PASS] test_case4_exactOutputSell_t1day() (gas: 174575)\n[PASS] test_case4_exactOutputSell_t900() (gas: 175715)\n[PASS] test_constructorRejectsImdAsLaunchToken() (gas: 63997)\n[PASS] test_constructorRejectsWrongFlagBits() (gas: 115010453)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 182731)\n[PASS] test_constructorStoresImmutablesAndConstants() (gas: 58919)\n[PASS] test_deploymentAddressFlagBitsMatchPermissions() (gas: 18488)\n[PASS] test_emptySweepSucceedsAndEmitsZero() (gas: 75461)\n[PASS] test_exactOutputGrossLegOverflowRevertsBeforeMint() (gas: 107257)\n[PASS] test_feeAccruedEventsPerCase() (gas: 410040)\n[PASS] test_fixedCallbacksOnlyChargeTheirOwnCases() (gas: 165507)\n[PASS] test_hookNeverMintsMoneybackClaimsEvenIfGiftedImdClaims() (gas: 295089)\n[PASS] test_hookRejectsEthAndHasNoAdminSurface() (gas: 169036)\n[PASS] test_hugeSpecifiedAmountRevertsAmountOverflow() (gas: 98731)\n[PASS] test_initializeAcceptsEitherCurrencyOrder() (gas: 46362180)\n[PASS] test_initializeBindsPoolAndRecordsTimestamp() (gas: 58346)\n[PASS] test_initializeEmitsPoolBound() (gas: 46367154)\n[PASS] test_initializeRejectsSecondPool() (gas: 74581)\n[PASS] test_initializeRejectsWrongKeys() (gas: 47724457)\n[PASS] test_specifiedFeesAtInt128Boundary() (gas: 208889)\n[PASS] test_specifiedZeroFeeUnderfillRefused() (gas: 53628)\n[PASS] test_surchargeAt1799IsStillChargedOnSells() (gas: 136266)\n[PASS] test_surchargeAt1800IsZeroOnSells() (gas: 135401)\n[PASS] test_surchargeBoundaries() (gas: 83038)\n[PASS] test_swapBeforeInitializationReverts() (gas: 46393621)\n[PASS] test_swapRejectsWrongPoolKey() (gas: 260875)\n[PASS] test_sweepAccumulatesAcrossRounds() (gas: 454150)\n[PASS] test_sweepCannotBeNestedInsideAnotherUnlock() (gas: 774909)\n[PASS] test_sweepReentrancyFromPayoutTransferIsRejected() (gas: 760265)\n[PASS] test_sweepRevertsCleanlyIfTakeFails() (gas: 453897)\n[PASS] test_sweepTakesAllImdClaimsToPayout() (gas: 452883)\n[PASS] test_unlockCallbackOutsideSweepIsRejected() (gas: 167674)\n[PASS] test_viewsBeforeInitialization() (gas: 46220834)\n[PASS] test_zeroAmountSwapAccruesNothing() (gas: 205472)\nSuite result: ok. 60 passed; 0 failed; 0 skipped; finished in 367.81ms (1.24s CPU time)\n\nRan 56 tests for test/RoundPayout.t.sol:RoundPayoutTest\n[PASS]\nRoundPayoutTest invariants:\n[PASS] invariant_balanceEqualsFundedMinusPaidMinusSwept\n[PASS] invariant_ownerNeverChangesWithoutHandoff\n[PASS] invariant_roundRecordsMatchLedger\n[PASS] invariant_sumPaidNeverExceedsFunded\n RoundPayoutTest invariants (runs: 96, calls: 3840, reverts: 19)\n\n╭---------------+---------------+-------+---------+----------╮\n| Contract      | Selector      | Calls | Reverts | Discards |\n+============================================================+\n| PayoutHandler | fund          | 459   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | pay           | 420   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | replay        | 429   | 19      | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | retry         | 410   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | setBlocked    | 407   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | strangerCalls | 431   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | sweep         | 414   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | togglePause   | 441   | 0       | 0        |\n|---------------+---------------+-------+---------+----------|\n| PayoutHandler | writeOff      | 429   | 0       | 0        |\n╰---------------+---------------+-------+---------+----------╯\n\n[PASS] testFuzz_batchSizeBoundary(uint16) (runs: 256, μ: 2141604, ~: 1374684)\n[PASS] testFuzz_payRoundAccounting(uint256,uint256,uint256,uint256,uint8) (runs: 512, μ: 318031, ~: 231431)\n[PASS] test_allDuplicateFailuresStillRevert() (gas: 226537)\n[PASS] test_allTransfersFailedReverts() (gas: 349732)\n[PASS] test_constructorRejectsZeroOwner() (gas: 4100)\n[PASS] test_constructorSetsOwner() (gas: 22667)\n[PASS] test_duplicateFailedRecipientAccumulates() (gas: 387635)\n[PASS] test_duplicateFailedRecipientRetryClearsWholeDebtAndCount() (gas: 683078)\n[PASS] test_duplicateFailedRecipientWriteOffClearsWholeDebtAndCount() (gas: 500642)\n[PASS] test_fundAcceptsNoReturnToken() (gas: 476949)\n[PASS] test_fundPullsTokensFromAnyone() (gas: 174918)\n[PASS] test_fundRejectsEoaToken() (gas: 38646)\n[PASS] test_fundRejectsFalseReturningToken() (gas: 196919)\n[PASS] test_fundWithoutApprovalReverts() (gas: 96318)\n[PASS] test_fundZeroAmountSucceeds() (gas: 47919)\n[PASS] test_ownable2StepHandoff() (gas: 285110)\n[PASS] test_partialFailureIsRecordedAndBatchContinues() (gas: 430837)\n[PASS] test_partialFailureSilentRevertAndFalseReturn() (gas: 454037)\n[PASS] test_pauseTwiceAndUnpauseWhenRunningRevert() (gas: 108689)\n[PASS] test_payRoundBatchTooLarge() (gas: 283154)\n[PASS] test_payRoundBlockedWhilePaused() (gas: 388431)\n[PASS] test_payRoundEmptyBatch() (gas: 50414)\n[PASS] test_payRoundExactBalanceSucceeds() (gas: 329414)\n[PASS] test_payRoundIdempotent() (gas: 668322)\n[PASS] test_payRoundInsufficientBalanceUpFront() (gas: 127472)\n[PASS] test_payRoundIsTokenAgnosticPerRound() (gas: 821078)\n[PASS] test_payRoundLengthMismatch() (gas: 47260)\n[PASS] test_payRoundMaxBatchOf500() (gas: 17388182)\n[PASS] test_payRoundPaysEveryLegAndRecords() (gas: 404660)\n[PASS] test_payRoundRoundIdZeroIsOrdinary() (gas: 287441)\n[PASS] test_payRoundSumOverflowReverts() (gas: 48383)\n[PASS] test_payRoundWithEoaTokenReverts() (gas: 47866)\n[PASS] test_payRoundWithNoReturnToken() (gas: 539387)\n[PASS] test_payRoundZeroAmountLegIsPaid() (gas: 162387)\n[PASS] test_payRoundZeroToken() (gas: 44994)\n[PASS] test_privilegedFunctionsRejectNonOwner() (gas: 165748)\n[PASS] test_reentrantTokenCannotReenterFundOrSweepDuringPayRound() (gas: 840049)\n[PASS] test_reentrantTokenCannotReenterPayRound() (gas: 699804)\n[PASS] test_renounceOwnershipLocksEverything() (gas: 254602)\n[PASS] test_retryFailedInsufficientBalanceAfterSweep() (gas: 525680)\n[PASS] test_retryFailedPaysAndClears() (gas: 574104)\n[PASS] test_retryFailedRevertsForUnpaidRound() (gas: 43015)\n[PASS] test_retryFailedShapeChecks() (gas: 806977)\n[PASS] test_retryFailedStillFailingKeepsRecord() (gas: 467231)\n[PASS] test_singleLegFailureIsAllTransfersFailed() (gas: 188077)\n[PASS] test_sweepAboveBalanceReverts() (gas: 116001)\n[PASS] test_sweepByOwnerMovesTokens() (gas: 155282)\n[PASS] test_sweepRejectsShortReturnData() (gas: 174911)\n[PASS] test_sweepRejectsZeroRecipient() (gas: 101034)\n[PASS] test_sweepWorksWhilePaused() (gas: 185829)\n[PASS] test_transferOwnershipOnlyOwnerAndCancelable() (gas: 142657)\n[PASS] test_writeOffFailedClearsWithoutPaying() (gas: 517696)\n[PASS] test_writeOffFailedNothingOwedReverts() (gas: 40337)\n[PASS] test_zeroAmountFailuresCreateNoOutstandingRecipient() (gas: 342693)\n[PASS] test_zeroAmountSuccessPreventsAllFailedRevert() (gas: 352531)\nSuite result: ok. 56 passed; 0 failed; 0 skipped; finished in 468.40ms (788.00ms CPU time)\n\nRan 52 tests for test/MoneyBackRouter.t.sol:MoneyBackRouterTest\n[PASS]\nMoneyBackRouterTest invariants:\n[PASS] invariant_bindingsNeverChange\n[PASS] invariant_hookPendingEqualsRouterFeesMinusSwept\n[PASS] invariant_routerHoldsNothing\n[PASS] invariant_valueIsConserved\n MoneyBackRouterTest invariants (runs: 64, calls: 2560, reverts: 0)\n\n╭---------------+----------+-------+---------+----------╮\n| Contract      | Selector | Calls | Reverts | Discards |\n+=======================================================+\n| RouterHandler | buy      | 544   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| RouterHandler | quote    | 522   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| RouterHandler | sell     | 482   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| RouterHandler | sweep    | 514   | 0       | 0        |\n|---------------+----------+-------+---------+----------|\n| RouterHandler | warp     | 498   | 0       | 0        |\n╰---------------+----------+-------+---------+----------╯\n\n[PASS] testFuzz_buyMatchesFormulaAndLeavesNothing(uint256,uint32,uint256,uint256) (runs: 600, μ: 1308118, ~: 1343789)\n[PASS] testFuzz_sellMatchesFormulaAndLeavesNothing(uint256,uint32,uint256,uint256) (runs: 600, μ: 1508508, ~: 1526247)\n[PASS] test_acceptanceRouterBuyPaysHookExactlyLikeDirectImdBuy() (gas: 1743717)\n[PASS] test_beforePoolBoundEverythingReverts() (gas: 10489440)\n[PASS] test_buyAboveInt128RevertsAmountOverflow() (gas: 1027153)\n[PASS] test_buyAndSellRevertOnDeadline() (gas: 1396894)\n[PASS] test_buyHappyPath() (gas: 754133)\n[PASS] test_buyRefundsEthDustWhenPublicPoolFillsPartially() (gas: 738812)\n[PASS] test_buyRejectsPartialLaunchFillAndRollsBackBothLegs() (gas: 770020)\n[PASS] test_buyRejectsPartialLaunchFillEvenWithPartialPublicFill() (gas: 816982)\n[PASS] test_buyRevertsOnMinOut() (gas: 2020115)\n[PASS] test_buySwapLegsAreExactInputWithWidestLimitsAndRouterAsSender() (gas: 633160)\n[PASS] test_buyThenSellRoundTrip() (gas: 1302853)\n[PASS] test_buyTwiceAndByTwoUsers() (gas: 1752780)\n[PASS] test_buyWorksWithImdAsCurrency1() (gas: 9937027)\n[PASS] test_constructorRejectsZeroAddresses() (gas: 1005804)\n[PASS] test_constructorStoresImmutablesFromHook() (gas: 39385)\n[PASS] test_contractWithoutReceiveCanBuyFullFillOnly() (gas: 2498756)\n[PASS] test_deadlineIsCheckedBeforeAmount() (gas: 65550)\n[PASS] test_directTokenGiftsRemainUnrecoverable() (gas: 2374002)\n[PASS] test_feesFromRouterTradesAreSweepableToPayout() (gas: 1465812)\n[PASS] test_forcedEthGoesToNextTrader() (gas: 549206)\n[PASS] test_guardResetsAfterReverts() (gas: 2106373)\n[PASS] test_launchPoolKeyComesFromHook() (gas: 24076)\n[PASS] test_managerThatNeverCallsBackIsRejected() (gas: 320438)\n[PASS] test_publicPoolKeyIsHardcoded() (gas: 7323)\n[PASS] test_quoteBubblesUpForeignReverts() (gas: 15859555)\n[PASS] test_quoteBuyMatchesBuyAndChangesNothing() (gas: 3113722)\n[PASS] test_quoteCannotBeCalledWhileLocked() (gas: 839997)\n[PASS] test_quoteSellMatchesSellAndChangesNothing() (gas: 3301026)\n[PASS] test_quoteZeroReturnsZeroWithoutTouchingTheManager() (gas: 61476)\n[PASS] test_receiveRejectsEthFromAnyoneButTheManager() (gas: 775245)\n[PASS] test_reentrancyFromBuyDustRefundIsBlocked() (gas: 5521303)\n[PASS] test_reentrancyFromSellProceedsIsBlocked() (gas: 5539511)\n[PASS] test_routerHasNoAdminSurface() (gas: 163696)\n[PASS] test_sellAfterSurchargeDecayPaysOnlyBaseFee() (gas: 856182)\n[PASS] test_sellHappyPathAtT0WithSurcharge() (gas: 855788)\n[PASS] test_sellPrefundsRouterThenSettlesAndRefundsFromItsOwnBalance() (gas: 827408)\n[PASS] test_sellPullsExactlyTokensAndKeepsNoAllowance() (gas: 694656)\n[PASS] test_sellRefundsBothDusts() (gas: 925165)\n[PASS] test_sellRefundsImdDustWhenPublicPoolFillsPartially() (gas: 877077)\n[PASS] test_sellRefundsTokenDustWhenOurPoolFillsPartially() (gas: 863321)\n[PASS] test_sellRevertsOnMinOut() (gas: 1570902)\n[PASS] test_sellSwapLegsAreExactInputWithWidestLimits() (gas: 745732)\n[PASS] test_sellWhereSecondLegYieldsNothingRefundsNothingAndReverts() (gas: 942502)\n[PASS] test_sellWithoutAllowanceOrBalanceReverts() (gas: 334704)\n[PASS] test_tamperedCallbackDataIsRejected() (gas: 1062246)\n[PASS] test_tinyBuyThatYieldsNothingReverts() (gas: 2158204)\n[PASS] test_unlockCallbackRejectsManagerWithNoCallInFlight() (gas: 120933)\n[PASS] test_unlockCallbackRejectsNonManager() (gas: 59848)\n[PASS] test_zeroValueBuyAndZeroTokenSellRevert() (gas: 237280)\nSuite result: ok. 52 passed; 0 failed; 0 skipped; finished in 764.41ms (936.37ms CPU time)\n\nRan 7 test suites in 767.72ms (2.33s CPU time): 239 tests passed, 0 failed, 0 skipped (239 total tests)\n","passed":true},{"durationMs":54,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"MoneyBackHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"MoneyBackHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"MoneyBackHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"MoneyBackHook.sweep()\",\"MoneyBackHook.unlockCallback(bytes)\",\"MoneyBackRouter.buyWithEth(uint256,uint256)\",\"MoneyBackRouter.quoteBuy(uint256)\",\"MoneyBackRouter.quoteSell(uint256)\",\"MoneyBackRouter.receive()\",\"MoneyBackRouter.sellForEth(uint256,uint256,uint256)\",\"MoneyBackRouter.unlockCallback(bytes)\",\"MoneyBackToken.approve(address,uint256)\",\"MoneyBackToken.transfer(address,uint256)\",\"MoneyBackToken.transferFrom(address,address,uint256)\",\"RoundPayout.acceptOwnership()\",\"RoundPayout.fund(address,uint256)\",\"RoundPayout.pause()\",\"RoundPayout.payRound(uint256,address,address[],uint256[],bytes32,uint256,uint256)\",\"RoundPayout.renounceOwnership()\",\"RoundPayout.retryFailed(uint256,address[])\",\"RoundPayout.sweep(address,address,uint256)\",\"RoundPayout.transferOwnership(address)\",\"RoundPayout.unpause()\",\"RoundPayout.writeOffFailed(uint256,address)\"],\"files\":{\"README.md\":138,\"assets/moneyback.png\":39,\"assets/moneyback.svg\":8,\"docs/AUDIT_RESPONSES.md\":99,\"docs/LAUNCH_REVIEW.md\":71,\"docs/REVISION.md\":78,\"docs/network-observations.json\":36,\"docs/requester-share.json\":4,\"foundry.toml\":13,\"launch.json\":31,\"script/DeployAuxiliaries.s.sol\":64,\"src/MoneyBackHook.sol\":547,\"src/MoneyBackRouter.sol\":445,\"src/MoneyBackToken.sol\":153,\"src/RoundPayout.sol\":383,\"test/DeployAuxiliaries.t.sol\":73,\"test/MoneyBackHook.t.sol\":1360,\"test/MoneyBackRealV4.t.sol\":568,\"test/MoneyBackRouter.t.sol\":1860,\"test/MoneyBackToken.t.sol\":372,\"test/RoundPayout.t.sol\":1318,\"test/vendor/README.md\":8,\"test/vendor/solmate/LICENSE\":661,\"test/vendor/solmate/src/auth/Owned.sol\":44,\"test/vendor/v4-core/licenses/BUSL_LICENSE\":63,\"test/vendor/v4-core/licenses/MIT_LICENSE\":7,\"test/vendor/v4-core/src/ERC6909.sol\":90,\"test/vendor/v4-core/src/ERC6909Claims.sol\":23,\"test/vendor/v4-core/src/Extsload.sol\":64,\"test/vendor/v4-core/src/Exttload.sol\":40,\"test/vendor/v4-core/src/NoDelegateCall.sol\":33,\"test/vendor/v4-core/src/PoolManager.sol\":394,\"test/vendor/v4-core/src/ProtocolFees.sol\":71,\"test/vendor/v4-core/src/interfaces/IExtsload.sol\":21,\"test/vendor/v4-core/src/interfaces/IExttload.sol\":15,\"test/vendor/v4-core/src/interfaces/IHooks.sol\":152,\"test/vendor/v4-core/src/interfaces/IPoolManager.sol\":217,\"test/vendor/v4-core/src/interfaces/IProtocolFees.sol\":52,\"test/vendor/v4-core/src/interfaces/callback/IUnlockCallback.sol\":10,\"test/vendor/v4-core/src/interfaces/external/IERC20Minimal.sol\":48,\"test/vendor/v4-core/src/interfaces/external/IERC6909Claims.sol\":66,\"test/vendor/v4-core/src/libraries/BitMath.sol\":49,\"test/vendor/v4-core/src/libraries/CurrencyDelta.sol\":42,\"test/vendor/v4-core/src/libraries/CurrencyReserves.sol\":39,\"test/vendor/v4-core/src/libraries/CustomRevert.sol\":120,\"test/vendor/v4-core/src/libraries/FixedPoint128.sol\":8,\"test/vendor/v4-core/src/libraries/FixedPoint96.sol\":10,\"test/vendor/v4-core/src/libraries/FullMath.sol\":117,\"test/vendor/v4-core/src/libraries/Hooks.sol\":344,\"test/vendor/v4-core/src/libraries/LPFeeLibrary.sol\":79,\"test/vendor/v4-core/src/libraries/LiquidityMath.sol\":20,\"test/vendor/v4-core/src/libraries/Lock.sol\":28,\"test/vendor/v4-core/src/libraries/NonzeroDeltaCount.sol\":35,\"test/vendor/v4-core/src/libraries/ParseBytes.sol\":29,\"test/vendor/v4-core/src/libraries/Pool.sol\":620,\"test/vendor/v4-core/src/libraries/Position.sol\":103,\"test/vendor/v4-core/src/libraries/ProtocolFeeLibrary.sol\":47,\"test/vendor/v4-core/src/libraries/SafeCast.sol\":60,\"test/vendor/v4-core/src/libraries/SqrtPriceMath.sol\":292,\"test/vendor/v4-core/src/libraries/StateLibrary.sol\":345,\"test/vendor/v4-core/src/libraries/SwapMath.sol\":108,\"test/vendor/v4-core/src/libraries/TickBitmap.sol\":121,\"test/vendor/v4-core/src/libraries/TickMath.sol\":238,\"test/vendor/v4-core/src/libraries/UnsafeMath.sol\":29,\"test/vendor/v4-core/src/types/BalanceDelta.sol\":72,\"test/vendor/v4-core/src/types/BeforeSwapDelta.sol\":38,\"test/vendor/v4-core/src/types/Currency.sol\":118,\"test/vendor/v4-core/src/types/PoolId.sol\":17,\"test/vendor/v4-core/src/types/PoolKey.sol\":22,\"test/vendor/v4-core/src/types/PoolOperation.sol\":26,\"test/vendor/v4-core/src/types/Slot0.sol\":95},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"888d7f8553e676ab3f95d6650389f4a8685d431765cfae0ed2e0954eee1f7ab5","verifiedTreeHash":"c4728bdba8721c24ec8ba7dfef1602a06821299e","verifierVersion":"0.1.0+0e08a464"}]}