{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"8b5c783a-4491-4d02-b45d-0a5d75d263f8","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"6e4b63fd0121372376626afd46e178859ecce869ee09fced0cb821513c4e32b5","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":"2df98e7f494b99fc030a7a4802592a1f1868b3ecaa3cb9350158b5607a3c3996","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":"e685e46b32db58d3c7967d1051e45eb306803a58fe8794b0bf77cd90d9d7d24f","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":"242639c7d62fb92dedaa0edf3e6fc2f4395ea65939f0a12f1991bd88dfee268b","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":"77d6b5960b52659eb468a028dca896d13e15107340c40146272baf36d43f7baa","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":"f5a81ec0f24d09a2d0e85ab5d991f1f5e6b538476df9d2f6f9e2c7324bfb84d5","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":"1b4f8fb6c9388543ec6d2723afeb5beb9ed102212da8f4e787e8271f2fb5dc84","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":"f2fcaebdd00ff3c222644e5eb2ff75af9ac12dcd2e5bc7c069b35ac600367061","dependsOn":["build_contract_project"],"execution":{"forkChains":[4663],"network":false,"profile":"foundry","requires":[],"skillHash":"68506fdcd4ed07ed9dcf40cddecfe9a7d7282e399586d1d75a902eca2f5d692b","skillId":"write-foundry-tests","subagents":4,"tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"68506fdcd4ed07ed9dcf40cddecfe9a7d7282e399586d1d75a902eca2f5d692b","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Token name: SIMD TEST 2 - DO NOT BUY\nToken symbol: SIMDTEST2\nLogo: https://www.si-md.xyz/logo.jpg\n\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 holders, Identity.md swarm jobs, the dev and marketing. The launch's remainder is held by a claim contract.\n\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.\n\nContracts:\n1. TestHook, the Uniswap v4 hook on the SIMDTEST2/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 0xa9d495A0b10EE4A2a735c2E0f6D38C583194813A. 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.\n   - The hook never reverts a real swap. No other contracts.\n\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.\n","parentJobId":null,"planHash":"eb42e04a0b11dff0ff262b74a20804ffc11d1cbcb4eb2a472788fd21931d4575","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"8b5c783a-4491-4d02-b45d-0a5d75d263f8","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-3099-simd-test-2-do-not-buy"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51468","feedbackHash":"8c420d38a64adb71315c4d9296c3036f37941e7240d9fafebe611a44d3c9f200","nodeKey":"audit_economics","submissionHash":"6e4b63fd0121372376626afd46e178859ecce869ee09fced0cb821513c4e32b5","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52178","feedbackHash":"57ab5242e8df1af88cbe318aa1798dd5461d5cfe90232aa782939c379e8f4f7d","nodeKey":"audit_flow","submissionHash":"2df98e7f494b99fc030a7a4802592a1f1868b3ecaa3cb9350158b5607a3c3996","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52673","feedbackHash":"fd61f329a6211cd30e4e90c87841456ffc54eb73e9e555df31b4605a44e47c81","nodeKey":"audit_judge","submissionHash":"e685e46b32db58d3c7967d1051e45eb306803a58fe8794b0bf77cd90d9d7d24f","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51438","feedbackHash":"82c6208fc13823e64340dbae2fea7818577e347483613161aee97d2f31015e9b","nodeKey":"audit_judge","submissionHash":"333af718dc4d5111c49c7c11f80d049ecf7a91b5d9a2de559b7b9cf4c4aa7bd7","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51872","feedbackHash":"bbc990d59ea31ab8a2547e267860b88b5631365cb66fcd416970b980217df9d8","nodeKey":"audit_math","submissionHash":"242639c7d62fb92dedaa0edf3e6fc2f4395ea65939f0a12f1991bd88dfee268b","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51880","feedbackHash":"7a149cafda830ac40759a26ffb0644c6f98ed1aad3e7920a5d2ae9e2889bc141","nodeKey":"audit_permissions","submissionHash":"77d6b5960b52659eb468a028dca896d13e15107340c40146272baf36d43f7baa","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51357","feedbackHash":"8ca72a4dad78cd809721d82fbf5bd0710061ddf80c015274d906cab90a3ffa06","nodeKey":"build_contract_project","submissionHash":"6ee710828f6f63a3c6d366f2070b058615562c00627bc3f426d37b3c35b75e46","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52676","feedbackHash":"b7250493b7f67967d3c89f3ae0bbdf4bed8a9bd901a5348737b589da522a61d1","nodeKey":"build_contract_project","submissionHash":"f5a81ec0f24d09a2d0e85ab5d991f1f5e6b538476df9d2f6f9e2c7324bfb84d5","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52611","feedbackHash":"e5d0210c64104f34477f71936dc70b19dfc496c6bbeef177beb36b61d988f469","nodeKey":"manifest","submissionHash":"0c605df1e035237ef3a6ba0c6e489d1b710c0178e10e8a82879e43c8926df87c","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52626","feedbackHash":"c210d1d18135512c42b7315c7ef44a909ab2da48619a556ca19f89bc5b9f29b8","nodeKey":"manifest","submissionHash":"1b4f8fb6c9388543ec6d2723afeb5beb9ed102212da8f4e787e8271f2fb5dc84","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51064","feedbackHash":"ea158ea54f9d16971c1860aca08eeaf8859cddd3ebd0921e7ec99efd9bcafbd9","nodeKey":"write_foundry_tests","submissionHash":"aefd01e10b3c3dba8478b973a81ec66a02d5e656458eb5a50cc49a5a02b46735","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52171","feedbackHash":"ee93b065da904f038c04206791915c360f2404299dd6662e9ebe6d194e59b571","nodeKey":"write_foundry_tests","submissionHash":"f2fcaebdd00ff3c222644e5eb2ff75af9ac12dcd2e5bc7c069b35ac600367061","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"01ba87850e6193561a7e51c80380e02cdc6c3374cbe813f1281dbcadcf016da8","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"c23d285f8003c6238caa07d16c51246f714182694c654efa71ed94c6d675e9a4","device":"2a4ad9be53991100","findings":[],"hash":"0c605df1e035237ef3a6ba0c6e489d1b710c0178e10e8a82879e43c8926df87c","nodeId":"54e4f749-6819-4acb-84bc-d7edbdce6a10","outcome":"completed","summary":"I wrote `launch.json`, and it's the only file changed. It parses as JSON, its first field is `\"kind\": \"univ4_hook\"`, and the notes are 1,931 characters, under the 4,000 cap. `forge build` succeeds. `forge test` with the verifier's fuzz seed ran 57 tests and all passed; 2 were skipped, which I take to be the fork tests skipping because this machine has no Robinhood Chain RPC set up.\n\nWhat the manifest says, taken from the code in the repository:\n- **Hook:** `TestHook`, with constructor arguments `[\"$poolManager\", \"$token\"]` in declaration order.\n- **Permissions:** `beforeInitialize`, `beforeSwap`, `afterSwap`, `beforeSwapReturnDelta` and `afterSwapReturnDelta`. These are exactly what `getHookPermissions` returns, and they match the `0x20cc` address flags in `src/TestHook.sol`.\n- **No owner, factory or launch number:** the hook takes no `$owner`, `$factory` or `$launchNumber` argument, because it has no owner or admin and every address and rate is fixed at deploy, as the brief asks.\n- **Token:** `TestToken`, named \"SIMD TEST 2 - DO NOT BUY\", symbol `SIMDTEST2`, 18 decimals, no constructor arguments.\n- **Pool:** paired with IMD at `0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127`, fee `12500`, tickSpacing `60`, initialPrice `\"79228162514264337593543950336\"`. The hook's `beforeInitialize` only accepts calls from the PoolManager, only for this exact pool key with fee 12500, and returns the correct selector.\n- **Notes:** these state that it is a test token not to be bought, give the logo URL, and describe the fee. The hook takes 0.75% of the IMD side of every buy and sell and pays it in IMD to the fixed vault at `0xa9d495…813a`. If that payment can't be made during a swap, the hook keeps the fee as a claim and anyone can call `flushFees()` to send it to the vault, so a real swap never reverts.\n\nThe example manifest has no logo field, so I didn't add one and left the logo only in the notes.","treeHash":"740e425e68b4b1ea05444a426efefe675bb6045d","usage":{"cachedInputTokens":226744,"cost":{"source":"runtime","usdMicros":405210},"inputTokens":14,"model":"claude-opus-5-5","outputTokens":3515,"runtime":"claude","turns":8,"wallClockMs":54074}},{"artifacts":[],"attempt":1,"bundleHash":"9698ef8c90719d6b2a38b48e5117692ab6d3ad056ffa6df9c9c7cc37ce22a941","device":"790f4e545968ee92","findings":[],"hash":"1b4f8fb6c9388543ec6d2723afeb5beb9ed102212da8f4e787e8271f2fb5dc84","nodeId":"54e4f749-6819-4acb-84bc-d7edbdce6a10","outcome":"completed","summary":"I made one small change to `launch.json`, in its notes. Everything else in the manifest already matched the code and is unchanged.\n\n**What I checked against the code:**\n- `kind` is the first field and is `\"univ4_hook\"`.\n- **Hook:** `TestHook`. Its constructor is `(IPoolManager manager_, address token_)`, written as `[\"$poolManager\", \"$token\"]`. It has no owner, factory or launch-number argument because it has no admin.\n- **Permissions:** `beforeInitialize`, `beforeSwap`, `afterSwap`, `beforeSwapReturnDelta`, `afterSwapReturnDelta`. These are exactly what `getHookPermissions` (`src/TestHook.sol:64`) returns, and they match the hook's address flags `0x20cc`.\n- **Token:** `TestToken`, named \"SIMD TEST 2 - DO NOT BUY\", symbol SIMDTEST2, 18 decimals, no constructor arguments.\n- **Pool:** paired with IMD `0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127`, fee `12500`, tickSpacing `60`, initialPrice `\"79228162514264337593543950336\"`. The hook's `beforeInitialize` accepts fee 12500.\n\n**The change:** after the manifest was first written, the hook gained one more case where it holds the fee instead of paying it straight to the vault. If IMD is the pool manager's currently synced currency, it keeps the fee as a claim, so a pending IMD payment isn't under-credited. That claim goes to the fixed vault when anyone calls `flushFees()`. The notes now say this. They are 1,985 characters, well under the 4,000 cap.\n\n**Checks:** `forge build` passes. With the given fuzz seed, `forge test` ran 59 tests: all passed, and 2 were skipped. The 2 skipped are fork tests, which skip when no Robinhood Chain RPC is configured, so the fork checks of the fee landing in the vault have not run against the live chain here. `launch.json` is the only file changed, and I haven't committed it.","treeHash":"aef3d60e2a90f6d6dd311d1026ba4252f740fd9b","usage":{"cachedInputTokens":223801,"cost":{"source":"runtime","usdMicros":321454},"inputTokens":14,"model":"claude-opus-5-5","outputTokens":2408,"runtime":"claude","turns":8,"wallClockMs":47891}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f5666f1d1aa75678","findings":[{"citation":"resolved","description":"Boundary: the external call poolManager.take(IMD, VAULT, fee) inside afterSwap moves IMD out of the PoolManager in the middle of the swapper's unlock. v4's settle() computes what the payer delivered as balanceOf(manager) - reservesOf(synced). A router that calls sync(IMD) before poolManager.swap and transfer+settle after it (a legal and documented v4 ordering; the project's own PoolActor happens to sync afterwards) sees the manager's IMD balance drop by the fee between its sync and its settle, so settle credits it (transferred - fee), its IMD debt stays open by exactly the fee, and unlock reverts with CurrencyNotSettled. The README promises the hook never rejects a valid, funded swap; for this router class every IMD->SIMDTEST2 buy fails while sells succeed (the fee currency is the output there, not the synced one). No funds are lost (the whole unlock reverts). Standard Uniswap routers (V4Router/UniversalRouter) sync immediately before transferring and are unaffected, which is why this is low. The mint path already present in _pay has no such side effect: minting the ERC-6909 claim (always, or whenever a synced currency equals IMD, readable via poolManager.getSyncedCurrency()) and letting flushFees redeem it would keep such routers working.","line":161,"path":"src/TestHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {BalanceDelta} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {ModifyLiquidityParams, SwapParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {TestHook} from \"src/TestHook.sol\";\nimport {TestToken} from \"src/TestToken.sol\";\n\ncontract PlainERC20 is ERC20 {\n    constructor() ERC20(\"IMD double\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Seeds liquidity with the usual sync -> transfer -> settle order after the operation.\ncontract Seeder is IUnlockCallback {\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function add(PoolKey memory key, int256 liquidity) external {\n        manager.unlock(abi.encode(key, liquidity));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (PoolKey memory key, int256 liquidity) = abi.decode(data, (PoolKey, int256));\n        (BalanceDelta d,) = manager.modifyLiquidity(\n            key, ModifyLiquidityParams(TickMath.minUsableTick(60), TickMath.maxUsableTick(60), liquidity, 0), \"\"\n        );\n        _pay(key.currency0, d.amount0());\n        _pay(key.currency1, d.amount1());\n        return \"\";\n    }\n\n    function _pay(Currency c, int128 amt) private {\n        if (amt < 0) {\n            manager.sync(c);\n            c.transfer(address(manager), uint256(-int256(amt)));\n            manager.settle();\n        }\n    }\n}\n\n/// @dev A router that calls sync on its input currency BEFORE the swap and settles after it.\n/// Every amount it transfers is exactly what the swap says it owes.\ncontract SyncFirstRouter is IUnlockCallback {\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, SwapParams memory params) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(key, params)), (BalanceDelta));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (PoolKey memory key, SwapParams memory params) = abi.decode(data, (PoolKey, SwapParams));\n        Currency input = params.zeroForOne ? key.currency0 : key.currency1;\n        Currency output = params.zeroForOne ? key.currency1 : key.currency0;\n        manager.sync(input);\n        BalanceDelta delta = manager.swap(key, params, \"\");\n        int128 inAmt = params.zeroForOne ? delta.amount0() : delta.amount1();\n        int128 outAmt = params.zeroForOne ? delta.amount1() : delta.amount0();\n        input.transfer(address(manager), uint256(-int256(inAmt)));\n        manager.settle();\n        if (outAmt > 0) manager.take(output, address(this), uint128(outAmt));\n        return abi.encode(delta);\n    }\n}\n\ncontract SyncFirstRouterProof is Test {\n    address constant IMD = 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127;\n    uint160 constant FLAGS = 0x20cc;\n    uint160 constant Q96 = 79228162514264337593543950336;\n\n    IPoolManager manager;\n    TestToken token;\n    TestHook hook;\n    PoolKey key;\n    bool imdIs0;\n\n    function setUp() public {\n        vm.etch(IMD, address(new PlainERC20()).code);\n        manager = IPoolManager(address(new PoolManager(address(this))));\n        token = new TestToken();\n        imdIs0 = IMD < address(token);\n        hook = _deployHook();\n        key = PoolKey(\n            Currency.wrap(imdIs0 ? IMD : address(token)),\n            Currency.wrap(imdIs0 ? address(token) : IMD),\n            12_500,\n            60,\n            IHooks(address(hook))\n        );\n        manager.initialize(key, Q96);\n        Seeder seeder = new Seeder(manager);\n        token.transfer(address(seeder), 10_000_000 ether);\n        PlainERC20(IMD).mint(address(seeder), 10_000_000 ether);\n        seeder.add(key, 1e24);\n    }\n\n    function _deployHook() internal returns (TestHook) {\n        bytes32 hash =\n            keccak256(abi.encodePacked(type(TestHook).creationCode, abi.encode(manager, address(token))));\n        for (uint256 i; i < 300_000; ++i) {\n            bytes32 salt = bytes32(i);\n            address predicted =\n                address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, hash)))));\n            if (uint160(predicted) & ((1 << 14) - 1) == FLAGS) return new TestHook{salt: salt}(manager, address(token));\n        }\n        revert(\"no salt\");\n    }\n\n    /// @notice A buy (IMD in) through a router that syncs IMD before the swap must settle.\n    /// Fails today: afterSwap's take(IMD) lowers the manager's IMD balance between sync and settle,\n    /// so settle credits the router (transferred - fee) and unlock reverts CurrencyNotSettled.\n    function test_syncFirstRouterCanBuy() public {\n        SyncFirstRouter router = new SyncFirstRouter(manager);\n        PlainERC20(IMD).mint(address(router), 1000 ether);\n        uint256 vaultBefore = ERC20(IMD).balanceOf(hook.VAULT());\n        bool zeroForOne = imdIs0; // IMD in, token out\n        uint160 limit = zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1;\n        BalanceDelta d = router.swap(key, SwapParams(zeroForOne, -100 ether, limit));\n        int128 imdDelta = imdIs0 ? d.amount0() : d.amount1();\n        assertEq(imdDelta, -100 ether, \"router paid the full budget\");\n        assertEq(\n            ERC20(IMD).balanceOf(hook.VAULT()) + hook.pendingFees() - vaultBefore, 0.75 ether, \"fee accounted\"\n        );\n    }\n\n    /// @notice A sell (token in) through the same router works, because the fee currency is not synced.\n    function test_syncFirstRouterCanSell() public {\n        SyncFirstRouter router = new SyncFirstRouter(manager);\n        token.transfer(address(router), 1000 ether);\n        bool zeroForOne = !imdIs0; // token in, IMD out\n        uint160 limit = zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1;\n        router.swap(key, SwapParams(zeroForOne, -100 ether, limit));\n    }\n}","reproduction":"State: local PoolManager, IMD stand-in ERC20, TestToken, TestHook mined to flags 0x20cc, pool initialized at sqrtPrice 2^96, full-range liquidity 1e24 (manager holds IMD). Router R holds 1000 IMD and does, inside unlockCallback: sync(IMD); delta = swap(key, {zeroForOne: IMD is currency0, amountSpecified: -100e18, limit: far}); IMD.transfer(manager, 100e18); settle(); take(token). Expected: swap settles, trader pays 100 IMD, vault gets 0.75 IMD. Actual: afterSwap's take(IMD, VAULT, 0.75e18) runs between sync and settle; settle credits 100e18 - 0.75e18, so R's IMD delta is -0.75e18 at the end of unlock and PoolManager reverts CurrencyNotSettled(). The same router selling 100 SIMDTEST2 (token synced, IMD taken) succeeds. Run: forge test --match-path test/scratch/SyncFirstRouterProof.t.sol -> test_syncFirstRouterCanBuy FAILS with CurrencyNotSettled(), test_syncFirstRouterCanSell passes.","severity":"low","snippet":"        try poolManager.take(Currency.wrap(IMD), VAULT, fee) {","title":"afterSwap pays the fee with take(), which breaks any router that syncs IMD before the swap: every buy through such a router reverts CurrencyNotSettled"},{"citation":"resolved","description":"The task requires fork tests against Robinhood Chain. The test file reads ROBINHOOD_RPC_URL and pins FORK_BLOCK = 108_279_434 (changed from 108_268_099 in commit cb96917). README.md line 157-159 and docs/VALIDATION.md line 26-28 instruct `forge test --match-path test/RobinhoodFork.t.sol --fork-url ... --fork-block-number 108268099`; with that command ROBINHOOD_RPC_URL is unset, _forkSetup calls vm.skip(true) and both fixtures are skipped, so the documented procedure exercises zero fork tests. docs/fork-evidence.json and VALIDATION.md record header/code-hash evidence for block 108268099, a different block from the one the current test pins, so the recorded '8 passed' run cannot be reproduced by the shipped test file. Running the suite as the test expects (ROBINHOOD_RPC_URL=<url>) at 108279434 could not be completed here: the public RPC answers 'historical state ... is not available' for that block and the Pinax archive endpoint rate-limits (HTTP 429) during setUp. The fork evidence for the shipped code is therefore not verifiable from the repository as documented.","line":28,"path":"test/RobinhoodFork.t.sol","reproduction":"1) Run the README's command: `forge test --match-path test/RobinhoodFork.t.sol --fork-url https://robinhood.rpc.pinax.network --fork-block-number 108268099` -> output: '0 tests passed, 0 failed, 2 skipped' (RobinhoodIMD0ForkTest and RobinhoodIMD1ForkTest setUp SKIP). Expected per README/VALIDATION.md: eight fork tests executed. 2) `grep -rn 108268099 README.md docs` vs `grep -n FORK_BLOCK test/RobinhoodFork.t.sol` -> docs say 108268099, test says 108_279_434. 3) `ROBINHOOD_RPC_URL=https://rpc.mainnet.chain.robinhood.com forge test --match-path test/RobinhoodFork.t.sol` -> setUp fails: 'historical state ... is not available'; with the Pinax URL -> setUp 'EvmError: Revert' / HTTP 429 rate limit.","severity":"low","snippet":"    uint256 internal constant FORK_BLOCK = 108_279_434;","title":"Fork suite pins block 108279434 and skips unless ROBINHOOD_RPC_URL is set, while README, VALIDATION.md and fork-evidence.json document block 108268099 and a --fork-url command that skips every fork te"},{"citation":"resolved","description":"Math precision / zero-rounding: _fraction floors, so fee = 0 whenever gross IMD * 75 < denominator. Exact-input buys with A <= 133 wei, exact-input sells with G <= 133 wei, exact-output buys with core input P <= 132 wei and exact-output sells with R <= 132 wei all pay nothing to the vault; every larger trade loses at most 1 wei of fee to rounding. _fraction itself is exact (q*75 + floor(r*75/den) == floor(a*75/den)); slither's divide-before-multiply lead is a false positive. Splitting a trade into <134-wei pieces costs gas and the 1.25% LP fee per piece, so the loss is not farmable; the README documents 'dust can pay zero'. Reported only to record the boundary with concrete inputs; no change needed unless the author prefers fees to round up (replace the floor with a ceiling in _fraction, which keeps the preview/afterSwap identities since both sides use the same function).","line":202,"path":"src/TestHook.sol","reproduction":"Local pool (HookFixture, either currency order): _trade(true, true, 133, 0, \"\") -> fee 0, core receives 133; _trade(true, true, 134, 0, \"\") -> fee 1. _trade(false, false, 132, 0, \"\") -> fee 0; R = 133 -> maxFee = floor(133*75/9925) = 1, preview requests 134, fee = floor(134*75/10000) = 1. Expected under 'fee = 0.75% of IMD side' with rounding up: 1 wei on each of these. Actual: 0 on the first and third.","severity":"info","snippet":"        return (amount / denominator) * FEE_BPS + ((amount % denominator) * FEE_BPS) / denominator;","title":"Fee rounds down, so IMD legs below 134 wei pay zero hook fee (dust only, not farmable)"},{"citation":"resolved","description":"Stale documentation: README.md line 50 says the manifest is supplied later, yet launch.json exists at the repository root and describes TestHook/TestToken (constructor args $poolManager,$token; permissions matching getHookPermissions; pool fee 12500, tickSpacing 60; notes 1931 chars). The manifest itself checks out against the implementation; only the README sentence is wrong.","line":50,"path":"README.md","reproduction":"`ls launch.json` -> file exists; `sed -n 50p README.md` -> 'No `launch.json` is included: ...'. Expected: README describes the shipped manifest or omits the sentence.","severity":"info","snippet":"No `launch.json` is included: the network's later manifest step supplies it. No deployment or","title":"README states no launch.json is included, but launch.json is present in the tree"}],"hash":"242639c7d62fb92dedaa0edf3e6fc2f4395ea65939f0a12f1991bd88dfee268b","nodeId":"997bb1f0-a450-4cc8-a176-ac00fba483c3","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json` with 4 findings and 11 coverage rows.\n\n**What I audited.** The Math Precision, Boundary and Numerical Gap passes over `TestHook`, with the Pashov mindset applied first. I traced all four swap modes in both currency orders: the fee reserved in beforeSwap via the reverting self-quote, the fee recomputed in afterSwap, and the hook's delta settlement. I confirmed the identities by hand and by scratch fuzzing: 8000 random partial-fill cases across multiple liquidity ranges, exhaustive tiny amounts 1 to 400 wei with full and partial fills, and swaps under the maximum protocol fee. The preview and real swap always agree, the hook's delta always nets to zero, and the fee always equals floor of 0.75% of gross executed IMD. The `_fraction` quotient-plus-remainder form is exact, so Slither's divide-before-multiply lead is a false positive.\n\n**Findings.**\n- **Low, with proof.** afterSwap pays the fee with `take`, which lowers the manager's IMD balance mid-swap. A router that syncs IMD before calling swap and settles afterwards sees its payment credited short by the fee, so every buy through it reverts with CurrencyNotSettled while sells succeed. Standard Uniswap routers sync right before transferring and are unaffected. The proof in `test/scratch/SyncFirstRouterProof.t.sol` fails on the buy today.\n- **Low.** The fork suite pins block 108279434 and skips unless an environment variable is set, while the README, VALIDATION.md and fork-evidence.json document block 108268099 and a command that skips both fixtures. I ran the documented command and got two skips and no fork tests.\n- **Info.** Fees floor to zero for IMD legs under 134 wei. Dust only, documented, not farmable.\n- **Info.** README says no launch.json is included, but one is present and checks out against the implementation.\n\n**Not reached.** The live Robinhood fork run. The public RPC has no historical state at the pinned block and the Pinax archive endpoint rate-limited during setUp, so that row is marked unreached rather than passed.","treeHash":null,"usage":{"cachedInputTokens":1345280,"cost":{"source":"runtime","usdMicros":6196414},"inputTokens":354,"model":"claude-fable-5-1","outputTokens":53068,"runtime":"claude","turns":37,"wallClockMs":839535}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f768e94767a9dde3","findings":[{"citation":"resolved","description":"The hook pays the vault inside afterSwap with poolManager.take, which moves `fee` IMD out of the PoolManager while the swapper's unlock is still open. v4 settlement credits a payer with `reservesNow - reservesBefore` measured from the last sync of that currency (PoolManager._settle, lib/v4-core/src/PoolManager.sol:372-374). A router that checkpoints the input currency before calling swap (sync -> swap -> transfer -> settle, which v4 allows: sync only has to precede the ERC-20 transfer) is therefore credited `owed - fee` IMD on every buy, its IMD delta stays at -fee, and the unlock reverts with CurrencyNotSettled. The same router, same flow and same funds succeed on an unhooked twin pool, so the revert is caused by the hook alone, which contradicts the brief's 'the hook never reverts a real swap'. The deferred path (mint of an ERC-6909 claim) does not move tokens and does not have this effect. Routers that sync immediately before transferring (Universal Router, V4Router, PoolSwapTest) are unaffected. Impact is availability for one class of integrators, not loss of funds; if the author keeps the direct-pay design this should at least be documented beside the other router requirements in README 'Delivery failure and recovery'. The attached test expects the sync-first buy to succeed, so it fails on the current code and passes if the payout no longer changes the manager's IMD balance between a caller's sync and settle (e.g. always minting the claim and letting flushFees deliver it).","line":161,"path":"src/TestHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {BalanceDelta} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {ModifyLiquidityParams, SwapParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {TestHook} from \"src/TestHook.sol\";\nimport {TestToken} from \"src/TestToken.sol\";\nimport {HookFlags} from \"src/HookFlags.sol\";\n\ncontract MintableERC20 is ERC20 {\n    constructor() ERC20(\"IMD double\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev A router that checkpoints (syncs) the input currency BEFORE the swap and settles after it.\ncontract SyncFirstRouter is IUnlockCallback {\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, SwapParams memory p) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(key, p)), (BalanceDelta));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager));\n        (PoolKey memory key, SwapParams memory p) = abi.decode(data, (PoolKey, SwapParams));\n        Currency input = p.zeroForOne ? key.currency0 : key.currency1;\n        Currency output = p.zeroForOne ? key.currency1 : key.currency0;\n        manager.sync(input);\n        BalanceDelta d = manager.swap(key, p, \"\");\n        int128 owed = p.zeroForOne ? d.amount0() : d.amount1();\n        input.transfer(address(manager), uint256(uint128(-owed)));\n        manager.settle();\n        int128 got = p.zeroForOne ? d.amount1() : d.amount0();\n        if (got > 0) manager.take(output, address(this), uint128(got));\n        return abi.encode(d);\n    }\n}\n\ncontract Seeder is IUnlockCallback {\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function seed(PoolKey memory key, int256 liq) external {\n        manager.unlock(abi.encode(key, liq));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (PoolKey memory key, int256 liq) = abi.decode(data, (PoolKey, int256));\n        (BalanceDelta d,) = manager.modifyLiquidity(\n            key, ModifyLiquidityParams(TickMath.minUsableTick(60), TickMath.maxUsableTick(60), liq, 0), \"\"\n        );\n        _pay(key.currency0, d.amount0());\n        _pay(key.currency1, d.amount1());\n        return \"\";\n    }\n\n    function _pay(Currency c, int128 a) private {\n        if (a < 0) {\n            manager.sync(c);\n            c.transfer(address(manager), uint256(uint128(-a)));\n            manager.settle();\n        }\n    }\n}\n\ncontract SyncFirstRouterProofTest is Test {\n    address constant IMD = 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127;\n    uint160 constant Q96 = 79228162514264337593543950336;\n\n    PoolManager manager;\n    TestToken token;\n    TestHook hook;\n    PoolKey key;\n    PoolKey plain;\n    bool imdIs0;\n\n    function setUp() public {\n        vm.etch(IMD, address(new MintableERC20()).code);\n        manager = new PoolManager(address(this));\n        token = new TestToken();\n        imdIs0 = IMD < address(token);\n        hook = _deployHook();\n        key = PoolKey(\n            Currency.wrap(imdIs0 ? IMD : address(token)),\n            Currency.wrap(imdIs0 ? address(token) : IMD),\n            12_500,\n            60,\n            IHooks(address(hook))\n        );\n        plain = key;\n        plain.hooks = IHooks(address(0));\n        manager.initialize(key, Q96);\n        manager.initialize(plain, Q96);\n        Seeder s = new Seeder(manager);\n        token.transfer(address(s), 100_000_000 ether);\n        MintableERC20(IMD).mint(address(s), 100_000_000 ether);\n        s.seed(key, 1e24);\n        s.seed(plain, 1e24);\n    }\n\n    function _deployHook() internal returns (TestHook) {\n        bytes32 hash = keccak256(abi.encodePacked(type(TestHook).creationCode, abi.encode(manager, address(token))));\n        for (uint256 i; i < 200_000; ++i) {\n            address predicted = address(\n                uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), bytes32(i), hash))))\n            );\n            if (HookFlags.matches(predicted, 0x20cc)) return new TestHook{salt: bytes32(i)}(manager, address(token));\n        }\n        revert(\"no salt\");\n    }\n\n    function _buy(uint256 amount) internal view returns (SwapParams memory) {\n        bool zeroForOne = imdIs0; // IMD in, token out\n        return SwapParams(zeroForOne, -int256(amount), zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1);\n    }\n\n    /// @dev Fails on the current code: the launch pool reverts with CurrencyNotSettled for a router\n    /// that syncs IMD before the swap, while the unhooked twin pool accepts the same flow.\n    function test_syncFirstRouterBuySucceedsOnLaunchPool() public {\n        SyncFirstRouter r = new SyncFirstRouter(manager);\n        MintableERC20(IMD).mint(address(r), 1_000 ether);\n        r.swap(plain, _buy(100 ether)); // reference: works without the hook\n        r.swap(key, _buy(100 ether)); // expected to work with the hook too\n        assertEq(MintableERC20(IMD).balanceOf(address(r)), 800 ether, \"trader paid exactly 100 IMD per buy\");\n    }\n}","reproduction":"Local PoolManager; IMD stand-in etched at 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127; TestToken; TestHook mined to flags 0x20cc; launch key (fee 12500, spacing 60) initialized at 1:1 and seeded with 1e24 liquidity; an identical key with hooks=0 seeded the same way. Router R holds 1000 IMD and runs inside unlock: sync(IMD); swap(key, {zeroForOne: IMD is currency0, amountSpecified: -100e18, limit: min/max}); transfer 100e18 IMD to the manager; settle(); take(token). Expected (and observed on the hookless twin): swap completes, trader pays 100 IMD. Actual on the launch pool: afterSwap's take sends 0.75e18 IMD to the vault between the router's sync and settle, settle() credits only 99.25e18, the router's IMD delta is -0.75e18 at the end of the callback and PoolManager.unlock reverts with CurrencyNotSettled(). Verified with test/scratch/SyncFirstRouter.t.sol (asserting the revert) on the current tree.","severity":"low","snippet":"        try poolManager.take(Currency.wrap(IMD), VAULT, fee) {","title":"Direct take() in afterSwap breaks routers that sync the IMD input before the swap: the launch pool reverts with CurrencyNotSettled where an unhooked pool succeeds"},{"citation":"resolved","description":"The fork suite hard-codes vm.createSelectFork(url, 108_279_434). Every piece of recorded evidence names a different block: README.md:159 tells the reader to run with `--fork-block-number 108268099`, docs/VALIDATION.md's 'Pinned header' and docs/fork-evidence.json (l2Block 108268099, blockHash 0x8d0a...df42, code hashes of IMD/PoolManager/Vault) are all for 108268099, and the 'eight passed' fork run is claimed at 108,268,099. Because createSelectFork overrides the CLI block, the README command silently runs at 108279434 (forge prints `(block: 108279434)` for every fork test, as observed here). The claimed evidence therefore attests a block the committed test never forks, and the verifier that reruns 'at the same block' has two candidates. This is a verification gap, not an on-chain defect: the hook's logic is block-independent. Fix by making the constant, the README command and docs/fork-evidence.json agree on one block and re-recording the header/code hashes at it. The live suite could not be re-run from this machine: the public Pinax endpoint answered HTTP 429 (free-tier rate limit) during setUp for both tests tried.","line":28,"path":"test/RobinhoodFork.t.sol","reproduction":"Run the README's exact command: `ROBINHOOD_RPC_URL=https://robinhood.rpc.pinax.network forge test --match-path test/RobinhoodFork.t.sol --fork-url https://robinhood.rpc.pinax.network --fork-block-number 108268099`. Expected: the tests execute at block 108268099, the block whose hash and code hashes docs/fork-evidence.json records. Actual: forge reports every fork test as `(block: 108279434)` because test/RobinhoodFork.t.sol:36 calls vm.createSelectFork(url, FORK_BLOCK) with FORK_BLOCK = 108_279_434; nothing in the tree records a header or code hash for that block.","severity":"low","snippet":"    uint256 internal constant FORK_BLOCK = 108_279_434;","title":"Fork test pins block 108279434 while README, VALIDATION.md and fork-evidence.json record the evidence at block 108268099, so the recorded fork pass does not describe the committed test"},{"citation":"resolved","description":"launch.json exists at the repository root and describes this hook (TestHook, constructorArgs [\"$poolManager\", \"$token\"], permissions beforeInitialize/beforeSwap/afterSwap/beforeSwapReturnDelta/afterSwapReturnDelta, pool fee 12500, tickSpacing 60). Its contents match src/TestHook.sol (constructor order, getHookPermissions, LP_FEE, TICK_SPACING) and the paired currency is the IMD constant in lowercase, so the manifest itself is consistent; only the README sentence is stale. A reader following the README would not look for or review the manifest.","line":50,"path":"README.md","reproduction":"`ls launch.json` in the repository root lists the file; README.md line 50 says none is included. Expected: README describes the manifest that ships. Actual: README denies its existence.","severity":"info","snippet":"No `launch.json` is included: the network's later manifest step supplies it. No deployment or","title":"README states that no launch.json is included, but launch.json is present in the tree"},{"citation":"resolved","description":"By design the token is a plain ERC-20 and the fee lives in the hook of one v4 pool key (token, IMD, 12500, 60, TestHook). PoolManager.initialize is permissionless, so any address can initialize e.g. (token, IMD, 3000, 60, hooks=0) and provide liquidity there; swaps on that pool pay nothing to the vault. This is inherent to a 'plain token whose pool hook sends a small fee' and the brief asked for exactly that, so it is reported for the record only: the brief's 'on every buy and every sell' holds for the launch pool, not for the token. No code change is suggested; the README could state it under 'After launch'.","line":8,"path":"src/TestToken.sol","reproduction":"Local PoolManager, TestToken, TestHook, launch key initialized. Then manager.initialize(PoolKey(token/IMD sorted, 12500, 60, IHooks(address(0))), 2^96) succeeds from any address; seeding it with 1e24 liquidity and swapping 100e18 IMD exact-input through it leaves the vault balance and hook.pendingFees() unchanged (demonstrated by the hookless twin pool in test/scratch/SyncFirstRouter.t.sol). Expected per the brief's wording: every buy pays 0.75%; actual: buys on the second pool pay 0.","severity":"info","snippet":"contract TestToken is ERC20 {","title":"The 0.75% fee applies only to the launch pool: anyone can open a second SIMDTEST2/IMD pool without the hook and trade fee-free"}],"hash":"2df98e7f494b99fc030a7a4802592a1f1868b3ecaa3cb9350158b5607a3c3996","nodeId":"c41e9d4d-d38f-4cd9-9381-6abf906b1cbb","outcome":"completed","summary":"The review is complete. The findings file is written at the repository root, no tracked file was changed, and the scratch tests live under the gitignored `test/scratch/` directory.\n\n**Outcome.** The hook's control flow is sound in my area. I traced every swap mode, including partial fills at the price limit and dust amounts, and the fee the hook credits itself in `beforeSwap` always equals the fee it debits in `afterSwap`, so no swap can be stranded with an unsettled hook delta. A 3,000-run boundary fuzz against near-full partial fills confirmed this empirically. The preview path cannot recurse because the vendored PoolManager skips callbacks when the hook is the caller. The deferred-claim path, `flushFees`, and the pool binding behave as documented. The on-chain IMD token is an Ownable LayerZero OFT with standard ERC-20 transfers, so no fee-on-transfer or callback risk reaches the hook.\n\n**Findings written (4):**\n- **Low, with proof:** paying the vault with `take` inside `afterSwap` moves IMD out of the manager mid-unlock. A router that syncs IMD before the swap and settles after is credited the fee short and reverts with `CurrencyNotSettled`, while the same flow succeeds on an unhooked twin pool. This contradicts \"the hook never reverts a real swap\". The attached proof test fails on the current tree for that reason.\n- **Low:** the fork test pins block 108279434, but the README command, VALIDATION.md and fork-evidence.json all record block 108268099, so the recorded fork pass does not describe the committed test.\n- **Info:** the README says no launch.json is included, but one ships and is consistent with the hook.\n- **Info:** the fee applies only to the launch pool. Anyone can open a hookless SIMDTEST2/IMD pool and trade fee-free, which is inherent to the requested plain-token design.\n\n**Coverage.** All eight listed entry points have rows, plus six invariant rows. Seven entry points hold; `afterSwap` is marked as a finding for the router case.\n\n**Not verified.** The live Robinhood fork suite could not run here: the public Pinax endpoint returned HTTP 429 during setUp for both tests I tried. The fork evidence remains as the author recorded it.","treeHash":null,"usage":{"cachedInputTokens":1961323,"cost":{"source":"runtime","usdMicros":7119986},"inputTokens":418,"model":"claude-fable-5-1","outputTokens":55255,"runtime":"claude","turns":34,"wallClockMs":774891}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"a93cc1aa32976b68","findings":[{"citation":"resolved","description":"Merges audit_math e3a0887e and audit_flow 4bd9dc4c (same root cause). _pay moves the fee out of the PoolManager with take() while the swapper's unlock is still open. PoolManager._settle (lib/v4-core/src/PoolManager.sol:372-374) credits a payer with reservesNow - reservesBefore, measured from the last sync. A router that syncs IMD before calling swap (sync -> swap -> transfer -> settle) is a legal v4 ordering: IPoolManager only requires sync before tokens are sent in. For that router the fee leaves the manager between its sync and its settle, so it is credited owed - fee, its IMD delta stays at -fee and unlock reverts. An unhooked twin pool accepts the same router and funds, so the hook alone causes the revert. The brief says the hook never reverts a real swap. README's definition of that phrase (lines 109-113) says no hook policy, time gate or failed fee payout rejects a valid, funded v4 swap, and its list of exclusions does not include this case. No funds are lost because the whole unlock reverts. Routers that sync immediately before transferring (V4Router, UniversalRouter, the project's PoolActor) are unaffected, so this is a broken guarantee for one integrator class, not an outage: medium. Sells through the same router work because IMD is then the output, not the synced currency. Fix: use the existing mint path when IMD is the synced currency. Trial fix, in a throwaway copy, at the top of _pay: if (Currency.unwrap(TransientStateLibrary.getSyncedCurrency(poolManager)) == IMD) mint the claim, emit FeeDeferred and return. With it this proof passes, and so does the other specialist's proof.","line":161,"path":"src/TestHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {BalanceDelta} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {ModifyLiquidityParams, SwapParams} from \"v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {TestHook} from \"src/TestHook.sol\";\nimport {TestToken} from \"src/TestToken.sol\";\n\ncontract PlainERC20 is ERC20 {\n    constructor() ERC20(\"IMD double\", \"IMD\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Seeds liquidity with the usual sync -> transfer -> settle order after the operation.\ncontract Seeder is IUnlockCallback {\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function add(PoolKey memory key, int256 liquidity) external {\n        manager.unlock(abi.encode(key, liquidity));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (PoolKey memory key, int256 liquidity) = abi.decode(data, (PoolKey, int256));\n        (BalanceDelta d,) = manager.modifyLiquidity(\n            key, ModifyLiquidityParams(TickMath.minUsableTick(60), TickMath.maxUsableTick(60), liquidity, 0), \"\"\n        );\n        _pay(key.currency0, d.amount0());\n        _pay(key.currency1, d.amount1());\n        return \"\";\n    }\n\n    function _pay(Currency c, int128 amt) private {\n        if (amt < 0) {\n            manager.sync(c);\n            c.transfer(address(manager), uint256(-int256(amt)));\n            manager.settle();\n        }\n    }\n}\n\n/// @dev A router that calls sync on its input currency BEFORE the swap and settles after it.\n/// Every amount it transfers is exactly what the swap says it owes.\ncontract SyncFirstRouter is IUnlockCallback {\n    IPoolManager immutable manager;\n\n    constructor(IPoolManager m) {\n        manager = m;\n    }\n\n    function swap(PoolKey memory key, SwapParams memory params) external returns (BalanceDelta) {\n        return abi.decode(manager.unlock(abi.encode(key, params)), (BalanceDelta));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (PoolKey memory key, SwapParams memory params) = abi.decode(data, (PoolKey, SwapParams));\n        Currency input = params.zeroForOne ? key.currency0 : key.currency1;\n        Currency output = params.zeroForOne ? key.currency1 : key.currency0;\n        manager.sync(input);\n        BalanceDelta delta = manager.swap(key, params, \"\");\n        int128 inAmt = params.zeroForOne ? delta.amount0() : delta.amount1();\n        int128 outAmt = params.zeroForOne ? delta.amount1() : delta.amount0();\n        input.transfer(address(manager), uint256(-int256(inAmt)));\n        manager.settle();\n        if (outAmt > 0) manager.take(output, address(this), uint128(outAmt));\n        return abi.encode(delta);\n    }\n}\n\ncontract SyncFirstRouterProof is Test {\n    address constant IMD = 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127;\n    uint160 constant FLAGS = 0x20cc;\n    uint160 constant Q96 = 79228162514264337593543950336;\n\n    IPoolManager manager;\n    TestToken token;\n    TestHook hook;\n    PoolKey key;\n    bool imdIs0;\n\n    function setUp() public {\n        vm.etch(IMD, address(new PlainERC20()).code);\n        manager = IPoolManager(address(new PoolManager(address(this))));\n        token = new TestToken();\n        imdIs0 = IMD < address(token);\n        hook = _deployHook();\n        key = PoolKey(\n            Currency.wrap(imdIs0 ? IMD : address(token)),\n            Currency.wrap(imdIs0 ? address(token) : IMD),\n            12_500,\n            60,\n            IHooks(address(hook))\n        );\n        manager.initialize(key, Q96);\n        Seeder seeder = new Seeder(manager);\n        token.transfer(address(seeder), 10_000_000 ether);\n        PlainERC20(IMD).mint(address(seeder), 10_000_000 ether);\n        seeder.add(key, 1e24);\n    }\n\n    function _deployHook() internal returns (TestHook) {\n        bytes32 hash =\n            keccak256(abi.encodePacked(type(TestHook).creationCode, abi.encode(manager, address(token))));\n        for (uint256 i; i < 300_000; ++i) {\n            bytes32 salt = bytes32(i);\n            address predicted =\n                address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, hash)))));\n            if (uint160(predicted) & ((1 << 14) - 1) == FLAGS) return new TestHook{salt: salt}(manager, address(token));\n        }\n        revert(\"no salt\");\n    }\n\n    /// @notice A buy (IMD in) through a router that syncs IMD before the swap must settle.\n    /// Fails today: afterSwap's take(IMD) lowers the manager's IMD balance between sync and settle,\n    /// so settle credits the router (transferred - fee) and unlock reverts CurrencyNotSettled.\n    function test_syncFirstRouterCanBuy() public {\n        SyncFirstRouter router = new SyncFirstRouter(manager);\n        PlainERC20(IMD).mint(address(router), 1000 ether);\n        uint256 vaultBefore = ERC20(IMD).balanceOf(hook.VAULT());\n        bool zeroForOne = imdIs0; // IMD in, token out\n        uint160 limit = zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1;\n        BalanceDelta d = router.swap(key, SwapParams(zeroForOne, -100 ether, limit));\n        int128 imdDelta = imdIs0 ? d.amount0() : d.amount1();\n        assertEq(imdDelta, -100 ether, \"router paid the full budget\");\n        assertEq(\n            ERC20(IMD).balanceOf(hook.VAULT()) + hook.pendingFees() - vaultBefore, 0.75 ether, \"fee accounted\"\n        );\n    }\n\n    /// @notice A sell (token in) through the same router works, because the fee currency is not synced.\n    function test_syncFirstRouterCanSell() public {\n        SyncFirstRouter router = new SyncFirstRouter(manager);\n        token.transfer(address(router), 1000 ether);\n        bool zeroForOne = !imdIs0; // token in, IMD out\n        uint160 limit = zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1;\n        router.swap(key, SwapParams(zeroForOne, -100 ether, limit));\n    }\n}","reproduction":"State: local PoolManager, IMD stand-in etched at 0x5F7B...7127, TestToken, TestHook mined to flags 0x20cc, launch key (fee 12500, spacing 60) initialized at 2^96, full-range liquidity 1e24. Router R holds 1000 IMD and runs inside unlock: sync(IMD); swap(key, {zeroForOne: IMD is currency0, amountSpecified: -100e18, limit: min/max}); IMD.transfer(manager, 100e18); settle(); take(token). Expected: swap settles, R pays 100 IMD, 0.75 IMD reaches the vault or pendingFees. Actual: afterSwap's take sends 0.75e18 IMD out between sync and settle, settle credits 99.25e18, and unlock reverts CurrencyNotSettled(). Reproduced by running both specialist proofs: forge test --match-path on the attached proof gives [FAIL: CurrencyNotSettled()] test_syncFirstRouterCanBuy and [PASS] test_syncFirstRouterCanSell; Proof_4bd9dc4c08d3 gives [FAIL: CurrencyNotSettled()] test_syncFirstRouterBuySucceedsOnLaunchPool.","severity":"medium","snippet":"        try poolManager.take(Currency.wrap(IMD), VAULT, fee) {","title":"afterSwap's direct take() makes every IMD-input swap revert CurrencyNotSettled for a router that syncs IMD before the swap, breaking 'the hook never reverts a real swap'"},{"citation":"resolved","description":"Merges audit_permissions 0be84a2d, audit_math a01d7fef and audit_flow d189f67e. test/RobinhoodFork.t.sol reads ROBINHOOD_RPC_URL, skips when it is empty (lines 32-36) and pins FORK_BLOCK = 108_279_434 (line 28). README.md:157-159 and docs/VALIDATION.md:26-29 still give a --fork-url/--fork-block-number 108268099 command that never sets that variable, so it skips everything. If the variable is set, createSelectFork overrides the flag and the suite runs at 108279434. README.md, docs/VALIDATION.md:14,33 and docs/fork-evidence.json record a pass of 'eight' tests and header and code hashes for 108268099; the committed suite has 11 tests in each of two fixtures (22 in total, not the 16 one specialist counted). Nothing in the tree records a pass or code hashes for the committed suite at its pinned block. This is an evidence defect, not a logic defect: fork tests are a stated deliverable, but the suite itself works.","line":159,"path":"README.md","reproduction":"1. With ROBINHOOD_RPC_URL unset, forge test --match-path test/RobinhoodFork.t.sol reports '0 tests passed, 0 failed, 2 skipped' (run here; the --fork-url flag does not change the gate, and audit_permissions and audit_math ran it with the flag). Expected per README: the fork tests execute. 2. Compare test/RobinhoodFork.t.sol:28 (108_279_434) with README.md:159, docs/VALIDATION.md:28,33 and docs/fork-evidence.json l2Block (108268099). 3. cast code <IMD> --block 108279434 --rpc-url https://rpc.mainnet.chain.robinhood.com returns 'historical state ... is not available', so the pinned block needs an archive endpoint. For calibration: the same suite in a scratch copy with only FORK_BLOCK changed to 108_314_000, against that public endpoint, gave 22 passed, 0 failed. The behaviour holds on live state; the committed block was not run here. Fix: make README.md, docs/VALIDATION.md and docs/fork-evidence.json use ROBINHOOD_RPC_URL and the pinned block, and record a run of the current suite.","severity":"low","snippet":"  --fork-block-number 108268099","title":"The documented fork procedure runs no fork test, and the recorded fork evidence describes a different suite and block than the committed test"},{"citation":"resolved","description":"Merges audit_permissions 6d2f9267, audit_math 8f888526 and audit_flow 503dc547. The sentence predates the manifest step. The manifest itself was read and matches the code: constructorArgs [$poolManager, $token] in declaration order, permissions equal to getHookPermissions, fee 12500, tickSpacing 60, lowercase pairedCurrency equal to the IMD constant, notes under 4,000 characters. Only the README is wrong.","line":50,"path":"README.md","reproduction":"ls launch.json lists the file; README.md:50 says 'No `launch.json` is included'. Expected: the README points to the manifest. Actual: it denies that the manifest exists.","severity":"info","snippet":"No `launch.json` is included: the network's later manifest step supplies it. No deployment or","title":"README says no launch.json is included, but launch.json is in the tree"}],"hash":"333af718dc4d5111c49c7c11f80d049ecf7a91b5d9a2de559b7b9cf4c4aa7bd7","nodeId":"04a8ea06-318c-425b-8676-f8c7f294d11b","outcome":"completed","summary":"The review is done and written to `.imd-findings.json`: 3 findings, none critical or high, and coverage rows for all 8 entry points plus 3 invariants. The only source defect is the medium one, a hook revert on a class of routers. The other two are stale docs and evidence. I removed my scratch tests and changed no tracked files.\n\n## Findings kept\n\n| # | Severity | Finding | Merged from | How it was reproduced |\n|---|---|---|---|---|\n| 1 | Medium | At `src/TestHook.sol:161`, `afterSwap` pays the fee with a direct `take()`. A router that syncs IMD before the swap then fails every buy with `CurrencyNotSettled`. | `audit_math`, `audit_flow` | Ran both specialist proofs: each fails on the buy for that reason, and the sell through the same router passes. |\n| 2 | Low | The fork command in the README runs no fork tests, and the recorded evidence is for an older suite and block (8 tests at 108268099; the tree has 22 at 108279434). | All three specialists who reported it | With `ROBINHOOD_RPC_URL` unset, the suite reports `0 passed, 2 skipped`. I compared the block numbers across the test, README and docs. |\n| 3 | Info | `README.md:50` says no `launch.json` is included, but one is. The manifest itself matches the code. | All three specialists who reported it | Read the file and the README line. |\n\n**Finding 1 severity:** the specialists rated it low; I raised it to medium because it breaks the brief's \"the hook never reverts a real swap\", and the README's exclusions don't cover this case. No funds are lost, and V4Router and UniversalRouter should be unaffected because they sync right before transferring. A trial fix in a throwaway copy, minting the claim when IMD is the synced currency, made both proofs pass. The attached proof is the `audit_math` specialist's.\n\n## Dropped findings and tool leads\n\n- **Dust rounding (under 134 wei pays no fee):** the README documents it, and each split trade still pays the LP fee and gas, so it isn't a defect.\n- **A second pool without the hook:** this is inherent to a plain token with a pool hook, which is what the brief asked for.\n- **Slither `divide-before-multiply`:** false positive; `_fraction` computes exactly `floor(a*75/den)`.\n- **Slither and Aderyn low-severity lines:** no defect came out of them.\n\n## My own pass\n\nI found nothing the specialists missed. I traced whether the previewed fee can differ from the fee `afterSwap` charges, on full and partial fills, and it can't. A mismatch would have left the hook with an unsettled balance and reverted the swap.\n\n| Check | Result |\n|---|---|\n| Offline suite | 57 passed, 2 fork fixtures skipped |\n| Hook floor, with factory probe | 4 passed |\n| Fork suite at recent block 108314000, public RPC | 22 passed |\n\n## Limits\n\n- **Pinned fork block not run:** the public RPC has no state for 108279434, so the 22 passes are from a scratch copy that changed only the block number.\n- **Other floor suites not run:** I didn't run `Owners`, `OwnToken` or `Token`. `Owners` needs harness files that aren't in the tree. I read `Owners` in full, and its checks fit the hook since it has no owner or liquidity callbacks. I read only the start of `OwnToken`. The token rows rest on it being unmodified OpenZeppelin ERC-20.\n- **Possible floor compile problem, not reported as a finding:** `Hook.protected.t.sol` calls `fail(string)`, which the repo's `lib/forge-std` doesn't have. I added a small local helper to run it. This only matters if the verifier compiles that file against this `lib/`, which I can't see, and `lib/` is off-limits to the author anyway.","treeHash":null,"usage":{"cachedInputTokens":790087,"cost":{"source":"runtime","usdMicros":1652668},"inputTokens":18,"model":"claude-opus-5-5","outputTokens":30983,"runtime":"claude","turns":14,"wallClockMs":411324}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ea62f3bebece9304","findings":[],"hash":"6e4b63fd0121372376626afd46e178859ecce869ee09fced0cb821513c4e32b5","nodeId":"bf56a55c-c773-4021-a357-e363717d3d87","outcome":"completed","summary":"I found no defects in the economics of `TestHook` / `TestToken`. `.imd-findings.json` contains an empty `findings` list and a coverage row for each of the 8 entry points, all marked `holds`, plus two invariant rows. Apart from that file, I changed nothing in the repository.\n\n**What I checked against v4-core's `PoolManager.swap` and `Hooks.sol` in `lib/`:**\n- **Fee in every mode:** the fee is always `floor(gross executed IMD × 75 / 10000)`. I worked through all four cases:\n  - **Exact-input buy:** a full fill pays `floor(A·75/10000)`. A partial fill pays `floor(P·75/9925)`, which comes to the same 0.75% of what was actually spent, `P+f`.\n  - **Exact-output buy:** the fee is charged after the swap, so the trader pays `P` plus `floor(P·75/9925)`.\n  - **Exact-input sell:** the trader receives the IMD output minus `floor(G·75/10000)`.\n  - **Exact-output sell:** the trader still receives exactly the IMD amount they asked for.\n- **Swaps that only partly fill:** for exact-input buys and exact-output sells the hook previews the swap first. I showed that the fee it charges based on that preview can never exceed its maximum estimate, and that the real swap then fills exactly what the preview did. So the fee worked out before the swap always equals the one paid after it. If they ever differed, the hook's balance with the pool manager would not settle and the swap would revert; that can't happen here.\n- **Who balances what:** the fee the hook claims is cancelled exactly by what it pays out, either sent straight to the vault or recorded as an IMD claim (ERC-6909) it holds when the direct transfer fails. The preview always reverts, which rolls back its price change, fee growth and balance changes. Only the hook can call the preview, and the pool manager skips the hook's own callbacks when the hook is the one swapping, so it can't loop back on itself.\n- **Nothing can redirect the fee:** the vault, IMD address and rate are constants. There are no setters, no owner, no `hookData` input and no time or block logic. The hook never approves anyone to move its IMD claims, and `flushFees` only ever pays the fixed vault.\n- **Static-analysis leads:** Slither's divide-before-multiply warning on `_fraction` is a false positive; the formula is algebraically the same as `floor(a·75/d)` and can't overflow. The reentrancy-events warnings have no economic effect.\n- **Existing tests:** I ran the suite: 57 passed and 2 were skipped. The skips are fork tests that only run when a Robinhood Chain RPC URL is configured.\n\n**Not covered:**\n- I didn't run any fork tests against live Robinhood Chain state.\n- No test sets a protocol fee. My reasoning says it doesn't change the result, because the preview runs the same pool code the real swap does, but nothing exercises it.\n- How the vault splits the fees each week and the claim contract holding the launch remainder are outside this code.\n\n**Two minor points, not reported as findings:**\n- The two fees apply one after the other, so a trade actually costs about 1.99%, not exactly 2%. The README already says this.\n- Trades under 134 wei of IMD pay no hook fee because of rounding down. The amount is economically meaningless.","treeHash":null,"usage":{"cachedInputTokens":461456,"cost":{"source":"runtime","usdMicros":854136},"inputTokens":17,"model":"claude-opus-5-5","outputTokens":16211,"runtime":"claude","turns":9,"wallClockMs":188505}},{"artifacts":[],"attempt":1,"bundleHash":"07cf32acb3b7dee9086adf67ac6be0c4dd4ef991e6b044d682877093596f649c","device":"7eb57f222fe63696","findings":[],"hash":"6ee710828f6f63a3c6d366f2070b058615562c00627bc3f426d37b3c35b75e46","nodeId":"13ac6ca0-ed53-452c-85ec-e205d5a3dfcd","outcome":"completed","summary":"Implemented the immutable 0.75% IMD-fee hook, fixed-supply token, vendored dependencies and [deployment documentation](/root/.identitymd/work/8b5c783a-4491-4d02-b45d-0a5d75d263f8/13ac6ca0-ed53-452c-85ec-e205d5a3dfcd/README.md).\n\n- Build, formatting and 57 local tests pass.\n- Eight Robinhood fork tests passed initially.\n- A later fork rerun stopped with HTTP 429: “Free tier rate limit exceeded.”\n\nNo deployment was performed.","treeHash":"087fcfe81a740a7e11aa6445f72a7740ad89aeb1","usage":{"cachedInputTokens":5368320,"cost":{"source":"estimated","usdMicros":9119590},"inputTokens":141137,"model":"gpt-6-astra","outputTokens":46798,"runtime":"codex","turns":10,"wallClockMs":1455082}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"26f033b775c04047","findings":[],"hash":"73a554404d8a1e37a34ac679f21808e3c06f4c2abd3e1546f363a923c7bc5a21","nodeId":"e2f71d8d-d092-4059-9680-f2f0b71181d2","outcome":"failed","summary":"this task's fork tests failed here, so it was not submitted:\nThis machine's own RPC endpoint for chain 4663 failed 16 of the fork tests' 60 call(s): chain 4663 endpoint: archive relay busy. An endpoint without archive state cannot serve block 108279434.","treeHash":null,"usage":{"cachedInputTokens":4442112,"inputTokens":204210,"model":"gpt-6-astra","outputTokens":45629,"runtime":"codex","turns":15,"wallClockMs":1573844}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"09078b7cdfb673fe","findings":[{"citation":"resolved","description":"Commit cb96917 rewrote test/RobinhoodFork.t.sol: it now has 16 tests in two contracts, reads the endpoint from the ROBINHOOD_RPC_URL variable, skips when that variable is empty, and pins L2 block 108279434 with vm.createSelectFork. README.md (lines 157-160), docs/VALIDATION.md (lines 14, 26-29, 33) and docs/fork-evidence.json still describe the first contributor's suite: eight tests, block 108268099, run with `--fork-url ... --fork-block-number 108268099`. Two consequences. (1) The documented command no longer runs any fork test: with the variable unset both setUp functions call vm.skip(true), so forge reports '0 tests passed, 0 failed, 2 skipped' and none of the 16 fork tests executes, whatever endpoint the flag names. (2) The only recorded passing fork run (VALIDATION.md: 'Eight passed ... at L2 block 108,268,099') is for a suite and block that are no longer in the tree; no passing run of the current 16-test suite at 108279434 is recorded. During this review the current suite could not be run either: the Pinax free tier answered HTTP 429 at setUp and the official endpoint rpc.mainnet.chain.robinhood.com has no historical state at that block. This is a documentation and evidence gap, not a code defect; the offline suite and the verifier's protected hook floor (emulated here with the factory probe) pass.","line":28,"path":"test/RobinhoodFork.t.sol","reproduction":"1. `forge test --match-path test/RobinhoodFork.t.sol --fork-url <any live endpoint>` with ROBINHOOD_RPC_URL unset (the README command): expected per README 'Run the Robinhood suite', actual: '0 tests passed, 0 failed, 2 skipped' (both setUps skip, no fork test body runs), because _forkSetup reads vm.envOr(\"ROBINHOOD_RPC_URL\", \"\") and skips. Verified here against a local anvil as the --fork-url. 2. Compare test/RobinhoodFork.t.sol:28 (108_279_434) with docs/VALIDATION.md:14,28,33 and docs/fork-evidence.json l2Block (108268099). 3. `ROBINHOOD_RPC_URL=https://robinhood.rpc.pinax.network forge test --match-path test/RobinhoodFork.t.sol`: setUp fails with HTTP 429 'Free tier rate limit exceeded'; `cast code 0x5F7B...7127 --block 108279434 --rpc-url https://rpc.mainnet.chain.robinhood.com` returns 'historical state ... is not available'. Fix: update README.md, docs/VALIDATION.md and docs/fork-evidence.json to the ROBINHOOD_RPC_URL form and the pinned block, and record a run of the current suite (or state that none has passed).","severity":"info","snippet":"    uint256 internal constant FORK_BLOCK = 108_279_434;","title":"Fork documentation describes a different suite and block than the one in the tree; the documented command runs no fork test"},{"citation":"resolved","description":"Commit d3bcafd added launch.json (constructorArgs $poolManager, $token; permissions matching getHookPermissions; pairedCurrency 0x5f7b...7127; fee 12500; tickSpacing 60; notes 1931 chars). The README, written before that step, still says no manifest is included. The manifest itself was checked and matches the implementation; only the README sentence is stale.","line":50,"path":"README.md","reproduction":"`ls launch.json` shows the file; README.md:50 says it is not included. Expected: README describes the manifest that is present. Fix: replace the sentence with a pointer to launch.json.","severity":"info","snippet":"No `launch.json` is included: the network's later manifest step supplies it. No deployment or","title":"README states no launch.json is included, but launch.json is in the tree"}],"hash":"77d6b5960b52659eb468a028dca896d13e15107340c40146272baf36d43f7baa","nodeId":"306772f9-6b16-461c-b420-be5e474e65f9","outcome":"completed","summary":"The review is complete and `.imd-findings.json` holds two info-level findings and fifteen coverage rows. No permission, trust-gap or asymmetry defect was found in the contracts.\n\n**Result for the assigned area (Access Control, Trust Gap, Asymmetry)**\n\n- **Access control holds.** Every callback the hook implements checks the caller is the PoolManager, and the quote entry point accepts only the hook itself and always reverts. The fee rate, vault, IMD address and LP tier are constants, with no owner, setter, fallback, receive, proxy or ERC-6909 approval anywhere. The pool id is fixed in the constructor, so only the launch pool can ever initialize on this hook, from any initializer and at any price.\n- **Trust gap holds.** The payout path pays the constant vault directly and falls back to a self-held claim if the IMD transfer fails. Anyone may redeem claims, but only to the vault. A gas-forced fallback is infeasible, since the inner transfer would need to run out of gas with over six million gas still available.\n- **Asymmetry holds.** The simulate-then-execute pair in beforeSwap and afterSwap agrees in every case I fuzzed. I added three extra fuzz tests in test/scratch covering any price limit, dust amounts at limits, tick crossings and multi-swap sequences in both currency orders. All passed with zero nonzero hook deltas, so no valid swap is reverted by the hook.\n\n**Verification I ran**\n\n- The offline suite passes with 57 tests.\n- A copy of the verifier's protected hook floor, run with a factory probe and token probe, passes all four tests including initialization from the launch factory.\n- On Robinhood Chain the hardcoded IMD address answers symbol IMD with 18 decimals, and both the vault and the PoolManager have code.\n\n**Findings written (both info)**\n\n1. The fork documentation describes the previous contributor's suite. README and VALIDATION.md give a command using a fork-url flag at block 108268099, but the current suite reads the endpoint from an environment variable and pins block 108279434. The documented command skips both suites, which I confirmed against a local anvil. No passing run of the current suite is recorded.\n2. README still says no launch.json is included, while one is present and matches the implementation.\n\n**Not reached**\n\nThe fork suite could not be executed here. The Pinax free tier returned HTTP 429 at setUp, and the official endpoint has no historical state at the pinned block. That row is marked unreached in the coverage record.","treeHash":null,"usage":{"cachedInputTokens":1963467,"cost":{"source":"runtime","usdMicros":5973097},"inputTokens":450,"model":"claude-fable-5-1","outputTokens":41425,"runtime":"claude","turns":54,"wallClockMs":664589}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"8f08088e7a7f557f","findings":[],"hash":"881abd310e6f1931b00b4231a27eb491eabf08ce242d6e2eec8aaa34a54e21fa","nodeId":"e2f71d8d-d092-4059-9680-f2f0b71181d2","outcome":"failed","summary":"this task's fork tests failed here, so it was not submitted:\nThe tests failed:\n948063)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2320725)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139081)\n[PASS] test_exactInputBuyPaysIMD() (gas: 483926)\n[PASS] test_exactInputSellPaysIMD() (gas: 416842)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 422628)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 472220)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 461578)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1175251)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 499968)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1964114)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218821)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141817)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 617051)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1271823)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 920654)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1365622)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1964288)\n[PASS] test_syncFirstRouterBuysAndSellsWithFeeAccounted() (gas: 1332290)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 13435645)\n[PASS] test_zeroFillPaysNoFee() (gas: 13892366)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 933.84ms (2.76s CPU time)\n\nRan 26 tests for test/TestHook.t.sol:HookIMD1Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 448018, ~: 427327)\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 973610, ~: 1015376)\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 471851, ~: 496062)\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 927727, ~: 948896)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2317696)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139596)\n[PASS] test_exactInputBuyPaysIMD() (gas: 471141)\n[PASS] test_exactInputSellPaysIMD() (gas: 423195)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 417639)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 482491)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 461446)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1169141)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 505144)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1962869)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218761)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141999)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 604242)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1259038)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 907148)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1359688)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1963043)\n[PASS] test_syncFirstRouterBuysAndSellsWithFeeAccounted() (gas: 1326187)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 17120021)\n[PASS] test_zeroFillPaysNoFee() (gas: 17591410)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 1.06s (1.63s CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD1InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime","treeHash":null,"usage":{"cachedInputTokens":1251444,"cost":{"source":"runtime","usdMicros":1800103},"inputTokens":41,"model":"claude-opus-5-5","outputTokens":26109,"runtime":"claude","turns":22,"wallClockMs":364495}},{"artifacts":[],"attempt":2,"bundleHash":"dda59dc5df124edb4e131c0aaf8ea35cc00408a2cf36e6c7a4ea0b4a6406a4ed","device":"63274cc86a9400de","findings":[{"description":"The brief says the 0.75% hook fee and the factory's 1.25% LP fee together cost 2% per trade. In v4 the two are taken one after the other on different bases. On an exact-input buy the hook takes 0.75% of the IMD budget and the LP fee then takes 1.25% of what is left, so 0.9925 * 0.9875 = 0.98009375 of the input goes on to the curve. That is a deduction of 1.990625%. The README already says so, and the hook charges exactly 0.75% of gross executed IMD as asked. This is a wording gap in the spec, not a code defect.","line":22,"path":"src/TestHook.sol","reproduction":"Fork test testFork_exactInputBuyAndSellChargeExactly75Bps (test/RobinhoodFork.t.sol) at Robinhood block 108279434: an exact-input buy of 100 IMD pays exactly 0.75 IMD to the vault, and the core swap's IMD input is 99.25 IMD, of which the pool's LP fee is 1.25%. Expected under a literal '2% total': 2 IMD in fees. Actual: 0.75 + 1.240625 = 1.990625 IMD.","severity":"info","title":"Hook fee plus LP fee comes to 1.990625%, not exactly 2%"}],"hash":"aefd01e10b3c3dba8478b973a81ec66a02d5e656458eb5a50cc49a5a02b46735","nodeId":"e2f71d8d-d092-4059-9680-f2f0b71181d2","outcome":"completed","summary":"I left the committed tests unchanged. The local check failure was caused by forge's RPC requests, not by the tests: no test failed, and I couldn't fix it from the test files.\n\n**What failed:** 2 of the fork run's 128 RPC calls were `eth_getAccountInfo`, which the Robinhood Chain endpoint doesn't offer. Forge 1.8.3 sends that call on its own. The first time it loads an account it hasn't cached, it sends `eth_getAccountInfo` and the plain balance/nonce/code calls side by side, then keeps the plain ones once it sees which works. Robinhood Chain gets this \"try both\" mode by default, and only `foundry.toml` or forge itself can change it, neither of which I may touch. I confirmed this in forge's source for the installed build (`foundry-fork-db/src/backend.rs`). So the first uncached account a fork test touches always produces at least one failed call, whatever the test does.\n\n**Why not editing should get through:** the check's last run left a fork cache on this machine (`~/.foundry/cache/rpc/robinhood/108279434`). Forge reuses it for the same chain and block even with a different host, and doesn't re-fetch accounts already in it. Every address the current fork tests touch is fixed (named test addresses, deployment addresses, the pinned fuzz seed), so they should all come from the cache and no new `eth_getAccountInfo` should go out. The `eth_chainId` and block lookups the fork makes still reach the chain, so the tests do run against it. Any edit that touched a new address would bring the failure back. If the check runs with that cache cleared, it will fail the same way again no matter what the tests contain, and the fix would have to come from the endpoint or forge.\n\n**Checked here:**\n- With `ROBINHOOD_RPC_URL` empty and the verifier's fuzz seed, `forge test` passes: 57 passed, 0 failed. The 2 fork suites skip cleanly, and the invariant test passes 128 runs (4,096 calls, no reverts).\n- With the variable set, the fork suites can't connect from this shell because the sandbox blocks the socket. Only the check after I finish can run them.\n\nThe fork tests already cover the brief:\n- the 0.75% fee in IMD reaching the vault on exact-input and exact-output buys and sells;\n- the rate staying the same across blocks, time, routers and hook data;\n- no admin function, caller or faked hook call able to redirect the fee;\n- partial fills charging only on the IMD that actually traded;\n- the fresh-manager case, where the fee is held as an IMD claim and anyone can send it to the vault, and only to the vault.\n\nThey also cover the failure paths: wrong pool settings, reopening the pool, an invalid price limit and an unfunded router.\n\nI filed no findings and left no `.imd-findings.json` or `.imd-blocked.json`.","treeHash":"f1e48aeaa3a8b259f5a3071d22307c68db8ac157","usage":{"cachedInputTokens":2144078,"chain":[{"calls":260,"chainId":4663,"failed":0,"own":252,"public":0,"refused":8}],"cost":{"source":"runtime","usdMicros":2378065},"inputTokens":67,"model":"claude-opus-5-5","outputTokens":36666,"runtime":"claude","turns":39,"wallClockMs":503248}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fd7c32744e171d7d","findings":[],"hash":"b9d43747aeb397188a043afa670fb3a8839458fe57a255f318fc54f5af736f29","nodeId":"e2f71d8d-d092-4059-9680-f2f0b71181d2","outcome":"failed","summary":"dependency 07cf32acb3b7 unavailable","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":0,"wallClockMs":125213}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"476778ae63dff113","findings":[{"citation":"resolved","description":"Settles edb402bb (the author answered not_reproducible). The author's reply describes their own branch. In the tree under review, which is the merge with the reapplied tester commit cb96917 and the manifest commit d3bcafd, test/RobinhoodFork.t.sol reads ROBINHOOD_RPC_URL and skips when it is empty (lines 33-36: `string memory url = vm.envOr(\"ROBINHOOD_RPC_URL\", string(\"\"));` then `vm.skip(true);`). It pins `uint256 internal constant FORK_BLOCK = 108_279_434;` (line 29) and has 12 tests in each of two fixtures (24 in total). README.md:155-163 says the suite skips when 'no active fork exists' and gives a --fork-url/--fork-block-number 108268099 command. README.md:172-176 and docs/VALIDATION.md:14-15,28-34 report 'eight' passing tests, and docs/fork-evidence.json records header and code hashes at 108268099. None of that matches the committed suite. This is an evidence and documentation defect, not a logic defect. I ran the committed suite against live state (see reproduction), and all 24 tests pass, including the new testFork_syncFirstRouterOnRealManagerStillPaysVaultInIMD.","line":162,"path":"README.md","reproduction":"1. With ROBINHOOD_RPC_URL unset, `forge test` on this tree gives '64 tests passed, 0 failed, 2 skipped'. Both RobinhoodFork fixtures skip in setUp, and the --fork-url flag that README.md:160-162 documents does not change the env gate. Expected per README: the fork tests execute. 2. Compare test/RobinhoodFork.t.sol:29 (108_279_434, 24 tests) with README.md:162, docs/VALIDATION.md:14,29,34 and docs/fork-evidence.json l2Block (108268099, 'eight'). 3. `cast code 0x5F7B...7127 --block 108279434 --rpc-url https://rpc.mainnet.chain.robinhood.com` returns 'historical state ... is not available' (rerun today; head is 108335545), so the pinned block needs an archive endpoint. 4. Calibration: a scratch copy that changes only FORK_BLOCK to 108_335_000, run with ROBINHOOD_RPC_URL=https://rpc.mainnet.chain.robinhood.com, gives 24 passed, 0 failed. Fix: make README.md, docs/VALIDATION.md and docs/fork-evidence.json describe the committed suite (ROBINHOOD_RPC_URL, block 108279434, 24 tests), and record a run of it or say that none was recorded.","severity":"low","snippet":"  --fork-block-number 108268099","title":"Documented fork command runs no fork test, and the recorded fork evidence (eight tests, block 108268099) does not describe the committed 24-test suite pinned at 108279434"},{"citation":"resolved","description":"Settles 62aa9a08 (the author answered not_reproducible). The author's branch has no manifest, but the tree being judged does: launch.json was added by the reapplied manifest commits d3bcafd/f40492d and adjusted by 354df54. So README.md:50 is false here. This sentence is the only problem. I checked the manifest again against the revised code. Its constructorArgs [$poolManager, $token] match constructor(IPoolManager manager_, address token_) in declaration order. Its permissions match getHookPermissions (beforeInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta, afterSwapReturnDelta; flags 0x20cc). fee 12500 = LP_FEE, tickSpacing 60 = TICK_SPACING, pairedCurrency is the IMD constant in lowercase, and the notes are one string under 4,000 characters that describes the sync-first claim path. Nothing to fix beyond the sentence.","line":50,"path":"README.md","reproduction":"`ls launch.json` in the repository root at HEAD 354df54 lists the file, but README.md:50 says none is included. Expected: the README points to the manifest. Actual: it says the manifest does not exist.","severity":"info","snippet":"No `launch.json` is included: the network's later manifest step supplies it. No deployment or","title":"README says no launch.json is included, but the tree under review ships launch.json"}],"hash":"e685e46b32db58d3c7967d1051e45eb306803a58fe8794b0bf77cd90d9d7d24f","nodeId":"04a8ea06-318c-425b-8676-f8c7f294d11b","outcome":"completed","summary":"The medium finding is fixed. The two advisory findings still stand in the tree under review, though the author's replies were accurate for their own branch. My review is written to `.imd-findings.json`: 2 findings, and all 8 entry points marked `holds`.\n\n**[medium] The sync-first router buy reverted with `CurrencyNotSettled`: fixed.** `_pay` in `src/TestHook.sol:164` now checks whether IMD is the PoolManager's synced currency. If it is, the hook records the fee as a claim it holds instead of sending it out mid-swap, and `flushFees` later pays it only to the fixed vault. I ran all three earlier proofs against the revised code and all pass (`Proof_fcdfccacd77d`, `Proof_e3a0887ea581`, `Proof_4bd9dc4c08d3`). The full suite gives 64 passed and 0 failed; the 2 Robinhood fork fixtures skip without an RPC URL. I also ran the fork suite against live Robinhood state at a recent block (108,335,000), changing nothing but the block number: 24 of 24 passed, including the new test of a sync-first router on the real PoolManager. The fix introduced nothing new that I could find: holding the fee as a claim is safe whatever was last synced.\n\n**Why the two advisory findings still stand.** The author answered both as not reproducible, and their branch didn't have these problems. The tree being judged is that branch merged with two other contributors' reapplied commits (the fork tests and the manifest), and both problems show up there:\n\n- **[low] Fork documentation doesn't match the committed fork suite.**\n  - `test/RobinhoodFork.t.sol` reads `ROBINHOOD_RPC_URL`, pins block 108,279,434 and has 24 tests.\n  - `README.md:162` and `docs/VALIDATION.md` give a `--fork-block-number 108268099` command that never sets that variable. Run that way, both fork fixtures skip and no fork test executes.\n  - The docs and `docs/fork-evidence.json` still record \"eight\" tests and the hashes for block 108,268,099.\n  - The public endpoint still can't serve state at 108,279,434 (\"historical state ... is not available\"), so the suite needs an archive node at its own pinned block.\n\n  This is a problem with the evidence, not the code: the suite itself passes against live state.\n- **[info] `README.md:50` says no `launch.json` is included, but one is in the tree.** I checked the manifest against the revised hook and it matches: constructor arguments in declaration order, permissions, fee 12500, tick spacing 60 and the IMD address. Only the README sentence is wrong.\n\nThe four protected floor tests could not be run here: they import harness files (`mocks/`, `floor/`) that aren't in the repository. My scratch files are removed and no project files were changed.","treeHash":null,"usage":{"cachedInputTokens":567248,"cost":{"source":"runtime","usdMicros":715809},"inputTokens":19,"model":"claude-opus-5-5","outputTokens":7438,"runtime":"claude","turns":12,"wallClockMs":126730}},{"artifacts":[],"attempt":2,"bundleHash":"9ddeedc953eba0cd9d7d08238a4e053fea9df9d5559c11f6ff7f6f697edbee07","device":"a6bc9bc7471a6711","findings":[],"hash":"f2fcaebdd00ff3c222644e5eb2ff75af9ac12dcd2e5bc7c069b35ac600367061","nodeId":"e2f71d8d-d092-4059-9680-f2f0b71181d2","outcome":"completed","summary":"A background search I started earlier was stopped when it hit its time limit. It was a slow scan of the forge binary for `eth_getAccountInfo`, and I don't need to restart it. Faster searches turned up the same thing, and I confirmed it in forge 1.8.3's source.\n\nNothing else has changed since my last message: I made no code changes. The fork run fails because the chain 4663 endpoint doesn't support `eth_getAccountInfo`. Forge sends that call once at the start of each fork before switching to other calls, and the local check counts those two failed calls against the run. The offline suite passes with the verifier's seed: 59 passed, 0 failed, and the 2 fork suites skipped because `ROBINHOOD_RPC_URL` was empty.","treeHash":"92590d1636b334bf7918f39f87cf863afc2fe9ae","usage":{"cachedInputTokens":1180010,"chain":[{"calls":413,"chainId":4663,"failed":0,"own":401,"public":0,"refused":12}],"cost":{"source":"runtime","usdMicros":2000581},"inputTokens":42,"model":"claude-opus-5-5","outputTokens":12316,"runtime":"claude","turns":21,"wallClockMs":963467}},{"artifacts":[],"attempt":1,"bundleHash":"028e0f328b949b752d2468b9e9c4585d787f06aff10d2e27e67a1fa23e097679","device":"970da38f1d564250","findings":[],"hash":"f5a81ec0f24d09a2d0e85ab5d991f1f5e6b538476df9d2f6f9e2c7324bfb84d5","nodeId":"13ac6ca0-ed53-452c-85ec-e205d5a3dfcd","outcome":"completed","summary":"I fixed the medium finding (swaps reverting for routers that sync IMD first) and answered all four findings in `.imd-responses.json`. `forge build`, `forge test` (with the given fuzz seed) and `forge fmt --check` all pass.\n\n**Medium: buys revert for routers that sync IMD before swapping. Fixed.**\n- **Reproduced:** on the starting tree, the reviewer's `test_syncFirstRouterCanBuy` failed with `CurrencyNotSettled()`.\n- **Cause:** the hook paid the fee out of the PoolManager in the middle of the swap. A router that syncs IMD before the swap and settles after it was then credited 0.75 IMD short, so the whole swap reverted.\n- **Change:** in `src/TestHook.sol`, when IMD is the synced currency, the hook no longer sends the fee. It records it as a claim the hook holds inside the PoolManager, using the deferral path that was already there. Anyone can call `flushFees()` to send it, and it only ever goes to the fixed vault. Every other case still pays the vault directly.\n- **New test:** `test_syncFirstRouterBuysAndSellsWithFeeAccounted`, run for both token orders. A buy through such a router holds back exactly 0.75 IMD, a sell pays the vault directly, and the flush delivers the held fee. The test fails with `CurrencyNotSettled` on the old hook and passes now, as do both of the reviewer's proof tests.\n- **Live check:** the pinned fork block can't be read today. The public endpoint no longer keeps that old state, and Pinax rate-limits with HTTP 429. So I ran a throwaway copy of the fork suite with only the two block-pinning checks removed. It passed 8/8 against Robinhood Chain at block 108,318,098.\n- **Docs:** I updated `README.md` and `docs/VALIDATION.md` to describe the new behaviour, the test count (59) and that live run.\n\n**Advisory findings:**\n- **Fork-test docs describe a different suite (low): not reproducible.** In this tree the fork test doesn't read `ROBINHOOD_RPC_URL`. It runs when you pass `--fork-url`, is pinned to block 108,268,099 and has 8 tests, which matches the README, `docs/VALIDATION.md` and `docs/fork-evidence.json`. The reviewer seems to have looked at a different version.\n- **README says there's no `launch.json` (info): not reproducible.** There is no `launch.json` in this tree, so the README is accurate. I didn't add one, since the manifest step writes it.\n- **Fees total 1.990625%, not 2% (info): disputed.** The brief asks for exactly 0.75%, and that's what the hook takes. The pool's 1.25% fee is then taken from what's left, so the two add up to slightly under 2%. The README already explains this, and raising the hook's rate to reach exactly 2% would break the brief.\n\nNothing is committed.","treeHash":"557dd277273e18377607be3319761fcd6adb9d71","usage":{"cachedInputTokens":642562,"cost":{"source":"runtime","usdMicros":723272},"inputTokens":27,"model":"claude-opus-5-5","outputTokens":9389,"runtime":"claude","turns":14,"wallClockMs":169221}}],"verification":[{"checks":[{"durationMs":6052,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.89s\nCompiler run successful!\n","passed":true},{"durationMs":1569,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodIMD1ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 928.43µs (0.00ns CPU time)\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodIMD0ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 4.61ms (0.00ns CPU time)\n\nRan 1 test for test/Runtime.t.sol:RuntimeTest\n[PASS] test_deployedContractsHaveNoProxyOrDestructionOpcodes() (gas: 4109872)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 21.42ms (12.09ms CPU time)\n\nRan 4 tests for test/TestToken.t.sol:TestTokenTest\n[PASS] testFuzz_launchTransfersAndAllowanceConserveSupply(uint256) (runs: 1000, μ: 268327, ~: 268159)\nLogs:\n  Bound result 28651210425792660876\n\n[PASS] test_fixedLaunchSupplyAndMetadata() (gas: 52745)\n[PASS] test_invalidTransfersDoNotChangeBalances() (gas: 80596)\n[PASS] test_noOwnerMintPauseBlacklistOrUpgradeEvenForDeployer() (gas: 314945)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 500.75ms (501.43ms CPU time)\n\nRan 25 tests for test/TestHook.t.sol:HookIMD0Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 446915, ~: 425798)\nLogs:\n  Bound result 9193624835078910437800\n\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 970420, ~: 926452)\nLogs:\n  Bound result 80000000000000000013841\n\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 469592, ~: 443620)\nLogs:\n  Bound result 90000000000000000006311\n\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 925428, ~: 902690)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2319105)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139170)\n[PASS] test_exactInputBuyPaysIMD() (gas: 483094)\n[PASS] test_exactInputSellPaysIMD() (gas: 416032)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 421818)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 471410)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 459936)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1173609)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 499158)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1960874)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218821)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141817)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 616241)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1270991)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 919034)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1363980)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1961048)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 19877809)\n[PASS] test_zeroFillPaysNoFee() (gas: 20355653)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 546.01ms (2.00s CPU time)\n\nRan 25 tests for test/TestHook.t.sol:HookIMD1Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 446485, ~: 426144)\nLogs:\n  Bound result 1147\n\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 970199, ~: 926899)\nLogs:\n  Bound result 80000000000000067530065\n\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 469403, ~: 444007)\nLogs:\n  Bound result 90000000000000000000997\n\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 925066, ~: 904269)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2316076)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139685)\n[PASS] test_exactInputBuyPaysIMD() (gas: 470309)\n[PASS] test_exactInputSellPaysIMD() (gas: 422385)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 416829)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 481681)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 459804)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1167499)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 504334)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1959629)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218761)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141999)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 603432)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1258206)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 905528)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1358046)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1959803)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 8235119)\n[PASS] test_zeroFillPaysNoFee() (gas: 8671031)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 545.94ms (1.08s CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD0InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime        | 981   | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | flush              | 1019  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | setDeliveryFailure | 1063  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | trade              | 1033  | 0       | 0        |\n╰------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2424\n  Bound result 4846\n  Bound result 5066\n  Bound result 1433\n  Bound result 10\n  Bound result 2978\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.17s (1.13s CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD1InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime        | 981   | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | flush              | 1019  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | setDeliveryFailure | 1063  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | trade              | 1033  | 0       | 0        |\n╰------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 391\n  Bound result 4844\n  Bound result 9921\n  Bound result 5578\n  Bound result 10\n  Bound result 2958\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.33s (1.31s CPU time)\n\nRan 8 test suites in 1.34s (4.13s CPU time): 57 tests passed, 0 failed, 2 skipped (59 total tests)\n","passed":true},{"durationMs":90,"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.flushFees()\",\"TestHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"TestHook.unlockCallback(bytes)\",\"TestToken.approve(address,uint256)\",\"TestToken.transfer(address,uint256)\",\"TestToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":175,\"docs/SECURITY.md\":37,\"docs/VALIDATION.md\":60,\"docs/dependencies.json\":26,\"docs/fork-evidence.json\":25,\"docs/token-metadata.json\":8,\"foundry.toml\":22,\"launch.json\":30,\"remappings.txt\":4,\"src/HookFlags.sol\":29,\"src/TestHook.sol\":220,\"src/TestToken.sol\":12,\"test/Invariant.t.sol\":94,\"test/RobinhoodFork.t.sol\":297,\"test/Runtime.t.sol\":34,\"test/TestHook.t.sol\":374,\"test/TestToken.t.sol\":72,\"test/helpers/HookFixture.sol\":189,\"test/helpers/PoolActor.sol\":55,\"test/mocks/MockERC20.sol\":46},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"0c605df1e035237ef3a6ba0c6e489d1b710c0178e10e8a82879e43c8926df87c","verifiedTreeHash":"740e425e68b4b1ea05444a426efefe675bb6045d","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":4420,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.32s\nCompiler run successful!\n","passed":true},{"durationMs":994,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodIMD1ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 528.64µs (0.00ns CPU time)\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodIMD0ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 488.96µs (0.00ns CPU time)\n\nRan 1 test for test/Runtime.t.sol:RuntimeTest\n[PASS] test_deployedContractsHaveNoProxyOrDestructionOpcodes() (gas: 4208350)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 18.14ms (12.41ms CPU time)\n\nRan 4 tests for test/TestToken.t.sol:TestTokenTest\n[PASS] testFuzz_launchTransfersAndAllowanceConserveSupply(uint256) (runs: 1000, μ: 268332, ~: 268159)\nLogs:\n  Bound result 387691433952205441080904553\n\n[PASS] test_fixedLaunchSupplyAndMetadata() (gas: 52745)\n[PASS] test_invalidTransfersDoNotChangeBalances() (gas: 80596)\n[PASS] test_noOwnerMintPauseBlacklistOrUpgradeEvenForDeployer() (gas: 314945)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 329.23ms (329.26ms CPU time)\n\nRan 26 tests for test/TestHook.t.sol:HookIMD1Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 447172, ~: 426634)\nLogs:\n  Bound result 137\n\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 971009, ~: 927709)\nLogs:\n  Bound result 62088196246489519433200\n\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 470214, ~: 444817)\nLogs:\n  Bound result 90000014434924834868579\n\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 925876, ~: 905079)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2317696)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139596)\n[PASS] test_exactInputBuyPaysIMD() (gas: 471141)\n[PASS] test_exactInputSellPaysIMD() (gas: 423195)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 417639)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 482491)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 461446)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1169141)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 505144)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1962869)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218761)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141999)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 604242)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1259038)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 907148)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1359688)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1963043)\n[PASS] test_syncFirstRouterBuysAndSellsWithFeeAccounted() (gas: 1326187)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 17120021)\n[PASS] test_zeroFillPaysNoFee() (gas: 17591410)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 329.45ms (875.01ms CPU time)\n\nRan 26 tests for test/TestHook.t.sol:HookIMD0Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 447867, ~: 426649)\nLogs:\n  Bound result 1097\n\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 971230, ~: 927262)\nLogs:\n  Bound result 80000000000000000000475\n\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 470402, ~: 444430)\nLogs:\n  Bound result 90000000000001865706980\n\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 926238, ~: 903500)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2320725)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139081)\n[PASS] test_exactInputBuyPaysIMD() (gas: 483926)\n[PASS] test_exactInputSellPaysIMD() (gas: 416842)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 422628)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 472220)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 461578)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1175251)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 499968)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1964114)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218821)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141817)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 617051)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1271823)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 920654)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1365622)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1964288)\n[PASS] test_syncFirstRouterBuysAndSellsWithFeeAccounted() (gas: 1332290)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 13435645)\n[PASS] test_zeroFillPaysNoFee() (gas: 13892366)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 329.72ms (1.33s CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD0InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime        | 981   | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | flush              | 1019  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | setDeliveryFailure | 1063  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | trade              | 1033  | 0       | 0        |\n╰------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2806\n  Bound result 1643\n  Bound result 5450\n  Bound result 514264337593543950334\n  Bound result 10\n  Bound result 819074626\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 839.88ms (814.06ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD1InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime        | 981   | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | flush              | 1019  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | setDeliveryFailure | 1063  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | trade              | 1033  | 0       | 0        |\n╰------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2443\n  Bound result 1641\n  Bound result 5452\n  Bound result 2056570213\n  Bound result 10\n  Bound result 819074624\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 882.69ms (879.45ms CPU time)\n\nRan 8 test suites in 884.42ms (2.73s CPU time): 59 tests passed, 0 failed, 2 skipped (61 total tests)\n","passed":true},{"durationMs":58,"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.flushFees()\",\"TestHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"TestHook.unlockCallback(bytes)\",\"TestToken.approve(address,uint256)\",\"TestToken.transfer(address,uint256)\",\"TestToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":180,\"docs/SECURITY.md\":37,\"docs/VALIDATION.md\":61,\"docs/dependencies.json\":26,\"docs/fork-evidence.json\":25,\"docs/token-metadata.json\":8,\"foundry.toml\":22,\"launch.json\":30,\"remappings.txt\":4,\"src/HookFlags.sol\":29,\"src/TestHook.sol\":228,\"src/TestToken.sol\":12,\"test/Invariant.t.sol\":94,\"test/RobinhoodFork.t.sol\":335,\"test/Runtime.t.sol\":34,\"test/TestHook.t.sol\":429,\"test/TestToken.t.sol\":72,\"test/helpers/HookFixture.sol\":189,\"test/helpers/PoolActor.sol\":55,\"test/mocks/MockERC20.sol\":46},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"1b4f8fb6c9388543ec6d2723afeb5beb9ed102212da8f4e787e8271f2fb5dc84","verifiedTreeHash":"aef3d60e2a90f6d6dd311d1026ba4252f740fd9b","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":4168,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.07s\nCompiler run successful!\n","passed":true},{"durationMs":953,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodIMD1ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 771.81µs (0.00ns CPU time)\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodIMD0ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 761.65µs (0.00ns CPU time)\n\nRan 1 test for test/Runtime.t.sol:RuntimeTest\n[PASS] test_deployedContractsHaveNoProxyOrDestructionOpcodes() (gas: 4109872)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 24.76ms (14.18ms CPU time)\n\nRan 4 tests for test/TestToken.t.sol:TestTokenTest\n[PASS] testFuzz_launchTransfersAndAllowanceConserveSupply(uint256) (runs: 1000, μ: 268332, ~: 268159)\nLogs:\n  Bound result 10000000000000000000000\n\n[PASS] test_fixedLaunchSupplyAndMetadata() (gas: 52745)\n[PASS] test_invalidTransfersDoNotChangeBalances() (gas: 80596)\n[PASS] test_noOwnerMintPauseBlacklistOrUpgradeEvenForDeployer() (gas: 314945)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 310.32ms (310.37ms CPU time)\n\nRan 25 tests for test/TestHook.t.sol:HookIMD1Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 446485, ~: 426144)\nLogs:\n  Bound result 67530064\n\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 970199, ~: 926899)\nLogs:\n  Bound result 80000000000000000005634\n\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 469403, ~: 444007)\nLogs:\n  Bound result 90000000000000000013592\n\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 925066, ~: 904269)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2316076)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139685)\n[PASS] test_exactInputBuyPaysIMD() (gas: 470309)\n[PASS] test_exactInputSellPaysIMD() (gas: 422385)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 416829)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 481681)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 459804)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1167499)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 504334)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1959629)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218761)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141999)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 603432)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1258206)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 905528)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1358046)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1959803)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 8235119)\n[PASS] test_zeroFillPaysNoFee() (gas: 8671031)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 321.50ms (772.74ms CPU time)\n\nRan 25 tests for test/TestHook.t.sol:HookIMD0Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 446915, ~: 425798)\nLogs:\n  Bound result 1776585205\n\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 970420, ~: 926452)\nLogs:\n  Bound result 80000000000000000006311\n\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 469592, ~: 443620)\nLogs:\n  Bound result 90000000000000067530065\n\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 925428, ~: 902690)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2319105)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139170)\n[PASS] test_exactInputBuyPaysIMD() (gas: 483094)\n[PASS] test_exactInputSellPaysIMD() (gas: 416032)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 421818)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 471410)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 459936)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1173609)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 499158)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1960874)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218821)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141817)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 616241)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1270991)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 919034)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1363980)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1961048)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 19877809)\n[PASS] test_zeroFillPaysNoFee() (gas: 20355653)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 321.56ms (1.33s CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD1InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime        | 981   | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | flush              | 1019  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | setDeliveryFailure | 1063  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | trade              | 1033  | 0       | 0        |\n╰------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 391\n  Bound result 4844\n  Bound result 9921\n  Bound result 5578\n  Bound result 10\n  Bound result 2958\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 805.70ms (796.85ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD0InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime        | 981   | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | flush              | 1019  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | setDeliveryFailure | 1063  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | trade              | 1033  | 0       | 0        |\n╰------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2424\n  Bound result 4846\n  Bound result 5066\n  Bound result 1433\n  Bound result 10\n  Bound result 2978\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 831.39ms (802.90ms CPU time)\n\nRan 8 test suites in 832.74ms (2.62s CPU time): 57 tests passed, 0 failed, 2 skipped (59 total tests)\n","passed":true},{"durationMs":48,"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.flushFees()\",\"TestHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"TestHook.unlockCallback(bytes)\",\"TestToken.approve(address,uint256)\",\"TestToken.transfer(address,uint256)\",\"TestToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":175,\"docs/SECURITY.md\":37,\"docs/VALIDATION.md\":60,\"docs/dependencies.json\":26,\"docs/fork-evidence.json\":25,\"docs/token-metadata.json\":8,\"foundry.toml\":22,\"remappings.txt\":4,\"src/HookFlags.sol\":29,\"src/TestHook.sol\":220,\"src/TestToken.sol\":12,\"test/Invariant.t.sol\":94,\"test/RobinhoodFork.t.sol\":90,\"test/Runtime.t.sol\":34,\"test/TestHook.t.sol\":374,\"test/TestToken.t.sol\":72,\"test/helpers/HookFixture.sol\":189,\"test/helpers/PoolActor.sol\":55,\"test/mocks/MockERC20.sol\":46},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1205,"exitCode":0,"name":"slither","output":"[medium/medium] divide-before-multiply at src/TestHook.sol:200: TestHook._fraction(uint256,uint256) (src/TestHook.sol#200-203) performs a multiplication on the result of a division:\n[low/medium] reentrancy-events at src/TestHook.sol:160: Reentrancy in TestHook._pay(uint256) (src/TestHook.sol#160-168):\n[low/medium] reentrancy-events at src/TestHook.sol:160: Reentrancy in TestHook._pay(uint256) (src/TestHook.sol#160-168):\n[low/medium] reentrancy-events at src/TestHook.sol:143: Reentrancy in TestHook.unlockCallback(bytes) (src/TestHook.sol#143-154):","passed":true},{"durationMs":424,"exitCode":0,"name":"aderyn","output":"[low] internal-function-used-once at src/HookFlags.sol:22: Internal Function Used Only Once\n[low] large-numeric-literal at src/TestHook.sol:24: Large Numeric Literal (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"6ee710828f6f63a3c6d366f2070b058615562c00627bc3f426d37b3c35b75e46","verifiedTreeHash":"087fcfe81a740a7e11aa6445f72a7740ad89aeb1","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":4373,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.26s\nCompiler run successful!\n","passed":true},{"durationMs":1927,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Runtime.t.sol:RuntimeTest\n[PASS] test_deployedContractsHaveNoProxyOrDestructionOpcodes() (gas: 4109872)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 22.30ms (14.35ms CPU time)\n\nRan 4 tests for test/TestToken.t.sol:TestTokenTest\n[PASS] testFuzz_launchTransfersAndAllowanceConserveSupply(uint256) (runs: 1000, μ: 268327, ~: 268159)\nLogs:\n  Bound result 600\n\n[PASS] test_fixedLaunchSupplyAndMetadata() (gas: 52745)\n[PASS] test_invalidTransfersDoNotChangeBalances() (gas: 80596)\n[PASS] test_noOwnerMintPauseBlacklistOrUpgradeEvenForDeployer() (gas: 314945)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 321.67ms (321.65ms CPU time)\n\nRan 25 tests for test/TestHook.t.sol:HookIMD1Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 446485, ~: 426144)\nLogs:\n  Bound result 996\n\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 970199, ~: 926899)\nLogs:\n  Bound result 80000000000000000000997\n\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 469403, ~: 444007)\nLogs:\n  Bound result 90000000377634026187443\n\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 925066, ~: 904269)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2316076)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139685)\n[PASS] test_exactInputBuyPaysIMD() (gas: 470309)\n[PASS] test_exactInputSellPaysIMD() (gas: 422385)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 416829)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 481681)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 459804)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1167499)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 504334)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1959629)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218761)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141999)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 603432)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1258206)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 905528)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1358046)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1959803)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 8235119)\n[PASS] test_zeroFillPaysNoFee() (gas: 8671031)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 321.78ms (810.05ms CPU time)\n\nRan 25 tests for test/TestHook.t.sol:HookIMD0Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 446915, ~: 425798)\nLogs:\n  Bound result 8092\n\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 970420, ~: 926452)\nLogs:\n  Bound result 80000000000000067530065\n\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 469592, ~: 443620)\nLogs:\n  Bound result 90000000000000000008093\n\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 925428, ~: 902690)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2319105)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139170)\n[PASS] test_exactInputBuyPaysIMD() (gas: 483094)\n[PASS] test_exactInputSellPaysIMD() (gas: 416032)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 421818)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 471410)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 459936)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1173609)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 499158)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1960874)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218821)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141817)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 616241)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1270991)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 919034)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1363980)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1961048)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 19877809)\n[PASS] test_zeroFillPaysNoFee() (gas: 20355653)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 321.79ms (1.31s CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD0InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime        | 981   | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | flush              | 1019  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | setDeliveryFailure | 1063  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | trade              | 1033  | 0       | 0        |\n╰------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2424\n  Bound result 4846\n  Bound result 5066\n  Bound result 1433\n  Bound result 10\n  Bound result 2978\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 816.98ms (781.10ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD1InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime        | 981   | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | flush              | 1019  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | setDeliveryFailure | 1063  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | trade              | 1033  | 0       | 0        |\n╰------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 391\n  Bound result 4844\n  Bound result 9921\n  Bound result 5578\n  Bound result 10\n  Bound result 2958\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 863.78ms (850.43ms CPU time)\n\nRan 11 tests for test/RobinhoodFork.t.sol:RobinhoodIMD0ForkTest\n[PASS] testForkFuzz_rateIsAlways75BpsOfGrossIMD(uint96,bool,bool,uint32,uint32) (runs: 1000, μ: 432947, ~: 412638)\nLogs:\n  Bound result 12261\n\n[PASS] testFork_everyBuyAndSellModePaysVaultInIMD() (gas: 1715235)\n[PASS] testFork_exactInputBuyAndSellChargeExactly75Bps() (gas: 924397)\n[PASS] testFork_invalidOrUnfundedSwapsLeaveVaultUntouched() (gas: 1223770)\n[PASS] testFork_liquidityFeesRemainCollectibleWithoutHookFee() (gas: 1163111)\n[PASS] testFork_noCallerCanRedirectOrRetuneTheFee() (gas: 1625319)\n[PASS] testFork_partialFillsChargeOnlyExecutedIMD() (gas: 923100)\n[PASS] testFork_pinnedChainHasRealIMDVaultAndManager() (gas: 102082)\n[PASS] testFork_rateIdenticalAcrossTimeBlocksRoutersAndHookData() (gas: 2959020)\n[PASS] testFork_realManagerRejectsAnyOtherPoolOnThisHook() (gas: 140989)\n[PASS] testFork_tokenOnlyManagerDefersRealIMDThenFlushesToVault() (gas: 7759187)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 1.81s (1.81s CPU time)\n\nRan 11 tests for test/RobinhoodFork.t.sol:RobinhoodIMD1ForkTest\n[PASS] testForkFuzz_rateIsAlways75BpsOfGrossIMD(uint96,bool,bool,uint32,uint32) (runs: 1000, μ: 432426, ~: 413115)\nLogs:\n  Bound result 7643\n\n[PASS] testFork_everyBuyAndSellModePaysVaultInIMD() (gas: 1713941)\n[PASS] testFork_exactInputBuyAndSellChargeExactly75Bps() (gas: 911719)\n[PASS] testFork_invalidOrUnfundedSwapsLeaveVaultUntouched() (gas: 1247708)\n[PASS] testFork_liquidityFeesRemainCollectibleWithoutHookFee() (gas: 1158101)\n[PASS] testFork_noCallerCanRedirectOrRetuneTheFee() (gas: 1615244)\n[PASS] testFork_partialFillsChargeOnlyExecutedIMD() (gas: 923408)\n[PASS] testFork_pinnedChainHasRealIMDVaultAndManager() (gas: 102082)\n[PASS] testFork_rateIdenticalAcrossTimeBlocksRoutersAndHookData() (gas: 2947608)\n[PASS] testFork_realManagerRejectsAnyOtherPoolOnThisHook() (gas: 140989)\n[PASS] testFork_tokenOnlyManagerDefersRealIMDThenFlushesToVault() (gas: 16188987)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 1.81s (1.81s CPU time)\n\nRan 8 test suites in 1.81s (6.28s CPU time): 79 tests passed, 0 failed, 0 skipped (79 total tests)\n","passed":true},{"durationMs":51,"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.flushFees()\",\"TestHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"TestHook.unlockCallback(bytes)\",\"TestToken.approve(address,uint256)\",\"TestToken.transfer(address,uint256)\",\"TestToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":175,\"docs/SECURITY.md\":37,\"docs/VALIDATION.md\":60,\"docs/dependencies.json\":26,\"docs/fork-evidence.json\":25,\"docs/token-metadata.json\":8,\"foundry.toml\":22,\"remappings.txt\":4,\"src/HookFlags.sol\":29,\"src/TestHook.sol\":220,\"src/TestToken.sol\":12,\"test/Invariant.t.sol\":94,\"test/RobinhoodFork.t.sol\":297,\"test/Runtime.t.sol\":34,\"test/TestHook.t.sol\":374,\"test/TestToken.t.sol\":72,\"test/helpers/HookFixture.sol\":189,\"test/helpers/PoolActor.sol\":55,\"test/mocks/MockERC20.sol\":46},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"aefd01e10b3c3dba8478b973a81ec66a02d5e656458eb5a50cc49a5a02b46735","verifiedTreeHash":"f1e48aeaa3a8b259f5a3071d22307c68db8ac157","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":4666,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.56s\nCompiler run successful!\n","passed":true},{"durationMs":1687,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Runtime.t.sol:RuntimeTest\n[PASS] test_deployedContractsHaveNoProxyOrDestructionOpcodes() (gas: 4208350)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 25.48ms (13.54ms CPU time)\n\nRan 26 tests for test/TestHook.t.sol:HookIMD0Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 447867, ~: 426649)\nLogs:\n  Bound result 67530064\n\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 971230, ~: 927262)\nLogs:\n  Bound result 80000014434924834868579\n\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 470402, ~: 444430)\nLogs:\n  Bound result 90000000000000000015848\n\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 926238, ~: 903500)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2320725)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139081)\n[PASS] test_exactInputBuyPaysIMD() (gas: 483926)\n[PASS] test_exactInputSellPaysIMD() (gas: 416842)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 422628)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 472220)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 461578)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1175251)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 499968)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1964114)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218821)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141817)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 617051)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1271823)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 920654)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1365622)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1964288)\n[PASS] test_syncFirstRouterBuysAndSellsWithFeeAccounted() (gas: 1332290)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 13435645)\n[PASS] test_zeroFillPaysNoFee() (gas: 13892366)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 342.56ms (1.38s CPU time)\n\nRan 4 tests for test/TestToken.t.sol:TestTokenTest\n[PASS] testFuzz_launchTransfersAndAllowanceConserveSupply(uint256) (runs: 1000, μ: 268332, ~: 268159)\nLogs:\n  Bound result 243\n\n[PASS] test_fixedLaunchSupplyAndMetadata() (gas: 52745)\n[PASS] test_invalidTransfersDoNotChangeBalances() (gas: 80596)\n[PASS] test_noOwnerMintPauseBlacklistOrUpgradeEvenForDeployer() (gas: 314945)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 342.63ms (342.53ms CPU time)\n\nRan 26 tests for test/TestHook.t.sol:HookIMD1Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 447172, ~: 426634)\nLogs:\n  Bound result 42088196246489519583526\n\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 971009, ~: 927709)\nLogs:\n  Bound result 80000000000000000000138\n\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 470214, ~: 444817)\nLogs:\n  Bound result 90000000000000067530065\n\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 925876, ~: 905079)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2317696)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139596)\n[PASS] test_exactInputBuyPaysIMD() (gas: 471141)\n[PASS] test_exactInputSellPaysIMD() (gas: 423195)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 417639)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 482491)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 461446)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1169141)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 505144)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1962869)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218761)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141999)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 604242)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1259038)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 907148)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1359688)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1963043)\n[PASS] test_syncFirstRouterBuysAndSellsWithFeeAccounted() (gas: 1326187)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 17120021)\n[PASS] test_zeroFillPaysNoFee() (gas: 17591410)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 342.65ms (880.47ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD1InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime        | 981   | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | flush              | 1019  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | setDeliveryFailure | 1063  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | trade              | 1033  | 0       | 0        |\n╰------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2443\n  Bound result 1641\n  Bound result 5452\n  Bound result 2056570213\n  Bound result 10\n  Bound result 819074624\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 888.53ms (878.02ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD0InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime        | 981   | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | flush              | 1019  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | setDeliveryFailure | 1063  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | trade              | 1033  | 0       | 0        |\n╰------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2806\n  Bound result 1643\n  Bound result 5450\n  Bound result 514264337593543950334\n  Bound result 10\n  Bound result 819074626\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 972.14ms (920.34ms CPU time)\n\nRan 12 tests for test/RobinhoodFork.t.sol:RobinhoodIMD0ForkTest\n[PASS] testForkFuzz_rateIsAlways75BpsOfGrossIMD(uint96,bool,bool,uint32,uint32) (runs: 1000, μ: 434178, ~: 414008)\nLogs:\n  Bound result 4431\n\n[PASS] testFork_everyBuyAndSellModePaysVaultInIMD() (gas: 1720405)\n[PASS] testFork_exactInputBuyAndSellChargeExactly75Bps() (gas: 927037)\n[PASS] testFork_invalidOrUnfundedSwapsLeaveVaultUntouched() (gas: 1225046)\n[PASS] testFork_liquidityFeesRemainCollectibleWithoutHookFee() (gas: 1165707)\n[PASS] testFork_noCallerCanRedirectOrRetuneTheFee() (gas: 1626635)\n[PASS] testFork_partialFillsChargeOnlyExecutedIMD() (gas: 925762)\n[PASS] testFork_pinnedChainHasRealIMDVaultAndManager() (gas: 102210)\n[PASS] testFork_rateIdenticalAcrossTimeBlocksRoutersAndHookData() (gas: 2964310)\n[PASS] testFork_realManagerRejectsAnyOtherPoolOnThisHook() (gas: 140853)\n[PASS] testFork_syncFirstRouterOnRealManagerStillPaysVaultInIMD() (gas: 1531477)\n[PASS] testFork_tokenOnlyManagerDefersRealIMDThenFlushesToVault() (gas: 11287172)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 1.56s (2.01s CPU time)\n\nRan 12 tests for test/RobinhoodFork.t.sol:RobinhoodIMD1ForkTest\n[PASS] testForkFuzz_rateIsAlways75BpsOfGrossIMD(uint96,bool,bool,uint32,uint32) (runs: 1000, μ: 434216, ~: 414491)\nLogs:\n  Bound result 14459\n\n[PASS] testFork_everyBuyAndSellModePaysVaultInIMD() (gas: 1719159)\n[PASS] testFork_exactInputBuyAndSellChargeExactly75Bps() (gas: 914383)\n[PASS] testFork_invalidOrUnfundedSwapsLeaveVaultUntouched() (gas: 1249008)\n[PASS] testFork_liquidityFeesRemainCollectibleWithoutHookFee() (gas: 1160743)\n[PASS] testFork_noCallerCanRedirectOrRetuneTheFee() (gas: 1616608)\n[PASS] testFork_partialFillsChargeOnlyExecutedIMD() (gas: 926094)\n[PASS] testFork_pinnedChainHasRealIMDVaultAndManager() (gas: 102210)\n[PASS] testFork_rateIdenticalAcrossTimeBlocksRoutersAndHookData() (gas: 2952946)\n[PASS] testFork_realManagerRejectsAnyOtherPoolOnThisHook() (gas: 140901)\n[PASS] testFork_syncFirstRouterOnRealManagerStillPaysVaultInIMD() (gas: 1526477)\n[PASS] testFork_tokenOnlyManagerDefersRealIMDThenFlushesToVault() (gas: 13058587)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 1.56s (2.04s CPU time)\n\nRan 8 test suites in 1.57s (6.04s CPU time): 83 tests passed, 0 failed, 0 skipped (83 total tests)\n","passed":true},{"durationMs":57,"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.flushFees()\",\"TestHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"TestHook.unlockCallback(bytes)\",\"TestToken.approve(address,uint256)\",\"TestToken.transfer(address,uint256)\",\"TestToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":180,\"docs/SECURITY.md\":37,\"docs/VALIDATION.md\":61,\"docs/dependencies.json\":26,\"docs/fork-evidence.json\":25,\"docs/token-metadata.json\":8,\"foundry.toml\":22,\"remappings.txt\":4,\"src/HookFlags.sol\":29,\"src/TestHook.sol\":228,\"src/TestToken.sol\":12,\"test/Invariant.t.sol\":94,\"test/RobinhoodFork.t.sol\":335,\"test/Runtime.t.sol\":34,\"test/TestHook.t.sol\":429,\"test/TestToken.t.sol\":72,\"test/helpers/HookFixture.sol\":189,\"test/helpers/PoolActor.sol\":55,\"test/mocks/MockERC20.sol\":46},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f2fcaebdd00ff3c222644e5eb2ff75af9ac12dcd2e5bc7c069b35ac600367061","verifiedTreeHash":"92590d1636b334bf7918f39f87cf863afc2fe9ae","verifierVersion":"0.1.0+18543036"},{"checks":[{"durationMs":4322,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.21s\nCompiler run successful!\n","passed":true},{"durationMs":990,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodIMD1ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.04ms (0.00ns CPU time)\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodIMD0ForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.01ms (0.00ns CPU time)\n\nRan 1 test for test/Runtime.t.sol:RuntimeTest\n[PASS] test_deployedContractsHaveNoProxyOrDestructionOpcodes() (gas: 4208350)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 20.41ms (12.47ms CPU time)\n\nRan 4 tests for test/TestToken.t.sol:TestTokenTest\n[PASS] testFuzz_launchTransfersAndAllowanceConserveSupply(uint256) (runs: 1000, μ: 268332, ~: 268159)\nLogs:\n  Bound result 600\n\n[PASS] test_fixedLaunchSupplyAndMetadata() (gas: 52745)\n[PASS] test_invalidTransfersDoNotChangeBalances() (gas: 80596)\n[PASS] test_noOwnerMintPauseBlacklistOrUpgradeEvenForDeployer() (gas: 314945)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 320.26ms (319.45ms CPU time)\n\nRan 26 tests for test/TestHook.t.sol:HookIMD1Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 447172, ~: 426634)\nLogs:\n  Bound result 5374\n\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 971009, ~: 927709)\nLogs:\n  Bound result 80000000000000000000138\n\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 470214, ~: 444817)\nLogs:\n  Bound result 90000000000001865706980\n\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 925876, ~: 905079)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2317696)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139596)\n[PASS] test_exactInputBuyPaysIMD() (gas: 471141)\n[PASS] test_exactInputSellPaysIMD() (gas: 423195)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 417639)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 482491)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 461446)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1169141)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 505144)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1962869)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218761)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141999)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 604242)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1259038)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 907148)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1359688)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1963043)\n[PASS] test_syncFirstRouterBuysAndSellsWithFeeAccounted() (gas: 1326187)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 17120021)\n[PASS] test_zeroFillPaysNoFee() (gas: 17591410)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 320.33ms (871.15ms CPU time)\n\nRan 26 tests for test/TestHook.t.sol:HookIMD0Test\n[PASS] testFuzz_allSwapModesConserveFunds(uint96,bool,bool) (runs: 1000, μ: 447867, ~: 426649)\nLogs:\n  Bound result 7175\n\n[PASS] testFuzz_crossingInitializedTicksPreservesFeeAccounting(uint96,bool,bool) (runs: 1000, μ: 971230, ~: 927262)\nLogs:\n  Bound result 80000000000000000003885\n\n[PASS] testFuzz_partialFillsTaxOnlyExecutedIMD(uint96,bool,bool) (runs: 1000, μ: 470402, ~: 444430)\nLogs:\n  Bound result 90000000000000000000475\n\n[PASS] testFuzz_previewLeavesPoolStateIdenticalToOneCoreSwap(bool,bool) (runs: 1000, μ: 926238, ~: 903500)\n[PASS] test_constructorRejectsInvalidInputsAndWrongFlags() (gas: 6053)\n[PASS] test_dustRoundingDoesNotRevertOrOvercharge() (gas: 2320725)\n[PASS] test_everyEntryRefusesForgedManagerAndSelfCalls() (gas: 139081)\n[PASS] test_exactInputBuyPaysIMD() (gas: 483926)\n[PASS] test_exactInputSellPaysIMD() (gas: 416842)\n[PASS] test_exactOutputBuyPaysIMD() (gas: 422628)\n[PASS] test_exactOutputSellPreservesRequestedNetIMD() (gas: 472220)\n[PASS] test_extremeSpecifiedRequestsCanStillPartiallyFill() (gas: 461578)\n[PASS] test_factoryCanCollectLPFeesAndRemoveLiquidity() (gas: 1175251)\n[PASS] test_failedRouterSettlementRollsBackVaultPayment() (gas: 499968)\n[PASS] test_falseReturningVaultTransferDoesNotBlockAnySwapMode() (gas: 1964114)\n[PASS] test_initializationAndWrongPoolRejection() (gas: 218821)\n[PASS] test_invalidSwapRollsBackFees() (gas: 141817)\n[PASS] test_noAddressCanStealDeferredClaims() (gas: 617051)\n[PASS] test_noAdminCanRedirectOrChangeTheFee() (gas: 1271823)\n[PASS] test_permissionsAndConstructorImmutables() (gas: 34636)\n[PASS] test_rateDoesNotChangeWithTimeBlocksOrHookData() (gas: 920654)\n[PASS] test_reentrantTokenCannotForgeCallbacksOrRedeemDuringSwap() (gas: 1365622)\n[PASS] test_revertingVaultTransferDoesNotBlockAnySwapMode() (gas: 1964288)\n[PASS] test_syncFirstRouterBuysAndSellsWithFeeAccounted() (gas: 1332290)\n[PASS] test_tokenOnlyManagerDefersFeeThenAnyoneCanFlush() (gas: 13435645)\n[PASS] test_zeroFillPaysNoFee() (gas: 13892366)\nSuite result: ok. 26 passed; 0 failed; 0 skipped; finished in 320.44ms (1.28s CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD0InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime        | 981   | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | flush              | 1019  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | setDeliveryFailure | 1063  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | trade              | 1033  | 0       | 0        |\n╰------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2806\n  Bound result 1643\n  Bound result 5450\n  Bound result 514264337593543950334\n  Bound result 10\n  Bound result 819074626\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 802.22ms (776.98ms CPU time)\n\nRan 1 test for test/Invariant.t.sol:IMD1InvariantTest\n[PASS] invariant_vaultClaimsAndPoolBalancesConserveAllFunds() (runs: 128, calls: 4096, reverts: 0)\n\n╭------------+--------------------+-------+---------+----------╮\n| Contract   | Selector           | Calls | Reverts | Discards |\n+==============================================================+\n| FeeHandler | advanceTime        | 981   | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | flush              | 1019  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | setDeliveryFailure | 1063  | 0       | 0        |\n|------------+--------------------+-------+---------+----------|\n| FeeHandler | trade              | 1033  | 0       | 0        |\n╰------------+--------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2443\n  Bound result 1641\n  Bound result 5452\n  Bound result 2056570213\n  Bound result 10\n  Bound result 819074624\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 866.51ms (862.14ms CPU time)\n\nRan 8 test suites in 868.75ms (2.65s CPU time): 59 tests passed, 0 failed, 2 skipped (61 total tests)\n","passed":true},{"durationMs":60,"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.flushFees()\",\"TestHook.quoteSwap((address,address,uint24,int24,address),(bool,int256,uint160))\",\"TestHook.unlockCallback(bytes)\",\"TestToken.approve(address,uint256)\",\"TestToken.transfer(address,uint256)\",\"TestToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":180,\"docs/SECURITY.md\":37,\"docs/VALIDATION.md\":61,\"docs/dependencies.json\":26,\"docs/fork-evidence.json\":25,\"docs/token-metadata.json\":8,\"foundry.toml\":22,\"remappings.txt\":4,\"src/HookFlags.sol\":29,\"src/TestHook.sol\":228,\"src/TestToken.sol\":12,\"test/Invariant.t.sol\":94,\"test/RobinhoodFork.t.sol\":90,\"test/Runtime.t.sol\":34,\"test/TestHook.t.sol\":429,\"test/TestToken.t.sol\":72,\"test/helpers/HookFixture.sol\":189,\"test/helpers/PoolActor.sol\":55,\"test/mocks/MockERC20.sol\":46},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1289,"exitCode":0,"name":"slither","output":"[medium/medium] divide-before-multiply at src/TestHook.sol:208: TestHook._fraction(uint256,uint256) (src/TestHook.sol#208-211) performs a multiplication on the result of a division:\n[low/medium] reentrancy-events at src/TestHook.sol:161: Reentrancy in TestHook._pay(uint256) (src/TestHook.sol#161-176):\n[low/medium] reentrancy-events at src/TestHook.sol:161: Reentrancy in TestHook._pay(uint256) (src/TestHook.sol#161-176):\n[low/medium] reentrancy-events at src/TestHook.sol:161: Reentrancy in TestHook._pay(uint256) (src/TestHook.sol#161-176):\n[low/medium] reentrancy-events at src/TestHook.sol:144: Reentrancy in TestHook.unlockCallback(bytes) (src/TestHook.sol#144-155):","passed":true},{"durationMs":440,"exitCode":0,"name":"aderyn","output":"[low] internal-function-used-once at src/HookFlags.sol:22: Internal Function Used Only Once\n[low] large-numeric-literal at src/TestHook.sol:25: Large Numeric Literal (2 places)","passed":true},{"durationMs":2074,"exitCode":0,"name":"proof fcdfccacd77d","output":"Compiling 78 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.40s\nCompiler run successful!\n\nRan 2 tests for test/imd-proof-35987482/Proof_fcdfccacd77d.t.sol:SyncFirstRouterProof\n[PASS] test_syncFirstRouterCanBuy() (gas: 964254)\n[PASS] test_syncFirstRouterCanSell() (gas: 859509)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 53.68ms (906.73µs CPU time)\n\nRan 1 test suite in 54.34ms (53.68ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f5a81ec0f24d09a2d0e85ab5d991f1f5e6b538476df9d2f6f9e2c7324bfb84d5","verifiedTreeHash":"557dd277273e18377607be3319761fcd6adb9d71","verifierVersion":"0.1.0+18543036"}]}