{"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":"64ce1d7b-85d2-42cf-a196-0dea01c6cb65","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"0e46e460333e17705dcd3c41012bb3482426b50d10ff89c84d69689be28b4f9f","dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","tools":[]},"key":"adapt_contract_project","kind":"code","role":"implement","skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","state":"accepted"},{"acceptedSubmissionHash":"bf605edcafe633a9981716ad7d863eebb06245310bf905780ddb852dce430662","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"256727dc9766b001bf508772fa87f42fa8d02f02a38229b7f3382341db56fe38","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"c0efcb4a5d97908a469b21626aec4d9fbd63575dc84dcd1a43c4dff2c35e9e6e","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","tools":[]},"key":"audit_imported_code","kind":"code","role":"review","skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","state":"accepted"},{"acceptedSubmissionHash":"73676121684bc35d2feaa863ff89a3fc78166434aa9c083ba1c0c966ad0a24a7","dependsOn":["adapt_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":"2084ee499c8ed683c88261559e038df600aa11b537a75c9421b5c110b2471168","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"737e5bf968bf63379902bbc88662d540a4a181f8e0e844a169ece8b214466cdd","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"94fad1d04037929c8d57defa8b12875fd695677d74d05726a058733d3a8f102c","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"9f433016dec1589b19a622d415d341aa05f69019f9cac22d019338fc591ebe96","dependsOn":["adapt_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":"Deploy the audited IMD Vault factory on Robinhood Chain, unchanged. This is the exact code audited and deployed on Ethereum mainnet in launch 2244 (VaultFactory at 0xdbaff609cd0500527a2a102f5cbbbbc70fd2ff5b). Deploy exactly src/VaultFactory.sol with constructor arguments: imd_=0x0000000000000000000000000000000000000000, feeRecipient_=0x0000000000000000000000000000000000000000. With imd_ zero this copy charges no fee on Robinhood; projects pay their one-time 5 IMD on the Ethereum factory with payFor(4663, token or vault). Do not change any source file: the audit is done; only confirm it builds, its tests pass, and deploy it.","parentJobId":null,"planHash":"d96db05274b2d46a93fc8aea9f7bed59c61f853c41e60be7e19254aedbfb0938","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"64ce1d7b-85d2-42cf-a196-0dea01c6cb65","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2251-deploy-audited-imd-vault"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52164","feedbackHash":"ed8b1d825ce16438c4282a476add19439a5beaea77b8c7d97ad0d68a66e7b3c9","nodeKey":"adapt_contract_project","submissionHash":"11ae73fcb048f9ad42ff0cce2c598cfcc97501e460e3570a4b30a870e225e890","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51343","feedbackHash":"0780addcfecfb36a64fe16f9d3a8f422bb2c4570ffe7c14c9f73e3cbc7d8ea81","nodeKey":"adapt_contract_project","submissionHash":"0e46e460333e17705dcd3c41012bb3482426b50d10ff89c84d69689be28b4f9f","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51420","feedbackHash":"092464819b82c15226a57c5a6be00f2fa7659d27b65304609e2f8474148c0886","nodeKey":"audit_economics","submissionHash":"bf605edcafe633a9981716ad7d863eebb06245310bf905780ddb852dce430662","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51419","feedbackHash":"f1ac6a2452eb9179fbc398c220b4f13155bc4c9b64443a3b2f9f196ec21baee4","nodeKey":"audit_flow","submissionHash":"256727dc9766b001bf508772fa87f42fa8d02f02a38229b7f3382341db56fe38","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51339","feedbackHash":"b37fe4b29ba5f66f09106d2661bb4fddd292e0a9a7f5bac09690d3a8192004f5","nodeKey":"audit_imported_code","submissionHash":"c0efcb4a5d97908a469b21626aec4d9fbd63575dc84dcd1a43c4dff2c35e9e6e","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52665","feedbackHash":"eb59c979ec2820502c45f644ac07644f2bc9ea219b91bdcb5a4041a00ba029aa","nodeKey":"audit_judge","submissionHash":"73676121684bc35d2feaa863ff89a3fc78166434aa9c083ba1c0c966ad0a24a7","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52256","feedbackHash":"992b3f2e1b16dd89c4e2f1ab50fd6bfef7c022355dbb3aebfba4aaf6a7ef3610","nodeKey":"audit_judge","submissionHash":"f2e643172eec6547fb466bca0daa6273f683dac718cc92c9b391a06991e01da4","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51432","feedbackHash":"d89b535b9992fed52bfb6890a1b3497af43fd0bdfdc5c0c54da21bcaf10dd8a6","nodeKey":"audit_math","submissionHash":"2084ee499c8ed683c88261559e038df600aa11b537a75c9421b5c110b2471168","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51430","feedbackHash":"963719f5af42d439f2f1373042b605199f25c4d8be600da1a30d6d7161e80faf","nodeKey":"audit_permissions","submissionHash":"737e5bf968bf63379902bbc88662d540a4a181f8e0e844a169ece8b214466cdd","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51334","feedbackHash":"0278e0855f56ae40b253417f0bd38747e399641f4d3fda6b43063eaf10fbbbcb","nodeKey":"manifest","submissionHash":"94fad1d04037929c8d57defa8b12875fd695677d74d05726a058733d3a8f102c","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51333","feedbackHash":"b18161451fbf010714beb17be06ba24c004d3463173e8d46f6242d45dd1f792a","nodeKey":"write_foundry_tests","submissionHash":"e6ecba2dc68d9b9d125bc2d9150e9f85bd7d5ac67cd61a1b703aa81a9f16a682","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51345","feedbackHash":"a0e320c0af1e2d2e212af89d97753bea9ef6b08bc677f8cc110bede7be41af73","nodeKey":"write_foundry_tests","submissionHash":"9f433016dec1589b19a622d415d341aa05f69019f9cac22d019338fc591ebe96","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"5f635a8deec096d756de1969a6c815c2d7bd39c947ce556974eb6c7681382ebb","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"dcf2aee4e4b01ed8a53feb2864a8ced29920d5353644c843b30013f1b8cf9ca0","device":"feba2a869621cab2","findings":[],"hash":"0e46e460333e17705dcd3c41012bb3482426b50d10ff89c84d69689be28b4f9f","nodeId":"a4f840af-5c0b-4da3-8f59-6e77b434607a","outcome":"completed","summary":"The revision is complete. The audited sources are restored, the suite passes, and every finding is answered.\n\n**What I found**\n\n- **Finding 8094ebd9 (tree not the audited code) reproduces and is right.** The live mainnet factory read today is 18,760 bytes with the stated hash. The accepted base commit compiles to exactly those bytes. The previous round's edits to the two vault sources changed the factory runtime because it embeds their init code.\n- **Findings 9c7bf39d (mirror-token freeze) and 4e5bed0c (dust clock) reproduce too**, but their proofs demand edits to the vault sources. Any such edit changes the factory runtime and fails the held proof for finding 8094ebd9. The three proofs cannot all pass, and the brief says \"unchanged\" and \"do not change any source file\".\n\n**What I changed**\n\n- Restored `src/TreasuryVault.sol` and `src/VestingVault.sol` byte for byte to the launch 2244 code. The factory source was never touched. `src/VaultFactoryNoFee.sol` stays, since its runtime equals the audited factory with two zero arguments.\n- Dropped the five tests and four mocks in `test/Audit.t.sol` that only passed on the reverted edits. Added a test that pins the compiled factory to the mainnet runtime's length and hash, so any future source or compiler edit fails the suite.\n- Rewrote the Robinhood section of `ADAPTATION.md` to say the brief's instruction governs, and to list every reproduced finding as a known behaviour of the audited code with the reason it is not fixed here.\n- Updated the README accounting notes to describe the audited code's real behaviour and the operating rules that follow: only queue trusted tokens, never vest a token that debits the sender beyond the amount, and use a bound vault for a token that existed before the vault.\n- Wrote `.imd-responses.json`: one finding fixed, five disputed with the reproduction and the reason.\n\n**Verification**\n\n| Check | Result |\n|---|---|\n| forge build | passes |\n| forge test with the verifier's seed, project suite | 114 passed, 0 failed |\n| Proof 8094ebd9 on the result | passes |\n| Proofs 9c7bf39d, 4e5bed0c and the overflow proof | still fail, by design, as disputed |\n\n**Open for the requester.** The fixes for the overflow, gas-burning, mirror-token and dust-clock findings exist in this repository's history at commit 98fd654. Carrying them means a new audited release on both chains, which is the requester's decision, not a change to this launch.","treeHash":"17d9e64b8c9a3f02582ceeb29be1187009b5e914","usage":{"cachedInputTokens":2083374,"inputTokens":674,"model":"claude-fable-5-1","outputTokens":26260,"runtime":"claude","turns":27,"wallClockMs":487267}},{"artifacts":[],"attempt":1,"bundleHash":"672f496ba9938c0de04f21ed9306ced270953d6c071e15b3890b3d37a0f8c8ca","device":"63458a1bd2b2d376","findings":[],"hash":"11ae73fcb048f9ad42ff0cce2c598cfcc97501e460e3570a4b30a870e225e890","nodeId":"a4f840af-5c0b-4da3-8f59-6e77b434607a","outcome":"completed","summary":"","treeHash":"1469c2eaab0ffc68500427538c59f00b5e8cff53","usage":{"cachedInputTokens":3025218,"inputTokens":968,"model":"claude-fable-5-1","outputTokens":60207,"runtime":"claude","turns":61,"wallClockMs":1035057}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"03f15d1296244279","findings":[{"citation":"resolved","description":"Area: Boundary (non-standard token at the safeTransfer call site) x Numerical Gap (precision x invariant). The documented invariant is that at most rateBps of an asset's balance leaves the treasury per epoch. For every OTHER tracked asset, execute() measures the real balance drop (`fell = before[i] - now_`, lines 209-212) and charges that. For the asset actually being withdrawn it charges only the nominal `w.amount` (line 193), before the transfer, and never re-reads the balance afterwards. A token whose transfer debits the sender by more than `amount` (a burn or tax taken from the sender in addition to the delivered amount) therefore lets more than rateBps of the balance leave in one epoch, and `windows[asset].spent` under-records what left. The destination still receives at most the cap, so nothing is paid to a wrong party; the breakage is the vault's own guarantee and the accuracy of `spent`/`availableThisWeek` for such tokens. Minimal fix preserving the design: read `_balance(w.asset)` after the transfer as well and charge `before - after` (as the shared path already does), reverting OverWeeklyCap if that exceeds the window.","line":193,"path":"src/TreasuryVault.sol","reproduction":"Token SB: ERC20 whose _update burns an extra value/10 from the sender before delivering value. createTreasury(SB, manager, 1 days, 2500, []). Mint 1000e18 SB to the vault. manager: queue(SB, dest, 250e18). warp +1 day. execute(id). Expected per the stated rule: at most 250e18 (25%) of the balance leaves this epoch. Actual: the vault balance falls from 1000e18 to 725e18 (275e18 = 27.5% left), windows[SB].cap = 250e18 and windows[SB].spent = 250e18, so spent under-records the outflow by 25e18. Verified with test/scratch/MathEdges.t.sol::test_SenderBurnTokenExceedsWeeklyCapOnTheWithdrawnAsset (logs: balance drop 275e18, cap 250e18, spent 250e18).","severity":"low","snippet":"        _charge(windows[w.asset], _balance(w.asset), w.amount);","title":"Weekly cap charges the withdrawn asset its nominal amount, not its measured balance drop (asymmetric with the shared-balance path)"},{"citation":"resolved","description":"Area: Boundary (non-standard token at the safeTransfer call site) x Numerical Gap (boundary x invariant). The vault's accounting is balance-based and documented to tolerate fee-on-transfer tokens, and it does when the fee is taken out of the delivered amount. It does not tolerate a token that takes its fee from the sender on top of the amount: once the schedule has fully elapsed, releasable(asset) == balanceOf(vault) exactly (vestedAmount returns total = balance + released, minus released), release() tries to transfer the entire balance, and the token reverts because the sender would need balance + fee. There is no way to release a smaller amount, no recover path for the vested token, and a top-up does not help (after it, releasable is again the whole balance). The remaining tokens are permanently locked. This also affects a bound vault whose token is upgraded or configured into such behaviour later. Preconditions: the vested token must charge the sender more than the transferred amount; standard and classic fee-on-transfer tokens (fee out of the amount) are unaffected. Minimal fix preserving the design: let the beneficiary release up to the releasable amount (e.g. release(asset, amount) with amount <= releasable), or treat a failed full-balance transfer by retrying with the token's own post-fee maximum; either keeps 'only to the beneficiary, only what has vested'.","line":161,"path":"src/VestingVault.sol","reproduction":"Token SB: ERC20 whose _update burns an extra value/10 from the sender before delivering value. Holder with 2000e18 SB approves the factory and calls createVesting(SB, team, 1000e18, now, 0, 28 days); the vault holds 1000e18. warp +28 days. releasable(SB) == 1000e18 (fully vested). Expected: release(SB) sends the vested tokens to the beneficiary. Actual: release(SB) reverts with ERC20InsufficientBalance(vault, 1000e18, 1100e18); a year later it still reverts, and no other function can move the 1000e18. Verified with test/scratch/MathEdges.t.sol::test_SenderBurnTokenFinalReleaseReverts.","severity":"low","snippet":"        IERC20(asset).safeTransfer(to, amount);","title":"release() always transfers the whole releasable amount, so a token that debits the sender by more than the amount can never complete the final release and the remainder is stuck"},{"citation":"resolved","description":"Area: Numerical Gap (boundary x invariant). The arrival record that anchors an open vault's linear unlock is set by the first sync()/release() while the vault holds ANY non-zero balance of the asset; 1 wei is enough, and the sender does not need to be anyone in particular. The documented property 'a launch that slips past start + cliff still vests from its arrival, never on it' therefore only holds for a token that cannot exist before it is paid in (a token minted inside the launch transaction). For any token that already exists (an open vault funded with a pre-existing token, or any other token the open vault vests 'the same way'), a third party or the beneficiary can send 1 wei at or after start + cliff and call sync(), after which the real tranche, arriving later, is unlocked by the fraction of the schedule already elapsed on arrival. The per-token calendar top-up rule is documented, but the 1-wei dust path turns the first real deposit into a 'top-up' against the stated intent and is triggerable by an unprivileged actor. Impact is bounded: tokens only ever go to the beneficiary; the harm is a weaker vesting commitment than the vault advertises for that token. Possible mitigations that keep the design: document that the arrival anchor is first-balance-based and that open vaults must only be used for tokens minted in the funding transaction, or anchor the clock per tranche (a scope decision).","line":221,"path":"src/VestingVault.sol","reproduction":"Token T exists. createLaunchVesting(team, now, 0, 28 days) -> open vault V. warp +14 days (past start + cliff). stranger mints/sends 1 wei of T to V and calls V.sync(T): arrivedAt[T] = now. warp +14 days. The project sends 1_000_000e18 T to V. Expected per the NatSpec: the tranche vests linearly from its own arrival, nothing unlocked on landing. Actual: releasable(T) == 500_000e18 immediately (elapsed 14 days of 28 since the dust arrival), i.e. 50% is unlocked the moment the tranche lands; 100% at 28 days after the dust. Verified with test/scratch/MathEdges.t.sol::test_OpenVaultDustPreseedStartsClockEarly (log: releasable at arrival 500000000000000000000000).","severity":"low","snippet":"        if (IERC20(asset).balanceOf(address(this)) == 0) return false;\n        arrivedAt[asset] = uint64(block.timestamp);","title":"Open vault arrival clock can be started by anyone with 1 wei, so a later real tranche of an existing token is partly unlocked the moment it lands"},{"citation":"resolved","description":"Area: Numerical Gap (view vs write seam). The view answers from the asset's own window only. execute() additionally charges every other tracked asset whose balance fell, so for a second address to a balance whose main address's window is already exhausted this epoch, the view reports a positive amount while any withdrawal through the second address, even 1 wei, reverts OverWeeklyCap(0). Enforcement is correct (this is the documented shared-cap mechanism working); only the radar-facing number is wrong, and only until the second address has been queued once (then the shared charge opens its window too). No funds at risk. Fix: document that the view ignores shared-balance charges, or have it also take the minimum over the windows of tracked assets whose balanceOf equals this asset's (a heuristic), or expose a per-id 'canExecute' simulation.","line":311,"path":"src/TreasuryVault.sol","reproduction":"DoubleEntryToken main with LegacyEntry legacy forwarding balanceOf/transfer to main (same balance). createTreasury(main, manager, 1 days, 2500, []). Mint 1000e18 main to vault. queue(main, dest, 250e18); warp +1 day; execute -> balance 750e18, windows[main] spent 250e18 of 250e18. Now availableThisWeek(legacy) returns 187.5e18 (legacy is untracked, so 25% of 750e18). queue(legacy, dest, 187.5e18) and also queue(legacy, dest, 1); warp +1 day; both execute() calls revert OverWeeklyCap(0) because main's balance fell and its window has 0 available. Verified with test/scratch/ViewGap.t.sol.","severity":"info","snippet":"        if (win.epoch != uint64(block.timestamp / EPOCH)) return _cap(_balance(asset));","title":"availableThisWeek() overstates what execute() will allow for an untracked second entry point to a shared balance"},{"citation":"resolved","description":"Area: Math Precision (zero-rounding) x Boundary. mulDiv rounds down, so any balance below 10_000 / rateBps wei (3 wei at the 25% ceiling, 9_999 wei at the 1 bps floor decreaseRate allows) gives cap 0 and every withdrawal of that asset, including 1 wei, reverts OverWeeklyCap(0). Combined with the geometric decay of a percentage cap, a treasury can never be fully emptied; the amount involved is dust (at most 9_999 wei of an 18-decimal token, under 1 cent of USDC) and is not farmable, so this is a documentation note rather than a defect to fix.","line":357,"path":"src/TreasuryVault.sol","reproduction":"createTreasury(T, manager, 1 days, 2500, []). Mint 3 wei T to the vault. queue(T, dest, 1); warp +1 day. availableThisWeek(T) == 0 and execute reverts OverWeeklyCap(0). decreaseRate(1); mint until the balance is 9_999 wei; next epoch availableThisWeek(T) is still 0. Verified with test/scratch/MathEdges.t.sol::test_DustBelowCapFloorCanNeverLeave.","severity":"info","snippet":"        return Math.mulDiv(balance, rateBps, BPS);","title":"Cap rounds down to zero below BPS/rateBps wei, so that remainder of each asset can never leave the treasury"}],"hash":"2084ee499c8ed683c88261559e038df600aa11b537a75c9421b5c110b2471168","nodeId":"d1dd1117-7791-4aee-bb8e-1d021397811a","outcome":"completed","summary":"The review is complete. The findings file is in place, and no source or configuration file was changed.\n\n**Outcome.** The build succeeds and all 117 existing tests pass. In the assigned area (Math Precision, Boundary, Numerical Gap) I found no critical, high or medium defect. The cap and vesting arithmetic use `mulDiv`, are overflow-free up to the uint256 maximum, round in the conservative direction, and never underflow. The epoch-straddle bound of 43.75% and the exact-second delay, expiry and handover boundaries behave as documented. I wrote five reproducible findings, all low or info, each verified with a scratch Foundry test under test/scratch/.\n\n**Findings written to .imd-findings.json:**\n\n1. **Low, TreasuryVault.execute line 193.** The withdrawn asset is charged its nominal amount, while every other tracked asset is charged its measured balance drop. A token that debits the sender more than the amount lets 27.5% leave against a 25% cap, and `spent` under-records the outflow.\n2. **Low, VestingVault.release line 161.** The final release must transfer the entire balance. A token that debits the sender more than the amount makes that transfer revert forever, with no partial release or recovery path.\n3. **Low, VestingVault._sync line 221.** Anyone can start an open vault's arrival clock with 1 wei of a pre-existing token, so a later real tranche lands partly unlocked. This contradicts the stated \"vests from its arrival, never on it\" for tokens that exist before funding.\n4. **Info, TreasuryVault.availableThisWeek line 311.** The view ignores shared-balance charges, so it reports a positive amount for an untracked second entry point while execution reverts. Enforcement itself is correct.\n5. **Info, TreasuryVault._cap line 357.** Balances below BPS/rateBps wei round to a zero cap and can never leave. Dust only, not farmable.\n\n**Coverage.** All 27 listed entry points have a row, plus two invariant rows. Six are marked `finding` and the rest `holds`, none `unreached`. The record notes the untested token class, since the suite's fee-on-transfer mocks only take fees out of the delivered amount, never from the sender on top of it.\n\n**Outside my area, for the judge.** The manifest deploys `VaultFactoryNoFee`, a zero-argument subclass, rather than `src/VaultFactory.sol` with two zero arguments as the brief words it. The suite asserts the runtime bytecode is identical, and I did not dispute that, but the deviation from \"deploy exactly\" is a policy question rather than a math one.","treeHash":null,"usage":{"cachedInputTokens":2335684,"inputTokens":706,"model":"claude-fable-5-1","outputTokens":44070,"runtime":"claude","turns":40,"wallClockMs":862626}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fea57d3e9d0ca7bf","findings":[{"citation":"resolved","description":"An open vault (token zero) records a token's arrival once, the first time sync(asset) or release(asset) runs while the balance is nonzero, and that record is the clock every later tranche of that token vests on (unlockStart = max(start + cliff, arrivedAt)). The threshold is any nonzero balance, and sync is public, so a third party holding one wei of a token that already exists can transfer it to the vault and call sync before the project's real tranche lands. The real tranche then vests by the fraction of the schedule elapsed since the dust, not since it arrived: with start = creation, cliff 0, duration 28 days and the tranche landing on day 21, 21/28 of it is releasable in the block it lands, versus 0 without the dust. This defeats the arrival-clock rule added for finding 5dbb3b32 ('a launch that slips past start + cliff still vests from its arrival, never on it') for every token that exists before it reaches the vault, which is the case for any Robinhood project using createLaunchVesting with an existing token, and for a second token sent to a launch vault. The ADAPTATION note that 'nobody can set the clock earlier with dust' only holds for a token created in the launch transaction itself. The beneficiary (or anyone) can trigger it, so a vault shown by the radar as locked can release most of a tranche at once; no test in the suite sends dust before a tranche. Fix needs a scope decision since the README's calendar top-up rule enables it: either account each arrival of an asset to an open vault as its own tranche with its own clock (released/arrival per tranche), or at least let the clock restart for the untracked delta when a later arrival dwarfs the recorded balance; both keep 'vests every token it receives' but change the stated top-up rule.","line":221,"path":"src/VestingVault.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 {VaultFactory} from \"src/VaultFactory.sol\";\nimport {VestingVault} from \"src/VestingVault.sol\";\n\ncontract DustToken is ERC20 {\n    constructor() ERC20(\"Dust\", \"DST\") {}\n    function mint(address to, uint256 amount) external { _mint(to, amount); }\n}\n\n/// An open vesting vault promises that a tranche landing after `start + cliff` vests from its arrival. A third\n/// party who holds one wei of the token can record the token's arrival earlier with dust and `sync`, so the real\n/// tranche is unlocked by the schedule fraction elapsed since the dust, not since it arrived.\ncontract ProofDustClockTest is Test {\n    DustToken token;\n    VaultFactory factory;\n    address team = makeAddr(\"team\");\n    address project = makeAddr(\"project\");\n    address attacker = makeAddr(\"attacker\");\n\n    function setUp() public {\n        vm.warp(1_760_000_000);\n        vm.chainId(4663);\n        token = new DustToken();\n        factory = new VaultFactory(IERC20(address(0)), address(0));\n        token.mint(project, 1_000_000e18);\n        token.mint(attacker, 1);\n    }\n\n    function test_RealTrancheVestsFromItsOwnArrivalDespiteEarlierDust() public {\n        uint64 start = uint64(block.timestamp);\n        vm.prank(project);\n        VestingVault v = factory.createLaunchVesting(team, start, 0, 28 days);\n\n        // Day 0: a stranger sends one wei and records the token's arrival.\n        vm.prank(attacker);\n        token.transfer(address(v), 1);\n        v.sync(address(token));\n\n        // Day 21: the project's real tranche lands after start + cliff.\n        vm.warp(block.timestamp + 21 days);\n        vm.prank(project);\n        token.transfer(address(v), 1_000_000e18);\n        v.sync(address(token));\n\n        // Expected: at most the dust is releasable; the tranche vests from day 21 and nothing of it is unlocked yet.\n        // Actual on this code: 21/28 of the tranche (750,000 tokens) is releasable in the same block.\n        assertLe(v.releasable(address(token)), 1, \"real tranche unlocked on arrival because dust started its clock\");\n    }\n}","reproduction":"chain 4663, factory = new VaultFactory(IERC20(0), address(0)); T0 = block.timestamp. project: v = factory.createLaunchVesting(team, T0, 0, 28 days). attacker: token.transfer(v, 1); v.sync(token) -> arrivedAt[token] = T0. warp T0 + 21 days. project: token.transfer(v, 1_000_000e18); v.sync(token) (no-op). Expected: releasable(token) <= 1 (the tranche vests from day 21, as it does when no dust was sent: 0 on arrival, 250_000e18 at day 28). Actual: releasable(token) = (1_000_000e18 + 1) * 21 / 28 = 750_000e18 + dust, and release(token) sends 750,000 tokens to team in the same block. Test: test/scratch/Proof_DustClock.t.sol fails with '750000000000000000000000 > 1'.","severity":"medium","snippet":"        if (IERC20(asset).balanceOf(address(this)) == 0) return false;","title":"Open vesting vault: anyone can start a token's unlock clock early with dust, so a tranche landing after start+cliff is unlocked on arrival"},{"citation":"resolved","description":"The brief says 'Deploy exactly src/VaultFactory.sol with constructor arguments imd_=0x0, feeRecipient_=0x0' and 'do not change any source file'. The tree adds src/VaultFactoryNoFee.sol, a zero-argument subclass whose constructor passes the two zero words, because the manifest schema refuses the zero address as a constructor word. I verified with forge inspect that the deployed (runtime) bytecode of VaultFactoryNoFee is byte-for-byte that of VaultFactory(0, 0) (37970 hex chars, identical), and test_NoFeeFactoryIsTheAuditedFactoryWithZeroArguments asserts the same; the runtime contains no DELEGATECALL, CALLCODE, SELFDESTRUCT, TSTORE or MCOPY (PUSH0 only, so it needs Shanghai, which Robinhood Chain has). What differs: the creation code (38134 vs 38510 hex chars, no constructor argument decoding) and the contract name and ABI constructor entry an explorer verification will show (VaultFactoryNoFee, not VaultFactory). Not a defect in behaviour; recorded so the requester consciously accepts the deviation from 'exactly src/VaultFactory.sol' and the verifier verifies the right contract name, or the schema is allowed to take the zero word for these two arguments as the brief's stated values.","line":5,"path":"launch.json","reproduction":"forge inspect VaultFactory deployedBytecode == forge inspect VaultFactoryNoFee deployedBytecode (identical); forge inspect VaultFactory bytecode != forge inspect VaultFactoryNoFee bytecode (38510 vs 38134 hex chars). launch.json contracts[0].contract = 'VaultFactoryNoFee', constructorArgs = []; the brief names src/VaultFactory.sol with two zero arguments. Expected per brief: VaultFactory with [0x0, 0x0]; actual: a subclass with [].","severity":"info","snippet":"      \"contract\": \"VaultFactoryNoFee\",","title":"Manifest deploys VaultFactoryNoFee, not src/VaultFactory.sol as the brief states (runtime identical, creation code and verified name differ)"}],"hash":"256727dc9766b001bf508772fa87f42fa8d02f02a38229b7f3382341db56fe38","nodeId":"d9963f23-37f3-4cf7-87ea-3893aa21a825","outcome":"completed","summary":"Review complete. The findings file is written and no source files were changed.\n\n**Outcome**\n\nThe build compiles and all 117 project tests pass. Across the Execution Trace, Periphery and First Principles passes I substantiated one medium defect and one informational deviation. All 27 listed entry points have a coverage row, none unreached.\n\n**Finding 1, medium: dust starts an open vault's unlock clock early.** An open vesting vault records a token's arrival the first time `sync` or `release` runs with any nonzero balance, and that timestamp becomes the clock for every later tranche of that token. Anyone holding one wei can transfer it and call `sync` before the project's real tranche lands. In the reproduction, a tranche landing on day 21 of a 28-day schedule has 75% releasable in the same block, versus 0% without the dust. This bypasses the arrival-clock rule the earlier audit round added for any token that exists before it reaches the vault, which covers every Robinhood project using an open vault with an existing token. The proof test under `test/scratch/` fails on the current code. The fix needs a scope decision because the documented calendar top-up rule is what enables it, and I say so in the finding.\n\n**Finding 2, info: the manifest deploys `VaultFactoryNoFee`, not `VaultFactory`.** The brief says to deploy exactly `src/VaultFactory.sol` with two zero arguments. I verified the subclass's runtime bytecode is byte-identical to `VaultFactory(0,0)` and contains no Cancun-only or forbidden opcodes. Only the creation code and the verified contract name differ. Recorded so the requester consciously accepts it.\n\n**What held.** The treasury's shared weekly cap behaves correctly in both orderings for a double-entry token, the window is charged before the transfer so reverts leave state intact, bounded-gas balance reads fail closed, and every manager and beneficiary rule only tightens. The factory with `imd` zero charges nothing and `payFor` reverts for every input. The manifest has only the schema's keys, one 17-character name, an empty argument list matching the zero-parameter constructor, and notes under the length limit.\n\n**Not reached.** No live check of Robinhood Chain was run, so the chain's opcode support is inferred from the bytecode needing only Shanghai.","treeHash":null,"usage":{"cachedInputTokens":1272194,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":30601,"runtime":"claude","turns":26,"wallClockMs":679621}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"cbc83f8151b8340d","findings":[],"hash":"4b27204dba10ffb0a11167095d7ef37172684fa62d46f014af61a3ff1f82b994","nodeId":"396f006d-0900-4690-b849-d26adc121e52","outcome":"failed","summary":"the task produced no changes; the agent's last message was: The test suite passes on the audited source as it now stands: `forge build` succeeds and `forge test` runs 143 tests with 0 failures under the verifier's fuzz seed. The audited code still has defects that can't be tested around, so I've reported them in `.imd-findings.json` instead of writing tests that treat them as correct.\n\n**Why this round needed work.** Another contributor put `src/TreasuryVault.sol` and `src/VestingVault.sol` back to the exact launch 2244 code, which the brief requires unchanged. That also brought back defects an earlier edit had fixed. My test file, `test/RobinhoodFactory.t.sol`, still passes against the restored code and doesn't assert any of these defects as correct, so I left it alone. No source or test file changed this round.\n\n**Findings reported:**\n\n| Severity | Where | Defect | Proof |\n|---|---|---|---|\n| medium | `TreasuryVault.sol:347` (also `:305`) | The weekly cap is calculated as `balance * rateBps` without overflow protection. A balance above 2^256 / rateBps makes every withdrawal of that token revert, so it is locked forever. | Fails with an overflow panic |\n| medium | `TreasuryVault.sol:336` | When reading the balances of watched tokens, the vault gives each read all its remaining gas. One watched token that burns gas makes every other token's withdrawal run out of gas at a 10M gas limit, which breaks the source comment's own promise. | Fails: the withdrawal is blocked |\n| medium | `TreasuryVault.sol:187` | A watched token that falsely reports its balance falling can make every token withdrawal revert `OverWeeklyCap`. Watched tokens can never be removed. | Reviewer's earlier reproduction |\n| medium | `VestingVault.sol:220` | In an open vesting vault, anyone can start an existing token's vesting clock by sending 1 wei of it. A tranche of 1,000,000e18 arriving 21 days later had 750,000e18 releasable straight away. | Reproduced here |\n| low | `VestingVault.sol:126` | Vesting is calculated as `total * elapsed` without overflow protection. A near-maximum allocation can't be released during the vesting period, then unlocks all at once at the end. | Fails with an overflow panic |\n| low | both vaults | Tokens that take more from the sender than the amount sent let more than the weekly cap leave the treasury, and can block a vesting vault's final release. | Reviewer's earlier reproduction |\n\nEach of the three proofs fails on the current code for the reason given. Each passes on the version with the fixes, commit `98fd654`, which I checked out temporarily and then restored. The source tree is unchanged.\n\nFixing any of these means changing a vault source. That changes the factory's deployed code, so it would no longer match the Ethereum deployment. A fix therefore needs a new audited release on both chains, and that decision is the requester's.\n\nOne side note, not reported because no real chain can hit it: while the block timestamp is under 7 days, the treasury's first weekly window never opens and every withdrawal reverts. My proofs set a realistic timestamp to avoid this.\n\n`.imd-findings.json` is in the repository root. It is listed in `.git/info/exclude`, so it doesn't appear in `git status`; the harness collects it separately.","treeHash":null,"usage":{"cachedInputTokens":1010854,"inputTokens":34,"model":"claude-opus-5-5","outputTokens":15354,"runtime":"claude","turns":18,"wallClockMs":315241}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"0ef0cddb99617930","findings":[{"citation":"resolved","description":"Settlement of last round's medium 9c7bf39d (merged with write_foundry_tests 44c6ff9b and 8d57da874, same root cause: execute() trusts what every asset the manager ever queued answers to balanceOf, and nothing removes an entry from _trackedAssets). The behaviour still reproduces exactly on the restored tree: the pinned proof Proof_9c7bf39dbae6 fails with OverWeeklyCap(2.5e39) and Proof_8d57da874a94 (gas-burning tracked token at a 10M gas limit) fails with an out-of-gas revert, since the restored _tryBalance (lines 334-339) forwards all gas and copies the whole payload. The author disputes a change for this launch and I accept that: the requester's brief says to deploy the launch 2244 code unchanged and to change no source file, the two fix options I offered both edit TreasuryVault.sol, whose init code the factory embeds, so either fails the held proof Proof_8094ebd9e169, and src/TreasuryVault.sol is now byte for byte the file at ccfe47a that produced the Ethereum factory (keccak 0x4e60104b...f98a, re-read over RPC today). It is therefore not a defect of this launch's deliverable but a property of the audited code already live on Ethereum; the README now states the operating rule (queue only trusted tokens; leave an unsolicited airdrop where it is; a hostile tracked token can block ERC-20 withdrawals, never ETH, never take anything), though the NatSpec at lines 21-25 and 332-333 still promises more than the code gives. Downgraded from medium to low for that reason, not because the impact changed: the requester accepts it knowingly, or commissions a new audited release on both chains (the previous round's edits are at commit 98fd654). Precondition stays: the manager must once have queued the hostile token.","line":206,"path":"src/TreasuryVault.sol","reproduction":"Chain id 4663. factory = new VaultFactoryNoFee(); treasury = factory.createTreasury(PRJ, manager, 1 days, 2500, []); mint 1000e18 PRJ to it. AIR: balanceOf(treasury) returns 100e18 until armed, then 1e40 while PRJ.balanceOf(treasury) >= floor and 0 otherwise. 1) manager queue(AIR, payroll, 1e18); warp +1 day; execute -> isTracked[AIR] true for ever. 2) warp +3 weeks; attacker AIR.arm(1000e18). 3) manager queue(PRJ, payroll, 10e18); warp +1 day; availableThisWeek(PRJ) == 250e18. Expected: execute sends 10e18 PRJ. Actual: before[AIR]=1e40, after the transfer AIR reports 0, fell=1e40, _charge(windows[AIR], 1e40, 1e40) reverts OverWeeklyCap(2500000000000000000000000000000000000000). Run: copy .imd/reads/proofs/Proof_9c7bf39dbae6.t.sol to test/scratch/ and forge test --match-path test/scratch/Proof_9c7bf39dbae6.t.sol: FAIL with that revert on this tree. Gas variant: copy Proof_8d57da874a94.t.sol the same way: FAIL EvmError: Revert (out of gas) at a 10M limit.","severity":"low","snippet":"                _charge(windows[other], before[i], fell);","title":"Known behaviour of the audited code, carried unchanged by the brief: a hostile tracked ERC-20 (mirroring balanceOf, or one that burns gas) can make every ERC-20 withdrawal of a treasury revert, and th"},{"citation":"resolved","description":"Settlement of last round's medium 4e5bed0c (merged with write_foundry_tests 4957b438, audit_flow 69712906, audit_economics c75e72d1, audit_math eab2d0a7). Still reproduces exactly on the restored tree: the pinned proof fails with '750000000000000000000000 > 1'. The author disputes a change for this launch and I accept that for the same reason as finding 1: a per-tranche clock edits VestingVault.sol, whose init code the factory embeds, so it fails the held mainnet-runtime proof, and the brief forbids any source change. The author took the documentation option I offered: README now states that an open vault's clock is only trustworthy for a token minted in the funding transaction (the IMD launch case, where nothing can arrive before the tranche), that an existing token belongs in a bound vault (createVesting), and that the radar refuses its seal to an open vault holding a token that existed before the vault; that last claim is radar-side behaviour I cannot verify from this tree and is recorded as such. Only the beneficiary's own lock is weakened; tokens never go to a third party. Downgraded from medium to low because it is a property of the audited code the requester deploys unchanged, not a defect of this launch's deliverable; the choice to fix it is a new audited release on both chains.","line":220,"path":"src/VestingVault.sol","reproduction":"Chain id 4663; factory = new VaultFactory(IERC20(address(0)), address(0)); T0 = block.timestamp. project: v = factory.createLaunchVesting(team, T0, 0, 28 days). attacker holding 1 wei of an existing token: token.transfer(v, 1); v.sync(token) -> arrivedAt[token] = T0. warp T0 + 21 days. project: token.transfer(v, 1_000_000e18). Expected: releasable(token) <= 1 (a tranche vests from its own arrival: 0 now, 250_000e18 at day 28). Actual: releasable(token) = (1_000_000e18 + 1) * 21 / 28 = 750_000e18 + dust, and release(token) sends it to team in the same block. Run: copy .imd/reads/proofs/Proof_4e5bed0c1d59.t.sol to test/scratch/ and forge test --match-path test/scratch/Proof_4e5bed0c1d59.t.sol: FAIL '750000000000000000000000 > 1' on this tree.","severity":"low","snippet":"        arrivedAt[asset] = uint64(block.timestamp);","title":"Known behaviour of the audited code, carried unchanged by the brief: anyone can start an open vesting vault's arrival clock for a pre-existing token with 1 wei, so a real tranche landing later is unlo"},{"citation":"resolved","description":"Settlement of last round's low d6e1656f (merged with audit_math bf1563ce and the treasury half of write_foundry_tests fc9381c7). Re-reproduced on the restored tree with a scratch test today: after one execute of 250e18 of a token that burns value/10 from the sender on top of the transfer, the vault holds 725e18 (27.5% left) while windows[SB].cap and spent are both 250e18. The author's answer stands: the minimal fix (re-read the balance after the transfer and charge the drop, as lines 203-206 already do for every other tracked asset) edits TreasuryVault.sol and the brief forbids it; README now documents the limitation. The destination still receives at most the cap, so nothing goes to a wrong party. Stays low, advisory, for the requester to accept knowingly.","line":187,"path":"src/TreasuryVault.sol","reproduction":"Token SB: OpenZeppelin ERC20 whose _update first burns value/10 from the sender, then delivers value. Chain 4663, warp to a realistic timestamp (epoch 0 of a fresh window must not coincide with block.timestamp/7 days == 0). createTreasury(SB, manager, 1 days, 2500, []); mint 1000e18 SB to the vault; manager queue(SB, dest, 250e18); warp +1 day; execute(id). Expected per the 25% rule: at most 250e18 leaves and spent records the outflow. Actual: SB.balanceOf(vault) == 725e18, windows(SB) cap == 250e18, spent == 250e18. Confirmed by test/scratch/Recheck.t.sol::test_SenderBurnTokenExceedsWeeklyCapOnWithdrawnAsset (passes, logging balance 725e18, cap 250e18, spent 250e18).","severity":"low","snippet":"        _charge(windows[w.asset], _balance(w.asset), w.amount);","title":"Known behaviour of the audited code: the weekly cap charges the withdrawn asset its nominal amount, not its measured balance drop, so a token that debits the sender beyond the amount lets more than ra"},{"citation":"resolved","description":"Settlement of last round's low 7e1f83d4 (merged with audit_math 0397a82a and the vesting half of write_foundry_tests fc9381c7). Re-reproduced on the restored tree today: once the schedule has elapsed, releasable(SB) equals the vault's whole balance, release tries to move all of it, and the token reverts because the sender would need balance plus fee; no partial release exists and recover() refuses the vested token, so the tokens stay for ever. The author's answer stands: release(asset, amount) edits VestingVault.sol, which the brief forbids; README now says such a token must not be vested in these vaults. Standard and classic fee-on-transfer tokens (fee out of the delivered amount) are unaffected. Stays low, advisory.","line":159,"path":"src/VestingVault.sol","reproduction":"Token SB as in finding 3. holder has 2000e18 SB, approves the factory and calls createVesting(IERC20(SB), team, 1000e18, now, 0, 28 days); the vault holds 1000e18. warp +28 days: releasable(SB) == 1000e18. Expected: release(SB) sends the vested tokens to team. Actual: release(SB) reverts (ERC20InsufficientBalance(vault, 1000e18, 1100e18)); after another 365 days it still reverts and the vault still holds 1000e18. Confirmed by test/scratch/Recheck.t.sol::test_SenderBurnTokenFinalReleaseReverts (passes with vm.expectRevert on both calls).","severity":"low","snippet":"        IERC20(asset).safeTransfer(to, amount);","title":"Known behaviour of the audited code: release() always transfers the whole releasable amount, so a token that debits the sender beyond the amount can never complete the final release of a bound vault a"},{"citation":"resolved","description":"Settlement of the tester's write_foundry_tests findings c90eca87 (treasury cap, this line and line 305 in availableThisWeek) and 0495c96a (VestingVault line 126, `return (total * elapsed) / duration;`), one root cause: the audited code computes both in checked arithmetic, so the previous round's Math.mulDiv edits removed it and the restore brought it back. Reproduced: the pinned proof Proof_c90eca876ac7 fails with panic 0x11 on this tree. Both are real but require a balance above about 4.6e73 base units at the 25% ceiling or an allocation above about 4.8e70 over 28 days, which only a uint256-max-supply token reaches; the asset cannot leave the treasury at all once it is there, since nothing else lowers its balance. Not fixed, by the brief; README now says such a token must not be put in these vaults. Low, advisory: the requester accepts it or commissions a new release on both chains.","line":347,"path":"src/TreasuryVault.sol","reproduction":"Treasury: chain 4663, createTreasury(T, manager, 1 days, 2500, []); mint type(uint256).max/2500 + 1 of T to the vault; manager queue(T, dest, 1); warp +1 day; execute. Expected: 1 wei transferred. Actual: panic 0x11 (arithmetic overflow) in _charge; availableThisWeek(T) panics too. Run: copy .imd/reads/proofs/Proof_c90eca876ac7.t.sol to test/scratch/ and forge test --match-path test/scratch/Proof_c90eca876ac7.t.sol: FAIL panic 0x11 on this tree. Vesting: createVesting with amount type(uint256).max/2, duration 28 days, start now; warp +14 days; release. Expected: total/2 released. Actual: panic 0x11 until day 28, then everything at once (test/RobinhoodBoundaries.t.sol::test_VestingLargestSafeAllocationReleasesLinearly pins the largest safe allocation).","severity":"low","snippet":"            win.cap = (balance * rateBps) / BPS;","title":"Known behaviour of the audited code: checked multiplication in the cap and the vesting math reverts for a balance above 2^256/rateBps or an allocation above 2^256/duration, locking that asset in a tre"},{"citation":"resolved","description":"Settlement of last round's info e6655eea (merged with audit_math 3aa7ee90). Re-reproduced on the restored tree today. Enforcement is correct (the shared cap works, lines 200-208); only the radar-facing view ignores shared-balance charges, and only until the second address has been queued once. The author's answer stands: a source change is forbidden; README documents it. No funds at risk. Info.","line":305,"path":"src/TreasuryVault.sol","reproduction":"MainToken main; LegacyEntry legacy forwards balanceOf and transfer to main (one balance). Chain 4663, realistic timestamp. createTreasury(main, manager, 1 days, 2500, []); mint 1000e18 main. queue(main, dest, 250e18); warp +1 day; execute -> balance 750e18, windows[main] spent 250e18 of 250e18. availableThisWeek(legacy) returns 187.5e18 (untracked, 25% of 750e18). queue(legacy, dest, 1); warp +1 day; execute reverts OverWeeklyCap(0). Confirmed by test/scratch/Recheck.t.sol::test_ViewOverstatesForUntrackedSecondEntry (passes with vm.expectRevert(OverWeeklyCap(0)) and logs availableThisWeek(legacy) 187.5e18).","severity":"info","snippet":"        if (win.epoch != uint64(block.timestamp / EPOCH)) return (_balance(asset) * rateBps) / BPS;","title":"Known behaviour of the audited code: availableThisWeek() overstates what execute() will allow for an untracked second entry point to a balance whose main address's window is exhausted"},{"citation":"resolved","description":"Settlement of last round's info ecc21142 (merged with audit_permissions 04f46168 and audit_flow 24b49607). The author confirms it is intentional and launch.json is unchanged. Re-verified today on the restored tree: forge inspect deployedBytecode of VaultFactoryNoFee and VaultFactory are identical (37,520 hex characters, 18,760 bytes), address(new VaultFactoryNoFee()).code equals address(new VaultFactory(IERC20(0), address(0))).code on chain 4663 with imd() zero (test/scratch/Recheck.t.sol::test_NoFeeRuntimeEqualsBase and test/Audit.t.sol::test_NoFeeFactoryIsTheAuditedFactoryWithZeroArguments), and that runtime is byte for byte the launch 2244 factory's apart from the two immutables (cast code 0xdbaff609...2ff5b over https://ethereum-rpc.publicnode.com today: 18,760 bytes, keccak 0x4e60104bbd26773631103e40fbbf5713e12e2d485564c8ee7222fbe57e30f98a; test/Audit.t.sol::test_TreeCompilesToTheAuditedMainnetFactory pins it). launch.json has exactly kind, contracts and notes, a 17-character identifier, an empty argument list matching the zero-parameter constructor and 3,011-character notes. The residual deviation (verified name and creation code are VaultFactoryNoFee's, and an 18-line source file exists beside the audited one, against the brief's 'add no source file') stands as a recorded, accepted workaround for the schema's zero-word rule, which no worker can change. Not a behaviour defect.","line":5,"path":"launch.json","reproduction":"Read launch.json: contracts[0].contract is 'VaultFactoryNoFee', constructorArgs []. The brief names src/VaultFactory.sol with [0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000]. forge inspect VaultFactory deployedBytecode | tr -d '\\n' | wc -c and the same for VaultFactoryNoFee both give 37522 (with the 0x prefix); forge inspect VaultFactoryNoFee abi shows a constructor with no inputs. Expected per the brief's wording: VaultFactory with two zero words; actual: a subclass with []. Runtime equal in both cases.","severity":"info","snippet":"      \"contract\": \"VaultFactoryNoFee\",","title":"Manifest deploys the subclass VaultFactoryNoFee with no arguments instead of src/VaultFactory.sol with two zero words as the brief states; runtime and immutables identical, creation code, ABI construc"},{"citation":"resolved","description":"New this round, from reading the manifest against the fixed tree. launch.json was written in the manifest round against the edited sources (18,984-byte runtime) and was not updated after the restore for finding 8094ebd9. Notes are explanatory text with no deployment authority, the schema keys and the contract entry are correct, and the deployer verifies the actual creation bytes, so nothing deploys wrong. But the notes are what a requester reads to accept the launch, and they now assert a byte count that contradicts the 'exact code of launch 2244' claim the same notes make (the mainnet factory is 18,760 bytes). The manifest step should refresh the two numbers when it next writes launch.json; no source file is involved. Info.","line":9,"path":"launch.json","reproduction":"grep -o 'runtime 18,984 bytes' launch.json matches. forge inspect VaultFactoryNoFee deployedBytecode | tr -d '\\n' | wc -c gives 37522 = 0x + 37,520 hex characters = 18,760 bytes (test/Audit.t.sol::test_TreeCompilesToTheAuditedMainnetFactory asserts 18,760 and passes). FOUNDRY_FUZZ_SEED=0xb224989c52744aedbf7c0bd00fdb05a72a2e7c35ff4eb1dbdd54ea3ed4ba2ae5 forge test --no-match-path 'test/scratch/*' reports 158 tests passed, 0 failed. Expected: notes match the tree; actual: 18,984 and 118.","severity":"info","snippet":"runtime 18,984 bytes). Checked before this manifest: forge build and the full suite (118 tests,","title":"Manifest notes describe the previous tree: they state a runtime of 18,984 bytes and a suite of 118 tests, but the restored tree compiles to 18,760 bytes and the suite now has 158 tests"}],"hash":"73676121684bc35d2feaa863ff89a3fc78166434aa9c083ba1c0c966ad0a24a7","nodeId":"92e381a7-9926-48f3-893e-ab1d6ca46f9c","outcome":"completed","summary":"The review is settled and written to `.imd-findings.json` (8 findings, 31 coverage rows covering all 27 entry points plus four invariants). No source file was touched; only the findings file and my scratch tests exist outside the tree.\n\n**The blocking finding is fixed.** The three audited sources are byte for byte the files at commit ccfe47a. The pinned proof passes, and I re-read the mainnet factory over a public RPC today: 18,760 bytes, keccak `0x4e60104b…f98a`, with the launch 2244 immutables. The no-fee subclass compiles to that same runtime with zero immutables. The full suite passes with the verifier's fuzz seed, 158 tests, 0 failures.\n\n**The disputed findings reproduce but are settled as advisory.** I ran every pinned proof and rewrote scratch tests for the three unproofed ones. All still fail on the restored code, exactly as before. I accept the author's position on each: the brief orders the launch 2244 code deployed unchanged, any fix edits a vault whose init code the factory embeds, and so it cannot coexist with the held mainnet-runtime proof. They are properties of the audited code already live on Ethereum, not defects of this launch's deliverable. I downgraded the two mediums to low, kept the two lows and the info, and merged the tester's duplicates into them:\n\n- Hostile tracked token freezes ERC-20 withdrawals (mirror balanceOf, or gas burn): one root cause, tracked set never shrinks.\n- Open vault dust clock lets a later tranche unlock on arrival.\n- Sender-debit tokens exceed the weekly cap and strand a bound vault's final release.\n- Checked multiplication overflows above roughly 2^256/rateBps and 2^256/duration (the tester's two overflow findings, one root cause).\n- `availableThisWeek` overstates for an untracked second entry point.\n- Manifest deploys `VaultFactoryNoFee` with no arguments; runtime identical, name and creation code differ, accepted knowingly.\n\n**One new item, info only.** The manifest notes still describe the previous tree: they say the runtime is 18,984 bytes and the suite has 118 tests. The restored tree is 18,760 bytes and 158 tests. Notes carry no deployment authority, so nothing deploys wrong, but the manifest step should refresh them.\n\nNothing outstanding blocks the launch. If the requester wants the vault behaviours fixed, that is a new audited release on both chains, with the earlier edits preserved at commit 98fd654.","treeHash":null,"usage":{"cachedInputTokens":1139212,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":25058,"runtime":"claude","turns":22,"wallClockMs":357366}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"2a5d68f89de314cb","findings":[{"citation":"resolved","description":"The brief for this launch is explicit: deploy the audited factory on Robinhood 'unchanged', 'the exact code audited and deployed on Ethereum mainnet in launch 2244 (VaultFactory at 0xdbaff609cd0500527a2a102f5cbbbbc70fd2ff5b)', and 'Do not change any source file: the audit is done; only confirm it builds, its tests pass, and deploy it.' The swarm brief adds the reason: both chains must run the same audited code. The factory embeds the init code of VestingVault and TreasuryVault (it deploys them with `new`), so its runtime bytecode fingerprints all three sources. The tree as it arrived at the previous commit (ccfe47a) compiles, with the Ethereum constructor arguments, to exactly the runtime at the mainnet address (18,760 bytes, keccak256 0x4e60104b...f98a, read with `cast code` today). The last commit (98fd654) then edited src/TreasuryVault.sol (new constant BALANCE_READ_GAS at this line, a rewritten assembly `_tryBalance` with a 100,000 gas stipend, new `_cap` using Math.mulDiv, used by `_charge` and `availableThisWeek`) and src/VestingVault.sol (line 128: `return Math.mulDiv(total, elapsed, duration);`). HEAD compiles to 18,984 bytes with a different hash. Deploying this tree on Robinhood therefore breaks the stated guarantee: every VestingVault and TreasuryVault created on Robinhood runs code that was never deployed or audited on Ethereum, the two chains' vaults have different bytecode, and the explorer source for Robinhood will not match the Ethereum source. The ADAPTATION.md justification ('findings that reproduce must be fixed') conflicts with the requester's instruction for this launch; the three changes address real but remote conditions (balances above 2^256/rateBps, a tracked token that burns gas or returns a huge payload, allocations above 2^256/duration) that the Ethereum factory lives with today. Access-control and trust-gap lens: the deliverable's trust claim ('same audited code') is what the radar and projects rely on, and it is currently false. Minimal fix that preserves the agreed design: restore src/TreasuryVault.sol and src/VestingVault.sol to their content at commit ccfe47a (git checkout ccfe47a -- src/TreasuryVault.sol src/VestingVault.sol) and drop the tests in test/Audit.t.sol that only pass on the changed source; keep src/VaultFactoryNoFee.sol, whose runtime is the same as VaultFactory(0,0) once the base is restored. If the requester instead wants the three fixes, that is a scope decision for them, and Ethereum would then be the chain running different code; both outcomes are acceptable only if chosen explicitly.","line":43,"path":"src/TreasuryVault.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {VaultFactory} from \"src/VaultFactory.sol\";\n\n/// @notice The brief: \"Deploy the audited IMD Vault factory on Robinhood Chain, unchanged. This is the exact code\n///         audited and deployed on Ethereum mainnet in launch 2244 (VaultFactory at\n///         0xdbaff609cd0500527a2a102f5cbbbbc70fd2ff5b) ... Do not change any source file.\"\n///         The factory embeds the init code of VestingVault and TreasuryVault, so its runtime bytecode is a fingerprint\n///         of all three sources. Compiled from this tree with the Ethereum constructor arguments, the runtime must\n///         therefore equal the code at that mainnet address (18,760 bytes, keccak256\n///         0x4e60104bbd26773631103e40fbbf5713e12e2d485564c8ee7222fbe57e30f98a, read with `cast code` on 2026-10-19;\n///         the tree at the previous commit compiles to exactly those bytes). This tree's last commit changed\n///         src/TreasuryVault.sol and src/VestingVault.sol, so the Robinhood copy would deploy vaults whose code is not\n///         the audited mainnet code. Fails on this tree; passes once the audited sources are restored.\ncontract AuditedCodeTest is Test {\n    bytes32 constant MAINNET_FACTORY_CODEHASH = 0x4e60104bbd26773631103e40fbbf5713e12e2d485564c8ee7222fbe57e30f98a;\n    uint256 constant MAINNET_FACTORY_CODE_LENGTH = 18_760;\n\n    function test_TreeCompilesToTheAuditedMainnetFactory() public {\n        vm.chainId(1);\n        VaultFactory f = new VaultFactory(\n            IERC20(0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7), 0x75dF3d2E7a1d23021e5d7EF06312D13009E9BBb8\n        );\n        assertEq(address(f).code.length, MAINNET_FACTORY_CODE_LENGTH, \"runtime length differs from launch 2244\");\n        assertEq(address(f).codehash, MAINNET_FACTORY_CODEHASH, \"runtime differs from the audited mainnet factory\");\n    }\n}","reproduction":"1. On this tree: `forge test --match-path test/scratch/AuditedCode.t.sol`. It deploys `new VaultFactory(IERC20(0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7), 0x75dF3d2E7a1d23021e5d7EF06312D13009E9BBb8)` (the launch 2244 arguments) and asserts code length 18,760 and codehash 0x4e60104bbd26773631103e40fbbf5713e12e2d485564c8ee7222fbe57e30f98a. Actual: FAIL, 'runtime length differs from launch 2244: 18984 != 18760'. 2. Copy the tree, `git checkout 98fd654~1 -- src`, run the same test: PASS (identical bytes and hash to `cast code 0xdbaff609cd0500527a2a102f5cbbbbc70fd2ff5b --rpc-url <ethereum rpc>`; `cast call ... 'imd()(address)'` returns 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7 and `feeRecipient()` 0x75dF3d2E7a1d23021e5d7EF06312D13009E9BBb8, confirming it is the launch 2244 factory). 3. `git diff 98fd654~1 98fd654 -- src/` lists the source changes (TreasuryVault.sol +39/-?, VestingVault.sol +4/-1). Expected per the brief: the Robinhood copy compiles to the same runtime as the mainnet factory apart from its two immutables; actual: it does not.","severity":"medium","snippet":"    uint256 public constant BALANCE_READ_GAS = 100_000;","title":"Robinhood tree is not the audited mainnet code: the last commit changed TreasuryVault.sol and VestingVault.sol, so the factory and the vaults it deploys differ from launch 2244"},{"citation":"resolved","description":"The objective says 'Deploy exactly src/VaultFactory.sol with constructor arguments imd_=0x00..00, feeRecipient_=0x00..00'. launch.json instead lists `VaultFactoryNoFee` with no constructor arguments: an 18-line subclass (src/VaultFactoryNoFee.sol) whose constructor passes the two zero words itself, added because the manifest schema refuses the zero address as a constructor word for anything but a Currency field. The runtime bytecode is proven equal to `new VaultFactory(IERC20(address(0)), address(0))` by test_NoFeeFactoryIsTheAuditedFactoryWithZeroArguments (passes), the contract has no owner, no msg.sender or tx.origin role and calls nothing in its constructor, and on this chain every creation path skips the fee (`_chargeOnce` returns when imd is zero) while payFor reverts NoFeeOnThisChain, as the brief intends. What differs from the brief's wording: the verified contract name on the Robinhood explorer will be VaultFactoryNoFee, its creation bytecode and ABI (zero-argument constructor) are not the Ethereum factory's, and a new source file exists in the tree although the brief said none should be added. This is a deliberate, documented workaround for a manifest rule, not a permission or value defect; it is recorded so the requester accepts it knowingly. Alternative, if the manifest policy can take the zero word for these two arguments: deploy `VaultFactory` with the two zero arguments and delete the subclass.","line":5,"path":"launch.json","reproduction":"Read launch.json: contracts[0].contract is 'VaultFactoryNoFee' with constructorArgs []; the brief names src/VaultFactory.sol with two zero-address arguments. `forge test --match-test test_NoFeeFactoryIsTheAuditedFactoryWithZeroArguments` passes: address(new VaultFactoryNoFee()).code == address(new VaultFactory(IERC20(address(0)), address(0))).code on chain id 4663. `forge inspect VaultFactoryNoFee abi` shows a constructor with no inputs, unlike VaultFactory's (address,address). Expected by the brief: contract VaultFactory; actual: VaultFactoryNoFee with identical runtime.","severity":"info","snippet":"      \"contract\": \"VaultFactoryNoFee\",","title":"Manifest deploys the subclass VaultFactoryNoFee instead of src/VaultFactory.sol named in the brief (runtime identical, creation code and verified name differ)"}],"hash":"737e5bf968bf63379902bbc88662d540a4a181f8e0e844a169ece8b214466cdd","nodeId":"2b7ca9a5-2a98-48be-82a3-1ae740a84f4e","outcome":"completed","summary":"Review complete. The findings file is at `.imd-findings.json` with two findings and a full coverage record.\n\n**Main finding (medium): the tree is no longer the audited mainnet code.** The brief says to deploy the launch 2244 code unchanged and to touch no source file. The last commit edited `src/TreasuryVault.sol` and `src/VestingVault.sol` (a gas-stipended balance read, mulDiv caps and vesting math). I verified this against Ethereum directly: the sources at the previous commit compile byte for byte to the runtime at the mainnet factory address, while HEAD compiles to a longer runtime with a different hash. Since the factory embeds the vault init code, every vault created on Robinhood would run code that was never deployed on Ethereum, breaking the \"same audited code on both chains\" guarantee. The proof test under `test/scratch/AuditedCode.t.sol` fails on this tree and passes on the restored sources. The minimal fix is restoring those two files to the previous commit; keeping the fixes instead is a scope decision for the requester.\n\n**Info finding: the manifest deploys `VaultFactoryNoFee`, not `VaultFactory`.** It is a zero-argument subclass added because the manifest refuses zero-address constructor words. Its runtime is proven identical to the factory with two zero arguments and it adds no role or call, but the verified name, creation code and ABI differ from what the brief names. Recorded so the deviation is accepted knowingly.\n\n**What I checked in the Access Control, Trust Gap and Asymmetry guides.** I mapped every role and guard in the factory and both vaults, diffed paired paths (queue vs execute, propose vs accept, ETH vs ERC-20 branches, bound vs open vaults, constructor vs later restriction), and traced the seams between caller permission and value flow. The permission model holds: no owner or admin exists, every manager and beneficiary power only tightens rules, public entry points pay only the announced destination or beneficiary, and I ruled out a gas-starvation bypass of the shared weekly cap. Build passes, all 117 tests pass, and the fork test against the live IMD token passes with a public RPC. Static-analysis leads were all timestamp or strict-equality notes on intended logic and reproduced no defect.\n\n**Coverage.** All 27 entry points have rows: 23 hold, the three `VaultFactoryNoFee` creation paths and one invariant row point at the main finding, none unreached. Not covered: the protected owner-check harness, whose base file is not in the tree, and no live call to Robinhood Chain itself.","treeHash":null,"usage":{"cachedInputTokens":1566207,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":40385,"runtime":"claude","turns":42,"wallClockMs":698071}},{"artifacts":[],"attempt":1,"bundleHash":"72621cd7df9213f6d99936e5e316a7519922cfe928f4c3398ac4973c7d6b6be4","device":"433c37ef2c9c708d","findings":[],"hash":"94fad1d04037929c8d57defa8b12875fd695677d74d05726a058733d3a8f102c","nodeId":"315e9b45-c50b-4a79-8b45-b62c37fd460b","outcome":"completed","summary":"Done. The manifest is written and verified, and only launch.json changed.\n\n**What launch.json now says**\n\n- One contract, VaultFactoryNoFee, with an empty constructor argument list.\n- Notes explain that this is the Robinhood copy of the audited factory with the brief's two zero words, why it is named through the zero-argument subclass, and what was checked.\n\n**Why VaultFactoryNoFee rather than VaultFactory with two zero words**\n\nThe brief asks for the factory with both constructor addresses set to zero. The manifest rules refuse the zero address in any address argument except a native ETH currency field. The prior accepted work resolved this by adding a zero-argument subclass whose constructor passes the two zero values itself, with a test asserting its runtime bytecode and immutables are identical to the audited factory constructed with zeros. The accepted tree, its tests and the existing manifest all point at that contract, so the manifest describes it. The audited VaultFactory.sol is untouched.\n\n**Checks run**\n\n- `forge build` compiles cleanly.\n- The full suite passes with the verifier's fuzz seed: 118 tests, 0 failures.\n- A scratch rehearsal of the protected floor passes: create2 of the contract from a factory on chain 4663, runtime under the EIP-170 limit, no DELEGATECALL, CALLCODE or SELFDESTRUCT in the factory or the vaults it creates, bytecode equal to the audited factory with zero arguments, and no owner-like function.\n- The manifest has exactly the three schema keys, a valid 17-character identifier, string arguments and notes under the 4000-character limit.\n\nThe owner checks in the protected suite need nothing from the manifest: the factory has no owner, admin or sender-derived role, so there is no `$owner` argument to supply.","treeHash":"d095edcb13f83206ecebc175f8fd6220966e6c88","usage":{"cachedInputTokens":457385,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":8995,"runtime":"claude","turns":20,"wallClockMs":354084}},{"artifacts":[],"attempt":2,"bundleHash":"27f2006d4c67760e735c02a30e486b0b057999f3fc0121ba0fa7b6a7475ab44a","device":"fba19b641cdd6f27","findings":[{"description":"_charge computes `(balance * rateBps) / BPS` with checked arithmetic (also availableThisWeek at line 305). A balance above type(uint256).max / rateBps makes every execute() of that asset panic, so the asset can never leave the treasury, and availableThisWeek reverts too. Fix: Math.mulDiv(balance, rateBps, BPS) (as in commit 98fd654).","line":347,"path":"src/TreasuryVault.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 {VaultFactory} from \"src/VaultFactory.sol\";\nimport {TreasuryVault} from \"src/TreasuryVault.sol\";\n\ncontract P1Token is ERC20 {\n    constructor() ERC20(\"P\", \"P\") {}\n\n    function mint(address to, uint256 a) external {\n        _mint(to, a);\n    }\n}\n\n/// A treasury balance above type(uint256).max / rateBps makes the weekly cap `balance * rateBps` overflow, so every\n/// withdrawal of that token panics and the balance is locked for good. Expected: 1 wei withdraws.\ncontract P1TreasuryCapOverflowTest is Test {\n    function test_HugeBalanceStillWithdraws() public {\n        vm.chainId(4663);\n        vm.warp(1_760_000_000);\n        VaultFactory f = new VaultFactory(IERC20(address(0)), address(0));\n        P1Token t = new P1Token();\n        address manager = makeAddr(\"manager\");\n        address dest = makeAddr(\"dest\");\n        TreasuryVault v = f.createTreasury(address(t), manager, 1 days, 2500, new address[](0));\n        t.mint(address(v), type(uint256).max / 2500 + 1);\n        vm.prank(manager);\n        uint256 id = v.queue(address(t), dest, 1);\n        skip(1 days);\n        v.execute(id); // reverts with panic 0x11 on the audited code\n        assertEq(t.balanceOf(dest), 1);\n    }\n}","reproduction":"Treasury rate 2500, mint type(uint256).max/2500 + 1 of a token to it, queue 1 wei, wait 1 day, execute. Expected: 1 wei transferred. Actual: panic 0x11. With type(uint256).max/2500 it works (test/RobinhoodBoundaries.t.sol::test_TreasuryLargestSafeBalanceStillWithdraws).","severity":"medium","title":"TreasuryVault weekly cap overflows for balances above type(uint256).max / rateBps, locking the asset"},{"description":"_tryBalance staticcalls each tracked asset's balanceOf with all remaining gas and copies the full return data. A tracked asset (any address the manager ever queued, never removable) whose balanceOf burns gas consumes 63/64 of the gas on each of the two reads per withdrawal, so execute() of every other ERC-20 runs out of gas, contrary to the comment 'a watched asset that can not be read never blocks the others' withdrawals'. A token can switch to this behaviour after being tracked (upgradeable/malicious token). Fix: fixed gas stipend and bounded returndata copy (as in commit 98fd654).","line":336,"path":"src/TreasuryVault.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 {VaultFactory} from \"src/VaultFactory.sol\";\nimport {TreasuryVault} from \"src/TreasuryVault.sol\";\n\ncontract P2Token is ERC20 {\n    constructor() ERC20(\"P\", \"P\") {}\n\n    function mint(address to, uint256 a) external {\n        _mint(to, a);\n    }\n}\n\n/// A token whose balanceOf burns all gas it is given once `burn` is set.\ncontract P2GasBurner is P2Token {\n    bool public burn;\n\n    function setBurn() external {\n        burn = true;\n    }\n\n    function balanceOf(address a) public view override returns (uint256) {\n        if (burn) {\n            while (true) {}\n        }\n        return super.balanceOf(a);\n    }\n}\n\n/// `_tryBalance` forwards all remaining gas to each tracked asset's balanceOf. One tracked asset that burns gas takes\n/// 63/64 of it twice per withdrawal, so a withdrawal of any other token runs out of gas even with 10M gas, contrary to\n/// the source's promise that \"a watched asset that can not be read never blocks the others' withdrawals\".\ncontract P2TrackedGasBurnTest is Test {\n    function test_GasBurningTrackedAssetDoesNotBlockOtherWithdrawals() public {\n        vm.chainId(4663);\n        vm.warp(1_760_000_000);\n        VaultFactory f = new VaultFactory(IERC20(address(0)), address(0));\n        P2Token good = new P2Token();\n        P2GasBurner bad = new P2GasBurner();\n        address manager = makeAddr(\"manager\");\n        address dest = makeAddr(\"dest\");\n        TreasuryVault v = f.createTreasury(address(good), manager, 1 days, 2500, new address[](0));\n        good.mint(address(v), 100e18);\n        vm.startPrank(manager);\n        v.queue(address(bad), dest, 1); // tracks `bad`; never executed\n        uint256 id = v.queue(address(good), dest, 1e18);\n        vm.stopPrank();\n        bad.setBurn();\n        skip(1 days);\n        v.execute{gas: 10_000_000}(id); // runs out of gas on the audited code\n        assertEq(good.balanceOf(dest), 1e18);\n    }\n}","reproduction":"Manager queues 1 wei of token BAD (tracked) and 1e18 of GOOD; BAD's balanceOf then loops forever; after 1 day execute(GOOD id) with 10,000,000 gas. Expected: 1e18 GOOD transferred. Actual: out-of-gas revert.","severity":"medium","title":"A gas-burning tracked asset blocks every ERC-20 withdrawal of the treasury"},{"description":"sync() is public and _sync records arrivedAt on any non-zero balance. For a token that already exists, anyone can send 1 wei to an open (launch) vesting vault and call sync, starting that token's linear unlock. When the project's real tranche arrives later it is unlocked by the elapsed fraction at once, breaking the documented guarantees that a late arrival 'still vests from its arrival, never on it' and that no schedule unlocks more than 25% in any week. Not a theft (funds still go to the beneficiary), but it defeats the lock the radar seals.","line":220,"path":"src/VestingVault.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 {VaultFactory} from \"src/VaultFactory.sol\";\nimport {VestingVault} from \"src/VestingVault.sol\";\n\ncontract P4Token is ERC20 {\n    constructor() ERC20(\"P\", \"P\") {}\n\n    function mint(address to, uint256 a) external {\n        _mint(to, a);\n    }\n}\n\n/// In an open vesting vault, anyone holding 1 wei of a token can start that token's vesting clock (`sync` is public\n/// and records arrival on any non-zero balance). The real tranche arriving 21 days later is then 75% releasable at\n/// once, although the contract promises that a late arrival \"still vests from its arrival, never on it\" and that no\n/// more than 25% unlocks in any week. Expected: at most 25% of the tranche releasable in the week after it arrives.\ncontract P4OpenVaultDustClockTest is Test {\n    function test_DustDoesNotStartTheClockForTheRealTranche() public {\n        vm.chainId(4663);\n        vm.warp(1_760_000_000);\n        VaultFactory f = new VaultFactory(IERC20(address(0)), address(0));\n        P4Token t = new P4Token();\n        address team = makeAddr(\"team\");\n        address anyone = makeAddr(\"anyone\");\n        VestingVault v = f.createLaunchVesting(team, uint64(block.timestamp), 0, 28 days);\n\n        t.mint(anyone, 1);\n        vm.startPrank(anyone);\n        t.transfer(address(v), 1);\n        v.sync(address(t));\n        vm.stopPrank();\n\n        skip(21 days);\n        t.mint(address(v), 1_000_000e18); // the launch delivers the project's share\n        uint256 tranche = 1_000_000e18 + 1;\n        // 750_000e18 on the audited code\n        assertLe(v.releasable(address(t)), tranche / 4, \"late tranche unlocked faster than 25% a week\");\n    }\n}","reproduction":"createLaunchVesting(team, now, 0, 28 days); anyone transfers 1 wei of token T and calls sync(T); 21 days later 1,000,000e18 T arrives. Expected: <= 25% releasable that week. Actual: releasable(T) = 750,000e18 immediately.","severity":"medium","title":"Anyone can start an open vesting vault's clock for a token with 1 wei, so the real tranche unlocks far faster than 25% a week"},{"description":"`(total * elapsed) / duration` is checked arithmetic; an allocation above type(uint256).max / (duration-1) makes releasable/release panic for the whole linear period, after which everything unlocks at once (elapsed >= duration short-circuits). Fix: Math.mulDiv (as in commit 98fd654).","line":126,"path":"src/VestingVault.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 {VaultFactory} from \"src/VaultFactory.sol\";\nimport {VestingVault} from \"src/VestingVault.sol\";\n\ncontract P3Token is ERC20 {\n    constructor() ERC20(\"P\", \"P\") {}\n\n    function mint(address to, uint256 a) external {\n        _mint(to, a);\n    }\n}\n\n/// `vestedAmount` computes `total * elapsed` unchecked against overflow. With total = type(uint256).max / 2 and half\n/// the duration elapsed, releasable() panics; nothing can be released until the schedule ends, then all at once.\n/// Expected: half the allocation releasable at the midpoint.\ncontract P3VestingOverflowTest is Test {\n    function test_LargeAllocationVestsLinearly() public {\n        vm.chainId(4663);\n        vm.warp(1_760_000_000);\n        VaultFactory f = new VaultFactory(IERC20(address(0)), address(0));\n        P3Token t = new P3Token();\n        address project = makeAddr(\"project\");\n        address team = makeAddr(\"team\");\n        uint256 total = type(uint256).max / 2;\n        t.mint(project, total);\n        vm.startPrank(project);\n        t.approve(address(f), total);\n        VestingVault v = f.createVesting(t, team, total, uint64(block.timestamp), 0, 28 days);\n        vm.stopPrank();\n        skip(14 days);\n        v.release(address(t)); // panics 0x11 on the audited code\n        assertEq(t.balanceOf(team), total / 2);\n    }\n}","reproduction":"createVesting with amount type(uint256).max/2, duration 28 days, start now; skip 14 days; release. Expected: total/2 released. Actual: panic 0x11. With type(uint256).max/(28 days - 1) it works (test/RobinhoodBoundaries.t.sol::test_VestingLargestSafeAllocationReleasesLinearly).","severity":"low","title":"VestingVault vestedAmount overflows for very large allocations, deferring all release to the end"},{"description":"After each ERC-20 transfer the vault charges any fall in another tracked asset's reported balance against that asset's window and reverts if it exceeds the cap. A tracked token (never removable) whose balanceOf reports a drop larger than its cap after any transfer blocks all other ERC-20 withdrawals.","line":187,"path":"src/TreasuryVault.sol","reproduction":"Track token LIAR (queue 1 wei); LIAR's balanceOf returns X before and 0 after any call in a later tx (e.g. toggled by block number); execute a GOOD withdrawal. Expected: GOOD withdrawal succeeds. Actual: OverWeeklyCap(cap of LIAR).","severity":"medium","title":"A tracked token that misreports a falling balance makes every ERC-20 withdrawal revert OverWeeklyCap"},{"description":"Both vaults charge/track the requested amount, not the balance change. A token whose transfer removes more than `amount` from the sender lets more than the weekly cap leave the treasury, and in VestingVault makes released[] understate what left, so the final release can exceed the balance and revert.","line":180,"path":"src/TreasuryVault.sol","reproduction":"Treasury with a token that debits 2x amount on transfer; queue and execute exactly the cap. Expected: at most the cap leaves. Actual: 2x the cap leaves the treasury.","severity":"low","title":"Tokens that debit the sender more than the amount bypass the treasury cap and can block final vesting release"},{"description":"The launch deploys VaultFactoryNoFee, a zero-argument subclass whose runtime code is byte for byte that of new VaultFactory(IERC20(address(0)), address(0)) (asserted by test/Audit.t.sol::test_NoFeeFactoryIsTheAuditedFactoryWithZeroArguments); only its creation code differs, because the manifest refuses zero-address constructor words. VaultFactory embeds the VestingVault and TreasuryVault creation code, so any fix to the vault findings above changes the factory's code on both chains and needs a new audited release. That decision is the requester's.","line":1,"path":"src/VaultFactoryNoFee.sol","reproduction":"Apply the mulDiv / gas-stipend fixes from commit 98fd654 to the vaults and compare VaultFactory's runtime code before and after: it differs.","severity":"info","title":"Fixing the findings above changes the factory's runtime code, so it would no longer match the Ethereum deployment"}],"hash":"9f433016dec1589b19a622d415d341aa05f69019f9cac22d019338fc591ebe96","nodeId":"396f006d-0900-4690-b849-d26adc121e52","outcome":"completed","summary":"`forge build` succeeds and `forge test` passes with the verifier's fuzz seed: 158 tests, 0 failures. The last attempt was rejected because it changed nothing; this round adds one test file. No source file was touched.\n\n**New file: `test/RobinhoodBoundaries.t.sol`** (15 tests). It deploys `VaultFactory(imd_ = 0, feeRecipient_ = 0)` exactly as the brief states, on chain id 4663. The tests pin the last values the audited code still handles correctly; the values one step past them fail, and those are reported below rather than written as passing tests.\n- **Factory:** with these arguments it charges no fee. It takes exactly the vested amount from the caller, and nothing is ever marked paid on either chain. Several vaults from one project are all registered, along with who created them. Treasury settings one step outside the allowed delay and rate are refused, and a refused creation leaves nothing behind.\n- **Treasury:** the largest balance whose weekly cap fits in uint256 still withdraws. A dust balance has a zero cap until one more wei arrives. A fuzz test checks that the amount the weekly-cap view reports is exactly what a withdrawal can take, and one wei more is refused. An ETH withdrawal never reads the tracked tokens' balances. A withdrawal can't run twice or after a cancel, and a stranger can't call any of the manager's functions.\n- **Vesting:** the largest safe allocation releases linearly. A schedule may end exactly at the uint64 maximum, and one second more is refused. One wei unlocks only at the very end. Releasing the zero address or a token the vault doesn't vest is refused, and the beneficiary-change failure paths are covered.\n\n**Defects reported in `.imd-findings.json`** (7 findings). The four with proofs each fail on the current code for the stated reason.\n\n| Severity | Where | Defect | Proof result |\n|---|---|---|---|\n| medium | `TreasuryVault.sol:347` | The weekly cap `balance * rateBps` overflows when the balance is above max/rateBps, so that token can never be withdrawn | overflow panic; passes on fixed code |\n| medium | `TreasuryVault.sol:336` | A tracked token whose balance check burns gas blocks every other token's withdrawal, even with 10M gas | out of gas; passes on fixed code |\n| medium | `VestingVault.sol:220` | Anyone can start an open vault's vesting clock for a token by sending 1 wei; a tranche arriving 21 days later has 750,000e18 of 1,000,000e18 releasable at once | assertion fails (750,000e18 > 250,000e18) |\n| low | `VestingVault.sol:126` | `total * elapsed` overflows for very large allocations, so nothing releases until the vesting period ends | overflow panic; passes on fixed code |\n| medium | `TreasuryVault.sol:187` | A tracked token that falsely reports its balance falling makes withdrawals of other tokens revert `OverWeeklyCap` | described only |\n| low | both vaults | Tokens that take more from the sender than the amount sent let more than the weekly cap leave the treasury, and can block the final vesting release | described only |\n| info | — | Any fix to the vaults changes the factory's deployed code on both chains, so it needs a new audited release; that is your decision | — |\n\n\"Fixed code\" is the vault source from commit `98fd654`. I checked it out temporarily to confirm the three proofs pass there, then restored the current source. No commit has this fix for the 1-wei clock defect.\n\nI first filed the info finding as \"the launch's factory isn't the audited bytecode\", but `test_NoFeeFactoryIsTheAuditedFactoryWithZeroArguments` already shows its deployed code matches `VaultFactory(0, 0)` byte for byte. I rewrote that finding before finishing.\n\nThe new test file is untracked and not committed. The findings file is excluded by `.git/info/exclude`.","treeHash":"43de3e39fb4a53dd1f8797b28913e032c0173e09","usage":{"cachedInputTokens":811163,"inputTokens":28,"model":"claude-opus-5-5","outputTokens":18985,"runtime":"claude","turns":15,"wallClockMs":350608}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"e382bd4d2b3e471f","findings":[{"citation":"resolved","description":"Seam: execution x periphery x first principles. execute() reads the balance of every asset ever queued before and after the transfer of w.asset (lines 200-214) and charges any fall against that asset's window with _charge, which reverts OverWeeklyCap when the fall exceeds what that window allows. The set _trackedAssets only grows (queue, lines 163-166) and nothing can shrink it. The code's own guarantee (lines 26-28, 338-342) is that a tracked asset can never block the others' withdrawals, but the gas stipend and single-word copy only cover an asset that burns gas or reverts: an asset whose balanceOf answers cheaply with a value that depends on the treasury's balance of another token is trusted. Such a token reports a huge balance while the treasury holds at least X of the project token and zero once it holds less, so any project-token withdrawal that crosses X is seen as a fall of 1e40 in the tracked token, larger than any cap, and reverts. The attacker (the tracked token's deployer or the admin of an upgradeable token that was once legitimate) picks X equal to the current balance and keeps it there: every ERC-20 withdrawal of the targeted assets reverts in every epoch, for as long as they want. The manager cannot cancel, untrack, or route around it; only ETH is unaffected. Precondition: the manager once queued a withdrawal of the hostile token (for example to clear an unsolicited airdrop, which is exactly the routine action a treasury manager takes; queue() accepts any address). Impact: the project treasury's ERC-20 holdings are frozen at an outsider's discretion; no funds are stolen. The existing tests cover gas-burning and payload-bombing tracked assets (test_GasBurningTrackedAssetCanNotBlockOtherWithdrawals, test_PayloadBombTrackedAssetCanNotBlockOtherWithdrawals) but not a readable asset whose answer depends on other balances. Fix options, the author's call: (a) only honour the shared charge when it is consistent with a genuine second entry point, i.e. before[i] equals the pre-transfer balance of w.asset and fell equals w.amount, and otherwise skip (and log) the asset; a mirror token then only ever opens a window at least as wide as the withdrawn asset's own, so it cannot be tighter; or (b) let the manager announce and, after the delay, remove a tracked asset through the same public-delay path the other rules use (this weakens the shared cap for an honest double-entry token, so (a) preserves the design better). The attached test asserts a routine project-token withdrawal executes; a fix of shape (b) would need the untracking call added before execute.","line":212,"path":"src/TreasuryVault.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {VaultFactoryNoFee} from \"src/VaultFactoryNoFee.sol\";\nimport {TreasuryVault} from \"src/TreasuryVault.sol\";\n\ncontract ProjectToken is ERC20 {\n    constructor() ERC20(\"Project\", \"PRJ\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev A token that was airdropped to the treasury. Its `balanceOf` is cheap (well under BALANCE_READ_GAS) and\n///      returns a full word, so `_tryBalance` trusts it. Once `armed`, its reported balance for the treasury is the\n///      treasury's PRJ balance minus `floor`: it therefore \"falls\" by exactly the PRJ amount whenever PRJ leaves,\n///      and the window it opens is 25% of a number the attacker chose to be tiny.\ncontract AirdropToken {\n    string public constant name = \"Airdrop\";\n    string public constant symbol = \"AIR\";\n    uint8 public constant decimals = 18;\n\n    IERC20 public immutable watched;\n    address public immutable target;\n    bool public armed;\n    uint256 public floor;\n\n    constructor(IERC20 watched_, address target_) {\n        watched = watched_;\n        target = target_;\n    }\n\n    function arm(uint256 floor_) external {\n        armed = true;\n        floor = floor_;\n    }\n\n    /// @dev Armed: reports a huge balance while the treasury still holds at least `floor` PRJ, and zero once it\n    ///      holds less. Any PRJ withdrawal that takes the balance below `floor` is then seen as a fall of 1e40 in\n    ///      this token, more than any window can allow, so `execute` reverts with OverWeeklyCap.\n    function balanceOf(address account) external view returns (uint256) {\n        if (account != target || !armed) return 100e18;\n        return watched.balanceOf(target) >= floor ? 1e40 : 0;\n    }\n\n    function transfer(address, uint256) external pure returns (bool) {\n        return true;\n    }\n}\n\ncontract TrackedAssetFreezeTest is Test {\n    VaultFactoryNoFee factory;\n    TreasuryVault treasury;\n    ProjectToken prj;\n    AirdropToken air;\n\n    address manager = makeAddr(\"manager\");\n    address attacker = makeAddr(\"attacker\");\n    address payroll = makeAddr(\"payroll\");\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(1_800_000_000);\n        factory = new VaultFactoryNoFee();\n        prj = new ProjectToken();\n        treasury = factory.createTreasury(address(prj), manager, 1 days, 2_500, new address[](0));\n        prj.mint(address(treasury), 1_000e18);\n        air = new AirdropToken(IERC20(address(prj)), address(treasury));\n    }\n\n    /// Finding: a tracked asset is never untracked, and its self-reported balance decides whether every other\n    /// ERC-20 withdrawal may execute. An attacker whose token the manager once queued (to clear an airdrop) can\n    /// later make every PRJ withdrawal revert with OverWeeklyCap, for as long as they like.\n    function test_TrackedAirdropTokenFreezesProjectTokenWithdrawals() public {\n        // 1. The manager clears the airdropped token once: queueing it is enough to track it forever.\n        vm.prank(manager);\n        uint256 airId = treasury.queue(address(air), payroll, 1e18);\n        vm.warp(block.timestamp + 1 days);\n        treasury.execute(airId);\n        assertTrue(treasury.isTracked(address(air)));\n\n        // 2. Weeks later the attacker arms the token against the treasury's current PRJ balance.\n        vm.warp(block.timestamp + 3 weeks);\n        vm.prank(attacker);\n        air.arm(prj.balanceOf(address(treasury)));\n\n        // 3. A routine PRJ withdrawal far under the weekly cap (10 of 1000, cap is 250).\n        vm.prank(manager);\n        uint256 id = treasury.queue(address(prj), payroll, 10e18);\n        vm.warp(block.timestamp + 1 days);\n        assertEq(treasury.availableThisWeek(address(prj)), 250e18);\n\n        // Expected: PRJ leaves, 10e18 charged against PRJ's window.\n        // Actual on this code: OverWeeklyCap from the AIR window, which the manager can neither close nor untrack.\n        treasury.execute(id);\n        assertEq(prj.balanceOf(payroll), 10e18, \"project token withdrawal must not depend on an airdropped token\");\n    }\n}","reproduction":"Robinhood chain id 4663. VaultFactoryNoFee.createTreasury(PRJ, manager, 1 day, 2500, []) and mint 1000e18 PRJ to the treasury. Deploy AIR, a token whose balanceOf(treasury) returns 100e18 until armed, and once armed returns 1e40 when PRJ.balanceOf(treasury) >= floor and 0 otherwise. Step 1: manager.queue(AIR, payroll, 1e18); +1 day; execute -> AIR is tracked for ever (isTracked[AIR] true). Step 2: +3 weeks; attacker calls AIR.arm(1000e18). Step 3: manager.queue(PRJ, payroll, 10e18); +1 day; availableThisWeek(PRJ) == 250e18. Expected: execute sends 10e18 PRJ to payroll and charges 10e18 against PRJ's window. Actual: before[AIR] = 1e40, after the PRJ transfer AIR reports 0, fell = 1e40, _charge(windows[AIR], 1e40, 1e40) opens AIR's window at cap 2.5e39 and reverts OverWeeklyCap(2.5e39). The whole execute reverts, in this epoch and every later one, for any PRJ amount >= 1 wei while floor stays at the current balance. test/scratch/TrackedAssetFreeze.t.sol fails on this code with that revert.","severity":"medium","snippet":"                uint256 now_ = _tryBalance(other);\n                if (now_ >= before[i]) continue;\n                uint256 fell = before[i] - now_;\n                _charge(windows[other], before[i], fell);","title":"A tracked asset's self-reported balance can permanently block every other ERC-20 withdrawal of the treasury"},{"citation":"resolved","description":"Seam: execution x first principles. For an open vault the stated guarantee (lines 21-25) is that a token's linear unlock begins no earlier than the moment the vault records that token's arrival, so a launch that slips past start + cliff still vests from its arrival. The arrival is recorded once, by _sync (lines 219-225), when the balance is non-zero, and vestedAmount then applies the elapsed fraction to totalAllocation, the current balance plus released. Anyone can therefore send 1 wei of the token and call sync() long before the real allocation arrives; the real allocation later counts as a top-up and is unlocked by the fraction already elapsed, up to 100% on arrival. The source documents that top-ups follow the calendar (line 25-26), so this is a known property, but it means the 25%-per-week bound of an open vault holds only for tokens that arrive before anyone (including the beneficiary) pre-seeds the clock. For a token minted in the launch transaction itself nothing can be sent earlier, so the swarm-launch remainderTo flow is not exposed; a pre-existing token routed into an open vault is. The radar can detect it by comparing the Arrived event's timestamp with the first transfer of the real amount. Minimal fix, if wanted: record the arrival balance and vest later arrivals from their own recorded time (per-tranche accounting), which is a design change the author must decide; otherwise document that an open vault's clock must not be trusted when the token existed before the vault.","line":128,"path":"src/VestingVault.sol","reproduction":"Chain 4663. VaultFactoryNoFee.createLaunchVesting(team, now, 0, 28 days). Token T exists. Anyone: T.transfer(vault, 1); vault.sync(T) -> arrivedAt[T] = now. Warp +28 days (launch slipped four weeks). The launch sends 1_000_000e18 T to the vault. Expected per the stated guarantee: the allocation vests linearly from its arrival, at most 25% in the first week. Actual: releasable(T) == 1_000_000e18 + 1 immediately; release(T) sends the whole amount to team in the same block. Confirmed with test/scratch/PreseedClock.t.sol (passes, demonstrating the behaviour).","severity":"low","snippet":"        uint256 total = totalAllocation(asset);\n        uint64 elapsed = timestamp - from;\n        if (elapsed >= duration) return total;\n        return Math.mulDiv(total, elapsed, duration);","title":"Open vault: 1 wei of a token sent early starts its clock, so a later real allocation of that token is unlocked at once"}],"hash":"bf605edcafe633a9981716ad7d863eebb06245310bf905780ddb852dce430662","nodeId":"389e2c7e-cfa7-41d8-b106-1c630ae01ec7","outcome":"completed","summary":"Review complete. The findings file is written and every listed entry point has a coverage row.\n\n**Outcome**\n\nThe code builds and all 118 existing tests pass. The Robinhood copy charges no fee and never reaches an IMD transfer. I found one substantive economic defect in my area and one lower-severity guarantee gap, both recorded in `.imd-findings.json` with reproductions.\n\n**Findings**\n\n- **Medium, with proof.** A tracked asset's self-reported balance can freeze every other ERC-20 withdrawal of a treasury. `execute` charges any fall in a tracked asset's balance against that asset's window and reverts when the fall exceeds the cap, and the tracked set can never shrink. The gas stipend only defends against assets that burn gas or revert. A cheap `balanceOf` that answers based on the treasury's project-token balance reports a fall of 1e40 whenever the project token leaves, so every such withdrawal reverts with OverWeeklyCap for as long as the attacker keeps it armed. The precondition is one routine manager action: queueing a withdrawal of that token once, for instance to clear an airdrop. The proof in `test/scratch/TrackedAssetFreeze.t.sol` fails on this code with that revert. I proposed two fix shapes and noted which preserves the shared-cap design.\n- **Low.** In an open vault, anyone can send 1 wei of a pre-existing token and call `sync`, which starts that token's clock. When the real allocation arrives later it counts as a top-up and is unlocked by the elapsed fraction, up to all of it at once. This is a consequence of the documented calendar-based accounting, and a token minted in the launch transaction itself cannot be pre-seeded, so it is reported as a guarantee gap rather than a loss.\n\n**Coverage**\n\nAll 27 entry points have a row. Four are marked finding, the rest hold after tracing checks, state changes and failure paths. I also recorded four invariant rows, including the documented 43.75% epoch-boundary bound and the one-time double-entry case, both accepted design and not reported. Nothing was marked unreached.\n\n**Outside my area, not reported as defects**\n\nThe manifest deploys `VaultFactoryNoFee`, a zero-argument subclass, rather than `VaultFactory` with two zero words, because the manifest refuses zero addresses. The existing test asserts the runtime bytecode is identical to the audited factory constructed with zero arguments, so there is no economic deviation. Robinhood Chain's sequencer-set timestamps could in principle skew the 24-hour delay, but that is not reproducible here and I left it out.\n\nNo source file was changed. Only `test/scratch/` and `.imd-findings.json` were written.","treeHash":null,"usage":{"cachedInputTokens":1290271,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":41735,"runtime":"claude","turns":24,"wallClockMs":951978}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"75052237a39b6e12","findings":[{"citation":"resolved","description":"This launch deploys the Robinhood copy with imd_=0x0 and feeRecipient_=0x0, but the manifest in the tree is the Ethereum one: imd_ is the mainnet IMD token address and feeRecipient_ is $owner. On chain 4663 that address has no code. VaultFactory._chargeOnce only skips the fee when imd is zero (src/VaultFactory.sol:149), so every creation path (createVesting, createLaunchVesting, createTreasury) calls imd.safeTransferFrom on a codeless address and OpenZeppelin SafeERC20 reverts (empty return data and code.length == 0). Both arguments are immutable, so a factory deployed from this manifest can never create a vault and the launch has to be redone. A second, policy-level problem for the manifest step: the launch reference says no address argument other than a native-ETH Currency field may be zero, while this launch requires two zero address words. The adapter and manifest step must confirm the manifest schema accepts \"0x0000000000000000000000000000000000000000\" twice for this contracts-only launch; if it does not, the brief's deployment cannot be expressed and needs a policy decision, not a source change.","line":7,"path":"launch.json","reproduction":"Deploy with the tree's current arguments on chain 4663: vm.chainId(4663); f = new VaultFactory(IERC20(0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7), 0x75dF3d2E7a1d23021e5d7EF06312D13009E9BBb8). Then f.createLaunchVesting(address(1), block.timestamp, 0, 28 days) -> reverts (SafeERC20FailedOperation from the codeless IMD address); same for createVesting and createTreasury; vaultCount() stays 0. Expected per the brief: new VaultFactory(IERC20(address(0)), address(0)) on 4663, where the same three calls succeed, vaultCount() == 3 and feePaid(4663, token) stays false. Both outcomes were checked with a scratch Foundry test here.","severity":"medium","snippet":"        \"0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7\",\n        \"$owner\"","title":"launch.json still carries the Ethereum constructor arguments; on Robinhood they brick the factory"},{"citation":"resolved","description":"_charge computes balance * rateBps in checked arithmetic (line 347), and availableThisWeek does the same (line 305). Once the vault's balance of an asset exceeds type(uint256).max / rateBps (about 4.6e73 base units at the 25% ceiling, lower for higher precision tokens with uint256-max supplies, which exist among meme tokens), the multiplication panics. Every execute for that asset reverts before the transfer, nothing else can lower the vault's balance of it, so the asset is locked permanently, and availableThisWeek reverts for the radar as well. Fix: compute the cap without an intermediate overflow, e.g. OpenZeppelin Math.mulDiv(balance, rateBps, BPS), in both places.","line":347,"path":"src/TreasuryVault.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {TreasuryVault} from \"src/TreasuryVault.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\n\n/// A token with a uint256-max supply, as some meme tokens have.\ncontract MaxSupplyToken is ERC20 {\n    constructor() ERC20(\"MAX\", \"MAX\") {\n        _mint(msg.sender, type(uint256).max);\n    }\n}\n\n/// `TreasuryVault._charge` and `availableThisWeek` compute `balance * rateBps` in checked arithmetic. When the\n/// vault's balance of an asset exceeds `type(uint256).max / rateBps` the multiplication overflows, every\n/// `execute` for that asset reverts with a panic, and since nothing can lower the balance through the vault, the\n/// asset is locked for good. Fails on the current code (panic 0x11) and passes once the cap is computed without\n/// an intermediate overflow (for example OpenZeppelin `Math.mulDiv(balance, rateBps, BPS)`).\ncontract ProofTreasuryCapOverflow is Test {\n    address manager = makeAddr(\"manager\");\n    address dest = makeAddr(\"dest\");\n\n    function test_hugeBalanceCanStillBeWithdrawnUnderTheCap() public {\n        vm.warp(1_760_000_000);\n        address[] memory none;\n        TreasuryVault vault = new TreasuryVault(address(0), manager, 1 days, 2500, none);\n        MaxSupplyToken token = new MaxSupplyToken();\n        token.transfer(address(vault), 1 << 255);\n\n        vm.prank(manager);\n        uint256 id = vault.queue(address(token), dest, 1e18);\n        vm.warp(block.timestamp + 1 days);\n\n        // 25% of 2^255 is a well-defined cap; a 1e18 withdrawal is far below it.\n        uint256 cap = vault.availableThisWeek(address(token));\n        assertLe(cap, 1 << 253);\n        assertGe(cap, (1 << 253) - 10_000);\n\n        vault.execute(id);\n        assertEq(token.balanceOf(dest), 1e18);\n    }\n}","reproduction":"Treasury with rateBps 2500. A token mints 2^255 units to the vault (any balance above 2^256/2500 works). Manager queues 1e18 of it to an allowed destination; after the delay anyone calls execute(id). Expected: the cap is 25% of 2^255 and 1e18 leaves. Actual: execute reverts with panic 0x11 (arithmetic overflow) at _charge, and availableThisWeek(token) reverts too; the balance never leaves. Proof test attached: test/scratch/Proof_TreasuryCapOverflow.t.sol fails on this code and passes with an overflow-free cap.","severity":"medium","snippet":"            win.cap = (balance * rateBps) / BPS;","title":"Weekly cap arithmetic overflows for large balances, locking that asset in the treasury for good"},{"citation":"resolved","description":"The NatSpec promises that a tracked asset that cannot be read never blocks the others' withdrawals, and _tryBalance only handles a reverting balanceOf. It forwards all remaining gas (EIP-150 leaves the caller 1/64) and the high-level staticcall copies the whole return payload into memory. A tracked token whose balanceOf consumes the gas it is given, or returns a payload large enough that copying it costs more than the caller has left, therefore makes execute itself run out of gas, for every ERC-20 withdrawal of every asset, because the loop at lines 196-208 visits every tracked asset twice. Tracked assets can never be removed, and the manager grows the set through normal use (queueing a withdrawal of any token the treasury holds), so the precondition is an asset that later changes behaviour: upgradeable tokens (the common USDC/USDT pattern) or a token whose admin wants to hurt the project. ETH withdrawals still work. Fix: cap the gas forwarded to the staticcall (a few thousand gas is enough for any ERC-20 balanceOf) and read only the first 32 bytes of return data via assembly (returndatasize check, no full copy), or let the manager drop an asset from the tracked set when its balance is zero.","line":336,"path":"src/TreasuryVault.sol","reproduction":"Manager queues a withdrawal of token X once (X becomes tracked). Later X's implementation changes so that balanceOf(vault) spends essentially all forwarded gas or returns a multi-megabyte payload. Manager queues 1e18 of unrelated token Y to an allowed destination; after the delay anyone calls execute(id) with any gas limit. Expected (per NatSpec): X is skipped as unreadable and Y's 1e18 is transferred. Actual: the outer call is left with 1/64 of its gas after the staticcall (or cannot afford the returndata copy) and execute reverts out of gas, for Y and every other ERC-20 asset, until X is benign again. Not executed as a Foundry test in this review; the mechanism follows from EIP-150 gas forwarding and the unbounded `bytes memory data` copy on line 335-336.","severity":"medium","snippet":"            asset.staticcall(abi.encodeWithSelector(IERC20.balanceOf.selector, address(this)));","title":"A tracked asset that turns hostile can make every ERC-20 execute run out of gas, freezing all token withdrawals"},{"citation":"resolved","description":"vestedAmount multiplies the total allocation by the elapsed seconds in checked arithmetic. For total above type(uint256).max / duration (about 4.8e70 units at the 28-day minimum, smaller for longer schedules) the multiplication panics, so releasable and release revert during the whole linear phase. When elapsed reaches duration the early return at line 125 bypasses the multiplication and the whole allocation is releasable in one go. The 25%-per-week guarantee is replaced by 0% then 100%, and the radar's releasable(token) view reverts in between. Fix: Math.mulDiv(total, elapsed, duration).","line":126,"path":"src/VestingVault.sol","reproduction":"Bound vault for a uint256-max-supply token: new VestingVault(token, beneficiary, now, 0, 28 days, 7 days); transfer type(uint256).max to it. After 1 day: releasable(token) and release(token) revert with panic 0x11 (expected 1/28 of the allocation). After 28 days: release(token) sends the entire balance in one transfer. Checked with a scratch test here.","severity":"low","snippet":"        return (total * elapsed) / duration;","title":"Vesting math overflows for very large allocations, so nothing vests during the linear phase and everything unlocks at once"},{"citation":"resolved","description":"The epoch cap for an asset is rateBps of the balance seen by the first execute of that epoch, and execute is open to anyone. An unprivileged actor can therefore choose the moment the cap is fixed: executing any small due withdrawal at the start of an epoch, in the block before a large deposit lands (front-running the deposit or a launch remainder), pins the cap to the small balance for the whole epoch, so the large withdrawal the manager queued cannot execute until the next epoch, and may expire (GRACE is 14 days, so two such griefs in a row kill it). No funds are lost; the harm is a delay the manager cannot undo, since no later execute reopens the window. Mitigation that keeps the design: open the window on max(balance at first execute, balance at this execute) or recompute the cap on each execute against the current balance while keeping spent.","line":187,"path":"src/TreasuryVault.sol","reproduction":"Treasury with 100e18 of token P, rate 2500, delay 1 day. Manager queues id0 = 1e18 and id1 = 25_000e18 to the destination. At the first second of a new epoch, attacker calls execute(0): window cap = 25e18, spent 1e18. Next, 100_000e18 of P is deposited. execute(1) now reverts OverWeeklyCap(24e18) and availableThisWeek(P) == 24e18 for the rest of the week, whereas executing id1 after the deposit on a fresh window would have passed (25% of 100_099e18). Checked with a scratch test here.","severity":"low","snippet":"        _charge(windows[w.asset], _balance(w.asset), w.amount);","title":"Anyone can fix the weekly cap on a low balance by executing a small queued withdrawal ahead of a deposit"},{"citation":"resolved","description":"Read in full: src/VaultFactory.sol, src/VestingVault.sol, src/TreasuryVault.sol, the six test files, README, ADAPTATION, SWARM_BRIEF and launch.json, plus the OpenZeppelin 5.1.0 SafeERC20 and ReentrancyGuard they inherit. forge build succeeds and all 109 project tests pass. VaultFactory runtime is 18,760 bytes and its init code 19,030 bytes, inside the floor's limits, and a scratch scan of the factory and both vault runtimes found no DELEGATECALL, CALLCODE or SELFDESTRUCT; the constructor makes no external call, so it deploys on an empty chain. No owner, pause, mint, blacklist, proxy or initializer exists in any of the three contracts. Not reached: test/Fork.t.sol (needs an RPC, skips without FORK_RPC) and any live check of Robinhood Chain's EVM version against the cancun target in foundry.toml; the brief states /requests/check passed on chain 4663. No source file was changed.","line":19,"path":"src/VaultFactory.sol","reproduction":"Not a defect. forge test: 109 passed, 0 failed.","severity":"info","snippet":"contract VaultFactory {","title":"Review coverage: all three source contracts read; fork test and live chain checks not run"}],"hash":"c0efcb4a5d97908a469b21626aec4d9fbd63575dc84dcd1a43c4dff2c35e9e6e","nodeId":"4dec9c25-8afe-42f6-b37e-0c977abe236b","outcome":"completed","summary":"Review complete. No source file was changed; the findings file is written and every snippet was verified against the tree.\n\n**Outcome**\n\n- `forge build` succeeds and all 109 project tests pass. `VaultFactory` runtime is 18,760 bytes with a 19,030-byte initcode, inside the floor's limits. A scratch scan of the factory and both vault runtimes found no DELEGATECALL, CALLCODE or SELFDESTRUCT, the constructor calls no contract, and the deployment with both arguments zero on chain 4663 works end to end.\n- Nothing the launch forbids is present: no owner, mint, pause, blacklist, proxy, initializer or post-deployment configuration in any of the three contracts.\n\n**Findings written to .imd-findings.json** (6 entries, 1 with a Foundry proof)\n\n- **Medium, launch.json line 7.** The manifest in the tree is still the Ethereum one (IMD token address and `$owner`). Deployed on Robinhood, that factory calls `safeTransferFrom` on a codeless address and every vault creation reverts forever. The required arguments are two zero addresses, which also conflicts with the manifest rule that only a native-ETH Currency field may be zero. The manifest step needs to confirm the schema accepts them.\n- **Medium, TreasuryVault.sol line 347.** The weekly cap computes `balance * rateBps` in checked arithmetic. A balance above roughly 4.6e73 units panics on every `execute` and on `availableThisWeek`, locking that asset permanently. Proof attached under test/scratch; it fails on this code with panic 0x11.\n- **Medium, TreasuryVault.sol line 336.** The tracked-asset balance read forwards all gas and copies the whole return payload, so a tracked token that later turns hostile can make every ERC-20 `execute` run out of gas. This contradicts the stated \"never blocks\" guarantee. Described from the EIP-150 mechanics; not executed as a test here.\n- **Low, VestingVault.sol line 126.** The same multiplication pattern makes `releasable` and `release` revert during the linear phase for very large allocations, then release everything at once.\n- **Low, TreasuryVault.sol line 187.** Anyone can pin the epoch cap to a low balance by executing a small due withdrawal just before a large deposit, delaying the manager's larger withdrawal by a week.\n- **Info.** Coverage statement.\n\n**Coverage**\n\nRead in full: the three contracts in `src`, all six test files, README, ADAPTATION, SWARM_BRIEF, launch.json, and the OpenZeppelin 5.1.0 SafeERC20 and ReentrancyGuard they use. Not reached: the fork test (needs an RPC) and any live check of Robinhood Chain's EVM version against the cancun target.","treeHash":null,"usage":{"cachedInputTokens":2080109,"inputTokens":678,"model":"claude-fable-5-1","outputTokens":42706,"runtime":"claude","turns":40,"wallClockMs":680963}},{"artifacts":[],"attempt":1,"bundleHash":"c51af6d145b54eeeb79dc81f60dd9e869814d13895df6cea5a38ca4f4665a013","device":"41f7c1458e1278ad","findings":[],"hash":"e6ecba2dc68d9b9d125bc2d9150e9f85bd7d5ac67cd61a1b703aa81a9f16a682","nodeId":"396f006d-0900-4690-b849-d26adc121e52","outcome":"completed","summary":"I added one test file, `test/RobinhoodFactory.t.sol`, which tests the Robinhood copy of the factory: `VaultFactory(imd_ = 0, feeRecipient_ = 0)`, deployed as `VaultFactoryNoFee`. I changed no source file and found no defect, so I didn't write `.imd-findings.json`. `forge build` succeeds, and the full `forge test` run with the verifier's seed passes: 147 tests in 11 suites, none failing or skipped (118 existed before, 29 are new). The file is not committed yet.\n\n**Deployment checks**\n- The launched copy has the same deployed code as `new VaultFactory(0, 0)`.\n- The constructor only refuses an IMD address with no fee recipient, which the brief's two zeros never trigger.\n- The constructor touches no account other than the factory itself, checked with state-diff recording.\n- Deploying the factory and creating vaults in a single transaction works.\n- The factory refuses ETH sent to it.\n\n**Fee-free behaviour**\n- `payFor` is refused with `NoFeeOnThisChain` for every caller, chain id and token or vault (fuzzed, 1,000 runs).\n- Creating an open vesting vault or a treasury touches only the factory and the new vault, so a wallet with no tokens and no approvals can do it.\n- Repeated creations never mark anything as paid.\n\n**Failure paths:** each one checks that a failed creation leaves the registry unchanged.\n- **`createVesting`:** refuses a zero token, a zero amount or a zero beneficiary. It also refuses:\n  - a duration 1 second short of 4 weeks;\n  - a start 1 second in the past;\n  - a schedule that overflows `uint64` (1 second less is accepted);\n  - a caller with no approval or too small a balance;\n  - a token that returns false;\n  - a token address with no code.\n- **Fuzzed boundaries:** two fuzz tests check that `createVesting` and `createTreasury` accept exactly the valid inputs, no more and no less.\n- **Fee-on-transfer token:** the creation event records the amount the vault actually received.\n- **Re-entering token:** a token that calls back into the factory mid-creation gets its own separately registered vault and doesn't corrupt the registry.\n- **`createLaunchVesting`:** refuses a zero beneficiary, a short duration, a past start and an overflowing schedule. A late-arriving launch remainder still vests over the full schedule from when it arrives.\n- **`createTreasury`:** refuses a delay of 0, a delay 1 second short of 1 day or past 30 days, a rate of 0 or above 2500, a zero manager and a zero destination. The bounds themselves are accepted, and a project token with no code yet is fine.\n\n**Invariant tests:** a handler with three creators and three beneficiaries randomly creates every vault type, funds vesting vaults and treasuries, releases, moves time forward, tries invalid creations and calls `payFor` (128 runs × depth 48, set inline in the file). After every sequence these hold:\n- **No fee and no custody:** the factory holds no tokens or ETH, both constructor values stay zero, and nothing is ever marked paid on chain 4663 or chain 1.\n- **Registry:** the vault count, order and creator of each vault, `isVault` and the per-token lists all match what the handler recorded.\n- **Conservation:** for each token, what the vesting vaults hold plus what they released equals what went in. The beneficiaries hold exactly what was released, and treasuries keep every deposit.\n- **Schedule:** nothing is released ahead of the schedule, nothing vests during the cliff, vesting never exceeds what the vault held, and the duration stays at least 28 days.\n\nA fixed-sequence test shows the handler really reaches creation, funding and paying releases, so the invariants aren't passing on empty runs.\n\nThe existing fork test still needs a live run: it only executes when `FORK_RPC` is set, and it targets the Ethereum factory with real IMD, not the Robinhood copy.","treeHash":"0f940233f1ffc58b3f6a4788484ca1c33cb5f474","usage":{"cachedInputTokens":704827,"inputTokens":24,"model":"claude-opus-5-5","outputTokens":24407,"runtime":"claude","turns":13,"wallClockMs":496125}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"78d8eb9b16352a18","findings":[],"hash":"ee37e7326416587f4449fc997b57c9c3634112085d5c297364d3ab44bd7716ca","nodeId":"315e9b45-c50b-4a79-8b45-b62c37fd460b","outcome":"no_change","summary":"The revision is complete. The manifest is unchanged and the finding is answered in `.imd-responses.json`.\n\n**Finding ecc21142 (manifest deploys VaultFactoryNoFee instead of VaultFactory with two zero words): disputed.** The observation is accurate but it is not a defect the manifest can fix. The manifest schema accepts a zero word only in a Currency field and refuses the zero address in any other address argument. The task rules also forbid a zero-address stand-in. A manifest naming VaultFactory with two zero words would therefore fail validation, and the verifier already accepted the current manifest in the previous round. The subclass was added by an earlier accepted round, and this task may touch only launch.json.\n\n**What I verified on the current tree**\n\n- `forge build` compiles cleanly.\n- The full suite with the verifier's fuzz seed passes: 114 tests, 0 failed.\n- The deployed bytecode of VaultFactoryNoFee is byte for byte that of VaultFactory. The subclass constructor takes no inputs, so the empty argument list is the only correct one.\n- launch.json has exactly the three schema keys, a 17-character identifier and notes of 3,011 characters.\n\n**Files changed:** only `.imd-responses.json`, which records the disputed verdict with the commands run and their results. No source file, configuration file or manifest was modified.","treeHash":null,"usage":{"cachedInputTokens":400970,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":5796,"runtime":"claude","turns":10,"wallClockMs":155302}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"72f49cf84b9ab056","findings":[{"citation":"resolved","description":"The brief says to deploy the exact code audited and deployed on Ethereum in launch 2244 and not to change any source file. Commit 98fd654 edited src/TreasuryVault.sol (new constant BALANCE_READ_GAS, an assembly _tryBalance with a 100,000 gas stipend, a new _cap using Math.mulDiv used by _charge and availableThisWeek) and src/VestingVault.sol (line 128 now Math.mulDiv(total, elapsed, duration)). The factory embeds both vaults' init code, so its runtime fingerprints all three sources. I read the mainnet factory with cast code from a public Ethereum RPC on 2026-10-19: 18,760 bytes, keccak256 0x4e60104bbd26773631103e40fbbf5713e12e2d485564c8ee7222fbe57e30f98a, imd() 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7, feeRecipient() 0x75dF3d2E7a1d23021e5d7EF06312D13009E9BBb8. The tree at commit ccfe47a (git archive into a temp dir, forge build) compiles with those arguments to exactly those bytes; HEAD compiles to 18,984 bytes with a different hash. Deploying HEAD on Robinhood breaks the stated guarantee that both chains run the same audited code: Robinhood vaults differ from Ethereum vaults, and the verified source will not match. The three edits fix remote conditions (balances above 2^256/rateBps, gas-burning or payload-bombing tracked tokens, allocations above 2^256/duration) that the Ethereum factory lives with today; whether to carry them is the requester's scope decision, not the adapter's, and ADAPTATION.md's reading that findings override the brief's 'unchanged' instruction is the defect. Minimal fix that honours the brief: git checkout ccfe47a -- src/TreasuryVault.sol src/VestingVault.sol, and drop the tests in test/Audit.t.sol and test/RobinhoodFactory.t.sol that only pass on the edited source (test_TreasuryCapHasNoOverflowForHugeBalances, test_VestingHugeAllocationVestsLinearly, test_GasBurningTrackedAssetCanNotBlockOtherWithdrawals, test_PayloadBombTrackedAssetCanNotBlockOtherWithdrawals, test_SlowButHonestSecondEntryPointIsStillCharged, and any that read BALANCE_READ_GAS); src/VaultFactoryNoFee.sol can stay since its runtime then equals VaultFactory(0,0) of the audited base. If the requester instead wants the fixes on both chains, that is a new Ethereum launch, and this finding stays open until they choose explicitly. Merged: audit_permissions 92552fd0.","line":43,"path":"src/TreasuryVault.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {VaultFactory} from \"src/VaultFactory.sol\";\n\n/// @notice The brief: \"Deploy the audited IMD Vault factory on Robinhood Chain, unchanged. This is the exact code\n///         audited and deployed on Ethereum mainnet in launch 2244 (VaultFactory at\n///         0xdbaff609cd0500527a2a102f5cbbbbc70fd2ff5b) ... Do not change any source file.\"\n///         The factory embeds the init code of VestingVault and TreasuryVault, so its runtime bytecode is a fingerprint\n///         of all three sources. Compiled from this tree with the Ethereum constructor arguments, the runtime must\n///         therefore equal the code at that mainnet address (18,760 bytes, keccak256\n///         0x4e60104bbd26773631103e40fbbf5713e12e2d485564c8ee7222fbe57e30f98a, read with `cast code` on 2026-10-19;\n///         the tree at the previous commit compiles to exactly those bytes). This tree's last commit changed\n///         src/TreasuryVault.sol and src/VestingVault.sol, so the Robinhood copy would deploy vaults whose code is not\n///         the audited mainnet code. Fails on this tree; passes once the audited sources are restored.\ncontract AuditedCodeTest is Test {\n    bytes32 constant MAINNET_FACTORY_CODEHASH = 0x4e60104bbd26773631103e40fbbf5713e12e2d485564c8ee7222fbe57e30f98a;\n    uint256 constant MAINNET_FACTORY_CODE_LENGTH = 18_760;\n\n    function test_TreeCompilesToTheAuditedMainnetFactory() public {\n        vm.chainId(1);\n        VaultFactory f = new VaultFactory(\n            IERC20(0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7), 0x75dF3d2E7a1d23021e5d7EF06312D13009E9BBb8\n        );\n        assertEq(address(f).code.length, MAINNET_FACTORY_CODE_LENGTH, \"runtime length differs from launch 2244\");\n        assertEq(address(f).codehash, MAINNET_FACTORY_CODEHASH, \"runtime differs from the audited mainnet factory\");\n    }\n}","reproduction":"1. On this tree: forge test --match-path test/scratch/Proof_92552fd050f7.t.sol. It deploys new VaultFactory(IERC20(0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7), 0x75dF3d2E7a1d23021e5d7EF06312D13009E9BBb8) on chain id 1 and asserts code length 18,760 and codehash 0x4e60104b...f98a. Actual: FAIL 'runtime length differs from launch 2244: 18984 != 18760'. 2. git archive ccfe47a | tar -x -C /tmp/prev; copy the same test there; forge test: PASS. 3. cast code 0xdbaff609cd0500527a2a102f5cbbbbc70fd2ff5b --rpc-url https://ethereum-rpc.publicnode.com returns 18,760 bytes whose keccak256 (cast keccak) is 0x4e60104bbd26773631103e40fbbf5713e12e2d485564c8ee7222fbe57e30f98a; cast call imd()(address) and feeRecipient()(address) return the launch 2244 arguments. 4. git diff ccfe47a HEAD -- src/ shows the edits. Expected per the brief: HEAD compiles to the mainnet runtime apart from the two immutables. Actual: it does not.","severity":"medium","snippet":"    uint256 public constant BALANCE_READ_GAS = 100_000;","title":"Tree is not the audited launch 2244 code: TreasuryVault.sol and VestingVault.sol were edited, so the Robinhood factory and every vault it creates run bytecode never deployed or audited on Ethereum"},{"citation":"resolved","description":"execute() reads the balance of every asset the manager ever queued before and after the transfer of w.asset and charges any fall against that asset's window with _charge, which reverts OverWeeklyCap when the fall exceeds the window. _trackedAssets only grows (queue, lines 163-166) and nothing removes an entry. The gas stipend and single-word copy in _tryBalance only neutralise an asset that reverts, burns gas or returns a huge payload; a cheap balanceOf that answers a value derived from the treasury's balance of another token is trusted. Such a token reports 1e40 while the treasury holds at least X of the project token and 0 once it holds less, so any project-token withdrawal that crosses X is seen as a fall of 1e40 in the tracked token, larger than any cap, and the whole execute reverts. The precondition is that the manager once queued the hostile token, which is the routine action of clearing an unsolicited airdrop, and queue() accepts any address. After that the token's deployer can freeze all ERC-20 withdrawals (ETH is unaffected) in every epoch, and the manager cannot cancel, untrack or route around it. README lines 38-40 state this as a trust assumption, but the NatSpec at lines 26-28 and 338-342 claims a tracked asset can never block the others, and the existing tests only cover gas-burning and payload-bombing assets, not a readable mirror. No funds are stolen; the project's ERC-20 treasury is frozen at an outsider's discretion. Fix options, the requester's call since it conflicts with finding 1's 'unchanged' instruction: (a) only honour the shared charge when it is consistent with a second entry point to the same balance (before[i] equals the pre-transfer balance of w.asset and fell equals w.amount), otherwise skip and log the asset; or (b) a manager path to announce and, after the delay, remove a tracked asset. Merged: audit_economics 57780e01.","line":212,"path":"src/TreasuryVault.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {VaultFactoryNoFee} from \"src/VaultFactoryNoFee.sol\";\nimport {TreasuryVault} from \"src/TreasuryVault.sol\";\n\ncontract ProjectToken is ERC20 {\n    constructor() ERC20(\"Project\", \"PRJ\") {}\n\n    function mint(address to, uint256 amount) external {\n        _mint(to, amount);\n    }\n}\n\n/// @dev A token that was airdropped to the treasury. Its `balanceOf` is cheap (well under BALANCE_READ_GAS) and\n///      returns a full word, so `_tryBalance` trusts it. Once `armed`, its reported balance for the treasury is the\n///      treasury's PRJ balance minus `floor`: it therefore \"falls\" by exactly the PRJ amount whenever PRJ leaves,\n///      and the window it opens is 25% of a number the attacker chose to be tiny.\ncontract AirdropToken {\n    string public constant name = \"Airdrop\";\n    string public constant symbol = \"AIR\";\n    uint8 public constant decimals = 18;\n\n    IERC20 public immutable watched;\n    address public immutable target;\n    bool public armed;\n    uint256 public floor;\n\n    constructor(IERC20 watched_, address target_) {\n        watched = watched_;\n        target = target_;\n    }\n\n    function arm(uint256 floor_) external {\n        armed = true;\n        floor = floor_;\n    }\n\n    /// @dev Armed: reports a huge balance while the treasury still holds at least `floor` PRJ, and zero once it\n    ///      holds less. Any PRJ withdrawal that takes the balance below `floor` is then seen as a fall of 1e40 in\n    ///      this token, more than any window can allow, so `execute` reverts with OverWeeklyCap.\n    function balanceOf(address account) external view returns (uint256) {\n        if (account != target || !armed) return 100e18;\n        return watched.balanceOf(target) >= floor ? 1e40 : 0;\n    }\n\n    function transfer(address, uint256) external pure returns (bool) {\n        return true;\n    }\n}\n\ncontract TrackedAssetFreezeTest is Test {\n    VaultFactoryNoFee factory;\n    TreasuryVault treasury;\n    ProjectToken prj;\n    AirdropToken air;\n\n    address manager = makeAddr(\"manager\");\n    address attacker = makeAddr(\"attacker\");\n    address payroll = makeAddr(\"payroll\");\n\n    function setUp() public {\n        vm.chainId(4663);\n        vm.warp(1_800_000_000);\n        factory = new VaultFactoryNoFee();\n        prj = new ProjectToken();\n        treasury = factory.createTreasury(address(prj), manager, 1 days, 2_500, new address[](0));\n        prj.mint(address(treasury), 1_000e18);\n        air = new AirdropToken(IERC20(address(prj)), address(treasury));\n    }\n\n    /// Finding: a tracked asset is never untracked, and its self-reported balance decides whether every other\n    /// ERC-20 withdrawal may execute. An attacker whose token the manager once queued (to clear an airdrop) can\n    /// later make every PRJ withdrawal revert with OverWeeklyCap, for as long as they like.\n    function test_TrackedAirdropTokenFreezesProjectTokenWithdrawals() public {\n        // 1. The manager clears the airdropped token once: queueing it is enough to track it forever.\n        vm.prank(manager);\n        uint256 airId = treasury.queue(address(air), payroll, 1e18);\n        vm.warp(block.timestamp + 1 days);\n        treasury.execute(airId);\n        assertTrue(treasury.isTracked(address(air)));\n\n        // 2. Weeks later the attacker arms the token against the treasury's current PRJ balance.\n        vm.warp(block.timestamp + 3 weeks);\n        vm.prank(attacker);\n        air.arm(prj.balanceOf(address(treasury)));\n\n        // 3. A routine PRJ withdrawal far under the weekly cap (10 of 1000, cap is 250).\n        vm.prank(manager);\n        uint256 id = treasury.queue(address(prj), payroll, 10e18);\n        vm.warp(block.timestamp + 1 days);\n        assertEq(treasury.availableThisWeek(address(prj)), 250e18);\n\n        // Expected: PRJ leaves, 10e18 charged against PRJ's window.\n        // Actual on this code: OverWeeklyCap from the AIR window, which the manager can neither close nor untrack.\n        treasury.execute(id);\n        assertEq(prj.balanceOf(payroll), 10e18, \"project token withdrawal must not depend on an airdropped token\");\n    }\n}","reproduction":"Chain id 4663. factory = new VaultFactoryNoFee(); treasury = factory.createTreasury(PRJ, manager, 1 days, 2500, []); mint 1000e18 PRJ to the treasury. AIR is a token whose balanceOf(treasury) returns 100e18 until armed and, once armed with floor F, returns 1e40 when PRJ.balanceOf(treasury) >= F and 0 otherwise; its transfer returns true. Step 1: manager queue(AIR, payroll, 1e18); warp +1 day; execute -> isTracked[AIR] is true for ever. Step 2: warp +3 weeks; attacker calls AIR.arm(1000e18). Step 3: manager queue(PRJ, payroll, 10e18); warp +1 day; availableThisWeek(PRJ) == 250e18. Expected: execute sends 10e18 PRJ to payroll and charges 10e18 against PRJ's window. Actual: before[AIR] = 1e40; after the PRJ transfer AIR reports 0; fell = 1e40; _charge(windows[AIR], 1e40, 1e40) opens the window at cap 2.5e39 and reverts OverWeeklyCap(2500000000000000000000000000000000000000). forge test --match-path test/scratch/Proof_57780e01ba29.t.sol fails with exactly that revert on this tree.","severity":"medium","snippet":"                _charge(windows[other], before[i], fell);","title":"A tracked ERC-20 whose balanceOf mirrors another balance can make every ERC-20 withdrawal of the treasury revert OverWeeklyCap for as long as its deployer wants; the set can never be shrunk"},{"citation":"resolved","description":"An open vault (token zero) records a token's arrival once, the first time sync(asset) or release(asset) runs while the balance is nonzero, and that record is the clock every later tranche of that token vests on (unlockStart = max(start + cliff, arrivedAt)). The threshold is any nonzero balance and sync is public, so a third party or the beneficiary holding one wei of a token that already exists can transfer it to the vault and call sync before the project's real tranche lands. The real tranche then counts as a calendar top-up and vests by the fraction elapsed since the dust, up to 100% on arrival. This defeats the documented guarantee at lines 23-25 ('a launch that slips past start + cliff still vests from its arrival, never on it') for every token that exists before it reaches the vault, which is the case for any Robinhood project using createLaunchVesting with an existing token or routing a second token into a launch vault. ADAPTATION.md's note that 'nobody can set the clock earlier with dust' only holds for a token minted in the launch transaction itself. The beneficiary can trigger it against their own lock, so a vault the radar shows as locked can release most of a tranche at once; nothing goes to a third party. No test in the suite sends dust before a tranche. The fix is a scope decision because the README's calendar top-up rule enables it: either account each arrival of an asset to an open vault as its own tranche with its own clock, or document that an open vault's clock is only trustworthy for a token minted in the funding transaction and have the radar refuse its seal otherwise. Merged: audit_flow 69712906 (medium), audit_economics c75e72d1 (low), audit_math eab2d0a7 (low); one root cause, kept at medium because it is a broken stated guarantee that an unprivileged actor triggers.","line":221,"path":"src/VestingVault.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 {VaultFactory} from \"src/VaultFactory.sol\";\nimport {VestingVault} from \"src/VestingVault.sol\";\n\ncontract DustToken is ERC20 {\n    constructor() ERC20(\"Dust\", \"DST\") {}\n    function mint(address to, uint256 amount) external { _mint(to, amount); }\n}\n\n/// An open vesting vault promises that a tranche landing after `start + cliff` vests from its arrival. A third\n/// party who holds one wei of the token can record the token's arrival earlier with dust and `sync`, so the real\n/// tranche is unlocked by the schedule fraction elapsed since the dust, not since it arrived.\ncontract ProofDustClockTest is Test {\n    DustToken token;\n    VaultFactory factory;\n    address team = makeAddr(\"team\");\n    address project = makeAddr(\"project\");\n    address attacker = makeAddr(\"attacker\");\n\n    function setUp() public {\n        vm.warp(1_760_000_000);\n        vm.chainId(4663);\n        token = new DustToken();\n        factory = new VaultFactory(IERC20(address(0)), address(0));\n        token.mint(project, 1_000_000e18);\n        token.mint(attacker, 1);\n    }\n\n    function test_RealTrancheVestsFromItsOwnArrivalDespiteEarlierDust() public {\n        uint64 start = uint64(block.timestamp);\n        vm.prank(project);\n        VestingVault v = factory.createLaunchVesting(team, start, 0, 28 days);\n\n        // Day 0: a stranger sends one wei and records the token's arrival.\n        vm.prank(attacker);\n        token.transfer(address(v), 1);\n        v.sync(address(token));\n\n        // Day 21: the project's real tranche lands after start + cliff.\n        vm.warp(block.timestamp + 21 days);\n        vm.prank(project);\n        token.transfer(address(v), 1_000_000e18);\n        v.sync(address(token));\n\n        // Expected: at most the dust is releasable; the tranche vests from day 21 and nothing of it is unlocked yet.\n        // Actual on this code: 21/28 of the tranche (750,000 tokens) is releasable in the same block.\n        assertLe(v.releasable(address(token)), 1, \"real tranche unlocked on arrival because dust started its clock\");\n    }\n}","reproduction":"Chain id 4663; factory = new VaultFactory(IERC20(0), address(0)); T0 = block.timestamp. project: v = factory.createLaunchVesting(team, T0, 0, 28 days). attacker (holds 1 wei of token): token.transfer(v, 1); v.sync(token) -> arrivedAt[token] = T0. warp T0 + 21 days. project: token.transfer(v, 1_000_000e18); v.sync(token) is a no-op. Expected: releasable(token) <= 1 (the tranche vests from day 21: 0 on arrival, 250_000e18 at day 28, as happens with no dust). Actual: releasable(token) = (1_000_000e18 + 1) * 21 / 28 = 750_000e18 + dust, and release(token) sends 750,000 tokens to team in the same block. forge test --match-path test/scratch/Proof_697129069e9a.t.sol fails with '750000000000000000000000 > 1'. Variant: dust at day 14, tranche at day 28 -> 500_000e18 releasable on landing; dust at day 0, tranche at day 28 or later -> the whole tranche releasable at once.","severity":"medium","snippet":"        if (IERC20(asset).balanceOf(address(this)) == 0) return false;","title":"Open vesting vault: anyone can start a pre-existing token's arrival clock with 1 wei, so a real tranche landing later is unlocked on arrival by the fraction of the schedule already elapsed"},{"citation":"resolved","description":"For every other tracked asset execute() measures the real balance drop and charges that (lines 209-212). For the asset being withdrawn it charges only w.amount, before the transfer, and never re-reads the balance afterwards. A token whose transfer debits the sender by more than amount (a burn or tax taken from the sender on top of the delivered amount) therefore lets more than rateBps of the balance leave in one epoch, and windows[asset].spent under-records what left. The destination receives at most the cap, so nothing goes to a wrong party; the broken piece is the vault's own 25% guarantee and the accuracy of spent and availableThisWeek for such tokens. Minimal fix preserving the design: read _balance(w.asset) after the transfer and charge before minus after, as the shared path already does. Merged: audit_math bf1563ce.","line":193,"path":"src/TreasuryVault.sol","reproduction":"Token SB: OpenZeppelin ERC20 whose _update burns an extra value/10 from the sender before delivering value. createTreasury(SB, manager, 1 days, 2500, []); mint 1000e18 SB to the vault. manager: queue(SB, dest, 250e18); warp +1 day; execute(id). Expected per the stated rule: at most 250e18 (25%) leaves this epoch and spent records the outflow. Actual: the vault balance falls from 1000e18 to 725e18 (27.5% left), windows[SB].cap == 250e18 and windows[SB].spent == 250e18. Confirmed by test/scratch/JudgeChecks.t.sol::test_SenderBurnTokenExceedsWeeklyCapOnWithdrawnAsset (logs: balance after 725e18, cap 250e18, spent 250e18).","severity":"low","snippet":"        _charge(windows[w.asset], _balance(w.asset), w.amount);","title":"Weekly cap charges the withdrawn asset its nominal amount, not its measured balance drop, so a token that debits the sender more than the amount lets more than rateBps leave per epoch"},{"citation":"resolved","description":"The vault's accounting is balance-based and tolerates fee-on-transfer tokens whose fee comes out of the delivered amount. It does not tolerate a token that charges the sender on top of the amount: once the schedule has fully elapsed, releasable(asset) equals the vault's balance exactly (vestedAmount returns balance + released, minus released), release() tries to transfer that entire balance, and the token reverts because the sender would need balance plus fee. There is no way to release a smaller amount, no recover path for the vested token (recover reverts VestingToken for it), and a top-up does not help because releasable is again the whole balance. The remaining tokens are permanently locked. Standard and classic fee-on-transfer tokens are unaffected; a bound vault whose token is later upgraded into such behaviour is. Minimal fix preserving the design: let the beneficiary release up to the releasable amount (release(asset, amount) with amount <= releasable). Merged: audit_math 0397a82a.","line":161,"path":"src/VestingVault.sol","reproduction":"Token SB as in finding 4. holder has 2000e18 SB, approves the factory and calls createVesting(SB, team, 1000e18, now, 0, 28 days); the vault holds 1000e18 (the factory's transferFrom burned 100e18 from the holder, not the vault). warp +28 days. releasable(SB) == 1000e18. Expected: release(SB) sends the vested tokens to team. Actual: release(SB) reverts with ERC20InsufficientBalance(vault, 1000e18, 1100e18); one year later it still reverts and the vault still holds 1000e18 with no function able to move them. Confirmed by test/scratch/JudgeChecks.t.sol::test_SenderBurnTokenFinalReleaseReverts.","severity":"low","snippet":"        IERC20(asset).safeTransfer(to, amount);","title":"release() always transfers the whole releasable amount, so a token that debits the sender more than the amount can never complete the final release and the remainder is stuck"},{"citation":"resolved","description":"The view answers from the asset's own window only. execute() additionally charges every other tracked asset whose balance fell, so for a second address to a balance whose main address's window is already spent this epoch, the view reports a positive amount while any withdrawal through the second address, even 1 wei, reverts OverWeeklyCap(0). Enforcement is correct (the shared cap works); only the radar-facing number is wrong, and only until the second address has been queued once. No funds at risk. Fix: document that the view ignores shared-balance charges, or expose a per-id canExecute simulation. Merged: audit_math 3aa7ee90.","line":311,"path":"src/TreasuryVault.sol","reproduction":"MainToken main; LegacyEntry legacy forwards balanceOf and transfer to main (one balance). createTreasury(main, manager, 1 days, 2500, []); mint 1000e18 main to the vault. queue(main, dest, 250e18); warp +1 day; execute -> balance 750e18, windows[main] spent 250e18 of 250e18. Now availableThisWeek(legacy) returns 187.5e18 (legacy is untracked, so 25% of 750e18). queue(legacy, dest, 1); warp +1 day; execute reverts OverWeeklyCap(0) because main's balance fell by 1 and its window has 0 available. Confirmed by test/scratch/JudgeChecks.t.sol::test_ViewOverstatesForUntrackedSecondEntry.","severity":"info","snippet":"        if (win.epoch != uint64(block.timestamp / EPOCH)) return _cap(_balance(asset));","title":"availableThisWeek() overstates what execute() will allow for an untracked second entry point to a balance whose main address's window is exhausted"},{"citation":"resolved","description":"The brief says to deploy exactly src/VaultFactory.sol with imd_ and feeRecipient_ both zero and to add no source file. launch.json lists VaultFactoryNoFee with constructorArgs [], an 18-line subclass in src/VaultFactoryNoFee.sol whose constructor passes the two zero words, added because the manifest schema refuses the zero address as a constructor word outside a Currency field. I verified with forge inspect that the deployed bytecode of VaultFactoryNoFee is byte for byte that of VaultFactory (37,970 hex characters, identical, 18,984 bytes) and in test/scratch/JudgeChecks.t.sol that address(new VaultFactoryNoFee()).code equals address(new VaultFactory(IERC20(0), address(0))).code on chain id 4663, imd() and feeRecipient() are zero, payFor reverts NoFeeOnThisChain and createLaunchVesting registers a vault with no token call. The runtime contains, outside push data, no DELEGATECALL, CALLCODE, SELFDESTRUCT, TLOAD, TSTORE or MCOPY, only PUSH0, so it needs Shanghai and nothing from Cancun. launch.json has exactly kind, contracts and notes, the name is 17 characters, notes are 3,011 characters, and the empty argument list matches the zero-parameter constructor. What differs from the brief: the explorer-verified contract name and ABI constructor will be VaultFactoryNoFee's, its creation code is not the Ethereum factory's, and a source file was added. Not a behaviour defect; recorded so the requester accepts the deviation knowingly, or lets the schema take the zero word for these two arguments and deploys VaultFactory directly. Merged: audit_permissions 04f46168 and audit_flow 24b49607 (same finding from two areas).","line":5,"path":"launch.json","reproduction":"Read launch.json: contracts[0].contract is 'VaultFactoryNoFee' with constructorArgs []; the brief names src/VaultFactory.sol with [0x0000000000000000000000000000000000000000, 0x0000000000000000000000000000000000000000]. forge inspect VaultFactory deployedBytecode equals forge inspect VaultFactoryNoFee deployedBytecode; forge inspect VaultFactoryNoFee abi shows a constructor with no inputs. test/scratch/JudgeChecks.t.sol::test_NoFeeRuntimeEqualsBaseWithZeroArgs passes. Expected per the brief: VaultFactory with two zero words; actual: a subclass with [].","severity":"info","snippet":"      \"contract\": \"VaultFactoryNoFee\",","title":"Manifest deploys the subclass VaultFactoryNoFee with no arguments instead of src/VaultFactory.sol with two zero arguments as the brief states; runtime identical, creation code, ABI and verified name d"}],"hash":"f2e643172eec6547fb466bca0daa6273f683dac718cc92c9b391a06991e01da4","nodeId":"92e381a7-9926-48f3-893e-ab1d6ca46f9c","outcome":"completed","summary":"The findings file is written, every snippet matches the tree, and all 27 entry points plus four invariant rows are covered. Review complete.\n\n**Outcome:** seven findings in `.imd-findings.json`, three of them medium with proofs that fail on this tree, after merging the specialists' eleven reports and dropping one.\n\n**Kept, reproduced and recalibrated**\n\n- **Medium, source changed from the audited code.** I fetched the launch 2244 factory from a public Ethereum RPC. Its runtime is 18,760 bytes with the hash the specialist quoted, and commit ccfe47a compiles to exactly those bytes. HEAD compiles to 18,984 bytes because commit 98fd654 edited both vault sources, which the brief forbade. Proof attached and confirmed.\n- **Medium, tracked-asset freeze.** A token the manager once queued can report a mirrored balance and make every ERC-20 withdrawal revert OverWeeklyCap indefinitely. The tracked set can never shrink. The README admits this as a trust assumption, but the NatSpec still promises no tracked asset can block the others. Proof attached and confirmed.\n- **Medium, dust-seeded arrival clock.** Three specialists reported the same root cause at three severities. Merged into one at medium: anyone with 1 wei of a pre-existing token starts its clock on an open vault, so a later real tranche is partly or fully unlocked on landing. Proof attached and confirmed.\n- **Low, two sender-burn token edges.** The withdrawn asset is charged its nominal amount rather than the measured drop, and the final full-balance release reverts forever. Both reproduced in my own scratch test.\n- **Info, view mismatch and manifest naming.** The availableThisWeek view overstates for an untracked second entry point. The two duplicate manifest reports are merged into one info item: VaultFactoryNoFee has a runtime byte-identical to VaultFactory with zero arguments, but the verified name, ABI and creation code differ from the brief's wording.\n\n**Dropped:** the cap-rounds-to-zero note. It reproduces but was settled and documented in the README in an earlier round.\n\n**My own pass found nothing further.** The runtime contains only PUSH0 beyond base opcodes, so nothing Cancun-only or forbidden. The manifest has the right keys, name length and argument count. The full suite passes except the three specialist proofs I copied under test/scratch, which fail for the stated reasons.\n\n**For the requester:** findings 1 and 2 conflict. Restoring the audited sources honours the brief but keeps the freeze path and the dust clock, which exist on mainnet today. Fixing them means both chains need a new launch. That choice is theirs.","treeHash":null,"usage":{"cachedInputTokens":738132,"inputTokens":226,"model":"claude-fable-5-1","outputTokens":40878,"runtime":"claude","turns":28,"wallClockMs":459782}}],"verification":[{"checks":[{"durationMs":4583,"exitCode":0,"name":"build","output":"Compiling 42 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.51s\nCompiler run successful!\n","passed":true},{"durationMs":8780,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Fork.t.sol:ForkTest\n[PASS] test_RealImdFeeAndVesting() (gas: 3689)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 229.68µs (65.37µs CPU time)\n\nRan 9 tests for test/SharedCap.t.sol:SharedCapEdgeTest\n[PASS] testFuzz_ExpiryNeverMovesWithTheDelay(uint64,uint64,uint256) (runs: 512, μ: 2313726, ~: 2298557)\n[PASS] test_CreatorIsRecordedForEveryPathAndFreeVaultsStillCarryIt() (gas: 4553862)\n[PASS] test_ExpiryIsInclusiveAndOneSecondLaterRefused() (gas: 618758)\n[PASS] test_IncreaseDelayShortensButKeepsAnOpenWindow() (gas: 440014)\n[PASS] test_PendingManagerEtaResetsOnReproposeAndClearsOnCancel() (gas: 222843)\n[PASS] test_QueueingTheSameAssetTwiceTracksItOnceAndEthNever() (gas: 689893)\n[PASS] test_SharedChargeOpensTheOtherWindowAtTheLowerRate() (gas: 981250)\n[PASS] test_SharedWindowSpansEpochsCorrectly() (gas: 812676)\n[PASS] test_UnreadableTrackedAssetIsSkippedEitherWay() (gas: 1485134)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 857.08ms (871.09ms CPU time)\n\nRan 2 tests for test/SharedCap.t.sol:SharedCapInvariantTest\n[PASS]\nSharedCapInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_sharedCap\n SharedCapInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+---------------+-------+---------+----------╮\n| Contract         | Selector      | Calls | Reverts | Discards |\n+===============================================================+\n| SharedCapHandler | cancel        | 2310  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | decreaseRate  | 2284  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | deposit       | 2289  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | execute       | 2370  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | increaseDelay | 2412  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | queue         | 2372  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | warp          | 2347  | 0       | 0        |\n╰------------------+---------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesBothSidesOfTheSharedCap() (gas: 1451014)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.52s (1.52s CPU time)\n\nRan 25 tests for test/EdgeCases.t.sol:EdgeCasesTest\n[PASS] testFuzz_ReleasedNeverExceedsDeposited(uint96,uint32[6],uint32) (runs: 512, μ: 1961688, ~: 1976136)\n[PASS] test_BeneficiaryHandoverAtTheExactSecondAndAfter() (gas: 1638487)\n[PASS] test_CancelledProposalCanNotBeAccepted() (gas: 2142459)\n[PASS] test_CreateTreasuryRejectsBadArguments() (gas: 2523801)\n[PASS] test_CreateVestingRejectsZeroAmountAndZeroBeneficiary() (gas: 286042)\n[PASS] test_DuplicateDestinationsAreListedOnce() (gas: 2240121)\n[PASS] test_DurationFloorIsExactlyFourWeeks() (gas: 1478893)\n[PASS] test_FeeFailureLeavesNothingBehind() (gas: 3112130)\n[PASS] test_FeeIsChargedToTheCallerOnlyOnceEvenAcrossKinds() (gas: 4436074)\n[PASS] test_NothingAtTheCliffSecondThenLinear() (gas: 1408324)\n[PASS] test_OnlyTheBeneficiaryChangesTheSchedule() (gas: 1389279)\n[PASS] test_OpenVaultReleaseBeforeAnythingArrivesReverts() (gas: 1298798)\n[PASS] test_PayForRejectsZeroTokenAndSecondPayment() (gas: 166042)\n[PASS] test_PayForThisChainCoversTheFirstVault() (gas: 1392806)\n[PASS] test_ReleaseTwiceInOneBlockPaysOnce() (gas: 1436148)\n[PASS] test_SyncIsANoOpOnABoundVaultAndRefusesOtherAssets() (gas: 1372320)\n[PASS] test_TreasuryCapExactlyAvailableThenOneWeiOver() (gas: 2759403)\n[PASS] test_TreasuryDelayAndGraceBoundaries() (gas: 2740954)\n[PASS] test_TreasuryDepositAndQueueRejectZero() (gas: 2117614)\n[PASS] test_TreasuryDestinationRulesFailurePaths() (gas: 2307670)\n[PASS] test_TreasuryEthToARefusingDestinationRollsBackAndCanRetry() (gas: 2718142)\n[PASS] test_TreasuryLastSecondOfGraceExecutes() (gas: 2450717)\n[PASS] test_TreasuryManagerHandoverFailurePaths() (gas: 2327925)\n[PASS] test_TreasuryRuleBoundsAtTheEdges() (gas: 2293639)\n[PASS] test_TreasuryUnknownIdsRevert() (gas: 2118538)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 1.66s (1.67s CPU time)\n\nRan 4 tests for test/Invariants.t.sol:TreasuryInvariantTest\n[PASS] invariant_conservation() (runs: 128, calls: 8192, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 769   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 754   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 729   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 689   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 668   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 761   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 818   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 730   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 737   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 770   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 767   | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] invariant_rulesBoundedAndStricter() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 1508  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 1513  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 1474  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 1440  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 1427  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 1522  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 1553  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 1468  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 1409  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 1558  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 1512  | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] invariant_windowNeverOverspent() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 1508  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 1513  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 1474  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 1440  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 1427  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 1522  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 1553  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 1468  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 1409  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 1558  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 1512  | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesExecution() (gas: 1283085)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.66s (3.54s CPU time)\n\nRan 28 tests for test/Audit.t.sol:AuditTest\n[PASS] testFuzz_LateArrivalNeverUnlocksMoreThan25PercentInAWeek(uint256,uint64,uint64,uint64,uint64) (runs: 512, μ: 1406486, ~: 1410821)\n[PASS] testFuzz_VestingNeverMoreThan25PercentInFirstWeek(uint64,uint64,uint64) (runs: 512, μ: 161969, ~: 157393)\n[PASS] test_ArrivalInsideTheCliffKeepsTheSchedule() (gas: 1402833)\n[PASS] test_BoundVaultHasNoArrivalClock() (gas: 1378350)\n[PASS] test_EachTokenInAnOpenVaultHasItsOwnClock() (gas: 1535026)\n[PASS] test_EarlyExecuteBeforeADepositOnlyDelaysToTheNextEpoch() (gas: 2800295)\n[PASS] test_ExtendDurationCannotOverflowTheSchedule() (gas: 1345094)\n[PASS] test_FactoryRecordsTheCreatorOfEveryVault() (gas: 4478729)\n[PASS] test_FirstReleaseOfANewTokenOnlyStartsItsClock() (gas: 1482328)\n[PASS] test_IncreaseDelayNeverRevivesAnExpiredWithdrawal() (gas: 2429868)\n[PASS] test_IncreaseDelayPastTheExpiryClosesAQueuedWithdrawal() (gas: 2675623)\n[PASS] test_LateArrivalVestsFromArrival() (gas: 1636748)\n[PASS] test_NoFeeFactoryCreatesVaultsAndTakesNoFeeOnRobinhood() (gas: 8959501)\n[PASS] test_NoFeeFactoryIsTheAuditedFactoryWithZeroArguments() (gas: 61685)\n[PASS] test_OpenVaultRecoverAlwaysRefuses() (gas: 1304167)\n[PASS] test_PendingManagerFollowsTheCurrentDelay() (gas: 2205226)\n[PASS] test_RecoverRefusesASecondEntryPointToTheVestedToken() (gas: 2442231)\n[PASS] test_RobinhoodFactoryWithZeroArgumentsCreatesVaultsAndTakesNoFee() (gas: 5517953)\n[PASS] test_SyncIsIdempotentAndNeedsABalance() (gas: 1459500)\n[PASS] test_TreasuryCapIsSharedByTwoEntryPointsToOneBalance() (gas: 4153961)\n[PASS] test_TreasuryCapThroughLegacyAddressFirstIsSharedToo() (gas: 3602827)\n[PASS] test_TreasuryConstructorEnforcesMinDelay() (gas: 8624)\n[PASS] test_TreasuryEpochBoundaryBoundIsRatePlusRateOfRemainder() (gas: 2855323)\n[PASS] test_TreasuryTrackedAssetsNeverBlockOtherWithdrawals() (gas: 3379825)\n[PASS] test_TreeCompilesToTheAuditedMainnetFactory() (gas: 4546)\n[PASS] test_VestingCreatedLogsWhatTheVaultReceived() (gas: 1971659)\n[PASS] test_VestingRejectsOverflowingSchedule() (gas: 1683748)\n[PASS] test_VestingRejectsStartInThePast() (gas: 2895533)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 2.94s (73.42ms CPU time)\n\nRan 38 tests for test/Vault.t.sol:VaultTest\n[PASS] testFuzz_TreasuryNeverExceedsWeeklyCap(uint256,uint16,uint256[5]) (runs: 512, μ: 3254982, ~: 3270426)\n[PASS] testFuzz_VestingNeverOverpays(uint256,uint64,uint64,uint32[4]) (runs: 512, μ: 1597262, ~: 1563724)\n[PASS] testFuzz_VestingNeverUnlocksMoreThan25PercentInAWeek(uint256,uint64,uint64,uint64) (runs: 512, μ: 1315516, ~: 1318933)\n[PASS] test_BoundVestingRejectsOtherTokens() (gas: 1377547)\n[PASS] test_CreateVestingNeedsAToken() (gas: 36022)\n[PASS] test_EachLaunchVaultPaysItsOwnFee() (gas: 4454024)\n[PASS] test_FactoryBurnsFeeOncePerProjectAndRegisters() (gas: 3289130)\n[PASS] test_FactoryChargesEachNewProject() (gas: 3816278)\n[PASS] test_FactoryNeedsRadarWalletWhenCharging() (gas: 6067)\n[PASS] test_FactoryRejectsShortDelay() (gas: 36613)\n[PASS] test_FactoryRevertsWithoutImd() (gas: 1915886)\n[PASS] test_FeeGoesToRadarNotBurned() (gas: 1301718)\n[PASS] test_FreeFactoryOnChainsWithoutImd() (gas: 1354185)\n[PASS] test_LaunchTreasuryReceivesRemainder() (gas: 3072408)\n[PASS] test_LaunchVestingReceivesRemainderAndVests() (gas: 2106028)\n[PASS] test_LaunchVestingVestsEveryTokenAndHasNoRecover() (gas: 2255256)\n[PASS] test_PayForRobinhoodProject() (gas: 149438)\n[PASS] test_RobinhoodLaunchVaultPaidOnEthereum() (gas: 1275945)\n[PASS] test_TreasuryCancelAndExpiry() (gas: 2521705)\n[PASS] test_TreasuryCapIgnoresDepositsWithinEpoch() (gas: 2669294)\n[PASS] test_TreasuryDelayAndAnyoneExecutes() (gas: 2600089)\n[PASS] test_TreasuryDestinationAllowlist() (gas: 2136658)\n[PASS] test_TreasuryEth() (gas: 2409792)\n[PASS] test_TreasuryManagerChangeIsDelayed() (gas: 2182109)\n[PASS] test_TreasuryOnlyManagerQueues() (gas: 2061089)\n[PASS] test_TreasuryRateCappedAt25Percent() (gas: 2117378)\n[PASS] test_TreasuryRulesOnlyTighten() (gas: 2466036)\n[PASS] test_TreasuryTighteningHoldsQueued() (gas: 2777026)\n[PASS] test_TreasuryWeeklyCap() (gas: 2734436)\n[PASS] test_VestingBeneficiaryCancel() (gas: 1388662)\n[PASS] test_VestingBeneficiaryChangeIsDelayed() (gas: 1519757)\n[PASS] test_VestingCliffThenLinear() (gas: 1600659)\n[PASS] test_VestingExtendAfterReleaseNeverUnderflows() (gas: 1416029)\n[PASS] test_VestingFeeOnTransferToken() (gas: 2072283)\n[PASS] test_VestingOnlyExtends() (gas: 1381714)\n[PASS] test_VestingRecoverOtherTokensOnly() (gas: 1436467)\n[PASS] test_VestingRejectsFasterThan25PercentAWeek() (gas: 1430192)\n[PASS] test_VestingTopUpFollowsSchedule() (gas: 1335236)\nSuite result: ok. 38 passed; 0 failed; 0 skipped; finished in 2.94s (3.04s CPU time)\n\nRan 6 tests for test/Invariants.t.sol:VestingInvariantTest\n[PASS] invariant_conservation() (runs: 128, calls: 8192, reverts: 1)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 876   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 928   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 869   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 920   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 946   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 935   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 883   | 1       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 942   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 893   | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_immutables() (runs: 256, calls: 16384, reverts: 3)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 3       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_noEarlyRelease() (runs: 256, calls: 16384, reverts: 3)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 3       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_openVaultClock() (runs: 256, calls: 16384, reverts: 3)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 3       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_schedule() (runs: 256, calls: 16384, reverts: 3)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 3       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesRelease() (gas: 1070291)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 2.94s (7.21s CPU time)\n\nRan 1 test for test/Invariants.t.sol:FactoryInvariantTest\n[PASS] invariant_feesAndRegistry() (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------+---------------------+-------+---------+----------╮\n| Contract       | Selector            | Calls | Reverts | Discards |\n+===================================================================+\n| FactoryHandler | createLaunchVesting | 4026  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | createTreasury      | 4156  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | createVesting       | 4051  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | payFor              | 4151  | 0       | 0        |\n╰----------------+---------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.69s (8.69s CPU time)\n\nRan 9 test suites in 8.69s (23.20s CPU time): 114 tests passed, 0 failed, 0 skipped (114 total tests)\n","passed":true},{"durationMs":51,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"TreasuryVault.acceptManager()\",\"TreasuryVault.cancel(uint256)\",\"TreasuryVault.cancelManager()\",\"TreasuryVault.decreaseRate(uint16)\",\"TreasuryVault.deposit(address,uint256)\",\"TreasuryVault.execute(uint256)\",\"TreasuryVault.increaseDelay(uint64)\",\"TreasuryVault.proposeManager(address)\",\"TreasuryVault.queue(address,address,uint256)\",\"TreasuryVault.receive()\",\"TreasuryVault.removeDestination(address)\",\"TreasuryVault.restrictDestinations(address[])\",\"VaultFactory.createLaunchVesting(address,uint64,uint64,uint64)\",\"VaultFactory.createTreasury(address,address,uint64,uint16,address[])\",\"VaultFactory.createVesting(address,address,uint256,uint64,uint64,uint64)\",\"VaultFactory.payFor(uint256,address)\",\"VaultFactoryNoFee.createLaunchVesting(address,uint64,uint64,uint64)\",\"VaultFactoryNoFee.createTreasury(address,address,uint64,uint16,address[])\",\"VaultFactoryNoFee.createVesting(address,address,uint256,uint64,uint64,uint64)\",\"VaultFactoryNoFee.payFor(uint256,address)\",\"VestingVault.acceptBeneficiary()\",\"VestingVault.cancelBeneficiary()\",\"VestingVault.extendDuration(uint64)\",\"VestingVault.proposeBeneficiary(address)\",\"VestingVault.recover(address)\",\"VestingVault.release(address)\",\"VestingVault.sync(address)\"],\"files\":{\".gitignore\":6,\"ADAPTATION.md\":164,\"README.md\":144,\"SWARM_BRIEF.md\":131,\"foundry.toml\":23,\"launch.json\":10,\"src/TreasuryVault.sol\":354,\"src/VaultFactory.sol\":165,\"src/VaultFactoryNoFee.sol\":18,\"src/VestingVault.sol\":224,\"test/Audit.t.sol\":707,\"test/EdgeCases.t.sol\":514,\"test/Fork.t.sol\":37,\"test/Invariants.t.sol\":761,\"test/SharedCap.t.sol\":477,\"test/Vault.t.sol\":615},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":994,"exitCode":0,"name":"slither","output":"[medium/high] incorrect-equality at src/VestingVault.sol:217: VestingVault._sync(address) (src/VestingVault.sol#217-223) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/TreasuryVault.sol:284: TreasuryVault.pendingManagerEta() (src/TreasuryVault.sol#284-287) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/VestingVault.sol:149: VestingVault.release(address) (src/VestingVault.sol#149-161) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/VestingVault.sol:120: VestingVault.vestedAmount(address,uint64) (src/VestingVault.sol#120-127) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/VestingVault.sol:111: VestingVault.unlockStart(address) (src/VestingVault.sol#111-117) uses a dangerous strict equality:\n[low/medium] missing-zero-check at src/TreasuryVault.sol:120: TreasuryVault.constructor(address,address,uint64,uint16,address[]).projectToken_ (src/TreasuryVault.sol#120) lacks a zero-check on :\n[low/medium] calls-loop at src/TreasuryVault.sol:334: TreasuryVault._tryBalance(address) (src/TreasuryVault.sol#334-339) has external calls inside a loop: (ok,data) = asset.staticcall(abi.encodeWithSelector(IERC20.balanceOf.selector,address(this))) (src/TreasuryVault.sol#335-336)\n[low/medium] timestamp at src/VestingVault.sol:120: VestingVault.vestedAmount(address,uint64) (src/VestingVault.sol#120-127) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:149: VestingVault.release(address) (src/VestingVault.sol#149-161) uses timestamp for comparisons\n[low/medium] timestamp at src/TreasuryVault.sol:343: TreasuryVault._charge(TreasuryVault.Window,uint256,uint256) (src/TreasuryVault.sol#343-353) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:189: VestingVault.acceptBeneficiary() (src/VestingVault.sol#189-198) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:217: VestingVault._sync(address) (src/VestingVault.sol#217-223) uses timestamp for comparisons\n[low/medium] timestamp at src/TreasuryVault.sol:284: TreasuryVault.pendingManagerEta() (src/TreasuryVault.sol#284-287) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:139: VestingVault.releasable(address) (src/VestingVault.sol#139-144) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:111: VestingVault.unlockStart(address) (src/VestingVault.sol#111-117) uses timestamp for comparisons\n[low/medium] timestamp at src/TreasuryVault.sol:269: TreasuryVault.acceptManager() (src/TreasuryVault.sol#269-279) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:75: VestingVault.constructor(IERC20,address,uint64,uint64,uint64,uint64) (src/VestingVault.sol#75-95) uses timestamp for comparisons\n[low/medium] timestamp at src/TreasuryVault.sol:179: TreasuryVault.execute(uint256) (src/TreasuryVault.sol#179-211) uses timestamp for comparisons\n[low/medium] timestamp at src/TreasuryVault.sol:303: TreasuryVault.availableThisWeek(address) (src/TreasuryVault.sol#303-307) uses timestamp for comparisons\n[low/medium] timestamp at src/TreasuryVault.sol:323: TreasuryVault._get(uint256) (src/TreasuryVault.sol#323-326) uses timestamp for comparisons","passed":true},{"durationMs":326,"exitCode":0,"name":"aderyn","output":"[low] costly-loop at src/TreasuryVault.sol:200: Costly operations inside loop (3 places)\n[low] large-numeric-literal at src/TreasuryVault.sol:35: Large Numeric Literal\n[low] require-revert-in-loop at src/TreasuryVault.sol:200: Loop Contains `require`/`revert` (2 places)\n[low] unchecked-return at src/VestingVault.sol:135: Unchecked Return\n[low] uninitialized-local-variable at src/TreasuryVault.sol:200: Uninitialized Local Variable (2 places)","passed":true},{"durationMs":1241,"exitCode":0,"name":"proof 8094ebd9e169","output":"Compiling 33 files with Solc 0.8.26\nSolc 0.8.26 finished in 779.06ms\nCompiler run successful!\n\nRan 1 test for test/imd-proof-c5c798ac/Proof_8094ebd9e169.t.sol:AuditedCodeTest\n[PASS] test_TreeCompilesToTheAuditedMainnetFactory() (gas: 4430)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 321.57µs (219.09µs CPU time)\n\nRan 1 test suite in 2.38ms (321.57µ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":"0e46e460333e17705dcd3c41012bb3482426b50d10ff89c84d69689be28b4f9f","verifiedTreeHash":"17d9e64b8c9a3f02582ceeb29be1187009b5e914","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":5868,"exitCode":0,"name":"build","output":"Compiling 45 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.76s\nCompiler run successful!\n","passed":true},{"durationMs":9241,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Fork.t.sol:ForkTest\n[PASS] test_RealImdFeeAndVesting() (gas: 3689)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 306.65µs (97.47µs CPU time)\n\nRan 9 tests for test/SharedCap.t.sol:SharedCapEdgeTest\n[PASS] testFuzz_ExpiryNeverMovesWithTheDelay(uint64,uint64,uint256) (runs: 512, μ: 2328528, ~: 2313589)\n[PASS] test_CreatorIsRecordedForEveryPathAndFreeVaultsStillCarryIt() (gas: 4583926)\n[PASS] test_ExpiryIsInclusiveAndOneSecondLaterRefused() (gas: 618825)\n[PASS] test_IncreaseDelayShortensButKeepsAnOpenWindow() (gas: 440081)\n[PASS] test_PendingManagerEtaResetsOnReproposeAndClearsOnCancel() (gas: 222843)\n[PASS] test_QueueingTheSameAssetTwiceTracksItOnceAndEthNever() (gas: 689893)\n[PASS] test_SharedChargeOpensTheOtherWindowAtTheLowerRate() (gas: 980051)\n[PASS] test_SharedWindowSpansEpochsCorrectly() (gas: 811544)\n[PASS] test_UnreadableTrackedAssetIsSkippedEitherWay() (gas: 1484017)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 56.23ms (58.69ms CPU time)\n\nRan 2 tests for test/SharedCap.t.sol:SharedCapInvariantTest\n[PASS]\nSharedCapInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_sharedCap\n SharedCapInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+---------------+-------+---------+----------╮\n| Contract         | Selector      | Calls | Reverts | Discards |\n+===============================================================+\n| SharedCapHandler | cancel        | 2310  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | decreaseRate  | 2284  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | deposit       | 2289  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | execute       | 2370  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | increaseDelay | 2412  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | queue         | 2372  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | warp          | 2347  | 0       | 0        |\n╰------------------+---------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesBothSidesOfTheSharedCap() (gas: 1450669)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.78s (1.78s CPU time)\n\nRan 25 tests for test/EdgeCases.t.sol:EdgeCasesTest\n[PASS] testFuzz_ReleasedNeverExceedsDeposited(uint96,uint32[6],uint32) (runs: 512, μ: 1992933, ~: 2006422)\n[PASS] test_BeneficiaryHandoverAtTheExactSecondAndAfter() (gas: 1668384)\n[PASS] test_CancelledProposalCanNotBeAccepted() (gas: 2157491)\n[PASS] test_CreateTreasuryRejectsBadArguments() (gas: 2538879)\n[PASS] test_CreateVestingRejectsZeroAmountAndZeroBeneficiary() (gas: 286116)\n[PASS] test_DuplicateDestinationsAreListedOnce() (gas: 2255175)\n[PASS] test_DurationFloorIsExactlyFourWeeks() (gas: 1508835)\n[PASS] test_FeeFailureLeavesNothingBehind() (gas: 3157030)\n[PASS] test_FeeIsChargedToTheCallerOnlyOnceEvenAcrossKinds() (gas: 4510846)\n[PASS] test_NothingAtTheCliffSecondThenLinear() (gas: 1438296)\n[PASS] test_OnlyTheBeneficiaryChangesTheSchedule() (gas: 1419151)\n[PASS] test_OpenVaultReleaseBeforeAnythingArrivesReverts() (gas: 1328666)\n[PASS] test_PayForRejectsZeroTokenAndSecondPayment() (gas: 166042)\n[PASS] test_PayForThisChainCoversTheFirstVault() (gas: 1422674)\n[PASS] test_ReleaseTwiceInOneBlockPaysOnce() (gas: 1466070)\n[PASS] test_SyncIsANoOpOnABoundVaultAndRefusesOtherAssets() (gas: 1402192)\n[PASS] test_TreasuryCapExactlyAvailableThenOneWeiOver() (gas: 2774697)\n[PASS] test_TreasuryDelayAndGraceBoundaries() (gas: 2756053)\n[PASS] test_TreasuryDepositAndQueueRejectZero() (gas: 2132646)\n[PASS] test_TreasuryDestinationRulesFailurePaths() (gas: 2322635)\n[PASS] test_TreasuryEthToARefusingDestinationRollsBackAndCanRetry() (gas: 2733373)\n[PASS] test_TreasuryLastSecondOfGraceExecutes() (gas: 2465816)\n[PASS] test_TreasuryManagerHandoverFailurePaths() (gas: 2342957)\n[PASS] test_TreasuryRuleBoundsAtTheEdges() (gas: 2308671)\n[PASS] test_TreasuryUnknownIdsRevert() (gas: 2133570)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 1.78s (81.14ms CPU time)\n\nRan 4 tests for test/Invariants.t.sol:TreasuryInvariantTest\n[PASS] invariant_conservation() (runs: 128, calls: 8192, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 769   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 754   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 729   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 689   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 668   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 761   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 818   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 730   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 737   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 770   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 767   | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] invariant_rulesBoundedAndStricter() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 1508  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 1513  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 1474  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 1440  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 1427  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 1522  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 1553  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 1468  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 1409  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 1558  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 1512  | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] invariant_windowNeverOverspent() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 1508  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 1513  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 1474  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 1440  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 1427  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 1522  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 1553  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 1468  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 1409  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 1558  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 1512  | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesExecution() (gas: 1283283)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.78s (3.84s CPU time)\n\nRan 38 tests for test/Vault.t.sol:VaultTest\n[PASS] testFuzz_TreasuryNeverExceedsWeeklyCap(uint256,uint16,uint256[5]) (runs: 512, μ: 3269204, ~: 3278231)\n[PASS] testFuzz_VestingNeverOverpays(uint256,uint64,uint64,uint32[4]) (runs: 512, μ: 1624948, ~: 1593098)\n[PASS] testFuzz_VestingNeverUnlocksMoreThan25PercentInAWeek(uint256,uint64,uint64,uint64) (runs: 512, μ: 1345410, ~: 1348875)\n[PASS] test_BoundVestingRejectsOtherTokens() (gas: 1407419)\n[PASS] test_CreateVestingNeedsAToken() (gas: 36022)\n[PASS] test_EachLaunchVaultPaysItsOwnFee() (gas: 4528792)\n[PASS] test_FactoryBurnsFeeOncePerProjectAndRegisters() (gas: 3334034)\n[PASS] test_FactoryChargesEachNewProject() (gas: 3861182)\n[PASS] test_FactoryNeedsRadarWalletWhenCharging() (gas: 6067)\n[PASS] test_FactoryRejectsShortDelay() (gas: 36613)\n[PASS] test_FactoryRevertsWithoutImd() (gas: 1930918)\n[PASS] test_FeeGoesToRadarNotBurned() (gas: 1331590)\n[PASS] test_FreeFactoryOnChainsWithoutImd() (gas: 1384053)\n[PASS] test_LaunchTreasuryReceivesRemainder() (gas: 3087508)\n[PASS] test_LaunchVestingReceivesRemainderAndVests() (gas: 2135921)\n[PASS] test_LaunchVestingVestsEveryTokenAndHasNoRecover() (gas: 2285174)\n[PASS] test_PayForRobinhoodProject() (gas: 149438)\n[PASS] test_RobinhoodLaunchVaultPaidOnEthereum() (gas: 1305813)\n[PASS] test_TreasuryCancelAndExpiry() (gas: 2536737)\n[PASS] test_TreasuryCapIgnoresDepositsWithinEpoch() (gas: 2684393)\n[PASS] test_TreasuryDelayAndAnyoneExecutes() (gas: 2615189)\n[PASS] test_TreasuryDestinationAllowlist() (gas: 2151691)\n[PASS] test_TreasuryEth() (gas: 2424892)\n[PASS] test_TreasuryManagerChangeIsDelayed() (gas: 2197141)\n[PASS] test_TreasuryOnlyManagerQueues() (gas: 2076121)\n[PASS] test_TreasuryRateCappedAt25Percent() (gas: 2132428)\n[PASS] test_TreasuryRulesOnlyTighten() (gas: 2481134)\n[PASS] test_TreasuryTighteningHoldsQueued() (gas: 2792125)\n[PASS] test_TreasuryWeeklyCap() (gas: 2749602)\n[PASS] test_VestingBeneficiaryCancel() (gas: 1418534)\n[PASS] test_VestingBeneficiaryChangeIsDelayed() (gas: 1549654)\n[PASS] test_VestingCliffThenLinear() (gas: 1630631)\n[PASS] test_VestingExtendAfterReleaseNeverUnderflows() (gas: 1445951)\n[PASS] test_VestingFeeOnTransferToken() (gas: 2102155)\n[PASS] test_VestingOnlyExtends() (gas: 1411611)\n[PASS] test_VestingRecoverOtherTokensOnly() (gas: 1466339)\n[PASS] test_VestingRejectsFasterThan25PercentAWeek() (gas: 1460103)\n[PASS] test_VestingTopUpFollowsSchedule() (gas: 1365133)\nSuite result: ok. 38 passed; 0 failed; 0 skipped; finished in 1.78s (1.92s CPU time)\n\nRan 32 tests for test/Audit.t.sol:AuditTest\n[PASS] testFuzz_LateArrivalNeverUnlocksMoreThan25PercentInAWeek(uint256,uint64,uint64,uint64,uint64) (runs: 512, μ: 1436624, ~: 1440786)\n[PASS] testFuzz_VestingNeverMoreThan25PercentInFirstWeek(uint64,uint64,uint64) (runs: 512, μ: 162168, ~: 157833)\n[PASS] test_ArrivalInsideTheCliffKeepsTheSchedule() (gas: 1432751)\n[PASS] test_BoundVaultHasNoArrivalClock() (gas: 1408247)\n[PASS] test_EachTokenInAnOpenVaultHasItsOwnClock() (gas: 1564907)\n[PASS] test_EarlyExecuteBeforeADepositOnlyDelaysToTheNextEpoch() (gas: 2815372)\n[PASS] test_ExtendDurationCannotOverflowTheSchedule() (gas: 1374877)\n[PASS] test_FactoryRecordsTheCreatorOfEveryVault() (gas: 4553523)\n[PASS] test_FirstReleaseOfANewTokenOnlyStartsItsClock() (gas: 1512246)\n[PASS] test_GasBurningTrackedAssetCanNotBlockOtherWithdrawals() (gas: 1040778435)\n[PASS] test_IncreaseDelayNeverRevivesAnExpiredWithdrawal() (gas: 2444922)\n[PASS] test_IncreaseDelayPastTheExpiryClosesAQueuedWithdrawal() (gas: 2690722)\n[PASS] test_LateArrivalVestsFromArrival() (gas: 1666716)\n[PASS] test_NoFeeFactoryCreatesVaultsAndTakesNoFeeOnRobinhood() (gas: 9109082)\n[PASS] test_NoFeeFactoryIsTheAuditedFactoryWithZeroArguments() (gas: 62114)\n[PASS] test_OpenVaultRecoverAlwaysRefuses() (gas: 1334035)\n[PASS] test_PayloadBombTrackedAssetCanNotBlockOtherWithdrawals() (gas: 4139231)\n[PASS] test_PendingManagerFollowsTheCurrentDelay() (gas: 2220258)\n[PASS] test_RecoverRefusesASecondEntryPointToTheVestedToken() (gas: 2472125)\n[PASS] test_RobinhoodFactoryWithZeroArgumentsCreatesVaultsAndTakesNoFee() (gas: 5622611)\n[PASS] test_SlowButHonestSecondEntryPointIsStillCharged() (gas: 4383602)\n[PASS] test_SyncIsIdempotentAndNeedsABalance() (gas: 1489390)\n[PASS] test_TreasuryCapHasNoOverflowForHugeBalances() (gas: 3519667)\n[PASS] test_TreasuryCapIsSharedByTwoEntryPointsToOneBalance() (gas: 4167861)\n[PASS] test_TreasuryCapThroughLegacyAddressFirstIsSharedToo() (gas: 3617315)\n[PASS] test_TreasuryConstructorEnforcesMinDelay() (gas: 8624)\n[PASS] test_TreasuryEpochBoundaryBoundIsRatePlusRateOfRemainder() (gas: 2870511)\n[PASS] test_TreasuryTrackedAssetsNeverBlockOtherWithdrawals() (gas: 3392783)\n[PASS] test_VestingCreatedLogsWhatTheVaultReceived() (gas: 2001531)\n[PASS] test_VestingHugeAllocationVestsLinearly() (gas: 2275972)\n[PASS] test_VestingRejectsOverflowingSchedule() (gas: 1713755)\n[PASS] test_VestingRejectsStartInThePast() (gas: 2955458)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 3.01s (3.21s CPU time)\n\nRan 6 tests for test/Invariants.t.sol:VestingInvariantTest\n[PASS] invariant_conservation() (runs: 128, calls: 8192, reverts: 1)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 876   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 928   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 869   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 920   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 946   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 935   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 883   | 1       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 942   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 893   | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_immutables() (runs: 256, calls: 16384, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_noEarlyRelease() (runs: 256, calls: 16384, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_openVaultClock() (runs: 256, calls: 16384, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_schedule() (runs: 256, calls: 16384, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesRelease() (gas: 1070541)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 3.01s (7.59s CPU time)\n\nRan 1 test for test/Invariants.t.sol:FactoryInvariantTest\n[PASS] invariant_feesAndRegistry() (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------+---------------------+-------+---------+----------╮\n| Contract       | Selector            | Calls | Reverts | Discards |\n+===================================================================+\n| FactoryHandler | createLaunchVesting | 4026  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | createTreasury      | 4156  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | createVesting       | 4051  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | payFor              | 4151  | 0       | 0        |\n╰----------------+---------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.12s (9.12s CPU time)\n\nRan 9 test suites in 9.13s (22.34s CPU time): 118 tests passed, 0 failed, 0 skipped (118 total tests)\n","passed":true},{"durationMs":67,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"TreasuryVault.acceptManager()\",\"TreasuryVault.cancel(uint256)\",\"TreasuryVault.cancelManager()\",\"TreasuryVault.decreaseRate(uint16)\",\"TreasuryVault.deposit(address,uint256)\",\"TreasuryVault.execute(uint256)\",\"TreasuryVault.increaseDelay(uint64)\",\"TreasuryVault.proposeManager(address)\",\"TreasuryVault.queue(address,address,uint256)\",\"TreasuryVault.receive()\",\"TreasuryVault.removeDestination(address)\",\"TreasuryVault.restrictDestinations(address[])\",\"VaultFactory.createLaunchVesting(address,uint64,uint64,uint64)\",\"VaultFactory.createTreasury(address,address,uint64,uint16,address[])\",\"VaultFactory.createVesting(address,address,uint256,uint64,uint64,uint64)\",\"VaultFactory.payFor(uint256,address)\",\"VaultFactoryNoFee.createLaunchVesting(address,uint64,uint64,uint64)\",\"VaultFactoryNoFee.createTreasury(address,address,uint64,uint16,address[])\",\"VaultFactoryNoFee.createVesting(address,address,uint256,uint64,uint64,uint64)\",\"VaultFactoryNoFee.payFor(uint256,address)\",\"VestingVault.acceptBeneficiary()\",\"VestingVault.cancelBeneficiary()\",\"VestingVault.extendDuration(uint64)\",\"VestingVault.proposeBeneficiary(address)\",\"VestingVault.recover(address)\",\"VestingVault.release(address)\",\"VestingVault.sync(address)\"],\"files\":{\".gitignore\":6,\"ADAPTATION.md\":133,\"README.md\":129,\"SWARM_BRIEF.md\":131,\"foundry.toml\":23,\"launch.json\":10,\"src/TreasuryVault.sol\":373,\"src/VaultFactory.sol\":165,\"src/VaultFactoryNoFee.sol\":18,\"src/VestingVault.sol\":226,\"test/Audit.t.sol\":892,\"test/EdgeCases.t.sol\":514,\"test/Fork.t.sol\":37,\"test/Invariants.t.sol\":761,\"test/SharedCap.t.sol\":477,\"test/Vault.t.sol\":615},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1536,"exitCode":0,"name":"slither","output":"[medium/high] incorrect-equality at src/VestingVault.sol:219: VestingVault._sync(address) (src/VestingVault.sol#219-225) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/TreasuryVault.sol:290: TreasuryVault.pendingManagerEta() (src/TreasuryVault.sol#290-293) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/VestingVault.sol:112: VestingVault.unlockStart(address) (src/VestingVault.sol#112-118) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/VestingVault.sol:122: VestingVault.vestedAmount(address,uint64) (src/VestingVault.sol#122-129) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/VestingVault.sol:151: VestingVault.release(address) (src/VestingVault.sol#151-163) uses a dangerous strict equality:\n[low/medium] missing-zero-check at src/TreasuryVault.sol:126: TreasuryVault.constructor(address,address,uint64,uint16,address[]).projectToken_ (src/TreasuryVault.sol#126) lacks a zero-check on :\n[low/medium] timestamp at src/TreasuryVault.sol:309: TreasuryVault.availableThisWeek(address) (src/TreasuryVault.sol#309-313) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:112: VestingVault.unlockStart(address) (src/VestingVault.sol#112-118) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:151: VestingVault.release(address) (src/VestingVault.sol#151-163) uses timestamp for comparisons\n[low/medium] timestamp at src/TreasuryVault.sol:329: TreasuryVault._get(uint256) (src/TreasuryVault.sol#329-332) uses timestamp for comparisons\n[low/medium] timestamp at src/TreasuryVault.sol:185: TreasuryVault.execute(uint256) (src/TreasuryVault.sol#185-217) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:76: VestingVault.constructor(IERC20,address,uint64,uint64,uint64,uint64) (src/VestingVault.sol#76-96) uses timestamp for comparisons\n[low/medium] timestamp at src/TreasuryVault.sol:362: TreasuryVault._charge(TreasuryVault.Window,uint256,uint256) (src/TreasuryVault.sol#362-372) uses timestamp for comparisons\n[low/medium] timestamp at src/TreasuryVault.sol:275: TreasuryVault.acceptManager() (src/TreasuryVault.sol#275-285) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:191: VestingVault.acceptBeneficiary() (src/VestingVault.sol#191-200) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:122: VestingVault.vestedAmount(address,uint64) (src/VestingVault.sol#122-129) uses timestamp for comparisons\n[low/medium] timestamp at src/TreasuryVault.sol:290: TreasuryVault.pendingManagerEta() (src/TreasuryVault.sol#290-293) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:219: VestingVault._sync(address) (src/VestingVault.sol#219-225) uses timestamp for comparisons\n[low/medium] timestamp at src/VestingVault.sol:141: VestingVault.releasable(address) (src/VestingVault.sol#141-146) uses timestamp for comparisons","passed":true},{"durationMs":461,"exitCode":0,"name":"aderyn","output":"[low] costly-loop at src/TreasuryVault.sol:206: Costly operations inside loop (3 places)\n[low] large-numeric-literal at src/TreasuryVault.sol:38: Large Numeric Literal (2 places)\n[low] require-revert-in-loop at src/TreasuryVault.sol:206: Loop Contains `require`/`revert` (2 places)\n[low] unchecked-return at src/VestingVault.sol:137: Unchecked Return\n[low] uninitialized-local-variable at src/TreasuryVault.sol:206: Uninitialized Local Variable (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"11ae73fcb048f9ad42ff0cce2c598cfcc97501e460e3570a4b30a870e225e890","verifiedTreeHash":"1469c2eaab0ffc68500427538c59f00b5e8cff53","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":12395,"exitCode":0,"name":"build","output":"Compiling 45 files with Solc 0.8.26\nSolc 0.8.26 finished in 12.19s\nCompiler run successful!\n","passed":true},{"durationMs":16152,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Fork.t.sol:ForkTest\n[PASS] test_RealImdFeeAndVesting() (gas: 3689)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 420.56µs (89.31µs CPU time)\n\nRan 9 tests for test/SharedCap.t.sol:SharedCapEdgeTest\n[PASS] testFuzz_ExpiryNeverMovesWithTheDelay(uint64,uint64,uint256) (runs: 512, μ: 2328528, ~: 2313589)\n[PASS] test_CreatorIsRecordedForEveryPathAndFreeVaultsStillCarryIt() (gas: 4583926)\n[PASS] test_ExpiryIsInclusiveAndOneSecondLaterRefused() (gas: 618825)\n[PASS] test_IncreaseDelayShortensButKeepsAnOpenWindow() (gas: 440081)\n[PASS] test_PendingManagerEtaResetsOnReproposeAndClearsOnCancel() (gas: 222843)\n[PASS] test_QueueingTheSameAssetTwiceTracksItOnceAndEthNever() (gas: 689893)\n[PASS] test_SharedChargeOpensTheOtherWindowAtTheLowerRate() (gas: 980051)\n[PASS] test_SharedWindowSpansEpochsCorrectly() (gas: 811544)\n[PASS] test_UnreadableTrackedAssetIsSkippedEitherWay() (gas: 1484017)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 98.01ms (104.33ms CPU time)\n\nRan 25 tests for test/EdgeCases.t.sol:EdgeCasesTest\n[PASS] testFuzz_ReleasedNeverExceedsDeposited(uint96,uint32[6],uint32) (runs: 512, μ: 1992933, ~: 2006422)\n[PASS] test_BeneficiaryHandoverAtTheExactSecondAndAfter() (gas: 1668384)\n[PASS] test_CancelledProposalCanNotBeAccepted() (gas: 2157491)\n[PASS] test_CreateTreasuryRejectsBadArguments() (gas: 2538879)\n[PASS] test_CreateVestingRejectsZeroAmountAndZeroBeneficiary() (gas: 286116)\n[PASS] test_DuplicateDestinationsAreListedOnce() (gas: 2255175)\n[PASS] test_DurationFloorIsExactlyFourWeeks() (gas: 1508835)\n[PASS] test_FeeFailureLeavesNothingBehind() (gas: 3157030)\n[PASS] test_FeeIsChargedToTheCallerOnlyOnceEvenAcrossKinds() (gas: 4510846)\n[PASS] test_NothingAtTheCliffSecondThenLinear() (gas: 1438296)\n[PASS] test_OnlyTheBeneficiaryChangesTheSchedule() (gas: 1419151)\n[PASS] test_OpenVaultReleaseBeforeAnythingArrivesReverts() (gas: 1328666)\n[PASS] test_PayForRejectsZeroTokenAndSecondPayment() (gas: 166042)\n[PASS] test_PayForThisChainCoversTheFirstVault() (gas: 1422674)\n[PASS] test_ReleaseTwiceInOneBlockPaysOnce() (gas: 1466070)\n[PASS] test_SyncIsANoOpOnABoundVaultAndRefusesOtherAssets() (gas: 1402192)\n[PASS] test_TreasuryCapExactlyAvailableThenOneWeiOver() (gas: 2774697)\n[PASS] test_TreasuryDelayAndGraceBoundaries() (gas: 2756053)\n[PASS] test_TreasuryDepositAndQueueRejectZero() (gas: 2132646)\n[PASS] test_TreasuryDestinationRulesFailurePaths() (gas: 2322635)\n[PASS] test_TreasuryEthToARefusingDestinationRollsBackAndCanRetry() (gas: 2733373)\n[PASS] test_TreasuryLastSecondOfGraceExecutes() (gas: 2465816)\n[PASS] test_TreasuryManagerHandoverFailurePaths() (gas: 2342957)\n[PASS] test_TreasuryRuleBoundsAtTheEdges() (gas: 2308671)\n[PASS] test_TreasuryUnknownIdsRevert() (gas: 2133570)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 132.96ms (135.06ms CPU time)\n\nRan 32 tests for test/Audit.t.sol:AuditTest\n[PASS] testFuzz_LateArrivalNeverUnlocksMoreThan25PercentInAWeek(uint256,uint64,uint64,uint64,uint64) (runs: 512, μ: 1436624, ~: 1440786)\n[PASS] testFuzz_VestingNeverMoreThan25PercentInFirstWeek(uint64,uint64,uint64) (runs: 512, μ: 162168, ~: 157833)\n[PASS] test_ArrivalInsideTheCliffKeepsTheSchedule() (gas: 1432751)\n[PASS] test_BoundVaultHasNoArrivalClock() (gas: 1408247)\n[PASS] test_EachTokenInAnOpenVaultHasItsOwnClock() (gas: 1564907)\n[PASS] test_EarlyExecuteBeforeADepositOnlyDelaysToTheNextEpoch() (gas: 2815372)\n[PASS] test_ExtendDurationCannotOverflowTheSchedule() (gas: 1374877)\n[PASS] test_FactoryRecordsTheCreatorOfEveryVault() (gas: 4553523)\n[PASS] test_FirstReleaseOfANewTokenOnlyStartsItsClock() (gas: 1512246)\n[PASS] test_GasBurningTrackedAssetCanNotBlockOtherWithdrawals() (gas: 1040778435)\n[PASS] test_IncreaseDelayNeverRevivesAnExpiredWithdrawal() (gas: 2444922)\n[PASS] test_IncreaseDelayPastTheExpiryClosesAQueuedWithdrawal() (gas: 2690722)\n[PASS] test_LateArrivalVestsFromArrival() (gas: 1666716)\n[PASS] test_NoFeeFactoryCreatesVaultsAndTakesNoFeeOnRobinhood() (gas: 9109082)\n[PASS] test_NoFeeFactoryIsTheAuditedFactoryWithZeroArguments() (gas: 62114)\n[PASS] test_OpenVaultRecoverAlwaysRefuses() (gas: 1334035)\n[PASS] test_PayloadBombTrackedAssetCanNotBlockOtherWithdrawals() (gas: 4139231)\n[PASS] test_PendingManagerFollowsTheCurrentDelay() (gas: 2220258)\n[PASS] test_RecoverRefusesASecondEntryPointToTheVestedToken() (gas: 2472125)\n[PASS] test_RobinhoodFactoryWithZeroArgumentsCreatesVaultsAndTakesNoFee() (gas: 5622611)\n[PASS] test_SlowButHonestSecondEntryPointIsStillCharged() (gas: 4383602)\n[PASS] test_SyncIsIdempotentAndNeedsABalance() (gas: 1489390)\n[PASS] test_TreasuryCapHasNoOverflowForHugeBalances() (gas: 3519667)\n[PASS] test_TreasuryCapIsSharedByTwoEntryPointsToOneBalance() (gas: 4167861)\n[PASS] test_TreasuryCapThroughLegacyAddressFirstIsSharedToo() (gas: 3617315)\n[PASS] test_TreasuryConstructorEnforcesMinDelay() (gas: 8624)\n[PASS] test_TreasuryEpochBoundaryBoundIsRatePlusRateOfRemainder() (gas: 2870511)\n[PASS] test_TreasuryTrackedAssetsNeverBlockOtherWithdrawals() (gas: 3392783)\n[PASS] test_VestingCreatedLogsWhatTheVaultReceived() (gas: 2001531)\n[PASS] test_VestingHugeAllocationVestsLinearly() (gas: 2275972)\n[PASS] test_VestingRejectsOverflowingSchedule() (gas: 1713755)\n[PASS] test_VestingRejectsStartInThePast() (gas: 2955458)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 363.00ms (566.63ms CPU time)\n\nRan 38 tests for test/Vault.t.sol:VaultTest\n[PASS] testFuzz_TreasuryNeverExceedsWeeklyCap(uint256,uint16,uint256[5]) (runs: 512, μ: 3269204, ~: 3278231)\n[PASS] testFuzz_VestingNeverOverpays(uint256,uint64,uint64,uint32[4]) (runs: 512, μ: 1624948, ~: 1593098)\n[PASS] testFuzz_VestingNeverUnlocksMoreThan25PercentInAWeek(uint256,uint64,uint64,uint64) (runs: 512, μ: 1345410, ~: 1348875)\n[PASS] test_BoundVestingRejectsOtherTokens() (gas: 1407419)\n[PASS] test_CreateVestingNeedsAToken() (gas: 36022)\n[PASS] test_EachLaunchVaultPaysItsOwnFee() (gas: 4528792)\n[PASS] test_FactoryBurnsFeeOncePerProjectAndRegisters() (gas: 3334034)\n[PASS] test_FactoryChargesEachNewProject() (gas: 3861182)\n[PASS] test_FactoryNeedsRadarWalletWhenCharging() (gas: 6067)\n[PASS] test_FactoryRejectsShortDelay() (gas: 36613)\n[PASS] test_FactoryRevertsWithoutImd() (gas: 1930918)\n[PASS] test_FeeGoesToRadarNotBurned() (gas: 1331590)\n[PASS] test_FreeFactoryOnChainsWithoutImd() (gas: 1384053)\n[PASS] test_LaunchTreasuryReceivesRemainder() (gas: 3087508)\n[PASS] test_LaunchVestingReceivesRemainderAndVests() (gas: 2135921)\n[PASS] test_LaunchVestingVestsEveryTokenAndHasNoRecover() (gas: 2285174)\n[PASS] test_PayForRobinhoodProject() (gas: 149438)\n[PASS] test_RobinhoodLaunchVaultPaidOnEthereum() (gas: 1305813)\n[PASS] test_TreasuryCancelAndExpiry() (gas: 2536737)\n[PASS] test_TreasuryCapIgnoresDepositsWithinEpoch() (gas: 2684393)\n[PASS] test_TreasuryDelayAndAnyoneExecutes() (gas: 2615189)\n[PASS] test_TreasuryDestinationAllowlist() (gas: 2151691)\n[PASS] test_TreasuryEth() (gas: 2424892)\n[PASS] test_TreasuryManagerChangeIsDelayed() (gas: 2197141)\n[PASS] test_TreasuryOnlyManagerQueues() (gas: 2076121)\n[PASS] test_TreasuryRateCappedAt25Percent() (gas: 2132428)\n[PASS] test_TreasuryRulesOnlyTighten() (gas: 2481134)\n[PASS] test_TreasuryTighteningHoldsQueued() (gas: 2792125)\n[PASS] test_TreasuryWeeklyCap() (gas: 2749602)\n[PASS] test_VestingBeneficiaryCancel() (gas: 1418534)\n[PASS] test_VestingBeneficiaryChangeIsDelayed() (gas: 1549654)\n[PASS] test_VestingCliffThenLinear() (gas: 1630631)\n[PASS] test_VestingExtendAfterReleaseNeverUnderflows() (gas: 1445951)\n[PASS] test_VestingFeeOnTransferToken() (gas: 2102155)\n[PASS] test_VestingOnlyExtends() (gas: 1411611)\n[PASS] test_VestingRecoverOtherTokensOnly() (gas: 1466339)\n[PASS] test_VestingRejectsFasterThan25PercentAWeek() (gas: 1460103)\n[PASS] test_VestingTopUpFollowsSchedule() (gas: 1365133)\nSuite result: ok. 38 passed; 0 failed; 0 skipped; finished in 367.33ms (633.02ms CPU time)\n\nRan 2 tests for test/SharedCap.t.sol:SharedCapInvariantTest\n[PASS]\nSharedCapInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_sharedCap\n SharedCapInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+---------------+-------+---------+----------╮\n| Contract         | Selector      | Calls | Reverts | Discards |\n+===============================================================+\n| SharedCapHandler | cancel        | 2310  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | decreaseRate  | 2284  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | deposit       | 2289  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | execute       | 2370  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | increaseDelay | 2412  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | queue         | 2372  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | warp          | 2347  | 0       | 0        |\n╰------------------+---------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesBothSidesOfTheSharedCap() (gas: 1450669)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 2.76s (2.76s CPU time)\n\nRan 4 tests for test/Invariants.t.sol:TreasuryInvariantTest\n[PASS] invariant_conservation() (runs: 128, calls: 8192, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 769   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 754   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 729   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 689   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 668   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 761   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 818   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 730   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 737   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 770   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 767   | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] invariant_rulesBoundedAndStricter() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 1508  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 1513  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 1474  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 1440  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 1427  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 1522  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 1553  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 1468  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 1409  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 1558  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 1512  | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] invariant_windowNeverOverspent() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 1508  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 1513  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 1474  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 1440  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 1427  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 1522  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 1553  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 1468  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 1409  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 1558  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 1512  | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesExecution() (gas: 1283283)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.78s (6.55s CPU time)\n\nRan 6 tests for test/Invariants.t.sol:VestingInvariantTest\n[PASS] invariant_conservation() (runs: 128, calls: 8192, reverts: 1)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 876   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 928   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 869   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 920   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 946   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 935   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 883   | 1       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 942   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 893   | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_immutables() (runs: 256, calls: 16384, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_noEarlyRelease() (runs: 256, calls: 16384, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_openVaultClock() (runs: 256, calls: 16384, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_schedule() (runs: 256, calls: 16384, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesRelease() (gas: 1070541)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 5.28s (13.02s CPU time)\n\nRan 1 test for test/Invariants.t.sol:FactoryInvariantTest\n[PASS] invariant_feesAndRegistry() (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------+---------------------+-------+---------+----------╮\n| Contract       | Selector            | Calls | Reverts | Discards |\n+===================================================================+\n| FactoryHandler | createLaunchVesting | 4026  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | createTreasury      | 4156  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | createVesting       | 4051  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | payFor              | 4151  | 0       | 0        |\n╰----------------+---------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 15.94s (15.94s CPU time)\n\nRan 9 test suites in 15.95s (27.72s CPU time): 118 tests passed, 0 failed, 0 skipped (118 total tests)\n","passed":true},{"durationMs":93,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"TreasuryVault.acceptManager()\",\"TreasuryVault.cancel(uint256)\",\"TreasuryVault.cancelManager()\",\"TreasuryVault.decreaseRate(uint16)\",\"TreasuryVault.deposit(address,uint256)\",\"TreasuryVault.execute(uint256)\",\"TreasuryVault.increaseDelay(uint64)\",\"TreasuryVault.proposeManager(address)\",\"TreasuryVault.queue(address,address,uint256)\",\"TreasuryVault.receive()\",\"TreasuryVault.removeDestination(address)\",\"TreasuryVault.restrictDestinations(address[])\",\"VaultFactory.createLaunchVesting(address,uint64,uint64,uint64)\",\"VaultFactory.createTreasury(address,address,uint64,uint16,address[])\",\"VaultFactory.createVesting(address,address,uint256,uint64,uint64,uint64)\",\"VaultFactory.payFor(uint256,address)\",\"VaultFactoryNoFee.createLaunchVesting(address,uint64,uint64,uint64)\",\"VaultFactoryNoFee.createTreasury(address,address,uint64,uint16,address[])\",\"VaultFactoryNoFee.createVesting(address,address,uint256,uint64,uint64,uint64)\",\"VaultFactoryNoFee.payFor(uint256,address)\",\"VestingVault.acceptBeneficiary()\",\"VestingVault.cancelBeneficiary()\",\"VestingVault.extendDuration(uint64)\",\"VestingVault.proposeBeneficiary(address)\",\"VestingVault.recover(address)\",\"VestingVault.release(address)\",\"VestingVault.sync(address)\"],\"files\":{\".gitignore\":6,\"ADAPTATION.md\":133,\"README.md\":129,\"SWARM_BRIEF.md\":131,\"foundry.toml\":23,\"launch.json\":10,\"src/TreasuryVault.sol\":373,\"src/VaultFactory.sol\":165,\"src/VaultFactoryNoFee.sol\":18,\"src/VestingVault.sol\":226,\"test/Audit.t.sol\":892,\"test/EdgeCases.t.sol\":514,\"test/Fork.t.sol\":37,\"test/Invariants.t.sol\":761,\"test/SharedCap.t.sol\":477,\"test/Vault.t.sol\":615},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"94fad1d04037929c8d57defa8b12875fd695677d74d05726a058733d3a8f102c","verifiedTreeHash":"d095edcb13f83206ecebc175f8fd6220966e6c88","verifierVersion":"0.1.0+a6ee6548"},{"checks":[{"durationMs":7214,"exitCode":0,"name":"build","output":"Compiling 44 files with Solc 0.8.26\nSolc 0.8.26 finished in 7.09s\nCompiler run successful!\n","passed":true},{"durationMs":10871,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Fork.t.sol:ForkTest\n[PASS] test_RealImdFeeAndVesting() (gas: 3689)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 324.07µs (82.90µs CPU time)\n\nRan 28 tests for test/Audit.t.sol:AuditTest\n[PASS] testFuzz_LateArrivalNeverUnlocksMoreThan25PercentInAWeek(uint256,uint64,uint64,uint64,uint64) (runs: 512, μ: 1406588, ~: 1410855)\n[PASS] testFuzz_VestingNeverMoreThan25PercentInFirstWeek(uint64,uint64,uint64) (runs: 512, μ: 161990, ~: 157772)\n[PASS] test_ArrivalInsideTheCliffKeepsTheSchedule() (gas: 1402833)\n[PASS] test_BoundVaultHasNoArrivalClock() (gas: 1378350)\n[PASS] test_EachTokenInAnOpenVaultHasItsOwnClock() (gas: 1535026)\n[PASS] test_EarlyExecuteBeforeADepositOnlyDelaysToTheNextEpoch() (gas: 2800295)\n[PASS] test_ExtendDurationCannotOverflowTheSchedule() (gas: 1345094)\n[PASS] test_FactoryRecordsTheCreatorOfEveryVault() (gas: 4478729)\n[PASS] test_FirstReleaseOfANewTokenOnlyStartsItsClock() (gas: 1482328)\n[PASS] test_IncreaseDelayNeverRevivesAnExpiredWithdrawal() (gas: 2429868)\n[PASS] test_IncreaseDelayPastTheExpiryClosesAQueuedWithdrawal() (gas: 2675623)\n[PASS] test_LateArrivalVestsFromArrival() (gas: 1636748)\n[PASS] test_NoFeeFactoryCreatesVaultsAndTakesNoFeeOnRobinhood() (gas: 8959501)\n[PASS] test_NoFeeFactoryIsTheAuditedFactoryWithZeroArguments() (gas: 61685)\n[PASS] test_OpenVaultRecoverAlwaysRefuses() (gas: 1304167)\n[PASS] test_PendingManagerFollowsTheCurrentDelay() (gas: 2205226)\n[PASS] test_RecoverRefusesASecondEntryPointToTheVestedToken() (gas: 2442231)\n[PASS] test_RobinhoodFactoryWithZeroArgumentsCreatesVaultsAndTakesNoFee() (gas: 5517953)\n[PASS] test_SyncIsIdempotentAndNeedsABalance() (gas: 1459500)\n[PASS] test_TreasuryCapIsSharedByTwoEntryPointsToOneBalance() (gas: 4153961)\n[PASS] test_TreasuryCapThroughLegacyAddressFirstIsSharedToo() (gas: 3602827)\n[PASS] test_TreasuryConstructorEnforcesMinDelay() (gas: 8624)\n[PASS] test_TreasuryEpochBoundaryBoundIsRatePlusRateOfRemainder() (gas: 2855323)\n[PASS] test_TreasuryTrackedAssetsNeverBlockOtherWithdrawals() (gas: 3379825)\n[PASS] test_TreeCompilesToTheAuditedMainnetFactory() (gas: 4546)\n[PASS] test_VestingCreatedLogsWhatTheVaultReceived() (gas: 1971659)\n[PASS] test_VestingRejectsOverflowingSchedule() (gas: 1683748)\n[PASS] test_VestingRejectsStartInThePast() (gas: 2895533)\nSuite result: ok. 28 passed; 0 failed; 0 skipped; finished in 102.55ms (179.15ms CPU time)\n\nRan 25 tests for test/EdgeCases.t.sol:EdgeCasesTest\n[PASS] testFuzz_ReleasedNeverExceedsDeposited(uint96,uint32[6],uint32) (runs: 512, μ: 1961666, ~: 1976180)\n[PASS] test_BeneficiaryHandoverAtTheExactSecondAndAfter() (gas: 1638487)\n[PASS] test_CancelledProposalCanNotBeAccepted() (gas: 2142459)\n[PASS] test_CreateTreasuryRejectsBadArguments() (gas: 2523801)\n[PASS] test_CreateVestingRejectsZeroAmountAndZeroBeneficiary() (gas: 286042)\n[PASS] test_DuplicateDestinationsAreListedOnce() (gas: 2240121)\n[PASS] test_DurationFloorIsExactlyFourWeeks() (gas: 1478893)\n[PASS] test_FeeFailureLeavesNothingBehind() (gas: 3112130)\n[PASS] test_FeeIsChargedToTheCallerOnlyOnceEvenAcrossKinds() (gas: 4436074)\n[PASS] test_NothingAtTheCliffSecondThenLinear() (gas: 1408324)\n[PASS] test_OnlyTheBeneficiaryChangesTheSchedule() (gas: 1389279)\n[PASS] test_OpenVaultReleaseBeforeAnythingArrivesReverts() (gas: 1298798)\n[PASS] test_PayForRejectsZeroTokenAndSecondPayment() (gas: 166042)\n[PASS] test_PayForThisChainCoversTheFirstVault() (gas: 1392806)\n[PASS] test_ReleaseTwiceInOneBlockPaysOnce() (gas: 1436148)\n[PASS] test_SyncIsANoOpOnABoundVaultAndRefusesOtherAssets() (gas: 1372320)\n[PASS] test_TreasuryCapExactlyAvailableThenOneWeiOver() (gas: 2759403)\n[PASS] test_TreasuryDelayAndGraceBoundaries() (gas: 2740954)\n[PASS] test_TreasuryDepositAndQueueRejectZero() (gas: 2117614)\n[PASS] test_TreasuryDestinationRulesFailurePaths() (gas: 2307670)\n[PASS] test_TreasuryEthToARefusingDestinationRollsBackAndCanRetry() (gas: 2718142)\n[PASS] test_TreasuryLastSecondOfGraceExecutes() (gas: 2450717)\n[PASS] test_TreasuryManagerHandoverFailurePaths() (gas: 2327925)\n[PASS] test_TreasuryRuleBoundsAtTheEdges() (gas: 2293639)\n[PASS] test_TreasuryUnknownIdsRevert() (gas: 2118538)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 103.24ms (111.76ms CPU time)\n\nRan 38 tests for test/Vault.t.sol:VaultTest\n[PASS] testFuzz_TreasuryNeverExceedsWeeklyCap(uint256,uint16,uint256[5]) (runs: 512, μ: 3254205, ~: 3269838)\n[PASS] testFuzz_VestingNeverOverpays(uint256,uint64,uint64,uint32[4]) (runs: 512, μ: 1598972, ~: 1563538)\n[PASS] testFuzz_VestingNeverUnlocksMoreThan25PercentInAWeek(uint256,uint64,uint64,uint64) (runs: 512, μ: 1315421, ~: 1318942)\n[PASS] test_BoundVestingRejectsOtherTokens() (gas: 1377547)\n[PASS] test_CreateVestingNeedsAToken() (gas: 36022)\n[PASS] test_EachLaunchVaultPaysItsOwnFee() (gas: 4454024)\n[PASS] test_FactoryBurnsFeeOncePerProjectAndRegisters() (gas: 3289130)\n[PASS] test_FactoryChargesEachNewProject() (gas: 3816278)\n[PASS] test_FactoryNeedsRadarWalletWhenCharging() (gas: 6067)\n[PASS] test_FactoryRejectsShortDelay() (gas: 36613)\n[PASS] test_FactoryRevertsWithoutImd() (gas: 1915886)\n[PASS] test_FeeGoesToRadarNotBurned() (gas: 1301718)\n[PASS] test_FreeFactoryOnChainsWithoutImd() (gas: 1354185)\n[PASS] test_LaunchTreasuryReceivesRemainder() (gas: 3072408)\n[PASS] test_LaunchVestingReceivesRemainderAndVests() (gas: 2106028)\n[PASS] test_LaunchVestingVestsEveryTokenAndHasNoRecover() (gas: 2255256)\n[PASS] test_PayForRobinhoodProject() (gas: 149438)\n[PASS] test_RobinhoodLaunchVaultPaidOnEthereum() (gas: 1275945)\n[PASS] test_TreasuryCancelAndExpiry() (gas: 2521705)\n[PASS] test_TreasuryCapIgnoresDepositsWithinEpoch() (gas: 2669294)\n[PASS] test_TreasuryDelayAndAnyoneExecutes() (gas: 2600089)\n[PASS] test_TreasuryDestinationAllowlist() (gas: 2136658)\n[PASS] test_TreasuryEth() (gas: 2409792)\n[PASS] test_TreasuryManagerChangeIsDelayed() (gas: 2182109)\n[PASS] test_TreasuryOnlyManagerQueues() (gas: 2061089)\n[PASS] test_TreasuryRateCappedAt25Percent() (gas: 2117378)\n[PASS] test_TreasuryRulesOnlyTighten() (gas: 2466036)\n[PASS] test_TreasuryTighteningHoldsQueued() (gas: 2777026)\n[PASS] test_TreasuryWeeklyCap() (gas: 2734436)\n[PASS] test_VestingBeneficiaryCancel() (gas: 1388662)\n[PASS] test_VestingBeneficiaryChangeIsDelayed() (gas: 1519757)\n[PASS] test_VestingCliffThenLinear() (gas: 1600659)\n[PASS] test_VestingExtendAfterReleaseNeverUnderflows() (gas: 1416029)\n[PASS] test_VestingFeeOnTransferToken() (gas: 2072283)\n[PASS] test_VestingOnlyExtends() (gas: 1381714)\n[PASS] test_VestingRecoverOtherTokensOnly() (gas: 1436467)\n[PASS] test_VestingRejectsFasterThan25PercentAWeek() (gas: 1430192)\n[PASS] test_VestingTopUpFollowsSchedule() (gas: 1335236)\nSuite result: ok. 38 passed; 0 failed; 0 skipped; finished in 105.98ms (234.33ms CPU time)\n\nRan 2 tests for test/SharedCap.t.sol:SharedCapInvariantTest\n[PASS]\nSharedCapInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_sharedCap\n SharedCapInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+---------------+-------+---------+----------╮\n| Contract         | Selector      | Calls | Reverts | Discards |\n+===============================================================+\n| SharedCapHandler | cancel        | 2310  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | decreaseRate  | 2284  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | deposit       | 2289  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | execute       | 2370  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | increaseDelay | 2412  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | queue         | 2372  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | warp          | 2347  | 0       | 0        |\n╰------------------+---------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesBothSidesOfTheSharedCap() (gas: 1451014)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.82s (1.82s CPU time)\n\nRan 4 tests for test/Invariants.t.sol:TreasuryInvariantTest\n[PASS] invariant_conservation() (runs: 128, calls: 8192, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 769   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 754   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 729   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 689   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 668   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 761   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 818   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 730   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 737   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 770   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 767   | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] invariant_rulesBoundedAndStricter() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 1508  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 1513  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 1474  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 1440  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 1427  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 1522  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 1553  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 1468  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 1409  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 1558  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 1512  | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] invariant_windowNeverOverspent() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 1508  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 1513  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 1474  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 1440  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 1427  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 1522  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 1553  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 1468  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 1409  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 1558  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 1512  | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesExecution() (gas: 1283085)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.86s (4.13s CPU time)\n\nRan 15 tests for test/RobinhoodBoundaries.t.sol:RobinhoodBoundariesTest\n[PASS] testFuzz_AvailableThisWeekIsExactlyWhatExecutes(uint256,uint16) (runs: 512, μ: 2665409, ~: 2665256)\n[PASS] test_BriefedArgumentsGiveAFactoryThatChargesNothing() (gas: 24018)\n[PASS] test_EthWithdrawalNeverReadsTrackedTokens() (gas: 3041064)\n[PASS] test_FreeCreationNeedsNoApprovalOfAnythingButTheVestedToken() (gas: 1374332)\n[PASS] test_SameProjectCanCreateManyVaultsAndEveryOneIsRegistered() (gas: 5198867)\n[PASS] test_SameWithdrawalCanNotExecuteTwiceOrAfterCancel() (gas: 2690132)\n[PASS] test_StrangerCanNotTouchTheRules() (gas: 2178170)\n[PASS] test_TreasuryDustBalanceHasZeroCap() (gas: 2540949)\n[PASS] test_TreasuryLargestSafeBalanceStillWithdraws() (gas: 2447244)\n[PASS] test_TreasuryWithDelayOneSecondShortOfADayIsRefused() (gas: 292908)\n[PASS] test_VestingBeneficiaryRulesFailurePaths() (gas: 1475755)\n[PASS] test_VestingLargestSafeAllocationReleasesLinearly() (gas: 1554908)\n[PASS] test_VestingOneWeiReleasesOnlyAtTheEnd() (gas: 1486250)\n[PASS] test_VestingReleaseOfZeroAddressOrForeignTokenIsRefused() (gas: 3139464)\n[PASS] test_VestingScheduleMayEndExactlyAtUint64Max() (gas: 1309324)\nSuite result: ok. 15 passed; 0 failed; 0 skipped; finished in 1.86s (109.48ms CPU time)\n\nRan 9 tests for test/SharedCap.t.sol:SharedCapEdgeTest\n[PASS] testFuzz_ExpiryNeverMovesWithTheDelay(uint64,uint64,uint256) (runs: 512, μ: 2311676, ~: 2298557)\n[PASS] test_CreatorIsRecordedForEveryPathAndFreeVaultsStillCarryIt() (gas: 4553862)\n[PASS] test_ExpiryIsInclusiveAndOneSecondLaterRefused() (gas: 618758)\n[PASS] test_IncreaseDelayShortensButKeepsAnOpenWindow() (gas: 440014)\n[PASS] test_PendingManagerEtaResetsOnReproposeAndClearsOnCancel() (gas: 222843)\n[PASS] test_QueueingTheSameAssetTwiceTracksItOnceAndEthNever() (gas: 689893)\n[PASS] test_SharedChargeOpensTheOtherWindowAtTheLowerRate() (gas: 981250)\n[PASS] test_SharedWindowSpansEpochsCorrectly() (gas: 812676)\n[PASS] test_UnreadableTrackedAssetIsSkippedEitherWay() (gas: 1485134)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 3.34s (1.04s CPU time)\n\nRan 27 tests for test/RobinhoodFactory.t.sol:RobinhoodFactoryTest\n[PASS] testFuzz_CreateTreasuryAcceptsExactlyTheBounds(uint64,uint16) (runs: 1000, μ: 142476, ~: 95224)\n[PASS] testFuzz_CreateVestingAcceptsExactlyValidSchedules(uint256,uint64,uint64,uint64) (runs: 1000, μ: 568095, ~: 100843)\n[PASS] testFuzz_PayForAlwaysRefusedHere(address,uint256,address) (runs: 1000, μ: 34152, ~: 34145)\n[PASS] test_ConstructorArgumentsOfTheBriefAreAcceptedAndStored() (gas: 47043)\n[PASS] test_ConstructorRejectsImdWithoutRecipientOnly() (gas: 6111)\n[PASS] test_ConstructorTouchesNoOtherAccount() (gas: 33095)\n[PASS] test_CreateLaunchVestingFailurePaths() (gas: 361480)\n[PASS] test_CreateTreasuryAtTheBounds() (gas: 3936932)\n[PASS] test_CreateTreasuryFailurePaths() (gas: 533050)\n[PASS] test_CreateTreasuryForATokenWithoutCode() (gas: 1983707)\n[PASS] test_CreateVestingBeyondBalanceReverts() (gas: 1107045)\n[PASS] test_CreateVestingLogsWhatAFeeTokenDelivered() (gas: 2026620)\n[PASS] test_CreateVestingRejectsDurationOneSecondShortOfFourWeeks() (gas: 1347332)\n[PASS] test_CreateVestingRejectsScheduleOverflowingUint64() (gas: 1348248)\n[PASS] test_CreateVestingRejectsStartOneSecondInThePast() (gas: 118010)\n[PASS] test_CreateVestingRejectsZeroAmount() (gas: 60949)\n[PASS] test_CreateVestingRejectsZeroBeneficiary() (gas: 115496)\n[PASS] test_CreateVestingRejectsZeroToken() (gas: 58673)\n[PASS] test_CreateVestingWithCodelessTokenReverts() (gas: 1059853)\n[PASS] test_CreateVestingWithTokenReturningFalseReverts() (gas: 1627979)\n[PASS] test_CreateVestingWithoutApprovalReverts() (gas: 1139103)\n[PASS] test_DeployAndCreateInOneTransaction() (gas: 3303299)\n[PASS] test_FactoryRefusesEth() (gas: 38822)\n[PASS] test_LaunchVestingVestsTheRemainderFromItsArrival() (gas: 1526732)\n[PASS] test_OpenVaultCreationTouchesNoTokenAccount() (gas: 3218340)\n[PASS] test_ReentrantTokenCannotCorruptTheRegistry() (gas: 3214943)\n[PASS] test_RepeatedCreationsNeverMarkAnythingPaid() (gas: 5592653)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 3.34s (4.48s CPU time)\n\nRan 6 tests for test/Invariants.t.sol:VestingInvariantTest\n[PASS] invariant_conservation() (runs: 128, calls: 8192, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 876   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 928   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 869   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 920   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 946   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 935   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 883   | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 942   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 893   | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_immutables() (runs: 256, calls: 16384, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_noEarlyRelease() (runs: 256, calls: 16384, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_openVaultClock() (runs: 256, calls: 16384, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_schedule() (runs: 256, calls: 16384, reverts: 2)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 2       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesRelease() (gas: 1070291)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 3.34s (8.26s CPU time)\n\nRan 2 tests for test/RobinhoodFactory.t.sol:RobinhoodInvariantTest\n[PASS]\nRobinhoodInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_neverAheadOfSchedule\n[PASS] invariant_noFeeAndNoCustody\n[PASS] invariant_registry\n RobinhoodInvariantTest invariants (runs: 128, calls: 6144, reverts: 0)\n\n╭------------------+---------------------+-------+---------+----------╮\n| Contract         | Selector            | Calls | Reverts | Discards |\n+=====================================================================+\n| RobinhoodHandler | badCreate           | 680   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | createLaunchVesting | 644   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | createTreasury      | 709   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | createVesting       | 714   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | depositTreasury     | 700   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | fundVesting         | 700   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | payFor              | 646   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | release             | 699   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | warp                | 652   | 0       | 0        |\n╰------------------+---------------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesRelease() (gas: 6535774)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 5.91s (5.91s CPU time)\n\nRan 1 test for test/Invariants.t.sol:FactoryInvariantTest\n[PASS] invariant_feesAndRegistry() (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------+---------------------+-------+---------+----------╮\n| Contract       | Selector            | Calls | Reverts | Discards |\n+===================================================================+\n| FactoryHandler | createLaunchVesting | 4026  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | createTreasury      | 4156  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | createVesting       | 4051  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | payFor              | 4151  | 0       | 0        |\n╰----------------+---------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 10.76s (10.75s CPU time)\n\nRan 12 test suites in 10.76s (32.53s CPU time): 158 tests passed, 0 failed, 0 skipped (158 total tests)\n","passed":true},{"durationMs":55,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"TreasuryVault.acceptManager()\",\"TreasuryVault.cancel(uint256)\",\"TreasuryVault.cancelManager()\",\"TreasuryVault.decreaseRate(uint16)\",\"TreasuryVault.deposit(address,uint256)\",\"TreasuryVault.execute(uint256)\",\"TreasuryVault.increaseDelay(uint64)\",\"TreasuryVault.proposeManager(address)\",\"TreasuryVault.queue(address,address,uint256)\",\"TreasuryVault.receive()\",\"TreasuryVault.removeDestination(address)\",\"TreasuryVault.restrictDestinations(address[])\",\"VaultFactory.createLaunchVesting(address,uint64,uint64,uint64)\",\"VaultFactory.createTreasury(address,address,uint64,uint16,address[])\",\"VaultFactory.createVesting(address,address,uint256,uint64,uint64,uint64)\",\"VaultFactory.payFor(uint256,address)\",\"VaultFactoryNoFee.createLaunchVesting(address,uint64,uint64,uint64)\",\"VaultFactoryNoFee.createTreasury(address,address,uint64,uint16,address[])\",\"VaultFactoryNoFee.createVesting(address,address,uint256,uint64,uint64,uint64)\",\"VaultFactoryNoFee.payFor(uint256,address)\",\"VestingVault.acceptBeneficiary()\",\"VestingVault.cancelBeneficiary()\",\"VestingVault.extendDuration(uint64)\",\"VestingVault.proposeBeneficiary(address)\",\"VestingVault.recover(address)\",\"VestingVault.release(address)\",\"VestingVault.sync(address)\"],\"files\":{\".gitignore\":6,\"ADAPTATION.md\":164,\"README.md\":144,\"SWARM_BRIEF.md\":131,\"foundry.toml\":23,\"launch.json\":10,\"src/TreasuryVault.sol\":354,\"src/VaultFactory.sol\":165,\"src/VaultFactoryNoFee.sol\":18,\"src/VestingVault.sol\":224,\"test/Audit.t.sol\":707,\"test/EdgeCases.t.sol\":514,\"test/Fork.t.sol\":37,\"test/Invariants.t.sol\":761,\"test/RobinhoodBoundaries.t.sol\":329,\"test/RobinhoodFactory.t.sol\":835,\"test/SharedCap.t.sol\":477,\"test/Vault.t.sol\":615},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"9f433016dec1589b19a622d415d341aa05f69019f9cac22d019338fc591ebe96","verifiedTreeHash":"43de3e39fb4a53dd1f8797b28913e032c0173e09","verifierVersion":"0.1.0+6a374fcd"},{"checks":[{"durationMs":6374,"exitCode":0,"name":"build","output":"Compiling 46 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.28s\nCompiler run successful!\n","passed":true},{"durationMs":9249,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/Fork.t.sol:ForkTest\n[PASS] test_RealImdFeeAndVesting() (gas: 3689)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 310.46µs (94.36µs CPU time)\n\nRan 9 tests for test/SharedCap.t.sol:SharedCapEdgeTest\n[PASS] testFuzz_ExpiryNeverMovesWithTheDelay(uint64,uint64,uint256) (runs: 512, μ: 2326253, ~: 2313589)\n[PASS] test_CreatorIsRecordedForEveryPathAndFreeVaultsStillCarryIt() (gas: 4583926)\n[PASS] test_ExpiryIsInclusiveAndOneSecondLaterRefused() (gas: 618825)\n[PASS] test_IncreaseDelayShortensButKeepsAnOpenWindow() (gas: 440081)\n[PASS] test_PendingManagerEtaResetsOnReproposeAndClearsOnCancel() (gas: 222843)\n[PASS] test_QueueingTheSameAssetTwiceTracksItOnceAndEthNever() (gas: 689893)\n[PASS] test_SharedChargeOpensTheOtherWindowAtTheLowerRate() (gas: 980051)\n[PASS] test_SharedWindowSpansEpochsCorrectly() (gas: 811544)\n[PASS] test_UnreadableTrackedAssetIsSkippedEitherWay() (gas: 1484017)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 88.77ms (89.90ms CPU time)\n\nRan 25 tests for test/EdgeCases.t.sol:EdgeCasesTest\n[PASS] testFuzz_ReleasedNeverExceedsDeposited(uint96,uint32[6],uint32) (runs: 512, μ: 1992434, ~: 2006869)\n[PASS] test_BeneficiaryHandoverAtTheExactSecondAndAfter() (gas: 1668384)\n[PASS] test_CancelledProposalCanNotBeAccepted() (gas: 2157491)\n[PASS] test_CreateTreasuryRejectsBadArguments() (gas: 2538879)\n[PASS] test_CreateVestingRejectsZeroAmountAndZeroBeneficiary() (gas: 286116)\n[PASS] test_DuplicateDestinationsAreListedOnce() (gas: 2255175)\n[PASS] test_DurationFloorIsExactlyFourWeeks() (gas: 1508835)\n[PASS] test_FeeFailureLeavesNothingBehind() (gas: 3157030)\n[PASS] test_FeeIsChargedToTheCallerOnlyOnceEvenAcrossKinds() (gas: 4510846)\n[PASS] test_NothingAtTheCliffSecondThenLinear() (gas: 1438296)\n[PASS] test_OnlyTheBeneficiaryChangesTheSchedule() (gas: 1419151)\n[PASS] test_OpenVaultReleaseBeforeAnythingArrivesReverts() (gas: 1328666)\n[PASS] test_PayForRejectsZeroTokenAndSecondPayment() (gas: 166042)\n[PASS] test_PayForThisChainCoversTheFirstVault() (gas: 1422674)\n[PASS] test_ReleaseTwiceInOneBlockPaysOnce() (gas: 1466070)\n[PASS] test_SyncIsANoOpOnABoundVaultAndRefusesOtherAssets() (gas: 1402192)\n[PASS] test_TreasuryCapExactlyAvailableThenOneWeiOver() (gas: 2774697)\n[PASS] test_TreasuryDelayAndGraceBoundaries() (gas: 2756053)\n[PASS] test_TreasuryDepositAndQueueRejectZero() (gas: 2132646)\n[PASS] test_TreasuryDestinationRulesFailurePaths() (gas: 2322635)\n[PASS] test_TreasuryEthToARefusingDestinationRollsBackAndCanRetry() (gas: 2733373)\n[PASS] test_TreasuryLastSecondOfGraceExecutes() (gas: 2465816)\n[PASS] test_TreasuryManagerHandoverFailurePaths() (gas: 2342957)\n[PASS] test_TreasuryRuleBoundsAtTheEdges() (gas: 2308671)\n[PASS] test_TreasuryUnknownIdsRevert() (gas: 2133570)\nSuite result: ok. 25 passed; 0 failed; 0 skipped; finished in 89.30ms (99.09ms CPU time)\n\nRan 32 tests for test/Audit.t.sol:AuditTest\n[PASS] testFuzz_LateArrivalNeverUnlocksMoreThan25PercentInAWeek(uint256,uint64,uint64,uint64,uint64) (runs: 512, μ: 1436189, ~: 1440651)\n[PASS] testFuzz_VestingNeverMoreThan25PercentInFirstWeek(uint64,uint64,uint64) (runs: 512, μ: 162172, ~: 157833)\n[PASS] test_ArrivalInsideTheCliffKeepsTheSchedule() (gas: 1432751)\n[PASS] test_BoundVaultHasNoArrivalClock() (gas: 1408247)\n[PASS] test_EachTokenInAnOpenVaultHasItsOwnClock() (gas: 1564907)\n[PASS] test_EarlyExecuteBeforeADepositOnlyDelaysToTheNextEpoch() (gas: 2815372)\n[PASS] test_ExtendDurationCannotOverflowTheSchedule() (gas: 1374877)\n[PASS] test_FactoryRecordsTheCreatorOfEveryVault() (gas: 4553523)\n[PASS] test_FirstReleaseOfANewTokenOnlyStartsItsClock() (gas: 1512246)\n[PASS] test_GasBurningTrackedAssetCanNotBlockOtherWithdrawals() (gas: 1040778435)\n[PASS] test_IncreaseDelayNeverRevivesAnExpiredWithdrawal() (gas: 2444922)\n[PASS] test_IncreaseDelayPastTheExpiryClosesAQueuedWithdrawal() (gas: 2690722)\n[PASS] test_LateArrivalVestsFromArrival() (gas: 1666716)\n[PASS] test_NoFeeFactoryCreatesVaultsAndTakesNoFeeOnRobinhood() (gas: 9109082)\n[PASS] test_NoFeeFactoryIsTheAuditedFactoryWithZeroArguments() (gas: 62114)\n[PASS] test_OpenVaultRecoverAlwaysRefuses() (gas: 1334035)\n[PASS] test_PayloadBombTrackedAssetCanNotBlockOtherWithdrawals() (gas: 4139231)\n[PASS] test_PendingManagerFollowsTheCurrentDelay() (gas: 2220258)\n[PASS] test_RecoverRefusesASecondEntryPointToTheVestedToken() (gas: 2472125)\n[PASS] test_RobinhoodFactoryWithZeroArgumentsCreatesVaultsAndTakesNoFee() (gas: 5622611)\n[PASS] test_SlowButHonestSecondEntryPointIsStillCharged() (gas: 4383602)\n[PASS] test_SyncIsIdempotentAndNeedsABalance() (gas: 1489390)\n[PASS] test_TreasuryCapHasNoOverflowForHugeBalances() (gas: 3519667)\n[PASS] test_TreasuryCapIsSharedByTwoEntryPointsToOneBalance() (gas: 4167861)\n[PASS] test_TreasuryCapThroughLegacyAddressFirstIsSharedToo() (gas: 3617315)\n[PASS] test_TreasuryConstructorEnforcesMinDelay() (gas: 8624)\n[PASS] test_TreasuryEpochBoundaryBoundIsRatePlusRateOfRemainder() (gas: 2870511)\n[PASS] test_TreasuryTrackedAssetsNeverBlockOtherWithdrawals() (gas: 3392783)\n[PASS] test_VestingCreatedLogsWhatTheVaultReceived() (gas: 2001531)\n[PASS] test_VestingHugeAllocationVestsLinearly() (gas: 2275972)\n[PASS] test_VestingRejectsOverflowingSchedule() (gas: 1713755)\n[PASS] test_VestingRejectsStartInThePast() (gas: 2955458)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 186.51ms (293.39ms CPU time)\n\nRan 4 tests for test/Invariants.t.sol:TreasuryInvariantTest\n[PASS] invariant_conservation() (runs: 128, calls: 8192, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 769   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 754   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 729   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 689   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 668   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 761   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 818   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 730   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 737   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 770   | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 767   | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] invariant_rulesBoundedAndStricter() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 1508  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 1513  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 1474  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 1440  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 1427  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 1522  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 1553  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 1468  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 1409  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 1558  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 1512  | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] invariant_windowNeverOverspent() (runs: 256, calls: 16384, reverts: 0)\n\n╭-----------------+-------------------+-------+---------+----------╮\n| Contract        | Selector          | Calls | Reverts | Discards |\n+==================================================================+\n| TreasuryHandler | cancel            | 1508  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | decreaseRate      | 1513  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | deposit           | 1474  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | execute           | 1440  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | increaseDelay     | 1427  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | queue             | 1522  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | removeDestination | 1553  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | reopen            | 1468  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | snapshot          | 1409  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | strangerQueue     | 1558  | 0       | 0        |\n|-----------------+-------------------+-------+---------+----------|\n| TreasuryHandler | warp              | 1512  | 0       | 0        |\n╰-----------------+-------------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesExecution() (gas: 1283283)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.70s (3.88s CPU time)\n\nRan 2 tests for test/SharedCap.t.sol:SharedCapInvariantTest\n[PASS]\nSharedCapInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_sharedCap\n SharedCapInvariantTest invariants (runs: 256, calls: 16384, reverts: 0)\n\n╭------------------+---------------+-------+---------+----------╮\n| Contract         | Selector      | Calls | Reverts | Discards |\n+===============================================================+\n| SharedCapHandler | cancel        | 2310  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | decreaseRate  | 2284  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | deposit       | 2289  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | execute       | 2370  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | increaseDelay | 2412  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | queue         | 2372  | 0       | 0        |\n|------------------+---------------+-------+---------+----------|\n| SharedCapHandler | warp          | 2347  | 0       | 0        |\n╰------------------+---------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesBothSidesOfTheSharedCap() (gas: 1450669)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.71s (1.71s CPU time)\n\nRan 6 tests for test/Invariants.t.sol:VestingInvariantTest\n[PASS] invariant_conservation() (runs: 128, calls: 8192, reverts: 3)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 876   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 928   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 869   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 920   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 946   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 935   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 883   | 3       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 942   | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 893   | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_immutables() (runs: 256, calls: 16384, reverts: 3)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 3       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_noEarlyRelease() (runs: 256, calls: 16384, reverts: 3)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 3       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_openVaultClock() (runs: 256, calls: 16384, reverts: 3)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 3       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] invariant_schedule() (runs: 256, calls: 16384, reverts: 3)\n\n╭----------------+-------------------------+-------+---------+----------╮\n| Contract       | Selector                | Calls | Reverts | Discards |\n+=======================================================================+\n| VestingHandler | accept                  | 1782  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | extend                  | 1829  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | propose                 | 1758  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | recoverVested           | 1825  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | release                 | 1881  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | strangerExtendOrPropose | 1843  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | sync                    | 1755  | 3       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | topUp                   | 1874  | 0       | 0        |\n|----------------+-------------------------+-------+---------+----------|\n| VestingHandler | warp                    | 1837  | 0       | 0        |\n╰----------------+-------------------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesRelease() (gas: 1070541)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 3.00s (7.52s CPU time)\n\nRan 27 tests for test/RobinhoodFactory.t.sol:RobinhoodFactoryTest\n[PASS] testFuzz_CreateTreasuryAcceptsExactlyTheBounds(uint64,uint16) (runs: 1000, μ: 152240, ~: 95242)\n[PASS] testFuzz_CreateVestingAcceptsExactlyValidSchedules(uint256,uint64,uint64,uint64) (runs: 1000, μ: 581375, ~: 100878)\n[PASS] testFuzz_PayForAlwaysRefusedHere(address,uint256,address) (runs: 1000, μ: 34146, ~: 34145)\n[PASS] test_ConstructorArgumentsOfTheBriefAreAcceptedAndStored() (gas: 47472)\n[PASS] test_ConstructorRejectsImdWithoutRecipientOnly() (gas: 6111)\n[PASS] test_ConstructorTouchesNoOtherAccount() (gas: 33528)\n[PASS] test_CreateLaunchVestingFailurePaths() (gas: 361620)\n[PASS] test_CreateTreasuryAtTheBounds() (gas: 3967040)\n[PASS] test_CreateTreasuryFailurePaths() (gas: 533141)\n[PASS] test_CreateTreasuryForATokenWithoutCode() (gas: 1998739)\n[PASS] test_CreateVestingBeyondBalanceReverts() (gas: 1136913)\n[PASS] test_CreateVestingLogsWhatAFeeTokenDelivered() (gas: 2056488)\n[PASS] test_CreateVestingRejectsDurationOneSecondShortOfFourWeeks() (gas: 1377235)\n[PASS] test_CreateVestingRejectsScheduleOverflowingUint64() (gas: 1378151)\n[PASS] test_CreateVestingRejectsStartOneSecondInThePast() (gas: 118045)\n[PASS] test_CreateVestingRejectsZeroAmount() (gas: 60949)\n[PASS] test_CreateVestingRejectsZeroBeneficiary() (gas: 115531)\n[PASS] test_CreateVestingRejectsZeroToken() (gas: 58673)\n[PASS] test_CreateVestingWithCodelessTokenReverts() (gas: 1089721)\n[PASS] test_CreateVestingWithTokenReturningFalseReverts() (gas: 1657847)\n[PASS] test_CreateVestingWithoutApprovalReverts() (gas: 1168971)\n[PASS] test_DeployAndCreateInOneTransaction() (gas: 3348199)\n[PASS] test_FactoryRefusesEth() (gas: 38822)\n[PASS] test_LaunchVestingVestsTheRemainderFromItsArrival() (gas: 1556675)\n[PASS] test_OpenVaultCreationTouchesNoTokenAccount() (gas: 3263650)\n[PASS] test_ReentrantTokenCannotCorruptTheRegistry() (gas: 3274679)\n[PASS] test_RepeatedCreationsNeverMarkAnythingPaid() (gas: 5697289)\nSuite result: ok. 27 passed; 0 failed; 0 skipped; finished in 3.00s (1.09s CPU time)\n\nRan 38 tests for test/Vault.t.sol:VaultTest\n[PASS] testFuzz_TreasuryNeverExceedsWeeklyCap(uint256,uint16,uint256[5]) (runs: 512, μ: 3269617, ~: 3285507)\n[PASS] testFuzz_VestingNeverOverpays(uint256,uint64,uint64,uint32[4]) (runs: 512, μ: 1627381, ~: 1593582)\n[PASS] testFuzz_VestingNeverUnlocksMoreThan25PercentInAWeek(uint256,uint64,uint64,uint64) (runs: 512, μ: 1345385, ~: 1348857)\n[PASS] test_BoundVestingRejectsOtherTokens() (gas: 1407419)\n[PASS] test_CreateVestingNeedsAToken() (gas: 36022)\n[PASS] test_EachLaunchVaultPaysItsOwnFee() (gas: 4528792)\n[PASS] test_FactoryBurnsFeeOncePerProjectAndRegisters() (gas: 3334034)\n[PASS] test_FactoryChargesEachNewProject() (gas: 3861182)\n[PASS] test_FactoryNeedsRadarWalletWhenCharging() (gas: 6067)\n[PASS] test_FactoryRejectsShortDelay() (gas: 36613)\n[PASS] test_FactoryRevertsWithoutImd() (gas: 1930918)\n[PASS] test_FeeGoesToRadarNotBurned() (gas: 1331590)\n[PASS] test_FreeFactoryOnChainsWithoutImd() (gas: 1384053)\n[PASS] test_LaunchTreasuryReceivesRemainder() (gas: 3087508)\n[PASS] test_LaunchVestingReceivesRemainderAndVests() (gas: 2135921)\n[PASS] test_LaunchVestingVestsEveryTokenAndHasNoRecover() (gas: 2285174)\n[PASS] test_PayForRobinhoodProject() (gas: 149438)\n[PASS] test_RobinhoodLaunchVaultPaidOnEthereum() (gas: 1305813)\n[PASS] test_TreasuryCancelAndExpiry() (gas: 2536737)\n[PASS] test_TreasuryCapIgnoresDepositsWithinEpoch() (gas: 2684393)\n[PASS] test_TreasuryDelayAndAnyoneExecutes() (gas: 2615189)\n[PASS] test_TreasuryDestinationAllowlist() (gas: 2151691)\n[PASS] test_TreasuryEth() (gas: 2424892)\n[PASS] test_TreasuryManagerChangeIsDelayed() (gas: 2197141)\n[PASS] test_TreasuryOnlyManagerQueues() (gas: 2076121)\n[PASS] test_TreasuryRateCappedAt25Percent() (gas: 2132428)\n[PASS] test_TreasuryRulesOnlyTighten() (gas: 2481134)\n[PASS] test_TreasuryTighteningHoldsQueued() (gas: 2792125)\n[PASS] test_TreasuryWeeklyCap() (gas: 2749602)\n[PASS] test_VestingBeneficiaryCancel() (gas: 1418534)\n[PASS] test_VestingBeneficiaryChangeIsDelayed() (gas: 1549654)\n[PASS] test_VestingCliffThenLinear() (gas: 1630631)\n[PASS] test_VestingExtendAfterReleaseNeverUnderflows() (gas: 1445951)\n[PASS] test_VestingFeeOnTransferToken() (gas: 2102155)\n[PASS] test_VestingOnlyExtends() (gas: 1411611)\n[PASS] test_VestingRecoverOtherTokensOnly() (gas: 1466339)\n[PASS] test_VestingRejectsFasterThan25PercentAWeek() (gas: 1460103)\n[PASS] test_VestingTopUpFollowsSchedule() (gas: 1365133)\nSuite result: ok. 38 passed; 0 failed; 0 skipped; finished in 3.00s (258.16ms CPU time)\n\nRan 2 tests for test/RobinhoodFactory.t.sol:RobinhoodInvariantTest\n[PASS]\nRobinhoodInvariantTest invariants:\n[PASS] invariant_conservation\n[PASS] invariant_neverAheadOfSchedule\n[PASS] invariant_noFeeAndNoCustody\n[PASS] invariant_registry\n RobinhoodInvariantTest invariants (runs: 128, calls: 6144, reverts: 0)\n\n╭------------------+---------------------+-------+---------+----------╮\n| Contract         | Selector            | Calls | Reverts | Discards |\n+=====================================================================+\n| RobinhoodHandler | badCreate           | 680   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | createLaunchVesting | 644   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | createTreasury      | 709   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | createVesting       | 714   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | depositTreasury     | 700   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | fundVesting         | 700   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | payFor              | 646   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | release             | 699   | 0       | 0        |\n|------------------+---------------------+-------+---------+----------|\n| RobinhoodHandler | warp                | 652   | 0       | 0        |\n╰------------------+---------------------+-------+---------+----------╯\n\n[PASS] test_HandlerReachesRelease() (gas: 6610745)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 5.33s (5.33s CPU time)\n\nRan 1 test for test/Invariants.t.sol:FactoryInvariantTest\n[PASS] invariant_feesAndRegistry() (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------+---------------------+-------+---------+----------╮\n| Contract       | Selector            | Calls | Reverts | Discards |\n+===================================================================+\n| FactoryHandler | createLaunchVesting | 4026  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | createTreasury      | 4156  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | createVesting       | 4051  | 0       | 0        |\n|----------------+---------------------+-------+---------+----------|\n| FactoryHandler | payFor              | 4151  | 0       | 0        |\n╰----------------+---------------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.13s (9.12s CPU time)\n\nRan 11 test suites in 9.13s (27.23s CPU time): 147 tests passed, 0 failed, 0 skipped (147 total tests)\n","passed":true},{"durationMs":81,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"TreasuryVault.acceptManager()\",\"TreasuryVault.cancel(uint256)\",\"TreasuryVault.cancelManager()\",\"TreasuryVault.decreaseRate(uint16)\",\"TreasuryVault.deposit(address,uint256)\",\"TreasuryVault.execute(uint256)\",\"TreasuryVault.increaseDelay(uint64)\",\"TreasuryVault.proposeManager(address)\",\"TreasuryVault.queue(address,address,uint256)\",\"TreasuryVault.receive()\",\"TreasuryVault.removeDestination(address)\",\"TreasuryVault.restrictDestinations(address[])\",\"VaultFactory.createLaunchVesting(address,uint64,uint64,uint64)\",\"VaultFactory.createTreasury(address,address,uint64,uint16,address[])\",\"VaultFactory.createVesting(address,address,uint256,uint64,uint64,uint64)\",\"VaultFactory.payFor(uint256,address)\",\"VaultFactoryNoFee.createLaunchVesting(address,uint64,uint64,uint64)\",\"VaultFactoryNoFee.createTreasury(address,address,uint64,uint16,address[])\",\"VaultFactoryNoFee.createVesting(address,address,uint256,uint64,uint64,uint64)\",\"VaultFactoryNoFee.payFor(uint256,address)\",\"VestingVault.acceptBeneficiary()\",\"VestingVault.cancelBeneficiary()\",\"VestingVault.extendDuration(uint64)\",\"VestingVault.proposeBeneficiary(address)\",\"VestingVault.recover(address)\",\"VestingVault.release(address)\",\"VestingVault.sync(address)\"],\"files\":{\".gitignore\":6,\"ADAPTATION.md\":133,\"README.md\":129,\"SWARM_BRIEF.md\":131,\"foundry.toml\":23,\"launch.json\":10,\"src/TreasuryVault.sol\":373,\"src/VaultFactory.sol\":165,\"src/VaultFactoryNoFee.sol\":18,\"src/VestingVault.sol\":226,\"test/Audit.t.sol\":892,\"test/EdgeCases.t.sol\":514,\"test/Fork.t.sol\":37,\"test/Invariants.t.sol\":761,\"test/RobinhoodFactory.t.sol\":835,\"test/SharedCap.t.sol\":477,\"test/Vault.t.sol\":615},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"e6ecba2dc68d9b9d125bc2d9150e9f85bd7d5ac67cd61a1b703aa81a9f16a682","verifiedTreeHash":"0f940233f1ffc58b3f6a4788484ca1c33cb5f474","verifierVersion":"0.1.0+a6ee6548"}]}