{"assessments":[],"deployments":[{"attestationHash":"178db2527a3dcfe49d607562fac8b855f9dad10dbece61f67752a5b75bdeb831","chainId":4663,"contracts":[{"address":"0x92259cf12826a8ad3d1de67fc5a01c3a29e6a0cc","blockNumber":111284489,"name":"TestHook","role":"hook","txHash":"0x40b220f3bb182c35f731d544700055de5570953c30191b13aacf9496e4ffbfba"},{"address":"0x583c0e9d02d5e9e4f82915151ab57fc0a683cc46","blockNumber":111284489,"name":"TestToken","role":"token","txHash":"0x40b220f3bb182c35f731d544700055de5570953c30191b13aacf9496e4ffbfba"},{"address":"0x46ba5b5aeb2799331e0ceeafe79bf4a56335c7b0","blockNumber":111284489,"name":"MerkleDistributor","role":"distributor","txHash":"0x40b220f3bb182c35f731d544700055de5570953c30191b13aacf9496e4ffbfba"}],"id":"74bfcc03-f6e2-4184-884e-7d6f7da46b42","manifestHash":"57d7102605fee746a66c7125877e60e88b4b0852d2aba55b8af19125c7d493d5","status":"live"}],"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":"a2baa685-1529-4885-8e78-285cdab32763","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"bb73d04229f2f9bccc66c345b229f474495dd4e93da7988b73490118a5293026","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"e3db53d0478a2108263dbbddd8c811f5b54c2579455d8338708ece15d2102c41","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"bcb8ba4c119213ecc0c529cb159dd844fba0195c3db58abf58fd9f7d37b54b12","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"b89279670f5a00f5d26286edee00c97dd158b997031350a01cafe6e0329ad227","skillId":"audit-judge","subagents":4,"tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"b89279670f5a00f5d26286edee00c97dd158b997031350a01cafe6e0329ad227","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"5130be2c7cc3c0c642899bcd030b28378682dfb634a3df6d0876bc5f34a564d3","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"bd3c77b5f731421553a74a2303167b0a577a82f5ef668cd62cc52c62956610c0","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","subagents":3,"tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"bc5bfc8455464381f2db18badb2b55cf8bec1e535c019349a0378fa990741171","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"6f4e0ea79137861abd60bae5abf547a43bcfe9922fc20498d032bdaf04542552","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"15b04fc8f60521936b9e2b37c09a804685ec689fc98cd5b04510d986d3106a15","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"15b04fc8f60521936b9e2b37c09a804685ec689fc98cd5b04510d986d3106a15","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"74b59b1c2838b829c95213d435bfacf4ce8545833b66ce8352f5e8f6846d6c73","dependsOn":["build_contract_project","write_foundry_tests"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"9bca83b74aee3ce5a082c42d27572b3068856577f7596edc507024b25583516e","dependsOn":["build_contract_project"],"execution":{"forkChains":[4663],"network":false,"profile":"foundry","requires":[],"skillHash":"1ddb4d02eb75e52d5f38cfd5c9bb54f332687b81929c68880cf1e8a6ab27254d","skillId":"write-foundry-tests","subagents":4,"tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"1ddb4d02eb75e52d5f38cfd5c9bb54f332687b81929c68880cf1e8a6ab27254d","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Token name: SIMD TEST 3 - DO NOT BUY\r\nToken symbol: SIMDTEST3\r\nLogo: https://www.si-md.xyz/logo.jpg\r\n\r\nTHIS IS A TEST TOKEN, NOT A REAL COIN. It has no value and must not be bought. It tests the SIMD relaunch on Robinhood Chain: a plain token whose pool hook sends a small fee to the SIMD Test Vault, which splits it every week between stakers, Identity.md swarm jobs, the dev and marketing. The launch's remainder is held by a claim contract.\r\n\r\nWho can change it: nobody. No owner, no admin, no setters, no upgrade path, no pause, no blacklist. Every address and rate is fixed at deploy.\r\n\r\nContracts:\r\n1. TestHook, the Uniswap v4 hook on the SIMDTEST3/IMD pool this launch opens. On every buy and every sell it takes 0.75% of the swap from the IMD side and keeps it in IMD (do NOT sell it for ETH), sent to the SIMD Test Vault at 0x8Fe1805a83bCF70F514bC716Cd88C05051d6fF21. Together with the factory's 1.25% trading fee a trade costs 2% in total. There is no anti-sniper tax and no time or block logic of any kind: the rate is always the same.\r\n   - The hook never reverts a real swap. No other contracts.\r\n\r\nTests: fork tests against Robinhood Chain: the 0.75% on buys and sells lands in the vault in IMD, the rate never changes, and nothing can redirect the fee.\r\n","parentJobId":null,"planHash":"8f1501bf3c38fb3712bed0caad46f6d8516031be8a5ed57153d3544275a0e8cb","previousHash":"05f58b0ec66b63614147654c81071cb89598bd85abcda27b69f0794693aeb239","projectId":"a2baa685-1529-4885-8e78-285cdab32763","publication":{"commit":"f79f473874baad59142449dd70064d34f755d5d1","deliveredAt":"2026-11-11T00:05:40.556Z","repoUrl":"https://github.com/identity-md-launches/launch-3195-simd-test-3-do-not-buy"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[],"site":null,"snapshotHash":"c8ed75ae2074059c0bf1f67811dbea5a67a2913f1dd45cabd3e235fb2720008f","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"03845cacb54c3a73","findings":[{"citation":"resolved","description":"All four fee formulas floor (FullMath.mulDiv at src/TestHook.sol:110, 111, 154, 158, 183, 185). The Math Precision guide's rule is that fees round up; here they round down, so the vault receives floor(gross*75/10000) rather than ceil. Boundary x precision seam: for any gross IMD amount below 134 wei the fee is exactly 0, and for every larger swap the vault loses the fractional wei. The identity floor(75*(C+floor(75C/9925))/10000) == floor(75C/9925) holds for all C, so the four modes are mutually consistent and the under-collection never exceeds 1 wei per swap; it is not compoundable and cannot be farmed (a 133-wei swap costs far more in gas than the 0.01 wei it saves). The README documents 'Trades below the rounding threshold can owe zero fee', so this is a design choice rather than an exploit; it is reported because it is the one failure mode in the assigned guides the code actually exhibits.","line":154,"path":"src/TestHook.sol","reproduction":"Offline fixture (test/helpers/HookFixture.sol, either currency order, pool seeded at price 1:1 with 1e24 liquidity). 1) actor.swap(key, {zeroForOne: buy, amountSpecified: -133, sqrtPriceLimitX96: MIN+1}): beforeSwap computes proposedFee = mulDiv(133, 75, 10000) = 0, quote executes 133, fee = 0; vault balance and pendingFees unchanged, user pays 133 IMD for tokens with 0 hook fee although the stated rate is 0.75%. 2) Same with amountSpecified = -134: proposedFee = 1, vault receives exactly 1 wei. 3) amountSpecified = -1 ether + 1 (999999999999999999): proposedFee = floor(74999999999999999.925) = 74999999999999999, i.e. 0.925 wei less than 75 bps. Expected under a round-up policy: 1, 2 and 75000000000000000 wei respectively. Verified with test/scratch/MathSeams.t.sol test_dustGrossFeeIsZeroBelow134 (passes on current code, confirming the floor behaviour).","severity":"low","snippet":"            proposedFee = FullMath.mulDiv(requested, FEE_BPS, BPS);","title":"Hook fee rounds down in the trader's favour: gross IMD below 134 wei pays no fee, and every swap under-collects up to 1 wei"},{"citation":"resolved","description":"The hook takes 75 bps of the gross IMD side and v4 takes 12500 pips of the AMM input. On a buy the AMM input is the IMD left after the hook fee, so the LP fee is 1.25% of 99.25% = 1.240625% and the total is 1.990625%, not 2%. On a sell the LP fee is paid in SIMDTEST3 on the input and the hook fee in IMD on the output, so the two percentages apply to different bases and again do not sum to a flat 2% of one amount. No funds are lost or misdirected; the README's 'Exact fee accounting' section already states this composition. Reported so the brief's wording ('a trade costs 2% in total') is reconciled with the deployed rates before publication; if a flat 2% of gross IMD were intended, FEE_BPS would need to be 75.95 bps on buys, which is not representable with BPS = 10000.","line":109,"path":"src/TestHook.sol","reproduction":"Offline fixture, token-first or IMD-first order, pool seeded with 1e24 liquidity at 1:1. actor.swap(key, buy exact input, amountSpecified = -100 ether): vault receives 0.75 ether IMD (FeePaid), the AMM receives 99.25 ether, and actor.modify(key,-600,600,0) afterwards collects 1.240625 ether IMD of LP fees (test/TestHook.t.sol test_revertedQuoteDoesNotDoubleLPFees asserts 1.240625 ether +/- 1 wei). Total fee on the 100 IMD paid = 1.990625 IMD = 1.990625%, versus the 2.000000 IMD the brief's wording implies.","severity":"info","snippet":"            fee = imdDelta < 0\n                ? FullMath.mulDiv(uint256(-imdDelta), FEE_BPS, BPS - FEE_BPS)\n                : FullMath.mulDiv(uint256(imdDelta), FEE_BPS, BPS);","title":"Brief's '2% in total' is nominal: the composed cost of a trade is 1.990625%, because the 1.25% LP fee applies to the IMD remaining after the 0.75% hook fee (buys) or to the token side (sells)"},{"citation":"resolved","description":"The brief says the hook never reverts a real swap. For a sell with exact IMD output, the hook grosses the request up by 75/9925 before quoting; when requested + proposedFee would exceed int256.max it reverts instead of letting v4 partially fill to the price limit. Boundary x invariant seam, but the threshold (about 5.7e76 wei, 10^54 times the 69,420 IMD that exist on Robinhood Chain per totalSupply() at the current block) cannot correspond to a real order, and rejecting it is safer than wrapping the sign into an exact-input swap. No change recommended; recorded so the 'never reverts' guarantee is read with this documented exception.","line":161,"path":"src/TestHook.sol","reproduction":"Offline fixture. vm.prank(address(manager)); hook.beforeSwap(address(this), key, SwapParams({zeroForOne: sell direction, amountSpecified: type(int256).max, sqrtPriceLimitX96: MAX-1}), \"\") reverts with SwapAmountTooLarge() (test/TestHook.t.sol test_unrepresentableExactOutputCannotWrapIntoExactInput). A plain v4 pool without the hook partially fills the same request to the price limit. Any requested value with requested + floor(requested*75/9925) > 2^255-1, i.e. requested > about 5.745e76, triggers it; requested = 5.7e76 - 1e60 does not.","severity":"info","snippet":"            if (requested > uint256(type(int256).max) - proposedFee) revert SwapAmountTooLarge();","title":"Exact-output IMD requests above int256.max*9925/10000 are rejected by the hook (SwapAmountTooLarge) although v4 alone would partially fill them"}],"hash":"5130be2c7cc3c0c642899bcd030b28378682dfb634a3df6d0876bc5f34a564d3","nodeId":"218a0c6d-0d94-4ae9-8cf5-14d4e0449fef","outcome":"completed","summary":"The review is complete and the findings file is written and validated.\n\n**Outcome.** The hook's fee arithmetic holds. No critical, high or medium defect was found in the Math Precision, Boundary or Numerical Gap areas. The findings file at `.imd-findings.json` holds one low and two info findings, plus a coverage record answering all eight entry points.\n\n**What I verified**\n\n- The four fee formulas are mutually consistent. I proved the identity `floor(75·(C + floor(75C/9925))/10000) == floor(75C/9925)`, so buys, sells, exact input and exact output all collect exactly `floor(gross IMD × 75 / 10000)`.\n- The reverted self-quote always leaves the real swap at least the quoted budget, so partial fills execute the same amount in both. A scratch fuzz with disjoint liquidity positions and random price limits passed in both currency orders over a thousand runs.\n- A 0.1% protocol fee layered on the pool does not change the hook rate, and the reverted quote accrues no protocol or LP fees.\n- Deferred claims stay backed by the manager's IMD and redeem only to the vault.\n- The live IMD token on Robinhood Chain is a LayerZero OFT with no pause, blocklist or proxy slot. A scratch fork at a recent block paid the vault in live IMD in all four modes and both orders.\n\n**Findings written**\n\n1. Low: fees round down, so gross IMD below 134 wei pays nothing and every swap under-collects up to 1 wei. Documented in the README, not farmable.\n2. Info: the brief's \"2% in total\" is nominal. A 100 IMD buy costs 1.990625 IMD across hook and LP fees.\n3. Info: exact-output IMD requests above roughly `int256.max × 0.9925` revert with `SwapAmountTooLarge` where plain v4 would partially fill. Unreachable by any real order.\n\n**Not reached.** The project's pinned fork at block 111252324 could not run here because the endpoint has no historical state at that block. The README also names a different block, 111250446, than the test pins. Both are recorded in the coverage rows. Scratch files under `test/scratch/` are mine and are not part of the submission.","treeHash":null,"usage":{"cachedInputTokens":1653003,"chain":[{"calls":122,"chainId":4663,"failed":0,"own":114,"public":0,"refused":8}],"cost":{"source":"runtime","usdMicros":6718723},"inputTokens":418,"model":"claude-fable-5-1","outputTokens":60506,"runtime":"claude","turns":29,"wallClockMs":898440}},{"artifacts":[],"attempt":1,"bundleHash":"b4d3ac2cd103c875ccf1eb9c74107c812c46a5ae108a1365bd1c90240389a66e","device":"c4e8f98d7a2a37dc","findings":[],"hash":"6f4e0ea79137861abd60bae5abf547a43bcfe9922fc20498d032bdaf04542552","nodeId":"20f11f83-8c8a-48a5-99a6-51e0a62601be","outcome":"completed","summary":"Implemented the immutable hook and fixed-supply token, with 0.75% IMD fees routed to the fixed vault.\n\n- `forge build`, `forge test`, and `forge fmt --check` pass.\n- 47 local tests and 14 Robinhood fork tests pass.\n- Deployment, deferred-fee redemption, and nominal 2% fee compounding are documented in [README.md](/home/imd/.identitymd/work/a2baa685-1529-4885-8e78-285cdab32763/20f11f83-8c8a-48a5-99a6-51e0a62601be/README.md).","treeHash":"106d244893704e9d1715a53303ecc867233cbf42","usage":{"cachedInputTokens":3822976,"cost":{"source":"estimated","usdMicros":7441126},"inputTokens":113230,"model":"gpt-6-astra","outputTokens":49717,"runtime":"codex","turns":8,"wallClockMs":1542873}},{"artifacts":[],"attempt":1,"bundleHash":"d19dbeac1f76108a9e989b2356a1018d245f3dc7c05368c1b096bfc467cd9c44","device":"ddfb1efa72fe9a94","findings":[],"hash":"74b59b1c2838b829c95213d435bfacf4ce8545833b66ce8352f5e8f6846d6c73","nodeId":"f645ed8e-42a5-4d85-bc41-ba9803bf3b8f","outcome":"completed","summary":"launch.json is written and is the only changed file. The build passes and the full suite passes with the verifier's fuzz seed.\n\n**What the manifest declares**, read directly from the accepted sources:\n\n- **Hook**: TestHook, constructor arguments `$poolManager` then `$token`, matching `constructor(IPoolManager manager_, address token_)` in src/TestHook.sol:47.\n- **Permissions**: beforeInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta, afterSwapReturnDelta, exactly the five flags getHookPermissions sets at src/TestHook.sol:61.\n- **Token**: TestToken, name \"SIMD TEST 3 - DO NOT BUY\", symbol SIMDTEST3, 18 decimals, no constructor arguments.\n- **Pool**: paired currency is the IMD address in lowercase, fee 12500, tick spacing 60, and the one-to-one initial price as the launch facts give it. The hook's own constants LP_FEE and TICK_SPACING agree with the fee and spacing.\n- **Notes**: a plain-text description of the fixed 0.75% IMD fee to the vault, the deferred-claim path, the absence of any owner or setter, and why no `$owner`, `$factory` or `$launchNumber` argument appears. Notes are well under the cap.\n\nNotes length and test results:\n\n| Check | Result |\n| --- | --- |\n| First key is `kind: univ4_hook` | yes |\n| Notes length | 2545 characters |\n| forge test with verifier seed | 60 passed, 0 failed, 2 skipped |\n\nThe two skipped tests are the Robinhood Chain fork tests, which skip when no fork URL is configured. No file other than launch.json was touched.","treeHash":"42a7c6ca0a6945e1900da7480c79c6c867dbeb9f","usage":{"cachedInputTokens":300501,"cost":{"source":"runtime","usdMicros":1134933},"inputTokens":226,"model":"claude-fable-5-1","outputTokens":4068,"runtime":"claude","turns":8,"wallClockMs":87203}},{"artifacts":[],"attempt":1,"bundleHash":"b77cc3a525374b4b86bb8eb19b1a26db1da42af3c453c013234625239a3c9882","device":"8a28a065f3378b87","findings":[],"hash":"9bca83b74aee3ce5a082c42d27572b3068856577f7596edc507024b25583516e","nodeId":"48ed3fe0-c9a8-4019-91cb-5b37a6d41f17","outcome":"completed","summary":"Preserved the tests and documented the blocker in [test/README.md](/home/imd/.identitymd/work/a2baa685-1529-4885-8e78-285cdab32763/48ed3fe0-c9a8-4019-91cb-5b37a6d41f17/test/README.md).\n\n- `forge build` passed.\n- All 84 tests passed with the required seed.\n- All 24 fork tests also passed with RPC caching disabled.\n\nThe external check remains unresolved: the endpoint rejects Foundry’s automatic `eth_getAccountInfo` probe, although fallback requests succeed. Repair requires the checker or RPC adapter to change, outside the permitted test paths.","treeHash":"771d8793eb16a637acae7213a7cacf7ff00d3dc3","usage":{"cachedInputTokens":3703424,"cost":{"source":"estimated","usdMicros":7197294},"inputTokens":186122,"model":"gpt-6-astra","outputTokens":32653,"runtime":"codex","turns":13,"wallClockMs":1586353}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"df74f6c887684f20","findings":[{"citation":"resolved","description":"README.md (lines 93-98) tells a reader to reproduce the recorded Robinhood fork run with `forge test --match-contract 'Robinhood.*ForkTest' --fork-url https://rpc.mainnet.chain.robinhood.com --fork-block-number 111250446 -vv`. The fork suite does not read the CLI fork: test/fork/RobinhoodFork.t.sol:27-31 forks only when ROBINHOOD_RPC_URL is set and otherwise calls vm.skip(true), and line 20 pins FORK_BLOCK = 111_252_324 itself, so --fork-block-number is ignored even when the env var is set. docs/VALIDATION.md:13 and :31 record '14 passed, seven tests per currency ordering' at block 111250446, but the shipped file has 12 tests per ordering (24) pinned at 111252324. test/README.md:11 and :24 describe the real behaviour (env var, block 111252324, 24 fork cases). The top-level README and VALIDATION.md are stale relative to the delivered suite, so anyone following them concludes the fork evidence ran when every fork test was skipped. The fork suite itself is correct: run with ROBINHOOD_RPC_URL set it passes 24/24 against live Robinhood state at block 111252324 (both currency orders), and the hook also passes the same trades on the chain's real PoolManager 0x8366a39cc670b4001a1121b8f6a443a643e40951.","line":98,"path":"README.md","reproduction":"State: repository as delivered, ROBINHOOD_RPC_URL unset. Input: the README's exact command with any reachable Robinhood RPC as --fork-url and --fork-block-number 111250446. Expected (per README/VALIDATION.md): 14 fork tests pass at block 111250446. Actual: `Ran 1 test for test/fork/RobinhoodFork.t.sol:RobinhoodForkTest [SKIP: skipped] setUp()` and the same for RobinhoodIMDFirstForkTest; `0 tests passed, 0 failed, 2 skipped`. With `ROBINHOOD_RPC_URL=<rpc> forge test --match-path 'test/fork/*'` the suite runs at block 111252324 (not 111250446) and reports 24 passed. Fix: make README.md and docs/VALIDATION.md match test/README.md (env var, block 111252324, 24 cases).","severity":"low","snippet":"  --fork-block-number 111250446 -vv","title":"README's fork reproduction command silently skips the fork suite; VALIDATION.md records a different block and test count than the shipped suite"},{"citation":"resolved","description":"Economic-security dependency check. The live IMD at 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127 (Robinhood Chain, block 111273066) is an owned LayerZero OFT with owner 0x047f606fd5b2baa5f5c6c4ab8958e45cb6b054b7 and exposes blocked(address) (0xe5962195), transfersEnabled() (0xbef97c87, currently true), enableTransfers() and setV4Config(address,address,bool) with a poolManager() getter (currently zero). blocked(VAULT) is currently false. The hook has no control over these: if the IMD owner blocks the vault or changes the v4 config so transfers from the PoolManager to the vault fail, _collect's take reverts on every swap, each fee is minted as an ERC-6909 claim (line 195) and redeemFees reverts in unlockCallback (line 142) until the gate is lifted. Swaps stay live and no fee is lost or redirected, which matches the README's 'Liveness and unpaid fees' section; the vault only receives fees after someone calls redeemFees. This is a trust boundary to record for the launch, not a hook defect. The IMD source is not verifiable here (Sourcify has no match for chain 4663; Blockscout is behind a browser challenge), so what setV4Config does to transfers involving the PoolManager could not be read.","line":192,"path":"src/TestHook.sol","reproduction":"Offline reproduction with the project's own IMD stand-in (same code path as a live block): in test/TestHook.t.sol::test_vaultTransferRevertDefersFeesAndAnyoneCanRedeemOnlyToVault, MockIMD.setFailureMode(1) makes transfer(VAULT, x) revert; an exact-input buy of 100 ether IMD then emits FeeDeferred(0.75 ether), vault balance unchanged, hook.pendingFees()==0.75 ether, and redeemFees(0.75 ether) reverts until failureMode is cleared. Live state at block 111273066: eth_call IMD.blocked(0x8Fe1805a83bCF70F514bC716Cd88C05051d6fF21) -> 0, IMD.transfersEnabled() -> 1, IMD.poolManager() -> 0x0, IMD.owner() -> 0x047f606fd5b2baa5f5c6c4ab8958e45cb6b054b7. Expected vs actual: no defect in the hook; the guarantee '0.75% lands in the vault on every swap' holds only while the IMD owner leaves the vault unblocked, otherwise it degrades to 'is claimable to the vault'.","severity":"info","snippet":"        try poolManager.take(Currency.wrap(IMD), VAULT, amount) {","title":"Fee delivery depends on the live IMD token's owner-controlled transfer gates; blocking the vault turns every fee into a claim that cannot be redeemed until unblocked"},{"citation":"resolved","description":"Value-flow check on the fee destination. At block 111273066 the vault contract (6,624 bytes of code) reports IMD()=0x5f7b...7127, FACTORY()=0xa25b02a1e93903b790e6aaf7da1b8b8d50294645 (the launch factory whose poolManager() is 0x8366a39cc670b4001a1121b8f6a443a643e40951), INTERVAL()=604800 (weekly), token()=0x0, launchNumber()=0, nextDistributionAt()=0, and eth_call distribute() reverts with NotBound() (0x179435b3); notifyDeposit() also reverts. It holds 1.95 IMD. Its selectors include bind(uint64), claimFees(uint64), positionOf(uint64), stakeRewards(), DEV(), MARKETING(), INTAKE(), airdrop(), which indicate it is bound to a launch number by the factory and then claims LP fees and splits them. Whether distribute() also sweeps IMD that arrived by plain ERC-20 transfer (which is how this hook pays, via PoolManager.take) could not be confirmed because the vault's source is not published on Sourcify and Blockscout was unreachable. The hook pays the right address in the right token; the remaining question is on the vault side and belongs to the launch checklist: bind the vault to this launch before trading, and confirm distribute() accounts hook-paid IMD by balance.","line":20,"path":"src/TestHook.sol","reproduction":"Live state, Robinhood Chain block 111273066: eth_call to 0x8Fe1805a83bCF70F514bC716Cd88C05051d6fF21 with selector 0xe4fc6b6d (distribute()) returns revert data 0x179435b3 (NotBound()); 0xfc0c546a (token()) returns 0; 0x7b21f5b0 (launchNumber()) returns 0; 0x32359d96 (nextDistributionAt()) returns 0. Expected for a working fee sink: a bound vault whose distribute() runs. Actual: unbound; any fee the hook pays before binding is held but not distributable. No hook change is needed.","severity":"info","snippet":"    address public constant VAULT = 0x8Fe1805a83bCF70F514bC716Cd88C05051d6fF21;","title":"The fixed vault is not yet bound to any launch: distribute() reverts NotBound, so fees paid before the factory binds it sit in the vault; distribution of balance-received IMD could not be verified"},{"citation":"resolved","description":"Every fork test deploys a fresh vendored PoolManager inside the fork, so the hook's reliance on the deployed manager's behaviour (self-call hook suppression used by quoteSwap, take/mint claim semantics, HookDeltaExceedsSwapAmount checks) is never checked against the manager the factory will actually use. The real manager's runtime (24,009 bytes) differs from the vendored build (19,725 bytes) so bytecode comparison is not possible. I drove the hook on the real manager at block 111252324 (test/scratch/RealManagerFork.t.sol, not kept): hook deployed with constructor (0x8366...0951, token), pool initialized, liquidity added, exact-input buy of 100 IMD paid exactly 0.75 IMD to the vault, exact-output buy / exact-input sell / exact-output sell (net 100 IMD) all paid 75 bps, partial fills at a one-tick price limit paid on executed IMD only, pendingFees()==0, for both currency orders. The real manager held 77,148 IMD, so direct payment (not deferral) is the expected production path. Nothing failed; this is a coverage note so the launch evidence names the production manager.","line":66,"path":"test/helpers/HookFixture.sol","reproduction":"ROBINHOOD_RPC_URL=<archive rpc> REAL_PM=0x8366a39cc670b4001a1121b8f6a443a643e40951 forge test --match-path test/scratch/RealManagerFork.t.sol (and TOKEN_FIRST=false). Expected and actual: 2 passed per ordering. Suggested fix: add a fork case that uses the launch factory's poolManager() instead of `new PoolManager` so the shipped suite covers the production dependency.","severity":"info","snippet":"        manager = IPoolManager(address(new PoolManager(address(this))));","title":"Fork suite never exercises the chain's real PoolManager; reviewer run on 0x8366a39cc670b4001a1121b8f6a443a643e40951 passes and should be added as evidence"}],"hash":"bb73d04229f2f9bccc66c345b229f474495dd4e93da7988b73490118a5293026","nodeId":"8d5e9427-0e4b-49ec-bed8-37355c93d80d","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds four findings and twelve coverage rows, with every cited snippet verified against the tree.\n\n**Outcome: no economic, invariant or flow defect in the hook.** The fee math, delta accounting and claim fallback behave as the brief requires, and I confirmed that on three independent surfaces: the offline suite, the project's fork suite on live Robinhood Chain state, and a scratch run on the chain's real PoolManager.\n\n**What I verified in my area**\n\n- **Economic Security.** The fee is a fixed 75 bps of gross IMD on every mode, with no price dependence, so there is nothing to sandwich or extract. Dust under 134 wei owes zero fee, which is not economically farmable. The claim fallback preserves fees when the manager cannot pay, and claims cannot be stolen or redirected.\n- **Invariants.** The README's rounding identities hold for every input I brute-forced up to three million units. The conservation law vault plus claims equals the sum of fees holds across the project's invariant campaign and all fork trades.\n- **Flow gaps.** The self-call quote is suppressed by the vendored Hooks library and by the real deployed manager, the transient fee slot is cleared before any transfer, and nested redemption is refused by the manager. The hook's real-manager run paid the vault exactly 0.75 IMD on a 100 IMD buy in both currency orders.\n\n**Findings written**\n\n1. **Low, documentation.** The README's reproduction command forks through the command line, but the suite only forks when an environment variable is set, so the command skips both fork suites. The validation doc also records a different block and test count than the shipped tests. Reproduced: the command reports zero passed and two skipped.\n2. **Info, dependency.** The live IMD token is owner-controlled with a blocklist and a transfer switch. If its owner blocks the vault, every fee becomes a claim and redemption reverts until unblocked. Current state is unblocked.\n3. **Info, dependency.** The fixed vault is not yet bound to a launch. Its distribute function reverts with NotBound, and its source is unavailable, so whether it sweeps balance-received IMD could not be verified.\n4. **Info, coverage.** The fork suite never uses the chain's real PoolManager. My scratch test on it passes and is described so the author can add it as evidence.\n\n**What I could not reach.** The IMD and vault sources are not on Sourcify, and Blockscout sits behind a browser challenge, so I could not read what IMD's v4 config does to transfers from the PoolManager, nor the vault's distribution accounting. Both are recorded as the one `finding` coverage row rather than claimed as holds.\n\nSources used for explorer discovery: [Blockscout Robinhood API docs](https://docs.blockscout.com/robinhood-api), [Bitquery Robinhood Chain page](https://docs.bitquery.io/docs/blockchain/robinhood/robinhood-chain-rpc-chain-id/), [GetBlock Robinhood reference](https://docs.getblock.io/api-reference/robinhood).","treeHash":null,"usage":{"cachedInputTokens":1582738,"chain":[{"calls":278,"chainId":4663,"failed":0,"own":261,"public":0,"refused":17}],"cost":{"source":"runtime","usdMicros":6654618},"inputTokens":418,"model":"claude-fable-5-1","outputTokens":60453,"runtime":"claude","turns":32,"wallClockMs":881844}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"9ab27edcfd62be02","findings":[{"citation":"resolved","description":"Merged from audit_flow, audit_economics and audit_permissions (same root cause: documentation out of step with the delivered fork suite). README.md:95-98 tells a reader to reproduce the recorded Robinhood fork run with `forge test --match-contract 'Robinhood.*ForkTest' --fork-url <rpc> --fork-block-number 111250446`. The fork suite ignores both flags: test/fork/RobinhoodFork.t.sol:27-32 forks only when the ROBINHOOD_RPC_URL environment variable is non-empty and otherwise calls vm.skip(true), and line 20 pins `FORK_BLOCK = 111_252_324` itself, so the command-line block is never used even when the variable is set. docs/VALIDATION.md:10 records '47 passed' and :13 '14 passed ... seven tests per currency ordering' at block 111250446 (:31), whereas the delivered tree has 60 offline tests and 24 fork cases (12 per currency order) pinned at 111252324, exactly as test/README.md:11 states. Anyone following README.md concludes the fork rehearsal ran when both fork suites were skipped, and cannot match the recorded block hash to the block the tests actually pin. No contract behaviour is involved; the hook and token are unaffected. The fork suite itself is sound: run as test/README.md describes it passes 24/24 at block 111252324 against live IMD in both currency orders (verified in this review through this machine's Robinhood endpoint).","line":98,"path":"README.md","reproduction":"State: repository as delivered, ROBINHOOD_RPC_URL unset. 1) Run the README's command form: `env -u ROBINHOOD_RPC_URL forge test --match-contract 'Robinhood.*ForkTest' --fork-url <any Robinhood RPC> --fork-block-number 111250446 -vv`. Expected per README/VALIDATION.md: 14 fork tests run at block 111250446. Actual: setUp() of both RobinhoodForkTest and RobinhoodIMDFirstForkTest reports `[SKIP: skipped]`, `0 tests passed, 0 failed, 2 skipped` (the same two skips appear in a plain `forge test`: '60 tests passed, 0 failed, 2 skipped'). 2) Run `ROBINHOOD_RPC_URL=<archive rpc> forge test --no-storage-caching --match-path 'test/fork/*'`: observed `24 tests passed, 0 failed` at block 111252324 (vm.createSelectFork(url, 111_252_324)), not 14 at 111250446. Fix: make README.md:93-101 set ROBINHOOD_RPC_URL and drop the fork flags, and refresh docs/VALIDATION.md to block 111252324, its hash, and the 60 offline + 24 fork counts.","severity":"low","snippet":"  --fork-block-number 111250446 -vv","title":"README's fork reproduction command never runs the fork suite, and docs/VALIDATION.md records a block and test count the shipped suite does not have"},{"citation":"resolved","description":"Merged from audit_economics (IMD gates) and audit_permissions (unrecoverable claims): one root cause, the fixed recipient on an externally governed token. The hook pays with poolManager.take to the bytecode-constant VAULT; when IMD's transfer reverts the catch at line 194-196 mints the same amount to the hook as an ERC-6909 IMD claim, and the only exit for a claim is redeemFees -> unlockCallback (lines 139-145), which again takes to VAULT. The live IMD at 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127 is an owned OFT exposing blocked(address) and transfersEnabled(); at the current block (0x6a20701) blocked(VAULT) is false and transfersEnabled() is true, so today fees land directly. If the IMD owner ever blocks the vault or disables transfers, swaps stay live (the brief's 'never reverts a real swap' holds) but every fee is deferred and redeemFees reverts until the gate is lifted; there is no sweep, no alternate recipient and no owner, by design. This is the requested behaviour ('nothing can redirect the fee') and the README's 'Liveness and unpaid fees' section describes it; it is recorded as the launch's trust boundary, not a hook defect. No change recommended unless the requester wants a fallback recipient, which would be a design change.","line":192,"path":"src/TestHook.sol","reproduction":"Offline, with the project's IMD stand-in (test/TestHook.t.sol:280 test_vaultTransferRevertDefersFeesAndAnyoneCanRedeemOnlyToVault, passing on this tree): MockIMD.setFailureMode(1) makes transfer(VAULT, x) revert; an exact-input buy of 100e18 IMD completes, emits FeeDeferred(0.75e18), vault balance unchanged, hook.pendingFees() == 0.75e18; redeemFees(0.75e18) reverts and pendingFees() stays 0.75e18 until the failure mode is cleared, after which anyone can redeem it only to VAULT. Live state read in this review (chain 4663, block 0x6a20701): eth_call IMD.blocked(0x8Fe1805a83bCF70F514bC716Cd88C05051d6fF21) -> 0, IMD.transfersEnabled() -> 1. Expected vs actual: no defect; '0.75% lands in the vault on every swap' holds only while IMD accepts the vault, otherwise it degrades to 'is claimable to the vault'.","severity":"info","snippet":"        try poolManager.take(Currency.wrap(IMD), VAULT, amount) {","title":"Fee delivery depends on the live IMD token's owner-controlled transfer gates; while IMD refuses transfers to the vault every fee becomes an ERC-6909 claim nobody can redeem"},{"citation":"resolved","description":"From audit_economics; reproduced live. The hook pays the right token (IMD) to the right address, but the vault at 0x8Fe1805a83bCF70F514bC716Cd88C05051d6fF21 currently reports token() == 0 and launchNumber() == 0, and eth_call distribute() reverts with selector 0x179435b3 (NotBound()). It already holds about 1.95 IMD. Its source is not published (no Sourcify match for chain 4663), so whether distribute() also sweeps IMD that arrived by plain ERC-20 transfer, which is how this hook pays, could not be confirmed. Nothing in the hook can change this; it is a launch-checklist item: bind the vault to this launch before trading opens and confirm that distribute() accounts hook-paid IMD by balance.","line":20,"path":"src/TestHook.sol","reproduction":"Chain 4663 via the public endpoint https://rpc.mainnet.chain.robinhood.com, block 0x6a20701: eth_call {to: 0x8Fe1805a83bCF70F514bC716Cd88C05051d6fF21, data: 0xe4fc6b6d (distribute())} -> execution reverted, data 0x179435b3; data 0xfc0c546a (token()) -> 0x0; data 0x7b21f5b0 (launchNumber()) -> 0; IMD.balanceOf(vault) -> 0x1b0fcaab20030000 (about 1.95e18). Expected for a working fee sink: a bound vault whose distribute() runs. Actual: unbound; fees paid before binding are held but not distributable. No hook change needed.","severity":"info","snippet":"    address public constant VAULT = 0x8Fe1805a83bCF70F514bC716Cd88C05051d6fF21;","title":"The fixed vault is not yet bound to a launch: its distribute() reverts NotBound, so hook fees paid before the factory binds it sit undistributed"},{"citation":"resolved","description":"From audit_permissions; reproduced. beforeInitialize only checks the caller is the PoolManager and that the key is (token, IMD, 12500, 60, this hook) via _checkPool (lines 205-211); the sender and sqrtPriceX96 arguments are ignored. The README documents this as deliberate, and the IMD factory deploys the hook and calls PoolManager.initialize in one transaction, so there is no window in the intended flow. It matters only if the hook were ever deployed in a transaction separate from the factory's initialize: a stranger could then open the pool at an arbitrary price and the factory's own initialize would revert with PoolAlreadyInitialized. Recorded as a trust assumption. If atomic deployment cannot be guaranteed, take the factory as a constructor argument written \"$factory\" and compare it with the sender in beforeInitialize; this keeps the no-owner design.","line":70,"path":"src/TestHook.sol","reproduction":"State: TestHook deployed, pool (token, IMD, 12500, 60, hook) not yet initialized. The project's own fixture is the reproduction: test/helpers/HookFixture.sol:81 calls manager.initialize(key, PRICE) from the test contract, which is not a factory, and it succeeds (every offline and fork test relies on it). Equivalently: vm.prank(stranger); poolManager.initialize(key, 79228162514264337593543950336 * 1000) succeeds, and a subsequent poolManager.initialize(key, PRICE) from any sender reverts PoolAlreadyInitialized. Expected if the pool were bound to the factory: the stranger's call reverts.","severity":"info","snippet":"    function beforeInitialize(address, PoolKey calldata key, uint160) external view onlyPoolManager returns (bytes4) {","title":"beforeInitialize binds the pool to its parameters but not to the launch factory: any address may open the pool at any price once the hook has code"},{"citation":"resolved","description":"From audit_math; reproduced. All fee formulas (lines 110, 111, 154, 158, 183, 185) use FullMath.mulDiv, which floors, so the vault receives floor(gross * 75 / 10000). For any gross IMD below 134 wei the fee is 0, and larger swaps lose the fractional wei. The four swap modes are mutually consistent (floor(75*(C+floor(75C/9925))/10000) == floor(75C/9925) for all C), so the under-collection never exceeds 1 wei per swap and cannot be farmed (a 133-wei trade costs far more gas than the fraction of a wei it saves). The README states that trades below the rounding threshold owe zero fee, so this is a documented design choice; it is recorded, at info rather than the specialist's low, because the economic effect is bounded at 1 wei.","line":154,"path":"src/TestHook.sol","reproduction":"Offline fixture (test/helpers/HookFixture.sol, token-first order, pool seeded 1e24 liquidity at 1:1), verified in this review with a scratch test: _trade(buy, exactInput, 133) -> vault balance and pendingFees unchanged (fee 0); _trade(buy, exactInput, 134) -> vault receives exactly 1 wei; _trade(sell, exactInput, 133) -> fee 0. Expected under a round-up policy: 1, 2 and 1 wei respectively. Actual: 0, 1, 0.","severity":"info","snippet":"            proposedFee = FullMath.mulDiv(requested, FEE_BPS, BPS);","title":"Hook fee floors in the trader's favour: gross IMD below 134 wei pays no fee and every swap under-collects at most 1 wei"},{"citation":"resolved","description":"From audit_math; reproduced. On an exact-input buy the hook reserves 75 bps of the gross IMD in beforeSwap (toBeforeSwapDelta(fee, 0) at line 90), so the AMM receives 99.25% of the input and v4 charges the 12500-pip LP fee on that: 1.25% of 99.25% = 1.240625%, total 1.990625%. On a sell the LP fee is paid in SIMDTEST3 on the input and the hook fee in IMD on the output, so the two rates apply to different bases. No funds are lost or misdirected and the README's 'Exact fee accounting' section already states the composition; recorded so the brief's wording is reconciled before publication. A flat 2% of gross IMD is not representable with FEE_BPS over 10000 (it would need 75.95 bps on buys).","line":21,"path":"src/TestHook.sol","reproduction":"Offline fixture, either currency order, pool seeded 1e24 liquidity at 1:1: actor.swap(key, buy exact input, amountSpecified = -100 ether): vault receives 0.75 ether IMD, the AMM receives 99.25 ether, and actor.modify(key, -600, 600, 0) afterwards collects 1.240625 ether IMD of LP fees (test/TestHook.t.sol:155 test_revertedQuoteDoesNotDoubleLPFees asserts this, passing). Total 1.990625 IMD on 100 IMD paid, versus the 2.000000 IMD the brief's wording implies.","severity":"info","snippet":"    uint256 public constant FEE_BPS = 75;","title":"The brief's '2% in total' is nominal: the composed cost of a buy is 1.990625%, because the 1.25% LP fee applies to the IMD left after the 0.75% hook fee"},{"citation":"resolved","description":"From audit_math; reproduced. For a sell with exact IMD output the hook grosses the request up by 75/9925 before quoting; when requested + proposedFee would exceed int256.max it reverts instead of letting v4 fill to the price limit. The threshold (about 5.7e76 wei, roughly 10^54 times IMD's total supply) cannot be a real order, and rejecting it is safer than letting the sum wrap into an exact-input swap. Recorded only so the brief's 'never reverts a real swap' is read with this documented exception.","line":161,"path":"src/TestHook.sol","reproduction":"Offline fixture: vm.prank(address(manager)); hook.beforeSwap(address(this), key, SwapParams({zeroForOne: sell direction, amountSpecified: type(int256).max, sqrtPriceLimitX96: MAX_SQRT_PRICE - 1}), \"\") reverts SwapAmountTooLarge() (test/TestHook.t.sol:208 test_unrepresentableExactOutputCannotWrapIntoExactInput, passing). Any requested with requested + floor(requested*75/9925) > 2^255-1 triggers it; a plain v4 pool without the hook would partially fill the same request to the price limit.","severity":"info","snippet":"            if (requested > uint256(type(int256).max) - proposedFee) revert SwapAmountTooLarge();","title":"Exact-output IMD requests above about int256.max * 9925 / 10000 revert with SwapAmountTooLarge although v4 alone would partially fill them"},{"citation":"resolved","description":"From audit_economics; reproduced by reading. Every fork test uses `new PoolManager(address(this))`, so the hook's reliance on manager behaviour that quoteSwap depends on (callback suppression when msg.sender is the hook, take/mint claim semantics, HookDeltaExceedsSwapAmount checks) is checked against the vendored build, not against the manager the launch factory uses (factory 0xa25b02a1e93903b790e6aaf7da1b8b8d50294645 reports poolManager() 0x8366a39cc670b4001a1121b8f6a443a643e40951, whose runtime size differs from the vendored build). The economics specialist drove the hook on that manager at block 111252324 in a scratch test and all four swap modes paid exactly 75 bps directly to the vault in both currency orders; nothing failed. Coverage note: add a fork case that uses the factory's poolManager() so the shipped evidence names the production dependency.","line":66,"path":"test/helpers/HookFixture.sol","reproduction":"grep -n 'new PoolManager' test/helpers/HookFixture.sol test/fork/RobinhoodFork.t.sol shows the only manager construction is HookFixture.sol:66, reached from RobinhoodFork.t.sol setUp via _setUp(_tokenFirst(), true, true); no fork test references 0x8366a39cc670b4001a1121b8f6a443a643e40951 or the factory's poolManager(). Expected for production evidence: at least one case on the deployed manager. Actual: none in the shipped suite.","severity":"info","snippet":"        manager = IPoolManager(address(new PoolManager(address(this))));","title":"Fork suite deploys a fresh vendored PoolManager inside the fork and never exercises the chain's deployed manager"}],"hash":"bcb8ba4c119213ecc0c529cb159dd844fba0195c3db58abf58fd9f7d37b54b12","nodeId":"e4d99276-4db5-43c4-a2e4-cec8a864189e","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds eight findings and a ten-row coverage record. No repository file was changed; the scratch test was deleted after it ran.\n\n**Outcome: no defect in the contracts.** The hook and token hold on every entry point. The only defect kept is a documentation one.\n\nWhat I verified against the code and chain:\n\n- **Hook logic holds.** I traced all four swap modes through v4's beforeSwap and afterSwap accounting, including the self-only reverted quote that v4 suppresses callbacks for. The fee is exactly floor of 75 bps of the executed IMD, the hook's delta nets to zero, and the take-or-mint fallback keeps swaps live. The offline suite passes 60 tests, and the fork suite passes 24 of 24 at the pinned block against live IMD in both currency orders through this machine's endpoint.\n- **Manifest holds.** Constructor arguments and permissions match the implementation, flags are 0x20cc, bytecode sizes are far under the limits, and the notes are 2,545 characters.\n- **Protected checks hold.** The hook refuses callbacks from non-managers, has no owner and no liquidity callbacks, and accepts the factory's initialize on the manifest's pool.\n\nFindings kept, after merging duplicates:\n\n1. **Low.** The README's fork reproduction command never runs the fork suite, because the tests read an environment variable and pin their own block. VALIDATION.md records a different block and test count than the shipped suite. Three specialists reported this; merged into one.\n2. **Info.** Fee delivery depends on IMD's owner-controlled transfer gates. Blocked transfers turn fees into claims nobody can redeem until unblocked. Two specialists' findings merged.\n3. **Info.** The vault is not bound to a launch yet. Its distribute() reverts with NotBound on the live chain, confirmed by eth_call.\n4. **Info.** beforeInitialize does not bind the pool to the factory. Harmless under the atomic deployment flow.\n5. **Info.** Fees floor, so gross below 134 wei pays nothing. I downgraded the specialist's low because the loss is bounded at 1 wei per swap.\n6. **Info.** The brief's 2% is nominal. The composed buy cost is 1.990625%.\n7. **Info.** Absurdly large exact-output requests revert rather than partially fill.\n8. **Info.** The fork suite never uses the chain's deployed PoolManager.\n\nNothing was left unreached. The one thing I could not do is confirm what the vault's distribute() does with hook-paid IMD, since its source is unpublished. That is recorded inside finding 3 as a launch-checklist item.","treeHash":null,"usage":{"cachedInputTokens":773869,"chain":[{"calls":105,"chainId":4663,"failed":0,"own":101,"public":0,"refused":4}],"cost":{"source":"runtime","usdMicros":4010562},"inputTokens":258,"model":"claude-fable-5-1","outputTokens":34096,"runtime":"claude","turns":15,"wallClockMs":475571}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f9cb4fd544aa3c68","findings":[{"citation":"resolved","description":"Access-control pass (Pashov Access Control guide, 'map the permission model'). The hook accepts PoolManager.initialize for the fixed key (token, IMD, fee 12500, spacing 60, this hook) from any sender and at any sqrtPriceX96. This is a deliberate design choice the README documents ('imposes no sender or initial-price restriction'), and the IMD launch reference says the factory deploys the hook and initializes the pool in one transaction, so no front-run window exists in the intended flow. It is recorded as a trust assumption, not a defect: it only matters if the hook is ever deployed in a transaction separate from the factory's initialize. In that state a stranger can open the pool at an arbitrary price and the factory's own initialize then reverts with PoolAlreadyInitialized, blocking the launch. Fix, if the deployment flow cannot guarantee atomicity: take the factory as a constructor argument written \"$factory\" and compare it to the sender in beforeInitialize. The proposed fix preserves the no-owner design.","line":70,"path":"src/TestHook.sol","reproduction":"State: TestHook deployed (hook code present) and the pool (token, IMD, 12500, 60, hook) not yet initialized. Call: vm.prank(stranger); poolManager.initialize(key, 79228162514264337593543950336 * 1000). Expected if the pool were bound to the factory: revert. Actual: succeeds (verified in a scratch test on the vendored PoolManager: a stranger's initialize at 1000x the fixture price succeeds, and the subsequent initialize(key, PRICE) from the factory reverts). Precondition: hook deployment and factory initialize in different transactions, which the documented launch flow excludes.","severity":"info","snippet":"    function beforeInitialize(address, PoolKey calldata key, uint160) external view onlyPoolManager returns (bytes4) {\n        _checkPool(key);\n        return IHooks.beforeInitialize.selector;\n    }","title":"beforeInitialize binds the pool to its parameters but not to the launch factory: any address may open the pool at any price once the hook has code"},{"citation":"resolved","description":"Trust-gap pass (access x economics). The brief requires that nothing can redirect the fee, and the code honors it: VAULT is a bytecode constant, redeemFees has no recipient argument and unlockCallback takes only to VAULT. The consequence of this immutability is that the fee's liveness depends entirely on the external IMD token continuing to accept transfers to 0x8Fe1805a83bCF70F514bC716Cd88C05051d6fF21. The swap path is protected (a failed take falls back to minting a claim, so trading never halts), but the claims then accumulate on the PoolManager with no recovery path, no sweep and no alternate recipient. This is the requested behavior and is documented in README and docs/SECURITY.md; it is recorded so the judge and author have the precondition and impact explicit. No change is recommended unless the requester wants a fallback recipient, which would be a design change.","line":141,"path":"src/TestHook.sol","reproduction":"State: IMD rejects transfers to VAULT (offline fixture: MockIMD.setFailureMode(1)). Call: buy 100e18 IMD exact input. Actual (verified): vault receives 0, hook.pendingFees() == 0.75e18, swap completes. Then redeemFees(0.75e18) reverts (the take inside unlockCallback fails) and pendingFees() stays 0.75e18. While the rejection persists there is no call by any actor that moves the 0.75e18 IMD anywhere; it is recoverable only if IMD later accepts the transfer again.","severity":"info","snippet":"        poolManager.burn(address(this), uint256(uint160(IMD)), amount);\n        poolManager.take(Currency.wrap(IMD), VAULT, amount);","title":"Fee destination is unrecoverable by design: if IMD permanently refuses transfers to the vault, every fee stays an ERC-6909 claim nobody can redeem"},{"citation":"resolved","description":"Test-coverage check (reading the tests as critically as the code). The brief asks for fork tests against Robinhood Chain. The shipped fork tests hard-pin FORK_BLOCK = 111_252_324, but docs/VALIDATION.md records the final fork run at block 111250446 (hash 0xbd03ab6c...) and the README's reproduction command passes --fork-block-number 111250446. vm.createSelectFork(url, FORK_BLOCK) ignores the command-line block, so running the README's command actually forks at 111252324, a block the recorded evidence never names. Because the public RPC prunes historical state, a verifier cannot tell which block the 14 recorded passes correspond to, and neither block is guaranteed to be served. No contract behavior is affected; the hook code is not involved. Fix: make FORK_BLOCK match the recorded block (or record the block that FORK_BLOCK pins) so the evidence and the test agree.","line":20,"path":"test/fork/RobinhoodFork.t.sol","reproduction":"Input: ROBINHOOD_RPC_URL=<archive endpoint> forge test --match-contract 'Robinhood.*ForkTest' --fork-block-number 111250446 as README instructs. Expected: the suite runs against block 111250446, the block docs/VALIDATION.md records. Actual: setUp calls vm.createSelectFork(url, 111_252_324) and the suite runs against block 111252324; any state difference between the two blocks (IMD balances, code) is invisible to the recorded evidence. Offline (no env var) the suite skips, so the mismatch is only visible on a fork run.","severity":"low","snippet":"    uint256 internal constant FORK_BLOCK = 111_252_324;","title":"Fork suite pins block 111252324 while the recorded validation and README cite block 111250446, so the documented fork evidence cannot be reproduced by running the shipped tests"}],"hash":"bd3c77b5f731421553a74a2303167b0a577a82f5ef668cd62cc52c62956610c0","nodeId":"11af131d-9070-4f07-8c85-62d8d1905867","outcome":"completed","summary":"The review is complete and `.imd-findings.json` is written with 3 findings and 13 coverage rows.\n\n**Outcome:** the permission model of `TestHook` and `TestToken` holds. I found no critical, high or medium defect in the Access Control, Trust Gap or Asymmetry area, so no proof tests were attached.\n\n**What I checked**\n- Every enabled callback and the unlock callback accept only the PoolManager. The self-quote accepts only the hook itself and always reverts, so no outsider can run a hook-suppressed swap. I confirmed the v4 self-call skip in the vendored `Hooks.sol`.\n- Fee recipient, rate, pair token, LP fee and tick spacing are constants or immutables. There is no setter, owner, proxy, delegatecall or sweep. Admin-selector probes from the vault and factory revert.\n- The hook's ERC-6909 claims cannot be burned or transferred by anyone else, and redemption lands only at the vault.\n- The specified and unspecified fee branches, buy and sell, and the take versus mint fallback are economically symmetric. A full-fill quote reproduces the real swap amount exactly.\n- The full project suite passes locally. A scratch probe suite I wrote confirmed each permission edge case and was removed afterwards.\n\n**Findings recorded**\n1. Info. Pool initialization is bound to the exact pool key but not to the factory. A stranger can open the pool at any price if the hook ever exists before the factory's initialize. The documented single-transaction launch flow excludes this. Recorded as a trust assumption.\n2. Info. The fee destination is unrecoverable by design. If IMD permanently refuses transfers to the vault, swaps keep working but fees sit as claims forever. This is the requested behavior, recorded with its precondition.\n3. Low. The fork suite pins block 111252324 while the validation record and README cite block 111250446, so the recorded fork evidence cannot be reproduced by running the shipped tests as documented.\n\n**Not reached:** nothing in the assigned area. The fork tests were not run here because no RPC variable was supplied for this task.","treeHash":null,"usage":{"cachedInputTokens":821204,"cost":{"source":"runtime","usdMicros":4678853},"inputTokens":258,"model":"claude-fable-5-1","outputTokens":36019,"runtime":"claude","turns":26,"wallClockMs":622864}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"401c4383398af47c","findings":[{"citation":"resolved","description":"The fork suite (test/fork/RobinhoodFork.t.sol:27-32) selects its fork only from the ROBINHOOD_RPC_URL environment variable and pins FORK_BLOCK = 111_252_324 itself; when the variable is empty, setUp calls vm.skip(true). The README's documented reproduction command passes --fork-url and --fork-block-number 111250446 instead and never sets ROBINHOOD_RPC_URL, so following it produces two skipped suites and zero fork assertions while reporting 'ok'. The block it names (111250446) is also not the block the tests pin. docs/VALIDATION.md repeats the stale state: line 10 records '47 passed' and line 13 '14 passed ... seven tests per currency ordering' at block 111250446 (line 31), whereas the shipped suite has 60 offline tests and 24 fork cases (12 per ordering) pinned at 111252324, as test/README.md:11 and :24 correctly state. A reviewer or the deployer following README.md will believe the fork rehearsal ran when it did not, and cannot match the recorded block hash to the suite's pinned block. The contracts are unaffected; the defect is in the delivered test documentation and validation record.","line":95,"path":"README.md","reproduction":"1) With a working Robinhood RPC available at <url>, run the README's command form: `env -u ROBINHOOD_RPC_URL forge test --match-contract 'Robinhood.*ForkTest' --fork-url <url> -vv`. Observed in this review (through this machine's RPC): both RobinhoodForkTest and RobinhoodIMDFirstForkTest print `[SKIP: skipped] setUp()`, final line `0 tests passed, 0 failed, 2 skipped`. Expected per README: 'Reproduce the recorded fork run' with fork assertions executed. 2) Run `ROBINHOOD_RPC_URL=<url> forge test --no-storage-caching --match-path test/fork/RobinhoodFork.t.sol -vv`: 24 tests pass at block 111252324 (observed), not 14 at 111250446 as docs/VALIDATION.md:10-13 and :31 record. Fix: make README.md:95-98 set ROBINHOOD_RPC_URL (as test/README.md:11 describes) and drop the fork flags, and refresh docs/VALIDATION.md to the pinned block 111252324, its hash, and the 60 + 24 test counts.","severity":"low","snippet":"FOUNDRY_FUZZ_SEED=0x76105d90bf01c6e1e5a45c5d3867eecbfd0e53653aa2d95e2c5cd48db02ea12f \\\n  forge test --match-contract 'Robinhood.*ForkTest' \\\n  --fork-url https://rpc.mainnet.chain.robinhood.com \\\n  --fork-block-number 111250446 -vv","title":"README fork reproduction command never runs the fork suite; recorded validation names a different block and test count than the shipped suite"}],"hash":"e3db53d0478a2108263dbbddd8c811f5b54c2579455d8338708ece15d2102c41","nodeId":"76c7b8d3-78dd-4bf1-84e7-dd59fcecd16b","outcome":"completed","summary":"Review complete. The findings file is written at the repository root and validated; no repository file was changed (the untracked dotfiles in `git status` are shell-profile artifacts of the sandbox, not mine, and I did not touch them).\n\n**Outcome**\n\nThe hook's control flow holds under all three assigned passes (Execution Trace, Periphery, First Principles). I found no defect in `src/TestHook.sol` or `src/TestToken.sol`. One low-severity documentation defect is reported.\n\n**What I verified**\n\n- **Execution trace.** Both fee paths net the hook's PoolManager delta to zero. The specified-currency path reserves the fee from a reverted self-quote, carries it through transient storage, clears the slot before any external call, and returns zero from afterSwap. The unspecified path derives the fee from the real pool delta. The partial-fill arithmetic `floor(75E/9925)` always equals `floor(75G/10000)` of the gross, so the 0.75% rate holds in all four modes. The take-or-mint fallback cannot strand a delta.\n- **Periphery.** The reverted quote depends on v4 skipping hook callbacks when the hook itself swaps. That early return is present in the vendored library at `lib/v4-core/src/libraries/Hooks.sol:254`. On Robinhood Chain, IMD is a non-proxy OpenZeppelin ERC-20 with a LayerZero OFT surface, no pause, blacklist, mint or fee selectors, and simulated transfers to the vault return true. The vault is a contract whose `IMD()` getter returns the IMD address.\n- **First principles.** Rate and destination are constants with no setters, delegatecall or selfdestruct. The only hook-originated reverts need IMD amounts above about 1.7e38 wei, far past IMD's total supply. The manifest matches the constructor order, permission mask 0x20cc, fee tier and tick spacing.\n- **Tests.** The offline suite passes (60 tests). The fork suite passes at its pinned block through this machine's RPC (24 tests), which I reached via a local relay since Forge bypasses the sandbox proxy.\n\n**Finding (low):** the README's fork reproduction command uses `--fork-url` and block 111250446, but the suite reads only `ROBINHOOD_RPC_URL` and pins block 111252324. Running the documented command yields two skipped suites and zero fork assertions. `docs/VALIDATION.md` also records 47 offline tests and 14 fork cases at the older block, while the shipped suite has 60 and 24.\n\n**Not reached.** The live Robinhood PoolManager's bytecode was not compared against the vendored one, because its address is not among the task inputs and the fork suite deploys a fresh manager. This is recorded as an `unreached` coverage row.\n\n**Static-analysis leads.** The unused `unlock` return value is harmless since the callback returns empty bytes. The reentrancy-events warnings concern event emission after calls into the trusted PoolManager with no hook state involved. Neither reproduces as a defect.","treeHash":null,"usage":{"cachedInputTokens":1147466,"chain":[{"calls":156,"chainId":4663,"failed":0,"own":143,"public":0,"refused":13}],"cost":{"source":"runtime","usdMicros":6640203},"inputTokens":290,"model":"claude-fable-5-1","outputTokens":60390,"runtime":"claude","turns":26,"wallClockMs":1005220}}],"verification":[{"checks":[{"durationMs":4936,"exitCode":0,"name":"build","output":"Compiling 79 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.82s\nCompiler run successful!\n","passed":true},{"durationMs":1267,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/fork/RobinhoodFork.t.sol:RobinhoodIMDFirstForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 541.36µs (0.00ns CPU time)\n\nRan 1 test for test/fork/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 649.82µs (0.00ns CPU time)\n\nRan 3 tests for test/TestToken.t.sol:TestTokenTest\n[PASS] test_factoryCannotMintPauseOrUpgrade() (gas: 184806)\n[PASS] test_launchSupplyMetadataAndFactoryAllocations() (gas: 268921)\n[PASS] test_transfersAreUntaxedAndUnauthorizedSpenderCannotMoveFunds() (gas: 238145)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 773.58µs (1.03ms CPU time)\n\nRan 2 tests for test/TestHook.t.sol:EmptyAndOneSidedPoolTest\n[PASS] test_freshManagerWithOnlyLaunchTokensCanProcessBuy() (gas: 1306035)\n[PASS] test_noLiquidityChargesNothingInAllFourModes() (gas: 1453296)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 121.67ms (2.79ms CPU time)\n\nRan 13 tests for test/TestHook.t.sol:HookFailureTest\n[PASS] test_adminSelectorsCannotRedirectFeesEvenFromFactoryOrVault() (gas: 938298)\n[PASS] test_allEnabledCallbacksRejectNonManager() (gas: 137867)\n[PASS] test_constructorRejectsInvalidParametersWithoutCallingExternalContracts() (gas: 50665785)\n[PASS] test_falseReturningTransferAlsoDefersFees() (gas: 1175570)\n[PASS] test_initializationAcceptsFactoryAndRejectsWrongPoolParameters() (gas: 172100)\n[PASS] test_invalidSwapIsAtomicAndDoesNotPoisonNextSwap() (gas: 596073)\n[PASS] test_noMintPauseUpgradeOrExternalOwnershipInRuntime() (gas: 2762500)\n[PASS] test_permissionsMatchRealCreate2Address() (gas: 16284)\n[PASS] test_poolCannotInitializeBeforePredictedHookHasCode() (gas: 45293)\n[PASS] test_rejectsInvalidRedemptionAmounts() (gas: 52589)\n[PASS] test_unrepresentableExactOutputCannotWrapIntoExactInput() (gas: 550242)\n[PASS] test_vaultTransferRevertDefersFeesAndAnyoneCanRedeemOnlyToVault() (gas: 1378381)\n[PASS] test_wrongPoolCannotSpoofSwapCallbacks() (gas: 72746)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 125.39ms (67.08ms CPU time)\n\nRan 14 tests for test/TestHook.t.sol:TokenFirstHookTest\n[PASS] testFuzz_fullSwapsConserveFunds(uint128,bool,bool) (runs: 1000, μ: 474507, ~: 458006)\nLogs:\n  Bound result 958663052000176423596\n\n[PASS] testFuzz_priceLimitChargesOnlyExecutedIMD(uint128,bool,bool) (runs: 1000, μ: 497277, ~: 474396)\nLogs:\n  Bound result 999000000000000000000037\n\n[PASS] test_buyExactInputPaysVaultInIMD() (gas: 498505)\n[PASS] test_buyExactOutputPaysVaultInIMD() (gas: 448109)\n[PASS] test_dustAmountsDoNotCauseReverts() (gas: 13435185)\n[PASS] test_factoryCanCollectAndWithdrawLiquidity() (gas: 1281836)\n[PASS] test_hugeExactInputPartialFillDoesNotOverflowOrTaxUnusedBudget() (gas: 495348)\n[PASS] test_largestSignedExactInputCanPartiallyFill() (gas: 496189)\n[PASS] test_multipleSwapsInOneUnlockClearTransientFee() (gas: 827389)\n[PASS] test_partialFillAcrossTicksAndExhaustedLiquidity() (gas: 2472730)\n[PASS] test_revertedQuoteDoesNotDoubleLPFees() (gas: 610097)\n[PASS] test_sellExactInputPaysVaultInIMD() (gas: 454575)\n[PASS] test_sellExactOutputPreservesRequestedNetIMD() (gas: 509134)\n[PASS] test_timeBlocksSenderAndHookDataCannotChangeRate() (gas: 1007949)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 125.46ms (271.54ms CPU time)\n\nRan 14 tests for test/TestHook.t.sol:IMDFirstHookTest\n[PASS] testFuzz_fullSwapsConserveFunds(uint128,bool,bool) (runs: 1000, μ: 474472, ~: 457257)\nLogs:\n  Bound result 5384777954863487141425\n\n[PASS] testFuzz_priceLimitChargesOnlyExecutedIMD(uint128,bool,bool) (runs: 1000, μ: 496892, ~: 473935)\nLogs:\n  Bound result 653824099729734696891658\n\n[PASS] test_buyExactInputPaysVaultInIMD() (gas: 511549)\n[PASS] test_buyExactOutputPaysVaultInIMD() (gas: 453229)\n[PASS] test_dustAmountsDoNotCauseReverts() (gas: 13525921)\n[PASS] test_factoryCanCollectAndWithdrawLiquidity() (gas: 1288121)\n[PASS] test_hugeExactInputPartialFillDoesNotOverflowOrTaxUnusedBudget() (gas: 507627)\n[PASS] test_largestSignedExactInputCanPartiallyFill() (gas: 509066)\n[PASS] test_multipleSwapsInOneUnlockClearTransientFee() (gas: 838879)\n[PASS] test_partialFillAcrossTicksAndExhaustedLiquidity() (gas: 2472836)\n[PASS] test_revertedQuoteDoesNotDoubleLPFees() (gas: 623153)\n[PASS] test_sellExactInputPaysVaultInIMD() (gas: 448101)\n[PASS] test_sellExactOutputPreservesRequestedNetIMD() (gas: 498643)\n[PASS] test_timeBlocksSenderAndHookDataCannotChangeRate() (gas: 1034081)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 125.45ms (216.55ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:HookInvariantTest\n[PASS]\nHookInvariantTest invariants:\n[PASS] invariant_feesAlwaysBelongToVaultAndAllDeltasSettle\n[PASS] invariant_physicalTokensAreConserved\n HookInvariantTest invariants (runs: 128, calls: 4096, reverts: 0)\n\n╭-------------+--------------------+-------+---------+----------╮\n| Contract    | Selector           | Calls | Reverts | Discards |\n+===============================================================+\n| SwapHandler | advance            | 1047  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | failVaultTransfers | 968   | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | redeem             | 1055  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | trade              | 1026  | 0       | 0        |\n╰-------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5652\n  Bound result 1462\n  Bound result 424827\n  Bound result 36028797018963969\n  Bound result 36572841834434129\n  Bound result 5122\n  Bound result 221\n  Bound result 36861116508484713128\n  Bound result 19503\n  Bound result 16828\n  Bound result 16348\n  Bound result 361\n  Bound result 1148\n  Bound result 17361\n  Bound result 244\n  Bound result 11\n  Bound result 44153024005457482065\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.15s (1.14s CPU time)\n\nRan 8 test suites in 1.15s (1.65s CPU time): 47 tests passed, 0 failed, 2 skipped (49 total tests)\n","passed":true},{"durationMs":65,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"TestHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"TestHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"TestHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"TestHook.redeemFees(uint256)\",\"TestHook.unlockCallback(bytes)\",\"TestToken.approve(address,uint256)\",\"TestToken.transfer(address,uint256)\",\"TestToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":14,\"LICENSE\":23,\"README.md\":103,\"docs/SECURITY.md\":37,\"docs/VALIDATION.md\":54,\"foundry.toml\":21,\"remappings.txt\":4,\"src/TestHook.sol\":212,\"src/TestToken.sol\":14,\"test/HookInvariant.t.sol\":127,\"test/TestHook.t.sol\":415,\"test/TestToken.t.sol\":68,\"test/fork/RobinhoodFork.t.sol\":108,\"test/helpers/HookFixture.sol\":178,\"test/helpers/MockIMD.sol\":30,\"test/helpers/PoolActor.sol\":81},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1116,"exitCode":0,"name":"slither","output":"[medium/medium] unused-return at src/TestHook.sol:134: TestHook.redeemFees(uint256) (src/TestHook.sol#134-137) ignores return value by poolManager.unlock(abi.encode(amount)) (src/TestHook.sol#136)\n[low/medium] reentrancy-events at src/TestHook.sol:139: Reentrancy in TestHook.unlockCallback(bytes) (src/TestHook.sol#139-145):\n[low/medium] reentrancy-events at src/TestHook.sol:189: Reentrancy in TestHook._collect(uint256) (src/TestHook.sol#189-198):\n[low/medium] reentrancy-events at src/TestHook.sol:189: Reentrancy in TestHook._collect(uint256) (src/TestHook.sol#189-198):","passed":true},{"durationMs":378,"exitCode":0,"name":"aderyn","output":"[low] large-numeric-literal at src/TestHook.sol:22: Large Numeric Literal (2 places)\n[low] unchecked-return at src/TestHook.sol:136: Unchecked Return","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"6f4e0ea79137861abd60bae5abf547a43bcfe9922fc20498d032bdaf04542552","verifiedTreeHash":"106d244893704e9d1715a53303ecc867233cbf42","verifierVersion":"0.1.0+2e42ff9c"},{"checks":[{"durationMs":7349,"exitCode":0,"name":"build","output":"Compiling 81 files with Solc 0.8.26\nSolc 0.8.26 finished in 7.23s\nCompiler run successful!\n","passed":true},{"durationMs":4774,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/fork/RobinhoodFork.t.sol:RobinhoodIMDFirstForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 694.86µs (0.00ns CPU time)\n\nRan 3 tests for test/TestToken.t.sol:TestTokenTest\n[PASS] test_factoryCannotMintPauseOrUpgrade() (gas: 184806)\n[PASS] test_launchSupplyMetadataAndFactoryAllocations() (gas: 268921)\n[PASS] test_transfersAreUntaxedAndUnauthorizedSpenderCannotMoveFunds() (gas: 238145)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 710.50µs (977.82µs CPU time)\n\nRan 1 test for test/fork/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 834.00µs (0.00ns CPU time)\n\nRan 2 tests for test/TestHook.t.sol:EmptyAndOneSidedPoolTest\n[PASS] test_freshManagerWithOnlyLaunchTokensCanProcessBuy() (gas: 1306035)\n[PASS] test_noLiquidityChargesNothingInAllFourModes() (gas: 1453296)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 7.27ms (2.97ms CPU time)\n\nRan 13 tests for test/TestHook.t.sol:HookFailureTest\n[PASS] test_adminSelectorsCannotRedirectFeesEvenFromFactoryOrVault() (gas: 938298)\n[PASS] test_allEnabledCallbacksRejectNonManager() (gas: 137867)\n[PASS] test_constructorRejectsInvalidParametersWithoutCallingExternalContracts() (gas: 50665785)\n[PASS] test_falseReturningTransferAlsoDefersFees() (gas: 1175570)\n[PASS] test_initializationAcceptsFactoryAndRejectsWrongPoolParameters() (gas: 172100)\n[PASS] test_invalidSwapIsAtomicAndDoesNotPoisonNextSwap() (gas: 596073)\n[PASS] test_noMintPauseUpgradeOrExternalOwnershipInRuntime() (gas: 2762500)\n[PASS] test_permissionsMatchRealCreate2Address() (gas: 16284)\n[PASS] test_poolCannotInitializeBeforePredictedHookHasCode() (gas: 45293)\n[PASS] test_rejectsInvalidRedemptionAmounts() (gas: 52589)\n[PASS] test_unrepresentableExactOutputCannotWrapIntoExactInput() (gas: 550242)\n[PASS] test_vaultTransferRevertDefersFeesAndAnyoneCanRedeemOnlyToVault() (gas: 1378381)\n[PASS] test_wrongPoolCannotSpoofSwapCallbacks() (gas: 72746)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 419.15ms (74.33ms CPU time)\n\nRan 6 tests for test/HookAtomic.t.sol:TokenFirstAtomicSwapTest\n[PASS] testFuzz_batchAndRetryMatchSeparateTransactions(uint96[4],uint8,bool,uint8,bool) (runs: 1000, μ: 2756390, ~: 2820609)\nLogs:\n  Bound result 2\n  Bound result 189116888038\n  Bound result 58919474860451623568\n  Bound result 19858119805560374849\n\n[PASS] test_deferredFeesRemainRedeemableAfterAllLiquidityIsRemoved() (gas: 2190338)\n[PASS] test_feeEventsDescribeActualPayoutClaimsAndRedemption() (gas: 1133455)\n[PASS] test_nestedRedemptionPreservesClaimsAndDoesNotBlockSwap() (gas: 915836)\n[PASS] test_unsettledBatchCannotKeepPaidOrDeferredFees() (gas: 1485800)\n[PASS] test_zeroSecondSwapRevertsEarlierPaymentsAndClaims() (gas: 2023541)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 419.40ms (424.93ms CPU time)\n\nRan 14 tests for test/TestHook.t.sol:IMDFirstHookTest\n[PASS] testFuzz_fullSwapsConserveFunds(uint128,bool,bool) (runs: 1000, μ: 474120, ~: 457257)\nLogs:\n  Bound result 958663052000176423596\n\n[PASS] testFuzz_priceLimitChargesOnlyExecutedIMD(uint128,bool,bool) (runs: 1000, μ: 496891, ~: 473935)\nLogs:\n  Bound result 136384777954863355584754\n\n[PASS] test_buyExactInputPaysVaultInIMD() (gas: 511549)\n[PASS] test_buyExactOutputPaysVaultInIMD() (gas: 453229)\n[PASS] test_dustAmountsDoNotCauseReverts() (gas: 13525921)\n[PASS] test_factoryCanCollectAndWithdrawLiquidity() (gas: 1288121)\n[PASS] test_hugeExactInputPartialFillDoesNotOverflowOrTaxUnusedBudget() (gas: 507627)\n[PASS] test_largestSignedExactInputCanPartiallyFill() (gas: 509066)\n[PASS] test_multipleSwapsInOneUnlockClearTransientFee() (gas: 838879)\n[PASS] test_partialFillAcrossTicksAndExhaustedLiquidity() (gas: 2472836)\n[PASS] test_revertedQuoteDoesNotDoubleLPFees() (gas: 623153)\n[PASS] test_sellExactInputPaysVaultInIMD() (gas: 448101)\n[PASS] test_sellExactOutputPreservesRequestedNetIMD() (gas: 498643)\n[PASS] test_timeBlocksSenderAndHookDataCannotChangeRate() (gas: 1034081)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 599.94ms (897.63ms CPU time)\n\nRan 14 tests for test/TestHook.t.sol:TokenFirstHookTest\n[PASS] testFuzz_fullSwapsConserveFunds(uint128,bool,bool) (runs: 1000, μ: 474156, ~: 458006)\nLogs:\n  Bound result 5384777954863487141425\n\n[PASS] testFuzz_priceLimitChargesOnlyExecutedIMD(uint128,bool,bool) (runs: 1000, μ: 497276, ~: 474396)\nLogs:\n  Bound result 261958663052000175156635\n\n[PASS] test_buyExactInputPaysVaultInIMD() (gas: 498505)\n[PASS] test_buyExactOutputPaysVaultInIMD() (gas: 448109)\n[PASS] test_dustAmountsDoNotCauseReverts() (gas: 13435185)\n[PASS] test_factoryCanCollectAndWithdrawLiquidity() (gas: 1281836)\n[PASS] test_hugeExactInputPartialFillDoesNotOverflowOrTaxUnusedBudget() (gas: 495348)\n[PASS] test_largestSignedExactInputCanPartiallyFill() (gas: 496189)\n[PASS] test_multipleSwapsInOneUnlockClearTransientFee() (gas: 827389)\n[PASS] test_partialFillAcrossTicksAndExhaustedLiquidity() (gas: 2472730)\n[PASS] test_revertedQuoteDoesNotDoubleLPFees() (gas: 610097)\n[PASS] test_sellExactInputPaysVaultInIMD() (gas: 454575)\n[PASS] test_sellExactOutputPreservesRequestedNetIMD() (gas: 509134)\n[PASS] test_timeBlocksSenderAndHookDataCannotChangeRate() (gas: 1007949)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 607.69ms (1.23s CPU time)\n\nRan 6 tests for test/HookAtomic.t.sol:IMDFirstAtomicSwapTest\n[PASS] testFuzz_batchAndRetryMatchSeparateTransactions(uint96[4],uint8,bool,uint8,bool) (runs: 1000, μ: 2758902, ~: 2824805)\nLogs:\n  Bound result 218795247581527\n  Bound result 484198378051\n  Bound result 1\n  Bound result 26040342143578417\n\n[PASS] test_deferredFeesRemainRedeemableAfterAllLiquidityIsRemoved() (gas: 2191908)\n[PASS] test_feeEventsDescribeActualPayoutClaimsAndRedemption() (gas: 1147523)\n[PASS] test_nestedRedemptionPreservesClaimsAndDoesNotBlockSwap() (gas: 922119)\n[PASS] test_unsettledBatchCannotKeepPaidOrDeferredFees() (gas: 1525080)\n[PASS] test_zeroSecondSwapRevertsEarlierPaymentsAndClaims() (gas: 2076438)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 607.82ms (609.36ms CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:IMDFirstHookInvariantTest\n[PASS]\nIMDFirstHookInvariantTest invariants:\n[PASS] invariant_feesAlwaysBelongToVaultAndAllDeltasSettle\n[PASS] invariant_physicalTokensAreConserved\n IMDFirstHookInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭-------------+--------------------+-------+---------+----------╮\n| Contract    | Selector           | Calls | Reverts | Discards |\n+===============================================================+\n| SwapHandler | advance            | 2107  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | attemptClaimTheft  | 2089  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | changeLiquidity    | 2051  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | collectLPFees      | 2064  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | failVaultTransfers | 1945  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | invalidRedemption  | 2016  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | redeem             | 2072  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | trade              | 2040  | 0       | 0        |\n╰-------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3155067299394000729\n  Bound result 418630440\n  Bound result 1318\n  Bound result 1282\n  Bound result 5025\n  Bound result 4846\n  Bound result 33\n  Bound result 25\n  Bound result 12\n  Bound result 24\n  Bound result 32\n  Bound result 6780841369626939248\n  Bound result 1\n  Bound result 1832\n  Bound result 4\n  Bound result 3776\n  Bound result 2\n  Bound result 25\n  Bound result 39\n  Bound result 34\n  Bound result 25\n  Bound result 33\n  Bound result 33\n  Bound result 688140559\n  Bound result 8157\n  Bound result 16912\n  Bound result 18\n  Bound result 5\n  Bound result 16644\n  Bound result 18105\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.58s (4.57s CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:HookInvariantTest\n[PASS]\nHookInvariantTest invariants:\n[PASS] invariant_feesAlwaysBelongToVaultAndAllDeltasSettle\n[PASS] invariant_physicalTokensAreConserved\n HookInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭-------------+--------------------+-------+---------+----------╮\n| Contract    | Selector           | Calls | Reverts | Discards |\n+===============================================================+\n| SwapHandler | advance            | 2107  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | attemptClaimTheft  | 2089  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | changeLiquidity    | 2051  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | collectLPFees      | 2064  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | failVaultTransfers | 1945  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | invalidRedemption  | 2016  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | redeem             | 2072  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | trade              | 2040  | 0       | 0        |\n╰-------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3155067299394000729\n  Bound result 8196246489519583527\n  Bound result 16408\n  Bound result 16380\n  Bound result 8995\n  Bound result 131072\n  Bound result 913\n  Bound result 171\n  Bound result 51\n  Bound result 6\n  Bound result 97\n  Bound result 2912869397430398755\n  Bound result 1\n  Bound result 15861\n  Bound result 452\n  Bound result 12834\n  Bound result 134\n  Bound result 335\n  Bound result 821\n  Bound result 254\n  Bound result 474\n  Bound result 253\n  Bound result 253\n  Bound result 1706857600\n  Bound result 4699\n  Bound result 10748\n  Bound result 559\n  Bound result 5\n  Bound result 2428483684\n  Bound result 90551182\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.65s (4.64s CPU time)\n\nRan 11 test suites in 4.65s (11.89s CPU time): 60 tests passed, 0 failed, 2 skipped (62 total tests)\n","passed":true},{"durationMs":69,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"TestHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"TestHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"TestHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"TestHook.redeemFees(uint256)\",\"TestHook.unlockCallback(bytes)\",\"TestToken.approve(address,uint256)\",\"TestToken.transfer(address,uint256)\",\"TestToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":14,\"LICENSE\":23,\"README.md\":103,\"docs/SECURITY.md\":37,\"docs/VALIDATION.md\":54,\"foundry.toml\":21,\"launch.json\":30,\"remappings.txt\":4,\"src/TestHook.sol\":212,\"src/TestToken.sol\":14,\"test/HookAtomic.t.sol\":201,\"test/HookInvariant.t.sol\":220,\"test/README.md\":38,\"test/TestHook.t.sol\":415,\"test/TestToken.t.sol\":68,\"test/fork/RobinhoodFork.t.sol\":297,\"test/helpers/AtomicSwapActor.sol\":108,\"test/helpers/HookFixture.sol\":178,\"test/helpers/MockIMD.sol\":30,\"test/helpers/PoolActor.sol\":81},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"74b59b1c2838b829c95213d435bfacf4ce8545833b66ce8352f5e8f6846d6c73","verifiedTreeHash":"42a7c6ca0a6945e1900da7480c79c6c867dbeb9f","verifierVersion":"0.1.0+2e42ff9c"},{"checks":[{"durationMs":7342,"exitCode":0,"name":"build","output":"Compiling 81 files with Solc 0.8.26\nSolc 0.8.26 finished in 7.21s\nCompiler run successful!\n","passed":true},{"durationMs":4785,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/TestToken.t.sol:TestTokenTest\n[PASS] test_factoryCannotMintPauseOrUpgrade() (gas: 184806)\n[PASS] test_launchSupplyMetadataAndFactoryAllocations() (gas: 268921)\n[PASS] test_transfersAreUntaxedAndUnauthorizedSpenderCannotMoveFunds() (gas: 238145)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 663.02µs (957.82µs CPU time)\n\nRan 2 tests for test/TestHook.t.sol:EmptyAndOneSidedPoolTest\n[PASS] test_freshManagerWithOnlyLaunchTokensCanProcessBuy() (gas: 1306035)\n[PASS] test_noLiquidityChargesNothingInAllFourModes() (gas: 1453296)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 57.70ms (3.46ms CPU time)\n\nRan 13 tests for test/TestHook.t.sol:HookFailureTest\n[PASS] test_adminSelectorsCannotRedirectFeesEvenFromFactoryOrVault() (gas: 938298)\n[PASS] test_allEnabledCallbacksRejectNonManager() (gas: 137867)\n[PASS] test_constructorRejectsInvalidParametersWithoutCallingExternalContracts() (gas: 50665785)\n[PASS] test_falseReturningTransferAlsoDefersFees() (gas: 1175570)\n[PASS] test_initializationAcceptsFactoryAndRejectsWrongPoolParameters() (gas: 172100)\n[PASS] test_invalidSwapIsAtomicAndDoesNotPoisonNextSwap() (gas: 596073)\n[PASS] test_noMintPauseUpgradeOrExternalOwnershipInRuntime() (gas: 2762500)\n[PASS] test_permissionsMatchRealCreate2Address() (gas: 16284)\n[PASS] test_poolCannotInitializeBeforePredictedHookHasCode() (gas: 45293)\n[PASS] test_rejectsInvalidRedemptionAmounts() (gas: 52589)\n[PASS] test_unrepresentableExactOutputCannotWrapIntoExactInput() (gas: 550242)\n[PASS] test_vaultTransferRevertDefersFeesAndAnyoneCanRedeemOnlyToVault() (gas: 1378381)\n[PASS] test_wrongPoolCannotSpoofSwapCallbacks() (gas: 72746)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 481.14ms (70.01ms CPU time)\n\nRan 14 tests for test/TestHook.t.sol:IMDFirstHookTest\n[PASS] testFuzz_fullSwapsConserveFunds(uint128,bool,bool) (runs: 1000, μ: 474120, ~: 457257)\nLogs:\n  Bound result 5384777954863487141425\n\n[PASS] testFuzz_priceLimitChargesOnlyExecutedIMD(uint128,bool,bool) (runs: 1000, μ: 496891, ~: 473935)\nLogs:\n  Bound result 312463374266808778300605\n\n[PASS] test_buyExactInputPaysVaultInIMD() (gas: 511549)\n[PASS] test_buyExactOutputPaysVaultInIMD() (gas: 453229)\n[PASS] test_dustAmountsDoNotCauseReverts() (gas: 13525921)\n[PASS] test_factoryCanCollectAndWithdrawLiquidity() (gas: 1288121)\n[PASS] test_hugeExactInputPartialFillDoesNotOverflowOrTaxUnusedBudget() (gas: 507627)\n[PASS] test_largestSignedExactInputCanPartiallyFill() (gas: 509066)\n[PASS] test_multipleSwapsInOneUnlockClearTransientFee() (gas: 838879)\n[PASS] test_partialFillAcrossTicksAndExhaustedLiquidity() (gas: 2472836)\n[PASS] test_revertedQuoteDoesNotDoubleLPFees() (gas: 623153)\n[PASS] test_sellExactInputPaysVaultInIMD() (gas: 448101)\n[PASS] test_sellExactOutputPreservesRequestedNetIMD() (gas: 498643)\n[PASS] test_timeBlocksSenderAndHookDataCannotChangeRate() (gas: 1034081)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 481.15ms (620.43ms CPU time)\n\nRan 14 tests for test/TestHook.t.sol:TokenFirstHookTest\n[PASS] testFuzz_fullSwapsConserveFunds(uint128,bool,bool) (runs: 1000, μ: 474156, ~: 458006)\nLogs:\n  Bound result 9518791651845794629620\n\n[PASS] testFuzz_priceLimitChargesOnlyExecutedIMD(uint128,bool,bool) (runs: 1000, μ: 497276, ~: 474396)\nLogs:\n  Bound result 999000000000000000000256\n\n[PASS] test_buyExactInputPaysVaultInIMD() (gas: 498505)\n[PASS] test_buyExactOutputPaysVaultInIMD() (gas: 448109)\n[PASS] test_dustAmountsDoNotCauseReverts() (gas: 13435185)\n[PASS] test_factoryCanCollectAndWithdrawLiquidity() (gas: 1281836)\n[PASS] test_hugeExactInputPartialFillDoesNotOverflowOrTaxUnusedBudget() (gas: 495348)\n[PASS] test_largestSignedExactInputCanPartiallyFill() (gas: 496189)\n[PASS] test_multipleSwapsInOneUnlockClearTransientFee() (gas: 827389)\n[PASS] test_partialFillAcrossTicksAndExhaustedLiquidity() (gas: 2472730)\n[PASS] test_revertedQuoteDoesNotDoubleLPFees() (gas: 610097)\n[PASS] test_sellExactInputPaysVaultInIMD() (gas: 454575)\n[PASS] test_sellExactOutputPreservesRequestedNetIMD() (gas: 509134)\n[PASS] test_timeBlocksSenderAndHookDataCannotChangeRate() (gas: 1007949)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 481.15ms (972.45ms CPU time)\n\nRan 6 tests for test/HookAtomic.t.sol:IMDFirstAtomicSwapTest\n[PASS] testFuzz_batchAndRetryMatchSeparateTransactions(uint96[4],uint8,bool,uint8,bool) (runs: 1000, μ: 2758902, ~: 2824805)\nLogs:\n  Bound result 4246\n  Bound result 1654\n  Bound result 7950\n  Bound result 28149\n\n[PASS] test_deferredFeesRemainRedeemableAfterAllLiquidityIsRemoved() (gas: 2191908)\n[PASS] test_feeEventsDescribeActualPayoutClaimsAndRedemption() (gas: 1147523)\n[PASS] test_nestedRedemptionPreservesClaimsAndDoesNotBlockSwap() (gas: 922119)\n[PASS] test_unsettledBatchCannotKeepPaidOrDeferredFees() (gas: 1525080)\n[PASS] test_zeroSecondSwapRevertsEarlierPaymentsAndClaims() (gas: 2076438)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 544.46ms (547.70ms CPU time)\n\nRan 6 tests for test/HookAtomic.t.sol:TokenFirstAtomicSwapTest\n[PASS] testFuzz_batchAndRetryMatchSeparateTransactions(uint96[4],uint8,bool,uint8,bool) (runs: 1000, μ: 2756390, ~: 2820609)\nLogs:\n  Bound result 4699\n  Bound result 66816762252833374\n  Bound result 617232\n  Bound result 14264337593543950332\n\n[PASS] test_deferredFeesRemainRedeemableAfterAllLiquidityIsRemoved() (gas: 2190338)\n[PASS] test_feeEventsDescribeActualPayoutClaimsAndRedemption() (gas: 1133455)\n[PASS] test_nestedRedemptionPreservesClaimsAndDoesNotBlockSwap() (gas: 915836)\n[PASS] test_unsettledBatchCannotKeepPaidOrDeferredFees() (gas: 1485800)\n[PASS] test_zeroSecondSwapRevertsEarlierPaymentsAndClaims() (gas: 2023541)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 620.01ms (625.09ms CPU time)\n\nRan 12 tests for test/fork/RobinhoodFork.t.sol:RobinhoodForkTest\n[PASS] testFork_buyExactInputFeeArrivesInLiveIMD() (gas: 488167)\n[PASS] testFork_buyExactOutputFeeArrivesInLiveIMD() (gas: 441188)\n[PASS] testFork_deferredClaimsCannotBeStolenOverdrawnOrRedeemedTwice() (gas: 10005904)\n[PASS] testFork_dustAndRoundingBoundariesAllFourModes() (gas: 14889238)\n[PASS] testFork_failedInputSettlementRollsBackFeeAndPool() (gas: 3816245)\n[PASS] testFork_freshManagerDefersThenRedeemsToFixedVault() (gas: 9211475)\n[PASS] testFork_invalidPriceAndZeroAmountAreAtomic() (gas: 868532)\n[PASS] testFork_noTimeBasedTaxAndNoFeeRedirect() (gas: 5378318)\n[PASS] testFork_partialFillsChargeOnlyExecutedIMD() (gas: 1878025)\n[PASS] testFork_sellExactInputFeeArrivesInLiveIMD() (gas: 444144)\n[PASS] testFork_sellExactOutputFeeArrivesInLiveIMD() (gas: 502074)\n[PASS] testFork_unpaidBatchRollsBackAndRetryPaysExactlyOnce() (gas: 2986733)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 2.42s (4.76s CPU time)\n\nRan 12 tests for test/fork/RobinhoodFork.t.sol:RobinhoodIMDFirstForkTest\n[PASS] testFork_buyExactInputFeeArrivesInLiveIMD() (gas: 501221)\n[PASS] testFork_buyExactOutputFeeArrivesInLiveIMD() (gas: 446296)\n[PASS] testFork_deferredClaimsCannotBeStolenOverdrawnOrRedeemedTwice() (gas: 12550718)\n[PASS] testFork_dustAndRoundingBoundariesAllFourModes() (gas: 14905653)\n[PASS] testFork_failedInputSettlementRollsBackFeeAndPool() (gas: 3809965)\n[PASS] testFork_freshManagerDefersThenRedeemsToFixedVault() (gas: 11721853)\n[PASS] testFork_invalidPriceAndZeroAmountAreAtomic() (gas: 881390)\n[PASS] testFork_noTimeBasedTaxAndNoFeeRedirect() (gas: 5380752)\n[PASS] testFork_partialFillsChargeOnlyExecutedIMD() (gas: 1878071)\n[PASS] testFork_sellExactInputFeeArrivesInLiveIMD() (gas: 437680)\n[PASS] testFork_sellExactOutputFeeArrivesInLiveIMD() (gas: 491571)\n[PASS] testFork_unpaidBatchRollsBackAndRetryPaysExactlyOnce() (gas: 2989921)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 2.42s (4.72s CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:HookInvariantTest\n[PASS]\nHookInvariantTest invariants:\n[PASS] invariant_feesAlwaysBelongToVaultAndAllDeltasSettle\n[PASS] invariant_physicalTokensAreConserved\n HookInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭-------------+--------------------+-------+---------+----------╮\n| Contract    | Selector           | Calls | Reverts | Discards |\n+===============================================================+\n| SwapHandler | advance            | 2107  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | attemptClaimTheft  | 2089  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | changeLiquidity    | 2051  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | collectLPFees      | 2064  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | failVaultTransfers | 1945  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | invalidRedemption  | 2016  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | redeem             | 2072  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | trade              | 2040  | 0       | 0        |\n╰-------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3155067299394000729\n  Bound result 8196246489519583527\n  Bound result 16408\n  Bound result 16380\n  Bound result 8995\n  Bound result 131072\n  Bound result 913\n  Bound result 171\n  Bound result 51\n  Bound result 6\n  Bound result 97\n  Bound result 2912869397430398755\n  Bound result 1\n  Bound result 15861\n  Bound result 452\n  Bound result 12834\n  Bound result 134\n  Bound result 335\n  Bound result 821\n  Bound result 254\n  Bound result 474\n  Bound result 253\n  Bound result 253\n  Bound result 1706857600\n  Bound result 4699\n  Bound result 10748\n  Bound result 559\n  Bound result 5\n  Bound result 2428483684\n  Bound result 90551182\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.62s (4.61s CPU time)\n\nRan 1 test for test/HookInvariant.t.sol:IMDFirstHookInvariantTest\n[PASS]\nIMDFirstHookInvariantTest invariants:\n[PASS] invariant_feesAlwaysBelongToVaultAndAllDeltasSettle\n[PASS] invariant_physicalTokensAreConserved\n IMDFirstHookInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭-------------+--------------------+-------+---------+----------╮\n| Contract    | Selector           | Calls | Reverts | Discards |\n+===============================================================+\n| SwapHandler | advance            | 2107  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | attemptClaimTheft  | 2089  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | changeLiquidity    | 2051  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | collectLPFees      | 2064  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | failVaultTransfers | 1945  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | invalidRedemption  | 2016  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | redeem             | 2072  | 0       | 0        |\n|-------------+--------------------+-------+---------+----------|\n| SwapHandler | trade              | 2040  | 0       | 0        |\n╰-------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3155067299394000729\n  Bound result 418630440\n  Bound result 1318\n  Bound result 1282\n  Bound result 5025\n  Bound result 4846\n  Bound result 33\n  Bound result 25\n  Bound result 12\n  Bound result 24\n  Bound result 32\n  Bound result 6780841369626939248\n  Bound result 1\n  Bound result 1832\n  Bound result 4\n  Bound result 3776\n  Bound result 2\n  Bound result 25\n  Bound result 39\n  Bound result 34\n  Bound result 25\n  Bound result 33\n  Bound result 33\n  Bound result 688140559\n  Bound result 8157\n  Bound result 16912\n  Bound result 18\n  Bound result 5\n  Bound result 16644\n  Bound result 18105\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.67s (4.66s CPU time)\n\nRan 11 test suites in 4.67s (16.79s CPU time): 84 tests passed, 0 failed, 0 skipped (84 total tests)\n","passed":true},{"durationMs":63,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"TestHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"TestHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"TestHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"TestHook.redeemFees(uint256)\",\"TestHook.unlockCallback(bytes)\",\"TestToken.approve(address,uint256)\",\"TestToken.transfer(address,uint256)\",\"TestToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"DEPENDENCIES.md\":14,\"LICENSE\":23,\"README.md\":103,\"docs/SECURITY.md\":37,\"docs/VALIDATION.md\":54,\"foundry.toml\":21,\"remappings.txt\":4,\"src/TestHook.sol\":212,\"src/TestToken.sol\":14,\"test/HookAtomic.t.sol\":201,\"test/HookInvariant.t.sol\":220,\"test/README.md\":38,\"test/TestHook.t.sol\":415,\"test/TestToken.t.sol\":68,\"test/fork/RobinhoodFork.t.sol\":297,\"test/helpers/AtomicSwapActor.sol\":108,\"test/helpers/HookFixture.sol\":178,\"test/helpers/MockIMD.sol\":30,\"test/helpers/PoolActor.sol\":81},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"9bca83b74aee3ce5a082c42d27572b3068856577f7596edc507024b25583516e","verifiedTreeHash":"771d8793eb16a637acae7213a7cacf7ff00d3dc3","verifierVersion":"0.1.0+2e42ff9c"}]}