{"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":"40aa3cf4-7ea3-493c-ba32-80aedb16ccb9","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"a9372550115b3ca2a326ff2057b5b7f022711409794e72cd08b41b0d213478e0","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"52c77656f96edcc87204d8dca8e43a8fcfdede031263d415e04ee0709f0c338b","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"52c77656f96edcc87204d8dca8e43a8fcfdede031263d415e04ee0709f0c338b","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"0afacc776a36d6c506a4c7eda4b39b085b976c3207c57c92e26d2799f4625ea3","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"52c77656f96edcc87204d8dca8e43a8fcfdede031263d415e04ee0709f0c338b","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"52c77656f96edcc87204d8dca8e43a8fcfdede031263d415e04ee0709f0c338b","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"5751bfc87ea46afdf4ac6cae02354fc2734625b4e7d325f4d92958d06609ebd8","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"647c9348a215ef5349cba3f6861c71bafaa0abf3723adf02f141ad0770ffced7","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"647c9348a215ef5349cba3f6861c71bafaa0abf3723adf02f141ad0770ffced7","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"f77712ed01f2448f995d508ce0ac65b04203156488692bdc7671cc0fcf565930","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"52c77656f96edcc87204d8dca8e43a8fcfdede031263d415e04ee0709f0c338b","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"52c77656f96edcc87204d8dca8e43a8fcfdede031263d415e04ee0709f0c338b","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"a7e7fa720324aa9caa09206cc1e039cf8fd5f19c6480226473a59b8121c4a3b8","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"52c77656f96edcc87204d8dca8e43a8fcfdede031263d415e04ee0709f0c338b","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"52c77656f96edcc87204d8dca8e43a8fcfdede031263d415e04ee0709f0c338b","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"24a1743d4a76e7662d1cb03e6766e88a0e5cc7c576b9b845a8687b5611deb82b","dependsOn":[],"execution":{"mustProduce":["src/LaunchToken.sol"],"network":false,"profile":"foundry","requires":[],"skillHash":"47381b166d1f406a34f0ebdc740ee21bc0383e56e7ad9084b26abf1bf7570904","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"47381b166d1f406a34f0ebdc740ee21bc0383e56e7ad9084b26abf1bf7570904","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"e2636557d13eae8945d7d5b546a78475a7410f08a4e56e2d68da03a8393b80f0","dependsOn":["build_contract_project","write_foundry_tests"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"223b7393693b4720245b336f677cda77d35504e3e2aca71758f4dd0dd36d9b4d","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"34855370ca090d6b3d67ca9be2b888d13d0f9f81b742b9f9f7eca5cad54281ba","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"34855370ca090d6b3d67ca9be2b888d13d0f9f81b742b9f9f7eca5cad54281ba","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Heirloom: a crypto inheritance vault (dead man's switch) on Sepolia, with a website.\n\nWHAT IT DOES\nAnyone can open their own vault, deposit ETH, and name one beneficiary (heir). The owner \"checks in\" from time to time. If the owner stops checking in for longer than the inactivity period they chose, the beneficiary can claim everything in the vault. Until a claim happens, the owner stays in full control.\n\nCONTRACT: one contract, HeirloomVault. No token. No admin, no owner of the contract, no fees, no upgrades, no pausing.\nEach wallet address has at most one vault, stored in the same contract. For each vault keep: balance, beneficiary, inactivity period, last check-in time.\n\nRules:\n1. openVault(beneficiary, period): creates the caller's vault. Beneficiary cannot be the zero address or the caller. Period must be between 1 day and 3650 days.\n2. deposit(): payable, adds ETH to the caller's vault. Counts as a check-in.\n3. checkIn(): resets the caller's last check-in time to now.\n4. withdraw(amount): owner takes ETH out at any time, even after the deadline has passed, as long as it has not been claimed. Counts as a check-in.\n5. setBeneficiary(newBeneficiary) and setPeriod(newPeriod): owner can change these at any time, same limits as rule 1. Each counts as a check-in.\n6. claim(owner): only that vault's beneficiary, and only when now > last check-in + period. Sends the whole balance to the beneficiary and closes the vault.\n7. closeVault(): owner withdraws everything and closes the vault.\n8. A view function returning a vault's balance, beneficiary, period, last check-in and the exact time it becomes claimable.\n9. Events for every action (opened, deposited, checked in, withdrew, beneficiary changed, period changed, claimed, closed) so the website can list vaults.\nSecurity: reentrancy guard on every function that sends ETH, update state before sending, reject direct ETH transfers that do not go through deposit().\n\nTESTS (Foundry)\n- The contract's ETH balance always equals the sum of all vault balances (invariant).\n- A beneficiary can never claim before the deadline, and nobody but the beneficiary can ever claim.\n- The owner can always withdraw before a claim, including after the deadline.\n- Any check-in pushes the deadline forward.\n- Fuzz the period limits and withdraw amounts.\n\nWEBSITE (built against the deployed contract)\n- Connect wallet. Clear banner: \"Sepolia test network, test ETH only.\"\n- \"My vault\": open a vault (beneficiary address, period picker with presets: 1 day for trying it out, 30 days, 6 months, 1 year), then show balance, beneficiary, a live countdown to when it becomes claimable, and buttons for Deposit, Check in, Withdraw, Change beneficiary, Change period, Close vault.\n- \"I'm a beneficiary\": list vaults naming the connected wallet as beneficiary (from events), each with its countdown and a Claim button that only works once the countdown reaches zero.\n- A short \"How it works\" section in plain English.\n- Look: calm and trustworthy, soft cream background, dark green accents, simple serif headings.","parentJobId":null,"planHash":"fcdc6d9be11feed3f28844b3b9b8bb5e8271783bc3e6c7224e40a13818c9d3f6","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"40aa3cf4-7ea3-493c-ba32-80aedb16ccb9","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-437-heirloom-crypto-inheritance-vault"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"50962","feedbackHash":"bd92a0fad809c43696303d78fbe45ce2d95a1ca3eeaf83362716a52dd07f02a3","nodeKey":"audit_economics","submissionHash":"e248f738fec028c2c3932c7951d9882dd37df1eddfbd50e428957feec14fd638","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50956","feedbackHash":"fdf984af513c97da589fdb59c694d1e889d60e33a0c19ff3fc36ae1bad795c8a","nodeKey":"audit_economics","submissionHash":"a9372550115b3ca2a326ff2057b5b7f022711409794e72cd08b41b0d213478e0","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51046","feedbackHash":"bd42fcf3fd1362b93c2fa1e74f276e047d38c30e8ec995ebdaecb15a2c012be1","nodeKey":"audit_flow","submissionHash":"0afacc776a36d6c506a4c7eda4b39b085b976c3207c57c92e26d2799f4625ea3","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50956","feedbackHash":"892314ff7a740383f2923a5cded7276e87c8393eb725dbda5c3f6fc569dff075","nodeKey":"audit_flow","submissionHash":"805ad4e24f55a8cb9a027431915399bcf4cb588564b179a9fe712a81c59c643b","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50975","feedbackHash":"ef8f96bf4ccfe8807b24ef967f57c25b55a28a90e7841e4d7b3d84aae0651593","nodeKey":"audit_judge","submissionHash":"6ea358ec5503aa3cc3505aac0578da399ec02483d1770e64757a2f3ffd28cabb","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50955","feedbackHash":"adddb12f1f87ea38956fec1dace2c491f32a9352abbb86c741d04d5963d6eb78","nodeKey":"audit_judge","submissionHash":"5751bfc87ea46afdf4ac6cae02354fc2734625b4e7d325f4d92958d06609ebd8","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50975","feedbackHash":"52b8dd83b1b54c1ec0c3a0dee8303920f264b01edc4965d4cd506ad5ec61caba","nodeKey":"audit_math","submissionHash":"f77712ed01f2448f995d508ce0ac65b04203156488692bdc7671cc0fcf565930","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50959","feedbackHash":"f310786300ed230a7ab3a60ced36f52e328c2814e155554f326ea6c73885e4f2","nodeKey":"audit_math","submissionHash":"76b2645e405b04fba6641a921deb1e8d3d34863852e55b9f1053d218c890acda","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51046","feedbackHash":"87620993f52f257bc62f47d9b37b38e2a2ace01558f536b0c055accab6fef5b8","nodeKey":"audit_permissions","submissionHash":"89537bd016e9450bc4fa39ce9888a10b11369912e0c169d9728f5477b7caad30","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50962","feedbackHash":"bb843affd37ce7ee3d220e000beeda43e0d155296641a05d03497e6fa02eee8d","nodeKey":"audit_permissions","submissionHash":"a7e7fa720324aa9caa09206cc1e039cf8fd5f19c6480226473a59b8121c4a3b8","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50971","feedbackHash":"6c08d0c62092d09c0c42b001e7634b26742df0c3c985ea4f9023f048ba407181","nodeKey":"build_contract_project","submissionHash":"24a1743d4a76e7662d1cb03e6766e88a0e5cc7c576b9b845a8687b5611deb82b","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51035","feedbackHash":"eb119e09fef144e01e0ec424fa62cb8f973ac0970c843310d55e15d137b31143","nodeKey":"build_contract_project","submissionHash":"036686ab320bb47273c7f6dcc2871a153dc45fdc53394b4b64bf23e3c72f474e","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"50971","feedbackHash":"4f107a7c23d48c8ae4eb63a9b66f4645a8f24556bff119e0de24c3d5137eacf4","nodeKey":"manifest","submissionHash":"e2636557d13eae8945d7d5b546a78475a7410f08a4e56e2d68da03a8393b80f0","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"50971","feedbackHash":"2d3bbc3889bf31ae0727af4e29f881209c4f23108a2575e199f22ad43be40164","nodeKey":"write_foundry_tests","submissionHash":"223b7393693b4720245b336f677cda77d35504e3e2aca71758f4dd0dd36d9b4d","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"40f9d0ed827daa5326a779d7e872bfbf3deade706b21ae7d9deeb5f87f06a89a","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"7f6d872cdf529112fce4f8744d0de6d365c21974aab8195784429e40cb5d59fe","device":"8472a2af9a9cf974","findings":[],"hash":"036686ab320bb47273c7f6dcc2871a153dc45fdc53394b4b64bf23e3c72f474e","nodeId":"945cddc2-f961-4884-abba-647a5f2c3e4e","outcome":"completed","summary":"Everything is committed on main and verified. Nothing else is pending.\n\n**Outcome.** The Heirloom deliverable is complete and committed as one commit on `main`. All three acceptance commands pass offline with an empty environment, matching how the verifier runs.\n\n| Check | Result |\n| --- | --- |\n| `forge build` | passes with solc 0.8.26 |\n| `forge test` | 74 tests pass, 0 fail |\n| `forge fmt --check` | clean |\n\n**What was built.**\n- `src/HeirloomVault.sol` implements all nine rules: one vault per address, beneficiary and period limits, deposit, check-in, withdraw at any time before a claim, setting changes that count as check-ins, claim strictly after the deadline, close, a full view including the exact claimable timestamp, and events for every action. Sends happen last behind a reentrancy guard, and direct ETH transfers are rejected.\n- `src/LaunchToken.sol` is the required fixed-supply ERC-20 with no constructor arguments and no admin paths, matching the protected token test.\n- `script/Deploy.s.sol` exposes plain deploy functions the tests call directly. It reads no environment variables.\n- Tests cover every success and failure path, fuzz period limits and withdraw amounts, prove any check-in pushes the deadline forward, that nobody but the beneficiary can claim and never before the deadline, that the owner can always withdraw before a claim, reentrancy through claim and withdraw, and an invariant suite asserting the contract balance always equals the sum of vault balances.\n- `web/` is a static site with the Sepolia banner, \"My vault\" with the period presets and live countdown, \"I'm a beneficiary\" built from events with Claim enabled only at zero, and a plain-English \"How it works\". It uses a vendored ethers build, so no network fetch is needed. Its read and event logic was exercised end to end against a local anvil chain from a scratch script.\n- `README.md` documents design assumptions, deployment parameters, website setup and the operational responsibilities of owners, beneficiaries and hosts.\n\n**Two things to know.**\n- The contract is not deployed. This task does not authorize transactions, so `web/config.js` holds a placeholder address and deploy block. Fill both in after running the deploy script, as the README describes.\n- Three linter warnings are excluded in `foundry.toml`, each with its reason. The reentrancy one is a false positive on the guarded send, which the reentrancy tests exercise directly.","treeHash":"362b296250617bd5c723199cb16f08a4f8ddb483","usage":{"cachedInputTokens":1357325,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":72229,"runtime":"claude","turns":49,"wallClockMs":836123}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"02ae6543274731ab","findings":[],"hash":"0afacc776a36d6c506a4c7eda4b39b085b976c3207c57c92e26d2799f4625ea3","nodeId":"58444487-5f5b-490f-aac0-804111632033","outcome":"completed","summary":"Both prior findings are confirmed fixed:\n\n- Crafted URLs cannot override the configured vault.\n- Beneficiary controls refresh and revalidate eligibility before claims.\n\nAll 91 Foundry tests and 12 browser regression tests passed. No remaining defects were substantiated.\n\nWrote `{\"findings\":[]}` to [.imd-findings.json](/home/imd-worker/.identitymd/work/40aa3cf4-7ea3-493c-ba32-80aedb16ccb9/58444487-5f5b-490f-aac0-804111632033/.imd-findings.json). Source files are unchanged. Validation was local; no live Sepolia deployment was checked.","treeHash":null,"usage":{"cachedInputTokens":533504,"inputTokens":81338,"model":"gpt-6-astra","outputTokens":3550,"runtime":"codex","turns":4,"wallClockMs":171941}},{"artifacts":[],"attempt":1,"bundleHash":"04b0b225cf4895abd2d18259f66f178ab8e728be9823283de3f1865adbd19d90","device":"35c52a5b502e847c","findings":[{"description":"The requirement and contract comments say the contract ETH balance always equals the sum of vault balances. Reverting receive/fallback only prevents ordinary calls. A contract can force ETH into HeirloomVault with SELFDESTRUCT, including under the configured Cancun EVM, without executing either function. This leaves unaccounted ETH permanently in the contract. Existing deposits remain backed; this reproduction does not demonstrate theft, insolvency, or blocked withdrawals. Exact equality cannot generally be guaranteed for native ETH against forced transfers. The specification should distinguish equality over normal vault calls from solvency in the presence of forced ETH, and explicitly decide how unaccounted surplus is treated. The submitted handler retains exact equality over normal vault calls; the universal guarantee is reported here instead of asserted as correct.","line":265,"path":"src/HeirloomVault.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {HeirloomVault} from \"src/HeirloomVault.sol\";\n\ncontract ForcedEtherSender {\n    constructor(address payable recipient) payable {\n        selfdestruct(recipient);\n    }\n}\n\ncontract ForcedEtherFindingTest is Test {\n    function test_forcedEtherMustNotBreakRequiredBalanceEquality() public {\n        HeirloomVault vault = new HeirloomVault();\n        address owner = address(0xA11CE);\n        address heir = address(0xB0B);\n        vm.deal(owner, 1 ether);\n        vm.startPrank(owner);\n        vault.openVault(heir, 1 days);\n        vault.deposit{value: 1 ether}();\n        vm.stopPrank();\n\n        vm.deal(address(this), 1 wei);\n        new ForcedEtherSender{value: 1 wei}(payable(address(vault)));\n\n        // There is exactly one vault, so its balance is the sum of all vault balances.\n        (uint256 accounted,,,,) = vault.getVault(owner);\n        assertEq(address(vault).balance, accounted, \"forced ETH violates the required exact equality\");\n    }\n}","reproduction":"At the default local timestamp, deploy HeirloomVault. As 0xA11CE, openVault(0xB0B, 1 days) and deposit 1 ether. Deploy a payable helper with 1 wei whose constructor selfdestructs to the vault. Expected by the requested universal invariant: address(vault).balance equals the only vault balance. Actual: 1000000000000000001 wei held versus 1000000000000000000 wei accounted. Ran forge test --offline --out test/scratch/out --cache-path test/scratch/cache --match-path test/scratch/ForcedEtherFinding.t.sol -vv; the single test failed at this equality assertion.","severity":"low","title":"Forced ETH violates the promised exact-balance invariant"}],"hash":"12c160daee1aa93ef8a5728036726b801bbfd406f306ed072a7da37178d91c28","nodeId":"6651b130-21f7-46e1-a856-85236672b6a3","outcome":"completed","summary":"Added failure-path, reentrancy, payout rollback, fuzz, and independent accounting invariant tests covering 24,576 random calls.\n\nOffline `forge build` and `forge test` passed: **87 tests, zero failures**.\n\nReported one low-severity limitation in [.imd-findings.json](/home/identitymd5/.identitymd/work/40aa3cf4-7ea3-493c-ba32-80aedb16ccb9/6651b130-21f7-46e1-a856-85236672b6a3/.imd-findings.json): forced ETH breaks the promised exact balance equality. The report includes the reproduced failing test; no loss of deposited funds was demonstrated.","treeHash":"e436d16fddfba6454471a6ac1ea05d8b70d04c75","usage":{"cachedInputTokens":1025664,"inputTokens":87195,"model":"gpt-6-astra","outputTokens":17468,"runtime":"codex","turns":6,"wallClockMs":586193}},{"artifacts":[],"attempt":1,"bundleHash":"a8ec4810801658706ee3e13ed7889ba2078a12315fadafd14a1d2a84d03a7d9f","device":"35c52a5b502e847c","findings":[],"hash":"223b7393693b4720245b336f677cda77d35504e3e2aca71758f4dd0dd36d9b4d","nodeId":"6651b130-21f7-46e1-a856-85236672b6a3","outcome":"completed","summary":"Added two regression tests covering rejected actions on expired/absent vaults and withdrawal boundaries after expiry. Updated stale test documentation.\n\nVerified offline:\n\n- `forge build` passed.\n- `forge test` passed: **91 tests, 0 failures**, including random-sequence invariants.\n\nChanges are confined to `test/`. The existing forced-ETH limitation remains documented.","treeHash":"f7e8e1956a97d3c881a5e0eaf1a5fce5edfde608","usage":{"cachedInputTokens":865792,"inputTokens":74169,"model":"gpt-6-astra","outputTokens":6252,"runtime":"codex","turns":5,"wallClockMs":239396}},{"artifacts":[],"attempt":1,"bundleHash":"3d40c1a264cbcb330c5579156e090ebc8021a13c0b2d7414e0513af13b2ce6e0","device":"35c52a5b502e847c","findings":[],"hash":"24a1743d4a76e7662d1cb03e6766e88a0e5cc7c576b9b845a8687b5611deb82b","nodeId":"945cddc2-f961-4884-abba-647a5f2c3e4e","outcome":"completed","summary":"Addressed all five reproduced findings: removed URL overrides, refreshed claim eligibility, bounded discovery with recovery paths, and corrected forced-ETH documentation and tests. Vault executable logic is unchanged.\n\nValidation passed: `forge build`, `forge test` (76 tests), `forge fmt --check`, and 12 browser regression tests.\n\nRecorded every verdict in `.imd-responses.json`.","treeHash":"77046d8dfb7bdcad9ebaa408f8bff2ce043179f6","usage":{"cachedInputTokens":1050112,"inputTokens":86141,"model":"gpt-6-astra","outputTokens":23597,"runtime":"codex","turns":7,"wallClockMs":755023}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"3c7630b22a73c1fb","findings":[{"description":"setup() sends openVault and then deposit() with no explicit gasLimit, so ethers uses Anvil's exact eth_estimateGas result. deposit() writes vault.lastCheckIn = block.timestamp (src/HeirloomVault.sol:286). If the estimate is taken while the pending timestamp still equals the openVault block's timestamp, that SSTORE writes the value already stored and is priced as a no-op. The estimate comes back as 53840 gas. The deposit is then mined in a block with a later timestamp, where the write really changes the slot and costs more, so the transaction runs out of gas and reverts. The test fails inside setup, before any website assertion runs. Every test calls setup(), so any of the 12 regression tests added as evidence for this round's frontend fixes can fail at random. The other transactions in the file already pass an explicit gasLimit (checkIn/setPeriod use { gasLimit: 100000 }), but setup's deposit does not. This does not affect the contract or its Foundry suite: forge test passes 91/91. Real wallets estimate against a later pending timestamp and add headroom. Fix: pass an explicit gasLimit, e.g. vault.deposit({ value: ethers.parseEther('1'), gasLimit: 100000 }), in setup(). Also do this for the other test transactions that call _checkIn and have no explicit limit, such as the attacker deposit at line 211.","line":42,"path":"test/web/app.test.cjs","reproduction":"Ran `node --test test/web/app.test.cjs` 12 times on the unmodified tree. 7 runs each had exactly one failure, in varying tests (lines 177, 52, 140, 93, 149, 52, 52). Full stacks were captured for 4 of these failures (tests at lines 177, 93, 149 and 52; the other 3 runs were filtered to test names only), and all 4 had the same stack: `at async setup (test/web/app.test.cjs:42:3)`, CALL_EXCEPTION, receipt status 0, gasUsed 53840 == gas limit (out of gas). To isolate the cause, I ran the compiled HeirloomVault on a fresh Anvil (chain 11155111, cancun) with automine off. openVault(0x7099...79C8, 86400) was mined at timestamp 1800001000. `cast estimate --value 1ether <vault> deposit()` then returned 53840. I sent deposit with gas limit 53840 and mined it in a later block: receipt status false, gasUsed 53840. Expected: setup always funds the vault and each website test checks the frontend behaviour. Actual: more than half of full-suite runs (7 of 12) fail in fixture setup because of an under-estimated gas limit.","severity":"low","title":"Website regression suite is flaky: setup's deposit runs out of gas when estimated in the same second as openVault"}],"hash":"5751bfc87ea46afdf4ac6cae02354fc2734625b4e7d325f4d92958d06609ebd8","nodeId":"58175c75-ef5b-4648-82e4-27ef3c33bb21","outcome":"completed","summary":"All four findings from last round are fixed. I found one new low-severity problem, which is in the website tests, not the product. It's written to `.imd-findings.json`, and no repository files were changed.\n\n**Earlier findings**\n- **[medium] A crafted `?vault=` URL could redirect deposits — fixed.** `web/app.js:11` now takes the contract address only from `HEIRLOOM_CONFIG.address`, and there is no URL override anywhere. The author's regression test (`test/web/app.test.cjs:52`) passes: the deposit goes to the real vault and the attacker's balance doesn't change.\n- **[low] Claim buttons kept outdated deadlines — fixed.** The page now re-reads vault state every 30 seconds. \"Claimable\" is decided from the chain's block time, not the device clock, and results older than 30 seconds disable the button. Before sending a claim, the page re-reads the vault and refreshes it afterwards whether the claim succeeded or failed. The tests for both earlier cases pass: an owner check-in elsewhere and an owner shortening the period elsewhere.\n- **[low] Spam empty vaults could hide funded ones — fixed.** At most 4 vaults are read at a time, each failed read is retried once, and results that loaded are kept when others fail. A loading error is now shown differently from \"no vaults\". Funded vaults are listed first, empty ones are hidden unless you ask for them, pages hold 20 rows, and you can look up an owner directly. The test with 257 vaults and a limit of 32 simultaneous requests passes, and the page never has more than 8 requests in flight.\n- **[low] The \"balance always equals the sum of vaults\" claim ignored forced ETH — fixed.** The README and contract comments now say the contract always holds at least what it owes, and exactly that amount only when no ETH has been forced in. They also say forced surplus can never be recovered. The new `HeirloomVaultForcedEther.t.sol` reproduces the 1-wei case and fuzzes it. The full Foundry suite passes (91 tests).\n\n**New finding**\n- **[low] The website tests fail intermittently** (`test/web/app.test.cjs:42`). I ran the suite 12 times and 7 runs had one failing test, a different one each time. In the 4 failures where I captured the full error, the cause was the same: the shared setup's `deposit()` ran out of gas at 53,840. The gas limit is estimated while the chain time still equals the `openVault` block's time. At that moment the check-in write stores the value already there and is priced cheaply. The deposit is then mined a second later, the write costs more, and it runs out of gas. I confirmed this on a separate local Anvil chain: the estimate was 53,840, and a deposit mined in the next block with that limit reverted using all 53,840 gas.\n  - **Effect:** any of the 12 tests offered as proof of this round's website fixes can fail randomly before reaching its checks.\n  - **Not affected:** the contract and real wallets.\n  - **Fix:** give that deposit an explicit gas limit, as the file already does for `checkIn` and `setPeriod`.","treeHash":null,"usage":{"cachedInputTokens":915495,"inputTokens":32,"model":"claude-fable-5-1","outputTokens":11256,"runtime":"claude","turns":17,"wallClockMs":244470}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"377843575071cdb1","findings":[{"description":"The unauthenticated vault query parameter overrides HEIRLOOM_CONFIG.address. connect() checks address syntax and network, then binds both reader and writer to this replacement at lines 175-188. A malicious contract can return a nonzero beneficiary from getVault(), expose the normal Deposit form, and receive the payment at line 412. The footer echoes the replacement address, but there is no override warning or contract-identity validation. A victim must follow the crafted link and approve the deposit. This diverts new Sepolia test ETH deposits; it does not grant access to balances already in the genuine vault. This merges the economics, flow, and permissions reports of the same root cause. Make the configured deployment authoritative in the published site and remove the unrestricted URL override, or confine it to an explicit development build.","line":12,"path":"web/app.js","reproduction":"Executed on local Anvil with chain ID 11155111, the compiled HeirloomVault, unmodified web/app.js, the vendored ethers library, and a DOM/EIP-1193 harness. Set the page configuration in memory to genuine vault V=0x5FbDB2315678afecb367f032d93F642f64180aa3; no repository configuration was edited. Attacker A=0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC deploys F=0x663F3ad617193148711d28f5334eE4Ed07016602 using: pragma solidity 0.8.26; contract FakeVault { address payable immutable thief; address immutable heir; constructor(address payable t,address h){thief=t;heir=h;} function getVault(address) external view returns(uint256,address,uint256,uint256,uint256){return(0,heir,86400,1800000000,1800086401);} function deposit() external payable{(bool ok,)=thief.call{value:msg.value}(\"\");require(ok);} } Pass A and a nonzero heir to the constructor. Open the normal website with ?vault=F, connect victim 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266, enter 1 in Deposit, and submit. The captured transaction targets F with data 0xd0e30db0 and value 0xde0b6b3a7640000. Execution increases A's balance by exactly 1000000000000000000 wei, leaves V with zero ETH and no victim vault credit, and displays 'Deposit: confirmed.' Expected: deposits target V, or an untrusted replacement is rejected before offering ordinary vault transactions. Actual: the URL alone changes the payment destination. All transactions in this reproduction were local.","severity":"medium","title":"A crafted website URL replaces the configured vault and redirects deposits"},{"description":"The interval only calls tick(), which uses cached claimableAt values. refreshAll() runs on connection and after successful transactions from this page, with no block/event subscription or periodic state refresh. Owner activity elsewhere therefore leaves a beneficiary's displayed deadline and Claim button stale indefinitely. A check-in enables a premature claim that the contract rejects; shortening the period can keep an eligible claim disabled for years. The error path also leaves the stale cache intact. Existing funds remain protected by the contract, and reloading works around the problem. Refresh relevant vault state on blocks/events or a bounded polling interval, refresh after failed claims, and check current eligibility before presenting a claim as available.","line":352,"path":"web/app.js","reproduction":"Executed against the real compiled vault on local Anvil using unmodified web/app.js and a controlled browser clock. Owner O opens a vault for H with period 86400 and deposits 1 ETH at T=1800000000. H connects and caches claimableAt=1800086401. At timestamp 1800043200, O calls checkIn() from another session; getVault(O) now reports 1800129601. Advance chain and browser time to 1800086401 and run the page's interval callback. Expected: the page shows the extended deadline and disables Claim. Actual: Claim is enabled while isClaimable(O) is false. Clicking it displays 'Claim failed: The inactivity period has not fully passed yet.' No additional getVault read occurs and Claim remains enabled. Also reproduced the inverse: H cached deadline 2115446402 for a 315360000-second period; O changed the period to 86400, moving the actual deadline to 1800172802. At 1800172802, isClaimable(O) was true but the existing page still disabled Claim using 2115446402.","severity":"low","title":"Claim controls retain obsolete deadlines after another session changes a vault"},{"description":"Anyone can name a target beneficiary in a zero-balance vault. Discovery collects every historical owner and starts all getVault calls concurrently with Promise.all, then renders every still-open vault sorted by deadline. A single read failure discards the entire result and presents an empty beneficiary list, including a misleading 'No vault currently names this wallet as beneficiary' message. Attackers can increase the workload without depositing ETH or obtaining the beneficiary's consent. The RPC failure is conditional on provider limits; even a provider that accepts all reads displays all the empty entries ahead of later funded vaults. This affects website discovery, not contract authorization or solvency; direct contract claims remain possible. Bound read concurrency, paginate discovery, retry failed reads without discarding successful results, and distinguish loading errors from an empty result. A direct owner-address lookup provides a useful fallback.","line":264,"path":"web/app.js","reproduction":"Executed against a fresh compiled HeirloomVault on local Anvil with the unchanged frontend. O opens a vault naming H with period 31536000 and deposits 5 ETH. An attacker creates 256 distinct owner contracts, each calling openVault(H,86400) in its constructor without depositing. This was done in eight transactions creating 32 owners each, consuming 29590752 gas in total. Advance time beyond the last spam vault's lastCheckIn+86400, while O's one-year deadline remains in the future. H connects: the page renders 257 rows, with 256 enabled zero-balance Claim buttons before O's funded row at position 257. The harness observes 257 concurrent getVault reads. Repeat connection with the EIP-1193 test adapter explicitly rejecting eth_call requests when more than 32 are concurrently in flight (a simulated provider limit, not a claim about a particular public RPC). It rejects 225 reads; Promise.all enters the catch branch, zero rows are displayed, and both the error toast and the empty-list message appear. Expected under this limit: bounded requests still discover O's 5 ETH vault, or retain successful results with a recoverable error. Actual: attacker-created empty records cause the funded vault to disappear from the website.","severity":"low","title":"Unbounded beneficiary discovery lets empty-vault spam hide funded vaults"},{"description":"The README and HeirloomVault NatSpec claim that rejecting receive/fallback makes the contract balance always equal the sum of vault balances. ETH can enter without executing either function. The invariant handlers only exercise ordinary vault calls, so their passing equality assertions do not establish the stated live-chain guarantee. Forced surplus is not credited to any vault and no existing payout can remove it. Recorded deposits remain fully backed and withdrawable; this is a specification/documentation and adversarial-coverage gap, not a depositor-loss exploit. This merges the economics and math reports. test/ADVERSARIAL_TESTS.md acknowledges the limitation, but the unconditional claims and missing forced-ETH coverage remain. State the general solvency property as contract balance >= recorded liabilities, restrict exact equality to sequences without forced ETH, document unrecoverable surplus, and test that forced ETH cannot impair legitimate payouts. Rejecting all forced transfers cannot be achieved by changing receive/fallback.","line":40,"path":"README.md","reproduction":"Executed on local Anvil configured for Cancun against the compiled production vault. Alice opens a vault for Bob with period 86400 and deposits 1000000000000000000 wei; this is the only funded vault. Compile and deploy with 1 wei: pragma solidity 0.8.26; contract Forcer { constructor(address payable target) payable { selfdestruct(target); } } Pass the vault address as target. The constructor transfers its wei without calling the vault's reverting receive/fallback. getVault(Alice).balance remains 1000000000000000000, while the contract balance becomes 1000000000000000001. Expected under the documented exact-equality invariant: both are 1000000000000000000. Actual: they differ by 1 wei. Alice then calls closeVault(); her full recorded 1 ETH is paid out, hasVault(Alice) becomes false, and the contract retains 1 wei with zero recorded liabilities and no function that can pay the surplus.","severity":"low","title":"The unconditional exact-balance invariant excludes forced ETH"}],"hash":"6ea358ec5503aa3cc3505aac0578da399ec02483d1770e64757a2f3ffd28cabb","nodeId":"58175c75-ef5b-4648-82e4-27ef3c33bb21","outcome":"completed","summary":"Wrote four reproduced, deduplicated findings—1 medium and 3 low—to [.imd-findings.json](/home/worker/.identitymd/work/40aa3cf4-7ea3-493c-ba32-80aedb16ccb9/58175c75-ef5b-4648-82e4-27ef3c33bb21/.imd-findings.json).\n\nThe Foundry suite and all eight protected checks passed. Project source and configuration remain unchanged.","treeHash":null,"usage":{"cachedInputTokens":1437696,"inputTokens":128935,"model":"gpt-6-astra","outputTokens":14218,"runtime":"codex","turns":6,"wallClockMs":474858}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"468e82a89b9bfe18","findings":[{"description":"Boundary guide, external-call corner case 'receiver reverts'. claim() pays with a single push (`_send(msg.sender, amount)`) and reverts the whole claim on failure. The only remedy the design offers is the owner calling setBeneficiary (README lines 59-62), but the scenario the product exists for is precisely the one where the owner can no longer act. If the beneficiary is a contract without a payable receive/fallback (a token contract, a contract wallet whose fallback reverts, or an address chosen by mistake), every claim() reverts with TransferFailed for ever and the ETH is locked in the vault permanently: nobody else may claim, the owner is gone, and there is no sweep. Not an attacker-exploitable loss (the owner chose the address), so low. Any fix touches the agreed interface: either a pull pattern (claim() credits a `pendingWithdrawals[beneficiary]` and a separate `collect()` sends), or claim(owner, address to) letting a contract beneficiary redirect. Either preserves 'only the beneficiary can claim'. The judge should decide whether the current push design is accepted as-is; if so, the README's 'only blocks itself' wording should say plainly that a dead owner plus a non-receiving beneficiary means permanent loss.","line":225,"path":"src/HeirloomVault.sol","reproduction":"State: alice opens vault with beneficiary = address of contract NoReceive (no receive/fallback), period 1 day, deposits 1 ether. Warp to lastCheckIn + 1 days + 1; isClaimable(alice) == true. NoReceive calls vault.claim(alice): reverts TransferFailed(NoReceive, 1 ether). Warp to +4000 days and retry: same revert. Expected: heir obtains 1 ether once the deadline passes. Actual: 1 ether stays in the vault with no path out (owner absent, no other claimant, no sweep). Verified in test/scratch/MathBoundary.t.sol::test_beneficiaryWithoutReceive_fundsStuck (passes on this code, i.e. the lock reproduces).","severity":"low","title":"Beneficiary that cannot accept ETH makes the inheritance unrecoverable once the owner is gone (push-only payout boundary)"},{"description":"Numerical-gap guide, boundary x invariant seam. receive()/fallback() revert, but three ETH paths bypass them: (1) selfdestruct of a contract created in the same transaction (still transfers under Cancun, which this project compiles for), (2) sending ETH to the predicted CREATE2 address before the factory deploys it (the launch attestation publishes IMD_PROJECT_ADDRESS_i in advance, so the address is public before the code exists), (3) a validator naming the vault as fee recipient. The surplus is not attributable to any vault, cannot be withdrawn, claimed or swept, and makes the README statement (line 40: 'the contract balance always equals the sum of vault balances') and the tested invariant false. No user deposit is at risk; the only loss is the forced amount itself. This was already noted as a low in an earlier review round (test/ADVERSARIAL_TESTS.md). Reported again only because it is the single arithmetic invariant in the contract and it is stated unconditionally. Disposition options: accept and reword the README/NatSpec to 'ETH received through deposit()', or add an owner-less `skim()` that forwards `address(this).balance - totalDeposits` to a fixed burn/beneficiary (needs a `totalDeposits` accumulator, which is also a cheaper on-chain check of the invariant).","line":266,"path":"src/HeirloomVault.sol","reproduction":"State: alice opens a vault (bob, 1 day) and deposits 1 ether. Deploy `new Forcer{value: 1 wei}(payable(address(vault)))` where Forcer's constructor does `selfdestruct(target)`. Now getVault(alice).balance == 1 ether but address(vault).balance == 1 ether + 1 wei. alice.closeVault() returns 1 ether; address(vault).balance == 1 wei with no function able to move it. Expected per README/spec: contract balance == sum of vault balances at all times. Verified in test/scratch/MathBoundary.t.sol::test_forcedEth_breaksSumInvariant (passes on this code, i.e. the drift reproduces). Alternative trigger without selfdestruct: `vm.deal(address(vault), 1)` models pre-funding the CREATE2 address before deployment.","severity":"low","title":"Contract-balance == sum-of-vaults invariant only holds for ETH that arrives through the ABI; forced ETH (selfdestruct, pre-funding the CREATE2 address, coinbase) is stuck and contradicts the README cl"}],"hash":"76b2645e405b04fba6641a921deb1e8d3d34863852e55b9f1053d218c890acda","nodeId":"a70ac056-4ef1-42b2-86d3-36c3cb0195a7","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json`, no source or test file was changed, and the scratch tests were removed.\n\n**Result: no math defect in HeirloomVault.** The contract has no division, no scaling, no casts, no unchecked blocks and no fixed-point math. Its only arithmetic is a checked add on deposit, a guarded subtract on withdraw, and `lastCheckIn + period + 1` for the deadline. I verified each against the three assigned guides:\n\n- **Math Precision:** nothing to round or truncate. Overflow is impossible with period capped at 3650 days; I confirmed the deadline sum is exact even at a `uint64`-max timestamp.\n- **Boundary:** the strict deadline is correct on both sides for every period in range (fuzzed). Claim at exactly `lastCheckIn + period` reverts and one second later succeeds. An owner check-in in the deadline second wins. Zero-amount deposit and withdraw revert. The zero-address sentinel for \"no vault\" cannot be corrupted because both setters reject the zero beneficiary. Payable entry points all revert or account correctly.\n- **Numerical Gap:** the zero-balance fast paths in claim and closeVault skip the send but still clear state, so the invariant survives them. The website's countdown, period presets and wei formatting use BigInt correctly.\n\n**Two low findings reported**, both at the seam between the payout boundary and the stated invariant:\n\n1. **Non-receiving beneficiary locks funds forever.** A beneficiary contract with no payable fallback makes every claim revert with `TransferFailed`, and once the owner is gone nobody can recover the ETH. The push-only design is per spec, so the disposition is a judge call. I described a pull or redirect option that keeps \"only the beneficiary can claim\".\n2. **Forced ETH breaks the \"balance always equals sum of vaults\" claim.** Selfdestruct, pre-funding the published CREATE2 address, or coinbase rewards bypass the reverting receive function and leave unsweepable dust. No deposits are at risk. This was already noted in an earlier round and is included only because it is the contract's one stated arithmetic invariant.\n\nBoth findings carry a concrete call sequence that I ran and observed on this code. Neither is high or critical, so no proof test file is attached. The existing suite of 92 tests passes offline.","treeHash":null,"usage":{"cachedInputTokens":726545,"inputTokens":258,"model":"claude-fable-5-1","outputTokens":13872,"runtime":"claude","turns":25,"wallClockMs":209901}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"bb0a3bf63233e5e5","findings":[{"description":"The page takes its transaction destination from the unauthenticated ?vault= query parameter before consulting the deployment configuration. connect() checks only address syntax and the chain, then binds both reader and writer to that destination (lines 175-188). A link sender can supply a contract that implements getVault but forwards deposit payments to the attacker. The ordinary Heirloom interface presents that contract's invented vault state and reports the payment as a confirmed deposit. The footer changes to the supplied address, but there is no warning that the configured deployment was replaced or validation of its identity. This requires a victim to follow the crafted link and approve a deposit; it diverts new Sepolia test ETH deposits, not funds already held by the real vault. Make the configured deployment authoritative in the published website and remove the unrestricted URL override.","line":12,"path":"web/app.js","reproduction":"Executed locally with Anvil chain ID 11155111, the compiled HeirloomVault, vendored ethers 6.13.5, and unmodified web/app.js evaluated with a DOM/EIP-1193 harness. Configure the page in memory with genuine vault V = 0x5FbDB2315678afecb367f032d93F642f64180aa3. Attacker A = 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC deploys F = 0x663F3ad617193148711d28f5334eE4Ed07016602 from the following source, passing A and a nonzero heir H to its constructor:\npragma solidity 0.8.26;\ncontract FakeVault {\n    address payable public immutable thief;\n    address public immutable heir;\n    constructor(address payable t, address h) { thief = t; heir = h; }\n    function getVault(address) external view returns (uint256, address, uint256, uint256, uint256) {\n        return (0, heir, 86400, 1730000007, 1730086408);\n    }\n    fallback() external payable {\n        (bool ok,) = thief.call{value: msg.value}(\"\");\n        require(ok);\n    }\n}\nVictim visits the legitimate website at index.html?vault=0x663F3ad617193148711d28f5334eE4Ed07016602, connects their Sepolia account, enters 1 in Deposit, and approves the transaction. Observed eth_sendTransaction.to is F, A gains exactly 1000000000000000000 wei, V still holds 0 wei, and the website displays 'Deposit: confirmed.' Expected: the published site's deposit targets configured V, or the untrusted replacement is rejected before normal vault transactions are offered. No repository configuration was changed for this reproduction.","severity":"medium","title":"A shared URL can replace the trusted vault and redirect deposits"},{"description":"refreshAll() runs when this page connects and after this page successfully sends a transaction. Its only timer calls tick(), which reuses cached claimableAt values without reading contract state or events. Changes made by the owner in another session therefore leave an open beneficiary page stale indefinitely. An owner check-in leaves a premature Claim button enabled; shortening the period can leave a currently claimable vault's button disabled for years. Failed claims do not refresh the cache either. The contract still enforces authorization and timing correctly, and reloading/reconnecting works around the defect. Refresh vault state on new blocks or relevant events, update discovery, and revalidate eligibility before claiming.","line":352,"path":"web/app.js","reproduction":"Executed against the real compiled vault on local Anvil through unmodified web/app.js with a DOM/EIP-1193 harness and a controlled browser clock. Owner O opens a vault for H with period 86400 and deposits 1 ETH at T = 1790629261. H connects the website and caches claimableAt = 1790715662. O calls checkIn from another session at T + 43200 = 1790672461, moving the real claimableAt to 1790758862. Advance the chain and browser to 1790715662 and execute the page's interval callback: Claim is enabled even though isClaimable(O) is false. Clicking it produces 'Claim failed: The inactivity period has not fully passed yet.'; no new getVault read occurs and Claim remains enabled. Also executed the inverse: H loaded O's vault with a 315360000-second period and cached deadline 2106075663; O then called setPeriod(86400), producing actual deadline 1790802063. At chain/browser time 1790802063, isClaimable(O) was true while the same page's Claim button remained disabled using 2106075663. Expected: owner activity updates the displayed deadline and claim controls without requiring the beneficiary to reload.","severity":"low","title":"Beneficiary claim controls never refresh after another wallet changes the vault"}],"hash":"805ad4e24f55a8cb9a027431915399bcf4cb588564b179a9fe712a81c59c643b","nodeId":"58444487-5f5b-490f-aac0-804111632033","outcome":"completed","summary":"Wrote two reproduced findings to [.imd-findings.json](/home/imd-worker/.identitymd/work/40aa3cf4-7ea3-493c-ba32-80aedb16ccb9/58444487-5f5b-490f-aac0-804111632033/.imd-findings.json):\n\n- **Medium:** Crafted URLs can redirect deposits.\n- **Low:** Beneficiary claim controls retain stale deadlines.\n\nCovered vault control flow, callbacks, helpers, and website integration. All 87 Foundry tests passed. No project source files changed.","treeHash":null,"usage":{"cachedInputTokens":969088,"inputTokens":91253,"model":"gpt-6-astra","outputTokens":9489,"runtime":"codex","turns":5,"wallClockMs":334984}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"02ae6543274731ab","findings":[{"description":"The untrusted ?vault= query parameter takes precedence over HEIRLOOM_CONFIG.address. connect() only validates address syntax and the network before binding both the reader and transaction signer to that address (lines 175-188). Consequently, an attacker can send a link on the legitimate website's origin that replaces the approved HeirloomVault with an attacker contract implementing getVault() and deposit(). Its getVault() response makes the normal Deposit form available, and submitting that form transfers the user's ETH to the replacement contract (line 412), outside all HeirloomVault permission and withdrawal guarantees. This requires the victim to follow the crafted link and approve the ordinary deposit transaction; it does not compromise existing balances in the real vault. The footer echoes the replacement address, but there is no contract-identity check or warning that the configured deployment was overridden. Remove the URL override from the deployed website, or restrict it to an explicit development mode; production transactions should remain bound to the configured deployment.","line":12,"path":"web/app.js","reproduction":"Concrete attacker contract: deploy M with Solidity source `pragma solidity 0.8.26; contract FakeVault { address payable immutable taker; constructor() { taker = payable(msg.sender); } function getVault(address) external view returns (uint256,address,uint256,uint256,uint256) { return (0,address(0x4444),86400,block.timestamp,block.timestamp+86401); } function deposit() external payable { (bool ok,) = taker.call{value:msg.value}(\"\"); require(ok); } }`. It needs no vault events: querying its empty event history succeeds. Let C be the real configured vault. Send the victim the legitimate site's /index.html?vault=<M> URL. On Sepolia, connect the victim's wallet; M.getVault(victim) returns a nonzero beneficiary, so the UI displays an existing vault and enables Deposit. Enter 1 ETH and submit. Expected: the site uses C, or rejects the untrusted contract override before requesting a transaction. Actual: it requests deposit() on M with 1 ETH; if approved, M forwards that ETH to its deployer and C credits nothing. The wrong-target transaction request was reproduced locally using the unchanged web/app.js, the vendored ethers library, a DOM stub, and an EIP-1193 RPC stub; no transaction was broadcast. In that reproduction C=0x0000000000000000000000000000000000001111, M=0x0000000000000000000000000000000000002222, and victim=0x0000000000000000000000000000000000003333. The RPC returned chainId 0xaa36a7, that victim account, empty logs, and getVault=(0,0x0000000000000000000000000000000000004444,86400,T,T+86401). With an empty query string the captured eth_sendTransaction targeted C; changing only the query to ?vault=0x0000000000000000000000000000000000002222 produced to=M, data=0xd0e30db0 (deposit()), and value=0xde0b6b3a7640000 (1 ETH), with no override warning. This verifies the frontend redirection; the fake contract above supplies the concrete payout path.","severity":"medium","title":"Attacker-controlled URL can redirect vault deposits to an arbitrary contract"}],"hash":"89537bd016e9450bc4fa39ce9888a10b11369912e0c169d9728f5477b7caad30","nodeId":"990a380f-c65d-4ece-bb1c-2bae57d16900","outcome":"completed","summary":"Saved one medium finding in [.imd-findings.json](./.imd-findings.json): a crafted URL can redirect deposits to an attacker’s contract.\n\nReviewed authorization, payout callbacks, state transitions, and frontend trust boundaries. All 87 Foundry tests passed; no Solidity permission bypass was substantiated.\n\nThe frontend redirect was reproduced offline. Live Sepolia and the protected deployment harness were not executed.","treeHash":null,"usage":{"cachedInputTokens":751744,"inputTokens":82698,"model":"gpt-6-astra","outputTokens":8894,"runtime":"codex","turns":5,"wallClockMs":336336}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"3f6a9bdd601cb99f","findings":[{"description":"The previous medium finding is fixed. web/app.js now derives the contract address solely from window.HEIRLOOM_CONFIG.address (line 11) and never reads window.location.search, the hash, localStorage or any other attacker-influenced source; the only remaining location uses are the reload() calls on wallet events (lines 217-218). connect() still validates the configured address and the network before binding reader and writer to it (lines 184-197). config.js documents that URL parameters cannot override the deployment. The author added a regression test, test/web/app.test.cjs line 52, that loads the page with ?vault=<attacker FakeVault>, submits a 1 ETH deposit and asserts the eth_sendTransaction target is the configured vault, the attacker balance is unchanged, the real vault credits the deposit, and the footer shows the configured address. I re-ran that test (see reproduction) and it passes. No further action is needed on this item.","line":11,"path":"web/app.js","reproduction":"forge build && node --test test/web/app.test.cjs. The test 'crafted URL cannot redirect a deposit away from the configured vault' passes: with search='?vault='+fake.target the captured transaction .to equals the configured HeirloomVault, the attacker's balance is unchanged, and getVault(owner).balance becomes 2 ETH. Static confirmation: grep -n 'location\\|searchParams\\|localStorage' web/app.js returns only the two reload() handlers. Expected: no override path. Actual: no override path. Fixed.","severity":"info","title":"Resolved: ?vault= URL override removed; transactions are bound to the configured deployment (prior finding 520b79a8b5bc22c27305e2e8166050ac71f8feef4cf3388d5f0f5807cca21127)"},{"description":"About one run in four of node --test test/web/app.test.cjs fails, and a different test fails each time, including the regression test for the URL-override finding. Every failure originates in setup() at line 42: `await mined(vault.deposit({ value: 1 ETH }))` gets receipt status 0 with gasUsed equal to the gas limit, i.e. out of gas, not a contract revert. Root cause: setup() calls openVault (line 41) and deposit (line 42) back to back, so eth_estimateGas for deposit usually runs while the pending timestamp equals the block.timestamp openVault stored in vault.lastCheckIn. In that state `vault.lastCheckIn = block.timestamp` in _checkIn (src/HeirloomVault.sol:286) is a same-value SSTORE (cold 2100 + 100), so anvil returns 53840. If a second boundary passes before automine includes the transaction, the store becomes a nonzero-to-different-nonzero write (cold 2100 + 2900) and the real cost is 56640. The harness signer (ethers v6 JsonRpcSigner) forwards the estimate with no buffer, so the deposit runs out of gas and setup() throws before the test body runs. The author already works around the same effect elsewhere by passing gasLimit: 100000 to checkIn and setPeriod (lines 72, 85) but not in setup(). Impact is limited to the test suite: a verifier can see the override regression test and other web tests fail on unchanged code. In production the site uses the injected wallet's own estimation, which pads the limit, and on Sepolia the estimate's timestamp normally differs from lastCheckIn anyway. Minimal fix: pass an explicit gasLimit (e.g. 100000) to the deposit in setup() and to the openVault call, or add a fixed buffer in mined(); alternatively let anvil mine with a fixed block time so the estimate and inclusion timestamps agree.","line":42,"path":"test/web/app.test.cjs","reproduction":"1) Flake: run `for i in 1 2 3 4 5 6 7 8; do node --test test/web/app.test.cjs 2>&1 | grep -E '^(✔|✖)'; done`. In my runs 3 of 12 invocations had 1-2 failing tests, each with 'transaction execution reverted' from setup at test/web/app.test.cjs:42, receipt.status 0 and gasUsed 53840. 2) Deterministic root-cause probe (node script in /tmp using web/vendor/ethers.umd.min.js and out/HeirloomVault.sol/HeirloomVault.json against `anvil --chain-id 11155111 --hardfork cancun --timestamp 1800000000`): deploy HeirloomVault; owner.openVault(heir, 86400) mined at timestamp T; est = vault.deposit.estimateGas({value: 1 ether}) -> 53840; evm_setNextBlockTimestamp(T+1); vault.deposit({value: 1 ether, gasLimit: est}) -> receipt status 0, gasUsed 53840 (out of gas). Re-running estimateGas after that timestamp-changing block returns 56640, exactly 2800 more. Expected: setup() deposit always mines successfully so every test exercises its body. Actual: the deposit fails whenever a second boundary falls between the estimate and inclusion, making the suite's pass/fail depend on wall-clock timing.","severity":"low","title":"Website regression suite is nondeterministic: shared setup() deposit is sent with an exact gas estimate that is 2800 gas short when the mined block's timestamp differs from the estimate's"}],"hash":"a7e7fa720324aa9caa09206cc1e039cf8fd5f19c6480226473a59b8121c4a3b8","nodeId":"990a380f-c65d-4ece-bb1c-2bae57d16900","outcome":"completed","summary":"The prior finding is settled as fixed, and I recorded one new low-severity item about the website test suite.\n\n**Prior medium finding (attacker-controlled `?vault=` override): fixed.** The website now takes the contract address only from the published config in web/config.js. No code path reads the query string, hash, or browser storage. The author added a regression test that loads the page with a crafted `?vault=` pointing at a fake vault and submits a deposit. I ran it and the transaction targets the configured vault, the attacker receives nothing, and the real vault credits the deposit.\n\n**New low finding: the website test suite is flaky.** Roughly one run in four fails, in a different test each time, including the override regression test. Every failure is the deposit in the shared setup helper running out of gas. The gas estimate is taken in the same second as the preceding openVault, so the contract's write of the last check-in timestamp looks like a same-value store. When the block is mined one second later the store costs 2800 gas more, and the harness signer sends the exact estimate with no buffer. I confirmed this deterministically against anvil by forcing the next block timestamp forward. The fix is to pass an explicit gas limit in the setup helper, as the author already does for other calls. This does not affect the deployed site, which relies on the wallet's padded estimate.\n\n**Other checks.** All 91 Foundry tests pass. The Slither leads (arbitrary ETH send in the private send helper, timestamp comparisons) are the intended owner and beneficiary payouts and deadline logic, not defects. Findings are written to `.imd-findings.json`. No repository files were changed and no scratch files remain.","treeHash":null,"usage":{"cachedInputTokens":787175,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":11495,"runtime":"claude","turns":14,"wallClockMs":229198}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"bb0a3bf63233e5e5","findings":[{"description":"The previous beneficiary-validation finding remains reproducible. _validateBeneficiary still rejects only address(0) and msg.sender, so both openVault and setBeneficiary accept address(this). claim requires the beneficiary itself to call, but HeirloomVault has no route that calls its own claim function. Its receive function also rejects ETH, so even a simulated call originating from the vault fails during payout. The owner can withdraw or correct the beneficiary while their key remains available; after the owner becomes unavailable, the inheritance path cannot release the recorded balance. Reject beneficiary == address(this) in the shared validator and cover both entry points with regression tests.","line":295,"path":"src/HeirloomVault.sol","reproduction":"Reproduced against the current compiled contract on local Cancun Anvil. Deploy V = 0x5FbDB2315678afecb367f032d93F642f64180aa3 at timestamp 1800000000. Alice = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 calls openVault(V, 86400), then deposit{value: 1000000000000000000}(). Both succeed. Separately, Carol = 0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC calls openVault(Bob, 86400), deposits 1 ether, then calls setBeneficiary(V); this also succeeds. Bob = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8. Mine at timestamp 1800172800. isClaimable returns true for both owners, but Bob's claim(owner) simulations revert NotBeneficiary(Bob, V). Every caller other than V fails the same authorization check, and V cannot initiate the required self-call. An eth_call simulation with from=V additionally reverts TransferFailed(V, 1000000000000000000), confirming that V refuses the payout. Both records retain 1 ether. Expected: openVault(V, 86400) and setBeneficiary(V) reject with InvalidBeneficiary(V), preventing this unusable inheritance configuration. Owner closeVault calls still recover both deposits, confirming the loss precondition is owner unavailability.","severity":"low","title":"Vault contract remains an accepted beneficiary with no usable claim path"}],"hash":"a9372550115b3ca2a326ff2057b5b7f022711409794e72cd08b41b0d213478e0","nodeId":"01436d73-5d4f-4742-b1e0-111e1ba6fb70","outcome":"completed","summary":"Wrote [.imd-findings.json](/home/imd-worker/.identitymd/work/40aa3cf4-7ea3-493c-ba32-80aedb16ccb9/01436d73-5d4f-4742-b1e0-111e1ba6fb70/.imd-findings.json) with one remaining low-severity finding.\n\n- **Unresolved:** vault address accepted as beneficiary; reproduced locally.\n- **Resolved:** forced-ETH documentation/tests, URL override, and empty-vault discovery flooding.\n- **Unchanged design assumption:** beneficiaries must accept ETH payouts.\n\nAll 91 repository Foundry tests and 12 browser tests passed. No implementation files changed.","treeHash":null,"usage":{"cachedInputTokens":562560,"inputTokens":76507,"model":"gpt-6-astra","outputTokens":5555,"runtime":"codex","turns":5,"wallClockMs":203419}},{"artifacts":[],"attempt":1,"bundleHash":"1c579dc449095b52d9f9a01537905b0b3c57c288c74dddce2d7d7e52850284c3","device":"35c52a5b502e847c","findings":[],"hash":"db3b95044107af03beb1ee0700effa6a0a4bc8bed705eea240fca3c9b4363130","nodeId":"24b12d99-75de-4d64-b145-debca5dd4d91","outcome":"completed","summary":"Created [launch.json](/home/identitymd5/.identitymd/work/40aa3cf4-7ea3-493c-ba32-80aedb16ccb9/24b12d99-75de-4d64-b145-debca5dd4d91/launch.json) matching `LaunchToken` and `HeirloomVault`.\n\n- Schema, metadata, and constructor checks passed.\n- `forge build` passed.\n- `forge test`: 87 passed, 0 failed.\n\nOnly `launch.json` changed outside the permitted scratch directory.","treeHash":"0b165796c3b7efeefbb3337f07adcd7ec3fe62ed","usage":{"cachedInputTokens":178048,"inputTokens":33122,"model":"gpt-6-astra","outputTokens":2885,"runtime":"codex","turns":4,"wallClockMs":116019}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"3f6a9bdd601cb99f","findings":[{"description":"_validateBeneficiary only rejects address(0) and msg.sender. Passing address(this) (the HeirloomVault address) passes validation in both openVault and setBeneficiary. claim(owner) requires msg.sender == vault.beneficiary, and the contract never calls itself, so no account can ever satisfy that check; even if it could, receive() reverts with DirectTransferRejected so _send would fail with TransferFailed. The result is a vault whose inheritance path is dead: the dead man's switch can never fire. While the owner is alive they can withdraw or fix the beneficiary, but the whole purpose of the product is the case where the owner is gone, and in that state every wei is locked forever. The website's beneficiary page would also never list it. This is a one-line validation gap analogous to the existing zero-address check, and the fix preserves the agreed design. (Verified in a scratch Foundry test: openVault(address(vault), 1 days) succeeds; after warp, a pranked claim from the vault address reverts with TransferFailed; no real caller can reach it at all.)","line":294,"path":"src/HeirloomVault.sol","reproduction":"State: fresh HeirloomVault at address V. Calls: alice.openVault(V, 1 days) -> succeeds (expected: revert InvalidBeneficiary). alice.deposit{value: 1 ether}(). vm.warp(+2 days): isClaimable(alice) == true. Any caller X != V calling claim(alice) reverts NotBeneficiary(X, V); V can never be msg.sender of its own external function. If alice's key is lost, 1 ether is unrecoverable by anyone. Fix: in _validateBeneficiary add `|| beneficiary == address(this)` to the revert condition.","severity":"low","title":"Vault contract itself is accepted as a beneficiary, producing a vault that can never be claimed"},{"description":"The specification and README state as an invariant that the contract's ETH balance always equals the sum of all vault balances, and receive()/fallback() revert to enforce it. That enforcement is bypassable: SELFDESTRUCT (still transfers value on Cancun under EIP-6780), block rewards to the vault address, or ETH sent to the deterministic CREATE2 address before the factory deploys the contract all raise address(this).balance without touching any vault. The contract has no code path that can ever move that surplus, so it is permanently locked. No depositor loses funds (every vault balance stays fully backed), so impact is limited to the invariant being unenforceable, the surplus being irrecoverable, and the three invariant test suites (HeirloomVaultInvariant, HeirloomVaultModelInvariant, and the README claim) asserting a property that does not hold on a live chain. ADVERSARIAL_TESTS.md acknowledges this and defers to a finding that is no longer in the tree, so it is restated here with the reproduction.","line":266,"path":"src/HeirloomVault.sol","reproduction":"State: alice.openVault(bob, 1 days); alice.deposit{value: 1 ether}(). Attack: deploy `contract Forcer { constructor(address payable t) payable { selfdestruct(t); } }` with `new Forcer{value: 0.5 ether}(payable(address(vault)))`. Observed: getVault(alice).balance == 1 ether, address(vault).balance == 1.5 ether (expected by the stated invariant: 1 ether). alice.closeVault() then leaves address(vault).balance == 0.5 ether with no vault open and no function able to send it. Verified in a scratch Foundry test on this code. Fix options that preserve the design: state the invariant as `balance >= sum of vault balances` (all accounting already reads storage, never address(this).balance, so nothing else changes), and add an invariant test that forces ETH in and checks every owner can still withdraw exactly their balance; optionally document that surplus is unrecoverable.","severity":"low","title":"Forced ETH (selfdestruct, pre-funded CREATE2 address, coinbase) breaks the exact-balance invariant and the surplus is stuck forever"},{"description":"app.js reads `params.get(\"vault\") || CFG.address` and uses that address for every read and every signed transaction, with no confirmation and only a small footer link showing the address. An attacker who shares `https://<legit-host>/index.html?vault=0xATTACKER` gets a page that looks identical, on the trusted domain, whose Deposit button sends the victim's ETH to 0xATTACKER. The attacker contract only needs a payable `deposit()` and a `getVault(address)` view returning an open vault so the UI renders the deposit form. The README documents the override as a testing convenience, but it ships in the production bundle. Economic impact is bounded by the victim's deposits (Sepolia test ETH today, but the same site design would carry to any deployment).","line":12,"path":"web/app.js","reproduction":"1. Deploy `contract Evil { function getVault(address) external pure returns (uint256,address,uint256,uint256,uint256) { return (0, address(1), 1 days, 1, 1); } function deposit() external payable {} }` at 0xEVIL. 2. Victim opens `index.html?vault=0xEVIL`, connects wallet: the page shows the vault view (exists == true because beneficiary != 0). 3. Victim enters 1 ETH and presses Deposit: `state.writer.deposit({value})` sends 1 ETH to 0xEVIL; no HeirloomVault state changes. Expected: the site only ever transacts with the configured contract, or shows a prominent 'address overridden' warning and requires confirmation. Fix: drop the override outside a dev build, or when it is present display a red banner with the full address and block transactions until the user acknowledges it.","severity":"low","title":"Website honours a ?vault= query override, so a link on the legitimate domain can route deposits to an attacker's contract"},{"description":"openVault has no cost beyond gas and no deposit requirement, and any address may name any other address as beneficiary. refreshHeirVaults collects every owner that ever emitted VaultOpened/BeneficiaryChanged for the connected wallet, calls getVault for all of them concurrently with Promise.all, keeps every open vault regardless of balance, and sorts by claimableAt. An attacker with N fresh addresses can open N zero-balance vaults naming the victim with period 1 day. On the victim's page these N entries all show 'Claimable now', sort above the real vault (which has a longer period), and each carries a live Claim button. With N in the thousands the Promise.all of eth_call requests fails on public RPCs, the catch branch sets heirVaults = [] and the real vault is not shown at all. The beneficiary can still claim by calling the contract directly, so funds are not at risk, but the website's core beneficiary flow is deniable at roughly one openVault (~70k gas) per entry. This is the cheapest griefing vector in the system and affects only the website, not the contract.","line":264,"path":"web/app.js","reproduction":"State: alice.openVault(victim, 365 days); alice.deposit{value: 5 ether}(). Attack: for i in 1..3000, from fresh address A_i: openVault(victim, 1 days) (no deposit). Victim connects wallet: queryFilter returns 3001 owners; readVault is issued 3001 times in parallel; on a rate-limited RPC the batch throws, toast 'Could not load beneficiary vaults', list is empty (expected: alice's 5 ETH vault listed). On an RPC that copes, 3000 'Claimable now' rows with Claim buttons precede alice's row. Fix: separate zero-balance vaults into a collapsed 'empty vaults' group, sort by balance desc then claimableAt, batch/paginate readVault calls, and add a 'look up a vault by owner address' input so a beneficiary can always reach a specific vault.","severity":"low","title":"Anyone can flood a target's 'I'm a beneficiary' list with empty vaults, burying or breaking discovery of the real one"},{"description":"claim() pays by push (`_send(msg.sender, amount)`) and reverts on failure. If the beneficiary is a contract without a payable receive/fallback (for example a token contract or a wallet contract with a receive that reverts), claim reverts with TransferFailed every time. The README documents that the owner can fix this with setBeneficiary, which is true while the owner is alive; the product's purpose, however, is the case where the owner is not. In that state the funds are locked with no recovery path. This is a design tradeoff required by the brief ('Sends the whole balance to the beneficiary'), so it is recorded as a trust assumption rather than a defect. A design-preserving mitigation is on the website: when the entered beneficiary has code, warn the owner, and optionally simulate a zero-value call to detect a non-payable recipient before submitting openVault/setBeneficiary.","line":225,"path":"src/HeirloomVault.sol","reproduction":"State: alice.openVault(address(R), 1 days) where R is `contract R { receive() external payable { revert(); } }` or any contract with no receive; alice.deposit{value: 1 ether}(); alice loses her key; warp +2 days. R.claimFrom(alice) reverts TransferFailed(R, 1 ether) forever; nobody else may call claim; alice cannot withdraw. Expected by product intent: the heir eventually receives 1 ether.","severity":"info","title":"Push-only payout: a beneficiary that cannot receive ETH leaves the vault permanently locked once the owner is gone"}],"hash":"e248f738fec028c2c3932c7951d9882dd37df1eddfbd50e428957feec14fd638","nodeId":"01436d73-5d4f-4742-b1e0-111e1ba6fb70","outcome":"completed","summary":"The findings file is in place and the harness excludes it from git on purpose. The scratch directory is removed and the working tree has no other changes.\n\n**Result: no critical, high, or medium findings.** The HeirloomVault contract holds up under the Economic Security, Invariant, and Flow Gap guides. Five findings are recorded in `.imd-findings.json`, four low and one informational.\n\n**What I covered**\n\n- Economic Security: every ETH-sending path, push-payment failure modes, griefing costs, front-running between owner and heir, and the timestamp boundary. No oracle, token, or ERC surfaces exist to attack.\n- Invariant: the balance-equals-liabilities law, the beneficiary-as-existence-flag coupling, view-versus-write agreement between isClaimable and claim, round-trips at 1 wei, and timer resets on every secondary path. All hold except against forced ETH.\n- Flow Gap: reentry into the unguarded functions from each payout callback, close-then-reopen inside a callback, claim on an empty vault, and period shortening after the deadline. All end in consistent state that matches the brief.\n- Ran the full existing suite, which passes, and three scratch probes, since deleted.\n\n**Findings written**\n\n- **Low, contract:** the vault contract's own address passes beneficiary validation. Such a vault can never be claimed by anyone, so funds are locked permanently once the owner is gone. One-line fix in the validator.\n- **Low, contract:** forced ETH via selfdestruct or a pre-funded CREATE2 address breaks the \"balance equals sum of vaults\" invariant and the surplus is stuck forever. No depositor loses funds. The invariant should be restated as greater-or-equal.\n- **Low, website:** the `?vault=` query override lets a link on the real domain route deposits to an attacker's contract.\n- **Low, website:** anyone can open unlimited empty vaults naming a victim, burying or breaking the beneficiary list, which loads every candidate in one parallel batch.\n- **Info, design:** push-only payout means a beneficiary contract that cannot receive ETH locks the vault after the owner is gone. Recorded as a trust assumption with a website-side mitigation.\n\nNone of the findings reach the severity that requires a Foundry proof file, so none carry one. The slither arbitrary-send lead was traced and rejected: ETH only ever goes to msg.sender or the owner-chosen beneficiary after state is cleared and behind the guard.","treeHash":null,"usage":{"cachedInputTokens":980896,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":19207,"runtime":"claude","turns":30,"wallClockMs":267734}},{"artifacts":[],"attempt":1,"bundleHash":"ebe47ddff880783da858704f4d8692f1c521d88f431ec907cd1b4110a15e0213","device":"35c52a5b502e847c","findings":[],"hash":"e2636557d13eae8945d7d5b546a78475a7410f08a4e56e2d68da03a8393b80f0","nodeId":"24b12d99-75de-4d64-b145-debca5dd4d91","outcome":"completed","summary":"Updated only `launch.json`, clarifying forced ETH behavior while preserving deployment fields.\n\nValidation passed: schema/refinements, token metadata, constructor ABIs, `forge build`, and all 99 tests, including both protected suites.","treeHash":"b13eedcc42a58a7f572067a0b1f8b5163a10e02a","usage":{"cachedInputTokens":236032,"inputTokens":26890,"model":"gpt-6-astra","outputTokens":4037,"runtime":"codex","turns":4,"wallClockMs":151030}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"377843575071cdb1","findings":[{"description":"The previously reported push-payment boundary remains unresolved. claim() still sends the entire balance to msg.sender, and _send() reverts the transaction when that beneficiary rejects ETH. With a permanently rejecting beneficiary and an unavailable owner, no callable recovery path exists: only the owner can withdraw, close or change the beneficiary, and the beneficiary cannot select another payout address. This is a conditional loss caused by the owner's beneficiary choice, not theft or interference with unrelated vaults. The current test_rejectingBeneficiary_onlyBlocksItself reproduces the failed claim and then relies on the owner withdrawing. The newer test_failedClaimRestoresEveryFieldAndCanBeRetried makes its receiver accept ETH before retrying; it does not cover an immutable rejecting receiver. README.md:64-67 still presents owner intervention as recovery without explicitly explaining permanent loss when the owner is gone. If push-only payout is accepted, explicitly document that condition as an unrecoverable inheritance limitation, as requested in the original advisory. A functional remedy would require an agreed interface change, such as allowing the authenticated beneficiary to select an ETH-capable payout recipient.","line":226,"path":"src/HeirloomVault.sol","reproduction":"Reproduced against the current source with: forge test --root . --offline --out /tmp/heirloom-review-out --cache-path /tmp/heirloom-review-cache --match-path test/HeirloomVault.t.sol --match-test test_rejectingBeneficiary_onlyBlocksItself -vvvv. At timestamp 1700000000, Alice calls openVault(address(heir), 86400) and deposit{value: 1 ether}(), where heir is the existing RejectingReceiver from test/HeirloomVault.t.sol:62. Its claimFrom(owner) calls vault.claim(owner), and its immutable receive() always reverts. Advance to timestamp 1700172800, after claimableAt = 1700086401, and call heir.claimFrom(alice). Expected inheritance outcome: the designated heir can collect Alice's 1 ether after inactivity. Actual trace: receive() reverts with 'no thanks', claim() reverts TransferFailed(address(heir), 1000000000000000000), hasVault(alice) remains true and getVault(alice).balance remains 1 ether. The test passes because it expects that failure and subsequently has Alice withdraw. If Alice is permanently unavailable after depositing, that final recovery transaction cannot occur; additional elapsed time cannot change the receiver's unconditional rejection, and the contract exposes no beneficiary-directed alternative payout.","severity":"low","title":"Beneficiary that cannot accept ETH still makes inheritance unrecoverable once the owner is gone"}],"hash":"f77712ed01f2448f995d508ce0ac65b04203156488692bdc7671cc0fcf565930","nodeId":"a70ac056-4ef1-42b2-86d3-36c3cb0195a7","outcome":"completed","summary":"Wrote [.imd-findings.json](/home/worker/.identitymd/work/40aa3cf4-7ea3-493c-ba32-80aedb16ccb9/a70ac056-4ef1-42b2-86d3-36c3cb0195a7/.imd-findings.json).\n\n- **Beneficiary payout:** low-severity advisory remains; reproduced the failed claim and confirmed recovery requires the absent owner.\n- **Forced ETH:** advisory resolved through corrected documentation and regression coverage; surplus remains unrecoverable.\n\n77 vault tests passed. No source or configuration files changed.","treeHash":null,"usage":{"cachedInputTokens":619904,"inputTokens":79563,"model":"gpt-6-astra","outputTokens":5264,"runtime":"codex","turns":5,"wallClockMs":185281}}],"verification":[{"checks":[{"durationMs":2254,"exitCode":0,"name":"build","output":"Compiling 27 files with Solc 0.8.26\nSolc 0.8.26 finished in 2.01s\nCompiler run successful!\n","passed":true},{"durationMs":722,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_deployToken_mintsSupplyToCaller() (gas: 358655)\n[PASS] test_deployVault_producesWorkingContract() (gas: 813728)\n[PASS] test_deploymentsAreIndependentOfCaller() (gas: 1300920)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 14.98ms (20.18ms CPU time)\n\nRan 11 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] testFuzz_transferConservesSupply(uint256) (runs: 256, μ: 90756, ~: 91099)\nLogs:\n  Bound result 3\n\n[PASS] test_approveAndTransferFrom() (gas: 195811)\n[PASS] test_approve_revertsToZeroAddress() (gas: 32332)\n[PASS] test_constructorEmitsMintTransfer() (gas: 8083)\n[PASS] test_fixedSupplyMintedToDeployer() (gas: 41674)\n[PASS] test_infiniteAllowanceIsNotDecremented() (gas: 122684)\n[PASS] test_metadata() (gas: 26725)\n[PASS] test_noMintOrAdminEntrypoints() (gas: 157457)\n[PASS] test_transfer() (gas: 93627)\n[PASS] test_transfer_revertsOnInsufficientBalance() (gas: 37492)\n[PASS] test_transfer_revertsToZeroAddress() (gas: 32408)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 40.74ms (24.24ms CPU time)\n\nRan 59 tests for test/HeirloomVault.t.sol:HeirloomVaultTest\n[PASS] testFuzz_anyCheckInPushesDeadlineForward(uint256,uint8) (runs: 256, μ: 265563, ~: 265827)\nLogs:\n  Bound result 192000001\n  Bound result 0\n\n[PASS] testFuzz_claim_neverBeforeDeadline(uint256,uint256) (runs: 256, μ: 222334, ~: 222614)\nLogs:\n  Bound result 315305104\n  Bound result 104878233\n\n[PASS] testFuzz_claim_onlyBeneficiary(address,uint256) (runs: 256, μ: 218872, ~: 219138)\nLogs:\n  Bound result 337629698\n\n[PASS] testFuzz_claim_succeedsOnceDeadlinePassed(uint256,uint256) (runs: 256, μ: 226233, ~: 226534)\nLogs:\n  Bound result 147018638\n  Bound result 352000000\n\n[PASS] testFuzz_openVault_periodLimits(uint256) (runs: 256, μ: 51000, ~: 43822)\n[PASS] testFuzz_ownerCanAlwaysWithdrawBeforeClaim(uint256,uint96) (runs: 256, μ: 237137, ~: 237215)\nLogs:\n  Bound result 183888891\n  Bound result 14570\n\n[PASS] testFuzz_setPeriod_limits(uint256) (runs: 256, μ: 143801, ~: 142809)\n[PASS] testFuzz_withdraw_amounts(uint96,uint96) (runs: 256, μ: 215881, ~: 201555)\nLogs:\n  Bound result 3000000000000000001\n\n[PASS] test_checkIn_afterDeadlineRestoresProtection() (gas: 271050)\n[PASS] test_checkIn_resetsTimerAndEmits() (gas: 174302)\n[PASS] test_checkIn_revertsWithoutVault() (gas: 34717)\n[PASS] test_claim_cannotRepeat() (gas: 242483)\n[PASS] test_claim_emptyVaultStillCloses() (gas: 151490)\n[PASS] test_claim_onlyAffectsThatVault() (gas: 393804)\n[PASS] test_claim_ownerLosesAccessAfterClaim() (gas: 417064)\n[PASS] test_claim_revertsBeforeDeadline() (gas: 201049)\n[PASS] test_claim_revertsExactlyAtDeadline() (gas: 292226)\n[PASS] test_claim_revertsForNonBeneficiary() (gas: 243512)\n[PASS] test_claim_revertsWithoutVault() (gas: 42312)\n[PASS] test_claim_succeedsAfterDeadlineAndClosesVault() (gas: 236518)\n[PASS] test_closeVault_afterDeadlineBeatsClaim() (gas: 241631)\n[PASS] test_closeVault_emptyVault() (gas: 147259)\n[PASS] test_closeVault_returnsEverythingAndClears() (gas: 219959)\n[PASS] test_closeVault_revertsWithoutVault() (gas: 39747)\n[PASS] test_closeVault_thenReopenStartsFresh() (gas: 321323)\n[PASS] test_constants() (gas: 16721)\n[PASS] test_deposit_addsBalanceAndChecksIn() (gas: 277343)\n[PASS] test_deposit_isolatedPerOwner() (gas: 358747)\n[PASS] test_deposit_revertsOnZeroValue() (gas: 126471)\n[PASS] test_deposit_revertsWithoutVault() (gas: 41485)\n[PASS] test_fallback_rejectsUnknownCalldata() (gas: 61982)\n[PASS] test_openVault_acceptsBoundaryPeriods() (gas: 224520)\n[PASS] test_openVault_revertsOnPeriodTooLong() (gas: 42918)\n[PASS] test_openVault_revertsOnPeriodTooShort() (gas: 37714)\n[PASS] test_openVault_revertsOnSelfBeneficiary() (gas: 35613)\n[PASS] test_openVault_revertsOnZeroBeneficiary() (gas: 35080)\n[PASS] test_openVault_revertsOnZeroPeriod() (gas: 37471)\n[PASS] test_openVault_revertsWhenAlreadyOpen() (gas: 131306)\n[PASS] test_openVault_storesFieldsAndEmits() (gas: 153352)\n[PASS] test_receive_rejectsDirectEth() (gas: 39580)\n[PASS] test_reentrancy_claimCannotDoubleSpend() (gas: 697332)\n[PASS] test_reentrancy_withdrawCannotDoubleSpend() (gas: 747805)\n[PASS] test_rejectingBeneficiary_onlyBlocksItself() (gas: 648164)\n[PASS] test_rejectingOwner_cannotBrickOthers() (gas: 863979)\n[PASS] test_setBeneficiary_oldHeirLosesClaim() (gas: 282393)\n[PASS] test_setBeneficiary_revertsOnInvalid() (gas: 155483)\n[PASS] test_setBeneficiary_revertsWithoutVault() (gas: 37362)\n[PASS] test_setBeneficiary_updatesAndChecksIn() (gas: 164567)\n[PASS] test_setPeriod_revertsOnInvalid() (gas: 164877)\n[PASS] test_setPeriod_revertsWithoutVault() (gas: 34912)\n[PASS] test_setPeriod_updatesAndChecksIn() (gas: 159899)\n[PASS] test_views_zeroForUnknownOwner() (gas: 40309)\n[PASS] test_withdraw_allowedAfterDeadlineBeforeClaim() (gas: 261516)\n[PASS] test_withdraw_cannotTouchAnotherVault() (gas: 353153)\n[PASS] test_withdraw_countsAsCheckIn() (gas: 228872)\n[PASS] test_withdraw_partialAndFull() (gas: 310335)\n[PASS] test_withdraw_revertsOnInsufficientBalance() (gas: 197649)\n[PASS] test_withdraw_revertsOnZeroAmount() (gas: 192759)\n[PASS] test_withdraw_revertsWithoutVault() (gas: 39931)\nSuite result: ok. 59 passed; 0 failed; 0 skipped; finished in 41.86ms (225.74ms CPU time)\n\nRan 1 test for test/HeirloomVaultInvariant.t.sol:HeirloomVaultInvariantTest\n[PASS]\nHeirloomVaultInvariantTest invariants:\n[PASS] invariant_claimRulesWereExercised\n[PASS] invariant_contractBalanceEqualsSumOfVaults\n[PASS] invariant_fundsAreConserved\n[PASS] invariant_openVaultsAreWellFormed\n HeirloomVaultInvariantTest invariants (runs: 64, calls: 3072, reverts: 0)\n\n╭--------------+----------------+-------+---------+----------╮\n| Contract     | Selector       | Calls | Reverts | Discards |\n+============================================================+\n| VaultHandler | checkIn        | 361   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | claim          | 337   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | closeVault     | 334   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | deposit        | 345   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | openVault      | 342   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | setBeneficiary | 338   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | setPeriod      | 336   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | warp           | 322   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | withdraw       | 357   | 0       | 0        |\n╰--------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 3280\n  Bound result 3543\n  Bound result 2493627\n  Bound result 140072578\n  Bound result 2944000\n  Bound result 315279730\n  Bound result 27277690\n  Bound result 2155\n  Bound result 315278584\n  Bound result 315273836\n  Bound result 98861836\n  Bound result 315273630\n  Bound result 999999999999999999\n  Bound result 315360000\n  Bound result 10000000000000000000\n  Bound result 315274705\n  Bound result 2563406\n  Bound result 5301\n  Bound result 3898\n  Bound result 136388450\n  Bound result 249228187\n  Bound result 125961078\n  Bound result 574476\n  Bound result 26710508\n  Bound result 305\n  Bound result 5050\n  Bound result 86399\n  Bound result 247792986\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 609.88ms (591.38ms CPU time)\n\nRan 4 test suites in 619.23ms (707.47ms CPU time): 74 tests passed, 0 failed, 0 skipped (74 total tests)\n","passed":true},{"durationMs":1156,"exitCode":0,"name":"slither","output":"[high/medium] arbitrary-send-eth at src/HeirloomVault.sol:304: HeirloomVault._send(address,uint256) (src/HeirloomVault.sol#304-307) sends eth to arbitrary user\n[low/medium] timestamp at src/HeirloomVault.sol:256: HeirloomVault.isClaimable(address) (src/HeirloomVault.sol#256-259) uses timestamp for comparisons\n[low/medium] timestamp at src/HeirloomVault.sol:214: HeirloomVault.claim(address) (src/HeirloomVault.sol#214-226) uses timestamp for comparisons\n[low/medium] timestamp at src/HeirloomVault.sol:251: HeirloomVault.hasVault(address) (src/HeirloomVault.sol#251-253) uses timestamp for comparisons","passed":true},{"durationMs":676,"exitCode":0,"name":"aderyn","output":"aderyn: no results at low impact or above","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"036686ab320bb47273c7f6dcc2871a153dc45fdc53394b4b64bf23e3c72f474e","verifiedTreeHash":"362b296250617bd5c723199cb16f08a4f8ddb483","verifierVersion":"0.1.0+48a57703"},{"checks":[{"durationMs":1991,"exitCode":0,"name":"build","output":"Compiling 29 files with Solc 0.8.26\nSolc 0.8.26 finished in 1.89s\nCompiler run successful!\n","passed":true},{"durationMs":4113,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_deployToken_mintsSupplyToCaller() (gas: 358655)\n[PASS] test_deployVault_producesWorkingContract() (gas: 813728)\n[PASS] test_deploymentsAreIndependentOfCaller() (gas: 1300920)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 30.42ms (609.62µs CPU time)\n\nRan 11 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] testFuzz_transferConservesSupply(uint256) (runs: 256, μ: 90732, ~: 91111)\nLogs:\n  Bound result 6000000000000000000\n\n[PASS] test_approveAndTransferFrom() (gas: 195811)\n[PASS] test_approve_revertsToZeroAddress() (gas: 32332)\n[PASS] test_constructorEmitsMintTransfer() (gas: 8083)\n[PASS] test_fixedSupplyMintedToDeployer() (gas: 41674)\n[PASS] test_infiniteAllowanceIsNotDecremented() (gas: 122684)\n[PASS] test_metadata() (gas: 26725)\n[PASS] test_noMintOrAdminEntrypoints() (gas: 157457)\n[PASS] test_transfer() (gas: 93627)\n[PASS] test_transfer_revertsOnInsufficientBalance() (gas: 37492)\n[PASS] test_transfer_revertsToZeroAddress() (gas: 32408)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 33.81ms (34.57ms CPU time)\n\nRan 11 tests for test/HeirloomVaultAdversarial.t.sol:HeirloomVaultAdversarialTest\n[PASS] testFuzz_rejectedOwnerActionsCannotDelayAnEligibleClaim(uint256,uint256) (runs: 1000, μ: 325179, ~: 324385)\nLogs:\n  Bound result 432000000\n\n[PASS] testFuzz_validWithdrawalsRoundTripEvenAfterDeadline(uint128,uint256,uint256) (runs: 1000, μ: 353195, ~: 354106)\nLogs:\n  Bound result 340282366920938463463374607431768211455\n  Bound result 99446373887807520737366459319295540565\n  Bound result 863955795\n\n[PASS] test_claimCallbackSeesDeletedStateAndBlocksEveryPayout() (gas: 1018758)\n[PASS] test_closeCallbackSeesDeletedStateAndBlocksEveryPayout() (gas: 1015560)\n[PASS] test_failedClaimRestoresEveryFieldAndCanBeRetried() (gas: 745965)\n[PASS] test_failedCloseRestoresEveryFieldAndCanBeRetried() (gas: 476131)\n[PASS] test_failedWithdrawalPreservesOriginalClaimDeadline() (gas: 411071)\n[PASS] test_oneWeiCanBeWithdrawnAndVaultReused() (gas: 442425)\n[PASS] test_periodCanBeShortenedWithoutReusingTheOldDeadline() (gas: 323787)\n[PASS] test_rejectingReceiverDoesNotBlockEmptyClaimOrClose() (gas: 396775)\n[PASS] test_withdrawCallbackSeesUpdatedStateAndBlocksEveryPayout() (gas: 1152027)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 39.87ms (75.92ms CPU time)\n\nRan 59 tests for test/HeirloomVault.t.sol:HeirloomVaultTest\n[PASS] testFuzz_anyCheckInPushesDeadlineForward(uint256,uint8) (runs: 256, μ: 265061, ~: 265827)\nLogs:\n  Bound result 24037\n  Bound result 1\n\n[PASS] testFuzz_claim_neverBeforeDeadline(uint256,uint256) (runs: 256, μ: 222297, ~: 222614)\nLogs:\n  Bound result 315297638\n  Bound result 11797\n\n[PASS] testFuzz_claim_onlyBeneficiary(address,uint256) (runs: 256, μ: 218910, ~: 219138)\nLogs:\n  Bound result 162179638\n\n[PASS] testFuzz_claim_succeedsOnceDeadlinePassed(uint256,uint256) (runs: 256, μ: 226193, ~: 226420)\nLogs:\n  Bound result 950400\n  Bound result 434434349\n\n[PASS] testFuzz_openVault_periodLimits(uint256) (runs: 256, μ: 47846, ~: 43798)\n[PASS] testFuzz_ownerCanAlwaysWithdrawBeforeClaim(uint256,uint96) (runs: 256, μ: 237060, ~: 237215)\nLogs:\n  Bound result 24037\n  Bound result 11797\n\n[PASS] testFuzz_setPeriod_limits(uint256) (runs: 256, μ: 143375, ~: 142785)\n[PASS] testFuzz_withdraw_amounts(uint96,uint96) (runs: 256, μ: 216266, ~: 201555)\nLogs:\n  Bound result 1946582233\n\n[PASS] test_checkIn_afterDeadlineRestoresProtection() (gas: 271050)\n[PASS] test_checkIn_resetsTimerAndEmits() (gas: 174302)\n[PASS] test_checkIn_revertsWithoutVault() (gas: 34717)\n[PASS] test_claim_cannotRepeat() (gas: 242483)\n[PASS] test_claim_emptyVaultStillCloses() (gas: 151490)\n[PASS] test_claim_onlyAffectsThatVault() (gas: 393804)\n[PASS] test_claim_ownerLosesAccessAfterClaim() (gas: 417064)\n[PASS] test_claim_revertsBeforeDeadline() (gas: 201049)\n[PASS] test_claim_revertsExactlyAtDeadline() (gas: 292226)\n[PASS] test_claim_revertsForNonBeneficiary() (gas: 243512)\n[PASS] test_claim_revertsWithoutVault() (gas: 42312)\n[PASS] test_claim_succeedsAfterDeadlineAndClosesVault() (gas: 236518)\n[PASS] test_closeVault_afterDeadlineBeatsClaim() (gas: 241631)\n[PASS] test_closeVault_emptyVault() (gas: 147259)\n[PASS] test_closeVault_returnsEverythingAndClears() (gas: 219959)\n[PASS] test_closeVault_revertsWithoutVault() (gas: 39747)\n[PASS] test_closeVault_thenReopenStartsFresh() (gas: 321323)\n[PASS] test_constants() (gas: 16721)\n[PASS] test_deposit_addsBalanceAndChecksIn() (gas: 277343)\n[PASS] test_deposit_isolatedPerOwner() (gas: 358747)\n[PASS] test_deposit_revertsOnZeroValue() (gas: 126471)\n[PASS] test_deposit_revertsWithoutVault() (gas: 41485)\n[PASS] test_fallback_rejectsUnknownCalldata() (gas: 61982)\n[PASS] test_openVault_acceptsBoundaryPeriods() (gas: 224520)\n[PASS] test_openVault_revertsOnPeriodTooLong() (gas: 42918)\n[PASS] test_openVault_revertsOnPeriodTooShort() (gas: 37714)\n[PASS] test_openVault_revertsOnSelfBeneficiary() (gas: 35613)\n[PASS] test_openVault_revertsOnZeroBeneficiary() (gas: 35080)\n[PASS] test_openVault_revertsOnZeroPeriod() (gas: 37471)\n[PASS] test_openVault_revertsWhenAlreadyOpen() (gas: 131306)\n[PASS] test_openVault_storesFieldsAndEmits() (gas: 153352)\n[PASS] test_receive_rejectsDirectEth() (gas: 39580)\n[PASS] test_reentrancy_claimCannotDoubleSpend() (gas: 697332)\n[PASS] test_reentrancy_withdrawCannotDoubleSpend() (gas: 747805)\n[PASS] test_rejectingBeneficiary_onlyBlocksItself() (gas: 648164)\n[PASS] test_rejectingOwner_cannotBrickOthers() (gas: 863979)\n[PASS] test_setBeneficiary_oldHeirLosesClaim() (gas: 282393)\n[PASS] test_setBeneficiary_revertsOnInvalid() (gas: 155483)\n[PASS] test_setBeneficiary_revertsWithoutVault() (gas: 37362)\n[PASS] test_setBeneficiary_updatesAndChecksIn() (gas: 164567)\n[PASS] test_setPeriod_revertsOnInvalid() (gas: 164877)\n[PASS] test_setPeriod_revertsWithoutVault() (gas: 34912)\n[PASS] test_setPeriod_updatesAndChecksIn() (gas: 159899)\n[PASS] test_views_zeroForUnknownOwner() (gas: 40309)\n[PASS] test_withdraw_allowedAfterDeadlineBeforeClaim() (gas: 261516)\n[PASS] test_withdraw_cannotTouchAnotherVault() (gas: 353153)\n[PASS] test_withdraw_countsAsCheckIn() (gas: 228872)\n[PASS] test_withdraw_partialAndFull() (gas: 310335)\n[PASS] test_withdraw_revertsOnInsufficientBalance() (gas: 197649)\n[PASS] test_withdraw_revertsOnZeroAmount() (gas: 192759)\n[PASS] test_withdraw_revertsWithoutVault() (gas: 39931)\nSuite result: ok. 59 passed; 0 failed; 0 skipped; finished in 39.85ms (201.82ms CPU time)\n\nRan 1 test for test/HeirloomVaultInvariant.t.sol:HeirloomVaultInvariantTest\n[PASS]\nHeirloomVaultInvariantTest invariants:\n[PASS] invariant_closedVaultsAreFullyCleared\n[PASS] invariant_contractBalanceEqualsSumOfVaults\n[PASS] invariant_fundsAreConserved\n[PASS] invariant_openVaultsAreWellFormed\n HeirloomVaultInvariantTest invariants (runs: 64, calls: 3072, reverts: 0)\n\n╭--------------+----------------+-------+---------+----------╮\n| Contract     | Selector       | Calls | Reverts | Discards |\n+============================================================+\n| VaultHandler | checkIn        | 311   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | claim          | 336   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | closeVault     | 348   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | deposit        | 350   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | openVault      | 337   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | setBeneficiary | 336   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | setPeriod      | 357   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | warp           | 353   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | withdraw       | 344   | 0       | 0        |\n╰--------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 6858\n  Bound result 7\n  Bound result 195500681\n  Bound result 864000000\n  Bound result 247792986\n  Bound result 3344\n  Bound result 13638845\n  Bound result 3051\n  Bound result 696\n  Bound result 2432000\n  Bound result 1171682\n  Bound result 0\n  Bound result 86399\n  Bound result 3407617\n  Bound result 604800\n  Bound result 5\n  Bound result 195319232\n  Bound result 34469420589221734\n  Bound result 574\n  Bound result 315275466\n  Bound result 315273648\n  Bound result 2596028446\n  Bound result 2\n  Bound result 4892\n  Bound result 864000\n  Bound result 315274143\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 821.50ms (819.51ms CPU time)\n\nRan 2 tests for test/HeirloomVaultModelInvariant.t.sol:HeirloomVaultModelInvariantTest\n[PASS] invariant_allVaultsAndWalletsMatchIndependentAccounting() (runs: 256, calls: 24576, reverts: 0)\n\n╭----------------------+-------------------+-------+---------+----------╮\n| Contract             | Selector          | Calls | Reverts | Discards |\n+=======================================================================+\n| HeirloomModelHandler | advanceTime       | 2416  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | changeBeneficiary | 2423  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | changePeriod      | 2457  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | checkIn           | 2521  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | claim             | 2412  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | close             | 2371  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | deposit           | 2419  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | invalidAction     | 2620  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | open              | 2460  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | withdraw          | 2477  | 0       | 0        |\n╰----------------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 86400\n  Bound result 1000000000000000000\n  Bound result 2592000\n  Bound result 2000000000000000000\n  Bound result 315360000\n  Bound result 3000000000000000000\n  Bound result 199278054\n  Bound result 10\n  Bound result 10\n  Bound result 211091291\n  Bound result 8586290\n  Bound result 1055\n  Bound result 221375771\n  Bound result 1332\n  Bound result 3\n  Bound result 0\n  Bound result 6\n  Bound result 1\n  Bound result 6457\n  Bound result 269529643\n  Bound result 2389\n  Bound result 86403\n  Bound result 315276128\n  Bound result 1226\n  Bound result 315275519\n  Bound result 557\n  Bound result 12800000\n  Bound result 3\n  Bound result 25600000\n  Bound result 315273606\n  Bound result 30326399\n  Bound result 270801118\n  Bound result 147018637\n  Bound result 2592000\n  Bound result 1700000003\n  Bound result 6619233459676263831\n  Bound result 6000000000000000000\n  Bound result 315360001\n  Bound result 4\n  Bound result 7729309\n  Bound result 174965574\n  Bound result 315273610\n  Bound result 278691832\n  Bound result 4102\n  Bound result 315280840\n  Bound result 8920\n  Bound result 179200000\n  Bound result 82\n  Bound result 315277794\n  Bound result 315303415\n  Bound result 0\n  Bound result 10138\n  Bound result 49244586253031080828711141536950295311166341130\n  Bound result 2013\n  Bound result 3956\n  Bound result 999999999997575800\n  Bound result 0\n  Bound result 33213009\n  Bound result 796724193621840659534\n  Bound result 86401\n  Bound result 345599999\n  Bound result 315278264\n  Bound result 315276589\n  Bound result 0\n  Bound result 0\n  Bound result 86400\n  Bound result 1797\n  Bound result 315273631\n\n[PASS] test_handlerExercisesClaimBoundariesAndReopens() (gas: 1654014)\nLogs:\n  Bound result 86400\n  Bound result 1000000000000000000\n  Bound result 2592000\n  Bound result 2000000000000000000\n  Bound result 315360000\n  Bound result 3000000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 315360000\n  Bound result 1\n  Bound result 1\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 4.04s (4.04s CPU time)\n\nRan 6 test suites in 4.04s (5.00s CPU time): 87 tests passed, 0 failed, 0 skipped (87 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"12c160daee1aa93ef8a5728036726b801bbfd406f306ed072a7da37178d91c28","verifiedTreeHash":"e436d16fddfba6454471a6ac1ea05d8b70d04c75","verifierVersion":"0.1.0+48a57703"},{"checks":[{"durationMs":3091,"exitCode":0,"name":"build","output":"Compiling 31 files with Solc 0.8.26\nSolc 0.8.26 finished in 2.96s\nCompiler run successful with warnings:\nWarning (5159): \"selfdestruct\" has been deprecated. Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code and data associated with an account and only transfers its Ether to the beneficiary, unless executed in the same transaction in which the contract was created (see EIP-6780). Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. Future changes to the EVM might further reduce the functionality of the opcode.\n  --> test/HeirloomVaultForcedEther.t.sol:10:9:\n   |\n10 |         selfdestruct(target);\n   |         ^^^^^^^^^^^^\n\n","passed":true},{"durationMs":4831,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_deployToken_mintsSupplyToCaller() (gas: 358655)\n[PASS] test_deployVault_producesWorkingContract() (gas: 813728)\n[PASS] test_deploymentsAreIndependentOfCaller() (gas: 1300920)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 24.01ms (629.87µs CPU time)\n\nRan 2 tests for test/HeirloomVaultForcedEther.t.sol:HeirloomVaultForcedEtherTest\n[PASS] testFuzz_forcedSurplusCannotImpairAnyPayout(uint256,uint256) (runs: 256, μ: 719925, ~: 720280)\nLogs:\n  Bound result 5392\n  Bound result 172800\n\n[PASS] test_forcedWeiBypassesReceiveAndRemainsAfterClose() (gas: 552499)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 45.61ms (45.58ms CPU time)\n\nRan 11 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] testFuzz_transferConservesSupply(uint256) (runs: 256, μ: 90839, ~: 91111)\nLogs:\n  Bound result 2\n\n[PASS] test_approveAndTransferFrom() (gas: 195811)\n[PASS] test_approve_revertsToZeroAddress() (gas: 32332)\n[PASS] test_constructorEmitsMintTransfer() (gas: 8083)\n[PASS] test_fixedSupplyMintedToDeployer() (gas: 41674)\n[PASS] test_infiniteAllowanceIsNotDecremented() (gas: 122684)\n[PASS] test_metadata() (gas: 26725)\n[PASS] test_noMintOrAdminEntrypoints() (gas: 157457)\n[PASS] test_transfer() (gas: 93627)\n[PASS] test_transfer_revertsOnInsufficientBalance() (gas: 37492)\n[PASS] test_transfer_revertsToZeroAddress() (gas: 32408)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 47.89ms (49.25ms CPU time)\n\nRan 11 tests for test/HeirloomVaultAdversarial.t.sol:HeirloomVaultAdversarialTest\n[PASS] testFuzz_rejectedOwnerActionsCannotDelayAnEligibleClaim(uint256,uint256) (runs: 1000, μ: 325066, ~: 324385)\nLogs:\n  Bound result 18\n\n[PASS] testFuzz_validWithdrawalsRoundTripEvenAfterDeadline(uint128,uint256,uint256) (runs: 1000, μ: 353325, ~: 354100)\nLogs:\n  Bound result 31664856\n  Bound result 33138\n  Bound result 758659\n\n[PASS] test_claimCallbackSeesDeletedStateAndBlocksEveryPayout() (gas: 1018758)\n[PASS] test_closeCallbackSeesDeletedStateAndBlocksEveryPayout() (gas: 1015560)\n[PASS] test_failedClaimRestoresEveryFieldAndCanBeRetried() (gas: 745965)\n[PASS] test_failedCloseRestoresEveryFieldAndCanBeRetried() (gas: 476131)\n[PASS] test_failedWithdrawalPreservesOriginalClaimDeadline() (gas: 411071)\n[PASS] test_oneWeiCanBeWithdrawnAndVaultReused() (gas: 442425)\n[PASS] test_periodCanBeShortenedWithoutReusingTheOldDeadline() (gas: 323787)\n[PASS] test_rejectingReceiverDoesNotBlockEmptyClaimOrClose() (gas: 396775)\n[PASS] test_withdrawCallbackSeesUpdatedStateAndBlocksEveryPayout() (gas: 1152027)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 53.24ms (108.47ms CPU time)\n\nRan 59 tests for test/HeirloomVault.t.sol:HeirloomVaultTest\n[PASS] testFuzz_anyCheckInPushesDeadlineForward(uint256,uint8) (runs: 256, μ: 265250, ~: 265827)\nLogs:\n  Bound result 26611827\n  Bound result 0\n\n[PASS] testFuzz_claim_neverBeforeDeadline(uint256,uint256) (runs: 256, μ: 222324, ~: 222614)\nLogs:\n  Bound result 659918\n  Bound result 6824\n\n[PASS] testFuzz_claim_onlyBeneficiary(address,uint256) (runs: 256, μ: 218866, ~: 219138)\nLogs:\n  Bound result 836137456\n\n[PASS] testFuzz_claim_succeedsOnceDeadlinePassed(uint256,uint256) (runs: 256, μ: 226219, ~: 226524)\nLogs:\n  Bound result 659918\n  Bound result 6824\n\n[PASS] testFuzz_openVault_periodLimits(uint256) (runs: 256, μ: 48998, ~: 43822)\n[PASS] testFuzz_ownerCanAlwaysWithdrawBeforeClaim(uint256,uint96) (runs: 256, μ: 237139, ~: 237215)\nLogs:\n  Bound result 659918\n  Bound result 6824\n\n[PASS] testFuzz_setPeriod_limits(uint256) (runs: 256, μ: 143539, ~: 142809)\n[PASS] testFuzz_withdraw_amounts(uint96,uint96) (runs: 256, μ: 216772, ~: 201977)\nLogs:\n  Bound result 14672\n\n[PASS] test_checkIn_afterDeadlineRestoresProtection() (gas: 271050)\n[PASS] test_checkIn_resetsTimerAndEmits() (gas: 174302)\n[PASS] test_checkIn_revertsWithoutVault() (gas: 34717)\n[PASS] test_claim_cannotRepeat() (gas: 242483)\n[PASS] test_claim_emptyVaultStillCloses() (gas: 151490)\n[PASS] test_claim_onlyAffectsThatVault() (gas: 393804)\n[PASS] test_claim_ownerLosesAccessAfterClaim() (gas: 417064)\n[PASS] test_claim_revertsBeforeDeadline() (gas: 201049)\n[PASS] test_claim_revertsExactlyAtDeadline() (gas: 292226)\n[PASS] test_claim_revertsForNonBeneficiary() (gas: 243512)\n[PASS] test_claim_revertsWithoutVault() (gas: 42312)\n[PASS] test_claim_succeedsAfterDeadlineAndClosesVault() (gas: 236518)\n[PASS] test_closeVault_afterDeadlineBeatsClaim() (gas: 241631)\n[PASS] test_closeVault_emptyVault() (gas: 147259)\n[PASS] test_closeVault_returnsEverythingAndClears() (gas: 219959)\n[PASS] test_closeVault_revertsWithoutVault() (gas: 39747)\n[PASS] test_closeVault_thenReopenStartsFresh() (gas: 321323)\n[PASS] test_constants() (gas: 16721)\n[PASS] test_deposit_addsBalanceAndChecksIn() (gas: 277343)\n[PASS] test_deposit_isolatedPerOwner() (gas: 358747)\n[PASS] test_deposit_revertsOnZeroValue() (gas: 126471)\n[PASS] test_deposit_revertsWithoutVault() (gas: 41485)\n[PASS] test_fallback_rejectsUnknownCalldata() (gas: 61982)\n[PASS] test_openVault_acceptsBoundaryPeriods() (gas: 224520)\n[PASS] test_openVault_revertsOnPeriodTooLong() (gas: 42918)\n[PASS] test_openVault_revertsOnPeriodTooShort() (gas: 37714)\n[PASS] test_openVault_revertsOnSelfBeneficiary() (gas: 35613)\n[PASS] test_openVault_revertsOnZeroBeneficiary() (gas: 35080)\n[PASS] test_openVault_revertsOnZeroPeriod() (gas: 37471)\n[PASS] test_openVault_revertsWhenAlreadyOpen() (gas: 131306)\n[PASS] test_openVault_storesFieldsAndEmits() (gas: 153352)\n[PASS] test_receive_rejectsDirectEth() (gas: 39580)\n[PASS] test_reentrancy_claimCannotDoubleSpend() (gas: 697332)\n[PASS] test_reentrancy_withdrawCannotDoubleSpend() (gas: 747805)\n[PASS] test_rejectingBeneficiary_onlyBlocksItself() (gas: 648164)\n[PASS] test_rejectingOwner_cannotBrickOthers() (gas: 863979)\n[PASS] test_setBeneficiary_oldHeirLosesClaim() (gas: 282393)\n[PASS] test_setBeneficiary_revertsOnInvalid() (gas: 155483)\n[PASS] test_setBeneficiary_revertsWithoutVault() (gas: 37362)\n[PASS] test_setBeneficiary_updatesAndChecksIn() (gas: 164567)\n[PASS] test_setPeriod_revertsOnInvalid() (gas: 164877)\n[PASS] test_setPeriod_revertsWithoutVault() (gas: 34912)\n[PASS] test_setPeriod_updatesAndChecksIn() (gas: 159899)\n[PASS] test_views_zeroForUnknownOwner() (gas: 40309)\n[PASS] test_withdraw_allowedAfterDeadlineBeforeClaim() (gas: 261516)\n[PASS] test_withdraw_cannotTouchAnotherVault() (gas: 353153)\n[PASS] test_withdraw_countsAsCheckIn() (gas: 228872)\n[PASS] test_withdraw_partialAndFull() (gas: 310335)\n[PASS] test_withdraw_revertsOnInsufficientBalance() (gas: 197649)\n[PASS] test_withdraw_revertsOnZeroAmount() (gas: 192759)\n[PASS] test_withdraw_revertsWithoutVault() (gas: 39931)\nSuite result: ok. 59 passed; 0 failed; 0 skipped; finished in 53.28ms (312.03ms CPU time)\n\nRan 1 test for test/HeirloomVaultInvariant.t.sol:HeirloomVaultInvariantTest\n[PASS]\nHeirloomVaultInvariantTest invariants:\n[PASS] invariant_closedVaultsAreFullyCleared\n[PASS] invariant_contractBalanceEqualsSumOfVaults\n[PASS] invariant_fundsAreConserved\n[PASS] invariant_openVaultsAreWellFormed\n HeirloomVaultInvariantTest invariants (runs: 64, calls: 3072, reverts: 0)\n\n╭--------------+----------------+-------+---------+----------╮\n| Contract     | Selector       | Calls | Reverts | Discards |\n+============================================================+\n| VaultHandler | checkIn        | 336   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | claim          | 349   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | closeVault     | 347   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | deposit        | 369   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | openVault      | 304   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | setBeneficiary | 346   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | setPeriod      | 330   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | warp           | 355   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | withdraw       | 336   | 0       | 0        |\n╰--------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 4126\n  Bound result 230596440316590123\n  Bound result 9290\n  Bound result 9999999999999999951\n  Bound result 714238156338161931\n  Bound result 86399\n  Bound result 210\n  Bound result 86399\n  Bound result 2\n  Bound result 1024\n  Bound result 74520653\n  Bound result 86399\n  Bound result 1247\n  Bound result 2592000\n  Bound result 7590\n  Bound result 86399\n  Bound result 32345691\n  Bound result 13638845\n  Bound result 1925067\n  Bound result 4839\n  Bound result 315274950\n  Bound result 6996729415562142264\n  Bound result 7134213631118066099\n  Bound result 315279176\n  Bound result 4328\n  Bound result 999999999999999970\n  Bound result 315277701\n  Bound result 3973\n  Bound result 920220\n  Bound result 2827\n  Bound result 315275146\n  Bound result 3978\n  Bound result 2944001\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 925.19ms (923.04ms CPU time)\n\nRan 4 tests for test/HeirloomVaultModelInvariant.t.sol:HeirloomVaultModelInvariantTest\n[PASS] invariant_allVaultsAndWalletsMatchIndependentAccounting() (runs: 256, calls: 24576, reverts: 0)\n\n╭----------------------+-------------------+-------+---------+----------╮\n| Contract             | Selector          | Calls | Reverts | Discards |\n+=======================================================================+\n| HeirloomModelHandler | advanceTime       | 2439  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | changeBeneficiary | 2470  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | changePeriod      | 2443  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | checkIn           | 2402  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | claim             | 2435  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | close             | 2393  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | deposit           | 2535  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | invalidAction     | 2557  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | open              | 2437  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | withdraw          | 2465  | 0       | 0        |\n╰----------------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 86400\n  Bound result 1000000000000000000\n  Bound result 2592000\n  Bound result 2000000000000000000\n  Bound result 315360000\n  Bound result 3000000000000000000\n  Bound result 34560000\n  Bound result 1700000003\n  Bound result 11043\n  Bound result 69697322\n  Bound result 10072\n  Bound result 16000\n  Bound result 123631995\n  Bound result 1558\n  Bound result 315276533\n  Bound result 11585\n  Bound result 172800\n  Bound result 111176693193\n  Bound result 3456000\n  Bound result 105186026\n  Bound result 6000000000000000000\n  Bound result 8330\n  Bound result 86399\n  Bound result 7013\n  Bound result 5000000236459729221\n  Bound result 315276282\n  Bound result 8635\n  Bound result 2963\n  Bound result 0\n  Bound result 6204\n  Bound result 11132\n  Bound result 127073980\n  Bound result 86401\n  Bound result 1000011906255209800\n  Bound result 10000000000000000000\n  Bound result 315360000\n  Bound result 2056\n  Bound result 10181\n  Bound result 49276688\n  Bound result 5714\n  Bound result 315285477\n  Bound result 2505600\n  Bound result 316847047\n  Bound result 51825957\n  Bound result 2871\n  Bound result 4187882992578885812\n  Bound result 2000000000000000000\n  Bound result 315285080\n  Bound result 315359997\n  Bound result 134854139\n  Bound result 7704410912332015508\n  Bound result 123631995\n  Bound result 9000000001665461072\n  Bound result 315274417\n  Bound result 6600022636858912492\n  Bound result 259188614\n  Bound result 324575034910421860462348072140622083\n  Bound result 315284238\n  Bound result 625805518\n  Bound result 8603824890064094587\n  Bound result 5511\n  Bound result 275538197\n  Bound result 86401\n  Bound result 1\n  Bound result 64000000\n  Bound result 148867548\n  Bound result 1\n\n[PASS] test_handlerExercisesClaimBoundariesAndReopens() (gas: 1654060)\nLogs:\n  Bound result 86400\n  Bound result 1000000000000000000\n  Bound result 2592000\n  Bound result 2000000000000000000\n  Bound result 315360000\n  Bound result 3000000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 315360000\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerExercisesWithdrawalEdgesAfterDeadline() (gas: 1636281)\nLogs:\n  Bound result 86400\n  Bound result 1000000000000000000\n  Bound result 2592000\n  Bound result 2000000000000000000\n  Bound result 315360000\n  Bound result 3000000000000000000\n  Bound result 1\n  Bound result 0\n  Bound result 1000000000000000001\n  Bound result 1\n  Bound result 1\n  Bound result 999999999999999999\n\n[PASS] test_handlerRejectedActionsPreserveExpiredAndAbsentVaults() (gas: 8112972)\nLogs:\n  Bound result 86400\n  Bound result 1000000000000000000\n  Bound result 2592000\n  Bound result 2000000000000000000\n  Bound result 315360000\n  Bound result 3000000000000000000\n  Bound result 1\n  Bound result 0\n  Bound result 86399\n  Bound result 0\n  Bound result 86399\n  Bound result 315360001\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639935\n  Bound result 315360001\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639935\n  Bound result 1000000000000000001\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639935\n  Bound result 1\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639935\n  Bound result 0\n  Bound result 999000000000000000000\n  Bound result 0\n  Bound result 1000000000000000000000\n  Bound result 0\n  Bound result 999000000000000000000\n  Bound result 0\n  Bound result 1000000000000000000000\n  Bound result 315360000\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 4.73s (4.75s CPU time)\n\nRan 7 test suites in 4.74s (5.88s CPU time): 91 tests passed, 0 failed, 0 skipped (91 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"223b7393693b4720245b336f677cda77d35504e3e2aca71758f4dd0dd36d9b4d","verifiedTreeHash":"f7e8e1956a97d3c881a5e0eaf1a5fce5edfde608","verifierVersion":"0.1.0+48a57703"},{"checks":[{"durationMs":2623,"exitCode":0,"name":"build","output":"Compiling 29 files with Solc 0.8.26\nSolc 0.8.26 finished in 2.49s\nCompiler run successful with warnings:\nWarning (5159): \"selfdestruct\" has been deprecated. Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code and data associated with an account and only transfers its Ether to the beneficiary, unless executed in the same transaction in which the contract was created (see EIP-6780). Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. Future changes to the EVM might further reduce the functionality of the opcode.\n  --> test/HeirloomVaultForcedEther.t.sol:10:9:\n   |\n10 |         selfdestruct(target);\n   |         ^^^^^^^^^^^^\n\n","passed":true},{"durationMs":846,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_deployToken_mintsSupplyToCaller() (gas: 358655)\n[PASS] test_deployVault_producesWorkingContract() (gas: 813728)\n[PASS] test_deploymentsAreIndependentOfCaller() (gas: 1300920)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 935.14µs (1.04ms CPU time)\n\nRan 11 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] testFuzz_transferConservesSupply(uint256) (runs: 256, μ: 91317, ~: 91135)\nLogs:\n  Bound result 5205\n\n[PASS] test_approveAndTransferFrom() (gas: 195811)\n[PASS] test_approve_revertsToZeroAddress() (gas: 32332)\n[PASS] test_constructorEmitsMintTransfer() (gas: 8083)\n[PASS] test_fixedSupplyMintedToDeployer() (gas: 41674)\n[PASS] test_infiniteAllowanceIsNotDecremented() (gas: 122684)\n[PASS] test_metadata() (gas: 26725)\n[PASS] test_noMintOrAdminEntrypoints() (gas: 157457)\n[PASS] test_transfer() (gas: 93627)\n[PASS] test_transfer_revertsOnInsufficientBalance() (gas: 37492)\n[PASS] test_transfer_revertsToZeroAddress() (gas: 32408)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 19.44ms (21.03ms CPU time)\n\nRan 2 tests for test/HeirloomVaultForcedEther.t.sol:HeirloomVaultForcedEtherTest\n[PASS] testFuzz_forcedSurplusCannotImpairAnyPayout(uint256,uint256) (runs: 256, μ: 719260, ~: 720208)\nLogs:\n  Bound result 2435924722050477492\n  Bound result 1894823045489241432\n\n[PASS] test_forcedWeiBypassesReceiveAndRemainsAfterClose() (gas: 552499)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 27.86ms (27.87ms CPU time)\n\nRan 59 tests for test/HeirloomVault.t.sol:HeirloomVaultTest\n[PASS] testFuzz_anyCheckInPushesDeadlineForward(uint256,uint8) (runs: 256, μ: 266071, ~: 266093)\nLogs:\n  Bound result 386477492\n  Bound result 4\n\n[PASS] testFuzz_claim_neverBeforeDeadline(uint256,uint256) (runs: 256, μ: 222276, ~: 222551)\nLogs:\n  Bound result 83969922\n  Bound result 60472451\n\n[PASS] testFuzz_claim_onlyBeneficiary(address,uint256) (runs: 256, μ: 218884, ~: 219138)\nLogs:\n  Bound result 765129068\n\n[PASS] testFuzz_claim_succeedsOnceDeadlinePassed(uint256,uint256) (runs: 256, μ: 226170, ~: 226420)\nLogs:\n  Bound result 233452798\n  Bound result 712946375\n\n[PASS] testFuzz_openVault_periodLimits(uint256) (runs: 256, μ: 50445, ~: 43852)\n[PASS] testFuzz_ownerCanAlwaysWithdrawBeforeClaim(uint256,uint96) (runs: 256, μ: 237044, ~: 237215)\nLogs:\n  Bound result 1330159996\n  Bound result 4264337593543950333\n\n[PASS] testFuzz_setPeriod_limits(uint256) (runs: 256, μ: 143740, ~: 142839)\n[PASS] testFuzz_withdraw_amounts(uint96,uint96) (runs: 256, μ: 220464, ~: 202239)\nLogs:\n  Bound result 2000000000000000001\n\n[PASS] test_checkIn_afterDeadlineRestoresProtection() (gas: 271050)\n[PASS] test_checkIn_resetsTimerAndEmits() (gas: 174302)\n[PASS] test_checkIn_revertsWithoutVault() (gas: 34717)\n[PASS] test_claim_cannotRepeat() (gas: 242483)\n[PASS] test_claim_emptyVaultStillCloses() (gas: 151490)\n[PASS] test_claim_onlyAffectsThatVault() (gas: 393804)\n[PASS] test_claim_ownerLosesAccessAfterClaim() (gas: 417064)\n[PASS] test_claim_revertsBeforeDeadline() (gas: 201049)\n[PASS] test_claim_revertsExactlyAtDeadline() (gas: 292226)\n[PASS] test_claim_revertsForNonBeneficiary() (gas: 243512)\n[PASS] test_claim_revertsWithoutVault() (gas: 42312)\n[PASS] test_claim_succeedsAfterDeadlineAndClosesVault() (gas: 236518)\n[PASS] test_closeVault_afterDeadlineBeatsClaim() (gas: 241631)\n[PASS] test_closeVault_emptyVault() (gas: 147259)\n[PASS] test_closeVault_returnsEverythingAndClears() (gas: 219959)\n[PASS] test_closeVault_revertsWithoutVault() (gas: 39747)\n[PASS] test_closeVault_thenReopenStartsFresh() (gas: 321323)\n[PASS] test_constants() (gas: 16721)\n[PASS] test_deposit_addsBalanceAndChecksIn() (gas: 277343)\n[PASS] test_deposit_isolatedPerOwner() (gas: 358747)\n[PASS] test_deposit_revertsOnZeroValue() (gas: 126471)\n[PASS] test_deposit_revertsWithoutVault() (gas: 41485)\n[PASS] test_fallback_rejectsUnknownCalldata() (gas: 61982)\n[PASS] test_openVault_acceptsBoundaryPeriods() (gas: 224520)\n[PASS] test_openVault_revertsOnPeriodTooLong() (gas: 42918)\n[PASS] test_openVault_revertsOnPeriodTooShort() (gas: 37714)\n[PASS] test_openVault_revertsOnSelfBeneficiary() (gas: 35613)\n[PASS] test_openVault_revertsOnZeroBeneficiary() (gas: 35080)\n[PASS] test_openVault_revertsOnZeroPeriod() (gas: 37471)\n[PASS] test_openVault_revertsWhenAlreadyOpen() (gas: 131306)\n[PASS] test_openVault_storesFieldsAndEmits() (gas: 153352)\n[PASS] test_receive_rejectsDirectEth() (gas: 39580)\n[PASS] test_reentrancy_claimCannotDoubleSpend() (gas: 697332)\n[PASS] test_reentrancy_withdrawCannotDoubleSpend() (gas: 747805)\n[PASS] test_rejectingBeneficiary_onlyBlocksItself() (gas: 648164)\n[PASS] test_rejectingOwner_cannotBrickOthers() (gas: 863979)\n[PASS] test_setBeneficiary_oldHeirLosesClaim() (gas: 282393)\n[PASS] test_setBeneficiary_revertsOnInvalid() (gas: 155483)\n[PASS] test_setBeneficiary_revertsWithoutVault() (gas: 37362)\n[PASS] test_setBeneficiary_updatesAndChecksIn() (gas: 164567)\n[PASS] test_setPeriod_revertsOnInvalid() (gas: 164877)\n[PASS] test_setPeriod_revertsWithoutVault() (gas: 34912)\n[PASS] test_setPeriod_updatesAndChecksIn() (gas: 159899)\n[PASS] test_views_zeroForUnknownOwner() (gas: 40309)\n[PASS] test_withdraw_allowedAfterDeadlineBeforeClaim() (gas: 261516)\n[PASS] test_withdraw_cannotTouchAnotherVault() (gas: 353153)\n[PASS] test_withdraw_countsAsCheckIn() (gas: 228872)\n[PASS] test_withdraw_partialAndFull() (gas: 310335)\n[PASS] test_withdraw_revertsOnInsufficientBalance() (gas: 197649)\n[PASS] test_withdraw_revertsOnZeroAmount() (gas: 192759)\n[PASS] test_withdraw_revertsWithoutVault() (gas: 39931)\nSuite result: ok. 59 passed; 0 failed; 0 skipped; finished in 29.49ms (190.41ms CPU time)\n\nRan 1 test for test/HeirloomVaultInvariant.t.sol:HeirloomVaultInvariantTest\n[PASS]\nHeirloomVaultInvariantTest invariants:\n[PASS] invariant_claimRulesWereExercised\n[PASS] invariant_contractBalanceEqualsSumOfVaults\n[PASS] invariant_fundsAreConserved\n[PASS] invariant_openVaultsAreWellFormed\n HeirloomVaultInvariantTest invariants (runs: 64, calls: 3072, reverts: 0)\n\n╭--------------+----------------+-------+---------+----------╮\n| Contract     | Selector       | Calls | Reverts | Discards |\n+============================================================+\n| VaultHandler | checkIn        | 333   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | claim          | 312   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | closeVault     | 356   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | deposit        | 336   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | openVault      | 339   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | setBeneficiary | 354   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | setPeriod      | 352   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | warp           | 346   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | withdraw       | 344   | 0       | 0        |\n╰--------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 315279140\n  Bound result 2592000\n  Bound result 259200\n  Bound result 2129\n  Bound result 999999999999999001\n  Bound result 751\n  Bound result 479\n  Bound result 750320\n  Bound result 172800\n  Bound result 315360001\n  Bound result 5307\n  Bound result 999999999999999700\n  Bound result 2\n  Bound result 2770406\n  Bound result 1000000000000000001\n  Bound result 3784\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 734.84ms (731.85ms CPU time)\n\nRan 5 test suites in 737.08ms (812.56ms CPU time): 76 tests passed, 0 failed, 0 skipped (76 total tests)\n","passed":true},{"durationMs":1252,"exitCode":0,"name":"slither","output":"[high/medium] arbitrary-send-eth at src/HeirloomVault.sol:305: HeirloomVault._send(address,uint256) (src/HeirloomVault.sol#305-308) sends eth to arbitrary user\n[low/medium] timestamp at src/HeirloomVault.sol:215: HeirloomVault.claim(address) (src/HeirloomVault.sol#215-227) uses timestamp for comparisons\n[low/medium] timestamp at src/HeirloomVault.sol:257: HeirloomVault.isClaimable(address) (src/HeirloomVault.sol#257-260) uses timestamp for comparisons\n[low/medium] timestamp at src/HeirloomVault.sol:252: HeirloomVault.hasVault(address) (src/HeirloomVault.sol#252-254) uses timestamp for comparisons","passed":true},{"durationMs":580,"exitCode":0,"name":"aderyn","output":"aderyn: no results at low impact or above","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"24a1743d4a76e7662d1cb03e6766e88a0e5cc7c576b9b845a8687b5611deb82b","verifiedTreeHash":"77046d8dfb7bdcad9ebaa408f8bff2ce043179f6","verifierVersion":"0.1.0+48a57703"},{"checks":[{"durationMs":3458,"exitCode":0,"name":"build","output":"Compiling 29 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.31s\nCompiler run successful!\n","passed":true},{"durationMs":6342,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_deployToken_mintsSupplyToCaller() (gas: 358655)\n[PASS] test_deployVault_producesWorkingContract() (gas: 813728)\n[PASS] test_deploymentsAreIndependentOfCaller() (gas: 1300920)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 76.32ms (66.66ms CPU time)\n\nRan 11 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] testFuzz_transferConservesSupply(uint256) (runs: 256, μ: 91243, ~: 91111)\nLogs:\n  Bound result 146149525318337898575661074\n\n[PASS] test_approveAndTransferFrom() (gas: 195811)\n[PASS] test_approve_revertsToZeroAddress() (gas: 32332)\n[PASS] test_constructorEmitsMintTransfer() (gas: 8083)\n[PASS] test_fixedSupplyMintedToDeployer() (gas: 41674)\n[PASS] test_infiniteAllowanceIsNotDecremented() (gas: 122684)\n[PASS] test_metadata() (gas: 26725)\n[PASS] test_noMintOrAdminEntrypoints() (gas: 157457)\n[PASS] test_transfer() (gas: 93627)\n[PASS] test_transfer_revertsOnInsufficientBalance() (gas: 37492)\n[PASS] test_transfer_revertsToZeroAddress() (gas: 32408)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 105.38ms (68.37ms CPU time)\n\nRan 59 tests for test/HeirloomVault.t.sol:HeirloomVaultTest\n[PASS] testFuzz_anyCheckInPushesDeadlineForward(uint256,uint8) (runs: 256, μ: 266003, ~: 265827)\nLogs:\n  Bound result 226092640\n  Bound result 4\n\n[PASS] testFuzz_claim_neverBeforeDeadline(uint256,uint256) (runs: 256, μ: 222275, ~: 222500)\nLogs:\n  Bound result 86400\n  Bound result 8\n\n[PASS] testFuzz_claim_onlyBeneficiary(address,uint256) (runs: 256, μ: 218870, ~: 219138)\nLogs:\n  Bound result 37553\n\n[PASS] testFuzz_claim_succeedsOnceDeadlinePassed(uint256,uint256) (runs: 256, μ: 226187, ~: 226420)\nLogs:\n  Bound result 263847215\n  Bound result 430764251\n\n[PASS] testFuzz_openVault_periodLimits(uint256) (runs: 256, μ: 47268, ~: 43786)\n[PASS] testFuzz_ownerCanAlwaysWithdrawBeforeClaim(uint256,uint96) (runs: 256, μ: 237125, ~: 237215)\nLogs:\n  Bound result 315360001\n  Bound result 9648\n\n[PASS] testFuzz_setPeriod_limits(uint256) (runs: 256, μ: 143280, ~: 142773)\n[PASS] testFuzz_withdraw_amounts(uint96,uint96) (runs: 256, μ: 215040, ~: 201555)\nLogs:\n  Bound result 34260688127339280551\n\n[PASS] test_checkIn_afterDeadlineRestoresProtection() (gas: 271050)\n[PASS] test_checkIn_resetsTimerAndEmits() (gas: 174302)\n[PASS] test_checkIn_revertsWithoutVault() (gas: 34717)\n[PASS] test_claim_cannotRepeat() (gas: 242483)\n[PASS] test_claim_emptyVaultStillCloses() (gas: 151490)\n[PASS] test_claim_onlyAffectsThatVault() (gas: 393804)\n[PASS] test_claim_ownerLosesAccessAfterClaim() (gas: 417064)\n[PASS] test_claim_revertsBeforeDeadline() (gas: 201049)\n[PASS] test_claim_revertsExactlyAtDeadline() (gas: 292226)\n[PASS] test_claim_revertsForNonBeneficiary() (gas: 243512)\n[PASS] test_claim_revertsWithoutVault() (gas: 42312)\n[PASS] test_claim_succeedsAfterDeadlineAndClosesVault() (gas: 236518)\n[PASS] test_closeVault_afterDeadlineBeatsClaim() (gas: 241631)\n[PASS] test_closeVault_emptyVault() (gas: 147259)\n[PASS] test_closeVault_returnsEverythingAndClears() (gas: 219959)\n[PASS] test_closeVault_revertsWithoutVault() (gas: 39747)\n[PASS] test_closeVault_thenReopenStartsFresh() (gas: 321323)\n[PASS] test_constants() (gas: 16721)\n[PASS] test_deposit_addsBalanceAndChecksIn() (gas: 277343)\n[PASS] test_deposit_isolatedPerOwner() (gas: 358747)\n[PASS] test_deposit_revertsOnZeroValue() (gas: 126471)\n[PASS] test_deposit_revertsWithoutVault() (gas: 41485)\n[PASS] test_fallback_rejectsUnknownCalldata() (gas: 61982)\n[PASS] test_openVault_acceptsBoundaryPeriods() (gas: 224520)\n[PASS] test_openVault_revertsOnPeriodTooLong() (gas: 42918)\n[PASS] test_openVault_revertsOnPeriodTooShort() (gas: 37714)\n[PASS] test_openVault_revertsOnSelfBeneficiary() (gas: 35613)\n[PASS] test_openVault_revertsOnZeroBeneficiary() (gas: 35080)\n[PASS] test_openVault_revertsOnZeroPeriod() (gas: 37471)\n[PASS] test_openVault_revertsWhenAlreadyOpen() (gas: 131306)\n[PASS] test_openVault_storesFieldsAndEmits() (gas: 153352)\n[PASS] test_receive_rejectsDirectEth() (gas: 39580)\n[PASS] test_reentrancy_claimCannotDoubleSpend() (gas: 697332)\n[PASS] test_reentrancy_withdrawCannotDoubleSpend() (gas: 747805)\n[PASS] test_rejectingBeneficiary_onlyBlocksItself() (gas: 648164)\n[PASS] test_rejectingOwner_cannotBrickOthers() (gas: 863979)\n[PASS] test_setBeneficiary_oldHeirLosesClaim() (gas: 282393)\n[PASS] test_setBeneficiary_revertsOnInvalid() (gas: 155483)\n[PASS] test_setBeneficiary_revertsWithoutVault() (gas: 37362)\n[PASS] test_setBeneficiary_updatesAndChecksIn() (gas: 164567)\n[PASS] test_setPeriod_revertsOnInvalid() (gas: 164877)\n[PASS] test_setPeriod_revertsWithoutVault() (gas: 34912)\n[PASS] test_setPeriod_updatesAndChecksIn() (gas: 159899)\n[PASS] test_views_zeroForUnknownOwner() (gas: 40309)\n[PASS] test_withdraw_allowedAfterDeadlineBeforeClaim() (gas: 261516)\n[PASS] test_withdraw_cannotTouchAnotherVault() (gas: 353153)\n[PASS] test_withdraw_countsAsCheckIn() (gas: 228872)\n[PASS] test_withdraw_partialAndFull() (gas: 310335)\n[PASS] test_withdraw_revertsOnInsufficientBalance() (gas: 197649)\n[PASS] test_withdraw_revertsOnZeroAmount() (gas: 192759)\n[PASS] test_withdraw_revertsWithoutVault() (gas: 39931)\nSuite result: ok. 59 passed; 0 failed; 0 skipped; finished in 106.79ms (562.12ms CPU time)\n\nRan 11 tests for test/HeirloomVaultAdversarial.t.sol:HeirloomVaultAdversarialTest\n[PASS] testFuzz_rejectedOwnerActionsCannotDelayAnEligibleClaim(uint256,uint256) (runs: 1000, μ: 325253, ~: 324385)\nLogs:\n  Bound result 5264\n\n[PASS] testFuzz_validWithdrawalsRoundTripEvenAfterDeadline(uint128,uint256,uint256) (runs: 1000, μ: 353258, ~: 354040)\nLogs:\n  Bound result 2003540593\n  Bound result 152695432\n  Bound result 62375315\n\n[PASS] test_claimCallbackSeesDeletedStateAndBlocksEveryPayout() (gas: 1018758)\n[PASS] test_closeCallbackSeesDeletedStateAndBlocksEveryPayout() (gas: 1015560)\n[PASS] test_failedClaimRestoresEveryFieldAndCanBeRetried() (gas: 745965)\n[PASS] test_failedCloseRestoresEveryFieldAndCanBeRetried() (gas: 476131)\n[PASS] test_failedWithdrawalPreservesOriginalClaimDeadline() (gas: 411071)\n[PASS] test_oneWeiCanBeWithdrawnAndVaultReused() (gas: 442425)\n[PASS] test_periodCanBeShortenedWithoutReusingTheOldDeadline() (gas: 323787)\n[PASS] test_rejectingReceiverDoesNotBlockEmptyClaimOrClose() (gas: 396775)\n[PASS] test_withdrawCallbackSeesUpdatedStateAndBlocksEveryPayout() (gas: 1152027)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 106.87ms (164.79ms CPU time)\n\nRan 1 test for test/HeirloomVaultInvariant.t.sol:HeirloomVaultInvariantTest\n[PASS]\nHeirloomVaultInvariantTest invariants:\n[PASS] invariant_closedVaultsAreFullyCleared\n[PASS] invariant_contractBalanceEqualsSumOfVaults\n[PASS] invariant_fundsAreConserved\n[PASS] invariant_openVaultsAreWellFormed\n HeirloomVaultInvariantTest invariants (runs: 64, calls: 3072, reverts: 0)\n\n╭--------------+----------------+-------+---------+----------╮\n| Contract     | Selector       | Calls | Reverts | Discards |\n+============================================================+\n| VaultHandler | checkIn        | 343   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | claim          | 362   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | closeVault     | 311   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | deposit        | 325   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | openVault      | 356   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | setBeneficiary | 358   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | setPeriod      | 341   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | warp           | 333   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | withdraw       | 343   | 0       | 0        |\n╰--------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 999999999000000001\n  Bound result 6597\n  Bound result 864001\n  Bound result 3470\n  Bound result 999999999999999998\n  Bound result 6191544\n  Bound result 86399\n  Bound result 2592000\n  Bound result 194377826\n  Bound result 67419151\n  Bound result 141931161\n  Bound result 222641915\n  Bound result 311\n  Bound result 1613\n  Bound result 3454345801460781366\n  Bound result 5000000000000000000\n  Bound result 315360001\n  Bound result 384000\n  Bound result 27737893\n  Bound result 315280396\n  Bound result 136388450\n  Bound result 116726397\n  Bound result 1\n  Bound result 1367\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.41s (1.37s CPU time)\n\nRan 2 tests for test/HeirloomVaultModelInvariant.t.sol:HeirloomVaultModelInvariantTest\n[PASS] invariant_allVaultsAndWalletsMatchIndependentAccounting() (runs: 256, calls: 24576, reverts: 0)\n\n╭----------------------+-------------------+-------+---------+----------╮\n| Contract             | Selector          | Calls | Reverts | Discards |\n+=======================================================================+\n| HeirloomModelHandler | advanceTime       | 2409  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | changeBeneficiary | 2511  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | changePeriod      | 2453  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | checkIn           | 2491  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | claim             | 2460  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | close             | 2409  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | deposit           | 2506  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | invalidAction     | 2464  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | open              | 2441  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | withdraw          | 2432  | 0       | 0        |\n╰----------------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 86400\n  Bound result 1000000000000000000\n  Bound result 2592000\n  Bound result 2000000000000000000\n  Bound result 315360000\n  Bound result 3000000000000000000\n  Bound result 6000000000000000000\n  Bound result 850\n  Bound result 306939735\n  Bound result 217600000\n  Bound result 2835875533\n  Bound result 151631995\n  Bound result 315285359\n  Bound result 632680700792434598\n  Bound result 304990974734085247695716684557188591949851879476422393909017\n  Bound result 10918\n  Bound result 57603920132812701\n  Bound result 2321\n  Bound result 225396278\n  Bound result 0\n  Bound result 2939\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639934\n  Bound result 254520631\n  Bound result 7790879718124859146\n  Bound result 2588\n  Bound result 123631995\n  Bound result 1\n  Bound result 189470634\n  Bound result 179200000\n  Bound result 315273628\n  Bound result 284023044\n  Bound result 315275571\n  Bound result 18\n  Bound result 1\n  Bound result 315274287\n  Bound result 11371\n  Bound result 764\n  Bound result 120370657315052\n  Bound result 315276023\n  Bound result 7096\n  Bound result 86401\n  Bound result 315276668\n  Bound result 34848661\n  Bound result 0\n  Bound result 345600000\n  Bound result 48460995\n  Bound result 778\n  Bound result 0\n  Bound result 0\n  Bound result 1\n  Bound result 0\n  Bound result 4202047189\n  Bound result 4595\n  Bound result 10889\n  Bound result 172751618\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007912814284129\n  Bound result 453\n  Bound result 86400\n\n[PASS] test_handlerExercisesClaimBoundariesAndReopens() (gas: 1654014)\nLogs:\n  Bound result 86400\n  Bound result 1000000000000000000\n  Bound result 2592000\n  Bound result 2000000000000000000\n  Bound result 315360000\n  Bound result 3000000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 315360000\n  Bound result 1\n  Bound result 1\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 6.19s (6.13s CPU time)\n\nRan 6 test suites in 6.20s (7.99s CPU time): 87 tests passed, 0 failed, 0 skipped (87 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"db3b95044107af03beb1ee0700effa6a0a4bc8bed705eea240fca3c9b4363130","verifiedTreeHash":"0b165796c3b7efeefbb3337f07adcd7ec3fe62ed","verifierVersion":"0.1.0+48a57703"},{"checks":[{"durationMs":2982,"exitCode":0,"name":"build","output":"Compiling 31 files with Solc 0.8.26\nSolc 0.8.26 finished in 2.85s\nCompiler run successful with warnings:\nWarning (5159): \"selfdestruct\" has been deprecated. Note that, starting from the Cancun hard fork, the underlying opcode no longer deletes the code and data associated with an account and only transfers its Ether to the beneficiary, unless executed in the same transaction in which the contract was created (see EIP-6780). Any use in newly deployed contracts is strongly discouraged even if the new behavior is taken into account. Future changes to the EVM might further reduce the functionality of the opcode.\n  --> test/HeirloomVaultForcedEther.t.sol:10:9:\n   |\n10 |         selfdestruct(target);\n   |         ^^^^^^^^^^^^\n\n","passed":true},{"durationMs":4848,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 3 tests for test/Deploy.t.sol:DeployTest\n[PASS] test_deployToken_mintsSupplyToCaller() (gas: 358655)\n[PASS] test_deployVault_producesWorkingContract() (gas: 813728)\n[PASS] test_deploymentsAreIndependentOfCaller() (gas: 1300920)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 38.83ms (697.47µs CPU time)\n\nRan 2 tests for test/HeirloomVaultForcedEther.t.sol:HeirloomVaultForcedEtherTest\n[PASS] testFuzz_forcedSurplusCannotImpairAnyPayout(uint256,uint256) (runs: 256, μ: 719928, ~: 720262)\nLogs:\n  Bound result 950400\n  Bound result 6798\n\n[PASS] test_forcedWeiBypassesReceiveAndRemainsAfterClose() (gas: 552499)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 40.72ms (40.65ms CPU time)\n\nRan 11 tests for test/LaunchToken.t.sol:LaunchTokenTest\n[PASS] testFuzz_transferConservesSupply(uint256) (runs: 256, μ: 91063, ~: 91075)\nLogs:\n  Bound result 436807866029379162489715437\n\n[PASS] test_approveAndTransferFrom() (gas: 195811)\n[PASS] test_approve_revertsToZeroAddress() (gas: 32332)\n[PASS] test_constructorEmitsMintTransfer() (gas: 8083)\n[PASS] test_fixedSupplyMintedToDeployer() (gas: 41674)\n[PASS] test_infiniteAllowanceIsNotDecremented() (gas: 122684)\n[PASS] test_metadata() (gas: 26725)\n[PASS] test_noMintOrAdminEntrypoints() (gas: 157457)\n[PASS] test_transfer() (gas: 93627)\n[PASS] test_transfer_revertsOnInsufficientBalance() (gas: 37492)\n[PASS] test_transfer_revertsToZeroAddress() (gas: 32408)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 51.88ms (32.80ms CPU time)\n\nRan 59 tests for test/HeirloomVault.t.sol:HeirloomVaultTest\n[PASS] testFuzz_anyCheckInPushesDeadlineForward(uint256,uint8) (runs: 256, μ: 265798, ~: 265827)\nLogs:\n  Bound result 416000000\n  Bound result 4\n\n[PASS] testFuzz_claim_neverBeforeDeadline(uint256,uint256) (runs: 256, μ: 222279, ~: 222602)\nLogs:\n  Bound result 315277818\n  Bound result 1\n\n[PASS] testFuzz_claim_onlyBeneficiary(address,uint256) (runs: 256, μ: 218871, ~: 219138)\nLogs:\n  Bound result 12810\n\n[PASS] testFuzz_claim_succeedsOnceDeadlinePassed(uint256,uint256) (runs: 256, μ: 226182, ~: 226420)\nLogs:\n  Bound result 132852036\n  Bound result 54788898\n\n[PASS] testFuzz_openVault_periodLimits(uint256) (runs: 256, μ: 50121, ~: 43786)\n[PASS] testFuzz_ownerCanAlwaysWithdrawBeforeClaim(uint256,uint96) (runs: 256, μ: 237097, ~: 237215)\nLogs:\n  Bound result 1647\n  Bound result 2761\n\n[PASS] testFuzz_setPeriod_limits(uint256) (runs: 256, μ: 143652, ~: 142773)\n[PASS] testFuzz_withdraw_amounts(uint96,uint96) (runs: 256, μ: 219559, ~: 202209)\nLogs:\n  Bound result 1647\n\n[PASS] test_checkIn_afterDeadlineRestoresProtection() (gas: 271050)\n[PASS] test_checkIn_resetsTimerAndEmits() (gas: 174302)\n[PASS] test_checkIn_revertsWithoutVault() (gas: 34717)\n[PASS] test_claim_cannotRepeat() (gas: 242483)\n[PASS] test_claim_emptyVaultStillCloses() (gas: 151490)\n[PASS] test_claim_onlyAffectsThatVault() (gas: 393804)\n[PASS] test_claim_ownerLosesAccessAfterClaim() (gas: 417064)\n[PASS] test_claim_revertsBeforeDeadline() (gas: 201049)\n[PASS] test_claim_revertsExactlyAtDeadline() (gas: 292226)\n[PASS] test_claim_revertsForNonBeneficiary() (gas: 243512)\n[PASS] test_claim_revertsWithoutVault() (gas: 42312)\n[PASS] test_claim_succeedsAfterDeadlineAndClosesVault() (gas: 236518)\n[PASS] test_closeVault_afterDeadlineBeatsClaim() (gas: 241631)\n[PASS] test_closeVault_emptyVault() (gas: 147259)\n[PASS] test_closeVault_returnsEverythingAndClears() (gas: 219959)\n[PASS] test_closeVault_revertsWithoutVault() (gas: 39747)\n[PASS] test_closeVault_thenReopenStartsFresh() (gas: 321323)\n[PASS] test_constants() (gas: 16721)\n[PASS] test_deposit_addsBalanceAndChecksIn() (gas: 277343)\n[PASS] test_deposit_isolatedPerOwner() (gas: 358747)\n[PASS] test_deposit_revertsOnZeroValue() (gas: 126471)\n[PASS] test_deposit_revertsWithoutVault() (gas: 41485)\n[PASS] test_fallback_rejectsUnknownCalldata() (gas: 61982)\n[PASS] test_openVault_acceptsBoundaryPeriods() (gas: 224520)\n[PASS] test_openVault_revertsOnPeriodTooLong() (gas: 42918)\n[PASS] test_openVault_revertsOnPeriodTooShort() (gas: 37714)\n[PASS] test_openVault_revertsOnSelfBeneficiary() (gas: 35613)\n[PASS] test_openVault_revertsOnZeroBeneficiary() (gas: 35080)\n[PASS] test_openVault_revertsOnZeroPeriod() (gas: 37471)\n[PASS] test_openVault_revertsWhenAlreadyOpen() (gas: 131306)\n[PASS] test_openVault_storesFieldsAndEmits() (gas: 153352)\n[PASS] test_receive_rejectsDirectEth() (gas: 39580)\n[PASS] test_reentrancy_claimCannotDoubleSpend() (gas: 697332)\n[PASS] test_reentrancy_withdrawCannotDoubleSpend() (gas: 747805)\n[PASS] test_rejectingBeneficiary_onlyBlocksItself() (gas: 648164)\n[PASS] test_rejectingOwner_cannotBrickOthers() (gas: 863979)\n[PASS] test_setBeneficiary_oldHeirLosesClaim() (gas: 282393)\n[PASS] test_setBeneficiary_revertsOnInvalid() (gas: 155483)\n[PASS] test_setBeneficiary_revertsWithoutVault() (gas: 37362)\n[PASS] test_setBeneficiary_updatesAndChecksIn() (gas: 164567)\n[PASS] test_setPeriod_revertsOnInvalid() (gas: 164877)\n[PASS] test_setPeriod_revertsWithoutVault() (gas: 34912)\n[PASS] test_setPeriod_updatesAndChecksIn() (gas: 159899)\n[PASS] test_views_zeroForUnknownOwner() (gas: 40309)\n[PASS] test_withdraw_allowedAfterDeadlineBeforeClaim() (gas: 261516)\n[PASS] test_withdraw_cannotTouchAnotherVault() (gas: 353153)\n[PASS] test_withdraw_countsAsCheckIn() (gas: 228872)\n[PASS] test_withdraw_partialAndFull() (gas: 310335)\n[PASS] test_withdraw_revertsOnInsufficientBalance() (gas: 197649)\n[PASS] test_withdraw_revertsOnZeroAmount() (gas: 192759)\n[PASS] test_withdraw_revertsWithoutVault() (gas: 39931)\nSuite result: ok. 59 passed; 0 failed; 0 skipped; finished in 54.49ms (335.79ms CPU time)\n\nRan 11 tests for test/HeirloomVaultAdversarial.t.sol:HeirloomVaultAdversarialTest\n[PASS] testFuzz_rejectedOwnerActionsCannotDelayAnEligibleClaim(uint256,uint256) (runs: 1000, μ: 325067, ~: 324385)\nLogs:\n  Bound result 420824555\n\n[PASS] testFuzz_validWithdrawalsRoundTripEvenAfterDeadline(uint128,uint256,uint256) (runs: 1000, μ: 353142, ~: 354034)\nLogs:\n  Bound result 14757\n  Bound result 10984\n  Bound result 1727916329\n\n[PASS] test_claimCallbackSeesDeletedStateAndBlocksEveryPayout() (gas: 1018758)\n[PASS] test_closeCallbackSeesDeletedStateAndBlocksEveryPayout() (gas: 1015560)\n[PASS] test_failedClaimRestoresEveryFieldAndCanBeRetried() (gas: 745965)\n[PASS] test_failedCloseRestoresEveryFieldAndCanBeRetried() (gas: 476131)\n[PASS] test_failedWithdrawalPreservesOriginalClaimDeadline() (gas: 411071)\n[PASS] test_oneWeiCanBeWithdrawnAndVaultReused() (gas: 442425)\n[PASS] test_periodCanBeShortenedWithoutReusingTheOldDeadline() (gas: 323787)\n[PASS] test_rejectingReceiverDoesNotBlockEmptyClaimOrClose() (gas: 396775)\n[PASS] test_withdrawCallbackSeesUpdatedStateAndBlocksEveryPayout() (gas: 1152027)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 55.00ms (111.43ms CPU time)\n\nRan 1 test for test/HeirloomVaultInvariant.t.sol:HeirloomVaultInvariantTest\n[PASS]\nHeirloomVaultInvariantTest invariants:\n[PASS] invariant_closedVaultsAreFullyCleared\n[PASS] invariant_contractBalanceEqualsSumOfVaults\n[PASS] invariant_fundsAreConserved\n[PASS] invariant_openVaultsAreWellFormed\n HeirloomVaultInvariantTest invariants (runs: 64, calls: 3072, reverts: 0)\n\n╭--------------+----------------+-------+---------+----------╮\n| Contract     | Selector       | Calls | Reverts | Discards |\n+============================================================+\n| VaultHandler | checkIn        | 333   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | claim          | 309   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | closeVault     | 344   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | deposit        | 343   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | openVault      | 364   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | setBeneficiary | 349   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | setPeriod      | 371   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | warp           | 333   | 0       | 0        |\n|--------------+----------------+-------+---------+----------|\n| VaultHandler | withdraw       | 326   | 0       | 0        |\n╰--------------+----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 315279420\n  Bound result 203933510420\n  Bound result 315273613\n  Bound result 1\n  Bound result 523704915751488514\n  Bound result 172800\n  Bound result 432000\n  Bound result 2944000\n  Bound result 2289979\n  Bound result 0\n  Bound result 5000000000000000000\n  Bound result 2552851541\n  Bound result 98974023\n  Bound result 604800\n  Bound result 86401\n  Bound result 1913548\n  Bound result 3456000\n  Bound result 211979088\n  Bound result 280587680\n  Bound result 8\n  Bound result 315273612\n  Bound result 3104000\n  Bound result 200397019\n  Bound result 329\n  Bound result 2209\n  Bound result 315276430\n  Bound result 499543953\n  Bound result 1730000007\n  Bound result 4\n  Bound result 10018259700474585\n  Bound result 55726\n  Bound result 2944001\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 965.94ms (964.06ms CPU time)\n\nRan 4 tests for test/HeirloomVaultModelInvariant.t.sol:HeirloomVaultModelInvariantTest\n[PASS] invariant_allVaultsAndWalletsMatchIndependentAccounting() (runs: 256, calls: 24576, reverts: 0)\n\n╭----------------------+-------------------+-------+---------+----------╮\n| Contract             | Selector          | Calls | Reverts | Discards |\n+=======================================================================+\n| HeirloomModelHandler | advanceTime       | 2489  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | changeBeneficiary | 2476  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | changePeriod      | 2528  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | checkIn           | 2415  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | claim             | 2403  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | close             | 2524  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | deposit           | 2425  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | invalidAction     | 2434  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | open              | 2419  | 0       | 0        |\n|----------------------+-------------------+-------+---------+----------|\n| HeirloomModelHandler | withdraw          | 2463  | 0       | 0        |\n╰----------------------+-------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 86400\n  Bound result 1000000000000000000\n  Bound result 2592000\n  Bound result 2000000000000000000\n  Bound result 315360000\n  Bound result 3000000000000000000\n  Bound result 85625838\n  Bound result 10829\n  Bound result 9\n  Bound result 86402\n  Bound result 65536\n  Bound result 161558616\n  Bound result 86400\n  Bound result 10000000000000000000\n  Bound result 864000000\n  Bound result 172800\n  Bound result 2588\n  Bound result 1\n  Bound result 315276256\n  Bound result 6881\n  Bound result 0\n  Bound result 11267\n  Bound result 950400\n  Bound result 315285080\n  Bound result 33213009\n  Bound result 86401\n  Bound result 2592000\n  Bound result 86399\n  Bound result 1\n  Bound result 1700000003\n  Bound result 10000000000000000001\n  Bound result 102355346\n  Bound result 866\n  Bound result 4320\n  Bound result 315277041\n  Bound result 2225\n  Bound result 315280324\n  Bound result 315273730\n  Bound result 69697322\n  Bound result 143193066\n  Bound result 0\n  Bound result 223631995\n  Bound result 179200001\n  Bound result 86400\n  Bound result 315273619\n  Bound result 315273609\n  Bound result 0\n  Bound result 315274025\n  Bound result 1850\n  Bound result 10000000000000000000\n  Bound result 2722\n  Bound result 192884927\n  Bound result 315278779\n  Bound result 315274893\n  Bound result 1\n  Bound result 1800086401\n  Bound result 289048545\n\n[PASS] test_handlerExercisesClaimBoundariesAndReopens() (gas: 1654060)\nLogs:\n  Bound result 86400\n  Bound result 1000000000000000000\n  Bound result 2592000\n  Bound result 2000000000000000000\n  Bound result 315360000\n  Bound result 3000000000000000000\n  Bound result 1\n  Bound result 1\n  Bound result 315360000\n  Bound result 1\n  Bound result 1\n\n[PASS] test_handlerExercisesWithdrawalEdgesAfterDeadline() (gas: 1636281)\nLogs:\n  Bound result 86400\n  Bound result 1000000000000000000\n  Bound result 2592000\n  Bound result 2000000000000000000\n  Bound result 315360000\n  Bound result 3000000000000000000\n  Bound result 1\n  Bound result 0\n  Bound result 1000000000000000001\n  Bound result 1\n  Bound result 1\n  Bound result 999999999999999999\n\n[PASS] test_handlerRejectedActionsPreserveExpiredAndAbsentVaults() (gas: 8112972)\nLogs:\n  Bound result 86400\n  Bound result 1000000000000000000\n  Bound result 2592000\n  Bound result 2000000000000000000\n  Bound result 315360000\n  Bound result 3000000000000000000\n  Bound result 1\n  Bound result 0\n  Bound result 86399\n  Bound result 0\n  Bound result 86399\n  Bound result 315360001\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639935\n  Bound result 315360001\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639935\n  Bound result 1000000000000000001\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639935\n  Bound result 1\n  Bound result 115792089237316195423570985008687907853269984665640564039457584007913129639935\n  Bound result 0\n  Bound result 999000000000000000000\n  Bound result 0\n  Bound result 1000000000000000000000\n  Bound result 0\n  Bound result 999000000000000000000\n  Bound result 0\n  Bound result 1000000000000000000000\n  Bound result 315360000\n\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 4.73s (4.74s CPU time)\n\nRan 7 test suites in 4.73s (5.94s CPU time): 91 tests passed, 0 failed, 0 skipped (91 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"e2636557d13eae8945d7d5b546a78475a7410f08a4e56e2d68da03a8393b80f0","verifiedTreeHash":"b13eedcc42a58a7f572067a0b1f8b5163a10e02a","verifierVersion":"0.1.0+48a57703"}]}