{"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":"b0d8cce2-431c-45ab-8eec-c125b83b4548","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"03d6d973c3e184019e68c4de58d30a39b0c61a4cc8eb92d0af4bf9d0c7af9c95","dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","tools":[]},"key":"adapt_contract_project","kind":"code","role":"implement","skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","state":"accepted"},{"acceptedSubmissionHash":"95360173c6572a2c317cf246165e411c437ed80cbeb3bd3e4ef135eebe2fbc55","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"5689b3f445ebdbc2dff2761d94dfddf96e29d34b59342beb2634e522904172cb","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"b76694072b6937116f899c4f5bff76c0f62b4f5dd1ab620d090e6d696034b541","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","tools":[]},"key":"audit_imported_code","kind":"code","role":"review","skillHash":"9897597976b0e72440db7ca402d225aea20f3d37205ba53e7df2400ff80f29cc","skillId":"audit-imported-code","state":"accepted"},{"acceptedSubmissionHash":"b19244a3444254ba27e56f3d2b95f18530640bef78cfc52fd497c366093e7bc6","dependsOn":["adapt_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"8e10e00b855eb32095acc1cff0f8ded8ca5fbf4c8b89cdfe1cb9f55ae9e8e711","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"f427e31c7234f246d963b23084c192e36dd5e62b16bd8108242c1b119143cc81","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"bbc3cab5cef255159af30fffb45af24afdf79415917cb035a36f7334cc6e43f0","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"ca2e16b315e58544345d80956948fc7ac8a6e2955616d6df3274f78db02cf0df","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Launch INFER from this repository as it is: a custom_token launch on Ethereum mainnet, paired with IMD.\n\nToken: contract InferToken, name \"Inference Backed Endogenous Financial Reserve\", symbol INFER, 18 decimals, constructorArgs [], totalSupply 1000000000000000000000000000 (1,000,000,000 INFER minted once to msg.sender, the factory). No owner, no mint or burn after construction, no fee or rule on transfer.\n\nApplication contracts, in this order:\n1. LegacyRedeemer, constructorArgs [\"$token\", \"0xa358491ca72b793ddf21cf46c7289cc6e0ce9e5a\", \"100000000000000000000000000\", \"29417342782992946700\", \"0x2bbc21129e072a2a92a0fd13da750c1f977a25fb\", \"40000000000000000000000000\", \"7851289668371509\", \"0x6ac8ff96fb8dcf5d79a8ef1e59d9ac23f2bd27bc\", \"0x5167d014a056e43883e1bbea5530c3c0dc993281\"]\n2. SeasonVault, constructorArgs [\"$token\", \"0xbefd108085613662356aa26a2466ad3426da9c32\", \"0x6ac8ff96fb8dcf5d79a8ef1e59d9ac23f2bd27bc\"]\n3. StakedInfer, constructorArgs [\"$token\"]\n4. FounderStream, constructorArgs [\"$token\", \"0x5167d014a056e43883e1bbea5530c3c0dc993281\"]\n5. InferDripper, constructorArgs [\"$token\", \"$contract:StakedInfer\", \"1000000000000000000000\"]\n\nEconomics, copied into launch.json exactly: poolBps 2000, initialMarketCapWei 14444444444444444444444 (14,444.44 IMD), remainderTo 0xe95311cccf156cb18194d523cbe44cea33ad026e.\n\nThe remainder goes to the GenesisSplitter at 0xe95311cccf156cb18194d523cbe44cea33ad026e, deployed earlier from the source in lib/genesis-splitter. After launch, split(launchNumber) pays LegacyRedeemer, SeasonVault and FounderStream the genesisAllocation() each declares and sends the rest to 0x6ac8ff96fb8dcf5d79a8ef1e59d9ac23f2bd27bc. StakedInfer and InferDripper declare none.\n\nWhat each does: LegacyRedeemer burns MIYA or MXXN for INFER at the fixed rates above for one year (large redemptions vest over 13 weeks), then sweeps to the treasury. SeasonVault pays the points allocation in seasons the governor opens with 48 hours' notice; roots are set by a swarm panel's attestation or by the governor behind 48 hours. FounderStream releases its allocation linearly over 12 months after a 30-day cliff. StakedInfer is an ERC-4626 vault over INFER. InferDripper streams INFER that the treasury buys with revenue into StakedInfer.\n\nEvery constructor is fully configured with static arguments and makes no external call, except InferDripper reading asset() of StakedInfer, an earlier contract of this launch. The tests must keep passing: forge test, and forge test --fork-url on mainnet, which includes this launch through the live ProjectFactory with the splitter paying every contract.\n\nAudit it as it arrived. Adapt only what the launch rules or a reproduced audit finding require, and record each change in ADAPTATION.md. Do not change allocations, rates, recipients, schedules or the token, and do not add an owner, a pause, minting or a fee.","parentJobId":null,"planHash":"8fdaa84d50a96de1ca0f2240e0169fa8cf44b649ca157a095e38db53152429d2","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"b0d8cce2-431c-45ab-8eec-c125b83b4548","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1282-launch-infer-repository-custom-token"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51606","feedbackHash":"c4d45e469208dbd40409b2c96d0c6df560b85c7a3b16a17bb180dd1a0179db95","nodeKey":"adapt_contract_project","submissionHash":"03d6d973c3e184019e68c4de58d30a39b0c61a4cc8eb92d0af4bf9d0c7af9c95","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51320","feedbackHash":"1f38abd51a1f7cab22f4f724bdf112ea343fd9db5476be035584d3d2a32c08b1","nodeKey":"adapt_contract_project","submissionHash":"3f903f10cc563630d5e362c6c64c68b3e3ccece0800283800f7f6b00953a1ad7","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"50981","feedbackHash":"c7e78f34a9771af162e7e0886c9c2a1d67b0bf0711de99648671aea122e3e8ad","nodeKey":"audit_economics","submissionHash":"95360173c6572a2c317cf246165e411c437ed80cbeb3bd3e4ef135eebe2fbc55","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51026","feedbackHash":"d518d881561fcc88cdc8c2e2c78fdc2ce9c895daf1d55999fd222dd99f3411a9","nodeKey":"audit_flow","submissionHash":"5689b3f445ebdbc2dff2761d94dfddf96e29d34b59342beb2634e522904172cb","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50969","feedbackHash":"1ce383cd88f28e927cd75dab0c0595853befa5759e1e48ea29c8d50e28af4a4b","nodeKey":"audit_imported_code","submissionHash":"b76694072b6937116f899c4f5bff76c0f62b4f5dd1ab620d090e6d696034b541","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52082","feedbackHash":"7107a3e83c2b93aa70deb4c9c8c3e391748941d271ecfcfcf70dcb583d6d91f9","nodeKey":"audit_judge","submissionHash":"63a216f3a0db0d7bf94ee6e2e923b7da71e4be6ec401bfda0ad5444efaa526ea","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51250","feedbackHash":"912fc6a7bcf83c67a4182d2f5d3f1b3665238b77155a21857cce3d227f50b347","nodeKey":"audit_judge","submissionHash":"b19244a3444254ba27e56f3d2b95f18530640bef78cfc52fd497c366093e7bc6","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50989","feedbackHash":"5d7b7aeb09a31956e39435b51a83e9de303b79b47e4450d30cd1d4b33124059e","nodeKey":"audit_math","submissionHash":"8e10e00b855eb32095acc1cff0f8ded8ca5fbf4c8b89cdfe1cb9f55ae9e8e711","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51285","feedbackHash":"a415f845062c3b024f912fb2e9910c7bb8978bd4db299b1ca9cf66be15a22180","nodeKey":"audit_permissions","submissionHash":"f427e31c7234f246d963b23084c192e36dd5e62b16bd8108242c1b119143cc81","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52242","feedbackHash":"4e778aa75a86b098f2300399ef2ec86f9cf7982a12db3b1ae43efc8a4ca65278","nodeKey":"manifest","submissionHash":"bbc3cab5cef255159af30fffb45af24afdf79415917cb035a36f7334cc6e43f0","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51180","feedbackHash":"4073797f7cf0cb36c7a13506c8fb565735abd3927415ffd9b8d2518d3806dfb3","nodeKey":"manifest","submissionHash":"9b4271205a0d13943ca9fd170a55403325ea4b590c1e53abcef2c430191ebbe7","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"50965","feedbackHash":"53e54eee0e2f724332b1c1773f71ed41a49da6280eed0c8dd29213c3922a14ba","nodeKey":"write_foundry_tests","submissionHash":"043f560b59772c298caf25554a0977e557287b4564e5582fca41faed15c63cac","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51088","feedbackHash":"3aa35a912831c3c17d8bc9a96c6b47989f8d96382d3325bba5bb04a14a02881d","nodeKey":"write_foundry_tests","submissionHash":"ca2e16b315e58544345d80956948fc7ac8a6e2955616d6df3274f78db02cf0df","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"a384a95516643517dc2ef7c39f83023491f4313654c1c1c9aef9d4165305c8b6","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"b4516975fff1f7285c5f8760a2cd186e695f1597fce2e527b681a61356420cbd","device":"a12c1a6f9430f757","findings":[],"hash":"03d6d973c3e184019e68c4de58d30a39b0c61a4cc8eb92d0af4bf9d0c7af9c95","nodeId":"4e0b3207-e85f-4111-922e-4dd273ec2f7d","outcome":"completed","summary":"Implemented four reproduced fixes and documented the remaining findings. All 13 verdicts are in [.imd-responses.json](.imd-responses.json); changes and evidence are in [ADAPTATION.md](ADAPTATION.md).\n\nChecks passed: `forge build`, 93 local tests, 97 mainnet-fork tests, all supplied proofs, and formatting.\n\nToken, allocations, rates, recipients, constructors, and protected configuration remain unchanged.","treeHash":"8924766a2297bd2ca415c5eefddf64c2cf050fdb","usage":{"cachedInputTokens":1374208,"inputTokens":117064,"model":"gpt-6-astra","outputTokens":18717,"runtime":"codex","turns":6,"wallClockMs":545474}},{"artifacts":[],"attempt":1,"bundleHash":"e8fbfe448f64322cbddab2c3342745ab43049d910cf69a28eb5ec97007cb59a1","device":"1272a65455eb54ca","findings":[],"hash":"043f560b59772c298caf25554a0977e557287b4564e5582fca41faed15c63cac","nodeId":"55467521-e7e6-4e63-826b-12a8ca9785b7","outcome":"completed","summary":"Added 40 tests, including six randomized invariant suites, entirely under `test/`.\n\nVerification:\n- `forge build`: passed.\n- `forge test`: 116 passed; 2 fork-only skips.\n- Mainnet fork: 120 passed; no failures or skips.\n\nProduction code and configuration remain unchanged. No additional defects were reproduced. Coverage and results are documented in `test/ADAPTATION.md`.","treeHash":"3a01e6963a9b5ebfccc322e1e05124484ab8236d","usage":{"cachedInputTokens":4575616,"inputTokens":167223,"model":"gpt-6-astra","outputTokens":41319,"runtime":"codex","turns":12,"wallClockMs":1150099}},{"artifacts":[],"attempt":1,"bundleHash":"6ea0b9c2ce892b148eea3d7fbf4d8d7e9d1d2ed26cef126a6accb334a7fde356","device":"fe36973c683b5abb","findings":[],"hash":"3f903f10cc563630d5e362c6c64c68b3e3ccece0800283800f7f6b00953a1ad7","nodeId":"4e0b3207-e85f-4111-922e-4dd273ec2f7d","outcome":"completed","summary":"Implemented four reproduced audit fixes and constructor compatibility. Documented the required post-launch NFT and MXXN dust steps in [ADAPTATION.md](/home/imd-worker-361/.identitymd/work/b0d8cce2-431c-45ab-8eec-c125b83b4548/4e0b3207-e85f-4111-922e-4dd273ec2f7d/ADAPTATION.md).\n\nVerification passed:\n\n- `forge build`\n- Local tests: 76 passed\n- Mainnet fork: 80 passed, including the live factory and specified splitter\n\nToken, allocations, rates, recipients, build configuration, and dependencies remain unchanged.","treeHash":"520c37eb41b6f4620de5d560019eadf7ffc2fca6","usage":{"cachedInputTokens":2052992,"inputTokens":123022,"model":"gpt-6-astra","outputTokens":25362,"runtime":"codex","turns":9,"wallClockMs":741610}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"56b13350bba65fd7","findings":[{"citation":"resolved","description":"drip() exists to move Treasury-bought INFER to stakers. Its only liveness guard is `totalSupply() == 0`, justified in the contract's own header: INFER landing in an empty ERC-4626 is captured by its virtual shares. But StakedInfer uses a 6-decimal offset, so the vault carries 1e6 virtual shares against 1 virtual asset. A single 1 wei deposit mints exactly 1e6 real shares (floor(1 * (0 + 1e6) / (0 + 1))), passes the guard, and leaves the virtual shares with 50% of the weight. Every drip while the vault stays that small is split 50/50 between the lone holder and the virtual shares; the virtual half can never be redeemed by anyone and is not recovered by later deposits (the proof shows 2,946 INFER still in the vault with zero shares outstanding after a 10,000 INFER staker enters and everyone exits). The assumption violated (first principles): 'non-zero supply' was taken to mean 'virtual shares are negligible'; the actual bound is a virtual weight of 1e6/(S+1e6), which is 50% at the minimum possible deposit and 1% only once S >= 99e6 shares. This state is the natural one at launch: the Treasury funds the dripper, the first curious staker deposits dust, a keeper (paid 1% of the release) drips hourly. Loss per hour is bounded by half of releasable(): with a 1,000,000 INFER buffer, 2,946 INFER per hour; with the floor, 500 INFER per hour. Suggested minimal fix preserving the design: refuse to drip while the vault's real backing is negligible, e.g. `if (totalSupply == 0 || IERC4626(vault).totalAssets() < minDrip) revert VaultEmpty();` (the proof passes with exactly that patch), or require totalSupply() >= 1e9 shares so the virtual weight is below 0.1%.","line":72,"path":"src/InferDripper.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {InferToken} from \"src/InferToken.sol\";\nimport {StakedInfer} from \"src/StakedInfer.sol\";\nimport {InferDripper} from \"src/InferDripper.sol\";\n\n/// @notice InferDripper.drip() refuses only while sINFER has NO shares. A vault holding a single 1-wei deposit\n/// (1e6 shares against 1e6 virtual shares) passes the guard, and half of every drip is captured by the virtual\n/// shares: INFER that no holder can ever redeem. The dripper's own comment names this exact failure (INFER\n/// landing in a near-empty ERC-4626 is stranded by its virtual shares) as the reason for the guard.\ncontract DripperVirtualShareLeakTest is Test {\n    InferToken infer;\n    StakedInfer vault;\n    InferDripper dripper;\n    address treasury = makeAddr(\"treasury\");\n    address staker = makeAddr(\"staker\");\n    address keeper = makeAddr(\"keeper\");\n\n    function setUp() public {\n        infer = new InferToken();\n        vault = new StakedInfer(IERC20(address(infer)));\n        dripper = new InferDripper(address(infer), address(vault), 1_000e18);\n        // The Treasury buys 1,000,000 INFER with revenue and parks it in the dripper for stakers.\n        infer.transfer(address(dripper), 1_000_000e18);\n        infer.transfer(staker, 1e18);\n    }\n\n    function test_oneWeiDepositPassesTheEmptyVaultGuardAndStrandsHalfOfEveryDrip() public {\n        // The only staker deposits 1 wei: 1e6 real shares, next to the 1e6 virtual shares.\n        vm.startPrank(staker);\n        infer.approve(address(vault), type(uint256).max);\n        uint256 shares = vault.deposit(1, staker);\n        vm.stopPrank();\n        assertEq(shares, 1e6);\n        assertEq(vault.totalSupply(), 1e6, \"the guard sees a non-empty vault\");\n\n        // An hour later a keeper drips: 1,000,000e18 * 3600 / 7 days = 5,952.38 INFER, 1% to the keeper.\n        vm.warp(block.timestamp + 1 hours);\n        vm.roll(block.number + 300);\n        vm.prank(keeper);\n        (bool ok, bytes memory ret) = address(dripper).call(abi.encodeCall(dripper.drip, ()));\n        if (!ok) return; // refused while the real shares are negligible: nothing stranded, the guard did its job\n        (uint256 toVault,) = abi.decode(ret, (uint256, uint256));\n        assertGt(toVault, 5_000e18);\n\n        // The vault now holds `toVault` + 1 wei. The staker, the ONLY share holder, can redeem only about half.\n        uint256 redeemable = vault.maxWithdraw(staker);\n        uint256 stranded = vault.totalAssets() - redeemable;\n        emit log_named_uint(\"INFER dripped into the vault\", toVault);\n        emit log_named_uint(\"INFER the only holder can withdraw\", redeemable);\n        emit log_named_uint(\"INFER stranded in virtual shares\", stranded);\n\n        // Expected: a drip into a vault whose real shares are negligible is refused, or at least the stranded part\n        // is negligible (the guard's stated purpose). Actual: about half the drip is unrecoverable by anyone.\n        assertLt(stranded, toVault / 100, \"no more than 1% of a drip may be captured by virtual shares\");\n    }\n\n    function test_strandedInferStaysStrandedAfterRealStakersArrive() public {\n        vm.startPrank(staker);\n        infer.approve(address(vault), type(uint256).max);\n        vault.deposit(1, staker);\n        vm.stopPrank();\n        vm.warp(block.timestamp + 1 hours);\n        vm.roll(block.number + 300);\n        vm.prank(keeper);\n        (bool ok, bytes memory ret) = address(dripper).call(abi.encodeCall(dripper.drip, ()));\n        if (!ok) return; // refused while the real shares are negligible: nothing stranded, the guard did its job\n        (uint256 toVault,) = abi.decode(ret, (uint256, uint256));\n\n        // A real staker deposits 10,000 INFER later; everyone redeems. What is left in the vault belongs to nobody.\n        address whale = makeAddr(\"whale\");\n        infer.transfer(whale, 10_000e18);\n        vm.startPrank(whale);\n        infer.approve(address(vault), type(uint256).max);\n        vault.deposit(10_000e18, whale);\n        vm.stopPrank();\n        vm.roll(block.number + 1);\n        uint256 whaleShares = vault.balanceOf(whale);\n        uint256 stakerShares = vault.balanceOf(staker);\n        vm.prank(whale);\n        vault.redeem(whaleShares, whale, whale);\n        vm.prank(staker);\n        vault.redeem(stakerShares, staker, staker);\n        assertEq(vault.totalSupply(), 0, \"no shares left\");\n        uint256 stranded = infer.balanceOf(address(vault));\n        emit log_named_uint(\"INFER left in the vault with zero shares outstanding\", stranded);\n        assertLt(stranded, toVault / 100, \"no more than 1% of a drip may be captured by virtual shares\");\n    }\n}","reproduction":"Deploy InferToken, StakedInfer(infer), InferDripper(infer, vault, 1000e18). Transfer 1,000,000e18 INFER to the dripper. Staker: approve and vault.deposit(1, staker) -> 1e6 shares, totalSupply = 1e6. Warp +1 hour, roll +300 blocks. Anyone: dripper.drip(). Expected: refused (guard's stated purpose) or at most a negligible part captured by virtual shares. Actual: drip succeeds, toVault = 5,892.857 INFER; vault.maxWithdraw(staker) = 2,946.43 INFER; totalAssets() - maxWithdraw(staker) = 2,946.43 INFER captured by virtual shares. Then a whale deposits 10,000e18, next block both redeem all shares: totalSupply() == 0 and infer.balanceOf(vault) = 2,946.43 INFER, unrecoverable. Run: forge test --match-path test/scratch/DripperVirtualShareLeak.t.sol (2 failing tests on this code; both pass once drip() refuses a dust-backed vault).","severity":"medium","snippet":"        if (IERC20(vault).totalSupply() == 0) revert VaultEmpty();","title":"InferDripper's empty-vault guard is defeated by a 1 wei deposit: half of every drip into a dust-sized sINFER is captured by virtual shares and stranded forever"},{"citation":"resolved","description":"The immediate cap is keyed on msg.sender per legacy token (IMMEDIATE_A = 200,000 MIYA worth, 5.88M INFER at the launch rate; IMMEDIATE_B_INFER = 930,000 INFER). Nothing ties the cap to the legacy holder's position: the legacy tokens are freely transferable to fresh EOAs (MIYA's whitelist is satisfied by the redeemer being the receiver, so a no-NFT fresh wallet can redeem exactly as the fork test shows), so a holder moves 200,000 MIYA to each of N wallets and each redeems at once with `later == 0`. The vesting guarantee the brief and header promise ('a wallet cannot start its clock with dust, wait, and then redeem the bulk') is therefore only a convenience limit, not a sell-pressure control; the 13-week vesting of large redemptions can be avoided at the cost of N extra transactions. Reported as low because the economic terms (rates, allocations, caps) are fixed by the brief and may not change; the author should either accept and document the per-wallet nature of the cap, or decide on a scope change. No funds are lost and no allocation is exceeded.","line":144,"path":"src/LegacyRedeemer.sol","reproduction":"Local, with two plain burnable ERC-20s standing in for MIYA/MXXN and the launch rates: whale holds 2,000,000 MIYA. (a) whale.redeem(MIYA, 1,000,000e18): paid now 5,883,468.55 INFER (= immediateCapA), vesting 23,533,874.22 INFER over 91 days. (b) whale transfers 200,000 MIYA to each of 5 fresh wallets; each calls redeem(MIYA, 200,000e18): each is paid 5,883,468.55 INFER immediately, vestingOf(w).total == 0 for every wallet; total paid at once 29,417,342.78 INFER = the full 1,000,000 MIYA at rate, versus 5.88M under the intended single-wallet path. Scratch test test/scratch/RedeemerSybil.t.sol demonstrates this (it passes on the current code, i.e. the bypass works).","severity":"low","snippet":"        uint256 roomNow = immediateCap(legacy) - paidImmediately[address(legacy)][msg.sender];","title":"LegacyRedeemer's 'large redemptions vest over 13 weeks' is a per-wallet cap only: splitting a position across fresh wallets redeems any amount at once"},{"citation":"resolved","description":"Trust assumption to record, not a code defect against the brief (which asks for no owner and for seasons the governor opens). Every path that moves INFER out of SeasonVault to anyone other than a registered claimant requires a prior governor action: openSeason (onlyGovernor) creates the pot a root can later pay from; endProgram and sweep require `endEta != 0`, which only announceEnd (onlyGovernor) sets. There is no time-based fallback (e.g. 'if no season has been opened for X years anyone may end the program'). FounderStream and LegacyRedeemer do have ownerless terminal paths (release after 365 days, sweep after closesAt), so the three buckets differ in liveness. The governor at 0xbefd108085613662356aa26a2466ad3426da9c32 holding this power is by design; the consequence of key loss is permanent lock of up to 310,000,000 INFER (31% of supply) plus any unassigned part of opened pots.","line":321,"path":"src/SeasonVault.sol","reproduction":"After split(launchNumber) pays 310,000,000e18 INFER to SeasonVault: with no call from the governor, for any time T: openSeason -> NotGovernor from any other caller; endProgram -> NoEndAnnounced; sweep -> NotEnded; expire(s) -> NoSuchSeason (nothing opened); available() == 310,000,000e18 but nothing can move it. Expected under 'no owner' design: at minimum a documented acceptance of this lock risk; actual: undocumented in README/ADAPTATION. State the trust assumption or add a long time-based ownerless end path if a scope decision allows.","severity":"info","snippet":"    function announceEnd() external onlyGovernor {","title":"SeasonVault's 310M INFER has no governor-independent exit: if the governor never opens a season or announces the end, the allocation is locked forever"}],"hash":"5689b3f445ebdbc2dff2761d94dfddf96e29d34b59342beb2634e522904172cb","nodeId":"278ec209-fe7d-4ee1-88d1-467a60d244de","outcome":"completed","summary":"Nothing is outstanding, so there is nothing to request. The review is finished and every remaining item would depend on work already done — no subagents, no further reads, no further tests. Calling a workflow here would be unjustified for a single bounded read-only review.\n\nDeliverable: `.imd-findings.json` in the repository root, holding 3 findings and 34 coverage rows. The tracked tree is unchanged (`git diff` empty), and the project's own suite is green at 76 passed, 0 failed, 2 skipped. Two scratch proofs under `test/scratch/` fail on the current code on purpose; `test/scratch/` is not kept.\n\nFindings, ranked:\n\n- **medium — `src/InferDripper.sol:72`** carries a proof. The empty-vault guard checks only `totalSupply() == 0`. Because sINFER has a 6-decimal offset (1e6 virtual shares vs 1 virtual asset), a single 1 wei deposit passes the guard and leaves the virtual shares holding 50% of the pool. Every drip then splits ~half to the lone holder and ~half into virtual shares that no one can ever redeem. Measured: a 1,000,000 INFER buffer dripped 5,892.86 INFER, of which 2,946.43 is stranded; after a later 10,000 INFER staker enters and all shares are redeemed, 2,946.43 INFER still sits in the vault with zero shares outstanding. The proof fails on the current code and passes once `drip()` refuses a dust-backed vault.\n- **low — `src/LegacyRedeemer.sol:144`**. The immediate cap that forces large redemptions to vest is keyed on `msg.sender`, so a holder spreads 200,000 MIYA across fresh wallets and takes the whole position at once with nothing vesting (29.4M INFER paid instantly versus 5.88M single-wallet). No allocation is exceeded and nothing is lost; the 13-week guarantee is per-wallet, not per-position.\n- **info — `src/SeasonVault.sol:321`**. Every path that moves the 310M allocation to anyone but a registered claimant needs a prior governor call, so if the governor never opens a season or announces the end, the allocation is locked with no time-based fallback — unlike `FounderStream` and `LegacyRedeemer`. A trust assumption to record, not a breach of the brief.\n\nCoverage: all 30 listed entry points have a verdict, all `holds` except `InferDripper.drip()` (finding 1), `LegacyRedeemer.redeem` (finding 2) and `SeasonVault.announceEnd()` (finding 3). I also added four rows for invariants and the periphery: balance-versus-`reserved` in SeasonVault, balance-versus-`owedVesting` in LegacyRedeemer, the splitter's handling of this launch, and a diff of `src/base/ERC4626StaticAsset.sol` against the vendored OpenZeppelin v5.7.0 base (only the constructor's decimals probe differs).\n\nI chased the static-analysis leads too: the aderyn `abi.encodePacked` collision in `SeasonAttestation` is rejected (alphanumeric-only CIDs between non-alphanumeric delimiters cannot collide), and the slither `IERC721Like`/`decimals()` lead on `releaseNft` is rejected (calling it with an 18-decimal ERC-20 reverts on zero self-allowance).","treeHash":null,"usage":{"cachedInputTokens":1659661,"inputTokens":306,"model":"claude-fable-5-1","outputTokens":44635,"runtime":"claude","turns":34,"wallClockMs":743206}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"077d2937780a81bc","findings":[{"citation":"resolved","description":"Merged from audit_flow (medium) and audit_economics (low); same root cause. drip() refuses only while sINFER has zero shares, and the contract's own header gives the reason: INFER landing in an empty ERC-4626 is captured by its virtual shares. StakedInfer uses _decimalsOffset() = 6, so the vault carries 1e6 virtual shares against 1 virtual asset. A deposit of 1 wei of INFER mints exactly 1e6 real shares (1 * (0 + 1e6) / (0 + 1)), which passes the guard while the virtual shares still hold 50% of the weight. Every drip while the vault stays that small is split between the lone dust holder and the virtual shares. The virtual holding 1e6*(T+1)/(S+1e6) is invariant under later deposits (a deposit of A assets mints A*(S+1e6)/(T+1) shares, which leaves that quotient unchanged), so what the virtual shares captured is never recoverable: it stays in the vault after every real share is redeemed. This is the state right after launch: the vault ships with no genesis deposit, the Treasury funds the dripper, any address deposits 1 wei (profitably, since it collects the other half of each drip), and keepers paid 1% drip hourly. Loss per hour is bounded by half of releasable(): about 2,946 INFER per hour from a 1,000,000 INFER buffer, 500 INFER per hour at the 1,000 INFER floor. Recalibrated to medium: loss of Treasury buyback funds under a specific but natural condition, no privileged actor needed. Minimal fix preserving the design: refuse to drip while the real backing is negligible, e.g. also revert VaultEmpty when IERC4626(vault).totalAssets() < minDrip, or require totalSupply() >= 1e9 shares so the virtual weight is below 0.1%. The attached proof passes with either.","line":72,"path":"src/InferDripper.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {InferToken} from \"src/InferToken.sol\";\nimport {StakedInfer} from \"src/StakedInfer.sol\";\nimport {InferDripper} from \"src/InferDripper.sol\";\n\n/// @notice InferDripper.drip() refuses only while sINFER has NO shares. A vault holding a single 1-wei deposit\n/// (1e6 shares against 1e6 virtual shares) passes the guard, and half of every drip is captured by the virtual\n/// shares: INFER that no holder can ever redeem. The dripper's own comment names this exact failure (INFER\n/// landing in a near-empty ERC-4626 is stranded by its virtual shares) as the reason for the guard.\ncontract DripperVirtualShareLeakTest is Test {\n    InferToken infer;\n    StakedInfer vault;\n    InferDripper dripper;\n    address treasury = makeAddr(\"treasury\");\n    address staker = makeAddr(\"staker\");\n    address keeper = makeAddr(\"keeper\");\n\n    function setUp() public {\n        infer = new InferToken();\n        vault = new StakedInfer(IERC20(address(infer)));\n        dripper = new InferDripper(address(infer), address(vault), 1_000e18);\n        // The Treasury buys 1,000,000 INFER with revenue and parks it in the dripper for stakers.\n        infer.transfer(address(dripper), 1_000_000e18);\n        infer.transfer(staker, 1e18);\n    }\n\n    function test_oneWeiDepositPassesTheEmptyVaultGuardAndStrandsHalfOfEveryDrip() public {\n        // The only staker deposits 1 wei: 1e6 real shares, next to the 1e6 virtual shares.\n        vm.startPrank(staker);\n        infer.approve(address(vault), type(uint256).max);\n        uint256 shares = vault.deposit(1, staker);\n        vm.stopPrank();\n        assertEq(shares, 1e6);\n        assertEq(vault.totalSupply(), 1e6, \"the guard sees a non-empty vault\");\n\n        // An hour later a keeper drips: 1,000,000e18 * 3600 / 7 days = 5,952.38 INFER, 1% to the keeper.\n        vm.warp(block.timestamp + 1 hours);\n        vm.roll(block.number + 300);\n        vm.prank(keeper);\n        (bool ok, bytes memory ret) = address(dripper).call(abi.encodeCall(dripper.drip, ()));\n        if (!ok) return; // refused while the real shares are negligible: nothing stranded, the guard did its job\n        (uint256 toVault,) = abi.decode(ret, (uint256, uint256));\n        assertGt(toVault, 5_000e18);\n\n        // The vault now holds `toVault` + 1 wei. The staker, the ONLY share holder, can redeem only about half.\n        uint256 redeemable = vault.maxWithdraw(staker);\n        uint256 stranded = vault.totalAssets() - redeemable;\n        emit log_named_uint(\"INFER dripped into the vault\", toVault);\n        emit log_named_uint(\"INFER the only holder can withdraw\", redeemable);\n        emit log_named_uint(\"INFER stranded in virtual shares\", stranded);\n\n        // Expected: a drip into a vault whose real shares are negligible is refused, or at least the stranded part\n        // is negligible (the guard's stated purpose). Actual: about half the drip is unrecoverable by anyone.\n        assertLt(stranded, toVault / 100, \"no more than 1% of a drip may be captured by virtual shares\");\n    }\n\n    function test_strandedInferStaysStrandedAfterRealStakersArrive() public {\n        vm.startPrank(staker);\n        infer.approve(address(vault), type(uint256).max);\n        vault.deposit(1, staker);\n        vm.stopPrank();\n        vm.warp(block.timestamp + 1 hours);\n        vm.roll(block.number + 300);\n        vm.prank(keeper);\n        (bool ok, bytes memory ret) = address(dripper).call(abi.encodeCall(dripper.drip, ()));\n        if (!ok) return; // refused while the real shares are negligible: nothing stranded, the guard did its job\n        (uint256 toVault,) = abi.decode(ret, (uint256, uint256));\n\n        // A real staker deposits 10,000 INFER later; everyone redeems. What is left in the vault belongs to nobody.\n        address whale = makeAddr(\"whale\");\n        infer.transfer(whale, 10_000e18);\n        vm.startPrank(whale);\n        infer.approve(address(vault), type(uint256).max);\n        vault.deposit(10_000e18, whale);\n        vm.stopPrank();\n        vm.roll(block.number + 1);\n        uint256 whaleShares = vault.balanceOf(whale);\n        uint256 stakerShares = vault.balanceOf(staker);\n        vm.prank(whale);\n        vault.redeem(whaleShares, whale, whale);\n        vm.prank(staker);\n        vault.redeem(stakerShares, staker, staker);\n        assertEq(vault.totalSupply(), 0, \"no shares left\");\n        uint256 stranded = infer.balanceOf(address(vault));\n        emit log_named_uint(\"INFER left in the vault with zero shares outstanding\", stranded);\n        assertLt(stranded, toVault / 100, \"no more than 1% of a drip may be captured by virtual shares\");\n    }\n}","reproduction":"Deploy InferToken, StakedInfer(infer), InferDripper(infer, vault, 1000e18). infer.transfer(dripper, 1_000_000e18). Staker: infer.approve(vault), vault.deposit(1, staker) -> returns 1e6 shares, vault.totalSupply() == 1e6. vm.warp(+1 hours); vm.roll(+300). Anyone: dripper.drip(). Expected: refused (the guard's stated purpose), or at most a negligible part captured by virtual shares. Actual: drip succeeds with toVault = 5,892,857,142,857,142,857,143 wei; vault.maxWithdraw(staker) = 2,946,428,571,428,571,428,572; totalAssets() - maxWithdraw(staker) = 2,946,428,571,428,571,428,572 captured by virtual shares. Then a whale deposits 10,000e18, next block both redeem every share: totalSupply() == 0 and infer.balanceOf(vault) = 2,946,428,786,617,190,675,894, unrecoverable by anyone. Run: forge test --match-path test/scratch/DripperVirtualShareLeak.t.sol (2 failing tests on this code; both pass once drip() refuses a dust-backed vault).","severity":"medium","snippet":"        if (IERC20(vault).totalSupply() == 0) revert VaultEmpty();","title":"InferDripper's empty-vault guard is defeated by a 1-wei deposit: half of every drip into a dust-sized sINFER is captured by virtual shares and stranded forever"},{"citation":"resolved","description":"Merged from audit_permissions, audit_economics and audit_flow (all low; same root cause). The only thing that decides how much of a redemption is paid now versus vested over VEST_PERIOD is paidImmediately[legacy][msg.sender]. Nothing ties addresses to holders and redeem() is permissionless, so a holder with N times the cap moves the legacy tokens into N fresh wallets and redeems the cap from each, receiving everything immediately with nothing vesting. MIYA: cap 5,883,468.55 INFER per wallet (200,000 MIYA at the launch rate), 17 wallets for the whole 100M allocation. MXXN: cap 930,000 INFER per wallet, 43 wallets for 40M. The legacy tokens' own transfer rules only raise the cost: MIYA transfers between plain holders need a listed-NFT holder on one side (the fork test shows the redeemer as receiver already satisfies MIYA's whitelist, and a Milady owner is exactly the kind of holder with 200,000+ MIYA), and MXXN transfers are free in the same full-moon window redemption needs anyway. The 13-week vesting the brief lists as a launch property therefore binds only holders who do not split. No INFER is lost and no allocation is exceeded; the header already says 'a wallet receives at once up to a fixed limit', so this is a guarantee weaker than the brief's wording rather than an implementation slip. A contract-level fix (a global immediate budget per token per period) changes the agreed rule, so it is a scope decision: either document the cap as per-wallet or change the brief. Kept at low. The economics specialist's proof (Proof_32ea6de252a1.t.sol) fails on this code as described; it is not attached because it asserts a global cap that the brief does not authorise.","line":144,"path":"src/LegacyRedeemer.sol","reproduction":"Plain burnable ERC-20 stand-ins for MIYA and MXXN, launch rates (rateA 29417342782992946700, rateB 7851289668371509, allocations 100M/40M), redeemer funded with genesisAllocation() = 140,000,000e18. (a) One wallet holding 2,000,000 MIYA calls redeem(MIYA, 2_000_000e18): out = 58,834,685.57e18; paid at once = immediateCapA = 5,883,468.556e18; owedVesting() = 52,951,217.01e18. (b) The same 2,000,000 MIYA transferred to ten fresh wallets of 200,000e18 each, each calling approve + redeem(MIYA, 200_000e18): every wallet is paid 5,883,468.556e18 immediately, vestingOf(w).total == 0 for all ten, sum of balances = 58,834,685.57e18 and owedVesting() == 0. Expected under the brief's 'large redemptions vest over 13 weeks': 52.95M INFER vesting regardless of how the holder splits it. Actual: nothing vests. Verified by running .imd/reads/proofs/Proof_32ea6de252a1.t.sol on this tree: fails with 'nothing vests when the redemption is split across wallets: 0 <= 0'.","severity":"low","snippet":"        uint256 roomNow = immediateCap(legacy) - paidImmediately[address(legacy)][msg.sender];","title":"LegacyRedeemer's 'large redemptions vest' rule is a per-wallet cap only: splitting a position across fresh wallets redeems any amount at once"},{"citation":"resolved","description":"From audit_permissions; reproduced. _checkRootable (lines 374-380) checks that the season is over, has no root, is not expired and total <= pot, but never looks at ROOT_DEADLINE, so proposeRoot is accepted at any time after the season ends, including the last 48 hours before endOf(s) + ROOT_DEADLINE, where its eta lands after the deadline. expire() for a rootless season (lines 308-313) becomes callable by anyone at that deadline and unconditionally clears the pending proposal at line 312, after which applyProposal reverts NoProposal forever and the whole pot is released to available(). Conversely, after the deadline submitAttestedRoot and applyProposal remain valid until someone calls expire, so whether a late season pays its points holders or returns its pot depends on who lands first in a block. Nothing leaves the vault and the governor can avoid the first case by proposing at least 48 hours before the deadline, so this is a broken guarantee rather than a loss: low. Minimal fix preserving the design: in proposeRoot require block.timestamp + GOVERNOR_DELAY <= endOf(s) + ROOT_DEADLINE, and in expire() refuse the rootless branch while x.pendingEta != 0 (the governor applies or withdraws, then expire works); or make _checkRootable refuse any root once the deadline has passed so the two paths are mutually exclusive.","line":312,"path":"src/SeasonVault.sol","reproduction":"SeasonVault(infer, governor, treasury) funded with 310,000,000e18; governor openSeason(now + 48h, 60_000_000e18, keccak256('bafkreiabc')). Let D = endOf(0) + ROOT_DEADLINE. (1) warp to D - 1 hours; governor proposeRoot(0, keccak256('root'), 50_000_000e18, 'bafkreiabc') -> succeeds, pendingEta = D + 47 hours. (2) warp to D; any address calls expire(0) -> succeeds, emits SeasonExpired(0, 60_000_000e18), available() == 310_000_000e18, pendingEta == 0. (3) warp to D + 47 hours; applyProposal(0) -> reverts NoProposal(0). Expected: step 1 refused (cannot complete before the deadline) or step 2 refused while a proposal is pending. Actual: the proposal is accepted and then destroyed by an unprivileged call. Verified with test/scratch/Leads.t.sol::test_lateProposalWipedByExpire (passes on this code, i.e. the behaviour is reproduced).","severity":"low","snippet":"            (x.pendingRoot, x.pendingTotal, x.pendingEta) = (0, 0, 0);","title":"SeasonVault: ROOT_DEADLINE is enforced only by expire(), so a governor proposal made in the last 48 hours is accepted but wiped by a permissionless expire() before it can be applied, and after the dea"},{"citation":"resolved","description":"From audit_math; reproduced. claim() is documented as 'registers an entitlement with its proof and releases what has vested' and _register is written to be idempotent for a registered account (line 396, `if (e.amount != 0) return;`), but that early return sits behind the deadline guard at line 394. From rootSetAt + REGISTRATION onward, and also as soon as expire(s) has set x.expired, every claim() for a registered account reverts PastDeadline even though the same account's INFER is fully vested (vesting completes at 13 weeks, the deadline is 26 weeks) and release(s, account) pays it. distribute() avoids the problem because it checks entitlements[..].amount == 0 before calling _register; claim does not. No INFER is lost, so low; a frontend or keeper that always calls claim with the stored proof sees every call fail after week 26 while funds sit releasable. Minimal fix: move the `if (e.amount != 0) return;` check above the rootSetAt/deadline checks in _register, or have claim call _release directly when the entitlement already exists.","line":394,"path":"src/SeasonVault.sol","reproduction":"SeasonVault funded with 310,000,000e18; governor opens season 0 with pot 60,000,000e18 starting in 48h. Warp to endOf(0); governor proposeRoot(0, root, 1_000e18, cid) with root = keccak256(bytes.concat(keccak256(abi.encode(0, alice, 1_000e18)))) (single leaf, empty proof); warp +48h; applyProposal(0). claim(0, alice, 1_000e18, []) -> registers, entitlements(0, alice).amount == 1_000e18. Warp to registrationDeadline(0) (= rootSetAt + 26 weeks): releasable(0, alice) == 1_000e18. Expected: claim(0, alice, 1_000e18, []) pays 1_000e18. Actual: reverts PastDeadline(0); release(0, alice) in the same state returns 1_000e18. Verified with test/scratch/Leads.t.sol::test_claimRegisteredAfterDeadlineReverts.","severity":"low","snippet":"        if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);","title":"SeasonVault.claim reverts for an already-registered account once the registration deadline passes or the season is expired, although the entitlement is releasable"},{"citation":"resolved","description":"From audit_economics; reproduced. The README (step 3) makes MXXN redemption depend on a one-off donation of MXXN dust to the redeemer, consumed 1 wei per clock step, after which 'anyone can call advanceClock'. The function takes an arbitrary `steps`, never reads MXXN's clock, and burns 1 wei per iteration whether or not the window is behind; only the first burn after a window ends advances the clock, every further burn in a current window only destroys upkeep balance. Any address can therefore call advanceClock(MXXN, balance) once and leave the redeemer unable to advance the clock for the rest of the year. Redemption is not permanently blocked (an MXXN holder can catch the clock up with burns of their own tokens and re-donate during the next window, as the fork test does), so the impact is operational: lost dust and a defeated permissionless upkeep model. Low. Minimal fix: bound `steps` (e.g. at most one per call, or stop once the clock is current by reading MXXN's state), which keeps the function permissionless.","line":219,"path":"src/LegacyRedeemer.sol","reproduction":"Plain burnable ERC-20 stand-in for MXXN; LegacyRedeemer with the launch terms. 1. Transfer 1,000 wei of MXXN to the redeemer (the README's upkeep donation). 2. Any address calls advanceClock(MXXN, 1000). Expected: the call stops once the clock is current, leaving the donation for later windows. Actual: all 1,000 wei are burned, MXXN.balanceOf(redeemer) == 0, and the next advanceClock(MXXN, 1) reverts with ERC20InsufficientBalance. Verified with test/scratch/Leads.t.sol::test_advanceClockBurnsAllDust.","severity":"low","snippet":"            IBurnable(address(legacy)).burn(1);","title":"LegacyRedeemer.advanceClock burns an unbounded number of dust units, so anyone can destroy the donated MXXN clock-upkeep balance in one call"},{"citation":"resolved","description":"Merged from audit_permissions (info) and audit_flow (info): both sides of the same governor trust. Documented as a trust assumption, not a defect: the brief asks for seasons the governor opens and for no owner. (a) Two governor-only paths move everything the vault holds to treasury without any attested root: never open a season, announceEnd(), wait GOVERNOR_DELAY, then anyone calls endProgram(), which sweeps available() (the full 310,000,000e18); or, for an opened season after it ends, proposeRoot(s, anyRoot, 0, formulaCid) and 48 hours later applyProposal(s) releases the whole pot to available(), then the first path. The proposal path also lets the governor set any root of their choosing (one leaf paying themselves the pot) unless an attested root lands in the 48-hour window. (b) Every path that moves INFER out of SeasonVault to anyone other than a registered claimant needs a prior governor action: openSeason creates the pot a root can pay from; endProgram and sweep require endEta != 0, which only announceEnd sets; there is no time-based ownerless end. FounderStream and LegacyRedeemer do have ownerless terminal paths (release after 365 days, sweep after closesAt), so the three buckets differ in liveness. The governor 0xbefd108085613662356aa26a2466ad3426da9c32 and the treasury 0x6ac8ff96fb8dcf5d79a8ef1e59d9ac23f2bd27bc must be trusted with the full points allocation and with its liveness; points holders have no on-chain recourse. No change proposed; the lock risk is undocumented in README/ADAPTATION and should be stated.","line":321,"path":"src/SeasonVault.sol","reproduction":"Path (a): fund SeasonVault with 310_000_000e18; vm.prank(governor) announceEnd(); warp +48 hours; endProgram() from any address -> infer.balanceOf(treasury) == 310_000_000e18. Path (b): with no governor call ever made, for any time T: openSeason -> NotGovernor from any other caller; endProgram -> NoEndAnnounced; sweep -> NotEnded; expire(0) -> NoSuchSeason; available() == 310_000_000e18 and nothing can move it. Both traced through the code paths quoted above; the first is exercised by the delivered suite's end-of-program tests.","severity":"info","snippet":"    function announceEnd() external onlyGovernor {","title":"Trust assumption: the governor alone decides whether the 310M points allocation is ever paid, and can route all of it to the treasury behind 48 to 96 hours; if the governor key is lost the allocation "},{"citation":"resolved","description":"From audit_permissions (info); verified by tracing. The governor path is delayed 48 hours and can be pre-empted; the attested path (submitAttestedRoot, callable by anyone) is applied in the same transaction and cannot be withdrawn or contested (RootAlreadySet). Its only guard is an EIP-712 signature by the constant attester plus the replay, expiry, chain and panel-size checks in _verifyYes. Whoever controls that key, or obtains one signed YES for a crafted leaf file with total <= pot, sets a season's root to a tree paying arbitrary accounts up to the pot, immediately after the season ends, with no reversal mechanism. The domain separator (chain id and this contract), question-hash binding to season/root/total/CIDs/window, half-order s check, requestId replay guard and the pot bound were traced and hold; the residual risk is the honesty and key security of the attester. This is the agreed design; no change proposed.","line":88,"path":"src/SeasonAttestation.sol","reproduction":"Precondition: a signature from 0x5598Aa9146215Bc13eb26f2c692Ad1461Fd32982 over an OracleAttestation whose questionHash equals keccak256(questionDocument(s, root, total, cid, formulaCid, fromBlock, toBlock)) for a root whose single leaf is (s, attacker, total), total <= pot. After the season ends, submitAttestedRoot(s, root, total, cid, formulaCid, a, sig) -> RootSet(s, root, total, true) in the same block; claim(s, attacker, total, []) after SEASON pays the full total. The governor cannot withdraw or override an attested root. Traced, not exploited: the delivered AttestationFailurePaths tests sign with a test key through the virtual _attester() hook.","severity":"info","snippet":"        return 0x5598Aa9146215Bc13eb26f2c692Ad1461Fd32982;","title":"Trust assumption: a single hard-coded attester key makes any root immediate and final, with no governor veto and no delay"},{"citation":"resolved","description":"From audit_economics (info); reproduced. Not a security defect. With the repository's own foundry.toml ([fmt] line_length = 120), forge fmt --check reports diffs in two source files: src/LegacyRedeemer.sol (line 197, 122 columns) and src/SeasonAttestation.sol (lines 109, 164, 195, 218 and 220). forge build and forge test pass. The fix is forge fmt on the two files; it changes no bytecode.","line":197,"path":"src/LegacyRedeemer.sol","reproduction":"In the repository root run `forge fmt --check`. Expected: no diff. Actual: prints 'Diff in src/SeasonAttestation.sol:' and 'Diff in src/LegacyRedeemer.sol:' and exits non-zero. git status is clean before the run, so this is how the tree arrived.","severity":"info","snippet":"    function quoteFor(address holder, IERC20 legacy, uint256 amount) external view returns (uint256 now_, uint256 later) {","title":"Tree does not pass forge fmt --check: src/LegacyRedeemer.sol and src/SeasonAttestation.sol are unformatted as delivered"}],"hash":"63a216f3a0db0d7bf94ee6e2e923b7da71e4be6ec401bfda0ad5444efaa526ea","nodeId":"d671c591-ce26-45bb-bb71-6d637c961a76","outcome":"completed","summary":"The review is complete. The findings file is written, valid, and the tracked tree is unchanged.\n\n**What I produced**\n\n`.imd-findings.json` in the repository root, with 8 findings and 32 coverage rows (all 30 listed entry points plus two invariants: manifest validity and CREATE2 deployability). No source file was changed. Scratch tests live under `test/scratch/`, which is ignored by git.\n\n**Findings kept, after reproducing each against the code**\n\n- **Medium, InferDripper line 72.** The empty-vault guard checks only `totalSupply() == 0`. A 1-wei deposit mints exactly 1e6 shares against 1e6 virtual shares, so half of every drip is captured by virtual shares and never redeemable. Merged from the flow and economics specialists. The flow specialist's proof is attached: I ran it and it fails on this code, and I confirmed it passes under a temporary guard on `totalAssets()` before restoring the file.\n- **Low, LegacyRedeemer line 144.** The immediate cap is keyed on `msg.sender`, so splitting legacy tokens across wallets avoids all vesting. Three specialists reported this; merged into one. Kept at low because the header documents a per-wallet cap and a fix changes the agreed rule.\n- **Low, SeasonVault line 312.** A governor proposal made in the last 48 hours before the root deadline is accepted, then wiped by a permissionless `expire()`. Reproduced with my own test.\n- **Low, SeasonVault line 394.** `claim()` on an already-registered account reverts after the registration deadline or expiry, while `release()` pays. Reproduced.\n- **Low, LegacyRedeemer line 219.** `advanceClock` burns any number of dust units, so one call destroys the donated MXXN upkeep balance. Reproduced.\n- **Info.** Governor trust assumption (merged from two specialists: full allocation routable to treasury, and permanent lock if the key is lost), the single attester key being immediate and final, and `forge fmt --check` failing on two source files.\n\n**Static analysis leads** were checked and dropped: the ERC-721 interface warning, strict equalities, uninitialized locals, the `encodePacked` collision (CIDs are alphanumeric and separated by quotes), the uint128 casts (bounded by the 140M allocation), and the ecrecover note (half-order check present).\n\n**My own pass** over the entry points found nothing beyond the specialists' leads. I specifically checked `releaseNft` against an arbitrary `collection` argument: calling INFER's `transferFrom` with the redeemer as both sender and spender fails on allowance, so vesting INFER cannot be drained that way.\n\n**Verification**: `forge build` and the 115 local tests pass. The mainnet fork suite was not run here; ADAPTATION.md reports it passing.","treeHash":null,"usage":{"cachedInputTokens":1297685,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":34285,"runtime":"claude","turns":21,"wallClockMs":466408}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"e36579e0223ff908","findings":[{"citation":"resolved","description":"Boundary x invariant seam. `claim(s, account, amount, proof)` is documented as 'registers an entitlement with its proof and releases what has vested', and `_register` is written to be idempotent for an account that is already registered (`if (e.amount != 0) return;`). But that early return sits BEHIND the deadline guard, so from `rootSetAt + REGISTRATION` onward every `claim` for a registered account reverts with `PastDeadline`, even though the same account's INFER is fully vested (vesting runs 13 weeks from the root, the deadline is 26 weeks) and `release(s, account)` pays it. `distribute` avoids the problem because it checks `entitlements[..].amount == 0` before calling `_register`; `claim` does not. No INFER is lost (release remains available) but the guarantee that `claim` works for a registered entitlement breaks at the deadline boundary, and a frontend or keeper that always calls `claim` with the stored proof will see every call fail after week 26 while funds sit releasable. Minimal fix preserving behaviour: in `_register`, move the `if (e.amount != 0) return;` check above the `rootSetAt`/deadline checks (or have `claim` call `_release` directly when the entitlement already exists).","line":394,"path":"src/SeasonVault.sol","reproduction":"Setup: SeasonVault funded with 310,000,000e18 INFER; governor opens season 0 with pot 60,000,000e18. Warp to endOf(0); governor proposeRoot(0, root, 1_000e18, RULES) where root = keccak256(bytes.concat(keccak256(abi.encode(0, alice, 1_000e18)))) (single leaf, empty proof); warp +48h; applyProposal(0). Call claim(0, alice, 1_000e18, []) -> registers (entitlements(0, alice).amount == 1_000e18), releases 0 at the root's second. Warp to registrationDeadline(0) (= rootSetAt + 26 weeks). releasable(0, alice) == 1_000e18. Expected: claim(0, alice, 1_000e18, []) pays 1_000e18 (account is registered, `_register` is meant to fall through to release). Actual: reverts PastDeadline(0). release(0, alice) in the same state returns 1_000e18. Verified with test/scratch/SeasonProbe.t.sol::test_claimOnRegisteredAccountAfterDeadlineReverts (passes, i.e. the revert is reproduced).","severity":"low","snippet":"        if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n        Entitlement storage e = entitlements[s][account];\n        if (e.amount != 0) return; // already registered: release only","title":"SeasonVault.claim reverts for an already-registered account once the registration deadline passes, although the entitlement is releasable"}],"hash":"8e10e00b855eb32095acc1cff0f8ded8ca5fbf4c8b89cdfe1cb9f55ae9e8e711","nodeId":"2e0f7171-9e76-418e-9705-b3d63047cdaa","outcome":"completed","summary":"Review complete. The findings file is written and validated, scratch probes removed, working tree clean.\n\n**Outcome:** the math in my area holds. One low-severity boundary defect was found and reproduced. The file `.imd-findings.json` holds that finding plus a coverage row for all 31 entry points and three checked invariants.\n\n**What I checked (Math Precision, Boundary, Numerical Gap guides):**\n- Every division in value-moving code: the redemption rate, both linear vesting formulas, the dripper's decay step and keeper fee, and the ERC-4626 conversions. All round in the protocol's favour, none can overflow at the supply cap, and none truncates to zero on realistic inputs.\n- Every narrowing cast: the vesting struct's uint128 fields are bounded by the 140M allocation, and all uint64 timestamps are bounded by the one-year notice caps.\n- Every time boundary at exact equality: close, cliff, season end, root delay, registration deadline, and the one-hour catch-up. Each pair of guards is mutually exclusive with no gap second.\n- Conservation invariants under random sequences, with 512-run fuzz probes I wrote and then deleted: LegacyRedeemer's owed vesting equals the sum of unclaimed schedules and the balance always covers it. StakedInfer's locked shares never exceed the balance, and the maximum withdraw always succeeds. The genesis split sums to the full supply on the mainnet fork.\n\n**The finding, low severity:** `SeasonVault.claim` for an already-registered account reverts with PastDeadline once the 26-week registration deadline passes, because the registered-account early return sits behind the deadline guard. The entitlement is fully vested and `release` pays it, so no funds are lost, but the documented claim path fails. The reproduction uses a single-leaf governor root and the exact deadline second.\n\n**Test runs:** the local suite passed with 76 tests. The mainnet fork suite passed with 88 tests and nothing skipped, including the live factory rehearsal and real MIYA and MXXN redemptions.\n\n**Static-analysis leads I checked and dismissed:** all strict-equality flags are zero-sentinels. The unsafe cast is bounded. The packed hash has fixed delimiters and alphanumeric-only CIDs so no collision exists. The dripper \"reentrancy\" is in the constructor.\n\n**Not reached:** the Uniswap v4 pool price derivation from `initialMarketCapWei` lives in the factory, outside this repository, so I did not verify it beyond the rehearsal passing.","treeHash":null,"usage":{"cachedInputTokens":1812387,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":40484,"runtime":"claude","turns":34,"wallClockMs":676818}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"5739ce0d803a43cd","findings":[{"citation":"resolved","description":"Economic Security / Invariant. The contract's stated guarantee (header, 'LARGE REDEMPTIONS VEST') is that INFER above a fixed immediate limit (immediateCapA = 200,000 MIYA worth = 5,883,468 INFER; immediateCapB = 930,000 INFER) vests over a 13-week season, so a big legacy position cannot be dumped as INFER at once. The limit is keyed only on msg.sender (`paidImmediately[legacy][msg.sender]`). There is no per-redemption, per-block or global immediate limit, so a holder who moves legacy tokens into N fresh wallets before redeeming receives N times the cap immediately and nothing vests. Cost: gas for N transfers and N redeems. For MXXN the split only needs the same full-moon window that redemption needs anyway; for MIYA, transfers between plain holders are refused by MIYA's whitelist, so the split needs a wallet that holds a listed NFT (a Milady owner, the kind of holder most likely to hold 200,000+ MIYA) or MIYA's owner. Who loses: everyone relying on the vesting schedule to pace sell pressure on the 140M INFER redemption bucket; the restart-the-season anti-dust rule is also moot since each wallet is used once. This is a design limitation of per-wallet caps rather than an implementation slip; it is reported because the header presents the vesting as a protection and the launch brief lists '(large redemptions vest over 13 weeks)' as a property of the launch.","line":144,"path":"src/LegacyRedeemer.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {LegacyRedeemer} from \"src/LegacyRedeemer.sol\";\n\ncontract PlainLegacy is ERC20 {\n    constructor(string memory n, address to, uint256 amount) ERC20(n, n) {\n        _mint(to, amount);\n    }\n\n    function burn(uint256 value) external {\n        _burn(msg.sender, value);\n    }\n}\n\n/// @notice LegacyRedeemer: the \"large redemptions vest\" rule is per wallet only. The same 2,000,000 MIYA that\n/// vests 52.95M of its 58.83M INFER when redeemed from one wallet is paid out in full, at once, when the holder\n/// spreads it over ten wallets of 200,000 MIYA (each exactly at IMMEDIATE_A). Fails on the current code because\n/// nothing vests; passes once redemptions above the immediate cap vest regardless of how they are split.\ncontract ProofCapBypassTest is Test {\n    uint256 constant RATE_A = 29417342782992946700;\n    uint256 constant RATE_B = 7851289668371509;\n\n    function test_splittingAcrossWalletsMustStillVest() public {\n        vm.warp(1_700_000_000);\n        PlainLegacy infer = new PlainLegacy(\"INFER\", address(this), 1_000_000_000e18);\n        PlainLegacy miya = new PlainLegacy(\"MIYA\", address(this), 2_000_000e18);\n        PlainLegacy mxxn = new PlainLegacy(\"MXXN\", address(this), 1e18);\n        LegacyRedeemer r = new LegacyRedeemer(\n            address(infer),\n            address(miya),\n            100_000_000e18,\n            RATE_A,\n            address(mxxn),\n            40_000_000e18,\n            RATE_B,\n            makeAddr(\"treasury\"),\n            makeAddr(\"founder\")\n        );\n        infer.transfer(address(r), r.genesisAllocation());\n\n        uint256 paidNow;\n        for (uint256 i; i < 10; ++i) {\n            address w = makeAddr(string.concat(\"wallet\", vm.toString(i)));\n            miya.transfer(w, 200_000e18);\n            vm.startPrank(w);\n            miya.approve(address(r), type(uint256).max);\n            r.redeem(miya, 200_000e18);\n            vm.stopPrank();\n            paidNow += infer.balanceOf(w);\n        }\n        uint256 total = 2_000_000e18 * RATE_A / 1e18; // 58,834,685 INFER for 2,000,000 MIYA\n        assertEq(paidNow + r.owedVesting(), total, \"everything earned is either paid or vesting\");\n        // 2,000,000 MIYA is ten times the immediate limit: the bulk of it must vest, as it does from one wallet.\n        assertGt(r.owedVesting(), 0, \"nothing vests when the redemption is split across wallets\");\n        assertLe(paidNow, r.immediateCapA(), \"more than one wallet's immediate cap was paid at once\");\n    }\n}","reproduction":"Plain ERC20 stand-ins with burn(), rateA = 29417342782992946700, allocations as launched, redeemer funded with genesisAllocation(). (a) One wallet holding 2,000,000 MIYA calls redeem(MIYA, 2_000_000e18): out = 58,834,685 INFER; balance paid at once = 5,883,468 INFER (immediateCapA); owedVesting = 52,951,217 INFER. (b) The same 2,000,000 MIYA transferred to ten fresh wallets of 200,000e18 each, each calling redeem(MIYA, 200_000e18): the ten wallets together hold 58,834,685 INFER immediately and owedVesting == 0. Expected per the contract's own description: the amount above one immediate cap (52.95M INFER) vests over 91 days regardless of how the holder splits it. Actual: nothing vests. Scratch test test/scratch/EconLeads.t.sol::test_capBypassBySplitting prints both outcomes; the attached proof asserts the intended behaviour and fails on this code.","severity":"low","snippet":"        uint256 roomNow = immediateCap(legacy) - paidImmediately[address(legacy)][msg.sender];","title":"LegacyRedeemer: the 'large redemptions vest' rule is per wallet and is bypassed by splitting a redemption across wallets"},{"citation":"resolved","description":"Flow Gap (execution x periphery x first principles). advanceClock exists so that 'the window keeps up with the chain' and the README (step 3) makes the launch depend on a one-off donation of MXXN dust to the redeemer, consumed 1 wei per step. The function takes an arbitrary `steps`, checks nothing about MXXN's moon clock (it never reads moonConfig()), and burns 1 wei per iteration whether or not the clock is behind. Only the first burn after a window ends advances MXXN's clock (the fork test test_mxxn_redeemsAtTheFullMoon shows one burn moves the window exactly one cycle and further burns in a current window do nothing); every extra burn is pure destruction of the upkeep balance. Any address can therefore call advanceClock(MXXN, balance) and leave the redeemer unable to advance the clock for the rest of the year, which is the only permissionless path to a new redemption window. Redemption is not permanently blocked, because an MXXN holder can still catch the clock up with burns of their own tokens (as the fork test does) and re-donate during the next window, but the documented 'thereafter anyone can call advanceClock' operating model is defeated at a cost of one transaction, and the same holds for any legacy MIYA dust that is sent to the contract. Impact is operational (lost dust, lost permissionless upkeep), hence low.","line":218,"path":"src/LegacyRedeemer.sol","reproduction":"Plain ERC20 stand-in for MXXN with burn(). 1. Holder transfers 1,000 wei of MXXN to the redeemer (the README's upkeep donation). 2. Any address calls advanceClock(MXXN, 1000). Expected: the call stops once the clock is current (at most one effective step per expired window), leaving the donation for later windows. Actual: all 1,000 wei are burned in one call, MXXN.balanceOf(redeemer) == 0, and the next advanceClock(MXXN, 1) reverts (ERC20InsufficientBalance), so the redeemer can no longer bring the window forward until someone donates again during a full moon. Scratch test test/scratch/EconLeads.t.sol::test_advanceClockBurnsAllDust.","severity":"low","snippet":"        for (uint256 i; i < steps; ++i) {\n            IBurnable(address(legacy)).burn(1);\n        }","title":"LegacyRedeemer.advanceClock burns an unbounded number of dust units, so anyone can destroy the donated MXXN clock-upkeep balance in one call"},{"citation":"resolved","description":"Economic Security / Invariant (conservation: INFER dripped == INFER redeemable by shareholders). The dripper refuses to feed an empty sINFER precisely because 'INFER landing in an empty ERC-4626 is captured by its virtual shares'. StakedInfer uses a decimals offset of 6, so the vault behaves as if a phantom holder owned 10^6 shares and 1 wei of assets. The guard only tests totalSupply() != 0. Depositing 1 wei of INFER mints exactly 10^6 real shares, so the vault is now half real, half phantom; every subsequent drip is split 50/50 between the 1-wei depositor and the virtual shares. The virtual share's portion is not recoverable by later depositors (ERC-4626 prices their deposits at the current rate, so the phantom's absolute holding is carried forward unchanged) and remains in the vault forever once every real share is redeemed. The depositor who put in 1 wei also collects the other half of each drip as the sole 'staker'. Preconditions: the Treasury has funded the dripper before any real staker exists (the launch ships sINFER with zero genesis deposit, so this is the state right after launch); any real deposit of 1 INFER (10^24 shares) makes the phantom fraction negligible from then on, but what was stranded stays stranded. Loss is bounded by the drips that occur before a real staker arrives (up to ~495 INFER per hourly floor drip from a small buffer, more from a large one), hence low; the fix is a stronger emptiness test (for example require totalSupply() >= 10 ** _decimalsOffset() * 1e18 or totalAssets() above a floor) that keeps the guard's intent.","line":72,"path":"src/InferDripper.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {InferToken} from \"src/InferToken.sol\";\nimport {StakedInfer} from \"src/StakedInfer.sol\";\nimport {InferDripper} from \"src/InferDripper.sol\";\n\n/// @notice InferDripper refuses to drip into an empty sINFER so the virtual shares cannot capture the buyback.\n/// A 1-wei deposit (1e6 shares, equal to the virtual share count) satisfies `totalSupply() != 0`, after which\n/// every drip is split half to the 1-wei depositor and half to the virtual shares, which nobody can ever redeem.\n/// Fails on the current code: the drip goes through and 495 INFER of the first 990 are stranded. Passes once the\n/// dripper refuses while the real share supply is still negligible against the virtual offset.\ncontract ProofDripperDustTest is Test {\n    function test_dustDepositMustNotUnlockDripping() public {\n        InferToken infer = new InferToken();\n        StakedInfer vault = new StakedInfer(infer);\n        InferDripper dripper = new InferDripper(address(infer), address(vault), 1_000e18);\n        infer.transfer(address(dripper), 100_000e18); // the Treasury's first buyback, before anyone has staked\n\n        address griefer = makeAddr(\"griefer\");\n        infer.transfer(griefer, 1);\n        vm.startPrank(griefer);\n        infer.approve(address(vault), 1);\n        uint256 shares = vault.deposit(1, griefer);\n        vm.stopPrank();\n        assertEq(shares, 1e6, \"1 wei of INFER mints as many shares as the virtual offset\");\n\n        vm.warp(block.timestamp + 1 hours);\n        uint256 before = infer.balanceOf(address(dripper));\n        (bool ok,) = address(dripper).call(abi.encodeWithSelector(InferDripper.drip.selector));\n        if (ok) {\n            vm.roll(block.number + 1);\n            uint256 dripped = before - infer.balanceOf(address(dripper));\n            uint256 toVault = dripped - dripped / 100;\n            uint256 redeemable = vault.previewRedeem(vault.totalSupply());\n            uint256 stranded = toVault + 1 - redeemable;\n            emit log_named_uint(\"to vault\", toVault);\n            emit log_named_uint(\"redeemable by every real share\", redeemable);\n            emit log_named_uint(\"stranded in the virtual shares\", stranded);\n            assertLt(stranded, 1e18, \"a drip into an effectively empty vault strands the buyback\");\n        }\n        assertFalse(ok, \"the dripper must still treat a 1-wei vault as empty\");\n    }\n}","reproduction":"InferToken, StakedInfer(token), InferDripper(token, vault, 1000e18). 1. Treasury sends 100,000 INFER to the dripper; drip() reverts VaultEmpty as intended. 2. Griefer deposits 1 wei of INFER: vault.deposit(1, griefer) mints 1,000,000 shares (== 10^6 virtual shares). 3. warp +1 hour; drip() succeeds: 990 INFER to the vault, 10 INFER to the keeper. 4. Next block: previewRedeem(totalSupply()) == 495.000000000000000001 INFER while totalAssets() == 990.000000000000000001 INFER, so 495 INFER are owned by nobody. 5. A real staker deposits 1,000,000 INFER and redeems everything next block: receives 999,999.9999999 INFER (its own deposit, not the stranded part); the griefer redeems its 10^6 shares for ~495 INFER; with totalSupply() == 0 the vault still holds 495.00000005 INFER that no share can ever claim. Expected: the dripper keeps refusing while the vault is effectively empty so the Treasury's buyback is not stranded. Scratch test test/scratch/EconLeads.t.sol::test_dustDepositUnlocksDripAndStrandsHalf; the attached proof asserts the guard and fails on this code.","severity":"low","snippet":"        if (IERC20(vault).totalSupply() == 0) revert VaultEmpty();","title":"InferDripper's empty-vault guard is defeated by a 1-wei deposit: half of every drip is then captured by sINFER's virtual shares and can never be redeemed"},{"citation":"resolved","description":"Not a security defect; a submission-hygiene gap against the stated rule that the tree must pass plain `forge build`, `forge test` and `forge fmt --check` at all times. With the repository's own foundry.toml ([fmt] line_length = 120) and forge 1.7.1, `forge fmt --check` exits 1 on two unchanged source files: src/LegacyRedeemer.sol (line 197, the quoteFor signature, 122 columns) and src/SeasonAttestation.sol (lines 109, 164, 195, 218 and 220: the _head signature, the BadWindow condition, the second abi.encode in _attestationHash, and the two single-line for loops in _decimal). `forge build` and `forge test` pass. The working tree is clean (git status reports nothing), so this is how the accepted tree arrived, not something introduced by this review. The fix is `forge fmt` on the two files; it changes no bytecode.","line":197,"path":"src/LegacyRedeemer.sol","reproduction":"In the repository root run `forge fmt --check`. Expected: exit 0 with no diff. Actual: exit 1 printing 'Diff in src/SeasonAttestation.sol:' and 'Diff in src/LegacyRedeemer.sol:' with the lines above reflowed. `git status --short` is empty before and after, confirming the source is as committed.","severity":"info","snippet":"    function quoteFor(address holder, IERC20 legacy, uint256 amount) external view returns (uint256 now_, uint256 later) {","title":"Tree does not pass `forge fmt --check`: src/LegacyRedeemer.sol and src/SeasonAttestation.sol are unformatted as delivered"}],"hash":"95360173c6572a2c317cf246165e411c437ed80cbeb3bd3e4ef135eebe2fbc55","nodeId":"dab522eb-ef8f-44b6-b9ee-208b8ad140b6","outcome":"completed","summary":"The review is complete. The findings file is on disk and every listed entry point has a coverage row.\n\n**Outcome.** The economics of the five application contracts hold up under the Economic Security, Invariant and Flow Gap checks. I found no loss-of-funds or misdirected-payment defect. Four findings are recorded in `.imd-findings.json`, three low and one info, each with a concrete reproduction. The scratch tests in `test/scratch/` demonstrate all of them.\n\n**Findings**\n\n1. **Per-wallet vesting cap is sybil-bypassable** in `LegacyRedeemer.redeem`. One wallet redeeming 2,000,000 MIYA gets 5.88M INFER at once and 52.95M vesting. The same MIYA spread over ten wallets of 200,000 is paid in full immediately and nothing vests. Low, because it is a design limitation and MIYA's whitelist restricts who can split. Proof test attached and failing on current code.\n2. **`advanceClock` burns unbounded dust.** Anyone can call it with `steps` equal to the donated MXXN balance and destroy the upkeep donation the README depends on, in one transaction. Redemption is not permanently blocked, since holders can catch the clock up with their own burns. Low.\n3. **Dripper's empty-vault guard is defeated by a 1-wei deposit.** One wei mints exactly the virtual share count, so half of each subsequent drip is captured by sINFER's virtual shares and is never redeemable. In the reproduction, 495 of the first 990 INFER dripped stay stranded even after a 1M INFER staker passes through. Low, bounded by drips before a real staker arrives. Proof test attached and failing on current code.\n4. **`forge fmt --check` fails on the tree as delivered** for `src/LegacyRedeemer.sol` and `src/SeasonAttestation.sol`. Build and tests pass. Info, flagged because the task rules require the check to pass.\n\n**Coverage.** All 30 listed entry points have rows: 3 marked `finding`, the rest `holds` with reasons. I also recorded the invariants I checked: SeasonVault balance ≥ reserved, LegacyRedeemer owedVesting conservation, StakedInfer locked ≤ balance, and the 1B supply split across swarm, pool, buckets and treasury. I did not re-run the mainnet fork suites. MIYA and MXXN behaviour is taken from the existing fork tests and the adaptation record rather than from fresh chain reads.\n\n**Notable non-findings.** The SeasonVault reserve accounting, the attestation binding, the one-block hold, ERC-4626 max-value consistency and the FounderStream schedule all traced cleanly. The governor's 48-hour root fallback is a documented trust assumption, not reported as a defect. No files outside `.imd-findings.json` and `test/scratch/` were created or changed, and `git status` is clean.","treeHash":null,"usage":{"cachedInputTokens":2546120,"inputTokens":578,"model":"claude-fable-5-1","outputTokens":52765,"runtime":"claude","turns":41,"wallClockMs":786160}},{"artifacts":[],"attempt":1,"bundleHash":"14c7fe99ffbef33676190d36b9900e989d7ab3e5d4af3805b55b2c35c9373578","device":"fef70a876212b4f0","findings":[],"hash":"9b4271205a0d13943ca9fd170a55403325ea4b590c1e53abcef2c430191ebbe7","nodeId":"959f6049-1608-4886-a817-b0030705e7aa","outcome":"completed","summary":"Created [launch.json](/home/imd/.identitymd/work/b0d8cce2-431c-45ab-8eec-c125b83b4548/959f6049-1608-4886-a817-b0030705e7aa/launch.json) with the exact token, constructor arguments, pool settings, and economics. Only the manifest is delivered.\n\nValidation passed:\n\n- Manifest fields and compiled constructor ABIs match.\n- `forge build` passed with existing warnings.\n- `forge test`: 76 passed, 2 fork-only skips.\n- Mainnet fork: all 80 passed, including live factory deployment and splitter payouts.","treeHash":"a8ee022f82c46e72d84487293944c2bf2f54058e","usage":{"cachedInputTokens":400128,"inputTokens":64141,"model":"gpt-6-astra","outputTokens":4687,"runtime":"codex","turns":4,"wallClockMs":122215}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"0cf632e317dfab7a","findings":[],"hash":"b19244a3444254ba27e56f3d2b95f18530640bef78cfc52fd497c366093e7bc6","nodeId":"d671c591-ce26-45bb-bb71-6d637c961a76","outcome":"completed","summary":"Review complete. Every finding from my previous round is settled, and I found nothing new to report.\n\n**Result:** `.imd-findings.json` holds an empty findings list and a 33-row coverage record (all 30 entry points as `holds`, plus three invariant rows).\n\n**What I verified, by running it against the revised code:**\n\n- **Dripper dust vault (medium, fixed).** The attached proof `Proof_e31963921ec5.t.sol` now passes both tests. My own scratch run confirms a 1-wei deposit is refused with `VaultEmpty`, a 1,000-wei deposit (exactly 1e9 shares) is accepted, and the virtual shares capture 0.0999% of the drip instead of 50%.\n- **Root-deadline race (low, fixed).** Proposals at D-1h and D-48h now revert `PastDeadline`; the last timely one (D-48h-1s) applies at D-1s; at D, `applyProposal` and `submitAttestedRoot` revert while `expire` succeeds. The two paths are mutually exclusive.\n- **Claim after registration deadline (low, fixed).** A registered account's `claim` pays the full 1,000e18 at the deadline and after `expire`, a repeat with a bogus amount pays 0, and a new account is still refused.\n- **advanceClock dust depletion (low, fixed).** With a clock-aware stand-in, a 1,000-step call burns nothing when the window is current and exactly one wei per stale cycle. The mainnet fork test against the real MXXN contract confirms 999 wei survive an oversized call.\n- **Per-wallet cap, governor trust, attester trust (documentation).** README now states all three explicitly; no code change was warranted, as I said last round.\n- **Formatting (info, fixed).** `forge fmt --check` exits 0.\n\n**Suite results:** local `forge test` 138 passed, 0 failed, 2 fork-only skips. `forge test --fork-url` on mainnet: 142 passed, 0 failed, 0 skipped, including the live factory rehearsal.\n\n**Manifest:** schema-only keys, lowercase addresses, string uints, five unique contract names, notes at 2,435 characters, economics copied verbatim. The only change this round was explanatory text in notes.\n\nStatic-analysis leads were all checked and none reproduced as a defect. Scratch tests were removed after use; no tracked file was modified.","treeHash":null,"usage":{"cachedInputTokens":923573,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":18577,"runtime":"claude","turns":27,"wallClockMs":352145}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"93ca4a1020037bf1","findings":[{"citation":"resolved","description":"_setRoot checks only that the declared `total` is <= the pot and releases `pot - total` from `reserved`. _register never checks `x.registered + amount <= x.total`, so the leaves of a root can sum to more than its declared total (and more than the pot). Each _release then subtracts from the global `reserved`, i.e. it pays out INFER that is reserved for another season's pot or for other seasons' registered entitlements; those later claimants revert with an underflow or find the balance gone. The season's own expire() computes `x.total - x.registered` (line 303) and reverts with a panic for good. The contract states that 'The root's total may not exceed the season's pot' and 'The governor's pot is the cap'. The governor's fallback root (proposeRoot/applyProposal) is the path where nobody checks the leaf sum, so the cap does not hold against a governor mistake or a malicious governor, and a mistaken panel YES has the same effect. Fix: in _register, `if (x.registered + amount > x.total) revert ...`.","line":396,"path":"src/SeasonVault.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {SeasonVault} from \"src/SeasonVault.sol\";\n\ncontract Tok is ERC20 {\n    constructor(address to) ERC20(\"INFER\", \"INFER\") {\n        _mint(to, 1_000_000_000e18);\n    }\n}\n\n/// A root's `total` is never compared with what its leaves register, so a season pays out more than its pot,\n/// out of INFER reserved for another season, and its own `expire` then underflows for good.\ncontract SeasonOverPotTest is Test {\n    Tok infer;\n    SeasonVault seasons;\n    address constant GOVERNOR = address(0x60F);\n    address constant TREASURY = address(0x7EA);\n    address alice = address(0xA11CE);\n    string constant RULES = \"bafkreihdwdcefgh4dqkjv67uzcmw7ojee6xedzdetojuzjevtenxquvyku\";\n\n    function setUp() public {\n        infer = new Tok(address(this));\n        seasons = new SeasonVault(address(infer), GOVERNOR, TREASURY);\n        infer.transfer(address(seasons), 310_000_000e18);\n    }\n\n    function test_registrationsCanExceedTheRootTotalAndThePot() public {\n        // Season 0: pot 100M. Season 1 opened right after with 210M reserved for its own claimants.\n        vm.prank(GOVERNOR);\n        seasons.openSeason(uint64(block.timestamp + 48 hours), 100_000_000e18, keccak256(bytes(RULES)));\n        uint256 end0 = seasons.endOf(0);\n        vm.warp(end0 - 48 hours);\n        vm.prank(GOVERNOR);\n        seasons.openSeason(uint64(end0), 210_000_000e18, keccak256(bytes(RULES)));\n        assertEq(seasons.reserved(), 310_000_000e18);\n\n        // Season 0's root: one leaf, alice 150M, declared total 1 INFER (<= pot, so accepted).\n        vm.warp(seasons.endOf(0));\n        bytes32 root = keccak256(bytes.concat(keccak256(abi.encode(uint256(0), alice, uint256(150_000_000e18)))));\n        vm.prank(GOVERNOR);\n        seasons.proposeRoot(0, root, 1e18, RULES);\n        vm.warp(block.timestamp + 48 hours);\n        seasons.applyProposal(0);\n\n        // Registering more than the root's total must be refused.\n        vm.warp(block.timestamp + 13 weeks);\n        bytes32[] memory proof = new bytes32[](0);\n        vm.expectRevert();\n        seasons.claim(0, alice, 150_000_000e18, proof);\n    }\n}","reproduction":"Vault funded with 310M INFER. Governor opens season 0 (pot 100M) and, after it ends, season 1 (pot 210M); reserved = 310M. At endOf(0) the governor calls proposeRoot(0, root, 1e18, RULES), where root is the single leaf keccak256(keccak256(abi.encode(0, alice, 150_000_000e18))). After 48h anyone calls applyProposal(0); reserved drops to 210M+1. 13 weeks later, claim(0, alice, 150_000_000e18, []) succeeds and pays alice 150M INFER. Expected: refused (registration above total 1e18 / above pot 100M). Actual: paid, and 149,999,999 INFER of season 1's 210M reserved pot is gone. expire(0) after the registration deadline reverts with an arithmetic underflow.","severity":"medium","snippet":"        x.registered += amount;","title":"SeasonVault: registrations are not bounded by the root's total, so a season can pay more than its pot out of other seasons' reserves, and its expire() then underflows forever"},{"citation":"resolved","description":"_update stamps lastDepositBlock[to] = block.number on every mint, whoever pays for it. ERC4626.deposit(assets, receiver) lets any caller mint to any receiver, so a stranger can stamp a victim who has held for any number of blocks. maxRedeem/maxWithdraw then return 0 for the victim and _withdraw reverts SameBlockRedeem for the rest of that block. Mallory can front-run or bundle this before the victim's redeem/withdraw in every block, for 1 wei plus gas each time, so they can delay the victim's exit, e.g. while a price moves or to break a liquidation or route that redeems atomically. The contract states the opposite ('nobody can block someone else's exit by sending them dust'). The existing test test_theHoldTravelsWithSharesAndCannotBeUsedToGrief deposits for 0xD00D, not for the victim, so it misses this path. Fix: stamp only when the minter is the receiver (track caller in deposit/mint), or hold only the newly minted shares rather than the whole balance.","line":42,"path":"src/StakedInfer.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {StakedInfer} from \"src/StakedInfer.sol\";\n\ncontract Tok is ERC20 {\n    constructor(address to) ERC20(\"INFER\", \"INFER\") {\n        _mint(to, 1_000_000_000e18);\n    }\n}\n\n/// A stranger's 1-wei deposit naming the victim as receiver stamps the victim's hold and blocks their exit\n/// in that block. Fails today; passes once a third party's mint can no longer set the receiver's hold.\ncontract HoldGriefTest is Test {\n    Tok infer;\n    StakedInfer sinfer;\n    address alice = address(0xA11CE);\n    address mallory = address(0xBAD);\n\n    function setUp() public {\n        infer = new Tok(address(this));\n        sinfer = new StakedInfer(IERC20(address(infer)));\n        infer.transfer(alice, 1_000e18);\n        infer.transfer(mallory, 1e18);\n        vm.prank(alice);\n        infer.approve(address(sinfer), type(uint256).max);\n        vm.prank(mallory);\n        infer.approve(address(sinfer), type(uint256).max);\n    }\n\n    function test_strangersDustDepositBlocksVictimsExit() public {\n        vm.prank(alice);\n        uint256 shares = sinfer.deposit(1_000e18, alice);\n        vm.roll(block.number + 10); // alice has held for many blocks\n\n        // Mallory front-runs alice's redeem in the same block with a 1-wei deposit for alice.\n        vm.prank(mallory);\n        sinfer.deposit(1, alice);\n\n        vm.prank(alice);\n        sinfer.redeem(shares, alice, alice);\n    }\n}","reproduction":"alice deposits 1,000 INFER and holds for 10 blocks. In block N mallory calls sinfer.deposit(1, alice); alice's sinfer.redeem(shares, alice, alice) in block N reverts ERC4626ExceededMaxRedeem(alice, 1e27, 0). Expected: alice's long-held shares stay redeemable. Actual: her exit is refused for the block, repeatably.","severity":"medium","snippet":"                lastDepositBlock[to] = block.number;","title":"StakedInfer: anyone can block a holder's redemption in a block by depositing 1 wei with the holder as receiver"},{"citation":"resolved","description":"releasable() = balance * min(elapsed, 1 hour) / 7 days, which is at most balance/168. With the launch's constructor word minDrip = 1000e18, drip() reverts BelowMinDrip whenever the balance is below 168,000 INFER, however long anyone waits, because elapsed is capped at MAX_CATCHUP. The contract has no other exit. So (a) a Treasury buyback smaller than 168k INFER never reaches sINFER, and (b) after any buyback the buffer decays toward 168k and then stops: up to ~168k INFER stays locked for good once buybacks stop. Fix (keeps the constructor word): when releasable() < minDrip, release min(balance, minDrip) once at least MAX_CATCHUP has elapsed, or drain the whole balance when it is below minDrip * DRAIN_PERIOD / MAX_CATCHUP.","line":63,"path":"src/InferDripper.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {ERC20} from \"@openzeppelin/contracts/token/ERC20/ERC20.sol\";\nimport {IERC20} from \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\nimport {StakedInfer} from \"src/StakedInfer.sol\";\nimport {InferDripper} from \"src/InferDripper.sol\";\n\ncontract Tok is ERC20 {\n    constructor(address to) ERC20(\"INFER\", \"INFER\") {\n        _mint(to, 1_000_000_000e18);\n    }\n}\n\n/// With the launch's minDrip of 1,000 INFER, any buffer below 168,000 INFER can never be released:\n/// releasable() is at most balance * 1h / 7d = balance / 168. There is no other way out of the dripper.\ncontract DripperFloorTest is Test {\n    Tok infer;\n    StakedInfer sinfer;\n    InferDripper dripper;\n    address staker = address(0x5);\n\n    function setUp() public {\n        infer = new Tok(address(this));\n        sinfer = new StakedInfer(IERC20(address(infer)));\n        dripper = new InferDripper(address(infer), address(sinfer), 1_000e18); // the launch's constructor word\n        infer.transfer(staker, 10_000e18);\n        vm.startPrank(staker);\n        infer.approve(address(sinfer), type(uint256).max);\n        sinfer.deposit(10_000e18, staker);\n        vm.stopPrank();\n    }\n\n    function test_aBufferBelow168kInferIsNeverReleased() public {\n        infer.transfer(address(dripper), 100_000e18); // the Treasury's buyback\n        for (uint256 i; i < 24 * 365; ++i) {\n            vm.warp(block.timestamp + 1 hours);\n            vm.roll(block.number + 300);\n            if (dripper.canDrip()) dripper.drip();\n        }\n        // A year of hourly keepers later, nothing has reached stakers.\n        assertLt(infer.balanceOf(address(dripper)), 100_000e18, \"the buyback should reach sINFER\");\n    }\n}","reproduction":"Deploy InferDripper(infer, sINFER, 1000e18) as in the launch, with a staker in sINFER. The Treasury sends 100,000 INFER to the dripper. Call drip() every hour for a year: canDrip() is always false and drip() reverts BelowMinDrip(595.2e18, 1000e18). Expected: the buyback streams into sINFER over about a week. Actual: all 100,000 INFER is still in the dripper after a year.","severity":"medium","snippet":"        if (amount < minDrip || amount == 0) revert BelowMinDrip(amount, minDrip);","title":"InferDripper: any buffer under 168,000 INFER can never be released, and there is no other way out, so INFER bought for stakers is stranded"},{"citation":"resolved","description":"Only a lower bound is checked. Once a season is opened its pot is reserved and can only be released by a root (after start + SEASON) or by expire (after start + SEASON + ROOT_DEADLINE). endProgram/sweep only move available(), which excludes reserved pots, and a season cannot be cancelled. A start given in milliseconds, or any far-future value, keeps the pot out of reach forever and also blocks every later season, since they must not overlap it. Fix: bound startsAt, e.g. startsAt <= block.timestamp + 365 days.","line":151,"path":"src/SeasonVault.sol","reproduction":"Governor calls openSeason(1_760_000_000_000 /* ms, not s */, 310_000_000e18, f). reserved = 310M and available() = 0. expire(0) reverts NotYet(~1.76e12 + 39 weeks) for ~55,000 years, announceEnd + endProgram sweeps 0, and openSeason for season 1 reverts Overlaps. Expected: the call is refused. Actual: the whole points allocation is locked.","severity":"low","snippet":"        if (startsAt < block.timestamp + NOTICE) revert TooSoon(block.timestamp + NOTICE);","title":"SeasonVault: openSeason has no upper bound on startsAt, so a mis-scaled start locks the pot permanently"},{"citation":"resolved","description":"As documented, a MIYA transfer into the redeemer reverts (MIYA's whitelist reaches Bonkler, which reverts) until someone transfers a listed NFT, e.g. Milady #1, to the redeemer. advanceClock can only burn MXXN that someone has donated, because redeem burns exactly what it receives. Neither the constructor nor the launch does either, so the 100M INFER MIYA allocation cannot be redeemed until an operator acts after deployment, and if nobody does it is swept to the treasury after a year. This is a configuration step after deployment. Record it in the launch plan and ADAPTATION.md rather than leave it implicit.","line":230,"path":"src/LegacyRedeemer.sol","reproduction":"test/LegacyRedeemer.t.sol test_miya_aMiladyInTheRedeemerOpensRedemption (mainnet fork): with no NFT held by the redeemer, a holder's redeem(MIYA, 1_000e18) reverts; it succeeds only after the Milady owner transfers token #1 to the redeemer.","severity":"info","snippet":"    function releaseNft(address collection, uint256 tokenId) external {","title":"LegacyRedeemer: MIYA and MXXN redemption depend on steps after deployment (an NFT deposit and MXXN dust) that nothing in the launch performs"}],"hash":"b76694072b6937116f899c4f5bff76c0f62b4f5dd1ab620d090e6d696034b541","nodeId":"1b609ac1-6327-4ca2-a59d-50d5d4f46b6a","outcome":"completed","summary":"I found three medium-severity defects, one low and one informational, and wrote them to `.imd-findings.json`. Each medium finding comes with a Foundry proof that fails on the current code for the reason given. No critical or high issues turned up. I changed no repository files: the scratch tests are deleted and `git status` is clean.\n\n**Findings**\n1. **Medium: `SeasonVault` can pay out more than a season's pot** (`src/SeasonVault.sol:396`). Nothing checks that a season's claims stay within the total declared with its Merkle root (the list of who gets what). In the proof the governor's fallback path sets a root with one leaf of 150M INFER for alice but declares a total of 1 INFER, which passes the pot check. Alice's claim succeeds and takes 150M INFER, of which 149,999,999 had been set aside for season 1. After that, `expire(0)` fails permanently with an underflow. The fix is to refuse a claim that would push the season past its declared total.\n2. **Medium: `StakedInfer` lets anyone block someone else's withdrawal for a block** (`src/StakedInfer.sol:42`). If Mallory calls `deposit(1, alice)` in the same block as alice's `redeem`, the redeem fails with `ERC4626ExceededMaxRedeem`. It costs 1 wei plus gas and can be repeated every block. This breaks the contract's stated guarantee that nobody can block another holder's exit. The existing test only deposits to a third address, so it misses this case.\n3. **Medium: `InferDripper` can never release a balance under 168,000 INFER** (`src/InferDripper.sol:63`). With the launch's `minDrip` of 1,000 INFER, one call releases at most 1/168 of the balance, so anything below 168k never meets the minimum and the contract has no other way to move it. In the proof, a 100,000 INFER buyback is still untouched after a year of hourly drip attempts.\n4. **Low: `SeasonVault.openSeason` has no upper limit on the start time** (`src/SeasonVault.sol:151`). If the governor passes a start time in milliseconds instead of seconds, the whole 310M points allocation is locked for good, and every later season is blocked too.\n5. **Info: MIYA and MXXN redemptions need manual steps after deployment.** Someone must send a Milady NFT to `LegacyRedeemer` before MIYA can be redeemed, and someone must donate MXXN dust before `advanceClock` can work. Nothing in the launch does either. If nobody sends the NFT, the 100M INFER MIYA allocation goes to the treasury after a year.\n\n**Coverage**\n- **Read:** all seven contracts in `src/` (InferToken, LegacyRedeemer, SeasonVault with SeasonAttestation, StakedInfer, FounderStream, InferDripper), the GenesisSplitter in `lib/genesis-splitter`, and the existing tests.\n- **No launch-rule problems:** InferToken matches the brief (fixed supply minted once to the factory, no owner, no fee). Nothing uses DELEGATECALL, CALLCODE, SELFDESTRUCT, proxies or initializers.\n- **Allocations add up:** the splitter pays 140M + 310M + 70M = 520M of the 700M remainder.\n- **Not reached:** there is no mainnet RPC on this machine, so I couldn't run the two fork tests (the live-factory launch and MIYA/MXXN behaviour). I didn't check MIYA's and MXXN's on-chain source myself, including whether either charges a fee on transfer. `launch.json` doesn't exist yet, so there was no manifest to check.\n\n`forge test` passes: 54 tests pass and the 2 fork tests are skipped.","treeHash":null,"usage":{"cachedInputTokens":1042943,"inputTokens":30,"model":"claude-opus-5-5","outputTokens":19865,"runtime":"claude","turns":15,"wallClockMs":231159}},{"artifacts":[],"attempt":1,"bundleHash":"d307cc216c6d5ed64e9493e21323b7d826759d506a59a22ce83fb3a60331d7cd","device":"b29f0968f2df07a0","findings":[],"hash":"bbc3cab5cef255159af30fffb45af24afdf79415917cb035a36f7334cc6e43f0","nodeId":"959f6049-1608-4886-a817-b0030705e7aa","outcome":"completed","summary":"Updated only [launch.json](/home/seat/.identitymd/work/b0d8cce2-431c-45ab-8eec-c125b83b4548/959f6049-1608-4886-a817-b0030705e7aa/launch.json), revising notes to match the accepted implementation. Verified exact launch terms and constructor ABIs.\n\nChecks passed:\n\n- `forge build`\n- `forge test`: 93 passed, 2 fork-only skips\n- Mainnet fork: 97 passed, including live factory launch and splitter payouts\n- `git diff --check`","treeHash":"457bff0665724e77b41d5a8729ae7b2f2080168e","usage":{"cachedInputTokens":757760,"inputTokens":60905,"model":"gpt-6-astra","outputTokens":5653,"runtime":"codex","turns":5,"wallClockMs":216179}},{"artifacts":[],"attempt":1,"bundleHash":"4f60ca669657197382701a2d0284bdba9aa3bef95dfff1319819dee071f437e2","device":"f801e761d7a1ec94","findings":[],"hash":"ca2e16b315e58544345d80956948fc7ac8a6e2955616d6df3274f78db02cf0df","nodeId":"55467521-e7e6-4e63-826b-12a8ca9785b7","outcome":"completed","summary":"Updated the existing failure-path and invariant tests for the accepted fixes. Only five files under `test/` changed; details are in [ADAPTATION.md](test/ADAPTATION.md).\n\n- `forge build`: passed.\n- `forge test`: 138 passed; 2 fork-only skips.\n- Mainnet fork: 142 passed, including live factory launch and splitter payouts.\n- All six invariant suites passed.\n\nNo additional contract defect was reproduced.","treeHash":"f2f9e76a4e9a8bf1544f065b4ddc41d73b1d0fff","usage":{"cachedInputTokens":1700992,"inputTokens":117981,"model":"gpt-6-astra","outputTokens":12752,"runtime":"codex","turns":6,"wallClockMs":396508}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"7e929507773df661","findings":[{"citation":"resolved","description":"Asymmetry between the root-setting path and the expiry path. `_checkRootable` (lines 374-380) checks that the season is over, has no root, is not expired and that total <= pot, but it never looks at ROOT_DEADLINE. `expire()` for a rootless season (lines 308-313) becomes callable by anyone at endOf(s) + ROOT_DEADLINE and unconditionally deletes a pending governor proposal. Two consequences. (1) `proposeRoot` is accepted at any time after the season ends, including the last 48 hours before the deadline, where its eta = now + GOVERNOR_DELAY lands after the deadline; any address can then call `expire(s)` at the deadline, wiping the proposal, and `applyProposal` reverts NoProposal forever. The governor's fallback, which the docs describe as the backstop 'when a panel refuses', is silently defeated by a permissionless call even though it was submitted in time by the contract's own acceptance rules. (2) After the deadline, `submitAttestedRoot` and `applyProposal` remain valid, so whether a late season pays its points holders or releases its pot to the next season / treasury depends on who lands first in a block. Nothing is lost from the vault, and the governor can avoid case (1) by proposing at least 48 hours before the deadline, so the impact is a broken guarantee (medium-low), not a loss. Minimal fix that preserves the design: in `_checkRootable` (or in `proposeRoot`) require `block.timestamp + GOVERNOR_DELAY <= endOf(s) + ROOT_DEADLINE` for proposals, and in `expire()` refuse the rootless branch while `x.pendingEta != 0` (the proposal then either applies or is withdrawn by the governor, after which expire works). Alternatively make `_checkRootable` refuse any root once the deadline has passed so the two paths are mutually exclusive.","line":309,"path":"src/SeasonVault.sol","reproduction":"State: SeasonVault funded with 310,000,000e18 INFER; governor G; season 0 opened with pot 50,000,000e18 and formula keccak256('cid'). Let D = endOf(0) + ROOT_DEADLINE (26 weeks after the season ends). Sequence: (1) warp to D - 1 hours; G calls proposeRoot(0, keccak256('root'), 50_000_000e18, 'cid') -> succeeds, season(0).pendingEta = D + 47 hours. (2) warp to D; any address calls expire(0) -> succeeds, emits SeasonExpired(0, 50_000_000e18), season(0).pendingEta == 0, available() == 310_000_000e18. (3) warp to D + 47 hours; applyProposal(0) -> reverts NoProposal(0). Expected: either step 1 is refused (proposal cannot complete before the deadline) or step 2 is refused while a proposal is pending. Actual: the proposal is accepted and then destroyed by an unprivileged call, and the pot goes to available() instead of the season's points holders. Verified with a Foundry test (test passes on the current code, demonstrating the behaviour): openSeason at t+48h, warp to deadline-1h, proposeRoot ok, warp to deadline, expire ok, assertEq(available, 310e24), warp +47h, expectRevert NoProposal on applyProposal.","severity":"low","snippet":"            uint256 at = endOf(s) + ROOT_DEADLINE;\n            if (block.timestamp < at) revert NotYet(at);\n            amount = x.pot;\n            (x.pendingRoot, x.pendingTotal, x.pendingEta) = (0, 0, 0);","title":"SeasonVault: ROOT_DEADLINE is enforced only by expire(), so a governor proposal made inside the final 48 hours is accepted but can never be applied, and after the deadline a permissionless expire() ra"},{"citation":"resolved","description":"Access x economics seam. `redeem` is permissionless and the only thing that decides how much of a redemption is paid now versus vested over VEST_PERIOD is `paidImmediately[legacy][msg.sender]`. The contract's stated purpose for the cap ('a wallet cannot start its clock with dust, wait, and then redeem the bulk already unlocked'; 'LARGE REDEMPTIONS VEST') is that large holders cannot take their whole INFER at once. Because the limit is per address and nothing ties addresses to holders, a holder with N times the cap moves the legacy tokens into N fresh wallets and redeems the cap from each, receiving everything immediately with no vesting at all. For MIYA the per-wallet cap is 5,880,000 INFER (200,000 MIYA x 29.417); the 100M allocation is 17 wallets. For MXXN the cap is 930,000 INFER; the 40M allocation is 43 wallets. The legacy tokens' own transfer rules only raise the cost slightly: a MIYA sender holding any whitelisted NFT from the first 43 collections may transfer to any address, and MXXN transfers are free during each full-moon window (the same window in which redemption is possible). The vesting rule therefore binds only holders who do not bother to split, which is an economic asymmetry between user classes rather than a protection. No funds are lost and allocations are unchanged, so severity is low; it is reported because the design text presents the vesting as a guarantee. A contract-level fix is not possible without changing the agreed rule (there is no on-chain identity), so the author should either accept and document the cap as per-wallet, or change the brief (e.g. a global immediate budget per token per period), which is a scope decision.","line":144,"path":"src/LegacyRedeemer.sol","reproduction":"State: LegacyRedeemer deployed with the launch terms (rateA = 29.4e18 in the test, cap A = 5,880,000e18) and funded with 140,000,000e18 INFER; HOLDER owns 400,000 MIYA. Baseline (one wallet): HOLDER.redeem(MIYA, 400_000e18) pays 5,880,000e18 now and puts 5,880,000e18 on a 91-day vesting schedule (owedVesting == 5.88e24). Bypass: (1) HOLDER.redeem(MIYA, 200_000e18) -> HOLDER receives 5,880,000e18 at once; (2) HOLDER transfers 200,000e18 MIYA to HOLDER2 (a wallet they control); (3) HOLDER2.approve + HOLDER2.redeem(MIYA, 200_000e18) -> HOLDER2 receives 5,880,000e18 at once. Result: balanceOf(HOLDER)+balanceOf(HOLDER2) == 11,760,000e18 and owedVesting() == 0. Expected under the stated rule: 5,880,000e18 now, 5,880,000e18 vesting. Verified with a Foundry test on the current code (assertEq(sum, 11_760_000e18); assertEq(r.owedVesting(), 0)).","severity":"low","snippet":"        uint256 roomNow = immediateCap(legacy) - paidImmediately[address(legacy)][msg.sender];","title":"LegacyRedeemer: the immediate-payout cap is keyed on msg.sender, so one holder splits a redemption across wallets and receives the whole amount at once, bypassing the 13-week vesting rule"},{"citation":"resolved","description":"Documented privileged power, recorded as a trust assumption rather than a defect, as the review guides require. Two governor-only paths move everything SeasonVault holds to `treasury` without any attested root: (a) never open a season, call `announceEnd()`, wait GOVERNOR_DELAY (48 h), then anyone calls `endProgram()`, which sweeps `available()` (the full 310,000,000e18) to the treasury; (b) for an already opened season, after it ends call `proposeRoot(s, anyRoot, 0, formulaCid)`; 48 h later `applyProposal(s)` executes line 387 with total = 0 and releases the whole pot back into `available()`; then (a). Path (b) also lets the governor set any root of their choosing (e.g. a single leaf paying themselves the pot) unless an attested root lands in the 48-hour window. The only counter-measure the code offers is that the 48-hour windows are public (events EndAnnounced / RootProposed) and an attested root beats a pending proposal. The governor (0xbefd108085613662356aa26a2466ad3426da9c32) and the treasury (0x6ac8ff96fb8dcf5d79a8ef1e59d9ac23f2bd27bc) must therefore be trusted with the full points allocation; holders of points have no on-chain recourse. No change is proposed: removing or constraining the governor fallback is a design decision that the brief forbids this review from making.","line":387,"path":"src/SeasonVault.sol","reproduction":"Path (a): fund SeasonVault with 310_000_000e18; vm.prank(governor) announceEnd(); warp +48 hours; endProgram() from any address -> treasury balance == 310_000_000e18 (verified with a Foundry test). Path (b): governor openSeason(now+48h, 310_000_000e18, keccak256('cid')); warp to endOf(0); governor proposeRoot(0, bytes32(1), 0, 'cid'); warp +48h; applyProposal(0) -> available() == 310_000_000e18; governor announceEnd(); warp +48h; endProgram() -> treasury balance == 310_000_000e18 (verified with a Foundry test).","severity":"info","snippet":"        reserved -= x.pot - total; // the pot's unassigned part is released at once","title":"Trust assumption: the governor can route the entire 310M points allocation to the treasury with 48 to 96 hours of delay and no panel involvement"},{"citation":"resolved","description":"Access asymmetry between the two root paths, documented as a trust assumption. The governor path is delayed 48 hours and can be pre-empted; the attested path (`submitAttestedRoot`, callable by anyone) is applied in the same transaction and cannot be withdrawn or contested. Its only guard is an EIP-712 signature by the constant attester address plus the replay/expiry/panel-size checks in `_verifyYes`. Whoever controls that key (or any party that obtains one signed YES for a crafted leaf file with total <= pot) can set a season's root to a tree paying arbitrary accounts up to the season's pot, immediately after the season ends, and the vault has no mechanism to reverse it. The replay protection, domain separator (chain id + this contract), question-hash binding to season/root/total/CIDs and the pot bound were traced and hold; the residual risk is entirely the honesty and key-security of the attester. No change proposed: it is the agreed design.","line":88,"path":"src/SeasonAttestation.sol","reproduction":"Precondition: possession of a signature from 0x5598Aa9146215Bc13eb26f2c692Ad1461Fd32982 over an OracleAttestation whose questionHash equals keccak256(questionDocument(s, root, total, cid, formulaCid, fromBlock, toBlock)) for a root whose single leaf is (s, attacker, total). Call submitAttestedRoot(s, root, total, cid, formulaCid, a, sig) once the season has ended -> RootSet(s, root, total, true) in the same block; then claim(s, attacker, total, []) after SEASON vests the full total. The governor cannot withdraw or override an attested root (RootAlreadySet). This is the designed behaviour and was verified by reading the code paths, not exploited.","severity":"info","snippet":"        return 0x5598Aa9146215Bc13eb26f2c692Ad1461Fd32982;","title":"Trust assumption: a single hard-coded attester key makes any root immediate and final, with no governor veto and no delay"}],"hash":"f427e31c7234f246d963b23084c192e36dd5e62b16bd8108242c1b119143cc81","nodeId":"1654f79b-44e7-4cdd-8ee0-888663c30fb1","outcome":"completed","summary":"The review is complete. The findings file is written and validated, and the scratch tests are removed.\n\n**Outcome.** The permission model of this launch holds up. Every state-changing entry point was traced against the Access Control, Trust Gap and Asymmetry guides, and no bypass, escalation path, inconsistent guard or value-moving asymmetry was found that loses funds. Four entries were recorded in `.imd-findings.json`, none above low:\n\n- **Low, SeasonVault deadline asymmetry.** ROOT_DEADLINE is enforced only by the permissionless `expire()`, not by the root-setting path. A governor proposal made within 48 hours of the deadline is accepted and then deleted by anyone calling `expire`, so it can never be applied. After the deadline, late roots and expiry simply race. Reproduced with a Foundry test. Nothing leaves the vault.\n- **Low, LegacyRedeemer per-wallet cap.** The immediate payout cap is keyed on the caller, so a holder splits 400,000 MIYA across two wallets and takes 11.76M INFER at once with nothing vesting. Reproduced. This is a design limitation rather than a code bug, and fixing it would change the agreed rule.\n- **Info, governor power.** The governor can send the whole 310M points allocation to the treasury via announce-and-end in 48 hours, or release an opened pot with a zero-total proposal. Reproduced both paths. Documented as a trust assumption, as the guides require.\n- **Info, attester key.** Attested roots are immediate and final under a single hard-coded key, with no governor veto. The replay, domain, question-hash and pot-bound checks were traced and hold.\n\n**Coverage.** All 31 listed entry points have a row, plus the splitter's `split` and two accounting invariants. Build and the full local suite of 76 tests pass. The static-analysis leads in this area were checked: the `releaseNft` ERC20 selector collision is blocked by OpenZeppelin allowance semantics, and the strict-equality and timestamp lines are benign. The mainnet fork suite was not rerun, since no source was changed.","treeHash":null,"usage":{"cachedInputTokens":1245449,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":38533,"runtime":"claude","turns":30,"wallClockMs":639702}}],"verification":[{"checks":[{"durationMs":4543,"exitCode":0,"name":"build","output":"Compiling 59 files with Solc 0.8.26\nSolc 0.8.26 finished in 4.38s\nCompiler run successful with warnings:\nWarning (2018): Function state mutability can be restricted to view\n   --> test/Buckets.t.sol:172:5:\n    |\n172 |     function test_neitherDeclaresAGenesisShare() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nWarning (2018): Function state mutability can be restricted to view\n   --> test/LegacyRedeemer.t.sol:165:5:\n    |\n165 |     function test_vest_quoteForSplitsNowAndLater() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/FounderStream.sol:36:17\n   │\n36 │         start = uint64(block.timestamp);\n   │                 ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint64' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:42:13\n   │\n42 │         if (elapsed < CLIFF) return 0;\n   │             ━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:126:20\n    │\n126 │         closesAt = uint64(block.timestamp + REDEMPTION_PERIOD);\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:43:13\n   │\n43 │         if (elapsed >= DURATION) return genesisAllocation;\n   │             ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:51:13\n   │\n51 │         if (block.timestamp - start >= DURATION) return held;\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:139:13\n    │\n139 │         if (block.timestamp >= closesAt) revert Closed();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/LegacyRedeemer.sol:156:9\n    │\n156 │         emit Redeemed(address(legacy), msg.sender, amount, out);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:163:42\n    │\n163 │         vestingOf[msg.sender].claimed += uint128(amount);\n    │                                          ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:174:28\n    │\n174 │         uint256 unlocked = elapsed >= VEST_PERIOD ? v.total : uint256(v.total) * elapsed / VEST_PERIOD;\n    │                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/StakedInfer.sol:50:13\n   │\n50 │         if (held != 0) {\n   │             ━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:189:37\n    │\n189 │         vestingOf[holder] = Vesting(uint128(onSchedule), 0, uint64(block.timestamp));\n    │                                     ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:189:61\n    │\n189 │         vestingOf[holder] = Vesting(uint128(onSchedule), 0, uint64(block.timestamp));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:216:13\n    │\n216 │         if (block.timestamp < closesAt) revert StillOpen(closesAt);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/LegacyRedeemer.sol:229:35\n    │\n229 │                 (, uint256 end) = IMoonClock(address(legacy)).moonConfig();\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/SeasonAttestation.sol:115:16\n    │\n115 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n116 │ ┃             '{\"answerType\":\"bool\",\"chainId\":1,\"definitions\":{\"formula\":\"The INFER points formula for this season, ipfs://',\n117 │ ┃             formulaCid,\n118 │ ┃             '\"},\"evidence\":\"panel\",\"question\":\"Does the file at ipfs://',\n    ‡ ┃\n125 │ ┃             _decimal(to)\n126 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/LegacyRedeemer.sol:232:13\n    │\n232 │             IBurnable(address(legacy)).burn(1);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/LegacyRedeemer.sol:229:35\n    │\n229 │                 (, uint256 end) = IMoonClock(address(legacy)).moonConfig();\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/SeasonAttestation.sol:130:16\n    │\n130 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n131 │ ┃             \"), exactly the allocations that definitions.formula produces from Ethereum mainnet for that period, \",\n132 │ ┃             \"as a standard Merkle tree whose leaves are keccak256(keccak256(abi.encode(season, account, amount))) \",\n133 │ ┃             \"with root 0x\",\n    ‡ ┃\n141 │ ┃             \"}}\"\n142 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:244:13\n    │\n244 │         if (block.timestamp < closesAt) revert StillOpen(closesAt);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/LegacyRedeemer.sol:246:9\n    │\n246 │         emit NftReleased(collection, tokenId, nftReturnTo);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonAttestation.sol:165:13\n    │\n165 │         if (block.timestamp > a.expiresAt) revert Expired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonAttestation.sol:166:13\n    │\n166 │         if (a.issuedAt > block.timestamp || a.issuedAt > a.expiresAt) revert InvalidTimestamp();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:54:13\n   │\n54 │         if (elapsed > MAX_CATCHUP) elapsed = MAX_CATCHUP;\n   │             ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:57:13\n   │\n57 │         if (elapsed == MAX_CATCHUP && amount < minDrip) {\n   │             ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:57:39\n   │\n57 │         if (elapsed == MAX_CATCHUP && amount < minDrip) {\n   │                                       ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonAttestation.sol:184:96\n    │\n184 │             if (!((c >= \"a\" && c <= \"z\") || (c >= \"A\" && c <= \"Z\") || (c >= \"0\" && c <= \"9\"))) revert BadCid();\n    │                                                                                                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:68:53\n   │\n68 │         return amount != 0 && (amount >= minDrip || block.timestamp - lastDripAt >= MAX_CATCHUP);\n   │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SeasonAttestation.sol:227:15\n    │\n227 │             ++digits;\n    │               ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonAttestation.sol:231:36\n    │\n231 │             out[--digits] = bytes1(uint8(48 + (v % 10)));\n    │                                    ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:156:13\n    │\n156 │         if (startsAt < block.timestamp + NOTICE) revert TooSoon(block.timestamp + NOTICE);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:157:13\n    │\n157 │         if (startsAt > block.timestamp + MAX_NOTICE) revert TooFar(block.timestamp + MAX_NOTICE);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/base/ERC4626StaticAsset.sol:264:9\n    │\n264 │         emit Deposit(caller, receiver, assets, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/base/ERC4626StaticAsset.sol:287:9\n    │\n287 │         emit Withdraw(caller, receiver, owner, assets, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[arbitrary-send-erc20]: `transferFrom` uses an arbitrary `from`; require it to equal `msg.sender` or `address(this)`\n    ╭▸ src/base/ERC4626StaticAsset.sol:292:9\n    │\n292 │         SafeERC20.safeTransferFrom(IERC20(asset()), from, address(this), assets);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-erc20\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:392:9\n    │\n392 │         emit RootSet(s, root, total, attested);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:209:9\n    │\n209 │         emit RootAttested(s, a.requestId, cid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:210:9\n    │\n210 │         emit FormulaRevealed(s, formulaCid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:238:13\n    │\n238 │         if (block.timestamp + GOVERNOR_DELAY >= endOf(s) + ROOT_DEADLINE) revert PastDeadline(s);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:240:71\n    │\n240 │         (x.pendingRoot, x.pendingTotal, x.pendingEta) = (root, total, uint64(block.timestamp + GOVERNOR_DELAY));\n    │                                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:255:13\n    │\n255 │         if (block.timestamp < x.pendingEta) revert TooEarly(x.pendingEta);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:418:9\n    │\n418 │         emit Released(s, account, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:407:9\n    │\n407 │         emit Registered(s, account, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:362:27\n    │\n362 │         if (s >= SEASONS) revert NoSuchSeason(s);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:368:27\n    │\n368 │         if (x.start == 0) revert NoSuchSeason(s);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:399:31\n    │\n399 │         if (x.rootSetAt == 0) revert NoRoot(s);\n    │                               ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:400:73\n    │\n400 │         if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:402:63\n    │\n402 │         if (!MerkleProof.verifyCalldata(proof, x.root, leaf)) revert BadProof();\n    │                                                               ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:404:33\n    │\n404 │         if (amount > remaining) revert OverRemainingTotal(amount, remaining);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:309:17\n    │\n309 │             if (block.timestamp < at) revert NotYet(at);\n    │                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:313:17\n    │\n313 │             if (block.timestamp < at) revert NotYet(at);\n    │                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:326:18\n    │\n326 │         endEta = uint64(block.timestamp + GOVERNOR_DELAY);\n    │                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:341:13\n    │\n341 │         if (block.timestamp < endEta) revert TooEarly(endEta);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:379:13\n    │\n379 │         if (block.timestamp < x.start + SEASON) revert SeasonNotOver(s);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:382:13\n    │\n382 │         if (block.timestamp >= x.start + SEASON + ROOT_DEADLINE) revert PastDeadline(s);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:389:56\n    │\n389 │         (x.root, x.total, x.rootSetAt) = (root, total, uint64(block.timestamp));\n    │                                                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:400:26\n    │\n400 │         if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:423:26\n    │\n423 │         if (from == 0 || block.timestamp <= from) return 0;\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:425:16\n    │\n425 │         return elapsed >= SEASON ? amount : amount * elapsed / SEASON;\n    │                ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Buckets.t.sol:34:17\n   │\n34 │         vm.warp(block.timestamp + 30 days - 1);\n   │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:115:21\n    │\n115 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n290 │         vm.warp(seasons.endOf(s) - 48 hours);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n340 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n344 │         vm.warp(seasons.endOf(0) + 13 weeks);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n344 │         vm.warp(seasons.endOf(0) + 13 weeks);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:348:17\n    │\n348 │         vm.warp(block.timestamp + 365 days);\n    │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:364:17\n    │\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ────────━━━━━━━━━━━━━━━──────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n377 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n380 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n380 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n391 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n399 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n410 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n446 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n471 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n476 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n476 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n485 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n499 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n517 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n531 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n567 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:569:17\n    │\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n588 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n609 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n636 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n681 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n697 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:701:17\n    │\n701 │         vm.warp(block.timestamp + 48 hours);\n    │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n723 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n741 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:798:26\n    │\n798 │         uint256 latest = block.timestamp + 365 days;\n    │                          ━━━━━━━━━━━━━━━\n    ‡\n805 │         vm.warp(seasons.endOf(0) + 5 * 365 days); // long pauses are still permitted\n    │         ──────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/LegacyRedeemer.t.sol:109:17\n    │\n109 │         vm.warp(block.timestamp + 91 days / 2);\n    │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/LegacyRedeemer.t.sol:139:17\n    │\n139 │         vm.warp(block.timestamp + 91 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Revision.t.sol:120:33\n    │\n120 │         vault.openSeason(uint64(block.timestamp + 48 hours), POT, keccak256(bytes(CID)));\n    │                                 ━━━━━━━━━━━━━━━\n    ‡\n200 │         vm.warp(deadline);\n    │         ───────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Revision.t.sol:120:33\n    │\n120 │         vault.openSeason(uint64(block.timestamp + 48 hours), POT, keccak256(bytes(CID)));\n    │                                 ━━━━━━━━━━━━━━━\n    ‡\n212 │         vm.warp(deadline - 1);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Revision.t.sol:120:33\n    │\n120 │         vault.openSeason(uint64(block.timestamp + 48 hours), POT, keccak256(bytes(CID)));\n    │                                 ━━━━━━━━━━━━━━━\n    ‡\n228 │         vm.warp(vault.endOf(0));\n    │         ─────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Revision.t.sol:267:17\n    │\n267 │         vm.warp(block.timestamp + 100 * 365 days);\n    │         ────────━━━━━━━━━━━━━━━────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n   ╭▸ test/StakingRegression.t.sol:41:17\n   │\n41 │         vm.roll(block.number + 10);\n   │         ────────━━━━━━━━━━━━────── `vm.roll` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockNumber()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/StakingRegression.t.sol:151:17\n    │\n151 │         vm.roll(block.number + 1);\n    │         ────────━━━━━━━━━━━━───── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:217:21\n    │\n217 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:235:17\n    │\n235 │         vm.warp(block.timestamp + 1 hours - 1);\n    │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:239:17\n    │\n239 │         vm.warp(block.timestamp + 1);\n    │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:255:17\n    │\n255 │         vm.warp(block.timestamp + 30 minutes);\n    │         ────────━━━━━━━━━━━━━━━────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:260:17\n    │\n260 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:264:21\n    │\n264 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":160,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/LaunchRehearsal.fork.t.sol:LaunchRehearsalForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 303.91µs (0.00ns CPU time)\n\nRan 1 test for test/LegacyRedeemer.t.sol:LegacyRedeemerForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 281.63µs (0.00ns CPU time)\n\nRan 1 test for test/LaunchRehearsal.fork.t.sol:InferTokenTest\n[PASS] test_nameSymbolSupply() (gas: 46429)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 871.90µs (682.04µs CPU time)\n\nRan 1 test for test/Buckets.t.sol:GenesisSplitIntegrationTest\n[PASS] test_theBucketsAndTheTreasury() (gas: 8407306)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 953.30µs (582.79µs CPU time)\n\nRan 3 tests for test/LegacyRedeemer.t.sol:ClockUpkeepRevisionTest\n[PASS] test_catchupStopsWhenCurrentAndLaterWindowsStillAdvance() (gas: 197330)\n[PASS] test_currentClockCannotConsumeDustEvenWithRepeatedCalls() (gas: 82523)\n[PASS] test_stepsStillLimitsCatchupAndZeroDoesNothing() (gas: 110173)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.77ms (331.90µs CPU time)\n\nRan 9 tests for test/Revision.t.sol:SeasonRevisionTest\n[PASS] test_attestedRootCannotRaceExpiryAtDeadline() (gas: 548732)\n[PASS] test_governorCanEndWithoutOpeningAnySeason() (gas: 121724)\n[PASS] test_governorProposalNeedsTimeToMatureBeforeExpiry() (gas: 280786)\n[PASS] test_lastTimelyAttestedRootIsImmediateAndFinal() (gas: 699010)\n[PASS] test_lastTimelyGovernorProposalCanApply() (gas: 399555)\n[PASS] test_registeredClaimStillPaysAfterExpiryWithoutReregistering() (gas: 683058)\n[PASS] test_registeredClaimStillPaysAtRegistrationDeadline() (gas: 669185)\n[PASS] test_timeAloneCannotReleaseThePointsAllocation() (gas: 127482)\n[PASS] test_unappliedProposalCannotRaceExpiryAtDeadline() (gas: 417466)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 3.30ms (3.84ms CPU time)\n\nRan 2 tests for test/LaunchConstructors.t.sol:LaunchConstructorsTest\n[PASS] test_stakingConstructorDoesNotReadTokenMetadata() (gas: 14792)\n[PASS] test_staticConstructorsCallOnlyTheEarlierVaultAsset() (gas: 183255)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 3.76ms (3.78ms CPU time)\n\nRan 3 tests for test/Buckets.t.sol:FounderStreamTest\n[PASS] test_allOfItAtAYearAndNothingStranded() (gas: 145522)\n[PASS] test_neverPaysMoreThanItHolds() (gas: 460372)\n[PASS] test_nothingBeforeTheCliffThenTheStraightLine() (gas: 211164)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 21.04ms (943.59µs CPU time)\n\nRan 4 tests for test/Revision.t.sol:DripperDustRevisionTest\n[PASS] testFuzz_virtualCaptureIsBounded(uint256,uint256) (runs: 512, μ: 323286, ~: 323647)\nLogs:\n  Bound result 9999999999999999999000001001\n  Bound result 999000000000000000000130\n\n[PASS] test_dustVaultResumesAfterRealStakeArrives() (gas: 537854)\n[PASS] test_oneWeiCannotEnableDripsEvenAfterADonation() (gas: 443044)\n[PASS] test_thresholdBoundaryAndWithdrawalsRecheckTheGuard() (gas: 605774)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 33.30ms (33.06ms CPU time)\n\nRan 7 tests for test/Buckets.t.sol:StakingTest\n[PASS] test_aDepositRightBeforeADripGainsOnlyThatSlice() (gas: 492959)\n[PASS] test_inflationAttackDoesNotPay() (gas: 379085)\n[PASS] test_neitherDeclaresAGenesisShare() (gas: 13820)\n[PASS] test_neverDripsIntoAnEmptyVault() (gas: 91967)\n[PASS] test_oneCallReleasesAtMostAnHoursSliceOfTheBuffer() (gas: 355328)\n[PASS] test_stakersEarnByTimeHeldAndTheBufferDrainsOverAboutAWeek() (gas: 10406632)\n[PASS] test_theHoldTravelsWithSharesAndCannotBeUsedToGrief() (gas: 477204)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 53.40ms (12.17ms CPU time)\n\nRan 8 tests for test/StakingRegression.t.sol:StakingRegressionTest\n[PASS] testFuzz_matchesVendoredERC4626AfterTheHold(uint256,uint256,uint256) (runs: 512, μ: 2093039, ~: 2093251)\nLogs:\n  Bound result 125\n  Bound result 441000000000000000000000\n  Bound result 168660\n\n[PASS] testFuzz_movingSharesPreservesTheHold(uint256,uint256,uint256) (runs: 512, μ: 494059, ~: 465282)\nLogs:\n  Bound result 999999999999999999999\n  Bound result 999999999999999999999\n  Bound result 1144334936026411176422064114\n\n[PASS] test_dustDepositDoesNotBlockOlderShares() (gas: 422848)\n[PASS] test_dustMintDoesNotBlockWithdrawOrDelegatedExit() (gas: 389264)\n[PASS] test_freshDepositToAnotherReceiverCannotBypassTheHold() (gas: 447030)\n[PASS] test_freshShareDustCannotHoldTheRecipientsOlderShares() (gas: 451319)\n[PASS] test_transfersSpendUnlockedSharesFirstAndCarryOnlyTheHeldPart() (gas: 543529)\n[PASS] test_zeroAndSelfTransfersCannotRefreshOrShedTheHold() (gas: 564503)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 59.30ms (90.59ms CPU time)\n\nRan 18 tests for test/LegacyRedeemer.t.sol:LegacyRedeemerTest\n[PASS] test_afterOneYearClosedAndSweptToTheTreasury() (gas: 303291)\n[PASS] test_clockUpkeepNeedsDonatedDust() (gas: 1206206)\n[PASS] test_constructorChecks() (gas: 10778)\n[PASS] test_dustThatRedeemsForNothingIsRefused() (gas: 33142)\n[PASS] test_eachTokenIsCappedAtItsOwnAllocation() (gas: 405225)\n[PASS] test_immediateCapIsPerWalletAtTheLaunchRate() (gas: 2772157)\n[PASS] test_oneBucketForBothTokens() (gas: 27163)\n[PASS] test_onlyTheTwoTokens() (gas: 626378)\n[PASS] test_redeemsEachTokenAtItsRateAndBurnsIt() (gas: 335866)\n[PASS] test_theUnlockingNftReturnsAfterTheYear() (gas: 330312)\n[PASS] test_vest_aboveTheCapVestsLinearlyOverASeason() (gas: 434047)\n[PASS] test_vest_addingMidSeasonKeepsWhatWasAlreadyUnlocked() (gas: 360292)\n[PASS] test_vest_addingRestartsTheSeasonForEverythingUnvested() (gas: 377979)\n[PASS] test_vest_caps() (gas: 30080)\n[PASS] test_vest_eachTokenHasItsOwnCap() (gas: 407997)\n[PASS] test_vest_quoteForSplitsNowAndLater() (gas: 28146)\n[PASS] test_vest_smallRedemptionIsPaidAtOnce() (gas: 190082)\n[PASS] test_vest_sweepLeavesWhatIsOwedToVestingWallets() (gas: 408170)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 65.19ms (3.84ms CPU time)\n\nRan 5 tests for test/StakingRegression.t.sol:DripperRegressionTest\n[PASS] testFuzz_finalBalanceIsReleasedExactlyOnce(uint256) (runs: 512, μ: 249438, ~: 251220)\nLogs:\n  Bound result 20314604182050066116\n\n[PASS] test_floorStillRefusesAnEmptyVault() (gas: 175370)\n[PASS] test_fullHourIsRequiredForTheFloorAndFinalDust() (gas: 332829)\n[PASS] test_largeBufferKeepsItsNormalRateThenCrossesIntoTheFloor() (gas: 21474799)\n[PASS] test_smallBuybackReachesStakersAndDoesNotLeaveATail() (gas: 12333490)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 71.81ms (96.31ms CPU time)\n\nRan 32 tests for test/Buckets.t.sol:SeasonVaultTest\n[PASS] testFuzz_registrationConservesTheDeclaredTotal(uint256,uint256,uint256) (runs: 512, μ: 873144, ~: 892222)\nLogs:\n  Bound result 73848951376661476315068167\n  Bound result 63558305485773052333916002\n  Bound result 1661104462628647516\n\n[PASS] test_aCidThatWouldBreakTheJsonIsRefused() (gas: 327055)\n[PASS] test_aForgedLeafIsRefused() (gas: 544838)\n[PASS] test_aNoOrAThinPanelOrAStrangersSignatureIsRefused() (gas: 851766)\n[PASS] test_aSeasonNeedsARulesCommitmentAndItHidesTheDocument() (gas: 209318)\n[PASS] test_aSeasonWhoseRootNeverComesIsReleasedWhole() (gas: 311261)\n[PASS] test_aYesToAnotherQuestionIsRefused() (gas: 1005746)\n[PASS] test_anAnnouncedEndCanBeWithdrawn() (gas: 246138)\n[PASS] test_anExpiredOrReplayedYesIsRefused() (gas: 998140)\n[PASS] test_atMostFiveSeasons() (gas: 690197)\n[PASS] test_attestedLeavesAreCappedCumulativelyEvenAfterPayment() (gas: 988023)\n[PASS] test_distributeCannotRegisterBeyondTotalAndRollsBackTheBatch() (gas: 739817)\n[PASS] test_distributePushesABatch() (gas: 939069)\n[PASS] test_endingSendsOnlyWhatIsAvailableAndOwedClaimsStillPay() (gas: 1084023)\n[PASS] test_governorFallbackWaits48hAndTheAttestorCanStillWin() (gas: 730737)\n[PASS] test_governorLeafAboveTotalCannotRaidAnotherSeason() (gas: 708676)\n[PASS] test_governorRootAppliesAfterTheDelay() (gas: 399425)\n[PASS] test_millisecondStartIsRefusedWithoutReservingThePot() (gas: 219460)\n[PASS] test_nothingIsScheduledUntilTheGovernorOpensASeason() (gas: 66342)\n[PASS] test_openingNeedsNoticeAFixed13WeeksAndNoOverlap() (gas: 425201)\n[PASS] test_pausesAndSkips_theEarlierSeasonVestsRegardless() (gas: 853921)\n[PASS] test_printQuestionDocument() (gas: 229145)\nLogs:\n  0x7b22616e7377657254797065223a22626f6f6c222c22636861696e4964223a312c22646566696e6974696f6e73223a7b22666f726d756c61223a2254686520494e46455220706f696e747320666f726d756c6120666f72207468697320736561736f6e2c20697066733a2f2f6261666b7265696864776463656667683464716b6a763637757a636d77376f6a6565367865647a6465746f6a757a6a657674656e78717576796b75227d2c2265766964656e6365223a2270616e656c222c227175657374696f6e223a22446f6573207468652066696c6520617420697066733a2f2f6261666b726569676832616b69736361696c646371616273796733646672366368753366677072656769796d73636b376537617161347335327a7920676976652c20666f7220494e46455220706f696e747320736561736f6e20696e64657820302028756e69782074696d652031373238303120746f2038303335323031292c2065786163746c792074686520616c6c6f636174696f6e73207468617420646566696e6974696f6e732e666f726d756c612070726f64756365732066726f6d20457468657265756d206d61696e6e657420666f72207468617420706572696f642c2061732061207374616e64617264204d65726b6c6520747265652077686f7365206c656176657320617265206b656363616b323536286b656363616b323536286162692e656e636f646528736561736f6e2c206163636f756e742c20616d6f756e74292929207769746820726f6f742030786436633636636164303666653134666462366365393239376438306433326632346437343238393936643030343563626639306363333435633637376261313620616e6420616d6f756e74732073756d6d696e6720746f2031323330303030303030303030303030303030303020776569206f6620494e4645523f222c2276223a312c2277696e646f77223a7b2266726f6d426c6f636b223a3130302c22746f426c6f636b223a3230307d7d\n\n[PASS] test_reservedNeverExceedsTheBalance() (gas: 1037934)\n[PASS] test_rootCannotPromiseMoreThanThePot() (gas: 447967)\n[PASS] test_rootsMustNameTheRulesCommittedAtOpening() (gas: 638611)\n[PASS] test_startHorizonIsInclusiveAndRelativeToOpening() (gas: 323473)\n[PASS] test_theQuestionNamesTheSeasonsOwnDates() (gas: 462398)\n[PASS] test_theQuestionNamesTheSeasonsRules() (gas: 311598)\n[PASS] test_theUnassignedPartOfAPotIsReleasedAtTheRoot() (gas: 533843)\n[PASS] test_unregisteredLeavesAreReleasedAfterTheRegistrationDeadline() (gas: 866420)\n[PASS] test_vestingOfOneSeasonOverlapsTheNext() (gas: 1365652)\n[PASS] test_vestingRunsFromTheRootSoALateRootCostsNothing() (gas: 1063514)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 71.84ms (110.68ms CPU time)\n\nRan 14 test suites in 72.64ms (388.11ms CPU time): 93 tests passed, 0 failed, 2 skipped (95 total tests)\n","passed":true},{"durationMs":46,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FounderStream.release()\",\"InferDripper.drip()\",\"InferToken.approve(address,uint256)\",\"InferToken.transfer(address,uint256)\",\"InferToken.transferFrom(address,address,uint256)\",\"LegacyRedeemer.advanceClock(address,uint256)\",\"LegacyRedeemer.claim()\",\"LegacyRedeemer.redeem(address,uint256)\",\"LegacyRedeemer.releaseNft(address,uint256)\",\"LegacyRedeemer.sweep()\",\"SeasonVault.announceEnd()\",\"SeasonVault.applyProposal(uint256)\",\"SeasonVault.claim(uint256,address,uint256,bytes32[])\",\"SeasonVault.distribute((uint256,address,uint256,bytes32[])[])\",\"SeasonVault.endProgram()\",\"SeasonVault.expire(uint256)\",\"SeasonVault.openSeason(uint64,uint256,bytes32)\",\"SeasonVault.proposeRoot(uint256,bytes32,uint256,string)\",\"SeasonVault.release(uint256,address)\",\"SeasonVault.submitAttestedRoot(uint256,bytes32,uint256,string,string,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"SeasonVault.sweep()\",\"SeasonVault.withdrawEnd()\",\"SeasonVault.withdrawProposal(uint256)\",\"StakedInfer.approve(address,uint256)\",\"StakedInfer.deposit(uint256,address)\",\"StakedInfer.mint(uint256,address)\",\"StakedInfer.redeem(uint256,address,address)\",\"StakedInfer.transfer(address,uint256)\",\"StakedInfer.transferFrom(address,address,uint256)\",\"StakedInfer.withdraw(uint256,address,address)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":272,\"README.md\":95,\"foundry.toml\":16,\"remappings.txt\":4,\"src/FounderStream.sol\":64,\"src/InferDripper.sol\":87,\"src/InferToken.sol\":15,\"src/LegacyRedeemer.sol\":261,\"src/SeasonAttestation.sol\":243,\"src/SeasonVault.sol\":427,\"src/StakedInfer.sol\":68,\"src/base/ERC4626StaticAsset.sol\":303,\"src/base/LICENSE\":22,\"test/Buckets.t.sol\":862,\"test/LaunchConstructors.t.sol\":74,\"test/LaunchRehearsal.fork.t.sol\":206,\"test/LegacyRedeemer.t.sol\":531,\"test/Revision.t.sol\":279,\"test/StakingRegression.t.sol\":296},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":2678,"exitCode":0,"name":"slither","output":"[medium/high] erc20-interface at src/LegacyRedeemer.sol:16: IERC721Like (src/LegacyRedeemer.sol#15-17) has incorrect ERC20 function interface:IERC721Like.transferFrom(address,address,uint256) (src/LegacyRedeemer.sol#16)\n[medium/high] incorrect-equality at src/StakedInfer.sol:60: StakedInfer.lockedShares(address) (src/StakedInfer.sol#60-62) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/LegacyRedeemer.sol:170: LegacyRedeemer.claimable(address) (src/LegacyRedeemer.sol#170-176) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/SeasonVault.sol:421: SeasonVault._vested(uint256,uint256) (src/SeasonVault.sol#421-426) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/SeasonVault.sol:410: SeasonVault._release(uint256,address) (src/SeasonVault.sol#410-419) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/FounderStream.sol:57: FounderStream.release() (src/FounderStream.sol#57-63) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/SeasonVault.sol:338: SeasonVault.endProgram() (src/SeasonVault.sol#338-345) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/LegacyRedeemer.sol:160: LegacyRedeemer.claim() (src/LegacyRedeemer.sol#160-167) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/InferDripper.sol:52: InferDripper.releasable() (src/InferDripper.sol#52-61) uses a dangerous strict equality:\n[medium/medium] uninitialized-local at src/StakedInfer.sol:40: StakedInfer._update(address,address,uint256).held (src/StakedInfer.sol#40) is a local variable never initialized\n[medium/medium] uninitialized-local at src/SeasonAttestation.sol:225: SeasonAttestation._decimal(uint256).digits (src/SeasonAttestation.sol#225) is a local variable never initialized\n[medium/medium] unused-return at src/LegacyRedeemer.sol:225: LegacyRedeemer.advanceClock(IERC20,uint256) (src/LegacyRedeemer.sol#225-234) ignores return value by (None,end) = IMoonClock(address(legacy)).moonConfig() (src/LegacyRedeemer.sol#229)\n[low/medium] calls-loop at src/LegacyRedeemer.sol:225: LegacyRedeemer.advanceClock(IERC20,uint256) (src/LegacyRedeemer.sol#225-234) has external calls inside a loop: (None,end) = IMoonClock(address(legacy)).moonConfig() (src/LegacyRedeemer.sol#229)\n[low/medium] calls-loop at src/LegacyRedeemer.sol:225: LegacyRedeemer.advanceClock(IERC20,uint256) (src/LegacyRedeemer.sol#225-234) has external calls inside a loop: IBurnable(address(legacy)).burn(1) (src/LegacyRedeemer.sol#232)\n[low/medium] reentrancy-events at src/LegacyRedeemer.sol:138: Reentrancy in LegacyRedeemer.redeem(IERC20,uint256) (src/LegacyRedeemer.sol#138-157):\n[low/medium] reentrancy-events at src/LegacyRedeemer.sol:243: Reentrancy in LegacyRedeemer.releaseNft(address,uint256) (src/LegacyRedeemer.sol#243-247):\n[low/medium] timestamp at src/LegacyRedeemer.sol:160: LegacyRedeemer.claim() (src/LegacyRedeemer.sol#160-167) uses timestamp for comparisons\n[low/medium] timestamp at src/InferDripper.sol:52: InferDripper.releasable() (src/InferDripper.sol#52-61) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:410: SeasonVault._release(uint256,address) (src/SeasonVault.sol#410-419) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:303: SeasonVault.expire(uint256) (src/SeasonVault.sol#303-320) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:152: SeasonVault.openSeason(uint64,uint256,bytes32) (src/SeasonVault.sol#152-168) uses timestamp for comparisons\n[low/medium] timestamp at src/LegacyRedeemer.sol:243: LegacyRedeemer.releaseNft(address,uint256) (src/LegacyRedeemer.sol#243-247) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:355: SeasonVault._sweep() (src/SeasonVault.sol#355-359) uses timestamp for comparisons\n[low/medium] timestamp at src/FounderStream.sol:57: FounderStream.release() (src/FounderStream.sol#57-63) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:377: SeasonVault._checkRootable(uint256,uint256) (src/SeasonVault.sol#377-384) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:330: SeasonVault.withdrawEnd() (src/SeasonVault.sol#330-334) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:235: SeasonVault.proposeRoot(uint256,bytes32,uint256,string) (src/SeasonVault.sol#235-243) uses timestamp for comparisons\n[low/medium] timestamp at src/LegacyRedeemer.sol:138: LegacyRedeemer.redeem(IERC20,uint256) (src/LegacyRedeemer.sol#138-157) uses timestamp for comparisons\n[low/medium] timestamp at src/InferDripper.sol:72: InferDripper.drip() (src/InferDripper.sol#72-82) uses timestamp for comparisons\n[low/medium] timestamp at src/LegacyRedeemer.sol:215: LegacyRedeemer.sweep() (src/LegacyRedeemer.sol#215-220) uses timestamp for comparisons\n[low/medium] timestamp at src/FounderStream.sol:40: FounderStream.vested() (src/FounderStream.sol#40-45) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:252: SeasonVault.applyProposal(uint256) (src/SeasonVault.sol#252-257) uses timestamp for comparisons\n[low/medium] timestamp at src/LegacyRedeemer.sol:170: LegacyRedeemer.claimable(address) (src/LegacyRedeemer.sol#170-176) uses timestamp for comparisons\n[low/medium] timestamp at src/LegacyRedeemer.sol:179: LegacyRedeemer._addVesting(address,uint256) (src/LegacyRedeemer.sol#179-192) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:395: SeasonVault._register(uint256,address,uint256,bytes32[]) (src/SeasonVault.sol#395-408) uses timestamp for comparisons\n[low/medium] timestamp at src/FounderStream.sol:49: FounderStream.releasable() (src/FounderStream.sol#49-54) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:187: SeasonVault.available() (src/SeasonVault.sol#187-190) uses timestamp for comparisons\n[low/medium] timestamp at src/InferDripper.sol:63: InferDripper.canDrip() (src/InferDripper.sol#63-65) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:338: SeasonVault.endProgram() (src/SeasonVault.sol#338-345) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:421: SeasonVault._vested(uint256,uint256) (src/SeasonVault.sol#421-426) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonAttestation.sol:160: SeasonAttestation._verifyYes(SeasonAttestation.OracleAttestation,bytes,bytes32) (src/SeasonAttestation.sol#160-175) uses timestamp for comparisons\n[low/medium] timestamp at src/InferDripper.sol:67: InferDripper._ready(uint256) (src/InferDripper.sol#67-69) uses timestamp for comparisons","passed":true},{"durationMs":550,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/SeasonAttestation.sol:115: `abi.encodePacked()` Hash Collision (2 places)\n[high] reentrancy-state-change at src/InferDripper.sol:44: Reentrancy: State change after external call (2 places)\n[high] unsafe-casting at src/LegacyRedeemer.sol:189: Unsafe Casting of integers\n[low] costly-loop at src/SeasonVault.sol:283: Costly operations inside loop\n[low] ecrecover at src/SeasonAttestation.sol:219: `ecrecover` Signature Malleability\n[low] large-numeric-literal at src/InferDripper.sol:77: Large Numeric Literal\n[low] literal-instead-of-constant at src/LegacyRedeemer.sol:127: Literal Instead of Constant (9 places)\n[low] push-zero-opcode at src/base/ERC4626StaticAsset.sol:8: PUSH0 Opcode\n[low] require-revert-in-loop at src/SeasonAttestation.sol:182: Loop Contains `require`/`revert` (2 places)\n[low] unchecked-return at src/LegacyRedeemer.sol:226: Unchecked Return (5 places)\n[low] uninitialized-local-variable at src/LegacyRedeemer.sol:227: Uninitialized Local Variable (3 places)\n[low] unspecific-solidity-pragma at src/base/ERC4626StaticAsset.sol:8: Unspecific Solidity Pragma\n[low] unused-public-function at src/SeasonVault.sol:175: Public Function Not Used Internally (4 places)","passed":true},{"durationMs":1519,"exitCode":0,"name":"proof e31963921ec5","output":"Compiling 44 files with Solc 0.8.26\nSolc 0.8.26 finished in 981.19ms\nCompiler run successful!\n\nRan 2 tests for test/imd-proof-ee98f627/Proof_e31963921ec5.t.sol:DripperVirtualShareLeakTest\n[PASS] test_oneWeiDepositPassesTheEmptyVaultGuardAndStrandsHalfOfEveryDrip() (gas: 257261)\n[PASS] test_strandedInferStaysStrandedAfterRealStakersArrive() (gas: 250759)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.29ms (564.87µs CPU time)\n\nRan 1 test suite in 3.18ms (1.29ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"03d6d973c3e184019e68c4de58d30a39b0c61a4cc8eb92d0af4bf9d0c7af9c95","verifiedTreeHash":"8924766a2297bd2ca415c5eefddf64c2cf050fdb","verifierVersion":"0.1.0+0e08a464"},{"checks":[{"durationMs":7013,"exitCode":0,"name":"build","output":"Compiling 69 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.86s\nCompiler run successful with warnings:\nWarning (2018): Function state mutability can be restricted to view\n   --> test/Buckets.t.sol:172:5:\n    |\n172 |     function test_neitherDeclaresAGenesisShare() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nWarning (2018): Function state mutability can be restricted to view\n   --> test/LegacyRedeemer.t.sol:145:5:\n    |\n145 |     function test_vest_quoteForSplitsNowAndLater() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/FounderStream.sol:36:17\n   │\n36 │         start = uint64(block.timestamp);\n   │                 ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint64' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:42:13\n   │\n42 │         if (elapsed < CLIFF) return 0;\n   │             ━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:43:13\n   │\n43 │         if (elapsed >= DURATION) return genesisAllocation;\n   │             ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:51:13\n   │\n51 │         if (block.timestamp - start >= DURATION) return held;\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:122:20\n    │\n122 │         closesAt = uint64(block.timestamp + REDEMPTION_PERIOD);\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:53:13\n   │\n53 │         if (elapsed > MAX_CATCHUP) elapsed = MAX_CATCHUP;\n   │             ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:56:13\n   │\n56 │         if (elapsed == MAX_CATCHUP && amount < minDrip) {\n   │             ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:56:39\n   │\n56 │         if (elapsed == MAX_CATCHUP && amount < minDrip) {\n   │                                       ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:67:53\n   │\n67 │         return amount != 0 && (amount >= minDrip || block.timestamp - lastDripAt >= MAX_CATCHUP);\n   │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:135:13\n    │\n135 │         if (block.timestamp >= closesAt) revert Closed();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/LegacyRedeemer.sol:152:9\n    │\n152 │         emit Redeemed(address(legacy), msg.sender, amount, out);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:159:42\n    │\n159 │         vestingOf[msg.sender].claimed += uint128(amount);\n    │                                          ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:170:28\n    │\n170 │         uint256 unlocked = elapsed >= VEST_PERIOD ? v.total : uint256(v.total) * elapsed / VEST_PERIOD;\n    │                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/SeasonAttestation.sol:111:16\n    │\n111 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n112 │ ┃             '{\"answerType\":\"bool\",\"chainId\":1,\"definitions\":{\"formula\":\"The INFER points formula for this season, ipfs://',\n113 │ ┃             formulaCid,\n114 │ ┃             '\"},\"evidence\":\"panel\",\"question\":\"Does the file at ipfs://',\n    ‡ ┃\n121 │ ┃             _decimal(to)\n122 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/SeasonAttestation.sol:126:16\n    │\n126 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n127 │ ┃             \"), exactly the allocations that definitions.formula produces from Ethereum mainnet for that period, \",\n128 │ ┃             \"as a standard Merkle tree whose leaves are keccak256(keccak256(abi.encode(season, account, amount))) \",\n129 │ ┃             \"with root 0x\",\n    ‡ ┃\n137 │ ┃             \"}}\"\n138 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:185:37\n    │\n185 │         vestingOf[holder] = Vesting(uint128(onSchedule), 0, uint64(block.timestamp));\n    │                                     ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/StakedInfer.sol:50:13\n   │\n50 │         if (held != 0) {\n   │             ━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:185:61\n    │\n185 │         vestingOf[holder] = Vesting(uint128(onSchedule), 0, uint64(block.timestamp));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:208:13\n    │\n208 │         if (block.timestamp < closesAt) revert StillOpen(closesAt);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/LegacyRedeemer.sol:219:13\n    │\n219 │             IBurnable(address(legacy)).burn(1);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:231:13\n    │\n231 │         if (block.timestamp < closesAt) revert StillOpen(closesAt);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/LegacyRedeemer.sol:233:9\n    │\n233 │         emit NftReleased(collection, tokenId, nftReturnTo);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/base/ERC4626StaticAsset.sol:264:9\n    │\n264 │         emit Deposit(caller, receiver, assets, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/base/ERC4626StaticAsset.sol:287:9\n    │\n287 │         emit Withdraw(caller, receiver, owner, assets, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[arbitrary-send-erc20]: `transferFrom` uses an arbitrary `from`; require it to equal `msg.sender` or `address(this)`\n    ╭▸ src/base/ERC4626StaticAsset.sol:292:9\n    │\n292 │         SafeERC20.safeTransferFrom(IERC20(asset()), from, address(this), assets);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-erc20\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonAttestation.sol:161:13\n    │\n161 │         if (block.timestamp > a.expiresAt) revert Expired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonAttestation.sol:162:13\n    │\n162 │         if (a.issuedAt > block.timestamp || a.issuedAt > a.expiresAt) revert InvalidTimestamp();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonAttestation.sol:178:96\n    │\n178 │             if (!((c >= \"a\" && c <= \"z\") || (c >= \"A\" && c <= \"Z\") || (c >= \"0\" && c <= \"9\"))) revert BadCid();\n    │                                                                                                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SeasonAttestation.sol:218:52\n    │\n218 │         for (uint256 v = value; v != 0; v /= 10) ++digits;\n    │                                                    ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonAttestation.sol:220:73\n    │\n220 │         for (uint256 v = value; v != 0; v /= 10) out[--digits] = bytes1(uint8(48 + (v % 10)));\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:154:13\n    │\n154 │         if (startsAt < block.timestamp + NOTICE) revert TooSoon(block.timestamp + NOTICE);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:155:13\n    │\n155 │         if (startsAt > block.timestamp + MAX_NOTICE) revert TooFar(block.timestamp + MAX_NOTICE);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:388:9\n    │\n388 │         emit RootSet(s, root, total, attested);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:207:9\n    │\n207 │         emit RootAttested(s, a.requestId, cid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:208:9\n    │\n208 │         emit FormulaRevealed(s, formulaCid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:237:71\n    │\n237 │         (x.pendingRoot, x.pendingTotal, x.pendingEta) = (root, total, uint64(block.timestamp + GOVERNOR_DELAY));\n    │                                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:252:13\n    │\n252 │         if (block.timestamp < x.pendingEta) revert TooEarly(x.pendingEta);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:414:9\n    │\n414 │         emit Released(s, account, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:403:9\n    │\n403 │         emit Registered(s, account, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:359:27\n    │\n359 │         if (s >= SEASONS) revert NoSuchSeason(s);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:365:27\n    │\n365 │         if (x.start == 0) revert NoSuchSeason(s);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:393:31\n    │\n393 │         if (x.rootSetAt == 0) revert NoRoot(s);\n    │                               ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:394:73\n    │\n394 │         if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:398:63\n    │\n398 │         if (!MerkleProof.verifyCalldata(proof, x.root, leaf)) revert BadProof();\n    │                                                               ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:400:33\n    │\n400 │         if (amount > remaining) revert OverRemainingTotal(amount, remaining);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:306:17\n    │\n306 │             if (block.timestamp < at) revert NotYet(at);\n    │                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:310:17\n    │\n310 │             if (block.timestamp < at) revert NotYet(at);\n    │                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:323:18\n    │\n323 │         endEta = uint64(block.timestamp + GOVERNOR_DELAY);\n    │                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:338:13\n    │\n338 │         if (block.timestamp < endEta) revert TooEarly(endEta);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:376:13\n    │\n376 │         if (block.timestamp < x.start + SEASON) revert SeasonNotOver(s);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:385:56\n    │\n385 │         (x.root, x.total, x.rootSetAt) = (root, total, uint64(block.timestamp));\n    │                                                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:394:26\n    │\n394 │         if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:419:26\n    │\n419 │         if (from == 0 || block.timestamp <= from) return 0;\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:421:16\n    │\n421 │         return elapsed >= SEASON ? amount : amount * elapsed / SEASON;\n    │                ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/AttestationFailurePaths.t.sol:109:38\n    │\n109 │         production.openSeason(uint64(block.timestamp + 48 hours), POT, keccak256(bytes(RULES)));\n    │                                      ━━━━━━━━━━━━━━━\n110 │         vm.warp(production.endOf(0));\n    │         ──────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Buckets.t.sol:34:17\n   │\n34 │         vm.warp(block.timestamp + 30 days - 1);\n   │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:115:21\n    │\n115 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n290 │         vm.warp(seasons.endOf(s) - 48 hours);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n340 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n344 │         vm.warp(seasons.endOf(0) + 13 weeks);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n344 │         vm.warp(seasons.endOf(0) + 13 weeks);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:348:17\n    │\n348 │         vm.warp(block.timestamp + 365 days);\n    │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:364:17\n    │\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ────────━━━━━━━━━━━━━━━──────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n377 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n380 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n380 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n391 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n399 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n410 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n446 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n471 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n476 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n476 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n485 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n499 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n517 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n531 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n567 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:569:17\n    │\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n588 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n609 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n636 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n681 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n697 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:701:17\n    │\n701 │         vm.warp(block.timestamp + 48 hours);\n    │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n723 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n741 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:798:26\n    │\n798 │         uint256 latest = block.timestamp + 365 days;\n    │                          ━━━━━━━━━━━━━━━\n    ‡\n805 │         vm.warp(seasons.endOf(0) + 5 * 365 days); // long pauses are still permitted\n    │         ──────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/LegacyRedeemer.t.sol:89:17\n   │\n89 │         vm.warp(block.timestamp + 91 days / 2);\n   │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/LegacyRedeemer.t.sol:119:17\n    │\n119 │         vm.warp(block.timestamp + 91 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/SeasonFailurePaths.t.sol:39:17\n   │\n39 │         vm.warp(block.timestamp + 48 hours);\n   │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n   ╭▸ test/StakingRegression.t.sol:41:17\n   │\n41 │         vm.roll(block.number + 10);\n   │         ────────━━━━━━━━━━━━────── `vm.roll` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockNumber()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/StakingRegression.t.sol:151:17\n    │\n151 │         vm.roll(block.number + 1);\n    │         ────────━━━━━━━━━━━━───── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:217:21\n    │\n217 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:235:17\n    │\n235 │         vm.warp(block.timestamp + 1 hours - 1);\n    │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:239:17\n    │\n239 │         vm.warp(block.timestamp + 1);\n    │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:255:17\n    │\n255 │         vm.warp(block.timestamp + 30 minutes);\n    │         ────────━━━━━━━━━━━━━━━────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:260:17\n    │\n260 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:264:21\n    │\n264 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/invariant/FounderAccounting.invariant.t.sol:74:21\n   │\n74 │         vm.warp(max(block.timestamp, uint256(stream.start()) + 365 days));\n   │         ────────────━━━━━━━━━━━━━━━────────────────────────────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/invariant/LegacyAccounting.invariant.t.sol:148:21\n    │\n148 │         vm.warp(max(block.timestamp, uint256(redeemer.closesAt()) + 91 days));\n    │         ────────────━━━━━━━━━━━━━━━────────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/invariant/SeasonAccounting.invariant.t.sol:274:22\n    │\n274 │         uint256 at = block.timestamp;\n    │                      ━━━━━━━━━━━━━━━\n    ‡\n278 │         vm.warp(at);\n    │         ─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/invariant/SeasonAccounting.invariant.t.sol:169:34\n    │\n169 │             if (!ended) endEta = block.timestamp + 48 hours;\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n288 │             vm.warp(block.timestamp + 48 hours);\n    │             ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/invariant/SeasonAccounting.invariant.t.sol:185:32\n    │\n185 │             if (endEta != 0 && block.timestamp >= endEta) ended = true;\n    │                                ━━━━━━━━━━━━━━━\n    ‡\n288 │             vm.warp(block.timestamp + 48 hours);\n    │             ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/invariant/SeasonAccounting.invariant.t.sol:181:24\n    │\n181 │             } else if (block.timestamp < endEta) {\n    │                        ━━━━━━━━━━━━━━━\n    ‡\n288 │             vm.warp(block.timestamp + 48 hours);\n    │             ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/invariant/SeasonAccounting.invariant.t.sol:288:21\n    │\n288 │             vm.warp(block.timestamp + 48 hours);\n    │             ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/invariant/StakingAccounting.invariant.t.sol:196:17\n    │\n196 │         vm.roll(block.number + bound(blocks_, 0, 3));\n    │         ────────━━━━━━━━━━━━──────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/invariant/StakingAccounting.invariant.t.sol:55:13\n    │\n 55 │         if (block.number != heldAtBlock) {\n    │             ━━━━━━━━━━━━\n    ‡\n212 │             vm.roll(block.number + 1);\n    │             ───────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/invariant/StakingAccounting.invariant.t.sol:56:27\n    │\n 56 │             heldAtBlock = block.number;\n    │                           ━━━━━━━━━━━━\n    ‡\n212 │             vm.roll(block.number + 1);\n    │             ───────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/invariant/StakingAccounting.invariant.t.sol:51:24\n    │\n 51 │         assertEq(held, block.number == heldAtBlock ? heldThisBlock : 0, \"hold lost or extended by a transfer\");\n    │                        ━━━━━━━━━━━━\n    ‡\n212 │             vm.roll(block.number + 1);\n    │             ───────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/invariant/StakingAccounting.invariant.t.sol:212:21\n    │\n212 │             vm.roll(block.number + 1);\n    │             ────────━━━━━━━━━━━━───── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/invariant/StakingAccounting.invariant.t.sol:248:17\n    │\n248 │         vm.roll(block.number + 1);\n    │         ────────━━━━━━━━━━━━───── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":9210,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/LaunchRehearsal.fork.t.sol:LaunchRehearsalForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 247.90µs (0.00ns CPU time)\n\nRan 1 test for test/LaunchRehearsal.fork.t.sol:InferTokenTest\n[PASS] test_nameSymbolSupply() (gas: 46429)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 352.14µs (225.18µs CPU time)\n\nRan 1 test for test/LegacyRedeemer.t.sol:LegacyRedeemerForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 504.46µs (0.00ns CPU time)\n\nRan 1 test for test/Buckets.t.sol:GenesisSplitIntegrationTest\n[PASS] test_theBucketsAndTheTreasury() (gas: 8332251)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.92ms (1.44ms CPU time)\n\nRan 8 tests for test/SeasonFailurePaths.t.sol:SeasonFailurePathsTest\n[PASS] test_badSecondBatchEntryRollsBackFirstRegistrationAndPayout() (gas: 852340)\n[PASS] test_endCannotAccelerateThenReopenOrSweepReservedClaims() (gas: 848890)\n[PASS] test_everyGovernorEntryRejectsAnUnprivilegedCallerWithoutChangingState() (gas: 196713)\n[PASS] test_expiringRootlessSeasonCancelsPendingProposalAtExactDeadline() (gas: 333179)\n[PASS] test_missingSeasonAndRootFailuresAreSpecific() (gas: 234607)\n[PASS] test_proofBindsSeasonRecipientAndAmountAndFailureDoesNotRegister() (gas: 543238)\n[PASS] test_registrationDeadlineIsExclusiveButRegisteredClaimsRemainPayable() (gas: 671923)\n[PASS] test_rootProposalBoundariesReplacementAndCancellation() (gas: 731012)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 3.51ms (2.96ms CPU time)\n\nRan 2 tests for test/LaunchConstructors.t.sol:LaunchConstructorsTest\n[PASS] test_stakingConstructorDoesNotReadTokenMetadata() (gas: 14792)\n[PASS] test_staticConstructorsCallOnlyTheEarlierVaultAsset() (gas: 182078)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 80.86ms (2.02ms CPU time)\n\nRan 7 tests for test/LaunchFailurePaths.t.sol:LaunchFailurePathsTest\n[PASS] testFuzz_founderPaymentsNeverDependOnReleaseFrequency(uint256,uint256) (runs: 1000, μ: 287970, ~: 291305)\nLogs:\n  Bound result 16031722\n  Bound result 20711405\n\n[PASS] test_allRequiredConstructorAddressesAreNonzero() (gas: 1252666)\n[PASS] test_founderCliffAndEndAreExactEvenWithLateFunding() (gas: 561228)\n[PASS] test_legacySlotsRequireDistinctTokensAndNonzeroTerms() (gas: 1252286)\n[PASS] test_maximumTokenTransferFailsWithoutArithmeticOrSupplyChange() (gas: 77008)\n[PASS] test_noAdministrativeOrSupplyMutationEntrypointAndFullSupplyRemainsTransferable() (gas: 895435)\n[PASS] test_stakingFailuresRollbackAssetsSharesAndHold() (gas: 672105)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 80.97ms (71.56ms CPU time)\n\nRan 5 tests for test/StakingRegression.t.sol:DripperRegressionTest\n[PASS] testFuzz_finalBalanceIsReleasedExactlyOnce(uint256) (runs: 512, μ: 249072, ~: 251184)\nLogs:\n  Bound result 3841497696373245\n\n[PASS] test_floorStillRefusesAnEmptyVault() (gas: 175363)\n[PASS] test_fullHourIsRequiredForTheFloorAndFinalDust() (gas: 332786)\n[PASS] test_largeBufferKeepsItsNormalRateThenCrossesIntoTheFloor() (gas: 21473612)\n[PASS] test_smallBuybackReachesStakersAndDoesNotLeaveATail() (gas: 12330283)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 100.36ms (139.42ms CPU time)\n\nRan 7 tests for test/Buckets.t.sol:StakingTest\n[PASS] test_aDepositRightBeforeADripGainsOnlyThatSlice() (gas: 492955)\n[PASS] test_inflationAttackDoesNotPay() (gas: 379085)\n[PASS] test_neitherDeclaresAGenesisShare() (gas: 13820)\n[PASS] test_neverDripsIntoAnEmptyVault() (gas: 91963)\n[PASS] test_oneCallReleasesAtMostAnHoursSliceOfTheBuffer() (gas: 355298)\n[PASS] test_stakersEarnByTimeHeldAndTheBufferDrainsOverAboutAWeek() (gas: 10405960)\n[PASS] test_theHoldTravelsWithSharesAndCannotBeUsedToGrief() (gas: 477204)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 113.27ms (9.13ms CPU time)\n\nRan 10 tests for test/AttestationFailurePaths.t.sol:AttestationFailurePathsTest\n[PASS] testFuzz_invalidSignatureLengths(uint256) (runs: 256, μ: 254534, ~: 254526)\nLogs:\n  Bound result 127\n\n[PASS] testFuzz_signatureBindsFieldsEvenWhenAllValidityGuardsStillPass(uint8) (runs: 256, μ: 271356, ~: 271518)\nLogs:\n  Bound result 0\n\n[PASS] testFuzz_signedInvalidAttestationFieldsAreRefused(uint8) (runs: 256, μ: 328866, ~: 328692)\nLogs:\n  Bound result 0\n\n[PASS] test_emptyOversizedAndEscapedCidsAreRejected() (gas: 517312)\n[PASS] test_expiryAndPanelThresholdAreInclusiveAndRequestIsSingleUse() (gas: 448353)\n[PASS] test_productionContractRejectsTheFixtureSigner() (gas: 3278233)\n[PASS] test_questionCidsAndWindowArePartOfTheSignedQuestion() (gas: 584644)\n[PASS] test_rootValidationFailureDoesNotConsumeAnOtherwiseValidRequest() (gas: 590128)\n[PASS] test_signatureCannotCrossConsumersOrChains() (gas: 3483433)\n[PASS] test_signatureRejectsHighSInvalidVAndZeroRecovery() (gas: 493912)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 113.37ms (296.48ms CPU time)\n\nRan 32 tests for test/Buckets.t.sol:SeasonVaultTest\n[PASS] testFuzz_registrationConservesTheDeclaredTotal(uint256,uint256,uint256) (runs: 512, μ: 868058, ~: 891395)\nLogs:\n  Bound result 97630358715654986518563370\n  Bound result 642582957926032843083\n  Bound result 7529\n\n[PASS] test_aCidThatWouldBreakTheJsonIsRefused() (gas: 327055)\n[PASS] test_aForgedLeafIsRefused() (gas: 544536)\n[PASS] test_aNoOrAThinPanelOrAStrangersSignatureIsRefused() (gas: 851766)\n[PASS] test_aSeasonNeedsARulesCommitmentAndItHidesTheDocument() (gas: 209318)\n[PASS] test_aSeasonWhoseRootNeverComesIsReleasedWhole() (gas: 311261)\n[PASS] test_aYesToAnotherQuestionIsRefused() (gas: 1005746)\n[PASS] test_anAnnouncedEndCanBeWithdrawn() (gas: 246138)\n[PASS] test_anExpiredOrReplayedYesIsRefused() (gas: 997841)\n[PASS] test_atMostFiveSeasons() (gas: 690197)\n[PASS] test_attestedLeavesAreCappedCumulativelyEvenAfterPayment() (gas: 988258)\n[PASS] test_distributeCannotRegisterBeyondTotalAndRollsBackTheBatch() (gas: 739512)\n[PASS] test_distributePushesABatch() (gas: 938764)\n[PASS] test_endingSendsOnlyWhatIsAvailableAndOwedClaimsStillPay() (gas: 1083721)\n[PASS] test_governorFallbackWaits48hAndTheAttestorCanStillWin() (gas: 729458)\n[PASS] test_governorLeafAboveTotalCannotRaidAnotherSeason() (gas: 707394)\n[PASS] test_governorRootAppliesAfterTheDelay() (gas: 398146)\n[PASS] test_millisecondStartIsRefusedWithoutReservingThePot() (gas: 219460)\n[PASS] test_nothingIsScheduledUntilTheGovernorOpensASeason() (gas: 66342)\n[PASS] test_openingNeedsNoticeAFixed13WeeksAndNoOverlap() (gas: 425201)\n[PASS] test_pausesAndSkips_theEarlierSeasonVestsRegardless() (gas: 853619)\n[PASS] test_printQuestionDocument() (gas: 229145)\nLogs:\n  0x7b22616e7377657254797065223a22626f6f6c222c22636861696e4964223a312c22646566696e6974696f6e73223a7b22666f726d756c61223a2254686520494e46455220706f696e747320666f726d756c6120666f72207468697320736561736f6e2c20697066733a2f2f6261666b7265696864776463656667683464716b6a763637757a636d77376f6a6565367865647a6465746f6a757a6a657674656e78717576796b75227d2c2265766964656e6365223a2270616e656c222c227175657374696f6e223a22446f6573207468652066696c6520617420697066733a2f2f6261666b726569676832616b69736361696c646371616273796733646672366368753366677072656769796d73636b376537617161347335327a7920676976652c20666f7220494e46455220706f696e747320736561736f6e20696e64657820302028756e69782074696d652031373238303120746f2038303335323031292c2065786163746c792074686520616c6c6f636174696f6e73207468617420646566696e6974696f6e732e666f726d756c612070726f64756365732066726f6d20457468657265756d206d61696e6e657420666f72207468617420706572696f642c2061732061207374616e64617264204d65726b6c6520747265652077686f7365206c656176657320617265206b656363616b323536286b656363616b323536286162692e656e636f646528736561736f6e2c206163636f756e742c20616d6f756e74292929207769746820726f6f742030786436633636636164303666653134666462366365393239376438306433326632346437343238393936643030343563626639306363333435633637376261313620616e6420616d6f756e74732073756d6d696e6720746f2031323330303030303030303030303030303030303020776569206f6620494e4645523f222c2276223a312c2277696e646f77223a7b2266726f6d426c6f636b223a3130302c22746f426c6f636b223a3230307d7d\n\n[PASS] test_reservedNeverExceedsTheBalance() (gas: 1037629)\n[PASS] test_rootCannotPromiseMoreThanThePot() (gas: 447668)\n[PASS] test_rootsMustNameTheRulesCommittedAtOpening() (gas: 638312)\n[PASS] test_startHorizonIsInclusiveAndRelativeToOpening() (gas: 323473)\n[PASS] test_theQuestionNamesTheSeasonsOwnDates() (gas: 462398)\n[PASS] test_theQuestionNamesTheSeasonsRules() (gas: 311598)\n[PASS] test_theUnassignedPartOfAPotIsReleasedAtTheRoot() (gas: 533544)\n[PASS] test_unregisteredLeavesAreReleasedAfterTheRegistrationDeadline() (gas: 863837)\n[PASS] test_vestingOfOneSeasonOverlapsTheNext() (gas: 1365048)\n[PASS] test_vestingRunsFromTheRootSoALateRootCostsNothing() (gas: 1063212)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 112.99ms (131.13ms CPU time)\n\nRan 8 tests for test/StakingRegression.t.sol:StakingRegressionTest\n[PASS] testFuzz_matchesVendoredERC4626AfterTheHold(uint256,uint256,uint256) (runs: 512, μ: 2093278, ~: 2093347)\nLogs:\n  Bound result 630358715654986518563370\n  Bound result 642582957926032843083\n  Bound result 7529\n\n[PASS] testFuzz_movingSharesPreservesTheHold(uint256,uint256,uint256) (runs: 512, μ: 490318, ~: 465282)\nLogs:\n  Bound result 940000000000000000000\n  Bound result 10976855\n  Bound result 2418890148\n\n[PASS] test_dustDepositDoesNotBlockOlderShares() (gas: 422848)\n[PASS] test_dustMintDoesNotBlockWithdrawOrDelegatedExit() (gas: 389264)\n[PASS] test_freshDepositToAnotherReceiverCannotBypassTheHold() (gas: 447030)\n[PASS] test_freshShareDustCannotHoldTheRecipientsOlderShares() (gas: 451319)\n[PASS] test_transfersSpendUnlockedSharesFirstAndCarryOnlyTheHeldPart() (gas: 543529)\n[PASS] test_zeroAndSelfTransfersCannotRefreshOrShedTheHold() (gas: 564503)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 113.34ms (206.55ms CPU time)\n\nRan 3 tests for test/Buckets.t.sol:FounderStreamTest\n[PASS] test_allOfItAtAYearAndNothingStranded() (gas: 145522)\n[PASS] test_neverPaysMoreThanItHolds() (gas: 460372)\n[PASS] test_nothingBeforeTheCliffThenTheStraightLine() (gas: 211164)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 113.24ms (905.18µs CPU time)\n\nRan 17 tests for test/LegacyRedeemer.t.sol:LegacyRedeemerTest\n[PASS] test_afterOneYearClosedAndSweptToTheTreasury() (gas: 303291)\n[PASS] test_clockUpkeepNeedsDonatedDust() (gas: 346460)\n[PASS] test_constructorChecks() (gas: 10778)\n[PASS] test_dustThatRedeemsForNothingIsRefused() (gas: 33120)\n[PASS] test_eachTokenIsCappedAtItsOwnAllocation() (gas: 405225)\n[PASS] test_oneBucketForBothTokens() (gas: 27163)\n[PASS] test_onlyTheTwoTokens() (gas: 626356)\n[PASS] test_redeemsEachTokenAtItsRateAndBurnsIt() (gas: 335931)\n[PASS] test_theUnlockingNftReturnsAfterTheYear() (gas: 330312)\n[PASS] test_vest_aboveTheCapVestsLinearlyOverASeason() (gas: 434047)\n[PASS] test_vest_addingMidSeasonKeepsWhatWasAlreadyUnlocked() (gas: 360292)\n[PASS] test_vest_addingRestartsTheSeasonForEverythingUnvested() (gas: 377979)\n[PASS] test_vest_caps() (gas: 30080)\n[PASS] test_vest_eachTokenHasItsOwnCap() (gas: 407997)\n[PASS] test_vest_quoteForSplitsNowAndLater() (gas: 28146)\n[PASS] test_vest_smallRedemptionIsPaidAtOnce() (gas: 190060)\n[PASS] test_vest_sweepLeavesWhatIsOwedToVestingWallets() (gas: 408170)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 113.40ms (2.18ms CPU time)\n\nRan 9 tests for test/LegacyFailurePaths.t.sol:LegacyFailurePathsTest\n[PASS] testFuzz_partitioningRedemptionsCannotRoundUpOrBypassImmediateCap(uint256,uint256) (runs: 1000, μ: 349287, ~: 315385)\nLogs:\n  Bound result 2\n  Bound result 1\n\n[PASS] test_exactClosingBoundaryAndLateVestingStayIndependent() (gas: 831897)\n[PASS] test_failedBurnRollsBackDepositAutoClaimAllowanceAndNewSchedule() (gas: 915700)\n[PASS] test_fullAllocationBoundaryRefusesOneMoreRedeemWithoutTouchingOtherSlot() (gas: 726568)\n[PASS] test_noAllowanceAndInsufficientLegacyBalanceCannotConsumeAllocation() (gas: 747439)\n[PASS] test_redeemingUnderOtherSlotsCapDoesNotRestartExistingVesting() (gas: 425874)\n[PASS] test_transferFailureRollsBackAnExistingVestingSchedule() (gas: 656209)\n[PASS] test_upkeepFailureIsAtomicAndDoesNotBurnUserTokens() (gas: 223328)\n[PASS] test_zeroUnknownAndSubWeiOutputsRevertWithoutAccountingChanges() (gas: 742214)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 113.44ms (115.51ms CPU time)\n\nRan 1 test for test/invariant/FounderAccounting.invariant.t.sol:FounderAccountingInvariantTest\n[PASS] invariant_founderCanOnlyReceiveFundedAndVestedInfer() (runs: 256, calls: 24576, reverts: 0)\n\n╭--------------------------+----------+-------+---------+----------╮\n| Contract                 | Selector | Calls | Reverts | Discards |\n+==================================================================+\n| FounderAccountingHandler | elapse   | 8198  | 0       | 0        |\n|--------------------------+----------+-------+---------+----------|\n| FounderAccountingHandler | fund     | 8219  | 0       | 0        |\n|--------------------------+----------+-------+---------+----------|\n| FounderAccountingHandler | release  | 8159  | 0       | 0        |\n╰--------------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 70000000000000000000000000\n  Bound result 239999999999999999999999998\n  Bound result 4096\n  Bound result 28999999999999999999999961\n  Bound result 26\n  Bound result 7862400\n  Bound result 112\n  Bound result 3128113\n  Bound result 599290590\n  Bound result 109000000000000000000000\n  Bound result 2387937\n  Bound result 2348164\n  Bound result 7793\n  Bound result 6810000000000000000000000\n  Bound result 2017098\n  Bound result 5795\n  Bound result 2290713\n  Bound result 1619\n  Bound result 8\n  Bound result 441054405408930170330397880\n  Bound result 339620\n  Bound result 61680\n  Bound result 3392763\n  Bound result 1143\n  Bound result 66483517642069087091\n  Bound result 6027\n  Bound result 494903\n  Bound result 999000000000000000000\n  Bound result 239\n  Bound result 365\n  Bound result 1414216\n  Bound result 2365719\n  Bound result 1779768\n  Bound result 93948941784895626013426352\n  Bound result 933\n  Bound result 80923412677343439020139528\n  Bound result 0\n  Bound result 1704\n  Bound result 2303952\n  Bound result 60\n  Bound result 495000000000000000000\n  Bound result 2290713\n  Bound result 7000000000000000000\n  Bound result 28791663391190177151097565\n  Bound result 2980925\n  Bound result 1814400\n  Bound result 6100500000000000000000000\n  Bound result 944542594660822847830161\n  Bound result 65749\n  Bound result 2412808\n  Bound result 1433623\n  Bound result 2483909\n  Bound result 2592000\n  Bound result 109000000000000000000000\n  Bound result 1393593824688218446611713\n  Bound result 400\n  Bound result 5435\n  Bound result 3024000\n  Bound result 202\n  Bound result 483\n  Bound result 2026093\n  Bound result 70472418888596839941700\n  Bound result 2152290\n  Bound result 3\n  Bound result 1161139\n  Bound result 2771\n  Bound result 349\n  Bound result 100000000000000000000000\n  Bound result 140174215110986658418476\n  Bound result 1652\n  Bound result 1580179\n  Bound result 861\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.81s (1.80s CPU time)\n\nRan 1 test for test/invariant/SplitterAccounting.invariant.t.sol:SplitterAccountingInvariantTest\n[PASS] invariant_splitPaysEachLaunchExactlyOnceWithoutCrossingTokens() (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------------------+----------+-------+---------+----------╮\n| Contract                  | Selector | Calls | Reverts | Discards |\n+===================================================================+\n| SplitterAccountingHandler | donate   | 5391  | 0       | 0        |\n|---------------------------+----------+-------+---------+----------|\n| SplitterAccountingHandler | split    | 5568  | 0       | 0        |\n|---------------------------+----------+-------+---------+----------|\n| SplitterAccountingHandler | sweep    | 5425  | 0       | 0        |\n╰---------------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 700000000000000000000000000\n  Bound result 100000000000000000000000000\n  Bound result 18\n  Bound result 29990\n  Bound result 166177721865365727230966461\n  Bound result 74875048454625145517202389\n  Bound result 6900\n  Bound result 6883\n  Bound result 833139570457887344135986497\n  Bound result 36790000000000000000000000\n  Bound result 5098\n  Bound result 1000000000000000000\n  Bound result 7760\n  Bound result 988253150419370841\n  Bound result 63470693003562928624146899\n  Bound result 14320179313868897799606241\n  Bound result 1276841370733268384369409\n  Bound result 29400000000000000000000\n  Bound result 237\n  Bound result 35400000000000000000000\n  Bound result 3044\n  Bound result 430649981998\n  Bound result 6986156623991992468591381\n  Bound result 440508799972181015147753\n  Bound result 5000000000000000000\n  Bound result 192\n  Bound result 500000000000000000\n  Bound result 3456000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.82s (2.81s CPU time)\n\nRan 1 test for test/invariant/TokenAccounting.invariant.t.sol:TokenAccountingInvariantTest\n[PASS] invariant_fixedSupplyExactTransfersAndAllowanceLedger() (runs: 256, calls: 24576, reverts: 0)\n\n╭------------------------+---------------+-------+---------+----------╮\n| Contract               | Selector      | Calls | Reverts | Discards |\n+=====================================================================+\n| TokenAccountingHandler | approve       | 4879  | 0       | 0        |\n|------------------------+---------------+-------+---------+----------|\n| TokenAccountingHandler | failAllowance | 4985  | 0       | 0        |\n|------------------------+---------------+-------+---------+----------|\n| TokenAccountingHandler | failTransfer  | 4883  | 0       | 0        |\n|------------------------+---------------+-------+---------+----------|\n| TokenAccountingHandler | transfer      | 4926  | 0       | 0        |\n|------------------------+---------------+-------+---------+----------|\n| TokenAccountingHandler | transferFrom  | 4903  | 0       | 0        |\n╰------------------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1603\n  Bound result 7258\n  Bound result 5765\n  Bound result 249000000000000000000455770\n  Bound result 362\n  Bound result 65\n  Bound result 60\n  Bound result 0\n  Bound result 0\n  Bound result 719\n  Bound result 2592000\n  Bound result 117483120413131158450701024\n  Bound result 3792478065\n  Bound result 2\n  Bound result 351\n  Bound result 0\n  Bound result 388\n  Bound result 0\n  Bound result 3000000000000000000\n  Bound result 249999999999999999999999637\n  Bound result 99590864021590710851968125\n  Bound result 12809551499661025300739336\n  Bound result 5000000000000000000000000\n  Bound result 0\n  Bound result 3829\n  Bound result 1592\n  Bound result 209522348846588586243245242\n  Bound result 129\n  Bound result 7216\n  Bound result 1\n  Bound result 0\n  Bound result 161\n  Bound result 1\n  Bound result 1\n  Bound result 12809551499661025300739336\n  Bound result 398\n  Bound result 27\n  Bound result 0\n  Bound result 2592286112\n  Bound result 117876797167843645511539\n  Bound result 161\n  Bound result 467766170518\n  Bound result 4663\n  Bound result 351\n  Bound result 174380891000677949398546998\n  Bound result 18986680975255689826451491\n  Bound result 8640000\n  Bound result 48150071953702771113972288\n  Bound result 0\n  Bound result 161\n  Bound result 999\n  Bound result 0\n  Bound result 8389\n  Bound result 937\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.37s (3.37s CPU time)\n\nRan 1 test for test/invariant/LegacyAccounting.invariant.t.sol:LegacyAccountingInvariantTest\n[PASS] invariant_legacyBurnsAndInferLiabilitiesConserveValue() (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------------------+----------+-------+---------+----------╮\n| Contract                | Selector | Calls | Reverts | Discards |\n+=================================================================+\n| LegacyAccountingHandler | claim    | 4052  | 0       | 0        |\n|-------------------------+----------+-------+---------+----------|\n| LegacyAccountingHandler | donate   | 4165  | 0       | 0        |\n|-------------------------+----------+-------+---------+----------|\n| LegacyAccountingHandler | elapse   | 4173  | 0       | 0        |\n|-------------------------+----------+-------+---------+----------|\n| LegacyAccountingHandler | redeem   | 4093  | 0       | 0        |\n|-------------------------+----------+-------+---------+----------|\n| LegacyAccountingHandler | sweep    | 3983  | 0       | 0        |\n|-------------------------+----------+-------+---------+----------|\n| LegacyAccountingHandler | upkeep   | 4110  | 0       | 0        |\n╰-------------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 300000000000000000000000\n  Bound result 200000000000000000000000000\n  Bound result 831082041185116727655861579\n  Bound result 8\n  Bound result 3\n  Bound result 1280871\n  Bound result 1296981559962819723574334\n  Bound result 176635437248659544500716\n  Bound result 1100\n  Bound result 2696576\n  Bound result 9816\n  Bound result 26\n  Bound result 18\n  Bound result 3\n  Bound result 20223704372139163048864763\n  Bound result 32\n  Bound result 1\n  Bound result 2086044\n  Bound result 7\n  Bound result 13171\n  Bound result 13\n  Bound result 60000000000000000000\n  Bound result 1130407743249164921389233\n  Bound result 4362\n  Bound result 32089749\n  Bound result 9\n  Bound result 6\n  Bound result 7864\n  Bound result 6295\n  Bound result 2904024740460644221563848\n  Bound result 2463471\n  Bound result 1799999999999999999999999884\n  Bound result 721476337584188487945141\n  Bound result 319999\n  Bound result 393\n  Bound result 7851289668371509\n  Bound result 2931306643152013082607939\n  Bound result 1516691593248968112987486\n  Bound result 3931200\n  Bound result 8695\n  Bound result 29400000000000000000\n  Bound result 30\n  Bound result 2405\n  Bound result 11138\n  Bound result 23\n  Bound result 2893031\n  Bound result 65\n  Bound result 3\n  Bound result 11\n  Bound result 1000000\n  Bound result 14\n  Bound result 15\n  Bound result 983\n  Bound result 3310\n  Bound result 11\n  Bound result 4441\n  Bound result 64555457018147656134118\n  Bound result 86400\n  Bound result 2141935\n  Bound result 19\n  Bound result 4\n  Bound result 10424\n  Bound result 1999999970599999999999995538\n  Bound result 400\n  Bound result 49689844377555278335\n  Bound result 300000012456565209414566\n  Bound result 31\n  Bound result 287477501712539770241391\n  Bound result 17\n  Bound result 4855\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 5.31s (5.31s CPU time)\n\nRan 1 test for test/invariant/StakingAccounting.invariant.t.sol:StakingAccountingInvariantTest\n[PASS] invariant_sharesAssetsRewardsAndHoldsAreConserved() (runs: 256, calls: 24576, reverts: 0)\n\n╭--------------------------+------------------+-------+---------+----------╮\n| Contract                 | Selector         | Calls | Reverts | Discards |\n+==========================================================================+\n| StakingAccountingHandler | deposit          | 2829  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | drip             | 2720  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | elapse           | 2763  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | exit             | 2699  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | fund             | 2777  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | mint             | 2707  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | roundTrip        | 2703  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | transfer         | 2622  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | unauthorizedExit | 2756  | 0       | 0        |\n╰--------------------------+------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 20000000000000000000000\n  Bound result 1000000000000000000000000\n  Bound result 6810000000000000000000000\n  Bound result 444444444444444444430\n  Bound result 4\n  Bound result 1197364496614346809382656\n  Bound result 3000000000000000000\n  Bound result 1\n  Bound result 2\n  Bound result 0\n  Bound result 10000000000000000\n  Bound result 15472\n  Bound result 26100517423756626117182406193\n  Bound result 8887437537147924055539341451\n  Bound result 2822\n  Bound result 4\n  Bound result 6810000000000000000000000\n  Bound result 24\n  Bound result 0\n  Bound result 1866\n  Bound result 3\n  Bound result 3963891460\n  Bound result 829\n  Bound result 2877\n  Bound result 1874\n  Bound result 2\n  Bound result 16799\n  Bound result 4\n  Bound result 153832894076450831534\n  Bound result 4\n  Bound result 100496000000000000000000\n  Bound result 999999999999999920001\n  Bound result 1\n  Bound result 16000000000000000000\n  Bound result 4176\n  Bound result 10\n  Bound result 6091\n  Bound result 8416\n  Bound result 210000001000000000000000000\n  Bound result 400\n  Bound result 2255\n  Bound result 1185\n  Bound result 0\n  Bound result 1022466445542084465542807\n  Bound result 719161065763\n  Bound result 999999999999999960001\n  Bound result 4\n  Bound result 358\n  Bound result 2\n  Bound result 0\n  Bound result 220500000000000000000000\n  Bound result 441000000000000000000000\n  Bound result 1499\n  Bound result 181686843680561261\n  Bound result 100000000000000000000000\n  Bound result 27000000000000000000\n  Bound result 999999999999999900001\n  Bound result 4\n  Bound result 2348\n  Bound result 4\n  Bound result 2020109193795434325388801\n  Bound result 1965600\n  Bound result 2780\n  Bound result 0\n  Bound result 8419852071282418268593281\n  Bound result 6000\n  Bound result 2\n  Bound result 3042843762539331216282052\n  Bound result 19489\n  Bound result 64000000000000000000000000\n  Bound result 3811\n  Bound result 0\n  Bound result 12404\n  Bound result 952075778657410867810822\n  Bound result 129\n  Bound result 122\n  Bound result 2\n  Bound result 30000000000000000000\n  Bound result 1843\n  Bound result 19498\n  Bound result 44903603155223964497179534745\n  Bound result 64\n  Bound result 8915373578563400364036468817\n  Bound result 836373797208544056112140\n  Bound result 2539\n  Bound result 4900\n  Bound result 0\n  Bound result 3524\n  Bound result 500\n  Bound result 1076700798\n  Bound result 4822\n  Bound result 0\n  Bound result 999999999818560000001\n  Bound result 1\n  Bound result 53261\n  Bound result 2\n  Bound result 6762000000000000000000000\n  Bound result 583437570671364866150838960\n  Bound result 2989\n  Bound result 4\n  Bound result 2\n  Bound result 22877180722809371746926401645\n  Bound result 23393631463437950032671649391\n  Bound result 3075916761587529616150439739\n  Bound result 84325176623748587918359495118\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.06s (9.06s CPU time)\n\nRan 1 test for test/invariant/SeasonAccounting.invariant.t.sol:SeasonAccountingInvariantTest\n[PASS] invariant_seasonReservesEqualAllOutstandingCommitments() (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------------------+------------------+-------+---------+----------╮\n| Contract                | Selector         | Calls | Reverts | Discards |\n+=========================================================================+\n| SeasonAccountingHandler | applyRoot        | 2503  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | claim            | 2477  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | controlEnd       | 2484  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | donate           | 2472  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | elapse           | 2445  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | expire           | 2483  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | open             | 2380  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | propose          | 2419  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | release          | 2411  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | withdrawProposal | 2502  | 0       | 0        |\n╰-------------------------+------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 100000000000000000000000000\n  Bound result 0\n  Bound result 80000000000000000000000000\n  Bound result 90000000000000000000000000\n  Bound result 204\n  Bound result 59698397150365677349556584\n  Bound result 1608421\n  Bound result 2644590\n  Bound result 30000000000000000000000000\n  Bound result 9\n  Bound result 9660969159\n  Bound result 736816\n  Bound result 604762\n  Bound result 14571148706582018394573704\n  Bound result 5882940000000000000000000\n  Bound result 66\n  Bound result 2026\n  Bound result 2666\n  Bound result 40000000000000000000000000\n  Bound result 3796\n  Bound result 4246\n  Bound result 363991\n  Bound result 5882940000000000000000000\n  Bound result 17175\n  Bound result 7164\n  Bound result 322702\n  Bound result 5880000000000000000000000\n  Bound result 1606926\n  Bound result 4000000002\n  Bound result 558217360136923147584732159\n  Bound result 15663\n  Bound result 5183998\n  Bound result 10724\n  Bound result 543040\n  Bound result 10023202592970964990693078\n  Bound result 283511\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 9.11s (9.11s CPU time)\n\nRan 21 test suites in 9.12s (32.55s CPU time): 116 tests passed, 0 failed, 2 skipped (118 total tests)\n","passed":true},{"durationMs":60,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FounderStream.release()\",\"InferDripper.drip()\",\"InferToken.approve(address,uint256)\",\"InferToken.transfer(address,uint256)\",\"InferToken.transferFrom(address,address,uint256)\",\"LegacyRedeemer.advanceClock(address,uint256)\",\"LegacyRedeemer.claim()\",\"LegacyRedeemer.redeem(address,uint256)\",\"LegacyRedeemer.releaseNft(address,uint256)\",\"LegacyRedeemer.sweep()\",\"SeasonVault.announceEnd()\",\"SeasonVault.applyProposal(uint256)\",\"SeasonVault.claim(uint256,address,uint256,bytes32[])\",\"SeasonVault.distribute((uint256,address,uint256,bytes32[])[])\",\"SeasonVault.endProgram()\",\"SeasonVault.expire(uint256)\",\"SeasonVault.openSeason(uint64,uint256,bytes32)\",\"SeasonVault.proposeRoot(uint256,bytes32,uint256,string)\",\"SeasonVault.release(uint256,address)\",\"SeasonVault.submitAttestedRoot(uint256,bytes32,uint256,string,string,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"SeasonVault.sweep()\",\"SeasonVault.withdrawEnd()\",\"SeasonVault.withdrawProposal(uint256)\",\"StakedInfer.approve(address,uint256)\",\"StakedInfer.deposit(uint256,address)\",\"StakedInfer.mint(uint256,address)\",\"StakedInfer.redeem(uint256,address,address)\",\"StakedInfer.transfer(address,uint256)\",\"StakedInfer.transferFrom(address,address,uint256)\",\"StakedInfer.withdraw(uint256,address,address)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":157,\"README.md\":68,\"foundry.toml\":16,\"remappings.txt\":4,\"src/FounderStream.sol\":64,\"src/InferDripper.sol\":86,\"src/InferToken.sol\":15,\"src/LegacyRedeemer.sol\":248,\"src/SeasonAttestation.sol\":231,\"src/SeasonVault.sol\":423,\"src/StakedInfer.sol\":68,\"src/base/ERC4626StaticAsset.sol\":303,\"src/base/LICENSE\":22,\"test/ADAPTATION.md\":92,\"test/AttestationFailurePaths.t.sol\":290,\"test/Buckets.t.sol\":862,\"test/LaunchConstructors.t.sol\":74,\"test/LaunchFailurePaths.t.sol\":222,\"test/LaunchRehearsal.fork.t.sol\":206,\"test/LegacyFailurePaths.t.sol\":238,\"test/LegacyRedeemer.t.sol\":398,\"test/SeasonFailurePaths.t.sol\":246,\"test/StakingRegression.t.sol\":296,\"test/helpers/InferFixtures.sol\":72,\"test/invariant/FounderAccounting.invariant.t.sol\":107,\"test/invariant/LegacyAccounting.invariant.t.sol\":187,\"test/invariant/SeasonAccounting.invariant.t.sol\":330,\"test/invariant/SplitterAccounting.invariant.t.sol\":174,\"test/invariant/StakingAccounting.invariant.t.sol\":288,\"test/invariant/TokenAccounting.invariant.t.sol\":124},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"043f560b59772c298caf25554a0977e557287b4564e5582fca41faed15c63cac","verifiedTreeHash":"3a01e6963a9b5ebfccc322e1e05124484ab8236d","verifierVersion":"0.1.0+0e08a464"},{"checks":[{"durationMs":3690,"exitCode":0,"name":"build","output":"Compiling 58 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.55s\nCompiler run successful with warnings:\nWarning (2018): Function state mutability can be restricted to view\n   --> test/Buckets.t.sol:172:5:\n    |\n172 |     function test_neitherDeclaresAGenesisShare() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nWarning (2018): Function state mutability can be restricted to view\n   --> test/LegacyRedeemer.t.sol:145:5:\n    |\n145 |     function test_vest_quoteForSplitsNowAndLater() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/FounderStream.sol:36:17\n   │\n36 │         start = uint64(block.timestamp);\n   │                 ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint64' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/SeasonAttestation.sol:111:16\n    │\n111 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n112 │ ┃             '{\"answerType\":\"bool\",\"chainId\":1,\"definitions\":{\"formula\":\"The INFER points formula for this season, ipfs://',\n113 │ ┃             formulaCid,\n114 │ ┃             '\"},\"evidence\":\"panel\",\"question\":\"Does the file at ipfs://',\n    ‡ ┃\n121 │ ┃             _decimal(to)\n122 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:42:13\n   │\n42 │         if (elapsed < CLIFF) return 0;\n   │             ━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/SeasonAttestation.sol:126:16\n    │\n126 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n127 │ ┃             \"), exactly the allocations that definitions.formula produces from Ethereum mainnet for that period, \",\n128 │ ┃             \"as a standard Merkle tree whose leaves are keccak256(keccak256(abi.encode(season, account, amount))) \",\n129 │ ┃             \"with root 0x\",\n    ‡ ┃\n137 │ ┃             \"}}\"\n138 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:43:13\n   │\n43 │         if (elapsed >= DURATION) return genesisAllocation;\n   │             ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:53:13\n   │\n53 │         if (elapsed > MAX_CATCHUP) elapsed = MAX_CATCHUP;\n   │             ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:51:13\n   │\n51 │         if (block.timestamp - start >= DURATION) return held;\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:56:13\n   │\n56 │         if (elapsed == MAX_CATCHUP && amount < minDrip) {\n   │             ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:56:39\n   │\n56 │         if (elapsed == MAX_CATCHUP && amount < minDrip) {\n   │                                       ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:122:20\n    │\n122 │         closesAt = uint64(block.timestamp + REDEMPTION_PERIOD);\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:67:53\n   │\n67 │         return amount != 0 && (amount >= minDrip || block.timestamp - lastDripAt >= MAX_CATCHUP);\n   │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:135:13\n    │\n135 │         if (block.timestamp >= closesAt) revert Closed();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/LegacyRedeemer.sol:152:9\n    │\n152 │         emit Redeemed(address(legacy), msg.sender, amount, out);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:159:42\n    │\n159 │         vestingOf[msg.sender].claimed += uint128(amount);\n    │                                          ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:170:28\n    │\n170 │         uint256 unlocked = elapsed >= VEST_PERIOD ? v.total : uint256(v.total) * elapsed / VEST_PERIOD;\n    │                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonAttestation.sol:161:13\n    │\n161 │         if (block.timestamp > a.expiresAt) revert Expired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonAttestation.sol:162:13\n    │\n162 │         if (a.issuedAt > block.timestamp || a.issuedAt > a.expiresAt) revert InvalidTimestamp();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonAttestation.sol:178:96\n    │\n178 │             if (!((c >= \"a\" && c <= \"z\") || (c >= \"A\" && c <= \"Z\") || (c >= \"0\" && c <= \"9\"))) revert BadCid();\n    │                                                                                                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SeasonAttestation.sol:218:52\n    │\n218 │         for (uint256 v = value; v != 0; v /= 10) ++digits;\n    │                                                    ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonAttestation.sol:220:73\n    │\n220 │         for (uint256 v = value; v != 0; v /= 10) out[--digits] = bytes1(uint8(48 + (v % 10)));\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:154:13\n    │\n154 │         if (startsAt < block.timestamp + NOTICE) revert TooSoon(block.timestamp + NOTICE);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:155:13\n    │\n155 │         if (startsAt > block.timestamp + MAX_NOTICE) revert TooFar(block.timestamp + MAX_NOTICE);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/base/ERC4626StaticAsset.sol:264:9\n    │\n264 │         emit Deposit(caller, receiver, assets, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:185:37\n    │\n185 │         vestingOf[holder] = Vesting(uint128(onSchedule), 0, uint64(block.timestamp));\n    │                                     ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/base/ERC4626StaticAsset.sol:287:9\n    │\n287 │         emit Withdraw(caller, receiver, owner, assets, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:185:61\n    │\n185 │         vestingOf[holder] = Vesting(uint128(onSchedule), 0, uint64(block.timestamp));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-erc20]: `transferFrom` uses an arbitrary `from`; require it to equal `msg.sender` or `address(this)`\n    ╭▸ src/base/ERC4626StaticAsset.sol:292:9\n    │\n292 │         SafeERC20.safeTransferFrom(IERC20(asset()), from, address(this), assets);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-erc20\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:208:13\n    │\n208 │         if (block.timestamp < closesAt) revert StillOpen(closesAt);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/StakedInfer.sol:50:13\n   │\n50 │         if (held != 0) {\n   │             ━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/LegacyRedeemer.sol:219:13\n    │\n219 │             IBurnable(address(legacy)).burn(1);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:231:13\n    │\n231 │         if (block.timestamp < closesAt) revert StillOpen(closesAt);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/LegacyRedeemer.sol:233:9\n    │\n233 │         emit NftReleased(collection, tokenId, nftReturnTo);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:388:9\n    │\n388 │         emit RootSet(s, root, total, attested);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:207:9\n    │\n207 │         emit RootAttested(s, a.requestId, cid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:208:9\n    │\n208 │         emit FormulaRevealed(s, formulaCid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:237:71\n    │\n237 │         (x.pendingRoot, x.pendingTotal, x.pendingEta) = (root, total, uint64(block.timestamp + GOVERNOR_DELAY));\n    │                                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:252:13\n    │\n252 │         if (block.timestamp < x.pendingEta) revert TooEarly(x.pendingEta);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:414:9\n    │\n414 │         emit Released(s, account, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:403:9\n    │\n403 │         emit Registered(s, account, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:359:27\n    │\n359 │         if (s >= SEASONS) revert NoSuchSeason(s);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:365:27\n    │\n365 │         if (x.start == 0) revert NoSuchSeason(s);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:393:31\n    │\n393 │         if (x.rootSetAt == 0) revert NoRoot(s);\n    │                               ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:394:73\n    │\n394 │         if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:398:63\n    │\n398 │         if (!MerkleProof.verifyCalldata(proof, x.root, leaf)) revert BadProof();\n    │                                                               ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:400:33\n    │\n400 │         if (amount > remaining) revert OverRemainingTotal(amount, remaining);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:306:17\n    │\n306 │             if (block.timestamp < at) revert NotYet(at);\n    │                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:310:17\n    │\n310 │             if (block.timestamp < at) revert NotYet(at);\n    │                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:323:18\n    │\n323 │         endEta = uint64(block.timestamp + GOVERNOR_DELAY);\n    │                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:338:13\n    │\n338 │         if (block.timestamp < endEta) revert TooEarly(endEta);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:376:13\n    │\n376 │         if (block.timestamp < x.start + SEASON) revert SeasonNotOver(s);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:385:56\n    │\n385 │         (x.root, x.total, x.rootSetAt) = (root, total, uint64(block.timestamp));\n    │                                                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:394:26\n    │\n394 │         if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:419:26\n    │\n419 │         if (from == 0 || block.timestamp <= from) return 0;\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:421:16\n    │\n421 │         return elapsed >= SEASON ? amount : amount * elapsed / SEASON;\n    │                ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Buckets.t.sol:34:17\n   │\n34 │         vm.warp(block.timestamp + 30 days - 1);\n   │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:115:21\n    │\n115 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n290 │         vm.warp(seasons.endOf(s) - 48 hours);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n340 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n344 │         vm.warp(seasons.endOf(0) + 13 weeks);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n344 │         vm.warp(seasons.endOf(0) + 13 weeks);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:348:17\n    │\n348 │         vm.warp(block.timestamp + 365 days);\n    │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:364:17\n    │\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ────────━━━━━━━━━━━━━━━──────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n377 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n380 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n380 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n391 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n399 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n410 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n446 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n471 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n476 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n476 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n485 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n499 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n517 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n531 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n567 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:569:17\n    │\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n588 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n609 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n636 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n681 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n697 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:701:17\n    │\n701 │         vm.warp(block.timestamp + 48 hours);\n    │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n723 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n741 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:798:26\n    │\n798 │         uint256 latest = block.timestamp + 365 days;\n    │                          ━━━━━━━━━━━━━━━\n    ‡\n805 │         vm.warp(seasons.endOf(0) + 5 * 365 days); // long pauses are still permitted\n    │         ──────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/LegacyRedeemer.t.sol:89:17\n   │\n89 │         vm.warp(block.timestamp + 91 days / 2);\n   │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/LegacyRedeemer.t.sol:119:17\n    │\n119 │         vm.warp(block.timestamp + 91 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n   ╭▸ test/StakingRegression.t.sol:41:17\n   │\n41 │         vm.roll(block.number + 10);\n   │         ────────━━━━━━━━━━━━────── `vm.roll` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockNumber()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/StakingRegression.t.sol:151:17\n    │\n151 │         vm.roll(block.number + 1);\n    │         ────────━━━━━━━━━━━━───── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:217:21\n    │\n217 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:235:17\n    │\n235 │         vm.warp(block.timestamp + 1 hours - 1);\n    │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:239:17\n    │\n239 │         vm.warp(block.timestamp + 1);\n    │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:255:17\n    │\n255 │         vm.warp(block.timestamp + 30 minutes);\n    │         ────────━━━━━━━━━━━━━━━────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:260:17\n    │\n260 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:264:21\n    │\n264 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":155,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/LegacyRedeemer.t.sol:LegacyRedeemerForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 258.54µs (0.00ns CPU time)\n\nRan 1 test for test/LaunchRehearsal.fork.t.sol:LaunchRehearsalForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 260.53µs (0.00ns CPU time)\n\nRan 1 test for test/LaunchRehearsal.fork.t.sol:InferTokenTest\n[PASS] test_nameSymbolSupply() (gas: 46429)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 406.34µs (235.61µs CPU time)\n\nRan 1 test for test/Buckets.t.sol:GenesisSplitIntegrationTest\n[PASS] test_theBucketsAndTheTreasury() (gas: 8332251)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.08ms (763.08µs CPU time)\n\nRan 2 tests for test/LaunchConstructors.t.sol:LaunchConstructorsTest\n[PASS] test_stakingConstructorDoesNotReadTokenMetadata() (gas: 14792)\n[PASS] test_staticConstructorsCallOnlyTheEarlierVaultAsset() (gas: 182078)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 15.24ms (1.67ms CPU time)\n\nRan 7 tests for test/Buckets.t.sol:StakingTest\n[PASS] test_aDepositRightBeforeADripGainsOnlyThatSlice() (gas: 492955)\n[PASS] test_inflationAttackDoesNotPay() (gas: 379085)\n[PASS] test_neitherDeclaresAGenesisShare() (gas: 13820)\n[PASS] test_neverDripsIntoAnEmptyVault() (gas: 91963)\n[PASS] test_oneCallReleasesAtMostAnHoursSliceOfTheBuffer() (gas: 355298)\n[PASS] test_stakersEarnByTimeHeldAndTheBufferDrainsOverAboutAWeek() (gas: 10405960)\n[PASS] test_theHoldTravelsWithSharesAndCannotBeUsedToGrief() (gas: 477204)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 15.21ms (7.35ms CPU time)\n\nRan 3 tests for test/Buckets.t.sol:FounderStreamTest\n[PASS] test_allOfItAtAYearAndNothingStranded() (gas: 145522)\n[PASS] test_neverPaysMoreThanItHolds() (gas: 460372)\n[PASS] test_nothingBeforeTheCliffThenTheStraightLine() (gas: 211164)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 47.87ms (466.56µs CPU time)\n\nRan 8 tests for test/StakingRegression.t.sol:StakingRegressionTest\n[PASS] testFuzz_matchesVendoredERC4626AfterTheHold(uint256,uint256,uint256) (runs: 512, μ: 2093153, ~: 2093183)\nLogs:\n  Bound result 25809949290115859370196\n  Bound result 395580514496211645502350\n  Bound result 34688267680698915\n\n[PASS] testFuzz_movingSharesPreservesTheHold(uint256,uint256,uint256) (runs: 512, μ: 494602, ~: 465224)\nLogs:\n  Bound result 1760000000\n  Bound result 1000000000000000000000\n  Bound result 500000000000000000000000000\n\n[PASS] test_dustDepositDoesNotBlockOlderShares() (gas: 422848)\n[PASS] test_dustMintDoesNotBlockWithdrawOrDelegatedExit() (gas: 389264)\n[PASS] test_freshDepositToAnotherReceiverCannotBypassTheHold() (gas: 447030)\n[PASS] test_freshShareDustCannotHoldTheRecipientsOlderShares() (gas: 451319)\n[PASS] test_transfersSpendUnlockedSharesFirstAndCarryOnlyTheHeldPart() (gas: 543529)\n[PASS] test_zeroAndSelfTransfersCannotRefreshOrShedTheHold() (gas: 564503)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 55.96ms (85.29ms CPU time)\n\nRan 5 tests for test/StakingRegression.t.sol:DripperRegressionTest\n[PASS] testFuzz_finalBalanceIsReleasedExactlyOnce(uint256) (runs: 512, μ: 249053, ~: 251184)\nLogs:\n  Bound result 832813729135901207999\n\n[PASS] test_floorStillRefusesAnEmptyVault() (gas: 175363)\n[PASS] test_fullHourIsRequiredForTheFloorAndFinalDust() (gas: 332786)\n[PASS] test_largeBufferKeepsItsNormalRateThenCrossesIntoTheFloor() (gas: 21473612)\n[PASS] test_smallBuybackReachesStakersAndDoesNotLeaveATail() (gas: 12330283)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 60.85ms (83.61ms CPU time)\n\nRan 17 tests for test/LegacyRedeemer.t.sol:LegacyRedeemerTest\n[PASS] test_afterOneYearClosedAndSweptToTheTreasury() (gas: 303291)\n[PASS] test_clockUpkeepNeedsDonatedDust() (gas: 346460)\n[PASS] test_constructorChecks() (gas: 10778)\n[PASS] test_dustThatRedeemsForNothingIsRefused() (gas: 33120)\n[PASS] test_eachTokenIsCappedAtItsOwnAllocation() (gas: 405225)\n[PASS] test_oneBucketForBothTokens() (gas: 27163)\n[PASS] test_onlyTheTwoTokens() (gas: 626356)\n[PASS] test_redeemsEachTokenAtItsRateAndBurnsIt() (gas: 335931)\n[PASS] test_theUnlockingNftReturnsAfterTheYear() (gas: 330312)\n[PASS] test_vest_aboveTheCapVestsLinearlyOverASeason() (gas: 434047)\n[PASS] test_vest_addingMidSeasonKeepsWhatWasAlreadyUnlocked() (gas: 360292)\n[PASS] test_vest_addingRestartsTheSeasonForEverythingUnvested() (gas: 377979)\n[PASS] test_vest_caps() (gas: 30080)\n[PASS] test_vest_eachTokenHasItsOwnCap() (gas: 407997)\n[PASS] test_vest_quoteForSplitsNowAndLater() (gas: 28146)\n[PASS] test_vest_smallRedemptionIsPaidAtOnce() (gas: 190060)\n[PASS] test_vest_sweepLeavesWhatIsOwedToVestingWallets() (gas: 408170)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 60.96ms (3.48ms CPU time)\n\nRan 32 tests for test/Buckets.t.sol:SeasonVaultTest\n[PASS] testFuzz_registrationConservesTheDeclaredTotal(uint256,uint256,uint256) (runs: 512, μ: 881096, ~: 900672)\nLogs:\n  Bound result 10311\n  Bound result 16000000000000000000\n  Bound result 15\n\n[PASS] test_aCidThatWouldBreakTheJsonIsRefused() (gas: 327055)\n[PASS] test_aForgedLeafIsRefused() (gas: 544536)\n[PASS] test_aNoOrAThinPanelOrAStrangersSignatureIsRefused() (gas: 851766)\n[PASS] test_aSeasonNeedsARulesCommitmentAndItHidesTheDocument() (gas: 209318)\n[PASS] test_aSeasonWhoseRootNeverComesIsReleasedWhole() (gas: 311261)\n[PASS] test_aYesToAnotherQuestionIsRefused() (gas: 1005746)\n[PASS] test_anAnnouncedEndCanBeWithdrawn() (gas: 246138)\n[PASS] test_anExpiredOrReplayedYesIsRefused() (gas: 997841)\n[PASS] test_atMostFiveSeasons() (gas: 690197)\n[PASS] test_attestedLeavesAreCappedCumulativelyEvenAfterPayment() (gas: 988258)\n[PASS] test_distributeCannotRegisterBeyondTotalAndRollsBackTheBatch() (gas: 739512)\n[PASS] test_distributePushesABatch() (gas: 938764)\n[PASS] test_endingSendsOnlyWhatIsAvailableAndOwedClaimsStillPay() (gas: 1083721)\n[PASS] test_governorFallbackWaits48hAndTheAttestorCanStillWin() (gas: 729458)\n[PASS] test_governorLeafAboveTotalCannotRaidAnotherSeason() (gas: 707394)\n[PASS] test_governorRootAppliesAfterTheDelay() (gas: 398146)\n[PASS] test_millisecondStartIsRefusedWithoutReservingThePot() (gas: 219460)\n[PASS] test_nothingIsScheduledUntilTheGovernorOpensASeason() (gas: 66342)\n[PASS] test_openingNeedsNoticeAFixed13WeeksAndNoOverlap() (gas: 425201)\n[PASS] test_pausesAndSkips_theEarlierSeasonVestsRegardless() (gas: 853619)\n[PASS] test_printQuestionDocument() (gas: 229145)\nLogs:\n  0x7b22616e7377657254797065223a22626f6f6c222c22636861696e4964223a312c22646566696e6974696f6e73223a7b22666f726d756c61223a2254686520494e46455220706f696e747320666f726d756c6120666f72207468697320736561736f6e2c20697066733a2f2f6261666b7265696864776463656667683464716b6a763637757a636d77376f6a6565367865647a6465746f6a757a6a657674656e78717576796b75227d2c2265766964656e6365223a2270616e656c222c227175657374696f6e223a22446f6573207468652066696c6520617420697066733a2f2f6261666b726569676832616b69736361696c646371616273796733646672366368753366677072656769796d73636b376537617161347335327a7920676976652c20666f7220494e46455220706f696e747320736561736f6e20696e64657820302028756e69782074696d652031373238303120746f2038303335323031292c2065786163746c792074686520616c6c6f636174696f6e73207468617420646566696e6974696f6e732e666f726d756c612070726f64756365732066726f6d20457468657265756d206d61696e6e657420666f72207468617420706572696f642c2061732061207374616e64617264204d65726b6c6520747265652077686f7365206c656176657320617265206b656363616b323536286b656363616b323536286162692e656e636f646528736561736f6e2c206163636f756e742c20616d6f756e74292929207769746820726f6f742030786436633636636164303666653134666462366365393239376438306433326632346437343238393936643030343563626639306363333435633637376261313620616e6420616d6f756e74732073756d6d696e6720746f2031323330303030303030303030303030303030303020776569206f6620494e4645523f222c2276223a312c2277696e646f77223a7b2266726f6d426c6f636b223a3130302c22746f426c6f636b223a3230307d7d\n\n[PASS] test_reservedNeverExceedsTheBalance() (gas: 1037629)\n[PASS] test_rootCannotPromiseMoreThanThePot() (gas: 447668)\n[PASS] test_rootsMustNameTheRulesCommittedAtOpening() (gas: 638312)\n[PASS] test_startHorizonIsInclusiveAndRelativeToOpening() (gas: 323473)\n[PASS] test_theQuestionNamesTheSeasonsOwnDates() (gas: 462398)\n[PASS] test_theQuestionNamesTheSeasonsRules() (gas: 311598)\n[PASS] test_theUnassignedPartOfAPotIsReleasedAtTheRoot() (gas: 533544)\n[PASS] test_unregisteredLeavesAreReleasedAfterTheRegistrationDeadline() (gas: 863837)\n[PASS] test_vestingOfOneSeasonOverlapsTheNext() (gas: 1365048)\n[PASS] test_vestingRunsFromTheRootSoALateRootCostsNothing() (gas: 1063212)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 66.55ms (99.19ms CPU time)\n\nRan 11 test suites in 67.37ms (324.65ms CPU time): 76 tests passed, 0 failed, 2 skipped (78 total tests)\n","passed":true},{"durationMs":41,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FounderStream.release()\",\"InferDripper.drip()\",\"InferToken.approve(address,uint256)\",\"InferToken.transfer(address,uint256)\",\"InferToken.transferFrom(address,address,uint256)\",\"LegacyRedeemer.advanceClock(address,uint256)\",\"LegacyRedeemer.claim()\",\"LegacyRedeemer.redeem(address,uint256)\",\"LegacyRedeemer.releaseNft(address,uint256)\",\"LegacyRedeemer.sweep()\",\"SeasonVault.announceEnd()\",\"SeasonVault.applyProposal(uint256)\",\"SeasonVault.claim(uint256,address,uint256,bytes32[])\",\"SeasonVault.distribute((uint256,address,uint256,bytes32[])[])\",\"SeasonVault.endProgram()\",\"SeasonVault.expire(uint256)\",\"SeasonVault.openSeason(uint64,uint256,bytes32)\",\"SeasonVault.proposeRoot(uint256,bytes32,uint256,string)\",\"SeasonVault.release(uint256,address)\",\"SeasonVault.submitAttestedRoot(uint256,bytes32,uint256,string,string,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"SeasonVault.sweep()\",\"SeasonVault.withdrawEnd()\",\"SeasonVault.withdrawProposal(uint256)\",\"StakedInfer.approve(address,uint256)\",\"StakedInfer.deposit(uint256,address)\",\"StakedInfer.mint(uint256,address)\",\"StakedInfer.redeem(uint256,address,address)\",\"StakedInfer.transfer(address,uint256)\",\"StakedInfer.transferFrom(address,address,uint256)\",\"StakedInfer.withdraw(uint256,address,address)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":157,\"README.md\":68,\"foundry.toml\":16,\"remappings.txt\":4,\"src/FounderStream.sol\":64,\"src/InferDripper.sol\":86,\"src/InferToken.sol\":15,\"src/LegacyRedeemer.sol\":248,\"src/SeasonAttestation.sol\":231,\"src/SeasonVault.sol\":423,\"src/StakedInfer.sol\":68,\"src/base/ERC4626StaticAsset.sol\":303,\"src/base/LICENSE\":22,\"test/Buckets.t.sol\":862,\"test/LaunchConstructors.t.sol\":74,\"test/LaunchRehearsal.fork.t.sol\":206,\"test/LegacyRedeemer.t.sol\":398,\"test/StakingRegression.t.sol\":296},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":2193,"exitCode":0,"name":"slither","output":"[medium/high] erc20-interface at src/LegacyRedeemer.sol:12: IERC721Like (src/LegacyRedeemer.sol#11-13) has incorrect ERC20 function interface:IERC721Like.transferFrom(address,address,uint256) (src/LegacyRedeemer.sol#12)\n[medium/high] incorrect-equality at src/StakedInfer.sol:60: StakedInfer.lockedShares(address) (src/StakedInfer.sol#60-62) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/SeasonVault.sol:417: SeasonVault._vested(uint256,uint256) (src/SeasonVault.sol#417-422) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/LegacyRedeemer.sol:156: LegacyRedeemer.claim() (src/LegacyRedeemer.sol#156-163) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/LegacyRedeemer.sol:166: LegacyRedeemer.claimable(address) (src/LegacyRedeemer.sol#166-172) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/InferDripper.sol:51: InferDripper.releasable() (src/InferDripper.sol#51-60) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/FounderStream.sol:57: FounderStream.release() (src/FounderStream.sol#57-63) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/SeasonVault.sol:335: SeasonVault.endProgram() (src/SeasonVault.sol#335-342) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/SeasonVault.sol:406: SeasonVault._release(uint256,address) (src/SeasonVault.sol#406-415) uses a dangerous strict equality:\n[medium/medium] uninitialized-local at src/StakedInfer.sol:40: StakedInfer._update(address,address,uint256).held (src/StakedInfer.sol#40) is a local variable never initialized\n[medium/medium] uninitialized-local at src/SeasonAttestation.sol:217: SeasonAttestation._decimal(uint256).digits (src/SeasonAttestation.sol#217) is a local variable never initialized\n[low/medium] calls-loop at src/LegacyRedeemer.sol:216: LegacyRedeemer.advanceClock(IERC20,uint256) (src/LegacyRedeemer.sol#216-221) has external calls inside a loop: IBurnable(address(legacy)).burn(1) (src/LegacyRedeemer.sol#219)\n[low/medium] reentrancy-events at src/LegacyRedeemer.sol:134: Reentrancy in LegacyRedeemer.redeem(IERC20,uint256) (src/LegacyRedeemer.sol#134-153):\n[low/medium] reentrancy-events at src/LegacyRedeemer.sol:230: Reentrancy in LegacyRedeemer.releaseNft(address,uint256) (src/LegacyRedeemer.sol#230-234):\n[low/medium] timestamp at src/LegacyRedeemer.sol:175: LegacyRedeemer._addVesting(address,uint256) (src/LegacyRedeemer.sol#175-188) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:300: SeasonVault.expire(uint256) (src/SeasonVault.sol#300-317) uses timestamp for comparisons\n[low/medium] timestamp at src/LegacyRedeemer.sol:207: LegacyRedeemer.sweep() (src/LegacyRedeemer.sol#207-212) uses timestamp for comparisons\n[low/medium] timestamp at src/LegacyRedeemer.sol:230: LegacyRedeemer.releaseNft(address,uint256) (src/LegacyRedeemer.sol#230-234) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:406: SeasonVault._release(uint256,address) (src/SeasonVault.sol#406-415) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:150: SeasonVault.openSeason(uint64,uint256,bytes32) (src/SeasonVault.sol#150-166) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:417: SeasonVault._vested(uint256,uint256) (src/SeasonVault.sol#417-422) uses timestamp for comparisons\n[low/medium] timestamp at src/InferDripper.sol:71: InferDripper.drip() (src/InferDripper.sol#71-81) uses timestamp for comparisons\n[low/medium] timestamp at src/FounderStream.sol:57: FounderStream.release() (src/FounderStream.sol#57-63) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:249: SeasonVault.applyProposal(uint256) (src/SeasonVault.sol#249-254) uses timestamp for comparisons\n[low/medium] timestamp at src/FounderStream.sol:40: FounderStream.vested() (src/FounderStream.sol#40-45) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:374: SeasonVault._checkRootable(uint256,uint256) (src/SeasonVault.sol#374-380) uses timestamp for comparisons\n[low/medium] timestamp at src/InferDripper.sol:62: InferDripper.canDrip() (src/InferDripper.sol#62-64) uses timestamp for comparisons\n[low/medium] timestamp at src/FounderStream.sol:49: FounderStream.releasable() (src/FounderStream.sol#49-54) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:391: SeasonVault._register(uint256,address,uint256,bytes32[]) (src/SeasonVault.sol#391-404) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:185: SeasonVault.available() (src/SeasonVault.sol#185-188) uses timestamp for comparisons\n[low/medium] timestamp at src/InferDripper.sol:51: InferDripper.releasable() (src/InferDripper.sol#51-60) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:335: SeasonVault.endProgram() (src/SeasonVault.sol#335-342) uses timestamp for comparisons\n[low/medium] timestamp at src/LegacyRedeemer.sol:166: LegacyRedeemer.claimable(address) (src/LegacyRedeemer.sol#166-172) uses timestamp for comparisons\n[low/medium] timestamp at src/InferDripper.sol:66: InferDripper._ready(uint256) (src/InferDripper.sol#66-68) uses timestamp for comparisons\n[low/medium] timestamp at src/LegacyRedeemer.sol:134: LegacyRedeemer.redeem(IERC20,uint256) (src/LegacyRedeemer.sol#134-153) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:327: SeasonVault.withdrawEnd() (src/SeasonVault.sol#327-331) uses timestamp for comparisons\n[low/medium] timestamp at src/LegacyRedeemer.sol:156: LegacyRedeemer.claim() (src/LegacyRedeemer.sol#156-163) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonVault.sol:352: SeasonVault._sweep() (src/SeasonVault.sol#352-356) uses timestamp for comparisons\n[low/medium] timestamp at src/SeasonAttestation.sol:156: SeasonAttestation._verifyYes(SeasonAttestation.OracleAttestation,bytes,bytes32) (src/SeasonAttestation.sol#156-169) uses timestamp for comparisons","passed":true},{"durationMs":510,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/SeasonAttestation.sol:111: `abi.encodePacked()` Hash Collision (2 places)\n[high] reentrancy-state-change at src/InferDripper.sol:43: Reentrancy: State change after external call (2 places)\n[high] unsafe-casting at src/LegacyRedeemer.sol:185: Unsafe Casting of integers\n[low] costly-loop at src/SeasonVault.sol:280: Costly operations inside loop\n[low] ecrecover at src/SeasonAttestation.sol:211: `ecrecover` Signature Malleability\n[low] large-numeric-literal at src/InferDripper.sol:76: Large Numeric Literal\n[low] literal-instead-of-constant at src/LegacyRedeemer.sol:123: Literal Instead of Constant (9 places)\n[low] push-zero-opcode at src/base/ERC4626StaticAsset.sol:8: PUSH0 Opcode\n[low] require-revert-in-loop at src/SeasonAttestation.sol:176: Loop Contains `require`/`revert` (2 places)\n[low] unchecked-return at src/LegacyRedeemer.sol:217: Unchecked Return (5 places)\n[low] uninitialized-local-variable at src/LegacyRedeemer.sol:218: Uninitialized Local Variable (3 places)\n[low] unspecific-solidity-pragma at src/base/ERC4626StaticAsset.sol:8: Unspecific Solidity Pragma\n[low] unused-public-function at src/SeasonVault.sol:173: Public Function Not Used Internally (4 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"3f903f10cc563630d5e362c6c64c68b3e3ccece0800283800f7f6b00953a1ad7","verifiedTreeHash":"520c37eb41b6f4620de5d560019eadf7ffc2fca6","verifierVersion":"0.1.0+0e08a464"},{"checks":[{"durationMs":3544,"exitCode":0,"name":"build","output":"Compiling 58 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.42s\nCompiler run successful with warnings:\nWarning (2018): Function state mutability can be restricted to view\n   --> test/Buckets.t.sol:172:5:\n    |\n172 |     function test_neitherDeclaresAGenesisShare() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nWarning (2018): Function state mutability can be restricted to view\n   --> test/LegacyRedeemer.t.sol:145:5:\n    |\n145 |     function test_vest_quoteForSplitsNowAndLater() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/FounderStream.sol:36:17\n   │\n36 │         start = uint64(block.timestamp);\n   │                 ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint64' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:53:13\n   │\n53 │         if (elapsed > MAX_CATCHUP) elapsed = MAX_CATCHUP;\n   │             ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:56:13\n   │\n56 │         if (elapsed == MAX_CATCHUP && amount < minDrip) {\n   │             ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/SeasonAttestation.sol:111:16\n    │\n111 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n112 │ ┃             '{\"answerType\":\"bool\",\"chainId\":1,\"definitions\":{\"formula\":\"The INFER points formula for this season, ipfs://',\n113 │ ┃             formulaCid,\n114 │ ┃             '\"},\"evidence\":\"panel\",\"question\":\"Does the file at ipfs://',\n    ‡ ┃\n121 │ ┃             _decimal(to)\n122 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:56:39\n   │\n56 │         if (elapsed == MAX_CATCHUP && amount < minDrip) {\n   │                                       ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:122:20\n    │\n122 │         closesAt = uint64(block.timestamp + REDEMPTION_PERIOD);\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/SeasonAttestation.sol:126:16\n    │\n126 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n127 │ ┃             \"), exactly the allocations that definitions.formula produces from Ethereum mainnet for that period, \",\n128 │ ┃             \"as a standard Merkle tree whose leaves are keccak256(keccak256(abi.encode(season, account, amount))) \",\n129 │ ┃             \"with root 0x\",\n    ‡ ┃\n137 │ ┃             \"}}\"\n138 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:67:53\n   │\n67 │         return amount != 0 && (amount >= minDrip || block.timestamp - lastDripAt >= MAX_CATCHUP);\n   │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:42:13\n   │\n42 │         if (elapsed < CLIFF) return 0;\n   │             ━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:43:13\n   │\n43 │         if (elapsed >= DURATION) return genesisAllocation;\n   │             ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:51:13\n   │\n51 │         if (block.timestamp - start >= DURATION) return held;\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:135:13\n    │\n135 │         if (block.timestamp >= closesAt) revert Closed();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/LegacyRedeemer.sol:152:9\n    │\n152 │         emit Redeemed(address(legacy), msg.sender, amount, out);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:159:42\n    │\n159 │         vestingOf[msg.sender].claimed += uint128(amount);\n    │                                          ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:170:28\n    │\n170 │         uint256 unlocked = elapsed >= VEST_PERIOD ? v.total : uint256(v.total) * elapsed / VEST_PERIOD;\n    │                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonAttestation.sol:161:13\n    │\n161 │         if (block.timestamp > a.expiresAt) revert Expired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonAttestation.sol:162:13\n    │\n162 │         if (a.issuedAt > block.timestamp || a.issuedAt > a.expiresAt) revert InvalidTimestamp();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonAttestation.sol:178:96\n    │\n178 │             if (!((c >= \"a\" && c <= \"z\") || (c >= \"A\" && c <= \"Z\") || (c >= \"0\" && c <= \"9\"))) revert BadCid();\n    │                                                                                                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SeasonAttestation.sol:218:52\n    │\n218 │         for (uint256 v = value; v != 0; v /= 10) ++digits;\n    │                                                    ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonAttestation.sol:220:73\n    │\n220 │         for (uint256 v = value; v != 0; v /= 10) out[--digits] = bytes1(uint8(48 + (v % 10)));\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/base/ERC4626StaticAsset.sol:264:9\n    │\n264 │         emit Deposit(caller, receiver, assets, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/StakedInfer.sol:50:13\n   │\n50 │         if (held != 0) {\n   │             ━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/base/ERC4626StaticAsset.sol:287:9\n    │\n287 │         emit Withdraw(caller, receiver, owner, assets, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[arbitrary-send-erc20]: `transferFrom` uses an arbitrary `from`; require it to equal `msg.sender` or `address(this)`\n    ╭▸ src/base/ERC4626StaticAsset.sol:292:9\n    │\n292 │         SafeERC20.safeTransferFrom(IERC20(asset()), from, address(this), assets);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-erc20\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:154:13\n    │\n154 │         if (startsAt < block.timestamp + NOTICE) revert TooSoon(block.timestamp + NOTICE);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:155:13\n    │\n155 │         if (startsAt > block.timestamp + MAX_NOTICE) revert TooFar(block.timestamp + MAX_NOTICE);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:185:37\n    │\n185 │         vestingOf[holder] = Vesting(uint128(onSchedule), 0, uint64(block.timestamp));\n    │                                     ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:185:61\n    │\n185 │         vestingOf[holder] = Vesting(uint128(onSchedule), 0, uint64(block.timestamp));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:208:13\n    │\n208 │         if (block.timestamp < closesAt) revert StillOpen(closesAt);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/LegacyRedeemer.sol:219:13\n    │\n219 │             IBurnable(address(legacy)).burn(1);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:231:13\n    │\n231 │         if (block.timestamp < closesAt) revert StillOpen(closesAt);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/LegacyRedeemer.sol:233:9\n    │\n233 │         emit NftReleased(collection, tokenId, nftReturnTo);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:388:9\n    │\n388 │         emit RootSet(s, root, total, attested);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:207:9\n    │\n207 │         emit RootAttested(s, a.requestId, cid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:208:9\n    │\n208 │         emit FormulaRevealed(s, formulaCid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:237:71\n    │\n237 │         (x.pendingRoot, x.pendingTotal, x.pendingEta) = (root, total, uint64(block.timestamp + GOVERNOR_DELAY));\n    │                                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:252:13\n    │\n252 │         if (block.timestamp < x.pendingEta) revert TooEarly(x.pendingEta);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:414:9\n    │\n414 │         emit Released(s, account, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:403:9\n    │\n403 │         emit Registered(s, account, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:359:27\n    │\n359 │         if (s >= SEASONS) revert NoSuchSeason(s);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:365:27\n    │\n365 │         if (x.start == 0) revert NoSuchSeason(s);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:393:31\n    │\n393 │         if (x.rootSetAt == 0) revert NoRoot(s);\n    │                               ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:394:73\n    │\n394 │         if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:398:63\n    │\n398 │         if (!MerkleProof.verifyCalldata(proof, x.root, leaf)) revert BadProof();\n    │                                                               ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:400:33\n    │\n400 │         if (amount > remaining) revert OverRemainingTotal(amount, remaining);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:306:17\n    │\n306 │             if (block.timestamp < at) revert NotYet(at);\n    │                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:310:17\n    │\n310 │             if (block.timestamp < at) revert NotYet(at);\n    │                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:323:18\n    │\n323 │         endEta = uint64(block.timestamp + GOVERNOR_DELAY);\n    │                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:338:13\n    │\n338 │         if (block.timestamp < endEta) revert TooEarly(endEta);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:376:13\n    │\n376 │         if (block.timestamp < x.start + SEASON) revert SeasonNotOver(s);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:385:56\n    │\n385 │         (x.root, x.total, x.rootSetAt) = (root, total, uint64(block.timestamp));\n    │                                                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:394:26\n    │\n394 │         if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:419:26\n    │\n419 │         if (from == 0 || block.timestamp <= from) return 0;\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:421:16\n    │\n421 │         return elapsed >= SEASON ? amount : amount * elapsed / SEASON;\n    │                ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Buckets.t.sol:34:17\n   │\n34 │         vm.warp(block.timestamp + 30 days - 1);\n   │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:115:21\n    │\n115 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n290 │         vm.warp(seasons.endOf(s) - 48 hours);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n340 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n344 │         vm.warp(seasons.endOf(0) + 13 weeks);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n344 │         vm.warp(seasons.endOf(0) + 13 weeks);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:348:17\n    │\n348 │         vm.warp(block.timestamp + 365 days);\n    │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:364:17\n    │\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ────────━━━━━━━━━━━━━━━──────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n377 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n380 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n380 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n391 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n399 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n410 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n446 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n471 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n476 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n476 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n485 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n499 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n517 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n531 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n567 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:569:17\n    │\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n588 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n609 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n636 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n681 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n697 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:701:17\n    │\n701 │         vm.warp(block.timestamp + 48 hours);\n    │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n723 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n741 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:798:26\n    │\n798 │         uint256 latest = block.timestamp + 365 days;\n    │                          ━━━━━━━━━━━━━━━\n    ‡\n805 │         vm.warp(seasons.endOf(0) + 5 * 365 days); // long pauses are still permitted\n    │         ──────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/LegacyRedeemer.t.sol:89:17\n   │\n89 │         vm.warp(block.timestamp + 91 days / 2);\n   │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/LegacyRedeemer.t.sol:119:17\n    │\n119 │         vm.warp(block.timestamp + 91 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n   ╭▸ test/StakingRegression.t.sol:41:17\n   │\n41 │         vm.roll(block.number + 10);\n   │         ────────━━━━━━━━━━━━────── `vm.roll` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockNumber()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/StakingRegression.t.sol:151:17\n    │\n151 │         vm.roll(block.number + 1);\n    │         ────────━━━━━━━━━━━━───── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:217:21\n    │\n217 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:235:17\n    │\n235 │         vm.warp(block.timestamp + 1 hours - 1);\n    │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:239:17\n    │\n239 │         vm.warp(block.timestamp + 1);\n    │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:255:17\n    │\n255 │         vm.warp(block.timestamp + 30 minutes);\n    │         ────────━━━━━━━━━━━━━━━────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:260:17\n    │\n260 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:264:21\n    │\n264 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":147,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/LaunchRehearsal.fork.t.sol:LaunchRehearsalForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 199.53µs (0.00ns CPU time)\n\nRan 1 test for test/LegacyRedeemer.t.sol:LegacyRedeemerForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 217.33µs (0.00ns CPU time)\n\nRan 1 test for test/LaunchRehearsal.fork.t.sol:InferTokenTest\n[PASS] test_nameSymbolSupply() (gas: 46429)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 312.88µs (201.30µs CPU time)\n\nRan 3 tests for test/Buckets.t.sol:FounderStreamTest\n[PASS] test_allOfItAtAYearAndNothingStranded() (gas: 145522)\n[PASS] test_neverPaysMoreThanItHolds() (gas: 460372)\n[PASS] test_nothingBeforeTheCliffThenTheStraightLine() (gas: 211164)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 632.32µs (417.55µs CPU time)\n\nRan 1 test for test/Buckets.t.sol:GenesisSplitIntegrationTest\n[PASS] test_theBucketsAndTheTreasury() (gas: 8332251)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 768.26µs (482.15µs CPU time)\n\nRan 2 tests for test/LaunchConstructors.t.sol:LaunchConstructorsTest\n[PASS] test_stakingConstructorDoesNotReadTokenMetadata() (gas: 14792)\n[PASS] test_staticConstructorsCallOnlyTheEarlierVaultAsset() (gas: 182078)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 15.13ms (1.36ms CPU time)\n\nRan 5 tests for test/StakingRegression.t.sol:DripperRegressionTest\n[PASS] testFuzz_finalBalanceIsReleasedExactlyOnce(uint256) (runs: 512, μ: 249192, ~: 251025)\nLogs:\n  Bound result 447302738879375813419\n\n[PASS] test_floorStillRefusesAnEmptyVault() (gas: 175363)\n[PASS] test_fullHourIsRequiredForTheFloorAndFinalDust() (gas: 332786)\n[PASS] test_largeBufferKeepsItsNormalRateThenCrossesIntoTheFloor() (gas: 21473612)\n[PASS] test_smallBuybackReachesStakersAndDoesNotLeaveATail() (gas: 12330283)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 48.11ms (69.25ms CPU time)\n\nRan 17 tests for test/LegacyRedeemer.t.sol:LegacyRedeemerTest\n[PASS] test_afterOneYearClosedAndSweptToTheTreasury() (gas: 303291)\n[PASS] test_clockUpkeepNeedsDonatedDust() (gas: 346460)\n[PASS] test_constructorChecks() (gas: 10778)\n[PASS] test_dustThatRedeemsForNothingIsRefused() (gas: 33120)\n[PASS] test_eachTokenIsCappedAtItsOwnAllocation() (gas: 405225)\n[PASS] test_oneBucketForBothTokens() (gas: 27163)\n[PASS] test_onlyTheTwoTokens() (gas: 626356)\n[PASS] test_redeemsEachTokenAtItsRateAndBurnsIt() (gas: 335931)\n[PASS] test_theUnlockingNftReturnsAfterTheYear() (gas: 330312)\n[PASS] test_vest_aboveTheCapVestsLinearlyOverASeason() (gas: 434047)\n[PASS] test_vest_addingMidSeasonKeepsWhatWasAlreadyUnlocked() (gas: 360292)\n[PASS] test_vest_addingRestartsTheSeasonForEverythingUnvested() (gas: 377979)\n[PASS] test_vest_caps() (gas: 30080)\n[PASS] test_vest_eachTokenHasItsOwnCap() (gas: 407997)\n[PASS] test_vest_quoteForSplitsNowAndLater() (gas: 28146)\n[PASS] test_vest_smallRedemptionIsPaidAtOnce() (gas: 190060)\n[PASS] test_vest_sweepLeavesWhatIsOwedToVestingWallets() (gas: 408170)\nSuite result: ok. 17 passed; 0 failed; 0 skipped; finished in 55.51ms (4.45ms CPU time)\n\nRan 8 tests for test/StakingRegression.t.sol:StakingRegressionTest\n[PASS] testFuzz_matchesVendoredERC4626AfterTheHold(uint256,uint256,uint256) (runs: 512, μ: 2093176, ~: 2093231)\nLogs:\n  Bound result 66829067286186610703438\n  Bound result 230942\n  Bound result 549646879990863651204930617\n\n[PASS] testFuzz_movingSharesPreservesTheHold(uint256,uint256,uint256) (runs: 512, μ: 489889, ~: 465212)\nLogs:\n  Bound result 732929820079988833297\n  Bound result 510463245512436986110\n  Bound result 521826976549679767115831151\n\n[PASS] test_dustDepositDoesNotBlockOlderShares() (gas: 422848)\n[PASS] test_dustMintDoesNotBlockWithdrawOrDelegatedExit() (gas: 389264)\n[PASS] test_freshDepositToAnotherReceiverCannotBypassTheHold() (gas: 447030)\n[PASS] test_freshShareDustCannotHoldTheRecipientsOlderShares() (gas: 451319)\n[PASS] test_transfersSpendUnlockedSharesFirstAndCarryOnlyTheHeldPart() (gas: 543529)\n[PASS] test_zeroAndSelfTransfersCannotRefreshOrShedTheHold() (gas: 564503)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 55.47ms (82.67ms CPU time)\n\nRan 7 tests for test/Buckets.t.sol:StakingTest\n[PASS] test_aDepositRightBeforeADripGainsOnlyThatSlice() (gas: 492955)\n[PASS] test_inflationAttackDoesNotPay() (gas: 379085)\n[PASS] test_neitherDeclaresAGenesisShare() (gas: 13820)\n[PASS] test_neverDripsIntoAnEmptyVault() (gas: 91963)\n[PASS] test_oneCallReleasesAtMostAnHoursSliceOfTheBuffer() (gas: 355298)\n[PASS] test_stakersEarnByTimeHeldAndTheBufferDrainsOverAboutAWeek() (gas: 10405960)\n[PASS] test_theHoldTravelsWithSharesAndCannotBeUsedToGrief() (gas: 477204)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 56.89ms (4.94ms CPU time)\n\nRan 32 tests for test/Buckets.t.sol:SeasonVaultTest\n[PASS] testFuzz_registrationConservesTheDeclaredTotal(uint256,uint256,uint256) (runs: 512, μ: 874409, ~: 891607)\nLogs:\n  Bound result 77066829067286186610703438\n  Bound result 230942\n  Bound result 49646872150314555068639044\n\n[PASS] test_aCidThatWouldBreakTheJsonIsRefused() (gas: 327055)\n[PASS] test_aForgedLeafIsRefused() (gas: 544536)\n[PASS] test_aNoOrAThinPanelOrAStrangersSignatureIsRefused() (gas: 851766)\n[PASS] test_aSeasonNeedsARulesCommitmentAndItHidesTheDocument() (gas: 209318)\n[PASS] test_aSeasonWhoseRootNeverComesIsReleasedWhole() (gas: 311261)\n[PASS] test_aYesToAnotherQuestionIsRefused() (gas: 1005746)\n[PASS] test_anAnnouncedEndCanBeWithdrawn() (gas: 246138)\n[PASS] test_anExpiredOrReplayedYesIsRefused() (gas: 997841)\n[PASS] test_atMostFiveSeasons() (gas: 690197)\n[PASS] test_attestedLeavesAreCappedCumulativelyEvenAfterPayment() (gas: 988258)\n[PASS] test_distributeCannotRegisterBeyondTotalAndRollsBackTheBatch() (gas: 739512)\n[PASS] test_distributePushesABatch() (gas: 938764)\n[PASS] test_endingSendsOnlyWhatIsAvailableAndOwedClaimsStillPay() (gas: 1083721)\n[PASS] test_governorFallbackWaits48hAndTheAttestorCanStillWin() (gas: 729458)\n[PASS] test_governorLeafAboveTotalCannotRaidAnotherSeason() (gas: 707394)\n[PASS] test_governorRootAppliesAfterTheDelay() (gas: 398146)\n[PASS] test_millisecondStartIsRefusedWithoutReservingThePot() (gas: 219460)\n[PASS] test_nothingIsScheduledUntilTheGovernorOpensASeason() (gas: 66342)\n[PASS] test_openingNeedsNoticeAFixed13WeeksAndNoOverlap() (gas: 425201)\n[PASS] test_pausesAndSkips_theEarlierSeasonVestsRegardless() (gas: 853619)\n[PASS] test_printQuestionDocument() (gas: 229145)\nLogs:\n  0x7b22616e7377657254797065223a22626f6f6c222c22636861696e4964223a312c22646566696e6974696f6e73223a7b22666f726d756c61223a2254686520494e46455220706f696e747320666f726d756c6120666f72207468697320736561736f6e2c20697066733a2f2f6261666b7265696864776463656667683464716b6a763637757a636d77376f6a6565367865647a6465746f6a757a6a657674656e78717576796b75227d2c2265766964656e6365223a2270616e656c222c227175657374696f6e223a22446f6573207468652066696c6520617420697066733a2f2f6261666b726569676832616b69736361696c646371616273796733646672366368753366677072656769796d73636b376537617161347335327a7920676976652c20666f7220494e46455220706f696e747320736561736f6e20696e64657820302028756e69782074696d652031373238303120746f2038303335323031292c2065786163746c792074686520616c6c6f636174696f6e73207468617420646566696e6974696f6e732e666f726d756c612070726f64756365732066726f6d20457468657265756d206d61696e6e657420666f72207468617420706572696f642c2061732061207374616e64617264204d65726b6c6520747265652077686f7365206c656176657320617265206b656363616b323536286b656363616b323536286162692e656e636f646528736561736f6e2c206163636f756e742c20616d6f756e74292929207769746820726f6f742030786436633636636164303666653134666462366365393239376438306433326632346437343238393936643030343563626639306363333435633637376261313620616e6420616d6f756e74732073756d6d696e6720746f2031323330303030303030303030303030303030303020776569206f6620494e4645523f222c2276223a312c2277696e646f77223a7b2266726f6d426c6f636b223a3130302c22746f426c6f636b223a3230307d7d\n\n[PASS] test_reservedNeverExceedsTheBalance() (gas: 1037629)\n[PASS] test_rootCannotPromiseMoreThanThePot() (gas: 447668)\n[PASS] test_rootsMustNameTheRulesCommittedAtOpening() (gas: 638312)\n[PASS] test_startHorizonIsInclusiveAndRelativeToOpening() (gas: 323473)\n[PASS] test_theQuestionNamesTheSeasonsOwnDates() (gas: 462398)\n[PASS] test_theQuestionNamesTheSeasonsRules() (gas: 311598)\n[PASS] test_theUnassignedPartOfAPotIsReleasedAtTheRoot() (gas: 533544)\n[PASS] test_unregisteredLeavesAreReleasedAfterTheRegistrationDeadline() (gas: 863837)\n[PASS] test_vestingOfOneSeasonOverlapsTheNext() (gas: 1365048)\n[PASS] test_vestingRunsFromTheRootSoALateRootCostsNothing() (gas: 1063212)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 59.37ms (97.81ms CPU time)\n\nRan 11 test suites in 60.03ms (292.61ms CPU time): 76 tests passed, 0 failed, 2 skipped (78 total tests)\n","passed":true},{"durationMs":45,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FounderStream.release()\",\"InferDripper.drip()\",\"InferToken.approve(address,uint256)\",\"InferToken.transfer(address,uint256)\",\"InferToken.transferFrom(address,address,uint256)\",\"LegacyRedeemer.advanceClock(address,uint256)\",\"LegacyRedeemer.claim()\",\"LegacyRedeemer.redeem(address,uint256)\",\"LegacyRedeemer.releaseNft(address,uint256)\",\"LegacyRedeemer.sweep()\",\"SeasonVault.announceEnd()\",\"SeasonVault.applyProposal(uint256)\",\"SeasonVault.claim(uint256,address,uint256,bytes32[])\",\"SeasonVault.distribute((uint256,address,uint256,bytes32[])[])\",\"SeasonVault.endProgram()\",\"SeasonVault.expire(uint256)\",\"SeasonVault.openSeason(uint64,uint256,bytes32)\",\"SeasonVault.proposeRoot(uint256,bytes32,uint256,string)\",\"SeasonVault.release(uint256,address)\",\"SeasonVault.submitAttestedRoot(uint256,bytes32,uint256,string,string,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"SeasonVault.sweep()\",\"SeasonVault.withdrawEnd()\",\"SeasonVault.withdrawProposal(uint256)\",\"StakedInfer.approve(address,uint256)\",\"StakedInfer.deposit(uint256,address)\",\"StakedInfer.mint(uint256,address)\",\"StakedInfer.redeem(uint256,address,address)\",\"StakedInfer.transfer(address,uint256)\",\"StakedInfer.transferFrom(address,address,uint256)\",\"StakedInfer.withdraw(uint256,address,address)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":157,\"README.md\":68,\"foundry.toml\":16,\"launch.json\":68,\"remappings.txt\":4,\"src/FounderStream.sol\":64,\"src/InferDripper.sol\":86,\"src/InferToken.sol\":15,\"src/LegacyRedeemer.sol\":248,\"src/SeasonAttestation.sol\":231,\"src/SeasonVault.sol\":423,\"src/StakedInfer.sol\":68,\"src/base/ERC4626StaticAsset.sol\":303,\"src/base/LICENSE\":22,\"test/Buckets.t.sol\":862,\"test/LaunchConstructors.t.sol\":74,\"test/LaunchRehearsal.fork.t.sol\":206,\"test/LegacyRedeemer.t.sol\":398,\"test/StakingRegression.t.sol\":296},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"9b4271205a0d13943ca9fd170a55403325ea4b590c1e53abcef2c430191ebbe7","verifiedTreeHash":"a8ee022f82c46e72d84487293944c2bf2f54058e","verifierVersion":"0.1.0+0e08a464"},{"checks":[{"durationMs":4070,"exitCode":0,"name":"build","output":"Compiling 59 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.94s\nCompiler run successful with warnings:\nWarning (2018): Function state mutability can be restricted to view\n   --> test/Buckets.t.sol:172:5:\n    |\n172 |     function test_neitherDeclaresAGenesisShare() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nWarning (2018): Function state mutability can be restricted to view\n   --> test/LegacyRedeemer.t.sol:165:5:\n    |\n165 |     function test_vest_quoteForSplitsNowAndLater() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/FounderStream.sol:36:17\n   │\n36 │         start = uint64(block.timestamp);\n   │                 ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint64' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:54:13\n   │\n54 │         if (elapsed > MAX_CATCHUP) elapsed = MAX_CATCHUP;\n   │             ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:57:13\n   │\n57 │         if (elapsed == MAX_CATCHUP && amount < minDrip) {\n   │             ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:57:39\n   │\n57 │         if (elapsed == MAX_CATCHUP && amount < minDrip) {\n   │                                       ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:42:13\n   │\n42 │         if (elapsed < CLIFF) return 0;\n   │             ━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:43:13\n   │\n43 │         if (elapsed >= DURATION) return genesisAllocation;\n   │             ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:126:20\n    │\n126 │         closesAt = uint64(block.timestamp + REDEMPTION_PERIOD);\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:68:53\n   │\n68 │         return amount != 0 && (amount >= minDrip || block.timestamp - lastDripAt >= MAX_CATCHUP);\n   │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:51:13\n   │\n51 │         if (block.timestamp - start >= DURATION) return held;\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:139:13\n    │\n139 │         if (block.timestamp >= closesAt) revert Closed();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/LegacyRedeemer.sol:156:9\n    │\n156 │         emit Redeemed(address(legacy), msg.sender, amount, out);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:163:42\n    │\n163 │         vestingOf[msg.sender].claimed += uint128(amount);\n    │                                          ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:174:28\n    │\n174 │         uint256 unlocked = elapsed >= VEST_PERIOD ? v.total : uint256(v.total) * elapsed / VEST_PERIOD;\n    │                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:156:13\n    │\n156 │         if (startsAt < block.timestamp + NOTICE) revert TooSoon(block.timestamp + NOTICE);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:189:37\n    │\n189 │         vestingOf[holder] = Vesting(uint128(onSchedule), 0, uint64(block.timestamp));\n    │                                     ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:189:61\n    │\n189 │         vestingOf[holder] = Vesting(uint128(onSchedule), 0, uint64(block.timestamp));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/base/ERC4626StaticAsset.sol:264:9\n    │\n264 │         emit Deposit(caller, receiver, assets, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:157:13\n    │\n157 │         if (startsAt > block.timestamp + MAX_NOTICE) revert TooFar(block.timestamp + MAX_NOTICE);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:216:13\n    │\n216 │         if (block.timestamp < closesAt) revert StillOpen(closesAt);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/StakedInfer.sol:50:13\n   │\n50 │         if (held != 0) {\n   │             ━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/base/ERC4626StaticAsset.sol:287:9\n    │\n287 │         emit Withdraw(caller, receiver, owner, assets, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/LegacyRedeemer.sol:229:35\n    │\n229 │                 (, uint256 end) = IMoonClock(address(legacy)).moonConfig();\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/LegacyRedeemer.sol:232:13\n    │\n232 │             IBurnable(address(legacy)).burn(1);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[arbitrary-send-erc20]: `transferFrom` uses an arbitrary `from`; require it to equal `msg.sender` or `address(this)`\n    ╭▸ src/base/ERC4626StaticAsset.sol:292:9\n    │\n292 │         SafeERC20.safeTransferFrom(IERC20(asset()), from, address(this), assets);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-erc20\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/LegacyRedeemer.sol:229:35\n    │\n229 │                 (, uint256 end) = IMoonClock(address(legacy)).moonConfig();\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/SeasonAttestation.sol:115:16\n    │\n115 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n116 │ ┃             '{\"answerType\":\"bool\",\"chainId\":1,\"definitions\":{\"formula\":\"The INFER points formula for this season, ipfs://',\n117 │ ┃             formulaCid,\n118 │ ┃             '\"},\"evidence\":\"panel\",\"question\":\"Does the file at ipfs://',\n    ‡ ┃\n125 │ ┃             _decimal(to)\n126 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:244:13\n    │\n244 │         if (block.timestamp < closesAt) revert StillOpen(closesAt);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/LegacyRedeemer.sol:246:9\n    │\n246 │         emit NftReleased(collection, tokenId, nftReturnTo);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/SeasonAttestation.sol:130:16\n    │\n130 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n131 │ ┃             \"), exactly the allocations that definitions.formula produces from Ethereum mainnet for that period, \",\n132 │ ┃             \"as a standard Merkle tree whose leaves are keccak256(keccak256(abi.encode(season, account, amount))) \",\n133 │ ┃             \"with root 0x\",\n    ‡ ┃\n141 │ ┃             \"}}\"\n142 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonAttestation.sol:165:13\n    │\n165 │         if (block.timestamp > a.expiresAt) revert Expired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonAttestation.sol:166:13\n    │\n166 │         if (a.issuedAt > block.timestamp || a.issuedAt > a.expiresAt) revert InvalidTimestamp();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonAttestation.sol:184:96\n    │\n184 │             if (!((c >= \"a\" && c <= \"z\") || (c >= \"A\" && c <= \"Z\") || (c >= \"0\" && c <= \"9\"))) revert BadCid();\n    │                                                                                                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SeasonAttestation.sol:227:15\n    │\n227 │             ++digits;\n    │               ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonAttestation.sol:231:36\n    │\n231 │             out[--digits] = bytes1(uint8(48 + (v % 10)));\n    │                                    ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:392:9\n    │\n392 │         emit RootSet(s, root, total, attested);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:209:9\n    │\n209 │         emit RootAttested(s, a.requestId, cid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:210:9\n    │\n210 │         emit FormulaRevealed(s, formulaCid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:238:13\n    │\n238 │         if (block.timestamp + GOVERNOR_DELAY >= endOf(s) + ROOT_DEADLINE) revert PastDeadline(s);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:240:71\n    │\n240 │         (x.pendingRoot, x.pendingTotal, x.pendingEta) = (root, total, uint64(block.timestamp + GOVERNOR_DELAY));\n    │                                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:255:13\n    │\n255 │         if (block.timestamp < x.pendingEta) revert TooEarly(x.pendingEta);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:418:9\n    │\n418 │         emit Released(s, account, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:407:9\n    │\n407 │         emit Registered(s, account, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:362:27\n    │\n362 │         if (s >= SEASONS) revert NoSuchSeason(s);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:368:27\n    │\n368 │         if (x.start == 0) revert NoSuchSeason(s);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:399:31\n    │\n399 │         if (x.rootSetAt == 0) revert NoRoot(s);\n    │                               ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:400:73\n    │\n400 │         if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:402:63\n    │\n402 │         if (!MerkleProof.verifyCalldata(proof, x.root, leaf)) revert BadProof();\n    │                                                               ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:404:33\n    │\n404 │         if (amount > remaining) revert OverRemainingTotal(amount, remaining);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:309:17\n    │\n309 │             if (block.timestamp < at) revert NotYet(at);\n    │                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:313:17\n    │\n313 │             if (block.timestamp < at) revert NotYet(at);\n    │                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:326:18\n    │\n326 │         endEta = uint64(block.timestamp + GOVERNOR_DELAY);\n    │                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:341:13\n    │\n341 │         if (block.timestamp < endEta) revert TooEarly(endEta);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:379:13\n    │\n379 │         if (block.timestamp < x.start + SEASON) revert SeasonNotOver(s);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:382:13\n    │\n382 │         if (block.timestamp >= x.start + SEASON + ROOT_DEADLINE) revert PastDeadline(s);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:389:56\n    │\n389 │         (x.root, x.total, x.rootSetAt) = (root, total, uint64(block.timestamp));\n    │                                                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:400:26\n    │\n400 │         if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:423:26\n    │\n423 │         if (from == 0 || block.timestamp <= from) return 0;\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:425:16\n    │\n425 │         return elapsed >= SEASON ? amount : amount * elapsed / SEASON;\n    │                ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Buckets.t.sol:34:17\n   │\n34 │         vm.warp(block.timestamp + 30 days - 1);\n   │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:115:21\n    │\n115 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n290 │         vm.warp(seasons.endOf(s) - 48 hours);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n340 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n344 │         vm.warp(seasons.endOf(0) + 13 weeks);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n344 │         vm.warp(seasons.endOf(0) + 13 weeks);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:348:17\n    │\n348 │         vm.warp(block.timestamp + 365 days);\n    │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:364:17\n    │\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ────────━━━━━━━━━━━━━━━──────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n377 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n380 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n380 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n391 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n399 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n410 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n446 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n471 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n476 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n476 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n485 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n499 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n517 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n531 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n567 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:569:17\n    │\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n588 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n609 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n636 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n681 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n697 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:701:17\n    │\n701 │         vm.warp(block.timestamp + 48 hours);\n    │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n723 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n741 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:798:26\n    │\n798 │         uint256 latest = block.timestamp + 365 days;\n    │                          ━━━━━━━━━━━━━━━\n    ‡\n805 │         vm.warp(seasons.endOf(0) + 5 * 365 days); // long pauses are still permitted\n    │         ──────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/LegacyRedeemer.t.sol:109:17\n    │\n109 │         vm.warp(block.timestamp + 91 days / 2);\n    │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/LegacyRedeemer.t.sol:139:17\n    │\n139 │         vm.warp(block.timestamp + 91 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Revision.t.sol:120:33\n    │\n120 │         vault.openSeason(uint64(block.timestamp + 48 hours), POT, keccak256(bytes(CID)));\n    │                                 ━━━━━━━━━━━━━━━\n    ‡\n200 │         vm.warp(deadline);\n    │         ───────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Revision.t.sol:120:33\n    │\n120 │         vault.openSeason(uint64(block.timestamp + 48 hours), POT, keccak256(bytes(CID)));\n    │                                 ━━━━━━━━━━━━━━━\n    ‡\n212 │         vm.warp(deadline - 1);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Revision.t.sol:120:33\n    │\n120 │         vault.openSeason(uint64(block.timestamp + 48 hours), POT, keccak256(bytes(CID)));\n    │                                 ━━━━━━━━━━━━━━━\n    ‡\n228 │         vm.warp(vault.endOf(0));\n    │         ─────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Revision.t.sol:267:17\n    │\n267 │         vm.warp(block.timestamp + 100 * 365 days);\n    │         ────────━━━━━━━━━━━━━━━────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n   ╭▸ test/StakingRegression.t.sol:41:17\n   │\n41 │         vm.roll(block.number + 10);\n   │         ────────━━━━━━━━━━━━────── `vm.roll` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockNumber()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/StakingRegression.t.sol:151:17\n    │\n151 │         vm.roll(block.number + 1);\n    │         ────────━━━━━━━━━━━━───── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:217:21\n    │\n217 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:235:17\n    │\n235 │         vm.warp(block.timestamp + 1 hours - 1);\n    │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:239:17\n    │\n239 │         vm.warp(block.timestamp + 1);\n    │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:255:17\n    │\n255 │         vm.warp(block.timestamp + 30 minutes);\n    │         ────────━━━━━━━━━━━━━━━────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:260:17\n    │\n260 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:264:21\n    │\n264 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":152,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/LegacyRedeemer.t.sol:LegacyRedeemerForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 269.04µs (0.00ns CPU time)\n\nRan 1 test for test/LaunchRehearsal.fork.t.sol:LaunchRehearsalForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 264.60µs (0.00ns CPU time)\n\nRan 1 test for test/LaunchRehearsal.fork.t.sol:InferTokenTest\n[PASS] test_nameSymbolSupply() (gas: 46429)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 330.35µs (227.50µs CPU time)\n\nRan 1 test for test/Buckets.t.sol:GenesisSplitIntegrationTest\n[PASS] test_theBucketsAndTheTreasury() (gas: 8407306)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 755.68µs (457.36µs CPU time)\n\nRan 2 tests for test/LaunchConstructors.t.sol:LaunchConstructorsTest\n[PASS] test_stakingConstructorDoesNotReadTokenMetadata() (gas: 14792)\n[PASS] test_staticConstructorsCallOnlyTheEarlierVaultAsset() (gas: 183255)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.67ms (1.10ms CPU time)\n\nRan 3 tests for test/Buckets.t.sol:FounderStreamTest\n[PASS] test_allOfItAtAYearAndNothingStranded() (gas: 145522)\n[PASS] test_neverPaysMoreThanItHolds() (gas: 460372)\n[PASS] test_nothingBeforeTheCliffThenTheStraightLine() (gas: 211164)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 6.39ms (466.58µs CPU time)\n\nRan 3 tests for test/LegacyRedeemer.t.sol:ClockUpkeepRevisionTest\n[PASS] test_catchupStopsWhenCurrentAndLaterWindowsStillAdvance() (gas: 197330)\n[PASS] test_currentClockCannotConsumeDustEvenWithRepeatedCalls() (gas: 82523)\n[PASS] test_stepsStillLimitsCatchupAndZeroDoesNothing() (gas: 110173)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 26.25ms (400.15µs CPU time)\n\nRan 18 tests for test/LegacyRedeemer.t.sol:LegacyRedeemerTest\n[PASS] test_afterOneYearClosedAndSweptToTheTreasury() (gas: 303291)\n[PASS] test_clockUpkeepNeedsDonatedDust() (gas: 1206206)\n[PASS] test_constructorChecks() (gas: 10778)\n[PASS] test_dustThatRedeemsForNothingIsRefused() (gas: 33142)\n[PASS] test_eachTokenIsCappedAtItsOwnAllocation() (gas: 405225)\n[PASS] test_immediateCapIsPerWalletAtTheLaunchRate() (gas: 2772157)\n[PASS] test_oneBucketForBothTokens() (gas: 27163)\n[PASS] test_onlyTheTwoTokens() (gas: 626378)\n[PASS] test_redeemsEachTokenAtItsRateAndBurnsIt() (gas: 335866)\n[PASS] test_theUnlockingNftReturnsAfterTheYear() (gas: 330312)\n[PASS] test_vest_aboveTheCapVestsLinearlyOverASeason() (gas: 434047)\n[PASS] test_vest_addingMidSeasonKeepsWhatWasAlreadyUnlocked() (gas: 360292)\n[PASS] test_vest_addingRestartsTheSeasonForEverythingUnvested() (gas: 377979)\n[PASS] test_vest_caps() (gas: 30080)\n[PASS] test_vest_eachTokenHasItsOwnCap() (gas: 407997)\n[PASS] test_vest_quoteForSplitsNowAndLater() (gas: 28146)\n[PASS] test_vest_smallRedemptionIsPaidAtOnce() (gas: 190082)\n[PASS] test_vest_sweepLeavesWhatIsOwedToVestingWallets() (gas: 408170)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 26.77ms (3.62ms CPU time)\n\nRan 9 tests for test/Revision.t.sol:SeasonRevisionTest\n[PASS] test_attestedRootCannotRaceExpiryAtDeadline() (gas: 548732)\n[PASS] test_governorCanEndWithoutOpeningAnySeason() (gas: 121724)\n[PASS] test_governorProposalNeedsTimeToMatureBeforeExpiry() (gas: 280786)\n[PASS] test_lastTimelyAttestedRootIsImmediateAndFinal() (gas: 699010)\n[PASS] test_lastTimelyGovernorProposalCanApply() (gas: 399555)\n[PASS] test_registeredClaimStillPaysAfterExpiryWithoutReregistering() (gas: 683058)\n[PASS] test_registeredClaimStillPaysAtRegistrationDeadline() (gas: 669185)\n[PASS] test_timeAloneCannotReleaseThePointsAllocation() (gas: 127482)\n[PASS] test_unappliedProposalCannotRaceExpiryAtDeadline() (gas: 417466)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 50.20ms (4.67ms CPU time)\n\nRan 4 tests for test/Revision.t.sol:DripperDustRevisionTest\n[PASS] testFuzz_virtualCaptureIsBounded(uint256,uint256) (runs: 512, μ: 323273, ~: 323649)\nLogs:\n  Bound result 8065454564613515869484696570\n  Bound result 757066580417417752329314\n\n[PASS] test_dustVaultResumesAfterRealStakeArrives() (gas: 537854)\n[PASS] test_oneWeiCannotEnableDripsEvenAfterADonation() (gas: 443044)\n[PASS] test_thresholdBoundaryAndWithdrawalsRecheckTheGuard() (gas: 605774)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 57.41ms (57.92ms CPU time)\n\nRan 5 tests for test/StakingRegression.t.sol:DripperRegressionTest\n[PASS] testFuzz_finalBalanceIsReleasedExactlyOnce(uint256) (runs: 512, μ: 249831, ~: 251220)\nLogs:\n  Bound result 338\n\n[PASS] test_floorStillRefusesAnEmptyVault() (gas: 175370)\n[PASS] test_fullHourIsRequiredForTheFloorAndFinalDust() (gas: 332829)\n[PASS] test_largeBufferKeepsItsNormalRateThenCrossesIntoTheFloor() (gas: 21474799)\n[PASS] test_smallBuybackReachesStakersAndDoesNotLeaveATail() (gas: 12333490)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 61.41ms (39.95ms CPU time)\n\nRan 8 tests for test/StakingRegression.t.sol:StakingRegressionTest\n[PASS] testFuzz_matchesVendoredERC4626AfterTheHold(uint256,uint256,uint256) (runs: 512, μ: 2093067, ~: 2093195)\nLogs:\n  Bound result 262\n  Bound result 4896\n  Bound result 240\n\n[PASS] testFuzz_movingSharesPreservesTheHold(uint256,uint256,uint256) (runs: 512, μ: 490581, ~: 465142)\nLogs:\n  Bound result 262\n  Bound result 4896\n  Bound result 240\n\n[PASS] test_dustDepositDoesNotBlockOlderShares() (gas: 422848)\n[PASS] test_dustMintDoesNotBlockWithdrawOrDelegatedExit() (gas: 389264)\n[PASS] test_freshDepositToAnotherReceiverCannotBypassTheHold() (gas: 447030)\n[PASS] test_freshShareDustCannotHoldTheRecipientsOlderShares() (gas: 451319)\n[PASS] test_transfersSpendUnlockedSharesFirstAndCarryOnlyTheHeldPart() (gas: 543529)\n[PASS] test_zeroAndSelfTransfersCannotRefreshOrShedTheHold() (gas: 564503)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 61.48ms (87.71ms CPU time)\n\nRan 7 tests for test/Buckets.t.sol:StakingTest\n[PASS] test_aDepositRightBeforeADripGainsOnlyThatSlice() (gas: 492959)\n[PASS] test_inflationAttackDoesNotPay() (gas: 379085)\n[PASS] test_neitherDeclaresAGenesisShare() (gas: 13820)\n[PASS] test_neverDripsIntoAnEmptyVault() (gas: 91967)\n[PASS] test_oneCallReleasesAtMostAnHoursSliceOfTheBuffer() (gas: 355328)\n[PASS] test_stakersEarnByTimeHeldAndTheBufferDrainsOverAboutAWeek() (gas: 10406632)\n[PASS] test_theHoldTravelsWithSharesAndCannotBeUsedToGrief() (gas: 477204)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 65.86ms (7.01ms CPU time)\n\nRan 32 tests for test/Buckets.t.sol:SeasonVaultTest\n[PASS] testFuzz_registrationConservesTheDeclaredTotal(uint256,uint256,uint256) (runs: 512, μ: 873084, ~: 892786)\nLogs:\n  Bound result 90317768700741916989336197\n  Bound result 57839162838909901342374784\n  Bound result 82553130066121022091275858\n\n[PASS] test_aCidThatWouldBreakTheJsonIsRefused() (gas: 327055)\n[PASS] test_aForgedLeafIsRefused() (gas: 544838)\n[PASS] test_aNoOrAThinPanelOrAStrangersSignatureIsRefused() (gas: 851766)\n[PASS] test_aSeasonNeedsARulesCommitmentAndItHidesTheDocument() (gas: 209318)\n[PASS] test_aSeasonWhoseRootNeverComesIsReleasedWhole() (gas: 311261)\n[PASS] test_aYesToAnotherQuestionIsRefused() (gas: 1005746)\n[PASS] test_anAnnouncedEndCanBeWithdrawn() (gas: 246138)\n[PASS] test_anExpiredOrReplayedYesIsRefused() (gas: 998140)\n[PASS] test_atMostFiveSeasons() (gas: 690197)\n[PASS] test_attestedLeavesAreCappedCumulativelyEvenAfterPayment() (gas: 988023)\n[PASS] test_distributeCannotRegisterBeyondTotalAndRollsBackTheBatch() (gas: 739817)\n[PASS] test_distributePushesABatch() (gas: 939069)\n[PASS] test_endingSendsOnlyWhatIsAvailableAndOwedClaimsStillPay() (gas: 1084023)\n[PASS] test_governorFallbackWaits48hAndTheAttestorCanStillWin() (gas: 730737)\n[PASS] test_governorLeafAboveTotalCannotRaidAnotherSeason() (gas: 708676)\n[PASS] test_governorRootAppliesAfterTheDelay() (gas: 399425)\n[PASS] test_millisecondStartIsRefusedWithoutReservingThePot() (gas: 219460)\n[PASS] test_nothingIsScheduledUntilTheGovernorOpensASeason() (gas: 66342)\n[PASS] test_openingNeedsNoticeAFixed13WeeksAndNoOverlap() (gas: 425201)\n[PASS] test_pausesAndSkips_theEarlierSeasonVestsRegardless() (gas: 853921)\n[PASS] test_printQuestionDocument() (gas: 229145)\nLogs:\n  0x7b22616e7377657254797065223a22626f6f6c222c22636861696e4964223a312c22646566696e6974696f6e73223a7b22666f726d756c61223a2254686520494e46455220706f696e747320666f726d756c6120666f72207468697320736561736f6e2c20697066733a2f2f6261666b7265696864776463656667683464716b6a763637757a636d77376f6a6565367865647a6465746f6a757a6a657674656e78717576796b75227d2c2265766964656e6365223a2270616e656c222c227175657374696f6e223a22446f6573207468652066696c6520617420697066733a2f2f6261666b726569676832616b69736361696c646371616273796733646672366368753366677072656769796d73636b376537617161347335327a7920676976652c20666f7220494e46455220706f696e747320736561736f6e20696e64657820302028756e69782074696d652031373238303120746f2038303335323031292c2065786163746c792074686520616c6c6f636174696f6e73207468617420646566696e6974696f6e732e666f726d756c612070726f64756365732066726f6d20457468657265756d206d61696e6e657420666f72207468617420706572696f642c2061732061207374616e64617264204d65726b6c6520747265652077686f7365206c656176657320617265206b656363616b323536286b656363616b323536286162692e656e636f646528736561736f6e2c206163636f756e742c20616d6f756e74292929207769746820726f6f742030786436633636636164303666653134666462366365393239376438306433326632346437343238393936643030343563626639306363333435633637376261313620616e6420616d6f756e74732073756d6d696e6720746f2031323330303030303030303030303030303030303020776569206f6620494e4645523f222c2276223a312c2277696e646f77223a7b2266726f6d426c6f636b223a3130302c22746f426c6f636b223a3230307d7d\n\n[PASS] test_reservedNeverExceedsTheBalance() (gas: 1037934)\n[PASS] test_rootCannotPromiseMoreThanThePot() (gas: 447967)\n[PASS] test_rootsMustNameTheRulesCommittedAtOpening() (gas: 638611)\n[PASS] test_startHorizonIsInclusiveAndRelativeToOpening() (gas: 323473)\n[PASS] test_theQuestionNamesTheSeasonsOwnDates() (gas: 462398)\n[PASS] test_theQuestionNamesTheSeasonsRules() (gas: 311598)\n[PASS] test_theUnassignedPartOfAPotIsReleasedAtTheRoot() (gas: 533843)\n[PASS] test_unregisteredLeavesAreReleasedAfterTheRegistrationDeadline() (gas: 866420)\n[PASS] test_vestingOfOneSeasonOverlapsTheNext() (gas: 1365652)\n[PASS] test_vestingRunsFromTheRootSoALateRootCostsNothing() (gas: 1063514)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 65.90ms (98.38ms CPU time)\n\nRan 14 test suites in 66.99ms (424.95ms CPU time): 93 tests passed, 0 failed, 2 skipped (95 total tests)\n","passed":true},{"durationMs":47,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FounderStream.release()\",\"InferDripper.drip()\",\"InferToken.approve(address,uint256)\",\"InferToken.transfer(address,uint256)\",\"InferToken.transferFrom(address,address,uint256)\",\"LegacyRedeemer.advanceClock(address,uint256)\",\"LegacyRedeemer.claim()\",\"LegacyRedeemer.redeem(address,uint256)\",\"LegacyRedeemer.releaseNft(address,uint256)\",\"LegacyRedeemer.sweep()\",\"SeasonVault.announceEnd()\",\"SeasonVault.applyProposal(uint256)\",\"SeasonVault.claim(uint256,address,uint256,bytes32[])\",\"SeasonVault.distribute((uint256,address,uint256,bytes32[])[])\",\"SeasonVault.endProgram()\",\"SeasonVault.expire(uint256)\",\"SeasonVault.openSeason(uint64,uint256,bytes32)\",\"SeasonVault.proposeRoot(uint256,bytes32,uint256,string)\",\"SeasonVault.release(uint256,address)\",\"SeasonVault.submitAttestedRoot(uint256,bytes32,uint256,string,string,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"SeasonVault.sweep()\",\"SeasonVault.withdrawEnd()\",\"SeasonVault.withdrawProposal(uint256)\",\"StakedInfer.approve(address,uint256)\",\"StakedInfer.deposit(uint256,address)\",\"StakedInfer.mint(uint256,address)\",\"StakedInfer.redeem(uint256,address,address)\",\"StakedInfer.transfer(address,uint256)\",\"StakedInfer.transferFrom(address,address,uint256)\",\"StakedInfer.withdraw(uint256,address,address)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":272,\"README.md\":95,\"foundry.toml\":16,\"launch.json\":68,\"remappings.txt\":4,\"src/FounderStream.sol\":64,\"src/InferDripper.sol\":87,\"src/InferToken.sol\":15,\"src/LegacyRedeemer.sol\":261,\"src/SeasonAttestation.sol\":243,\"src/SeasonVault.sol\":427,\"src/StakedInfer.sol\":68,\"src/base/ERC4626StaticAsset.sol\":303,\"src/base/LICENSE\":22,\"test/Buckets.t.sol\":862,\"test/LaunchConstructors.t.sol\":74,\"test/LaunchRehearsal.fork.t.sol\":206,\"test/LegacyRedeemer.t.sol\":531,\"test/Revision.t.sol\":279,\"test/StakingRegression.t.sol\":296},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"bbc3cab5cef255159af30fffb45af24afdf79415917cb035a36f7334cc6e43f0","verifiedTreeHash":"457bff0665724e77b41d5a8729ae7b2f2080168e","verifierVersion":"0.1.0+0e08a464"},{"checks":[{"durationMs":7330,"exitCode":0,"name":"build","output":"Compiling 70 files with Solc 0.8.26\nSolc 0.8.26 finished in 7.17s\nCompiler run successful with warnings:\nWarning (2018): Function state mutability can be restricted to view\n   --> test/Buckets.t.sol:172:5:\n    |\n172 |     function test_neitherDeclaresAGenesisShare() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nWarning (2018): Function state mutability can be restricted to view\n   --> test/LegacyRedeemer.t.sol:165:5:\n    |\n165 |     function test_vest_quoteForSplitsNowAndLater() public {\n    |     ^ (Relevant source part starts here and spans across multiple lines).\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/FounderStream.sol:36:17\n   │\n36 │         start = uint64(block.timestamp);\n   │                 ━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ├ note: consider disabling this lint if you're certain the cast is safe\n   │       \n   │       // casting to 'uint64' is safe because [explain why]\n   │       // forge-lint: disable-next-line(unsafe-typecast)\n   │       \n   │       \n   ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:54:13\n   │\n54 │         if (elapsed > MAX_CATCHUP) elapsed = MAX_CATCHUP;\n   │             ━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:57:13\n   │\n57 │         if (elapsed == MAX_CATCHUP && amount < minDrip) {\n   │             ━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:57:39\n   │\n57 │         if (elapsed == MAX_CATCHUP && amount < minDrip) {\n   │                                       ━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:126:20\n    │\n126 │         closesAt = uint64(block.timestamp + REDEMPTION_PERIOD);\n    │                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:42:13\n   │\n42 │         if (elapsed < CLIFF) return 0;\n   │             ━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:43:13\n   │\n43 │         if (elapsed >= DURATION) return genesisAllocation;\n   │             ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/InferDripper.sol:68:53\n   │\n68 │         return amount != 0 && (amount >= minDrip || block.timestamp - lastDripAt >= MAX_CATCHUP);\n   │                                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/FounderStream.sol:51:13\n   │\n51 │         if (block.timestamp - start >= DURATION) return held;\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:139:13\n    │\n139 │         if (block.timestamp >= closesAt) revert Closed();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/LegacyRedeemer.sol:156:9\n    │\n156 │         emit Redeemed(address(legacy), msg.sender, amount, out);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:163:42\n    │\n163 │         vestingOf[msg.sender].claimed += uint128(amount);\n    │                                          ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:174:28\n    │\n174 │         uint256 unlocked = elapsed >= VEST_PERIOD ? v.total : uint256(v.total) * elapsed / VEST_PERIOD;\n    │                            ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/StakedInfer.sol:50:13\n   │\n50 │         if (held != 0) {\n   │             ━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:189:37\n    │\n189 │         vestingOf[holder] = Vesting(uint128(onSchedule), 0, uint64(block.timestamp));\n    │                                     ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/LegacyRedeemer.sol:189:61\n    │\n189 │         vestingOf[holder] = Vesting(uint128(onSchedule), 0, uint64(block.timestamp));\n    │                                                             ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:216:13\n    │\n216 │         if (block.timestamp < closesAt) revert StillOpen(closesAt);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/LegacyRedeemer.sol:229:35\n    │\n229 │                 (, uint256 end) = IMoonClock(address(legacy)).moonConfig();\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/LegacyRedeemer.sol:232:13\n    │\n232 │             IBurnable(address(legacy)).burn(1);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/LegacyRedeemer.sol:229:35\n    │\n229 │                 (, uint256 end) = IMoonClock(address(legacy)).moonConfig();\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/LegacyRedeemer.sol:244:13\n    │\n244 │         if (block.timestamp < closesAt) revert StillOpen(closesAt);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/LegacyRedeemer.sol:246:9\n    │\n246 │         emit NftReleased(collection, tokenId, nftReturnTo);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/SeasonAttestation.sol:115:16\n    │\n115 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n116 │ ┃             '{\"answerType\":\"bool\",\"chainId\":1,\"definitions\":{\"formula\":\"The INFER points formula for this season, ipfs://',\n117 │ ┃             formulaCid,\n118 │ ┃             '\"},\"evidence\":\"panel\",\"question\":\"Does the file at ipfs://',\n    ‡ ┃\n125 │ ┃             _decimal(to)\n126 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/SeasonAttestation.sol:130:16\n    │\n130 │           return abi.encodePacked(\n    │ ┏━━━━━━━━━━━━━━━━┛\n131 │ ┃             \"), exactly the allocations that definitions.formula produces from Ethereum mainnet for that period, \",\n132 │ ┃             \"as a standard Merkle tree whose leaves are keccak256(keccak256(abi.encode(season, account, amount))) \",\n133 │ ┃             \"with root 0x\",\n    ‡ ┃\n141 │ ┃             \"}}\"\n142 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonAttestation.sol:165:13\n    │\n165 │         if (block.timestamp > a.expiresAt) revert Expired();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonAttestation.sol:166:13\n    │\n166 │         if (a.issuedAt > block.timestamp || a.issuedAt > a.expiresAt) revert InvalidTimestamp();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonAttestation.sol:184:96\n    │\n184 │             if (!((c >= \"a\" && c <= \"z\") || (c >= \"A\" && c <= \"Z\") || (c >= \"0\" && c <= \"9\"))) revert BadCid();\n    │                                                                                                ━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/base/ERC4626StaticAsset.sol:264:9\n    │\n264 │         emit Deposit(caller, receiver, assets, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/SeasonAttestation.sol:227:15\n    │\n227 │             ++digits;\n    │               ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/base/ERC4626StaticAsset.sol:287:9\n    │\n287 │         emit Withdraw(caller, receiver, owner, assets, shares);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonAttestation.sol:231:36\n    │\n231 │             out[--digits] = bytes1(uint8(48 + (v % 10)));\n    │                                    ━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[arbitrary-send-erc20]: `transferFrom` uses an arbitrary `from`; require it to equal `msg.sender` or `address(this)`\n    ╭▸ src/base/ERC4626StaticAsset.sol:292:9\n    │\n292 │         SafeERC20.safeTransferFrom(IERC20(asset()), from, address(this), assets);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-erc20\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:156:13\n    │\n156 │         if (startsAt < block.timestamp + NOTICE) revert TooSoon(block.timestamp + NOTICE);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:157:13\n    │\n157 │         if (startsAt > block.timestamp + MAX_NOTICE) revert TooFar(block.timestamp + MAX_NOTICE);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:392:9\n    │\n392 │         emit RootSet(s, root, total, attested);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:209:9\n    │\n209 │         emit RootAttested(s, a.requestId, cid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:210:9\n    │\n210 │         emit FormulaRevealed(s, formulaCid);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:238:13\n    │\n238 │         if (block.timestamp + GOVERNOR_DELAY >= endOf(s) + ROOT_DEADLINE) revert PastDeadline(s);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:240:71\n    │\n240 │         (x.pendingRoot, x.pendingTotal, x.pendingEta) = (root, total, uint64(block.timestamp + GOVERNOR_DELAY));\n    │                                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:255:13\n    │\n255 │         if (block.timestamp < x.pendingEta) revert TooEarly(x.pendingEta);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:418:9\n    │\n418 │         emit Released(s, account, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/SeasonVault.sol:407:9\n    │\n407 │         emit Registered(s, account, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:362:27\n    │\n362 │         if (s >= SEASONS) revert NoSuchSeason(s);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:368:27\n    │\n368 │         if (x.start == 0) revert NoSuchSeason(s);\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:399:31\n    │\n399 │         if (x.rootSetAt == 0) revert NoRoot(s);\n    │                               ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:400:73\n    │\n400 │         if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n    │                                                                         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:402:63\n    │\n402 │         if (!MerkleProof.verifyCalldata(proof, x.root, leaf)) revert BadProof();\n    │                                                               ━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/SeasonVault.sol:404:33\n    │\n404 │         if (amount > remaining) revert OverRemainingTotal(amount, remaining);\n    │                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:309:17\n    │\n309 │             if (block.timestamp < at) revert NotYet(at);\n    │                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:313:17\n    │\n313 │             if (block.timestamp < at) revert NotYet(at);\n    │                 ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:326:18\n    │\n326 │         endEta = uint64(block.timestamp + GOVERNOR_DELAY);\n    │                  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:341:13\n    │\n341 │         if (block.timestamp < endEta) revert TooEarly(endEta);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:379:13\n    │\n379 │         if (block.timestamp < x.start + SEASON) revert SeasonNotOver(s);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:382:13\n    │\n382 │         if (block.timestamp >= x.start + SEASON + ROOT_DEADLINE) revert PastDeadline(s);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/SeasonVault.sol:389:56\n    │\n389 │         (x.root, x.total, x.rootSetAt) = (root, total, uint64(block.timestamp));\n    │                                                        ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint64' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:400:26\n    │\n400 │         if (x.expired || block.timestamp >= x.rootSetAt + REGISTRATION) revert PastDeadline(s);\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:423:26\n    │\n423 │         if (from == 0 || block.timestamp <= from) return 0;\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/SeasonVault.sol:425:16\n    │\n425 │         return elapsed >= SEASON ? amount : amount * elapsed / SEASON;\n    │                ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/AttestationFailurePaths.t.sol:109:38\n    │\n109 │         production.openSeason(uint64(block.timestamp + 48 hours), POT, keccak256(bytes(RULES)));\n    │                                      ━━━━━━━━━━━━━━━\n110 │         vm.warp(production.endOf(0));\n    │         ──────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/Buckets.t.sol:34:17\n   │\n34 │         vm.warp(block.timestamp + 30 days - 1);\n   │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:115:21\n    │\n115 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n290 │         vm.warp(seasons.endOf(s) - 48 hours);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n340 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n344 │         vm.warp(seasons.endOf(0) + 13 weeks);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n344 │         vm.warp(seasons.endOf(0) + 13 weeks);\n    │         ──────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:348:17\n    │\n348 │         vm.warp(block.timestamp + 365 days);\n    │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n361 │         vm.warp(seasons.endOf(0) + 3 weeks); // the root is three weeks late\n    │         ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ─────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:364:17\n    │\n364 │         vm.warp(block.timestamp + 13 weeks / 2);\n    │         ────────━━━━━━━━━━━━━━━──────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n377 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n380 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n380 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n391 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n399 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n410 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n446 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n471 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n476 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n476 │         vm.warp(seasons.endOf(1));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n485 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n499 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n517 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n531 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n567 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:223:29\n    │\n223 │         a.issuedAt = uint64(block.timestamp);\n    │                             ━━━━━━━━━━━━━━━\n    ‡\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:224:30\n    │\n224 │         a.expiresAt = uint64(block.timestamp + 1 hours);\n    │                              ━━━━━━━━━━━━━━━\n    ‡\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:569:17\n    │\n569 │         vm.warp(block.timestamp + 1 weeks);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n588 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n609 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n636 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n681 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n697 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:701:17\n    │\n701 │         vm.warp(block.timestamp + 48 hours);\n    │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n723 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n741 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:285:39\n    │\n285 │         s = seasons.openSeason(uint64(block.timestamp + 48 hours), pot, keccak256(bytes(RULES)));\n    │                                       ━━━━━━━━━━━━━━━\n    ‡\n760 │         vm.warp(seasons.endOf(0));\n    │         ───────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Buckets.t.sol:798:26\n    │\n798 │         uint256 latest = block.timestamp + 365 days;\n    │                          ━━━━━━━━━━━━━━━\n    ‡\n805 │         vm.warp(seasons.endOf(0) + 5 * 365 days); // long pauses are still permitted\n    │         ──────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/LegacyRedeemer.t.sol:109:17\n    │\n109 │         vm.warp(block.timestamp + 91 days / 2);\n    │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/LegacyRedeemer.t.sol:139:17\n    │\n139 │         vm.warp(block.timestamp + 91 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Revision.t.sol:120:33\n    │\n120 │         vault.openSeason(uint64(block.timestamp + 48 hours), POT, keccak256(bytes(CID)));\n    │                                 ━━━━━━━━━━━━━━━\n    ‡\n200 │         vm.warp(deadline);\n    │         ───────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Revision.t.sol:120:33\n    │\n120 │         vault.openSeason(uint64(block.timestamp + 48 hours), POT, keccak256(bytes(CID)));\n    │                                 ━━━━━━━━━━━━━━━\n    ‡\n212 │         vm.warp(deadline - 1);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Revision.t.sol:120:33\n    │\n120 │         vault.openSeason(uint64(block.timestamp + 48 hours), POT, keccak256(bytes(CID)));\n    │                                 ━━━━━━━━━━━━━━━\n    ‡\n228 │         vm.warp(vault.endOf(0));\n    │         ─────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/Revision.t.sol:267:17\n    │\n267 │         vm.warp(block.timestamp + 100 * 365 days);\n    │         ────────━━━━━━━━━━━━━━━────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/SeasonFailurePaths.t.sol:39:17\n   │\n39 │         vm.warp(block.timestamp + 48 hours);\n   │         ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n   ╭▸ test/StakingRegression.t.sol:41:17\n   │\n41 │         vm.roll(block.number + 10);\n   │         ────────━━━━━━━━━━━━────── `vm.roll` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockNumber()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/StakingRegression.t.sol:151:17\n    │\n151 │         vm.roll(block.number + 1);\n    │         ────────━━━━━━━━━━━━───── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:217:21\n    │\n217 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:235:17\n    │\n235 │         vm.warp(block.timestamp + 1 hours - 1);\n    │         ────────━━━━━━━━━━━━━━━─────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:239:17\n    │\n239 │         vm.warp(block.timestamp + 1);\n    │         ────────━━━━━━━━━━━━━━━───── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:255:17\n    │\n255 │         vm.warp(block.timestamp + 30 minutes);\n    │         ────────━━━━━━━━━━━━━━━────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:260:17\n    │\n260 │         vm.warp(block.timestamp + 30 days);\n    │         ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/StakingRegression.t.sol:264:21\n    │\n264 │             vm.warp(block.timestamp + 1 hours);\n    │             ────────━━━━━━━━━━━━━━━─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n   ╭▸ test/invariant/FounderAccounting.invariant.t.sol:74:21\n   │\n74 │         vm.warp(max(block.timestamp, uint256(stream.start()) + 365 days));\n   │         ────────────━━━━━━━━━━━━━━━────────────────────────────────────── `vm.warp` changes this environment here\n   │\n   ├ help: capture it with `vm.getBlockTimestamp()` instead\n   ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/invariant/LegacyAccounting.invariant.t.sol:186:21\n    │\n186 │         vm.warp(max(block.timestamp, uint256(redeemer.closesAt()) + 91 days));\n    │         ────────────━━━━━━━━━━━━━━━────────────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/invariant/SeasonAccounting.invariant.t.sol:290:22\n    │\n290 │         uint256 at = block.timestamp;\n    │                      ━━━━━━━━━━━━━━━\n    ‡\n294 │         vm.warp(at);\n    │         ─────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/invariant/SeasonAccounting.invariant.t.sol:185:34\n    │\n185 │             if (!ended) endEta = block.timestamp + 48 hours;\n    │                                  ━━━━━━━━━━━━━━━\n    ‡\n306 │             vm.warp(block.timestamp + 48 hours);\n    │             ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/invariant/SeasonAccounting.invariant.t.sol:201:32\n    │\n201 │             if (endEta != 0 && block.timestamp >= endEta) ended = true;\n    │                                ━━━━━━━━━━━━━━━\n    ‡\n306 │             vm.warp(block.timestamp + 48 hours);\n    │             ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/invariant/SeasonAccounting.invariant.t.sol:197:24\n    │\n197 │             } else if (block.timestamp < endEta) {\n    │                        ━━━━━━━━━━━━━━━\n    ‡\n306 │             vm.warp(block.timestamp + 48 hours);\n    │             ─────────────────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/invariant/SeasonAccounting.invariant.t.sol:306:21\n    │\n306 │             vm.warp(block.timestamp + 48 hours);\n    │             ────────━━━━━━━━━━━━━━━──────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/invariant/StakingAccounting.invariant.t.sol:197:17\n    │\n197 │         vm.roll(block.number + bound(blocks_, 0, 3));\n    │         ────────━━━━━━━━━━━━──────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/invariant/StakingAccounting.invariant.t.sol:55:13\n    │\n 55 │         if (block.number != heldAtBlock) {\n    │             ━━━━━━━━━━━━\n    ‡\n213 │             vm.roll(block.number + 1);\n    │             ───────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/invariant/StakingAccounting.invariant.t.sol:56:27\n    │\n 56 │             heldAtBlock = block.number;\n    │                           ━━━━━━━━━━━━\n    ‡\n213 │             vm.roll(block.number + 1);\n    │             ───────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/invariant/StakingAccounting.invariant.t.sol:51:24\n    │\n 51 │         assertEq(held, block.number == heldAtBlock ? heldThisBlock : 0, \"hold lost or extended by a transfer\");\n    │                        ━━━━━━━━━━━━\n    ‡\n213 │             vm.roll(block.number + 1);\n    │             ───────────────────────── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/invariant/StakingAccounting.invariant.t.sol:213:21\n    │\n213 │             vm.roll(block.number + 1);\n    │             ────────━━━━━━━━━━━━───── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.number` may be reused across `vm.roll`\n    ╭▸ test/invariant/StakingAccounting.invariant.t.sol:249:17\n    │\n249 │         vm.roll(block.number + 1);\n    │         ────────━━━━━━━━━━━━───── `vm.roll` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockNumber()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":9148,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/LaunchRehearsal.fork.t.sol:LaunchRehearsalForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 202.13µs (0.00ns CPU time)\n\nRan 1 test for test/LegacyRedeemer.t.sol:LegacyRedeemerForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 201.97µs (0.00ns CPU time)\n\nRan 1 test for test/LaunchRehearsal.fork.t.sol:InferTokenTest\n[PASS] test_nameSymbolSupply() (gas: 46429)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 292.78µs (201.75µs CPU time)\n\nRan 1 test for test/Buckets.t.sol:GenesisSplitIntegrationTest\n[PASS] test_theBucketsAndTheTreasury() (gas: 8407306)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 704.78µs (455.44µs CPU time)\n\nRan 3 tests for test/LegacyRedeemer.t.sol:ClockUpkeepRevisionTest\n[PASS] test_catchupStopsWhenCurrentAndLaterWindowsStillAdvance() (gas: 197330)\n[PASS] test_currentClockCannotConsumeDustEvenWithRepeatedCalls() (gas: 82523)\n[PASS] test_stepsStillLimitsCatchupAndZeroDoesNothing() (gas: 110173)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 772.42µs (344.43µs CPU time)\n\nRan 2 tests for test/LaunchConstructors.t.sol:LaunchConstructorsTest\n[PASS] test_stakingConstructorDoesNotReadTokenMetadata() (gas: 14792)\n[PASS] test_staticConstructorsCallOnlyTheEarlierVaultAsset() (gas: 183255)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 1.53ms (1.48ms CPU time)\n\nRan 18 tests for test/LegacyRedeemer.t.sol:LegacyRedeemerTest\n[PASS] test_afterOneYearClosedAndSweptToTheTreasury() (gas: 303291)\n[PASS] test_clockUpkeepNeedsDonatedDust() (gas: 1206206)\n[PASS] test_constructorChecks() (gas: 10778)\n[PASS] test_dustThatRedeemsForNothingIsRefused() (gas: 33142)\n[PASS] test_eachTokenIsCappedAtItsOwnAllocation() (gas: 405225)\n[PASS] test_immediateCapIsPerWalletAtTheLaunchRate() (gas: 2772157)\n[PASS] test_oneBucketForBothTokens() (gas: 27163)\n[PASS] test_onlyTheTwoTokens() (gas: 626378)\n[PASS] test_redeemsEachTokenAtItsRateAndBurnsIt() (gas: 335866)\n[PASS] test_theUnlockingNftReturnsAfterTheYear() (gas: 330312)\n[PASS] test_vest_aboveTheCapVestsLinearlyOverASeason() (gas: 434047)\n[PASS] test_vest_addingMidSeasonKeepsWhatWasAlreadyUnlocked() (gas: 360292)\n[PASS] test_vest_addingRestartsTheSeasonForEverythingUnvested() (gas: 377979)\n[PASS] test_vest_caps() (gas: 30080)\n[PASS] test_vest_eachTokenHasItsOwnCap() (gas: 407997)\n[PASS] test_vest_quoteForSplitsNowAndLater() (gas: 28146)\n[PASS] test_vest_smallRedemptionIsPaidAtOnce() (gas: 190082)\n[PASS] test_vest_sweepLeavesWhatIsOwedToVestingWallets() (gas: 408170)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 2.17ms (3.66ms CPU time)\n\nRan 9 tests for test/SeasonFailurePaths.t.sol:SeasonFailurePathsTest\n[PASS] test_badSecondBatchEntryRollsBackFirstRegistrationAndPayout() (gas: 853775)\n[PASS] test_endCannotAccelerateThenReopenOrSweepReservedClaims() (gas: 850252)\n[PASS] test_everyGovernorEntryRejectsAnUnprivilegedCallerWithoutChangingState() (gas: 196831)\n[PASS] test_expiringRootlessSeasonCancelsPendingProposalAtExactDeadline() (gas: 398817)\n[PASS] test_missingSeasonAndRootFailuresAreSpecific() (gas: 236958)\n[PASS] test_proofBindsSeasonRecipientAndAmountAndFailureDoesNotRegister() (gas: 544144)\n[PASS] test_registrationDeadlineIsExclusiveButRegisteredClaimsRemainPayable() (gas: 710644)\n[PASS] test_rootProposalBoundariesReplacementAndCancellation() (gas: 734539)\n[PASS] test_validUnregisteredLeafIsRefusedAtDeadlineWithoutLosingRegisteredDebt() (gas: 606537)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 2.29ms (2.47ms CPU time)\n\nRan 7 tests for test/Buckets.t.sol:StakingTest\n[PASS] test_aDepositRightBeforeADripGainsOnlyThatSlice() (gas: 492959)\n[PASS] test_inflationAttackDoesNotPay() (gas: 379085)\n[PASS] test_neitherDeclaresAGenesisShare() (gas: 13820)\n[PASS] test_neverDripsIntoAnEmptyVault() (gas: 91967)\n[PASS] test_oneCallReleasesAtMostAnHoursSliceOfTheBuffer() (gas: 355328)\n[PASS] test_stakersEarnByTimeHeldAndTheBufferDrainsOverAboutAWeek() (gas: 10406632)\n[PASS] test_theHoldTravelsWithSharesAndCannotBeUsedToGrief() (gas: 477204)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 10.74ms (4.85ms CPU time)\n\nRan 3 tests for test/Buckets.t.sol:FounderStreamTest\n[PASS] test_allOfItAtAYearAndNothingStranded() (gas: 145522)\n[PASS] test_neverPaysMoreThanItHolds() (gas: 460372)\n[PASS] test_nothingBeforeTheCliffThenTheStraightLine() (gas: 211164)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 18.91ms (371.11µs CPU time)\n\nRan 5 tests for test/StakingRegression.t.sol:DripperRegressionTest\n[PASS] testFuzz_finalBalanceIsReleasedExactlyOnce(uint256) (runs: 512, μ: 249477, ~: 251055)\nLogs:\n  Bound result 1417097574089537500\n\n[PASS] test_floorStillRefusesAnEmptyVault() (gas: 175370)\n[PASS] test_fullHourIsRequiredForTheFloorAndFinalDust() (gas: 332829)\n[PASS] test_largeBufferKeepsItsNormalRateThenCrossesIntoTheFloor() (gas: 21474799)\n[PASS] test_smallBuybackReachesStakersAndDoesNotLeaveATail() (gas: 12333490)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 83.75ms (102.49ms CPU time)\n\nRan 10 tests for test/AttestationFailurePaths.t.sol:AttestationFailurePathsTest\n[PASS] testFuzz_invalidSignatureLengths(uint256) (runs: 256, μ: 254558, ~: 254667)\nLogs:\n  Bound result 111\n\n[PASS] testFuzz_signatureBindsFieldsEvenWhenAllValidityGuardsStillPass(uint8) (runs: 256, μ: 271419, ~: 271582)\nLogs:\n  Bound result 4\n\n[PASS] testFuzz_signedInvalidAttestationFieldsAreRefused(uint8) (runs: 256, μ: 329050, ~: 328719)\nLogs:\n  Bound result 2\n\n[PASS] test_emptyOversizedAndEscapedCidsAreRejected() (gas: 517312)\n[PASS] test_expiryAndPanelThresholdAreInclusiveAndRequestIsSingleUse() (gas: 448652)\n[PASS] test_productionContractRejectsTheFixtureSigner() (gas: 3309397)\n[PASS] test_questionCidsAndWindowArePartOfTheSignedQuestion() (gas: 584644)\n[PASS] test_rootValidationFailureDoesNotConsumeAnOtherwiseValidRequest() (gas: 590726)\n[PASS] test_signatureCannotCrossConsumersOrChains() (gas: 3514890)\n[PASS] test_signatureRejectsHighSInvalidVAndZeroRecovery() (gas: 493912)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 89.43ms (249.81ms CPU time)\n\nRan 7 tests for test/LaunchFailurePaths.t.sol:LaunchFailurePathsTest\n[PASS] testFuzz_founderPaymentsNeverDependOnReleaseFrequency(uint256,uint256) (runs: 1000, μ: 288649, ~: 291305)\nLogs:\n  Bound result 18692356\n  Bound result 22336604\n\n[PASS] test_allRequiredConstructorAddressesAreNonzero() (gas: 1252666)\n[PASS] test_founderCliffAndEndAreExactEvenWithLateFunding() (gas: 561228)\n[PASS] test_legacySlotsRequireDistinctTokensAndNonzeroTerms() (gas: 1252286)\n[PASS] test_maximumTokenTransferFailsWithoutArithmeticOrSupplyChange() (gas: 77008)\n[PASS] test_noAdministrativeOrSupplyMutationEntrypointAndFullSupplyRemainsTransferable() (gas: 895435)\n[PASS] test_stakingFailuresRollbackAssetsSharesAndHold() (gas: 672105)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 89.28ms (82.26ms CPU time)\n\nRan 9 tests for test/LegacyFailurePaths.t.sol:LegacyFailurePathsTest\n[PASS] testFuzz_partitioningRedemptionsCannotRoundUpOrBypassImmediateCap(uint256,uint256) (runs: 1000, μ: 350007, ~: 315385)\nLogs:\n  Bound result 61084527013936629924832\n  Bound result 18295717101383246315160\n\n[PASS] test_exactClosingBoundaryAndLateVestingStayIndependent() (gas: 831897)\n[PASS] test_failedBurnRollsBackDepositAutoClaimAllowanceAndNewSchedule() (gas: 915700)\n[PASS] test_fullAllocationBoundaryRefusesOneMoreRedeemWithoutTouchingOtherSlot() (gas: 726568)\n[PASS] test_noAllowanceAndInsufficientLegacyBalanceCannotConsumeAllocation() (gas: 747439)\n[PASS] test_redeemingUnderOtherSlotsCapDoesNotRestartExistingVesting() (gas: 425874)\n[PASS] test_transferFailureRollsBackAnExistingVestingSchedule() (gas: 656209)\n[PASS] test_upkeepFailureIsAtomicAndDoesNotBurnUserTokens() (gas: 223741)\n[PASS] test_zeroUnknownAndSubWeiOutputsRevertWithoutAccountingChanges() (gas: 742214)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 89.35ms (90.90ms CPU time)\n\nRan 9 tests for test/Revision.t.sol:SeasonRevisionTest\n[PASS] test_attestedRootCannotRaceExpiryAtDeadline() (gas: 548732)\n[PASS] test_governorCanEndWithoutOpeningAnySeason() (gas: 121724)\n[PASS] test_governorProposalNeedsTimeToMatureBeforeExpiry() (gas: 280786)\n[PASS] test_lastTimelyAttestedRootIsImmediateAndFinal() (gas: 699010)\n[PASS] test_lastTimelyGovernorProposalCanApply() (gas: 399555)\n[PASS] test_registeredClaimStillPaysAfterExpiryWithoutReregistering() (gas: 683058)\n[PASS] test_registeredClaimStillPaysAtRegistrationDeadline() (gas: 669185)\n[PASS] test_timeAloneCannotReleaseThePointsAllocation() (gas: 127482)\n[PASS] test_unappliedProposalCannotRaceExpiryAtDeadline() (gas: 417466)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 93.09ms (3.69ms CPU time)\n\nRan 32 tests for test/Buckets.t.sol:SeasonVaultTest\n[PASS] testFuzz_registrationConservesTheDeclaredTotal(uint256,uint256,uint256) (runs: 512, μ: 873181, ~: 892629)\nLogs:\n  Bound result 44963138787918234292869016\n  Bound result 100000000000000000000000000\n  Bound result 527012840963120\n\n[PASS] test_aCidThatWouldBreakTheJsonIsRefused() (gas: 327055)\n[PASS] test_aForgedLeafIsRefused() (gas: 544838)\n[PASS] test_aNoOrAThinPanelOrAStrangersSignatureIsRefused() (gas: 851766)\n[PASS] test_aSeasonNeedsARulesCommitmentAndItHidesTheDocument() (gas: 209318)\n[PASS] test_aSeasonWhoseRootNeverComesIsReleasedWhole() (gas: 311261)\n[PASS] test_aYesToAnotherQuestionIsRefused() (gas: 1005746)\n[PASS] test_anAnnouncedEndCanBeWithdrawn() (gas: 246138)\n[PASS] test_anExpiredOrReplayedYesIsRefused() (gas: 998140)\n[PASS] test_atMostFiveSeasons() (gas: 690197)\n[PASS] test_attestedLeavesAreCappedCumulativelyEvenAfterPayment() (gas: 988023)\n[PASS] test_distributeCannotRegisterBeyondTotalAndRollsBackTheBatch() (gas: 739817)\n[PASS] test_distributePushesABatch() (gas: 939069)\n[PASS] test_endingSendsOnlyWhatIsAvailableAndOwedClaimsStillPay() (gas: 1084023)\n[PASS] test_governorFallbackWaits48hAndTheAttestorCanStillWin() (gas: 730737)\n[PASS] test_governorLeafAboveTotalCannotRaidAnotherSeason() (gas: 708676)\n[PASS] test_governorRootAppliesAfterTheDelay() (gas: 399425)\n[PASS] test_millisecondStartIsRefusedWithoutReservingThePot() (gas: 219460)\n[PASS] test_nothingIsScheduledUntilTheGovernorOpensASeason() (gas: 66342)\n[PASS] test_openingNeedsNoticeAFixed13WeeksAndNoOverlap() (gas: 425201)\n[PASS] test_pausesAndSkips_theEarlierSeasonVestsRegardless() (gas: 853921)\n[PASS] test_printQuestionDocument() (gas: 229145)\nLogs:\n  0x7b22616e7377657254797065223a22626f6f6c222c22636861696e4964223a312c22646566696e6974696f6e73223a7b22666f726d756c61223a2254686520494e46455220706f696e747320666f726d756c6120666f72207468697320736561736f6e2c20697066733a2f2f6261666b7265696864776463656667683464716b6a763637757a636d77376f6a6565367865647a6465746f6a757a6a657674656e78717576796b75227d2c2265766964656e6365223a2270616e656c222c227175657374696f6e223a22446f6573207468652066696c6520617420697066733a2f2f6261666b726569676832616b69736361696c646371616273796733646672366368753366677072656769796d73636b376537617161347335327a7920676976652c20666f7220494e46455220706f696e747320736561736f6e20696e64657820302028756e69782074696d652031373238303120746f2038303335323031292c2065786163746c792074686520616c6c6f636174696f6e73207468617420646566696e6974696f6e732e666f726d756c612070726f64756365732066726f6d20457468657265756d206d61696e6e657420666f72207468617420706572696f642c2061732061207374616e64617264204d65726b6c6520747265652077686f7365206c656176657320617265206b656363616b323536286b656363616b323536286162692e656e636f646528736561736f6e2c206163636f756e742c20616d6f756e74292929207769746820726f6f742030786436633636636164303666653134666462366365393239376438306433326632346437343238393936643030343563626639306363333435633637376261313620616e6420616d6f756e74732073756d6d696e6720746f2031323330303030303030303030303030303030303020776569206f6620494e4645523f222c2276223a312c2277696e646f77223a7b2266726f6d426c6f636b223a3130302c22746f426c6f636b223a3230307d7d\n\n[PASS] test_reservedNeverExceedsTheBalance() (gas: 1037934)\n[PASS] test_rootCannotPromiseMoreThanThePot() (gas: 447967)\n[PASS] test_rootsMustNameTheRulesCommittedAtOpening() (gas: 638611)\n[PASS] test_startHorizonIsInclusiveAndRelativeToOpening() (gas: 323473)\n[PASS] test_theQuestionNamesTheSeasonsOwnDates() (gas: 462398)\n[PASS] test_theQuestionNamesTheSeasonsRules() (gas: 311598)\n[PASS] test_theUnassignedPartOfAPotIsReleasedAtTheRoot() (gas: 533843)\n[PASS] test_unregisteredLeavesAreReleasedAfterTheRegistrationDeadline() (gas: 866420)\n[PASS] test_vestingOfOneSeasonOverlapsTheNext() (gas: 1365652)\n[PASS] test_vestingRunsFromTheRootSoALateRootCostsNothing() (gas: 1063514)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 93.22ms (116.83ms CPU time)\n\nRan 4 tests for test/Revision.t.sol:DripperDustRevisionTest\n[PASS] testFuzz_virtualCaptureIsBounded(uint256,uint256) (runs: 512, μ: 323330, ~: 323649)\nLogs:\n  Bound result 25389504129713\n  Bound result 999999999999999999999999\n\n[PASS] test_dustVaultResumesAfterRealStakeArrives() (gas: 537854)\n[PASS] test_oneWeiCannotEnableDripsEvenAfterADonation() (gas: 443044)\n[PASS] test_thresholdBoundaryAndWithdrawalsRecheckTheGuard() (gas: 605774)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 93.12ms (93.78ms CPU time)\n\nRan 8 tests for test/StakingRegression.t.sol:StakingRegressionTest\n[PASS] testFuzz_matchesVendoredERC4626AfterTheHold(uint256,uint256,uint256) (runs: 512, μ: 2093094, ~: 2093219)\nLogs:\n  Bound result 1\n  Bound result 999999999\n  Bound result 3209\n\n[PASS] testFuzz_movingSharesPreservesTheHold(uint256,uint256,uint256) (runs: 512, μ: 491543, ~: 465210)\nLogs:\n  Bound result 1\n  Bound result 999999999\n  Bound result 3209\n\n[PASS] test_dustDepositDoesNotBlockOlderShares() (gas: 422848)\n[PASS] test_dustMintDoesNotBlockWithdrawOrDelegatedExit() (gas: 389264)\n[PASS] test_freshDepositToAnotherReceiverCannotBypassTheHold() (gas: 447030)\n[PASS] test_freshShareDustCannotHoldTheRecipientsOlderShares() (gas: 451319)\n[PASS] test_transfersSpendUnlockedSharesFirstAndCarryOnlyTheHeldPart() (gas: 543529)\n[PASS] test_zeroAndSelfTransfersCannotRefreshOrShedTheHold() (gas: 564503)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 93.16ms (144.98ms CPU time)\n\nRan 1 test for test/invariant/FounderAccounting.invariant.t.sol:FounderAccountingInvariantTest\n[PASS] invariant_founderCanOnlyReceiveFundedAndVestedInfer() (runs: 256, calls: 24576, reverts: 0)\n\n╭--------------------------+----------+-------+---------+----------╮\n| Contract                 | Selector | Calls | Reverts | Discards |\n+==================================================================+\n| FounderAccountingHandler | elapse   | 8114  | 0       | 0        |\n|--------------------------+----------+-------+---------+----------|\n| FounderAccountingHandler | fund     | 8255  | 0       | 0        |\n|--------------------------+----------+-------+---------+----------|\n| FounderAccountingHandler | release  | 8207  | 0       | 0        |\n╰--------------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 70000000000000000000000000\n  Bound result 2446881\n  Bound result 3310\n  Bound result 3963891461\n  Bound result 1699\n  Bound result 15\n  Bound result 168\n  Bound result 3194973\n  Bound result 2957397980\n  Bound result 0\n  Bound result 64199\n  Bound result 3661\n  Bound result 260938\n  Bound result 2944056\n  Bound result 1558466\n  Bound result 8299\n  Bound result 20\n  Bound result 794673\n  Bound result 3868\n  Bound result 1817673\n  Bound result 3429967\n  Bound result 929999999999999993078627199\n  Bound result 2\n  Bound result 339541\n  Bound result 966\n  Bound result 1\n  Bound result 0\n  Bound result 1314\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 0\n  Bound result 1853\n  Bound result 336\n  Bound result 1580179\n  Bound result 309\n  Bound result 74873\n  Bound result 0\n  Bound result 7086\n  Bound result 2641392\n  Bound result 1781162\n  Bound result 0\n  Bound result 0\n  Bound result 680359\n  Bound result 5864\n  Bound result 101\n  Bound result 0\n  Bound result 0\n  Bound result 441221\n  Bound result 2289210\n  Bound result 1821637\n  Bound result 2968407\n  Bound result 0\n  Bound result 0\n  Bound result 2205453\n  Bound result 6035\n  Bound result 0\n  Bound result 0\n  Bound result 478\n  Bound result 1\n  Bound result 2017098\n  Bound result 2006\n  Bound result 669\n  Bound result 8507\n  Bound result 569\n  Bound result 0\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.64s (1.64s CPU time)\n\nRan 1 test for test/invariant/SplitterAccounting.invariant.t.sol:SplitterAccountingInvariantTest\n[PASS] invariant_splitPaysEachLaunchExactlyOnceWithoutCrossingTokens() (runs: 256, calls: 16384, reverts: 0)\n\n╭---------------------------+----------+-------+---------+----------╮\n| Contract                  | Selector | Calls | Reverts | Discards |\n+===================================================================+\n| SplitterAccountingHandler | donate   | 5444  | 0       | 0        |\n|---------------------------+----------+-------+---------+----------|\n| SplitterAccountingHandler | split    | 5484  | 0       | 0        |\n|---------------------------+----------+-------+---------+----------|\n| SplitterAccountingHandler | sweep    | 5456  | 0       | 0        |\n╰---------------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 700000000000000000000000000\n  Bound result 100000000000000000000000000\n  Bound result 16000000000000000000\n  Bound result 176\n  Bound result 3325846995858744370698513\n  Bound result 4361\n  Bound result 12\n  Bound result 200000000000000000000118248\n  Bound result 963\n  Bound result 123\n  Bound result 153131121292030720060465250\n  Bound result 999000000000000000000\n  Bound result 12\n  Bound result 239659180817744\n  Bound result 1262\n  Bound result 825\n  Bound result 96\n  Bound result 69266515941085362576763449\n  Bound result 319794699120753761998004624\n  Bound result 122\n  Bound result 11068\n  Bound result 2665\n  Bound result 495000000000000000000\n  Bound result 16716225546131578100478916\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.66s (2.66s CPU time)\n\nRan 1 test for test/invariant/TokenAccounting.invariant.t.sol:TokenAccountingInvariantTest\n[PASS] invariant_fixedSupplyExactTransfersAndAllowanceLedger() (runs: 256, calls: 24576, reverts: 0)\n\n╭------------------------+---------------+-------+---------+----------╮\n| Contract               | Selector      | Calls | Reverts | Discards |\n+=====================================================================+\n| TokenAccountingHandler | approve       | 5018  | 0       | 0        |\n|------------------------+---------------+-------+---------+----------|\n| TokenAccountingHandler | failAllowance | 4970  | 0       | 0        |\n|------------------------+---------------+-------+---------+----------|\n| TokenAccountingHandler | failTransfer  | 4883  | 0       | 0        |\n|------------------------+---------------+-------+---------+----------|\n| TokenAccountingHandler | transfer      | 4808  | 0       | 0        |\n|------------------------+---------------+-------+---------+----------|\n| TokenAccountingHandler | transferFrom  | 4897  | 0       | 0        |\n╰------------------------+---------------+-------+---------+----------╯\n\nLogs:\n  Bound result 2026\n  Bound result 696\n  Bound result 987978284468365671379356360\n  Bound result 0\n  Bound result 6279\n  Bound result 203973738526292479774339859\n  Bound result 4096\n  Bound result 7287\n  Bound result 63609601450922971\n  Bound result 10000000000000000000000\n  Bound result 0\n  Bound result 549\n  Bound result 0\n  Bound result 0\n  Bound result 46127625429207709318210400\n  Bound result 70000000000000000000000000\n  Bound result 63\n  Bound result 148579835272527745074727644\n  Bound result 1000\n  Bound result 1064470259\n  Bound result 0\n  Bound result 304188142568701438811522925\n  Bound result 1000000000000000000000000000\n  Bound result 960\n  Bound result 7620\n  Bound result 6728\n  Bound result 548\n  Bound result 6000000000000000000\n  Bound result 978\n  Bound result 4236\n  Bound result 80000000000000000000000000\n  Bound result 6297\n  Bound result 52\n  Bound result 5811\n  Bound result 606\n  Bound result 10000000000000000\n  Bound result 2003\n  Bound result 4889\n  Bound result 35400000000000000000000\n  Bound result 1800\n  Bound result 47\n  Bound result 5815\n  Bound result 500\n  Bound result 8587\n  Bound result 35000000000000000000000000\n  Bound result 223\n  Bound result 101683837784984654316691090\n  Bound result 10976855\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.12s (3.11s CPU time)\n\nRan 2 tests for test/invariant/LegacyAccounting.invariant.t.sol:LegacyAccountingInvariantTest\n[PASS] invariant_legacyBurnsAndInferLiabilitiesConserveValue() (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------------------+----------+-------+---------+----------╮\n| Contract                | Selector | Calls | Reverts | Discards |\n+=================================================================+\n| LegacyAccountingHandler | claim    | 4082  | 0       | 0        |\n|-------------------------+----------+-------+---------+----------|\n| LegacyAccountingHandler | donate   | 4106  | 0       | 0        |\n|-------------------------+----------+-------+---------+----------|\n| LegacyAccountingHandler | elapse   | 4045  | 0       | 0        |\n|-------------------------+----------+-------+---------+----------|\n| LegacyAccountingHandler | redeem   | 4111  | 0       | 0        |\n|-------------------------+----------+-------+---------+----------|\n| LegacyAccountingHandler | sweep    | 4065  | 0       | 0        |\n|-------------------------+----------+-------+---------+----------|\n| LegacyAccountingHandler | upkeep   | 4167  | 0       | 0        |\n╰-------------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 300000000000000000000000\n  Bound result 200000000000000000000000000\n  Bound result 3\n  Bound result 2729\n  Bound result 31\n  Bound result 18\n  Bound result 1409066991\n  Bound result 140000000000000000000000017\n  Bound result 1999999999999999999999999933\n  Bound result 6\n  Bound result 20\n  Bound result 166\n  Bound result 1677230623747406690457030353\n  Bound result 7792\n  Bound result 109000000000000000000000\n  Bound result 1000000000000000000003320\n  Bound result 16777216\n  Bound result 522372\n  Bound result 179908\n  Bound result 2442666\n  Bound result 30\n  Bound result 1\n  Bound result 2237037\n  Bound result 0\n  Bound result 18\n  Bound result 21\n  Bound result 26\n  Bound result 13814\n  Bound result 647995\n  Bound result 0\n  Bound result 14\n  Bound result 599231\n  Bound result 7\n  Bound result 2925401\n  Bound result 101072871214045392006331516\n  Bound result 2179\n  Bound result 21\n  Bound result 785067\n  Bound result 699999999999999999996511\n  Bound result 3\n  Bound result 12\n  Bound result 4185\n  Bound result 4\n  Bound result 12\n  Bound result 106273827614967082264523679\n  Bound result 3587\n  Bound result 704933\n  Bound result 60\n  Bound result 4928\n  Bound result 1047042\n  Bound result 140000000000000000000000000\n  Bound result 329389448095661076587870459\n  Bound result 144431\n  Bound result 14\n  Bound result 1\n  Bound result 237116445915861939456265\n  Bound result 20\n  Bound result 2380\n  Bound result 12\n  Bound result 65663276710628158869325774\n\n[PASS] test_upkeepPreservesCurrentWindowDustAndStopsAfterOnlyTheOverdueCycles() (gas: 1011741)\nLogs:\n  Bound result 300000000000000000000000\n  Bound result 200000000000000000000000000\n  Bound result 32\n  Bound result 2\n  Bound result 32\n  Bound result 32\n  Bound result 0\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 5.54s (5.54s CPU time)\n\nRan 2 tests for test/invariant/StakingAccounting.invariant.t.sol:StakingAccountingInvariantTest\n[PASS] invariant_sharesAssetsRewardsAndHoldsAreConserved() (runs: 256, calls: 24576, reverts: 0)\n\n╭--------------------------+------------------+-------+---------+----------╮\n| Contract                 | Selector         | Calls | Reverts | Discards |\n+==========================================================================+\n| StakingAccountingHandler | deposit          | 2797  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | drip             | 2694  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | elapse           | 2762  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | exit             | 2678  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | fund             | 2733  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | mint             | 2787  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | roundTrip        | 2703  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | transfer         | 2652  | 0       | 0        |\n|--------------------------+------------------+-------+---------+----------|\n| StakingAccountingHandler | unauthorizedExit | 2770  | 0       | 0        |\n╰--------------------------+------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 20000000000000000000000\n  Bound result 1000000000000000000000000\n  Bound result 5880000000000000000000000\n  Bound result 3160\n  Bound result 3\n  Bound result 7423079501452078412063496096278\n  Bound result 7499999999999999999999994\n  Bound result 15350\n  Bound result 1736\n  Bound result 3624\n  Bound result 0\n  Bound result 999999999999999999999\n  Bound result 4\n  Bound result 2147886565497495022517181\n  Bound result 3310\n  Bound result 0\n  Bound result 500\n  Bound result 414619646561127\n  Bound result 1760000000\n  Bound result 4145\n  Bound result 0\n  Bound result 1820507070\n  Bound result 999999999999999920001\n  Bound result 1\n  Bound result 29464616541477976583009091\n  Bound result 10000000000000000000000\n  Bound result 1673\n  Bound result 9999000000000000000000000\n  Bound result 17856\n  Bound result 1395\n  Bound result 9979999999999999999998239\n  Bound result 3464\n  Bound result 1376547073095\n  Bound result 128\n  Bound result 156\n  Bound result 3\n  Bound result 9416266680933270762424938\n  Bound result 6904\n  Bound result 1\n  Bound result 177\n  Bound result 1\n  Bound result 26766621758638440272287468\n  Bound result 6625\n  Bound result 0\n  Bound result 3944\n  Bound result 66\n  Bound result 1573\n  Bound result 1\n  Bound result 89083224426469910293575513\n  Bound result 12\n  Bound result 1965600\n  Bound result 220500000000000000000000\n  Bound result 10\n  Bound result 3298\n  Bound result 999999999999999999801\n  Bound result 3\n  Bound result 8889\n  Bound result 7198\n  Bound result 0\n  Bound result 5184000\n  Bound result 3652\n  Bound result 4360\n  Bound result 4475544320534\n  Bound result 4\n  Bound result 1304\n  Bound result 2\n  Bound result 7851289668371509\n  Bound result 7610\n  Bound result 2714224148589704345\n  Bound result 1890982463321722534434634015885\n  Bound result 26\n  Bound result 807\n  Bound result 958999999999585380353438872\n  Bound result 6606\n  Bound result 2\n  Bound result 66\n  Bound result 680052212790337245723569259\n  Bound result 7200\n  Bound result 3674\n  Bound result 4\n  Bound result 1837366889\n  Bound result 4\n  Bound result 9\n  Bound result 3\n  Bound result 3907\n  Bound result 2359\n  Bound result 3714\n  Bound result 404098526\n  Bound result 60\n  Bound result 453762811877\n  Bound result 1000000\n  Bound result 9419\n  Bound result 10957407474479681906314426807\n  Bound result 276063865623024382772489876619\n  Bound result 4185862465402280444551546945981\n  Bound result 8145592893534186373288661552641\n\n[PASS] test_dripsStopBelowShareThresholdAndResumeAtTheExactBoundary() (gas: 2537085)\nLogs:\n  Bound result 10000000000000000000000\n  Bound result 20000000000000000000000\n  Bound result 1000000000000000000000000\n  Bound result 10000000000000000000000000000\n  Bound result 20000000000000000000000000000\n  Bound result 0\n  Bound result 0\n  Bound result 999999999\n  Bound result 3600\n  Bound result 1\n  Bound result 1\n  Bound result 3600\n  Bound result 1\n  Bound result 1\n  Bound result 1\n  Bound result 1000000000\n  Bound result 0\n  Bound result 0\n  Bound result 0\n\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 8.73s (8.73s CPU time)\n\nRan 3 tests for test/invariant/SeasonAccounting.invariant.t.sol:SeasonAccountingInvariantTest\n[PASS] invariant_seasonReservesEqualAllOutstandingCommitments() (runs: 256, calls: 24576, reverts: 0)\n\n╭-------------------------+------------------+-------+---------+----------╮\n| Contract                | Selector         | Calls | Reverts | Discards |\n+=========================================================================+\n| SeasonAccountingHandler | applyRoot        | 2490  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | claim            | 2468  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | controlEnd       | 2414  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | donate           | 2503  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | elapse           | 2441  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | expire           | 2431  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | open             | 2436  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | propose          | 2437  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | release          | 2457  | 0       | 0        |\n|-------------------------+------------------+-------+---------+----------|\n| SeasonAccountingHandler | withdrawProposal | 2499  | 0       | 0        |\n╰-------------------------+------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 0\n  Bound result 100000000000000000000000000\n  Bound result 0\n  Bound result 80000000000000000000000000\n  Bound result 90000000000000000000000000\n  Bound result 574632\n  Bound result 15356\n  Bound result 1961\n  Bound result 8695\n  Bound result 15804\n  Bound result 689\n  Bound result 287\n  Bound result 90355\n  Bound result 2419200000000000000000000\n  Bound result 17664\n  Bound result 555326\n  Bound result 14759\n  Bound result 44795\n  Bound result 244420\n  Bound result 905618455\n  Bound result 5\n  Bound result 454051\n  Bound result 5452\n  Bound result 15358\n  Bound result 1173\n  Bound result 2636\n  Bound result 256595012002030795248649972\n  Bound result 63\n  Bound result 3996768\n  Bound result 4207369\n  Bound result 2792\n  Bound result 6628\n  Bound result 427593\n  Bound result 6000000000000000000000\n  Bound result 3556281\n\n[PASS] test_lateProposalReplacementAndApplicationPreserveReservesUntilExpiry() (gas: 2099365)\nLogs:\n  Bound result 0\n  Bound result 100000000000000000000000000\n  Bound result 0\n  Bound result 80000000000000000000000000\n  Bound result 90000000000000000000000000\n  Bound result 60000000000000000000000000\n  Bound result 4\n\n[PASS] test_registeredClaimsAndBatchesSettleAcrossExpiryWhileNewClaimsFail() (gas: 2130750)\nLogs:\n  Bound result 0\n  Bound result 100000000000000000000000000\n  Bound result 0\n  Bound result 80000000000000000000000000\n  Bound result 90000000000000000000000000\n\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 9.05s (9.05s CPU time)\n\nRan 24 test suites in 9.05s (31.50s CPU time): 138 tests passed, 0 failed, 2 skipped (140 total tests)\n","passed":true},{"durationMs":67,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"FounderStream.release()\",\"InferDripper.drip()\",\"InferToken.approve(address,uint256)\",\"InferToken.transfer(address,uint256)\",\"InferToken.transferFrom(address,address,uint256)\",\"LegacyRedeemer.advanceClock(address,uint256)\",\"LegacyRedeemer.claim()\",\"LegacyRedeemer.redeem(address,uint256)\",\"LegacyRedeemer.releaseNft(address,uint256)\",\"LegacyRedeemer.sweep()\",\"SeasonVault.announceEnd()\",\"SeasonVault.applyProposal(uint256)\",\"SeasonVault.claim(uint256,address,uint256,bytes32[])\",\"SeasonVault.distribute((uint256,address,uint256,bytes32[])[])\",\"SeasonVault.endProgram()\",\"SeasonVault.expire(uint256)\",\"SeasonVault.openSeason(uint64,uint256,bytes32)\",\"SeasonVault.proposeRoot(uint256,bytes32,uint256,string)\",\"SeasonVault.release(uint256,address)\",\"SeasonVault.submitAttestedRoot(uint256,bytes32,uint256,string,string,(bytes32,uint256,bytes32,uint8,bytes,uint256,uint64,uint64,bytes32,bytes32,uint16,uint16,uint16,uint64,uint64),bytes)\",\"SeasonVault.sweep()\",\"SeasonVault.withdrawEnd()\",\"SeasonVault.withdrawProposal(uint256)\",\"StakedInfer.approve(address,uint256)\",\"StakedInfer.deposit(uint256,address)\",\"StakedInfer.mint(uint256,address)\",\"StakedInfer.redeem(uint256,address,address)\",\"StakedInfer.transfer(address,uint256)\",\"StakedInfer.transferFrom(address,address,uint256)\",\"StakedInfer.withdraw(uint256,address,address)\"],\"files\":{\".gitignore\":3,\"ADAPTATION.md\":272,\"README.md\":95,\"foundry.toml\":16,\"remappings.txt\":4,\"src/FounderStream.sol\":64,\"src/InferDripper.sol\":87,\"src/InferToken.sol\":15,\"src/LegacyRedeemer.sol\":261,\"src/SeasonAttestation.sol\":243,\"src/SeasonVault.sol\":427,\"src/StakedInfer.sol\":68,\"src/base/ERC4626StaticAsset.sol\":303,\"src/base/LICENSE\":22,\"test/ADAPTATION.md\":138,\"test/AttestationFailurePaths.t.sol\":290,\"test/Buckets.t.sol\":862,\"test/LaunchConstructors.t.sol\":74,\"test/LaunchFailurePaths.t.sol\":222,\"test/LaunchRehearsal.fork.t.sol\":206,\"test/LegacyFailurePaths.t.sol\":238,\"test/LegacyRedeemer.t.sol\":531,\"test/Revision.t.sol\":279,\"test/SeasonFailurePaths.t.sol\":269,\"test/StakingRegression.t.sol\":296,\"test/helpers/InferFixtures.sol\":72,\"test/invariant/FounderAccounting.invariant.t.sol\":107,\"test/invariant/LegacyAccounting.invariant.t.sol\":244,\"test/invariant/SeasonAccounting.invariant.t.sol\":386,\"test/invariant/SplitterAccounting.invariant.t.sol\":174,\"test/invariant/StakingAccounting.invariant.t.sol\":310,\"test/invariant/TokenAccounting.invariant.t.sol\":124},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"ca2e16b315e58544345d80956948fc7ac8a6e2955616d6df3274f78db02cf0df","verifiedTreeHash":"f2f9e76a4e9a8bf1544f065b4ddc41d73b1d0fff","verifierVersion":"0.1.0+0e08a464"}]}