{"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":"948f8b1b-a4bd-4689-a346-2536b218e486","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"d481b9e9f29b21742476853135af813633854436e0b401c00fc6ba7d1840aae4","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"95cfb5d9ec1eca90f1d2353fef1103321d7a387fb5e2039cbd4a85d581f7ba94","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"61a4ce25f1137435d99b941932d99efe0d3f0efcf9e63b1e878d417dadb76e15","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":"9d6254358b64d19c91435efb080eef018d5837ed19a4a0bb7c6e46349ac890bc","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"waiting"},{"acceptedSubmissionHash":"c28ace4594aa8009a1293f01f95412aeb73f36ca332b964e67600c63361214c3","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"6925d38dd8d17b63305bee59e4a9188859f80073d916509a28c60c82c9952996","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"2d8d683f9eb8c55210be2874a2a3a976af54d68c23fa60cc5a89bc9bbab47cc6","dependsOn":["audit_imported_code"],"execution":{"mustProduce":["src/LaunchToken.sol"],"network":false,"profile":"foundry","requires":[],"skillHash":"b6503de65ad02f845827887c23da4c3b56ccc5df7a459db263bae6e549d92f7f","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"b6503de65ad02f845827887c23da4c3b56ccc5df7a459db263bae6e549d92f7f","skillId":"build-contract-project","state":"failed"},{"acceptedSubmissionHash":"0e669920ae463b97995ee389d9cda417b24dda5a7643e3371db6dfc1526f6d3b","dependsOn":["audit_imported_code","build_contract_project","write_foundry_tests"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"waiting"},{"acceptedSubmissionHash":"f2e4c8ec27bda2cebc061bfdefe828b506da1e93546dd182e5171084fe1a55c4","dependsOn":["build_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":"waiting"}],"objective":"Token name: IMD Offsets. Token symbol: IMDO. Total supply 1,000,000,000 with 18 decimals. Chain id 11155111, paired with ETH. Ethereum mainnet is the real target, deployed manually from this repo.\n\nBASE. The workspace is the ADAM repo (MIT). Fork it: keep its layout, vendored lib/, foundry.toml unchanged (bytecode_hash \"none\"; README quotes it), errors, guards, pull payments, tests. Rename Adam* to Imdo*, drop @custom:x tags, delete AdamSplitOracle and every PNKSTR/IMDSTR path. README credits ADAM, lists each change and who can call what, never says yield, APY, returns, investment, carbon offset or net zero. Do not claim an audit.\n\nPURPOSE. Trades on one hooked ETH/IMDO pool pay a fee in ETH: it buys IMD for IMDO stakers, funds REGEN bought off-chain for stakers, funds retirement of ecological credits, and operations.\n\nCONTRACTS (exact).\n1. IMDOToken = LaunchToken; constructor() no args; name \"IMD Offsets\", symbol \"IMDO\".\n2. ImdoHook = AdamHook, behaviour unchanged; constructor(IPoolManager,address token,address treasury,address owner). Not in the manifest.\n3. ImdoStaking = AdamDistributorV2 reduced. constructor(address imdo,address imd,address manager,address claim,address regenSafe). One reward token (IMD) plus an ETH credit under key address(0). Keep stake, unstake, claim, exit, claimReward, earned, the 24h lock reset by every stake, stakeFor only by claim, the 7-day backlog gated at 10,000,000 staked, fixed exclusions. Remove directDistribution, claimIMDSTR, swaps, unlockCallback. Add notifyRegen() external payable: anyone, msg.value > 0, credits stakers under address(0) exactly as ADAM's non-direct notifyIMDSTR. regenCreditOf(address) view = lifetime credit, never reduced by claim, exit or unstake. withdrawRegen(uint256): only regenSafe, pays regenSafe, cumulative withdrawals <= cumulative notifyRegen ETH. claim/exit never pay ETH. Events RegenNotified(address indexed from,uint256 amount,uint256 distributed), RegenWithdrawn(uint256 amount).\n4. ImdoTreasury = AdamTreasuryV2 reduced. FLAT constructor(address staking,address opsWallet,address offsetsSafe,address regenSafe,address manager,address imd,uint24 imdFee,uint24 imdTickSpacing,address imdHooks,uint256 maxEthPerBuy,uint16 slippageBps,uint32 cooldown,uint256 regenCap,uint256 regenCapMin,uint256 regenCapMax). process(): anyone, cooldown, keeper bounty 50 bps of newly processed ETH as V2. The net splits by constants: OPS_BPS 1000 to opsOwed (payOps(), only opsWallet); OFFSETS_BPS 2500 to offsetsOwed (payOffsets(), only offsetsSafe); REGEN_BPS 2500 to the REGEN leg; the remaining 4000 to the IMD leg. IMD leg = ADAM's IMD buy leg (cap, checkpoint/spot min-out, halving retries; no rerouting). REGEN leg: at most regenCap wei accrue per epoch (block.timestamp / 7 days); the excess joins the IMD leg in the same call; accrued ETH goes to staking.notifyRegen{value}; a failed call stays pending for retry and never blocks process(). setRegenCap(uint256): only regenSafe, within [regenCapMin, regenCapMax]; the ONLY setter. No owner, upgrade or other withdrawal.\n5. ImdoClaim = NFTClaim changed. constructor(address imdo,address staking,address seatNFT,uint256 seatSize,bytes32 holderRoot,uint256 launch). SEAT_ALLOCATION 100,000,000e18 over seat ids [0, seatSize); HOLDER_ALLOCATION 10,000,000e18. Vesting and deadline as ADAM (10 daily tranches, launch + 39 days, burnUnclaimed). claimSeat(uint256[] ids,bool stake): caller must be ownerOf(id); sets seatClaimedBy[id] = caller and emits SeatClaimed(uint256 indexed id,address indexed owner) whenever it changes; pays vested tranches; the entitlement follows the NFT. claimHolder(uint256 total,bytes32[] proof,bool stake): leaf keccak256(bytes.concat(keccak256(abi.encode(msg.sender,total)))), OpenZeppelin MerkleProof, same tranches, per-address claimed; reverts when holderRoot is zero. stake = true calls staking.stakeFor(msg.sender, amount).\n\nMANIFEST. launch.json kind evm_project, token IMDOToken, contracts in order ImdoStaking then ImdoTreasury, FLAT constructorArgs (5 and 15 items), each $token, $owner, $contract:ImdoStaking or a literal. Later contracts cannot be referenced: use $owner for claim, regenSafe, offsetsSafe, opsWallet and say so in notes. Literals: manager 0x000000000004444c5dc75cB358380D2e3dE08A90, IMD 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7, 10000, 200, hooks zero, 1 ETH, 300, 600, cap 0.5 ETH, min 0.05, max 5. ImdoHook and ImdoClaim are deployed only by the script.\n\nSCRIPT. script/DeployImdo.s.sol, manual signer, no env or keys, all addresses, opening tick and holderRoot as arguments: token, staking (claim address predicted as DeployAdamExtension does), claim, treasury, mined hook, initialize, one single-sided position of 890,000,000 IMDO with its LP NFT sent to dead, fund claim with 110,000,000, start the two-step hook ownership transfer. Seat NFT 0x0000eC93127BAA929E58E97dd0095A2BFb38ec1D, seatSize 2000.\n\nTESTS (by another worker, from this text). Adapt ADAM's suites; add: exact split and bounty; cap, overflow, epoch roll; setRegenCap bounds and auth; notifyRegen credit incl. no stake; regenCreditOf monotonic; withdrawRegen auth and bound; failed notifyRegen retried; seat claim after NFT transfer; holder proof, double claim, zero root; stakeFor lock; script test; mainnet-fork IMD buy; invariants: staking ETH >= notified - withdrawn; treasury ETH = owed + pending.","parentJobId":null,"planHash":"76c4993574d492da571320cc93e2d96e72cc17d9f233d11c765c773ad6b88f24","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"948f8b1b-a4bd-4689-a346-2536b218e486","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-841-imd-offsets-token-symbol-imdo-total-supp"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"51022","feedbackHash":"5575fed7969ea1bc5dce76882c819da0726e36f0b794854914bffec3537da62a","nodeKey":"audit_economics","submissionHash":"d481b9e9f29b21742476853135af813633854436e0b401c00fc6ba7d1840aae4","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51024","feedbackHash":"fe7746270d68956ea684b486dca2f71c3308d7a266857ad78f0ef33e71648a35","nodeKey":"audit_flow","submissionHash":"95cfb5d9ec1eca90f1d2353fef1103321d7a387fb5e2039cbd4a85d581f7ba94","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51540","feedbackHash":"82a51c8821045cec6ca4bb517e312102e0f45681cfa24a4dccf68b166ad83192","nodeKey":"audit_imported_code","submissionHash":"61a4ce25f1137435d99b941932d99efe0d3f0efcf9e63b1e878d417dadb76e15","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51481","feedbackHash":"017bdd9d5e677edec88151866ca17bf0f1c7d87fcc25dd0f872b54b86017836d","nodeKey":"audit_judge","submissionHash":"9d6254358b64d19c91435efb080eef018d5837ed19a4a0bb7c6e46349ac890bc","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51288","feedbackHash":"e7938d14e352c4a704592716122723d8fb60130a26b355cfc0a62911483d960f","nodeKey":"audit_math","submissionHash":"c28ace4594aa8009a1293f01f95412aeb73f36ca332b964e67600c63361214c3","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51337","feedbackHash":"75bee92f6382db7ef7d759813a5829b0893cfe242ff94ea2bd8cefdfa58dd6f8","nodeKey":"audit_permissions","submissionHash":"6925d38dd8d17b63305bee59e4a9188859f80073d916509a28c60c82c9952996","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52084","feedbackHash":"eb916d054167e785012e18f70e5f16f6e70976b099746bf2c422b851dd630724","nodeKey":"build_contract_project","submissionHash":"2d8d683f9eb8c55210be2874a2a3a976af54d68c23fa60cc5a89bc9bbab47cc6","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"50956","feedbackHash":"e4162338abe5e2d1f06407057614ae91bc9184b60890057f4067e7531628ced3","nodeKey":"build_contract_project","submissionHash":"12306b54129c537470008e7ab930885d429903296387c0029c052307514e85cb","tag1":"verification:checks","tag2":"acceptance-v2","value":0},{"agentId":"51455","feedbackHash":"22c3ce3f80380cb0335d6ad09afd48e2ca023c88bb17190203daf1cc089ce946","nodeKey":"manifest","submissionHash":"0e669920ae463b97995ee389d9cda417b24dda5a7643e3371db6dfc1526f6d3b","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51224","feedbackHash":"53faea736c76ac5e00a80bd23b252ad1255ffe4d280364344565ed228a3786ad","nodeKey":"write_foundry_tests","submissionHash":"f2e4c8ec27bda2cebc061bfdefe828b506da1e93546dd182e5171084fe1a55c4","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"6c3d4cf488bdf61429d31800bb36399969e0476d6fadd925d32a7eb82018ae9b","state":"blocked","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"afc57407b1ed15c4dea5fd6679b28ac0254feaba869815a139732b2cb7c19b6d","device":"7989aa5d838d11d9","findings":[],"hash":"0e669920ae463b97995ee389d9cda417b24dda5a7643e3371db6dfc1526f6d3b","nodeId":"63e30c0f-10cb-41bb-9967-40b6a881d03c","outcome":"completed","summary":"Updated only `launch.json` with `LaunchToken`, verified constructor arguments, and deployment/finding notes.\n\nPassed schema and ABI validation, `forge build`, all 98 default tests, and 9 protected checks.\n\nThe documented Sepolia blocker remains: both required dependency addresses have no code there. Mainnet fork tests were not run.","treeHash":"2abb18b62515f87aa48e154c86805c4b5f9c38dd","usage":{"cachedInputTokens":1841920,"inputTokens":109702,"model":"gpt-6-astra","outputTokens":11360,"runtime":"codex","turns":6,"wallClockMs":367248}},{"artifacts":[],"attempt":1,"bundleHash":"3f885cdaa60cce48071c748b811b03e1ad6216b6f41da484f0a63c435601fd58","device":"bb0a3bf63233e5e5","findings":[],"hash":"12306b54129c537470008e7ab930885d429903296387c0029c052307514e85cb","nodeId":"a96c4981-3f28-4078-a8b8-4f7464bfd0a0","outcome":"completed","summary":"Implemented contracts, manifest, manual deployment script, documentation and tests.\n\nPassed: `forge build`, 134 offline tests, three mainnet fork tests, and `forge fmt --check`. Protected configuration and dependencies are unchanged.\n\nThe mandated Sepolia addresses lack code; that limitation and the tested manual mainnet path are documented in [review notes](/home/imd-worker/.identitymd/work/948f8b1b-a4bd-4689-a346-2536b218e486/a96c4981-3f28-4078-a8b8-4f7464bfd0a0/docs/REVIEW_NOTES.md). No transactions were broadcast.","treeHash":"7082b8eba57d26acd98955ef346285d3068a11df","usage":{"cachedInputTokens":6705152,"inputTokens":204580,"model":"gpt-6-astra","outputTokens":52624,"runtime":"codex","turns":11,"wallClockMs":1438298}},{"artifacts":[],"attempt":2,"bundleHash":"2cdf7799cbb38f4f576141b5a610b5c184aa3f69fd450a0967d6326a8ac94f23","device":"2564cef48373f7f3","findings":[],"hash":"2d8d683f9eb8c55210be2874a2a3a976af54d68c23fa60cc5a89bc9bbab47cc6","nodeId":"a96c4981-3f28-4078-a8b8-4f7464bfd0a0","outcome":"completed","summary":"Implemented the IMDO contracts, manual deployment script, manifest without root `chainId`, tests, and documentation. Protected configuration and dependencies remain unchanged.\n\nVerification passed: `forge build`, `forge fmt --check`, 83 local tests, and the mainnet IMD fork test.\n\nThe required addresses remain unavailable on Sepolia; deployment constraints and finding resolutions are documented in [review notes](/home/imd12/.identitymd/work/948f8b1b-a4bd-4689-a346-2536b218e486/a96c4981-3f28-4078-a8b8-4f7464bfd0a0/docs/REVIEW.md). No transactions were broadcast.","treeHash":"0f261f75efea907000d7e9de2de9b3e9d88d8ab3","usage":{"cachedInputTokens":7523200,"inputTokens":193687,"model":"gpt-6-astra","outputTokens":57463,"runtime":"codex","turns":13,"wallClockMs":1784012}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"90675b3128fe9096","findings":[],"hash":"3a57627ab503cd220eb7a593dfe33d5937b50d71f0034c68ec5fe75619ee3b46","nodeId":"d6e7cd13-f096-43bc-a3c2-e09329276d91","outcome":"failed","summary":"This content was flagged for possible cybersecurity risk. If this seems wrong, try rephrasing your request. If you’re doing authorized security work that requires more cyber permissive safeguards, apply for Daybreak access via https://platform.openai.com/settings/organization/status-and-access before retrying.","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":"gpt-6-astra","outputTokens":0,"runtime":"codex","turns":3,"wallClockMs":296279}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"589ef002581a5371","findings":[{"citation":"resolved","description":"_takeFee moves the fee from the PoolManager to the treasury immediately (`take`) in beforeSwap/afterSwap. At that moment the swapper's router has not settled its ETH, so the transfer is funded only by ETH the manager already holds from other pools. The launch position is ADAM-only (DeployAdam.launchPool, IMDO brief: single-sided 890,000,000 IMDO), so the manager's native balance comes entirely from unrelated pools. On a fresh PoolManager, or any manager whose ETH balance is below the fee (20% of the ETH leg during the first 30 minutes), CurrencyLibrary.transfer fails and the swap reverts with NativeTransferFailed wrapped in HookCallFailed although the trade itself is valid. On Ethereum mainnet the shared manager normally holds enough ETH, so this is a liveness dependency on third-party pools rather than a loss; on chain 11155111 or a sparsely used manager it blocks trading outright. Fix for the adapter: in ImdoHook mint an ERC-6909 claim to the hook (`poolManager.mint(address(this), 0, fee)`) or take with claims, and add a permissionless redeem that unlocks, burns and takes the ETH to the treasury; or take directly only when `CurrencyLibrary.ADDRESS_ZERO.balanceOf(address(poolManager)) >= fee` and mint otherwise. Behaviour must remain 'unchanged' only in fee amounts, not in the funding path.","line":273,"path":"src/AdamHook.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PoolManager} from \"@uniswap/v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"@uniswap/v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"@uniswap/v4-core/src/interfaces/IHooks.sol\";\nimport {Hooks} from \"@uniswap/v4-core/src/libraries/Hooks.sol\";\nimport {PoolKey} from \"@uniswap/v4-core/src/types/PoolKey.sol\";\nimport {Currency, CurrencyLibrary} from \"@uniswap/v4-core/src/types/Currency.sol\";\nimport {BalanceDelta} from \"@uniswap/v4-core/src/types/BalanceDelta.sol\";\nimport {SwapParams, ModifyLiquidityParams} from \"@uniswap/v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"@uniswap/v4-core/src/libraries/TickMath.sol\";\nimport {PoolSwapTest} from \"@uniswap/v4-core/src/test/PoolSwapTest.sol\";\nimport {PoolModifyLiquidityTest} from \"@uniswap/v4-core/src/test/PoolModifyLiquidityTest.sol\";\nimport {LiquidityAmounts} from \"@uniswap/v4-periphery/src/libraries/LiquidityAmounts.sol\";\n\nimport {LaunchToken} from \"src/LaunchToken.sol\";\nimport {AdamHook} from \"src/AdamHook.sol\";\n\n/// @notice AdamHook pays its ETH fee with `poolManager.take(ETH, treasury, fee)` inside the swap, before the\n/// swapper's router has settled any ETH. The transfer is funded only by ETH the PoolManager already holds.\n/// A PoolManager whose only pool is the launch pool, seeded with ADAM alone, holds no ETH, so the very\n/// first buy reverts with NativeTransferFailed even though the trade itself is fine.\ncontract HookFeeTakeFreshManagerTest is Test {\n    PoolManager internal poolManager;\n    PoolSwapTest internal swapRouter;\n    PoolModifyLiquidityTest internal lpRouter;\n    LaunchToken internal adam;\n    AdamHook internal hook;\n    PoolKey internal key;\n\n    address internal owner = makeAddr(\"owner\");\n    address internal treasury = makeAddr(\"treasury\");\n\n    int24 internal constant INITIAL_TICK = 177_240;\n    int24 internal constant LOWER_TICK = 108_180;\n\n    receive() external payable {}\n\n    function setUp() public {\n        vm.warp(1_800_000_000);\n        poolManager = new PoolManager(address(this));\n        swapRouter = new PoolSwapTest(poolManager);\n        lpRouter = new PoolModifyLiquidityTest(poolManager);\n        adam = new LaunchToken();\n\n        uint160 flags = uint160(\n            Hooks.BEFORE_INITIALIZE_FLAG | Hooks.BEFORE_SWAP_FLAG | Hooks.AFTER_SWAP_FLAG\n                | Hooks.BEFORE_SWAP_RETURNS_DELTA_FLAG | Hooks.AFTER_SWAP_RETURNS_DELTA_FLAG\n        );\n        bytes memory creation = abi.encodePacked(\n            type(AdamHook).creationCode, abi.encode(address(poolManager), address(adam), treasury, owner)\n        );\n        bytes32 initHash = keccak256(creation);\n        bytes32 salt;\n        address predicted;\n        for (uint256 i; i < 200_000; ++i) {\n            salt = bytes32(i);\n            predicted = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, initHash)))));\n            if (uint160(predicted) & Hooks.ALL_HOOK_MASK == flags) break;\n        }\n        hook = new AdamHook{salt: salt}(IPoolManager(address(poolManager)), address(adam), treasury, owner);\n        assertEq(address(hook), predicted, \"hook address\");\n\n        key = PoolKey({\n            currency0: CurrencyLibrary.ADDRESS_ZERO,\n            currency1: Currency.wrap(address(adam)),\n            fee: 0,\n            tickSpacing: 60,\n            hooks: IHooks(address(hook))\n        });\n\n        // Owner initializes; the launch position is ADAM only, exactly as the deploy script seeds it.\n        uint160 sqrtP = TickMath.getSqrtPriceAtTick(INITIAL_TICK);\n        vm.prank(owner);\n        poolManager.initialize(key, sqrtP);\n        uint128 liquidity =\n            LiquidityAmounts.getLiquidityForAmount1(TickMath.getSqrtPriceAtTick(LOWER_TICK), sqrtP, 890_000_000e18);\n        adam.approve(address(lpRouter), type(uint256).max);\n        lpRouter.modifyLiquidity(\n            key,\n            ModifyLiquidityParams({\n                tickLower: LOWER_TICK, tickUpper: INITIAL_TICK, liquidityDelta: int256(uint256(liquidity)), salt: 0\n            }),\n            \"\"\n        );\n        vm.deal(address(this), 100 ether);\n    }\n\n    /// @dev The manager holds no ETH at all: the deploy seeded tokens only.\n    function test_firstBuyOnTokenOnlyManagerMustSucceed() public {\n        assertEq(address(poolManager).balance, 0, \"fresh manager holds no ETH\");\n        uint256 adamBefore = adam.balanceOf(address(this));\n\n        // An ordinary 1 ETH exact-input buy. The trade is valid; only the fee transfer has nothing to draw on.\n        swapRouter.swap{value: 1 ether}(\n            key,\n            SwapParams({zeroForOne: true, amountSpecified: -int256(1 ether), sqrtPriceLimitX96: TickMath.MIN_SQRT_PRICE + 1}),\n            PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false}),\n            \"\"\n        );\n\n        assertGt(adam.balanceOf(address(this)), adamBefore, \"buyer received ADAM\");\n        // The 20% launch fee must have been retained for the treasury one way or another: either as ETH\n        // already at the treasury or as a claim the treasury can redeem. It must not block the swap.\n        uint256 feeAsEth = treasury.balance;\n        uint256 feeAsClaim = poolManager.balanceOf(address(hook), 0) + poolManager.balanceOf(treasury, 0);\n        assertEq(feeAsEth + feeAsClaim, 0.2 ether, \"fee retained\");\n    }\n}","reproduction":"State: new PoolManager; AdamHook mined with flags 0x20cc; owner initializes ETH/ADAM at tick 177240; one ADAM-only position [108180,177240] of 890,000,000 ADAM; address(poolManager).balance == 0. Input: PoolSwapTest.swap{value: 1 ether}(key, zeroForOne=true, amountSpecified=-1e18, MIN_SQRT_PRICE+1). Expected: buyer receives ADAM and 0.2 ETH fee is retained for the treasury. Actual: revert HookCallFailed(NativeTransferFailed) from PoolManager.take called in AdamHook.beforeSwap (backtrace: treasury <- PoolManager.take <- AdamHook.beforeSwap <- PoolManager.swap). Same call succeeds once the manager holds >= 0.2 ETH from another pool, which is why the LocalV4 suite (400 ETH in IMD/PNKSTR pools) never sees it.","severity":"medium","snippet":"        if (fee != 0) poolManager.take(CurrencyLibrary.ADDRESS_ZERO, treasury, fee);","title":"AdamHook pays the ETH fee with poolManager.take inside the swap, so every fee-bearing swap reverts when the PoolManager holds less ETH than the fee"},{"citation":"resolved","description":"quoteMinOut uses max(checkpoint, spot) as the reference and the checkpoint is updated only after a successful buy (post-buy price, which is always a lower sqrtPrice than pre-buy). If IMD appreciates (fewer IMD per ETH, spot sqrtPrice below the checkpoint) by more than slippageBps (3%) minus price impact, the actual output is below the floor and unlockCallback reverts InsufficientOutput on every attempt. Halving the retry cap does not help: the floor is a price, not a size. Failures also reset after any 2h gap, so there is no escape through time either. In ADAM V2 the leg reroutes to PNKSTR/IMDSTR after 3 days of continuous failures, which the README documents. The IMDO brief removes rerouting and makes IMD the only buy leg while keeping 'checkpoint/spot min-out' and 'no owner or other withdrawal', so 40% of every processed ETH (plus REGEN overflow) accrues in `pending` with no path out until IMD falls back to within 3% of the last buy price. Adapter options that preserve the circuit-breaker intent: let the checkpoint decay toward spot over time, use a TWAP from the pool as the reference, or raise the floor's tolerance after N consecutive failures.","line":297,"path":"src/AdamTreasury.sol","reproduction":"State: ExtensionFixture (IMD pool 200 ETH deep at tick 54000, treasury checkpoint cp0 = slot0 at construction, alice staked 100,000,000). Input: one organic buy of 8 ETH on the IMD pool moves spot sqrtPrice to 0.9619*cp0 (about 7.5% fewer IMD per ETH). Then fund treasury 1 ether and call process() every 600s for 70 hours (420 calls). Expected: IMD leg eventually buys. Actual: leg(0).pending stays at its first value, imd.balanceOf(distributor) == 0 after every call; LegFailed(0, ...) each time with InsufficientOutput. Verified in test/scratch/TreasuryCheckpointStall.t.sol::test_imdLegStallsAfterPriceRise.","severity":"medium","snippet":"        if (sqrtPriceX96 < l.checkpointSqrtPriceX96) sqrtPriceX96 = l.checkpointSqrtPriceX96;","title":"Treasury buy floor ratchets only after a successful buy: a >~3% IMD price rise stalls the IMD leg on every retry; with the brief's 'no rerouting' the ETH is stuck until the market reverts"},{"citation":"resolved","description":"The brief fixes chainId 11155111 and the literals manager 0x000000000004444c5dc75cB358380D2e3dE08A90 and IMD 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7. Both are Ethereum mainnet addresses: `cast code` on Sepolia (publicnode) returns 0x for both, while the Sepolia v4 PoolManager 0xE03A1074c86CFeDd5C142C4F04F1a1536e203543 has code; on mainnet the IMD/ETH pool (fee 10000, spacing 200, no hook, poolId 0xb07d640f...) exists with a live slot0. AdamTreasuryV2's constructor reverts InvalidParameter when the manager has no code (line 64), so a factory launch on 11155111 fails. AdamTreasury (V1) instead skips checkpoint seeding (line 178), leaving checkpointSqrtPriceX96 == 0; quoteMinOut then reverts InvalidParameter (line 296) on every call, and with no owner or withdrawal path the leg's share of every processed ETH is stuck permanently. Whichever shape ImdoTreasury's FLAT constructor takes, the adapter and deployer must reconcile the chain id with the literals (or verify the Sepolia addresses for IMD and its pool) before the manifest is accepted.","line":16,"path":"launch.json","reproduction":"State: chain 11155111; `cast code 0x000000000004444c5dc75cB358380D2e3dE08A90` == 0x and `cast code 0xD34a99Bc0f67aE1bbd63C660e6d0b0dd03E263B7` == 0x (checked 2026-10-06 against https://ethereum-sepolia-rpc.publicnode.com). Input: construct AdamTreasuryV2 with that manager -> revert InvalidParameter. Alternative state (manager with code, IMD pool not initialized): construct AdamTreasury(dist, team, pm2, imd, 10000, 200, 0, 0, pnkstr, 0, 60, hook, 1000, 1e18, 300, 600) -> leg(0).checkpointSqrtPriceX96 == 0; send 1 ETH; process() -> leg(0).pending > 0 and quoteMinOut(0, 1e18) reverts InvalidParameter. Verified in test/scratch/TreasuryCheckpointStall.t.sol::test_v1LegDeadWhenPoolMissing.","severity":"medium","snippet":"        \"0x000000000004444c5dc75cb358380d2e3de08a90\",","title":"Manifest literals for the PoolManager and IMD have no code on the declared chain 11155111; the treasury either reverts at construction or seeds a zero checkpoint that disables the IMD leg forever"},{"citation":"resolved","description":"AdamDistributorV2.stakeFor only checks amount != 0 and that the beneficiary is not excluded; the caller supplies the ADAM, so in ADAM it is a free donation of stake. The IMDO brief adds 'the 24h lock reset by every stake' and 'stakeFor only by claim'. If the adapter keeps this function open (the natural copy), any address can call stakeFor(victim, 1 wei) once a day and the victim can never unstake. The README also states NFTClaim is the intended caller. Fix: require msg.sender == claim (immutable) in ImdoStaking.stakeFor, and make the lock reset apply to stake and stakeFor alike.","line":63,"path":"src/AdamDistributorV2.sol","reproduction":"State: bob holds 1 wei ADAM approved to the distributor; alice has 1,000,000e18 staked. Input: bob calls stakeFor(alice, 1). Expected (brief): revert, only the claim contract may call. Actual (code as arrived): succeeds, stakedBalance[alice] += 1 and Staked(alice, 1) emitted by a stranger; with a 24h lock reset per stake this would postpone alice's unstake indefinitely at a cost of 1 wei per day.","severity":"low","snippet":"    function stakeFor(address beneficiary, uint256 amount) external nonReentrant {","title":"stakeFor is callable by anyone for any beneficiary; harmless today, but it becomes a lock-reset griefing vector once the brief's 24h unstake lock is added"},{"citation":"resolved","description":"run() uses vm.envAddress for DEPLOYER, TEAM_WALLET, ADAM_TOKEN, TREASURY and vm.envOr for LIQUIDITY_ADAM, then vm.startBroadcast() with no explicit signer. The brief requires script/DeployImdo.s.sol to be a manual-signer script with no env or keys and every address, the opening tick and holderRoot as arguments (DeployAdamExtension.run(Config) is the right pattern). launchPool also mints the LP NFT to cfg.deployer, while the brief sends it to the dead address, and the position's amount1Max is uint128(cfg.liquidityAdam) with the 890,000,000 cap only enforced by the caller's balance check.","line":149,"path":"script/DeployAdam.s.sol","reproduction":"Input: `forge script script/DeployAdam.s.sol --sig run()` with DEPLOYER unset. Actual: revert from vm.envAddress('DEPLOYER'). With it set, the LP NFT recipient is cfg.deployer (params[0] owner field), not 0x...dEaD.","severity":"info","snippet":"        address deployer = vm.envAddress(\"DEPLOYER\");","title":"DeployAdam.run() reads signer and addresses from environment variables and broadcasts with an implicit key; the IMDO script must take them as arguments"}],"hash":"61a4ce25f1137435d99b941932d99efe0d3f0efcf9e63b1e878d417dadb76e15","nodeId":"9f9076ac-05ec-4de4-9cb5-316e9bf040fb","outcome":"completed","summary":"The findings file is in place at the repository root (excluded from git by the harness, as intended) and nothing in the tree was modified.\n\n## Audit of the ADAM repo as it arrived, before adaptation to IMDO\n\n**Outcome:** no critical or high defects. Three medium findings, one low, one informational, all written to `.imd-findings.json` with concrete reproductions. The most severe one carries a Foundry proof that fails on the current code.\n\n**Findings**\n\n- **Medium, hook fee funding.** `AdamHook` pays its ETH fee with `poolManager.take` inside the swap, before the router has settled, so the transfer is funded only by ETH the manager already holds from other pools. On a manager holding less ETH than the fee, every fee-bearing buy reverts with `NativeTransferFailed`. Proof: fresh PoolManager, ADAM-only launch position, 1 ETH buy reverts. The existing suite never sees it because its fixture pre-funds the manager with 400 ETH through the IMD/PNKSTR pools. The adapter should mint an ERC-6909 claim or fall back to one when the manager balance is short, with a permissionless redeem.\n- **Medium, buy floor stall.** The treasury's minimum output uses the greater of checkpoint and spot, and the checkpoint only moves after a successful buy. Once IMD rises more than about 3% past the last buy, every retry fails. Verified: after one 8 ETH organic buy, 420 retries over 70 hours bought nothing. ADAM escapes by rerouting after 3 days. The IMDO brief removes rerouting and makes IMD the only buy leg, so the IMD share would sit in `pending` with no exit until the market reverts.\n- **Medium, chain and literals disagree.** The brief fixes chain 11155111 but the manager and IMD literals are mainnet addresses. Checked live: both return empty code on Sepolia, the real Sepolia PoolManager has code, and the mainnet IMD/ETH pool at fee 10000 / spacing 200 exists. A V2-style constructor reverts there; a V1-style one seeds a zero checkpoint and the leg fails closed forever with no withdrawal path.\n- **Low.** `stakeFor` is open to anyone. Harmless in ADAM, but with the brief's 24-hour lock reset on every stake it becomes a one-wei-per-day griefing vector unless restricted to the claim contract.\n- **Info.** `DeployAdam.run()` reads env vars and broadcasts with an implicit signer, and mints the LP NFT to the deployer rather than the dead address. The IMDO script must take everything as arguments.\n\n**Also checked, no defect found:** hook fee math on all four swap shapes and the partial-fill guard, distributor accumulator and backlog stream edge cases, V2 treasury ETH conservation and keeper bounty accounting, callback and self-call guards, reentrancy surfaces, NFTClaim vesting and duplicate-id handling, runtime sizes under EIP-170, and an opcode scan showing no DELEGATECALL, CALLCODE or SELFDESTRUCT in any contract. The hook owner's `lowerFee` is a brief-requested trust assumption, not a finding.\n\n**One brief-level conflict for the adapter**, not filed as a finding since no code exists yet: the manifest resolves ImdoStaking's `claim` to `$owner`, so the script-deployed ImdoClaim would be unable to call `stakeFor`, and the owner wallet would be excluded from staking.\n\n**Coverage.** Read in full: LaunchToken, AdamHook, AdamDistributor, AdamDistributorV2, AdamTreasury, AdamTreasuryV2, AdamSplitOracle, NFTClaim, the interface, both scripts, HookMiner, launch.json, README, SELF_AUDIT, the test fixtures and the extension suites. Not reached in depth: the remaining unit, fuzz and invariant test files beyond their names and the fixtures they share. No Slither or Mythril ran, no fork test ran, and the Pashov specialty passes were applied by one reviewer, not independent agents. The full existing suite was run and passes, 151 tests.","treeHash":null,"usage":{"cachedInputTokens":1986322,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":51897,"runtime":"claude","turns":42,"wallClockMs":1394742}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"6ef494db85781eec","findings":[{"citation":"resolved","description":"Access-control / initialization gap in the deployment sequence. ImdoStaking fixes `claimContract` (the only address allowed to call `stakeFor`) to a CREATE address predicted from the deployer's next nonce, and ImdoClaim's constructor refuses any staking whose `claimContract()` is not itself. A Foundry broadcast is a sequence of separate transactions, so the prediction only holds if the very next transaction mined for the deployer is the ImdoClaim creation and that creation succeeds. The ImdoClaim constructor is time-sensitive (`launch_ < block.timestamp` reverts), yet `preflight` only requires `c.launch >= block.timestamp` at simulation time with no margin (line 150). If `launch` elapses between simulation and the claim transaction, or if the operator's wallet emits any other transaction in between, the nonce is consumed and `claimContract` is permanently a dead address: `stakeFor` is unreachable, no ImdoClaim can ever be bound (constructor check at src/ImdoClaim.sol:58), and the staking and treasury (which immutably references staking) must be redeployed. The in-script `address(d.claim) != expectedClaim` revert only protects the simulation, not the on-chain sequence. No unprivileged actor can trigger this and no funds are lost, hence low. Minimal fix preserving the ADAM prediction: require `c.launch >= block.timestamp + <margin>` (e.g. 1 hour) in `preflight`, and document that staking and claim must be the deployer's two consecutive transactions.","line":87,"path":"script/DeployImdo.s.sol","reproduction":"Scratch test test_predictedClaimAddressWithoutCodeIsPermanent (test/scratch/Probe.t.sol): deployer creates IMDOToken, predicts nonce+1, deploys ImdoStaking(claim=predicted). Any other creation then lands at `predicted` (asserted equal). Expected: a claim contract can still be wired. Actual: `new ImdoClaim(token, staking, nft, 2000, 0, now+1 days)` reverts InvalidConfiguration because `staking.claimContract() != address(this)`; and with `launch_ = block.timestamp - 1` the claim creation itself reverts InvalidConfiguration, which in a broadcast consumes the predicted nonce. `staking.claimContract()` stays `predicted` (no ImdoClaim code) permanently; `stakeFor` can never be called.","severity":"low","snippet":"        address expectedClaim = vm.computeCreateAddress(c.deployer, vm.getNonce(c.deployer) + 1);\n        d.staking = new ImdoStaking(address(d.token), c.imd, c.poolManager, expectedClaim, c.regenSafe);\n        d.claim = new ImdoClaim(address(d.token), address(d.staking), c.seatNFT, SEAT_SIZE, c.holderRoot, c.launch);\n        if (address(d.claim) != expectedClaim) revert InvalidConfiguration();","title":"Claim-address prediction has no time margin: a late or failed claim creation leaves staking's immutable claimContract pointing at a codeless address forever"},{"citation":"resolved","description":"`_rewardTokens` holds both IMD and the address(0) REGEN credit key, and `rewardTokens()` returns that whole list, but `isRewardToken[address(0)]` is never set. `claimReward(address(0))` and `notifyReward(address(0), x)` therefore revert NotRewardToken, while `_claim` silently skips the key. The behaviour (never paying ETH to holders) is the intended one; the defect is that the public enumeration advertises a key that every write path rejects, so an integrator or frontend iterating `rewardTokens()` and calling `claimReward` for each entry hits a revert. Fix: expose only claimable tokens from `rewardTokens()` (or document that address(0) is a credit key, not a reward token).","line":90,"path":"src/ImdoStaking.sol","reproduction":"Scratch test test_rewardTokensListsNonClaimableKey: `staking.rewardTokens()` returns [imd, 0x0] (length 2, list[1] == address(0)); `staking.isRewardToken(address(0))` is false; `staking.claimReward(address(0))` reverts `NotRewardToken(0x0)`. Expected by the view: both entries are reward tokens; actual: the second cannot be claimed or notified.","severity":"info","snippet":"        _rewardTokens.push(imd_);\n        _rewardTokens.push(address(0));\n        isRewardToken[imd_] = true;","title":"View/write asymmetry: rewardTokens() lists address(0) although it is not a reward token and claimReward(address(0)) reverts"},{"citation":"resolved","description":"Storage-variable lifecycle check (written but never read). `failingSince` is set on the first failure and cleared by `_resetFailures`, and `MIN_FAILURES` only saturates the `failures` counter; neither influences any branch now that rerouting was removed (the only reads of failure state are `l.failures != 0` and `l.lastFailure` in `_executeImd`). This is forgotten state from the base contract, not a defect in behaviour: retries, halving and the 2-hour failure gap work from `lastFailure`/`retryCap` alone. Removing the field and constant (or reading them) would make the leg struct match what the code actually does.","line":337,"path":"src/ImdoTreasury.sol","reproduction":"Cause one failed IMD buy (e.g. FaultyStaking.configure(false, true) in the existing ImdoTreasuryTest fixture, fund 1 ether, process()). `treasury.leg(0).failingSince` equals the failure timestamp and `failures` is 1, but no code path reads `failingSince`: searching src/ImdoTreasury.sol shows writes at lines 337 and 346 only. Expected for a retained field: it gates some retry decision; actual: none.","severity":"info","snippet":"            if (l.failures == 0) l.failingSince = uint64(block.timestamp);\n            l.lastFailure = uint64(block.timestamp);\n            if (l.failures < MIN_FAILURES) ++l.failures;","title":"Dead state retained from ADAM rerouting: Leg.failingSince is written on failure but never read by any decision"}],"hash":"6925d38dd8d17b63305bee59e4a9188859f80073d916509a28c60c82c9952996","nodeId":"dc76a91d-1e32-4764-aacc-c8b9647f1e1d","outcome":"completed","summary":"The review is complete and the findings file is in place. Only `.imd-findings.json` and my scratch probe under `test/scratch/` were created; no project file was changed (the tree shows no modifications, both paths are git-ignored).\n\n**Outcome:** I found no permission bypass, fund-loss path, or broken guarantee in the Access Control, Trust Gap, or Asymmetry areas. The role model behaves as the brief and README describe. Three minor items are recorded with reproductions.\n\n**What I checked**\n\n- Every role gate: `stakeFor` (claim only), `withdrawRegen` (safe only, bounded, pays safe), `setRegenCap` (safe only, in bounds, prospective only), `payOps`/`payOffsets` (role pulls own balance), `claimKeeper` (own debt only), hook `lowerFee`/`beforeInitialize` (owner only, once, ETH/IMDO), both `unlockCallback`s (manager plus an in-flight flag), `executeImd` (self-call only).\n- Paired surfaces: `stake`/`stakeFor`, `stake`/`unstake`, `claim`/`claimReward`/`exit` (ETH key never paid), `claimSeat`/`claimHolder` launch and deadline handling, `regenCreditOf` view versus settle math, constructor versus setter bounds, Split event versus state writes.\n- Trust seams: keeper timing versus checkpoint floor, regen cap changes versus pending and accrued ETH, safe withdrawals versus staker credit, the manifest's `$owner` holding claim, regenSafe, offsetsSafe and opsWallet.\n- Probes run in `test/scratch/Probe.t.sol` (all passed as expected): lifetime REGEN credit unchanged when another staker's unstake drops the pool below the 10M threshold, stream restart equals remaining reserve, the view/write asymmetry below, and the claim-address prediction failure mode.\n- Baseline: full default suite passes (98 tests, 18 suites). README has none of the forbidden words and no audit claim; no PNKSTR/IMDSTR/SplitOracle paths remain.\n\n**Findings recorded**\n\n1. **Low, script/DeployImdo.s.sol:87.** The staking contract's immutable `claimContract` is a nonce-predicted address, and `preflight` accepts a `launch` equal to the current timestamp with no margin. Because a broadcast is separate transactions and `ImdoClaim`'s constructor reverts once `launch` has passed, a late or interleaved transaction leaves `claimContract` codeless forever; `stakeFor` is dead and no claim contract can ever be bound. No attacker trigger and no fund loss. Fix is a time margin in `preflight` plus a documented two-consecutive-transaction requirement.\n2. **Info, src/ImdoStaking.sol:90.** `rewardTokens()` lists the address(0) credit key, but `isRewardToken` is never set for it, so `claimReward(address(0))` reverts. A view/write asymmetry for integrators; behaviour itself is correct.\n3. **Info, src/ImdoTreasury.sol:337.** `Leg.failingSince` and `MIN_FAILURES` are written or saturate a counter but nothing reads them since rerouting was removed. Dead state from the base contract, harmless.\n\n**Coverage:** 36 of 36 listed entry points answered (35 holds, 1 finding), plus four invariant/manifest rows. Nothing was left unreached. Items outside my area that I did not pursue in depth and that belong to the economics specialist: the strength of the decaying checkpoint floor against price manipulation around treasury buys, and the sole-staker capture of live rewards while the backlog gate is unmet, both of which are documented ADAM behaviours rather than permission defects.","treeHash":null,"usage":{"cachedInputTokens":2009023,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":56860,"runtime":"claude","turns":28,"wallClockMs":1134156}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"553a378efc6a19cb","findings":[],"hash":"7448bec725be3328ffa96f56f8fba94cbbb7b348d583a2bc91935b96c50b3f05","nodeId":"26c20e22-a276-4f9a-b6be-453af6037c1e","outcome":"failed","summary":"{\"type\":\"error\",\"status\":400,\"error\":{\"type\":\"invalid_request_error\",\"message\":\"The 'gpt-6-astra' model is not supported when using Codex with a ChatGPT account.\"}}","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"codex","turns":0,"wallClockMs":7822}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"e6de8d4c6cf97551","findings":[{"citation":"resolved","description":"After a successful buy, _executeImd records the IMD pool's current sqrtPrice as the new checkpoint. quoteMinOut then uses max(spot, checkpoint*7d/(7d+age)) as the price floor for all later buys. Nothing bounds how far the checkpoint may jump above the previous one, and the buy that sets it can be as small as 1 gwei (MIN_ETH_PER_BUY) because anyone can donate dust to the treasury and call process(). An unprivileged actor therefore bundles: (1) sell IMD into the ETH/IMD pool so IMD-per-ETH (sqrtPrice) rises, (2) call process() with a few gwei of unsplit ETH so the treasury buys a negligible amount at the inflated price and stores the inflated sqrtPrice as checkpoint, (3) buy the IMD back. From then on spot < floor, quoteMinOut demands an output the pool cannot give, every executeImd reverts InsufficientOutput and is caught, retryCap halves to 1 gwei and the leg stays stalled until the floor has decayed by the manipulation ratio (age >= 7 days * (ratio - 1)). ETH keeps accruing in _imdLeg.pending and is not lost, keepers keep earning bounties on new ETH, and ops/offsets/REGEN legs are unaffected, so this is a liveness griefing rather than a theft. It is unprofitable for the attacker (round-trip LP fees and price impact), which is why it is rated low. ADAM's checkpoint design is kept on purpose per the brief, but the brief did not ask for an unbounded upward update. Minimal fix preserving the design: when updating the checkpoint after a buy, cap it relative to the previous floor, e.g. newCp = min(spot, oldFloor * K) with K such as 2, or set the checkpoint from the pre-swap price captured in unlockCallback only when the buy was at least a meaningful size (e.g. >= maxEthPerBuy/100).","line":332,"path":"src/ImdoTreasury.sol","reproduction":"Measured on the repository's LocalV4 fixture (IMD pool seeded full-range with 200 ETH at tick 54000; treasury maxEthPerBuy 1 ETH, slippage 300 bps, cooldown 600 s). 1) Fund treasury with 2.5 ETH, warp 601 s, process(): honest buy, checkpoint sqrtP = 1172928677002280207726384365093. 2) Attacker sells 30,000e18 IMD into the IMD pool: sqrtP becomes 1963733960245980566206534025674 (1.674x). 3) Attacker sends 3 gwei to the treasury, warps 601 s, calls process(): the treasury buys ~1.2 gwei of ETH worth of IMD and stores checkpoint 1961843107560085070987448782642. 4) Attacker buys back the 30,000 IMD; sqrtP returns to 1163049892162407941209519832559; attacker's total net cost 2.425 ETH. 5) Every subsequent process() (one per 601 s, each with 0.01 ETH new fees) fails the IMD leg with InsufficientOutput: 665 consecutive failed buys (about 4.6 days) before the decayed floor dropped under spot and a buy succeeded; 4.31 ETH had accumulated in leg(0).pending in the meantime. Expected: a buy of ~1 gwei should not be able to move the floor used for 1 ETH buys by 67%; the leg should keep buying at the real market price. Actual: the IMD leg is dead for days per few ETH spent by the griefer and can be re-stalled at will.","severity":"low","snippet":"            (l.checkpointSqrtPriceX96,,,) = poolManager.getSlot0(l.key.toId());\n            l.checkpointAt = uint64(block.timestamp);","title":"IMD-leg checkpoint is taken from the post-swap spot without any bound, so one sandwiched dust buy inflates the floor and stalls every IMD buy for days"},{"citation":"resolved","description":"preflight only rejects c.launch strictly below the simulation block timestamp, and ImdoClaim's constructor (src/ImdoClaim.sol:56) reverts with InvalidConfiguration when launch_ < block.timestamp at execution time. A Foundry broadcast is a sequence of separate transactions mined over several blocks, so the token and staking creations can land, then the claim creation lands in a later block whose timestamp exceeds launch and reverts. The failed CREATE still consumes the deployer nonce, so the address that ImdoStaking stored as its immutable claimContract (predicted at script line 87 from nonce+1) can never receive code: stakeFor is permanently unusable on that staking instance and the token and staking deployments are wasted. docs/DEPLOYMENT.md tells the operator to choose enough time, but the script does not enforce a margin although it is the only place that can.","line":150,"path":"script/DeployImdo.s.sol","reproduction":"Simulation at timestamp T0 with c.launch = T0 + 30: preflight passes (T0 + 30 >= T0). Broadcast: tx1 new IMDOToken mined at T0 + 12, tx2 new ImdoStaking(…, expectedClaim = computeCreateAddress(deployer, n+2), …) mined at T0 + 24, tx3 new ImdoClaim(…, launch = T0 + 30) mined at T0 + 36 -> constructor check launch_ < block.timestamp is true -> revert InvalidConfiguration, nonce n+2 consumed, no code at expectedClaim, and staking.claimContract() points at an address that can never be deployed. Expected: the script refuses launch values that leave no margin (e.g. require c.launch >= block.timestamp + 1 hours) so the sequence cannot fail at the claim step. Actual: any launch in [T_sim, T_mined(claim)) passes preflight and bricks the staking instance.","severity":"low","snippet":"                || c.seatNFT.code.length == 0 || c.launch < block.timestamp","title":"Deploy script accepts launch == now; a launch timestamp that passes simulation but is behind the mined block makes ImdoClaim's creation revert and permanently burns the claim address baked into ImdoSt"}],"hash":"95cfb5d9ec1eca90f1d2353fef1103321d7a387fb5e2039cbd4a85d581f7ba94","nodeId":"c65df050-6d7e-4ccb-8e6f-555af95d5262","outcome":"completed","summary":"Review complete. The findings file is written at `.imd-findings.json` with two low findings and 40 coverage rows, and my scratch tests are removed so no other file changed.\n\n**What I checked.** I read the Senior Auditor's Mindset and the Execution Trace, Periphery and First Principles guides, then traced every state-changing entry point of the five contracts and the deploy script. I verified the trickiest claims with scratch tests on the repository's local Uniswap v4 fixture: the exact 50 bps bounty and 1000/2500/2500/4000 split, the per-epoch REGEN cap with overflow into the IMD leg, and REGEN credit monotonicity when another staker's exit drops the total below the backlog threshold. All held. The existing suite of 98 tests also passes.\n\n**Findings, both low.**\n\n- **Checkpoint inflation stalls IMD buys** at `src/ImdoTreasury.sol:332`. The post-buy checkpoint is unbounded and can be set by a buy as small as a few gwei of donated ETH. An unprivileged actor sells IMD into the ETH/IMD pool, triggers a dust buy, buys back, and the inflated floor makes every later buy revert until it decays. Measured in the fixture: a 2.4 ETH round trip stalled the IMD leg for about 4.6 days with over 4 ETH piling up in pending. No funds are lost and the attacker gains nothing, hence low. Suggested fix is to bound the checkpoint jump relative to the previous floor, or require a meaningful buy size before updating it.\n- **Deploy script has no launch margin** at `script/DeployImdo.s.sol:150`. A launch timestamp that passes simulation but is behind the block that mines the claim creation makes the ImdoClaim constructor revert. The failed CREATE burns the nonce, so the claim address baked into ImdoStaking can never hold code and that staking instance is permanently without a working stakeFor. The docs warn about timing, but only the script can enforce it.\n\n**Coverage.** Every listed entry point has a row. All hold except `ImdoTreasury.process()` which carries finding 1, plus a script row for finding 2 and three invariant rows that hold. Nothing was left unreached. Neither finding reached critical or high, so no proof test files were attached.","treeHash":null,"usage":{"cachedInputTokens":2437342,"inputTokens":546,"model":"claude-fable-5-1","outputTokens":63770,"runtime":"claude","turns":36,"wallClockMs":1494501}},{"artifacts":[],"attempt":2,"bundleHash":null,"device":"3f91b58cf7cd2d45","findings":[{"citation":"resolved","description":"Merged from audit_math (medium), audit_economics (medium) and audit_flow (low): one root cause. quoteMinOut() (lines 281-289) protects each treasury IMD purchase with max(spot, checkpoint * 7d / (7d + age)) minus 300 bps, and README line 35 presents the checkpoint as a same-block reference that relaxes only over the 7-day decay. But after every successful buy _executeImd() overwrites the checkpoint with whatever slot0 holds immediately after the treasury's own swap, with no relation to the floor it just enforced or to the previous checkpoint. Because process() is permissionless and the buy that writes the checkpoint can be as small as 1 gwei, the input to the reference is attacker-controlled in both directions. Downward (value extraction / griefing): each cooldown a sandwicher buys IMD until the sqrt-price sits just above floor*0.985 (output still >= minOut), lets process() buy at that price, then sells back; the checkpoint now equals the manipulated post-swap price, so the next floor is ~1-3% lower, compounding every 600 s instead of the ~0.1% per 600 s the decay permits. In the local 200 ETH fixture the checkpoint is 77.45% of the market sqrt-price (60% in price terms) after 20 rounds (3h20m) and the treasury receives 84.9 IMD where the honest fill of the same ETH was 131-138 IMD, i.e. stakers lose ~38% of each buy; the attacker pays pool fees on the push volume, so on a deep pool this is griefing and on a thin pool (in-range ETH depth below roughly 100 ETH) it is direct extraction. Upward (liveness): the attacker sells IMD to push the sqrt-price up (1.5x in the reproduction), sends a few gwei to the treasury, calls process() so the treasury buys ~1 gwei of IMD and stores the inflated price as checkpoint, then buys the IMD back. From then on spot < floor, every executeImd reverts InsufficientOutput and is caught, retryCap collapses to 1 gwei (lastFailure is refreshed every 600 s so the 2-hour reset never fires) and the IMD leg is dead until the floor has decayed under spot: 489 consecutive failed process() calls (3.4 days) in the reproduction, 3.17 ETH parked in leg(0).pending meanwhile, for a round-trip cost of about 1.9 ETH in fees. No ETH is lost in either direction, so medium: broken stated guarantee and loss of IMD value to stakers under attacker-chosen conditions. Minimal fix preserving the design: when refreshing after a buy, clamp the new checkpoint to the floor computed for this call (never lower) and to a bounded multiple of it (never a free jump up), e.g. floorNow = cp*7d/(7d+age); post = slot0; cp = clamp(post, floorNow, floorNow*K); the checkpoint still tracks genuine moves through the decay and through bounded steps. The attached proof passes with cp = max(post, floorNow).","line":332,"path":"src/ImdoTreasury.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity 0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {PoolManager} from \"@uniswap/v4-core/src/PoolManager.sol\";\nimport {IPoolManager} from \"@uniswap/v4-core/src/interfaces/IPoolManager.sol\";\nimport {IHooks} from \"@uniswap/v4-core/src/interfaces/IHooks.sol\";\nimport {PoolKey} from \"@uniswap/v4-core/src/types/PoolKey.sol\";\nimport {PoolIdLibrary} from \"@uniswap/v4-core/src/types/PoolId.sol\";\nimport {Currency, CurrencyLibrary} from \"@uniswap/v4-core/src/types/Currency.sol\";\nimport {SwapParams, ModifyLiquidityParams} from \"@uniswap/v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"@uniswap/v4-core/src/libraries/TickMath.sol\";\nimport {StateLibrary} from \"@uniswap/v4-core/src/libraries/StateLibrary.sol\";\nimport {FullMath} from \"@uniswap/v4-core/src/libraries/FullMath.sol\";\nimport {PoolSwapTest} from \"@uniswap/v4-core/src/test/PoolSwapTest.sol\";\nimport {PoolModifyLiquidityTest} from \"@uniswap/v4-core/src/test/PoolModifyLiquidityTest.sol\";\nimport {LiquidityAmounts} from \"@uniswap/v4-periphery/src/libraries/LiquidityAmounts.sol\";\nimport {MockERC20} from \"solmate/src/test/utils/mocks/MockERC20.sol\";\nimport {IMDOToken} from \"src/IMDOToken.sol\";\nimport {ImdoStaking} from \"src/ImdoStaking.sol\";\nimport {ImdoTreasury} from \"src/ImdoTreasury.sol\";\n\n/// @notice ImdoTreasury._executeImd refreshes the IMD checkpoint from the post-swap spot with no bound\n/// relative to the floor it just enforced. A sandwicher who pushes the price ~1% below the floor\n/// each cooldown ratchets the floor down geometrically; the treasury then buys ever less IMD per ETH.\n/// Fails on the current code (checkpoint ends at ~77% of the market sqrt-price after 20 rounds);\n/// passes once the refresh is bounded below by the decayed floor used in the same call.\ncontract CheckpointRefreshProof is Test {\n    using PoolIdLibrary for PoolKey;\n    using StateLibrary for IPoolManager;\n\n    PoolManager manager;\n    PoolSwapTest swapRouter;\n    PoolModifyLiquidityTest lpRouter;\n    MockERC20 imd;\n    IMDOToken imdo;\n    ImdoStaking staking;\n    ImdoTreasury treasury;\n    PoolKey imdKey;\n    address alice = makeAddr(\"alice\");\n\n    receive() external payable {}\n\n    function setUp() public {\n        vm.warp(1_800_000_000);\n        manager = new PoolManager(address(this));\n        swapRouter = new PoolSwapTest(manager);\n        lpRouter = new PoolModifyLiquidityTest(manager);\n        vm.deal(address(this), 100_000 ether);\n        imd = new MockERC20(\"Identity.md\", \"IMD\", 18);\n        imd.mint(address(this), 1e36);\n        imd.approve(address(lpRouter), type(uint256).max);\n        imd.approve(address(swapRouter), type(uint256).max);\n        imdKey = PoolKey(CurrencyLibrary.ADDRESS_ZERO, Currency.wrap(address(imd)), 10000, 200, IHooks(address(0)));\n        uint160 sqrtP = TickMath.getSqrtPriceAtTick(54000);\n        manager.initialize(imdKey, sqrtP);\n        uint128 liquidity = LiquidityAmounts.getLiquidityForAmounts(\n            sqrtP, TickMath.getSqrtPriceAtTick(-887200), TickMath.getSqrtPriceAtTick(887200), 200 ether, type(uint128).max\n        );\n        lpRouter.modifyLiquidity{value: 200 ether}(\n            imdKey, ModifyLiquidityParams(-887200, 887200, int256(uint256(liquidity)), 0), \"\"\n        );\n        imdo = new IMDOToken();\n        staking = new ImdoStaking(\n            address(imdo), address(imd), address(manager), makeAddr(\"claim\"), makeAddr(\"regenSafe\")\n        );\n        treasury = new ImdoTreasury(\n            address(staking),\n            makeAddr(\"ops\"),\n            makeAddr(\"offsets\"),\n            makeAddr(\"regenSafe\"),\n            address(manager),\n            address(imd),\n            10000,\n            200,\n            address(0),\n            1 ether,\n            300,\n            600,\n            0.5 ether,\n            0.05 ether,\n            5 ether\n        );\n        imdo.transfer(alice, 1e18);\n        vm.startPrank(alice);\n        imdo.approve(address(staking), type(uint256).max);\n        staking.stake(1e18);\n        vm.stopPrank();\n    }\n\n    function _spot() internal view returns (uint160 p) {\n        (p,,,) = IPoolManager(address(manager)).getSlot0(imdKey.toId());\n    }\n\n    function _limitSwap(bool zeroForOne, uint160 limit, uint256 ethValue) internal {\n        swapRouter.swap{value: ethValue}(\n            imdKey,\n            SwapParams({zeroForOne: zeroForOne, amountSpecified: -int256(1e30), sqrtPriceLimitX96: limit}),\n            PoolSwapTest.TestSettings({takeClaims: false, settleUsingBurn: false}),\n            \"\"\n        );\n    }\n\n    function _fund(uint256 amount) internal {\n        (bool ok,) = address(treasury).call{value: amount}(\"\");\n        require(ok);\n    }\n\n    function test_checkpointRatchetsDownUnderSandwichedBuys() public {\n        _fund(1 ether);\n        treasury.process(); // honest buy: checkpoint == market\n        uint160 market = _spot();\n        uint256 firstOut;\n        uint256 lastOut;\n        for (uint256 r; r < 20; ++r) {\n            vm.warp(vm.getBlockTimestamp() + 600);\n            _fund(1 ether);\n            ImdoTreasury.Leg memory l = treasury.leg(0);\n            uint256 floor =\n                FullMath.mulDiv(l.checkpointSqrtPriceX96, 7 days, 7 days + vm.getBlockTimestamp() - l.checkpointAt);\n            // front-run: buy IMD until the sqrt-price sits 0.9% under the floor the treasury will enforce\n            uint160 target = uint160(floor * 991 / 1000);\n            if (target < _spot()) _limitSwap(true, target, 5000 ether);\n            uint256 before = imd.balanceOf(address(staking));\n            treasury.process();\n            assertEq(treasury.leg(0).pending, 0, \"treasury buy must still fill\");\n            uint256 out = imd.balanceOf(address(staking)) - before;\n            if (r == 2) firstOut = out;\n            lastOut = out;\n            // back-run: sell IMD until the pool is back at the market price\n            _limitSwap(false, market, 0);\n        }\n        uint160 cp = treasury.leg(0).checkpointSqrtPriceX96;\n        emit log_named_uint(\"checkpoint / market (bps)\", uint256(cp) * 10_000 / market);\n        emit log_named_uint(\"IMD bought in round 2\", firstOut);\n        emit log_named_uint(\"IMD bought in round 19\", lastOut);\n        // 20 cooldowns are 12,000 s; the 7-day decay alone allows at most ~2% of relaxation.\n        assertGe(uint256(cp), uint256(market) * 95 / 100, \"checkpoint fell far below what the decay allows\");\n    }\n}","reproduction":"Local v4 PoolManager; ETH/IMD pool fee 10000 spacing 200 initialised at tick 54000 and seeded full-range with 200 ETH; ImdoStaking with one 1e18 IMDO staker; ImdoTreasury with the manifest literals (1 ETH buy cap, 300 bps, 600 s cooldown, REGEN cap 0.5 ETH). Downward: send 1 ETH, process() (honest buy; market sqrtP 1176417014792598596257319829901). Then 20 times: warp +600 s; send 1 ETH; compute floor = cp*7d/(7d+age) from leg(0); swap zeroForOne exact-input with sqrtPriceLimitX96 = floor*991/1000 (push); process() (fills, pending == 0); swap oneForZero with sqrtPriceLimitX96 = market (restore). Expected per README: checkpoint stays within the 7-day decay of the market (>= 98% after 12,000 s) and every buy pays at least 0.97*floor. Actual: leg(0).checkpointSqrtPriceX96 = 911173681673239411629601839407 = 77.45% of market; IMD received for the ~0.647 ETH buy falls from 131179588254583985619 (round 2) to 84896715728792911209 (round 19). Upward: after an honest buy (cp 1172928677002280207726384365093), sell IMD with limit 1.5*spot (22,248e18 IMD sold, spot 1759393015503420311589576547639), send 3 gwei to the treasury, warp +600, process(): cp becomes 1757875050926914598303133772873; buy back exactly 22,248e18 IMD (spot 1165486830218692271168562251537). Then every 600 s send 0.01 ETH and call process(): expected a buy within a few calls; actual 489 consecutive LegFailed(InsufficientOutput) calls, first success after 294,000 s (3.4 days), leg(0).pending 3.169 ETH at that point, retryCap 1 gwei throughout. Run: forge test --match-path test/scratch/CheckpointRefresh.t.sol -vv (fails on this code with 'checkpoint fell far below what the decay allows'; passes once the refresh is floored).","severity":"medium","snippet":"            (l.checkpointSqrtPriceX96,,,) = poolManager.getSlot0(l.key.toId());\n            l.checkpointAt = uint64(block.timestamp);","title":"IMD-buy checkpoint is refreshed from the post-swap spot with no bound: a sandwicher ratchets the min-out floor down ~1-3% per cooldown, and one inflated dust buy stalls every IMD buy for days"},{"citation":"resolved","description":"Merged from audit_math and audit_economics (both low). stakeFor() is restricted to the immutable claimContract and, as the brief requires, resets the beneficiary's whole-position lock on every call. In the manual script deployment the claim caller is ImdoClaim, which only ever stakes for msg.sender, so a reset is self-inflicted. In launch.json contracts[0].constructorArgs[3] (claim) and [4] (regenSafe) are both \"$owner\", an externally controlled account. That account may call stakeFor(victim, 1) at any time (it is excluded from staking itself but can hold IMDO), and each call pushes unlockTime[victim] 24 h into the future, so unstake() and exit() revert StakeLocked for as long as the owner keeps spending 1 wei per day. This is a privileged-power trust assumption introduced by the manifest substitution, not a bypass: README line 65 lists stakeFor only as 'on behalf of beneficiaries' and the launch.json notes say the owner 'can stakeFor' without saying it can freeze every staker's principal. Report it in README/notes as a trust assumption, or keep the brief's rule while blunting the grief (e.g. a third-party stakeFor only extends the lock when the position was zero, or must stake at least some minimum fraction of the existing position).","line":168,"path":"src/ImdoStaking.sol","reproduction":"Deploy ImdoStaking(imdo, imd, manager, claim=OWNER, regenSafe=OWNER) exactly as launch.json does. Alice stakes 1,000,000e18 IMDO at t0 (unlockTime = t0 + 24 h). OWNER holds 1000 wei IMDO and approves staking. For d in 0..29: warp to t0 + d days + 23 h; OWNER calls stakeFor(alice, 1). At t0 + 30 days Alice calls unstake(1). Expected: success, her own last stake was 30 days ago. Actual: revert StakeLocked(t0 + 29 days + 23 h + 24 h). Verified in test/scratch/Probe.t.sol::test_ownerLockGrief (passes = grief reproduced).","severity":"low","snippet":"        unlockTime[beneficiary] = block.timestamp + LOCK_DURATION;","title":"Manifest wires $owner as the staking claim caller, so one privileged address can reset any staker's 24h lock for 1 wei a day and freeze their principal indefinitely"},{"citation":"resolved","description":"Merged from audit_permissions and audit_flow (both low). ImdoStaking fixes claimContract (the only stakeFor caller) to vm.computeCreateAddress(deployer, nonce+1) at script line 87, and ImdoClaim's constructor refuses to bind to a staking whose claimContract() is not itself and reverts InvalidConfiguration when launch_ < block.timestamp (src/ImdoClaim.sol:56-58). preflight() only rejects c.launch strictly below the simulation timestamp, with no margin. A Foundry broadcast is a sequence of separate transactions: the token and staking creations can be mined, then the claim creation lands in a later block whose timestamp exceeds launch (or any other deployer transaction slips in between) and reverts; the failed CREATE still consumes the nonce, so the predicted address can never receive code. staking.claimContract() then points at a codeless address forever: stakeFor is unreachable, no ImdoClaim can ever be bound, and token and staking must be redeployed (the in-script address check at line 90 only protects the simulation). No unprivileged actor can trigger it and no funds are lost, hence low. Fix: require c.launch >= block.timestamp + a margin (e.g. 1 hour) in preflight and state in docs/DEPLOYMENT.md that staking and claim must be consecutive deployer transactions.","line":150,"path":"script/DeployImdo.s.sol","reproduction":"Simulation at T0 with c.launch = T0 (passes preflight: T0 < T0 is false). Broadcast: tx1 new IMDOToken; tx2 new ImdoStaking(..., expectedClaim = computeCreateAddress(deployer, n+1), ...); tx3 new ImdoClaim(..., launch = T0) mined at T0 + 12 -> constructor check launch_ < block.timestamp true -> revert InvalidConfiguration, nonce n+1 consumed. Scratch test test/scratch/Probe.t.sol::test_claimPredictionBurned: deployer creates IMDOToken, predicts nonce+1, creates ImdoStaking(claim = predicted); warp +12 s past launch; new ImdoClaim(..., launch) reverts InvalidConfiguration; any other creation (new IMDOToken()) then lands exactly at `predicted` (assertEq holds); a later new ImdoClaim(..., now + 1 day) also reverts InvalidConfiguration because staking.claimContract() != address(this); staking.claimContract() still equals the codeless `predicted`. Expected: the script refuses launch values that leave no margin; actual: any launch in [T_sim, T_mined(claim)) passes preflight and bricks the staking instance.","severity":"low","snippet":"                || c.seatNFT.code.length == 0 || c.launch < block.timestamp","title":"Deploy script accepts launch == now, so a claim creation that lands after `launch` reverts on chain and permanently burns the claim address baked into ImdoStaking"},{"citation":"resolved","description":"From audit_math (info). The intake cap at line 168 is derived so that 40% of net equals maxEthPerBuy (cap = 2.512562814070351758 ETH, imd = 1.0 ETH). It ignores the REGEN overflow that line 187 routes into the same leg once regenAccrued[epoch] reaches regenCap (0.5 ETH, hit on the first saturated call since regen per call is 0.625 ETH). Every further call that epoch adds 1.0 + 0.625 ETH to the leg while _executeImd buys at most maxEthPerBuy. Not a loss: the accounting invariant holds and pending is bought down at 1 ETH per cooldown once inflow drops; reported so the author sizes regenCap / maxEthPerBuy / cooldown knowingly for launch-week inflow.","line":187,"path":"src/ImdoTreasury.sol","reproduction":"LocalV4 fixture with the manifest literals and one staker. Every 600 s send exactly 2512562814070351758 wei to the treasury and call process(). Observed leg(0).pending after calls 0..5: 0.125, 0.750, 1.375, 2.000, 2.625, 3.250 ETH; totalRegenNotified stays 0.5 ETH after call 0; unsplitEth() stays 0. Expected from the cap derivation: pending returns to 0 each call. Verified in test/scratch/Probe.t.sol::test_pendingGrowsWhenRegenCapSaturated.","severity":"info","snippet":"            _imdLeg.pending += imd + overflow;","title":"With the REGEN epoch cap saturated the IMD leg takes in 1.625 ETH per process() but buys at most 1 ETH, so leg(0).pending grows 0.625 ETH per call under sustained inflow"},{"citation":"resolved","description":"From audit_permissions (info). _rewardTokens holds IMD and the address(0) REGEN credit key so that _settle/_claim iterate both, but isRewardToken[address(0)] is never set and _claim skips the key. The behaviour (holders are never paid ETH) is intended; the defect is that the public enumeration lists a key every write path rejects, so an integrator iterating rewardTokens() and calling claimReward for each entry hits a revert. Expose only claimable tokens from rewardTokens(), or document that address(0) is a credit key.","line":91,"path":"src/ImdoStaking.sol","reproduction":"staking.rewardTokens() returns [imd, 0x0] (length 2, [1] == address(0)); staking.isRewardToken(address(0)) == false; staking.claimReward(address(0)) reverts NotRewardToken(0x0). Verified in test/scratch/Probe.t.sol::test_viewsAndDeadState.","severity":"info","snippet":"        _rewardTokens.push(address(0));","title":"rewardTokens() advertises address(0) although it is not a reward token: claimReward(address(0)) and notifyReward(address(0),x) revert NotRewardToken"},{"citation":"resolved","description":"From audit_permissions (info). failingSince is set on the first failure (line 337) and cleared in _resetFailures (line 346); MIN_FAILURES only saturates the failures counter. The only failure-state reads are `l.failures != 0` and `l.lastFailure` in _executeImd (line 322); retries, halving and the 2-hour gap work from lastFailure/retryCap alone. Harmless forgotten state; removing the field and constant (or reading them) makes the struct match what the code does.","line":337,"path":"src/ImdoTreasury.sol","reproduction":"Cause one failed IMD buy (FaultyStaking.configure(false, true) in ImdoTreasuryTest, fund 1 ether, process()): treasury.leg(0).failingSince equals the failure timestamp and failures == 1, yet grep of src/ImdoTreasury.sol shows failingSince written at lines 337 and 346 only and MIN_FAILURES read only at line 339; no branch depends on either.","severity":"info","snippet":"            if (l.failures == 0) l.failingSince = uint64(block.timestamp);","title":"Leg.failingSince and MIN_FAILURES are written but never read: dead rerouting state retained from ADAM"},{"citation":"resolved","description":"From audit_economics (low), recalibrated to info: this is ADAM's direct-distribution rule, the brief asks for ADAM's behaviour, and README line 43 documents it ('New rewards with any existing stake distribute immediately'). The gap is only that the MIN_BACKLOG_STAKE floor protects launch-time rewards notified with zero stake but not the live flow once any dust stake exists: at launch (20% anti-snipe fees, up to 1 ETH of IMD per 600 s, up to 0.5 ETH REGEN per epoch) whoever stakes first with dust receives everything until another holder stakes. If intended, say so explicitly in README; otherwise route direct distributions to unallocated while totalStaked < MIN_BACKLOG_STAKE.","line":226,"path":"src/ImdoStaking.sol","reproduction":"Fresh ImdoStaking: notifyReward(imd, 100e18) with totalStaked == 0 -> unallocated[imd] == 100e18 (gated). Sniper stakes 1 wei IMDO. notifyReward(imd, 1000e18) and notifyRegen{value: 10 ether}(). Actual: earned(sniper, imd) == 1000e18 and regenCreditOf(sniper) == 10 ether while the 100e18 backlog is still gated. Verified in test/scratch/Probe.t.sol::test_tinyFirstStaker.","severity":"info","snippet":"        if (totalStaked == 0) {","title":"The 10,000,000 IMDO floor gates only the backlog; a 1 wei stake captures 100% of every IMD reward and REGEN credit notified while it is the only stake"}],"hash":"9d6254358b64d19c91435efb080eef018d5837ed19a4a0bb7c6e46349ac890bc","nodeId":"d6e7cd13-f096-43bc-a3c2-e09329276d91","outcome":"completed","summary":"The review is complete. The findings file is written, the source tree is unchanged, and every snippet, line and proof was checked against the tree.\n\n**Outcome.** Of the eleven specialist findings, all reproduced, and they collapse into seven after merging duplicates. One is medium, two are low, four are informational. My own pass over every entry point found nothing the specialists missed. The full default suite passes (98 tests), and the manifest's use of `LaunchToken`, fee 3000 and the nominal price matches the launch reference, so those are not defects.\n\n**Findings written to `.imd-findings.json`:**\n\n1. **Medium, with proof.** The treasury refreshes the IMD-buy checkpoint from the post-swap spot with no bound (`src/ImdoTreasury.sol:332`). Three specialists reported this as two symptoms of one root cause, so they are merged. Downward, a sandwicher pushes the price just under the floor each cooldown and the floor ratchets down geometrically. After 20 rounds the checkpoint sits at 77% of the market sqrt-price and the treasury receives 85 IMD where the honest fill was 131 to 138. Upward, one dust buy at an inflated price stalls every IMD buy for 489 consecutive calls, about 3.4 days. The attached test fails on the current code and passes once the refresh is floored at the decayed checkpoint, which I confirmed by applying and then reverting a temporary patch.\n2. **Low.** The manifest wires `$owner` as the staking claim caller, so that one address can reset any staker's lock for 1 wei a day and freeze their principal. This is a trust assumption the README and notes do not disclose.\n3. **Low.** The deploy script accepts a launch timestamp equal to now, so a claim creation that lands after it reverts on chain and burns the claim address baked into staking. Merged from two specialists.\n4. **Info.** With the REGEN cap saturated, the IMD leg takes in 1.625 ETH per call but buys at most 1 ETH.\n5. **Info.** The reward-token view lists address(0) although every write path rejects it.\n6. **Info.** The failure-start timestamp and minimum-failures constant are dead state.\n7. **Info.** A 1 wei stake captures all live rewards while it is the only stake. This is documented ADAM behaviour, so I lowered it from the specialist's low.\n\n**Coverage.** All 36 listed entry points have a row plus three invariant rows: 36 hold, 3 point at findings, none unreached.","treeHash":null,"usage":{"cachedInputTokens":3169066,"inputTokens":642,"model":"claude-fable-5-1","outputTokens":58280,"runtime":"claude","turns":37,"wallClockMs":1053998}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"1559912e747bbcd4","findings":[{"citation":"resolved","description":"quoteMinOut() (src/ImdoTreasury.sol:281-284) floors the quote sqrt-price at checkpoint * 7d / (7d + age). The checkpoint is written at line 332 from getSlot0 immediately after the treasury's own swap, in the same block, with no bound on how far it may rise relative to the previous checkpoint. Whoever controls the IMD pool price in that block controls the floor for the following days. Numerical seam (boundary x precision x invariant): the decay is rational in sqrt-price, so the price floor relaxes as (7d/(7d+t))^2. For a floor that is k times the true spot price, buys resume only after t >= 7d*(sqrt(k*0.97/(1-impact)) - 1): k=2 -> ~2.9 days, k=10 -> ~15 days, k=100 -> ~63 days. During the stall every process() call fails the IMD leg with InsufficientOutput, retryCap collapses to MIN_ETH_PER_BUY (1 gwei) because lastFailure is refreshed every 600 s so the 2-hour reset never fires, and the ETH sits in _imdLeg.pending. The attack needs no privilege: the attacker sells IMD (oneForZero, sqrtP up), calls process() themselves (anyone may), then buys the IMD back; the checkpoint retains the inflated post-swap price. Cost on the local 200-ETH-deep fixture is ~1.99 ETH for a 3.05-day stall (the treasury's 1 ETH buy executes at 2x in the treasury's favour, plus two 1% pool fees). Cheaper on a thinner pool and cheaper still when retryCap is already at 1 gwei (then only pool fees are paid). An organic IMD price doubling (IMD per ETH halves) produces the same stall of ~2.75 days with no attacker. Funds are not lost; the IMD purchases are delayed and the keeper bounty continues to be paid on new ETH. README line 35 names manipulation as an operational risk, but the duration and the fact that the refresh itself is the manipulable input are not stated. Minimal fix preserving the design: bound the per-refresh rise of the checkpoint (e.g. new checkpoint = min(postSwapSqrtPrice, oldCheckpoint * (1 + maxRiseBps/BPS)), or use min(pre-swap, post-swap) slot0 when refreshing), and/or shorten CHECKPOINT_DECAY so a 2x move clears within hours rather than days.","line":332,"path":"src/ImdoTreasury.sol","reproduction":"LocalV4 fixture (test/utils/LocalV4.sol: IMD pool fee 10000 / spacing 200 seeded with 200 ETH full range at tick 54000, treasury with maxEthPerBuy 1 ETH, slippage 300, cooldown 600). 1) send 1 ETH to treasury; process() -> buy succeeds, checkpointSqrtPriceX96 = 1176417014792598596257319829901. 2) warp +600 s; send 1 ETH to treasury. 3) attacker sells 10 x 2,000e18 IMD (oneForZero exact input) -> sqrtP = 1703620536955065501910752936951 (1.45x, price 2.1x). 4) attacker calls treasury.process(): IMD leg buys 1 ETH at the pushed price and sets checkpointSqrtPriceX96 = 1698783458824452163630491664065. 5) attacker buys back exactly the 20,000e18 IMD sold -> sqrtP = 1166254648559334077111872364010; attacker net ETH cost 1.987 ETH, IMD balance unchanged. 6) send 1 ETH to treasury and call process() every 600 s: expected (spec: 'halving retries; no rerouting') a buy within a few calls; actual the IMD leg fails 438 consecutive calls (LegFailed / InsufficientOutput) and the first successful buy happens 263,400 s (3.05 days) later; retryCap sits at 1 gwei throughout. Organic variant: after step 1 do zeroForOne buys of 1 ETH until sqrtP <= spot/1.414 (IMD price doubles), then fund 1 ETH and process() every 600 s: first successful buy after 237,600 s (2.75 days).","severity":"medium","snippet":"            (l.checkpointSqrtPriceX96,,,) = poolManager.getSlot0(l.key.toId());\n            l.checkpointAt = uint64(block.timestamp);","title":"IMD buy leg stalls for days after any sqrt-price rise: checkpoint is refreshed from the same-block post-swap spot, so a sandwich around process() (or an organic 2x move) pins the min-out floor above s"},{"citation":"resolved","description":"stakeFor (src/ImdoStaking.sol:161-172) is restricted to the immutable claimContract and unconditionally resets the beneficiary's withdrawal lock, as the brief requires. In the script deployment the claim caller is ImdoClaim, which only ever stakes for msg.sender, so the reset is self-inflicted. In launch.json the ImdoStaking constructor receives \"$owner\" for both claim and regenSafe (contracts[0].constructorArgs[3] and [4]). That makes the policy owner an EOA/multisig that may call stakeFor(anyAccount, 1) at will: each call costs 1 wei IMDO and pushes unlockTime[anyAccount] 24 h into the future, so unstake() and exit() revert with StakeLocked for as long as the owner keeps calling. Boundary x invariant seam: the lock invariant 'a staker can withdraw 24 h after their own last stake' silently becomes 'a staker can withdraw 24 h after the owner's last action'. docs/REVIEW.md row b7ae43fd... records that the imported finding was exactly stranger-funded stakeFor lock grief and that it was closed by the immutable-claim check; the manifest substitution re-opens it for one privileged address. README (lines 41, 65) and launch.json notes describe stakeFor as a claim-contract power and do not disclose the freeze. Trust assumption rather than a permission bypass; report it so the author can either state it in README/notes or guard it while keeping the brief's rule that every stake resets the lock (e.g. have stakeFor extend the lock only when the resulting lock is not already later than block.timestamp + LOCK_DURATION of a non-dust stake, or require a minimum amount for third-party stakeFor).","line":168,"path":"src/ImdoStaking.sol","reproduction":"Deploy ImdoStaking(imdo, imd, manager, owner, owner) as launch.json does (claim = regenSafe = $owner). Alice stakes 1,000e18 IMDO at t0 (unlockTime = t0 + 24 h). Owner approves 100 wei IMDO and, at t0 + 23 h, t0 + 1 d 23 h, ... t0 + 29 d 23 h, calls staking.stakeFor(alice, 1). At t0 + 30 d Alice calls unstake(1): expected success (her own last stake was 30 days ago); actual revert StakeLocked(1802674800) with unlockTime = t0 + 30 d 23 h. Repeating the owner call daily keeps her 1,000e18 IMDO locked indefinitely at 1 wei/day.","severity":"low","snippet":"        unlockTime[beneficiary] = block.timestamp + LOCK_DURATION;","title":"Manifest wiring makes the policy owner the claim caller: stakeFor(victim, 1 wei) every <24 h resets the victim's lock indefinitely, so one address can freeze every staker's IMDO principal"},{"citation":"resolved","description":"The per-call intake cap at line 168 is derived so that the IMD share (40% of net) equals maxEthPerBuy: cap = 1e18 * 1e8 / (4000 * 9950) = 2.512562814070351758 ETH, net = 2.5 ETH, imd = 1.0 ETH exactly. The derivation ignores the REGEN overflow that line 187 also routes into the same leg. Once regenAccrued[epoch] reaches regenCap (0.5 ETH, reached on the first saturated call since regen per call is 0.625 ETH), every further call that epoch adds imd + overflow = 1.0 + 0.625 = 1.625 ETH to the leg while _executeImd buys at most 1 ETH. Boundary x invariant seam: the intended steady state 'pending is cleared by the buy in the same call' holds only while the REGEN cap is not binding. Not a loss: pending is bought down at 1 ETH per cooldown (144 ETH/day) once inflow drops, and the accounting invariant balance = owed + pending + unsplit holds throughout (checked). Reported so the author can size regenCap/maxEthPerBuy/cooldown knowingly (launch-week fee inflow at the 20% anti-snipe rate can be large) or raise the buy cap when overflow is present.","line":187,"path":"src/ImdoTreasury.sol","reproduction":"LocalV4 fixture, treasury with the manifest literals. Every 600 s send exactly cap = 2512562814070351758 wei to the treasury and call process(). Observed leg(0).pending after calls 0..5: 0.125, 0.750, 1.375, 2.000, 2.625, 3.250 ETH (+0.625 ETH per call); totalRegenNotified stays at 0.5 ETH after call 0; unsplitEth() stays 0. Expected from the cap derivation: pending returns to 0 each call.","severity":"info","snippet":"            _imdLeg.pending += imd + overflow;","title":"When the REGEN epoch cap is saturated the IMD leg takes in 1.625 ETH per process() but can buy only maxEthPerBuy (1 ETH), so _imdLeg.pending grows by 0.625 ETH per call under sustained inflow"}],"hash":"c28ace4594aa8009a1293f01f95412aeb73f36ca332b964e67600c63361214c3","nodeId":"e1d7f411-4634-4aed-949d-cd0c274537e9","outcome":"completed","summary":"The review is complete. Findings are in `.imd-findings.json` at the repository root, all three snippets verified against the tree, and the scratch probe has been removed so no project file changed.\n\n**What I found in the math area (Precision, Boundary, Numerical Gap)**\n\n- **Medium, `src/ImdoTreasury.sol:332`.** The IMD-buy checkpoint is refreshed from the same-block post-swap spot price with no bound on how far it may rise. Because the min-out floor then decays only as (7d/(7d+t))² in sqrt-price terms, a 2x rise in IMD-per-ETH pins the floor above spot for about three days. Reproduced two ways on the local fixture: an unprivileged sandwich around `process()` (sell IMD, call process, buy back) cost the attacker about 1.99 ETH and stalled the leg for 438 consecutive calls, 3.05 days. An organic IMD price doubling stalled it 2.75 days with no attacker. Funds are delayed, not lost. Suggested fix keeps the design: bound the per-refresh rise of the checkpoint or shorten the decay constant.\n- **Low, `src/ImdoStaking.sol:168`.** In the manifest wiring, claim and regenSafe both resolve to the policy owner. That address can call `stakeFor(victim, 1)` daily and reset any staker's 24-hour lock forever. Reproduced: thirty 1-wei calls kept Alice's 1,000 IMDO locked at day 30. This is the same lock-grief that the imported finding closed for strangers, reopened for one privileged address, and it is not disclosed in README or the manifest notes.\n- **Info, `src/ImdoTreasury.sol:187`.** The intake cap is derived so the IMD share equals one max buy, but REGEN overflow also flows into that leg. Once the epoch cap binds, each call adds 1.625 ETH while buying 1 ETH. Measured pending growth of 0.625 ETH per call under sustained inflow. No loss and the accounting invariant holds; reported for parameter sizing.\n\n**What held.** Split and bounty arithmetic conserves every wei, and the cap works out to exactly a 1 ETH IMD leg. The per-share accumulator cannot overflow for any plausible supply, the backlog stream never underflows across threshold crossings, and the lifetime REGEN credit is monotonic. Hook fee gross-up and partial-fill checks agree between beforeSwap and afterSwap. Claim tranche rounding sums to the exact allocation. The full suite of 98 tests passed before I started.\n\n**Coverage.** All 36 listed entry points have a row, plus four invariant rows. Three entry points point at findings; the rest hold. No critical or high findings, so no proof test files were attached.","treeHash":null,"usage":{"cachedInputTokens":2611569,"inputTokens":546,"model":"claude-fable-5-1","outputTokens":61513,"runtime":"claude","turns":36,"wallClockMs":1348288}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"c35be49d2f8f8def","findings":[{"citation":"resolved","description":"quoteMinOut() protects the treasury's IMD purchase with max(spot, decayed checkpoint) minus 300 bps. The checkpoint is the only defence against same-block manipulation (spot is read inside the swap transaction), and the README says it 'preserves a same-block reference' that only relaxes at CHECKPOINT_DECAY (7 days). But after every successful buy, _executeImd() overwrites the checkpoint with the post-swap spot (lines 332-333) with no lower bound relative to the previous checkpoint or its decayed floor. A sandwicher therefore does: (1) front-run process() by buying IMD with ETH until spot is just above checkpointFloor*0.985 (output still >= minOut); (2) the treasury buys at that price and records the manipulated post-swap spot as the new checkpoint; (3) back-run by selling the IMD back; an arbitrageur (or the attacker) restores the market price. Every 600 s cooldown the floor is now ~3% lower in price than the round before, independent of the decay schedule. After 20 rounds (3h20m) the checkpoint sqrt-price is 76% of the market sqrt-price and stakers receive 127 IMD per ETH where the market pays 219 (42% loss on each buy); the ratchet continues until InsufficientOutput would need minOut==0. Measured in the proof: round 1 212 IMD/ETH, round 5 187, round 10 160, round 15 142, round 20 127 against fair 219. The decayed-checkpoint variant is similar: after 7 days without a successful buy the floor is cp/2 in sqrt terms (1/4 in price), so one front-run can take 75% of a buy. Economics: the attacker's cost is the 1% IMD-pool fee on the push volume, roughly 0.02*D*(1/f - 1) ETH per round where D = L/sqrtP is the in-range virtual ETH depth and f the sqrt push factor; the treasury's loss per round is ethIn*(1 - 0.97^n) with ethIn <= 1 ETH. The attack is profitable while D < ~100 ETH; in the local 200 ETH pool the attacker nets -9.25 ETH over 20 rounds while stakers lose ~3.3 ETH of IMD value (pure griefing). Live mainnet pool (ETH/IMD 10000/200, read from PoolManager slot 0 and liquidity at block 26135979 via eth_call): sqrtPriceX96 0x10b245d1540410520f12587bb2 (~279 IMD/ETH), liquidity 0x26af0badb8e9676a8e4, D ~ 684 ETH, so today the sandwich is not profitable and is a griefing vector that defeats the stated guarantee; it becomes a direct extraction as soon as in-range IMD liquidity falls below roughly 100 ETH (or ~37 ETH for the 7-day-stale variant). Fix preserving the design: never let a successful buy lower the reference faster than the decay allows, e.g. newCheckpoint = max(postSwapSpot, checkpointFloor) where checkpointFloor is the value computed in quoteMinOut for this call (or keep the previous checkpoint when postSwapSpot < checkpointFloor). The checkpoint still rises freely on genuine price falls and still relaxes at the 7-day decay, so the stale-stall fix remains intact.","line":332,"path":"src/ImdoTreasury.sol","reproduction":"Local v4 PoolManager, ETH/IMD pool fee 10000 spacing 200 seeded with 200 ETH full-range at tick 54000, ImdoTreasury with the launch parameters (1 ETH buy cap, 300 bps, 600 s), one 1e18 IMDO staker so purchases distribute. Repeat 20 times: warp +600 s; send 1 ETH to the treasury; attacker buys IMD with the largest ETH amount P such that process() still buys (search downward from 150 ETH in 5% steps using snapshots); call process(); attacker sells the IMD back; restore market sqrt-price with a limit swap. Expected: leg(0).checkpointSqrtPriceX96 stays within the 7-day decay of the market (>= 99.8% after 3h20m) and every buy pays at least 0.97*decayedFloor ~ 212 IMD/ETH. Actual: checkpoint sqrt 897293422362808385229126947655 vs market 1178734673953441494749930375074 (76.1%), round-20 fill 127293044538942079239 IMD/ETH vs fair 219133182040037491989. Run: forge test --match-path test/scratch/CheckpointRatchet.t.sol -vv","severity":"medium","snippet":"            (l.checkpointSqrtPriceX96,,,) = poolManager.getSlot0(l.key.toId());\n            l.checkpointAt = uint64(block.timestamp);","title":"IMD-buy checkpoint re-anchors to the manipulated post-swap price, so sandwiched buys ratchet the min-out floor down geometrically"},{"citation":"resolved","description":"stakeFor(beneficiary, amount) resets the beneficiary's whole-position 24h lock (line 168), as the brief requires, and is callable by the immutable claimContract. In the manual script that address is ImdoClaim, which only ever passes msg.sender, so nobody can reset another holder's lock. In launch.json the ImdoStaking constructorArgs[3] (claim) is \"$owner\" (launch.json line 16), an externally controlled account. That account can call stakeFor(victim, 1) every day (1 wei of IMDO, it is excluded from staking itself so it must hold the token only to fund the wei) and the victim's unstake()/exit() revert with StakeLocked forever; a user who staked 1,000,000 IMDO cannot withdraw it while the owner keeps paying 1 wei per day. The notes say the owner 'can stakeFor' but not that this is a perpetual principal freeze over every staker, and the README's 'Who can call what' table lists stakeFor only as 'on behalf of beneficiaries'. This is a privileged-power risk introduced by the $owner substitution, not a bypass; it should be documented as a trust assumption in README and notes, or stakeFor should only extend the lock when the beneficiary's existing lock has expired (unlockTime[beneficiary] = max(existing, now+24h) does not help; a guard such as 'a stakeFor that adds less than X% of the existing position does not reset the lock' or 'stakeFor sets the lock only if the position was zero' would preserve the manual deployment's semantics). Note the brief's own tests only cover 'stakeFor lock' for the ImdoClaim caller.","line":168,"path":"src/ImdoStaking.sol","reproduction":"Deploy ImdoStaking(imdo, imd, manager, claim=OWNER, regenSafe=OWNER) as the manifest does; OWNER holds 1000 IMDO and approves staking. Alice stakes 1,000,000e18. Loop 30 times: warp +23h; OWNER calls stakeFor(alice, 1); warp +1h; alice.unstake(1,000,000e18) reverts StakeLocked(unlockTime) each time although 24h have passed since her own stake. Expected: alice can withdraw 24h after her last own stake. Actual: her principal is locked as long as OWNER spends 1 wei/day. Run: forge test --match-path test/scratch/OwnerLockGrief.t.sol -vv","severity":"low","snippet":"        unlockTime[beneficiary] = block.timestamp + LOCK_DURATION;","title":"Manifest deployment makes $owner the staking claim caller, which can freeze any staker's principal indefinitely for 1 wei a day"},{"citation":"resolved","description":"_distribute() routes rewards to the gated 7-day backlog only when totalStaked == 0 (line 226). With any non-zero stake, even 1 wei, the full amount is credited immediately and pro rata to the existing stakers, while the backlog that accumulated before them still waits for 10,000,000 IMDO (MIN_BACKLOG_STAKE). The inconsistency means the floor that protects the launch backlog from being captured by a trivial stake does not protect the live flow: at pool launch (the anti-snipe window charges 20% fees, the IMD leg buys up to 1 ETH of IMD per 600 s and REGEN credits up to 0.5 ETH per epoch) whoever stakes first with dust receives every IMD purchase and every REGEN credit until another holder stakes; claimants only become able to stake from `launch`, and seat/holder claims pay 10% on day one. Economics: cost is 1 wei of IMDO (buy from the pool) plus gas; gain is 100% of IMD bought and REGEN credited during the window where the sniper is alone (up to 1 ETH of IMD per process call and up to 0.5 ETH of REGEN credit per epoch), paid out of fees that the design directs to 'IMDO stakers'. Not a code-path bug but a gap between the gating rule the brief states for the backlog and the ungated direct distribution; the README documents the direct behaviour. If intended, say so explicitly; otherwise apply the same MIN_BACKLOG_STAKE floor to direct distribution (route to unallocated while totalStaked < MIN_BACKLOG_STAKE), which also removes the 1 wei capture.","line":226,"path":"src/ImdoStaking.sol","reproduction":"ImdoStaking fresh. notifyReward(imd, 100e18) with totalStaked == 0 -> unallocated[imd] == 100e18 (gated). Sniper stakes 1 wei of IMDO. notifyReward(imd, 1000e18) and notifyRegen{value: 10 ether}(). Expected under a consistent floor: both amounts back-logged until 10,000,000 IMDO is staked. Actual: earned(sniper, imd) == 1000e18 and regenCreditOf(sniper) == 10 ether, while the 100e18 backlog is still gated. Run: forge test --match-path test/scratch/TinyFirstStaker.t.sol -vv","severity":"low","snippet":"        if (totalStaked == 0) {\n            unallocated[token] += amount;\n            return 0;\n        }","title":"The 10,000,000 IMDO floor gates only the backlog; a 1 wei stake captures 100% of every reward and REGEN credit notified while it is the only stake"}],"hash":"d481b9e9f29b21742476853135af813633854436e0b401c00fc6ba7d1840aae4","nodeId":"9a8f638c-764a-45e8-8c49-f9cf7b4db2ff","outcome":"completed","summary":"Review complete. The findings file is at `.imd-findings.json` with 3 findings and coverage rows for all 36 entry points plus 6 invariants.\n\n**What I found**\n\n1. **Medium: the IMD-buy checkpoint re-anchors to the manipulated post-swap price** (`src/ImdoTreasury.sol:332`). The min-out guard is the only defence against same-block manipulation, but every successful buy overwrites the reference with the post-swap spot. A sandwicher who pushes spot to the floor each round lowers the next floor by ~3%, compounding every 600 s regardless of the 7-day decay. In the proof, after 20 rounds the checkpoint sits at 76% of the market sqrt-price and stakers receive 127 IMD per ETH against a fair 219. I read the live mainnet ETH/IMD pool: ~684 ETH of in-range depth, so today it is griefing (attacker pays ~9 ETH of pool fees per 20 rounds in a 200 ETH pool), and becomes profitable below roughly 100 ETH of depth. Proof test fails on current code and passes under a one-line fix that never lets a buy lower the reference below its decayed floor.\n\n2. **Low: manifest `$owner` as staking claim caller can freeze any staker's principal** (`src/ImdoStaking.sol:168`). stakeFor resets the whole-position lock, so the policy owner can call stakeFor(victim, 1 wei) daily and the victim can never unstake. Safe in the manual deployment where ImdoClaim only passes msg.sender; a trust-assumption risk introduced by the manifest substitution and not documented.\n\n3. **Low: the 10M IMDO floor gates only the backlog, not direct distribution** (`src/ImdoStaking.sol:226`). A 1 wei stake captures 100% of every IMD purchase and REGEN credit notified while it is the only stake, while the launch backlog remains gated behind it.\n\n**What held**: the exact split and bounty, REGEN epoch cap and overflow routing, failed-notify retry, ETH conservation in treasury and staking, lifetime REGEN credit monotonicity, withdrawRegen bound, claim vesting and entitlement following the NFT, holder proofs and global bound. I checked and rejected the keeper gas-starvation grief on the try/catch (the 63/64 rule makes it unreachable).\n\n**Not reached**: no fork run of the mainnet buy itself and no evaluation of the real seat NFT or holder list, which are off-chain trust inputs.","treeHash":null,"usage":{"cachedInputTokens":4092783,"inputTokens":740,"model":"claude-fable-5-1","outputTokens":76395,"runtime":"claude","turns":39,"wallClockMs":1610724}},{"artifacts":[],"attempt":2,"bundleHash":"82505aa2c5d34951b56ec62222d8238f5931c3633cb4f35f332321708a702b29","device":"5dc170d977094c92","findings":[],"hash":"f2e4c8ec27bda2cebc061bfdefe828b506da1e93546dd182e5171084fe1a55c4","nodeId":"26c20e22-a276-4f9a-b6be-453af6037c1e","outcome":"completed","summary":"Added 15 tests covering claim failures, treasury payment boundaries, and claim/hook stateful invariants. Only `test/**` changed.\n\nValidation passed:\n- `forge build`\n- All 98 offline tests\n- 55,296 invariant handler calls\n- Pinned mainnet-fork IMD buy test\n\nNo reproducible contract defects found.","treeHash":"846fd077dab87ad10303465038a6b703dcce810a","usage":{"cachedInputTokens":2810240,"inputTokens":121022,"model":"gpt-6-astra","outputTokens":20717,"runtime":"codex","turns":10,"wallClockMs":613896}}],"verification":[{"checks":[{"durationMs":222118,"exitCode":0,"name":"build","output":"Compiling 172 files with Solc 0.8.26\nSolc 0.8.26 finished in 221.57s\nCompiler run successful!\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/ImdoHook.sol:121:13\n    │\n121 │         if (elapsed >= DECAY_DURATION) return base;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[missing-events-arithmetic]: `regenCap` is changed without an event but is used in arithmetic\n    ╭▸ src/ImdoTreasury.sol:160:9\n    │\n160 │         regenCap = cap;\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:127:64\n    │\n127 │             maxEthPerBuy_ < MIN_ETH_PER_BUY || maxEthPerBuy_ > uint256(uint128(type(int128).max))\n    │                                                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoTreasury.sol:127:72\n    │\n127 │             maxEthPerBuy_ < MIN_ETH_PER_BUY || maxEthPerBuy_ > uint256(uint128(type(int128).max))\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/ImdoTreasury.sol:129:37\n    │\n129 │                 || imdTickSpacing > uint24(TickMath.MAX_TICK_SPACING) || imdFee > LPFeeLibrary.MAX_LP_FEE\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint24' 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/ImdoTreasury.sol:144:79\n    │\n144 │             PoolKey(CurrencyLibrary.ADDRESS_ZERO, Currency.wrap(imd), imdFee, int24(imdTickSpacing), IHooks(imdHooks));\n    │                                                                               ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' 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/ImdoHook.sol:175:31\n    │\n175 │             launchTimestamp = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoHook.sol:183:42\n    │\n183 │             params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:183:77\n    │\n183 │             params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:198:46\n    │\n198 │                 params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:198:81\n    │\n198 │                 params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:201:47\n    │\n201 │                 params.amountSpecified < 0 ? -int256(requested - prepaidFee) : int256(requested + prepaidFee);\n    │                                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/ImdoHook.sol:201:80\n    │\n201 │                 params.amountSpecified < 0 ? -int256(requested - prepaidFee) : int256(requested + prepaidFee);\n    │                                                                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/ImdoHook.sol:206:44\n    │\n206 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoTreasury.sol:149:77\n    │\n149 │             if (_imdLeg.checkpointSqrtPriceX96 != 0) _imdLeg.checkpointAt = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoHook.sol:206:52\n    │\n206 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\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/ImdoHook.sol:206:74\n    │\n206 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\n    │                                                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:206:82\n    │\n206 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\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-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/ImdoHook.sol:266:9\n    │\n266 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoHook.sol:268:9\n    │\n268 │         emit FeesRedeemed(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/ImdoHook.sol:266:9\n    │\n266 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoHook.sol:302:17\n    │\n302 │                 emit FeesDeferred(fee);\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/ImdoHook.sol:305:9\n    │\n305 │         emit FeeTaken(params.zeroForOne, ethAmount, fee, bps);\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/ImdoStaking.sol:116:9\n    │\n116 │         emit Staked(msg.sender, amount);\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/ImdoClaim.sol:56:51\n   │\n56 │                 || seatSize_ > SEAT_ALLOCATION || launch_ < block.timestamp\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/ImdoClaim.sol:76:13\n   │\n76 │         if (block.timestamp < launch || block.timestamp >= deadline) 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/ImdoClaim.sol:76:41\n   │\n76 │         if (block.timestamp < launch || block.timestamp >= deadline) 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/ImdoClaim.sol:78:13\n   │\n78 │         if (tranches > 10) tranches = 10;\n   │             ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/ImdoClaim.sol:128:13\n    │\n128 │             staking.stakeFor(msg.sender, amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoStaking.sol:257:13\n    │\n257 │             emit RewardClaimed(account, token, 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/ImdoStaking.sol:157:9\n    │\n157 │         emit RewardNotified(token, msg.sender, received, distributed);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_imdLeg` is updated\n    ╭▸ src/ImdoTreasury.sol:329:13\n    │\n329 │         try this.executeImd(ethIn) returns (uint256 out) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/ImdoClaim.sol:101:29\n    │\n101 │         if (amount == 0 && !changed) revert NothingUnlocked();\n    │                             ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/ImdoClaim.sol:98:13\n   │\n98 │             amount += due;\n   │             ━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/ImdoClaim.sol:83:13\n   │\n83 │         if (block.timestamp >= deadline) revert ClaimClosed();\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/ImdoClaim.sol:84:13\n   │\n84 │         if (block.timestamp < launch) revert NothingUnlocked();\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/ImdoClaim.sol:90:17\n   │\n90 │             if (seatNFT.ownerOf(id) != msg.sender) revert NotOwner();\n   │                 ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/ImdoClaim.sol:70:29\n   │\n70 │         if (id >= seatSize) revert InvalidToken();\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/ImdoClaim.sol:90:52\n   │\n90 │             if (seatNFT.ownerOf(id) != msg.sender) revert NotOwner();\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/ImdoClaim.sol:106:13\n    │\n106 │         if (block.timestamp >= deadline) revert ClaimClosed();\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/ImdoClaim.sol:121:9\n    │\n121 │         emit HolderClaimed(msg.sender, amount, stake);\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/ImdoClaim.sol:137:13\n    │\n137 │         if (block.timestamp < deadline) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/ImdoTreasury.sol:174:22\n    │\n174 │             bounty = amount * KEEPER_BPS / BPS;\n    │                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoStaking.sol:171:9\n    │\n171 │         emit Staked(beneficiary, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/ImdoTreasury.sol:208:13\n    │\n208 │         if (bounty != 0) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/ImdoTreasury.sol:165:13\n    │\n165 │         if (block.timestamp < availableAt) revert CooldownActive(availableAt);\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/ImdoTreasury.sol:201:17\n    │\n201 │                 emit RegenSent(regenPending);\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/ImdoTreasury.sol:204:17\n    │\n204 │                 emit RegenDeferred(regenPending, reason);\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/ImdoTreasury.sol:213:17\n    │\n213 │                 emit KeeperPaid(msg.sender, bounty);\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/ImdoTreasury.sol:217:17\n    │\n217 │                 emit KeeperPaymentDeferred(msg.sender, bounty);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:171:25\n    │\n171 │         lastProcessed = 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[return-bomb]: external call with a gas limit may copy unbounded return data\n    ╭▸ src/ImdoTreasury.sol:211:26\n    │\n211 │             (bool ok,) = msg.sender.call{value: bounty, gas: 100_000}(\"\");\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/return-bomb\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoStaking.sol:182:9\n    │\n182 │         emit RewardClaimed(msg.sender, token, 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/ImdoStaking.sol:190:9\n    │\n190 │         emit RegenNotified(msg.sender, msg.value, distributed);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `_status` is updated\n    ╭▸ src/ImdoTreasury.sol:251:22\n    │\n251 │         (bool ok,) = recipient.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `_status` is updated\n    ╭▸ src/ImdoStaking.sol:202:22\n    │\n202 │         (bool ok,) = regenSafe.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoTreasury.sol:228:9\n    │\n228 │         emit OpsPaid(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/ImdoStaking.sol:204:9\n    │\n204 │         emit RegenWithdrawn(amount);\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/ImdoStaking.sol:239:13\n    │\n239 │         if (block.timestamp < unlockTime[msg.sender]) revert StakeLocked(unlockTime[msg.sender]);\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/ImdoStaking.sol:244:9\n    │\n244 │         emit Unstaked(msg.sender, amount);\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/ImdoStaking.sol:286:13\n    │\n286 │         if (elapsed > BACKLOG_DURATION) elapsed = BACKLOG_DURATION;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoStaking.sol:308:77\n    │\n308 │                 backlogStream[token] = BacklogStream(unallocated[token], 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/ImdoTreasury.sol:237:9\n    │\n237 │         emit OffsetsPaid(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/ImdoTreasury.sol:247:9\n    │\n247 │         emit KeeperPaid(msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/ImdoTreasury.sol:251:22\n    │\n251 │         (bool ok,) = recipient.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/ImdoTreasury.sol:260:9\n    │\n260 │         staking.notifyReward(token, balance);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoTreasury.sol:262:9\n    │\n262 │         emit RewardsFlushed(token, balance);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:284:60\n    │\n284 │         if (sqrtPriceX96 < checkpointFloor) sqrtPriceX96 = uint160(checkpointFloor);\n    │                                                            ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' 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/ImdoTreasury.sol:299:61\n    │\n299 │             poolManager.swap(_imdLeg.key, SwapParams(true, -int256(ethIn), TickMath.MIN_SQRT_PRICE + 1), \"\");\n    │                                                             ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/ImdoTreasury.sol:300:37\n    │\n300 │         if (delta.amount0() >= 0 || uint256(-int256(delta.amount0())) != ethIn) revert PartialFill();\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoTreasury.sol:302:29\n    │\n302 │         uint256 amountOut = uint256(uint128(delta.amount1()));\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoTreasury.sol:302:37\n    │\n302 │         uint256 amountOut = uint256(uint128(delta.amount1()));\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[unused-return]: return value of an external call is not used\n    ╭▸ src/ImdoTreasury.sol:304:9\n    │\n304 │         poolManager.settle{value: ethIn}();\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/ImdoTreasury.sol:322:32\n    │\n322 │         if (l.failures != 0 && block.timestamp > uint256(l.lastFailure) + gap) _resetFailures();\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/ImdoTreasury.sol:334:13\n    │\n334 │             emit LegBought(LEG_IMD, Currency.unwrap(l.key.currency1), ethIn, out);\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/ImdoTreasury.sol:336:13\n    │\n336 │             emit LegFailed(LEG_IMD, ethIn, reason);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:333:30\n    │\n333 │             l.checkpointAt = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:337:51\n    │\n337 │             if (l.failures == 0) l.failingSince = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:338:29\n    │\n338 │             l.lastFailure = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:361:36\n    │\n361 │             _imdLeg.checkpointAt = 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\n","passed":true},{"durationMs":21163,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 11 tests for test/unit/DistributorAdversarial.t.sol:DistributorAdversarialTest\n[PASS] testFuzz_taxedRewardCreditsOnlyReceivedTokens(uint256,uint16) (runs: 1000, μ: 518826, ~: 522624)\n[PASS] testFuzz_twoEpochsFollowStakeOwnership(uint256,uint256,uint256,uint256) (runs: 1000, μ: 890530, ~: 898032)\n[PASS] test_allFixedExclusionsRejectStakeBeforeTokenInteraction() (gas: 418142)\n[PASS] test_claimCallbackCannotReenterAnyHolderOrRewardAction() (gas: 523169)\n[PASS] test_failedStakeRollsBackBacklogAndShares() (gas: 600805)\n[PASS] test_falseReturningIntakeCannotCreateCredit() (gas: 267523)\n[PASS] test_fullSupplyCanStakeAndExit() (gas: 601080)\n[PASS] test_noReturnTokenCanNotifyAndClaim() (gas: 478149)\n[PASS] test_notifyCallbackCannotReenterAnyHolderOrRewardAction() (gas: 370405)\n[PASS] test_oneWeiAndCompleteTransferTax() (gas: 543864)\n[PASS] test_pausedRewardCannotTrapUnstakedPrincipal() (gas: 633583)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 429.18ms (827.99ms CPU time)\n\nRan 6 tests for test/unit/LaunchToken.t.sol:IMDOTokenTest\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 80237, ~: 80289)\n[PASS] test_metadata() (gas: 24431)\n[PASS] test_mintsFixedSupplyToDeployer() (gas: 23278)\n[PASS] test_noMintOrAdminEntryPoints() (gas: 143123)\n[PASS] test_transferMovesExactAmount() (gas: 74094)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 34859)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 444.95ms (448.96ms CPU time)\n\nRan 9 tests for test/unit/ClaimAdversarial.t.sol:ClaimAdversarialTest\n[PASS] testFuzz_eachDailyTrancheIsPaidOnlyOnceAcrossWalletAndStake(uint16) (runs: 1000, μ: 3274760, ~: 3304165)\n[PASS] test_batchWithUnauthorizedLastSeatRollsBackEarlierEntitlement() (gas: 229421)\n[PASS] test_emptyBatchNeverChangesBalancesOrLocks() (gas: 56074)\n[PASS] test_excludedNftOwnerCanTakeWalletClaimAfterFailedStake() (gas: 352548)\n[PASS] test_holderRejectsSingleHashLeafAndReorderedProof() (gas: 86101)\n[PASS] test_operatorApprovalDoesNotGiveClaimRights() (gas: 101937)\n[PASS] test_overcommittedHolderTreeCannotSpendSeatReserve() (gas: 354193)\n[PASS] test_recordingNewOwnerWithoutNewTrancheDoesNotResetExistingStakeLock() (gas: 493375)\n[PASS] test_underfundedHolderPaymentRollsBackAndCanRetryAfterFunding() (gas: 281686)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 873.51ms (877.02ms CPU time)\n\nRan 7 tests for test/unit/ImdoStaking.t.sol:ImdoStakingTest\n[PASS] testFuzz_lifetimeCreditSurvivesClaimsAndExit(uint128,uint128,uint96) (runs: 256, μ: 742759, ~: 742692)\n[PASS] test_backlogThresholdStreamPauseAndNoPastCredit() (gas: 1742638)\n[PASS] test_failedOrReentrantWithdrawalRollsBack() (gas: 718463)\n[PASS] test_invalidIntakeAndClaims() (gas: 188271)\n[PASS] test_rewardsProRataAndEthIsNeverClaimed() (gas: 898336)\n[PASS] test_stakeForRestrictedAndEveryStakeResetsLock() (gas: 566570)\n[PASS] test_withdrawOnlySafeBoundedEvenWithForcedEthAndNoStake() (gas: 296995)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 874.31ms (435.15ms CPU time)\n\nRan 5 tests for test/unit/Revision.t.sol:RevisionTest\n[PASS] testFuzz_exactOutputFeesAreGrossRate(uint32,uint128) (runs: 256, μ: 339742, ~: 339848)\n[PASS] test_emptySellCannotChargeFeeOrStartClock() (gas: 372929)\n[PASS] test_exactOutputAndInputBuySameGrossFeeAtLaunch() (gas: 372223)\n[PASS] test_firstFilledSwapStartsFullDecayAfterDeploymentDelay() (gas: 227125)\n[PASS] test_partialInputBuyRevertsAndReturnsEntireFee() (gas: 458441)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 2.22s (200.17ms CPU time)\n\nRan 1 test for test/fuzz/Treasury.fuzz.t.sol:TreasuryFuzzTest\n[PASS] testFuzz_capAndConservation(uint96) (runs: 256, μ: 524667, ~: 524685)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.26s (124.77ms CPU time)\n\nRan 1 test for test/unit/Integration.t.sol:IntegrationTest\n[PASS] test_buyStakeProcessClaimAndWithdraw() (gas: 1555567)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.29s (1.12ms CPU time)\n\nRan 4 tests for test/unit/TreasuryBoundaries.t.sol:TreasuryBoundariesTest\n[PASS] test_failedFlushRetainsTokensAndClearsApprovalOnSuccessfulRetry() (gas: 680942)\n[PASS] test_keeperDebtsAreIsolatedAndFailedPullPreservesBoth() (gas: 1540690)\n[PASS] test_loweringCapBelowUsedCapacityCannotResplitPendingFunds() (gas: 1505127)\n[PASS] test_oldPendingRegenSurvivesEpochRollAndDoesNotConsumeNewCap() (gas: 1483236)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.32s (6.76ms CPU time)\n\nRan 4 tests for test/unit/HookAdversarial.t.sol:HookAdversarialTest\n[PASS] testFuzz_exactInputSellChargesGrossEthAtAnyDecayTime(uint256,uint32,uint16) (runs: 1000, μ: 386651, ~: 387162)\n[PASS] testFuzz_exactOutputSellDeliversRequestedNetAndGrossFee(uint256,uint32) (runs: 1000, μ: 352474, ~: 352672)\n[PASS] test_decayBoundariesAndNoFeeResurrection() (gas: 526585)\n[PASS] test_partialExactOutputSellRevertsFeeAndPoolMovement() (gas: 482068)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.43s (676.30ms CPU time)\n\nRan 3 tests for test/unit/ImdoDeploy.t.sol:ImdoDeployTest\n[PASS] test_manualSignerRunUsesArgumentsRegardlessOfCaller() (gas: 32656773)\n[PASS] test_scriptDeploysFundsSeedsBurnsLpAndStartsOwnershipTransfer() (gas: 32845340)\n[PASS] test_scriptRejectsMissingCodeWrongChainAndBadTickBeforeCreation() (gas: 19359033)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 2.43s (172.30ms CPU time)\n\nRan 12 tests for test/unit/ImdoTreasury.t.sol:ImdoTreasuryTest\n[PASS] testFuzz_splitConservesEveryWei(uint96) (runs: 256, μ: 809684, ~: 879977)\n[PASS] test_capOverflowAndEpochRoll() (gas: 1503255)\n[PASS] test_capSetterBoundsAuthAndNoRetroactiveResplit() (gas: 981747)\n[PASS] test_cooldownAndCallbackAuthorization() (gas: 897644)\n[PASS] test_dustAndFlush() (gas: 528446)\n[PASS] test_exactSplitBountyAndAuthorizedPulls() (gas: 671458)\n[PASS] test_failedRegenRetryDoesNotChargeAgainOrBlockBuy() (gas: 1012551)\n[PASS] test_failedRewardNotifyRollsBackBuyAndHalvesRetry() (gas: 1203028)\n[PASS] test_missingDependenciesDoNotSeedPermanentZeroCheckpoint() (gas: 6083833)\n[PASS] test_rejectedAndReentrantPullPaymentsPreserveOwed() (gas: 1149245)\n[PASS] test_rejectingKeeperCanPullToAlternateRecipient() (gas: 886966)\n[PASS] test_staleCheckpointRecoversWithoutRerouting() (gas: 4483731)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 2.61s (119.61ms CPU time)\n\nRan 4 tests for test/unit/HookDeferredFees.t.sol:HookDeferredFeesTest\n[PASS] test_failedRedemptionPreservesClaimAndCannotRedirect() (gas: 488920)\n[PASS] test_firstBuyOnTokenOnlyManagerThenPermissionlessRedemption() (gas: 354136)\n[PASS] test_freshManagerExactOutputBuyAlsoDefersFee() (gas: 278888)\n[PASS] test_fundedManagerUsesOriginalDirectPayment() (gas: 195363)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 2.92s (2.98ms CPU time)\n\nRan 1 test for test/invariant/Treasury.invariant.t.sol:TreasuryInvariantTest\n[PASS] invariant_ethEqualsOwedPlusPendingAndUnsplit() (runs: 128, calls: 6144, reverts: 0)\n\n╭-----------------+----------+-------+---------+----------╮\n| Contract        | Selector | Calls | Reverts | Discards |\n+=========================================================+\n| TreasuryHandler | cap      | 1021  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | fund     | 1070  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | keeper   | 1025  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | pay      | 1062  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | process  | 963   | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | withdraw | 1003  | 0       | 0        |\n╰-----------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.86s (1.11s CPU time)\n\nRan 1 test for test/invariant/Staking.invariant.t.sol:StakingInvariantTest\n[PASS] invariant_principalAndRewardConservation() (runs: 128, calls: 8192, reverts: 0)\n\n╭----------------+----------+-------+---------+----------╮\n| Contract       | Selector | Calls | Reverts | Discards |\n+========================================================+\n| StakingHandler | advance  | 1431  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | claim    | 1398  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | notify   | 1366  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | stake    | 1321  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | unstake  | 1334  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | withdraw | 1342  | 0       | 0        |\n╰----------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.61s (4.60s CPU time)\n\nRan 22 tests for test/unit/ImdoHook.t.sol:ImdoHookTest\n[PASS] testFuzz_buyFeeMatchesSchedule(uint256,uint32) (runs: 256, μ: 331731, ~: 331972)\n[PASS] testFuzz_decayIsMonotoneAndBounded(uint32,uint32) (runs: 256, μ: 196711, ~: 196697)\n[PASS] test_addressCarriesExactlyTheRequiredFlags() (gas: 6313)\n[PASS] test_antiSnipeFeeAtLaunchIs20Percent() (gas: 186101)\n[PASS] test_buyExactInput_takes1_5PercentOfEthIn() (gas: 345952)\n[PASS] test_buyExactOutput_feeIsAddedOnTopOfEthIn() (gas: 347053)\n[PASS] test_callbacksRejectNonPoolManager() (gas: 90785)\n[PASS] test_constructorRejectsUnflaggedAddress() (gas: 12526)\n[PASS] test_decaySchedule() (gas: 223606)\n[PASS] test_feeCannotBeRaisedOrKept() (gas: 122078)\n[PASS] test_feeEventsAreEmitted() (gas: 334937)\n[PASS] test_initialState() (gas: 43201)\n[PASS] test_initializeOnlyOnce() (gas: 46337)\n[PASS] test_initializeRejectsWrongPair() (gas: 159603622)\n[PASS] test_initializeRequiresOwner() (gas: 159715886)\n[PASS] test_lowerFee() (gas: 240864)\n[PASS] test_lowerFeeDuringDecayKeepsDecaying() (gas: 220297)\n[PASS] test_lowerFeeToZeroDisablesFee() (gas: 358443)\n[PASS] test_onlyOwnerLowersFee() (gas: 35224)\n[PASS] test_sellExactInput_takes1_5PercentOfEthOut() (gas: 465838)\n[PASS] test_sellExactOutput_feeIsAddedOnTopOfEthOut() (gas: 467897)\n[PASS] test_twoStepOwnershipTransfer() (gas: 157199)\nSuite result: ok. 22 passed; 0 failed; 0 skipped; finished in 4.91s (4.43s CPU time)\n\nRan 5 tests for test/unit/ImdoClaim.t.sol:ImdoClaimTest\n[PASS] test_duplicateIdsAndOwnerChecks() (gas: 250140)\n[PASS] test_holderProofPartialDoubleClaimAndStake() (gas: 529656)\n[PASS] test_launchDeadlineAndBurnBoundaries() (gas: 335174)\n[PASS] test_seatTransferMovesOnlyRemainingEntitlement() (gas: 522259)\n[PASS] test_zeroRootDisablesHolderClaims() (gas: 219423)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 4.91s (2.96ms CPU time)\n\nRan 1 test for test/invariant/Hook.invariant.t.sol:HookInvariantTest\n[PASS] invariant_allChargedFeesRemainRedeemableOnlyToTreasury() (runs: 256, calls: 16384, reverts: 0)\n\n╭-------------+----------+-------+---------+----------╮\n| Contract    | Selector | Calls | Reverts | Discards |\n+=====================================================+\n| HookHandler | advance  | 3269  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | buy      | 3339  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | lower    | 3259  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | redeem   | 3272  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | sell     | 3245  | 0       | 0        |\n╰-------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.40s (4.92s CPU time)\n\nRan 1 test for test/invariant/Claim.invariant.t.sol:ClaimInvariantTest\n[PASS] invariant_entitlementsAndCustodyMatchIndependentModel() (runs: 256, calls: 24576, reverts: 0)\n\n╭--------------+--------------+-------+---------+----------╮\n| Contract     | Selector     | Calls | Reverts | Discards |\n+==========================================================+\n| ClaimHandler | advance      | 3448  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| ClaimHandler | burn         | 3486  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| ClaimHandler | claimHolder  | 3574  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| ClaimHandler | claimSeat    | 3494  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| ClaimHandler | exit         | 3529  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| ClaimHandler | stakeAgain   | 3617  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| ClaimHandler | transferSeat | 3428  | 0       | 0        |\n╰--------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 20.58s (20.57s CPU time)\n\nRan 18 test suites in 20.58s (69.37s CPU time): 98 tests passed, 0 failed, 0 skipped (98 total tests)\n","passed":true},{"durationMs":211,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDOToken.approve(address,uint256)\",\"IMDOToken.transfer(address,uint256)\",\"IMDOToken.transferFrom(address,address,uint256)\",\"ImdoClaim.burnUnclaimed()\",\"ImdoClaim.claimHolder(uint256,bytes32[],bool)\",\"ImdoClaim.claimSeat(uint256[],bool)\",\"ImdoHook.acceptOwnership()\",\"ImdoHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"ImdoHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"ImdoHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"ImdoHook.lowerFee(uint16)\",\"ImdoHook.redeemFees()\",\"ImdoHook.renounceOwnership()\",\"ImdoHook.transferOwnership(address)\",\"ImdoHook.unlockCallback(bytes)\",\"ImdoStaking.claim()\",\"ImdoStaking.claimReward(address)\",\"ImdoStaking.exit()\",\"ImdoStaking.notifyRegen()\",\"ImdoStaking.notifyReward(address,uint256)\",\"ImdoStaking.stake(uint256)\",\"ImdoStaking.stakeFor(address,uint256)\",\"ImdoStaking.unstake(uint256)\",\"ImdoStaking.withdrawRegen(uint256)\",\"ImdoTreasury.claimKeeper(address)\",\"ImdoTreasury.executeImd(uint256)\",\"ImdoTreasury.flushRewards()\",\"ImdoTreasury.payOffsets()\",\"ImdoTreasury.payOps()\",\"ImdoTreasury.process()\",\"ImdoTreasury.receive()\",\"ImdoTreasury.setRegenCap(uint256)\",\"ImdoTreasury.unlockCallback(bytes)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"LICENSE\":21,\"README.md\":90,\"SELF_AUDIT.md\":7,\"docs/DEPLOYMENT.md\":42,\"docs/REVIEW.md\":49,\"foundry.toml\":32,\"launch.json\":48,\"remappings.txt\":9,\"script/DeployImdo.s.sol\":168,\"script/utils/HookMiner.sol\":60,\"src/IMDOToken.sol\":6,\"src/ImdoClaim.sol\":142,\"src/ImdoHook.sol\":307,\"src/ImdoStaking.sol\":312,\"src/ImdoTreasury.sol\":365,\"src/LaunchToken.sol\":17,\"src/interfaces/IImdoStaking.sol\":7,\"test/REVISION.md\":7,\"test/TESTING.md\":48,\"test/fork/MainnetFork.t.sol\":56,\"test/fuzz/Treasury.fuzz.t.sol\":28,\"test/invariant/Claim.invariant.t.sol\":229,\"test/invariant/Hook.invariant.t.sol\":175,\"test/invariant/Staking.invariant.t.sol\":145,\"test/invariant/Treasury.invariant.t.sol\":102,\"test/unit/ClaimAdversarial.t.sol\":175,\"test/unit/DistributorAdversarial.t.sol\":284,\"test/unit/HookAdversarial.t.sol\":101,\"test/unit/HookDeferredFees.t.sol\":125,\"test/unit/ImdoClaim.t.sol\":163,\"test/unit/ImdoDeploy.t.sol\":129,\"test/unit/ImdoHook.t.sol\":277,\"test/unit/ImdoStaking.t.sol\":214,\"test/unit/ImdoTreasury.t.sol\":338,\"test/unit/Integration.t.sol\":43,\"test/unit/LaunchToken.t.sol\":64,\"test/unit/Revision.t.sol\":78,\"test/unit/TreasuryBoundaries.t.sol\":115,\"test/utils/ClaimHarness.sol\":76,\"test/utils/LocalV4.sol\":159,\"test/utils/Permit2Mock.sol\":23},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"0e669920ae463b97995ee389d9cda417b24dda5a7643e3371db6dfc1526f6d3b","verifiedTreeHash":"2abb18b62515f87aa48e154c86805c4b5f9c38dd","verifierVersion":"0.1.0+94826a22"},{"checks":[{"durationMs":108469,"exitCode":0,"name":"build","output":"Compiling 168 files with Solc 0.8.26\nSolc 0.8.26 finished in 108.22s\nCompiler run successful!\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/ImdoClaim.sol:59:51\n   │\n59 │                 || seatSize_ > SEAT_ALLOCATION || launch_ < block.timestamp\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/ImdoClaim.sol:79:13\n   │\n79 │         if (block.timestamp < launch || block.timestamp >= deadline) 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/ImdoClaim.sol:79:41\n   │\n79 │         if (block.timestamp < launch || block.timestamp >= deadline) 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/ImdoClaim.sol:84:13\n   │\n84 │         if (block.timestamp < launch) 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/ImdoClaim.sol:86:13\n   │\n86 │         if (tranches > 10) tranches = 10;\n   │             ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[missing-events-arithmetic]: `regenCap` is changed without an event but is used in arithmetic\n    ╭▸ src/ImdoTreasury.sol:159:9\n    │\n159 │         regenCap = value;\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/ImdoClaim.sol:138:13\n    │\n138 │             staking.stakeFor(msg.sender, amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:127:36\n    │\n127 │                 || maxEthPerBuy_ > uint256(uint128(type(int128).max)) || slippageBps_ > MAX_SLIPPAGE_BPS\n    │                                    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoClaim.sol:110:29\n    │\n110 │         if (amount == 0 && !changed) revert NothingUnlocked();\n    │                             ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/ImdoClaim.sol:106:13\n    │\n106 │             amount += due;\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:127:44\n    │\n127 │                 || maxEthPerBuy_ > uint256(uint128(type(int128).max)) || slippageBps_ > MAX_SLIPPAGE_BPS\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/ImdoTreasury.sol:128:88\n    │\n128 │                 || cooldown_ > MAX_COOLDOWN || imdTickSpacing == 0 || imdTickSpacing > uint24(TickMath.MAX_TICK_SPACING)\n    │                                                                                        ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint24' 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/ImdoClaim.sol:91:13\n   │\n91 │         if (block.timestamp >= deadline) revert ClaimClosed();\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/ImdoClaim.sol:92:13\n   │\n92 │         if (block.timestamp < launch) revert NothingUnlocked();\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/ImdoHook.sol:116:13\n    │\n116 │         if (elapsed >= DECAY_DURATION) return base;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/ImdoClaim.sol:98:17\n   │\n98 │             if (seatNFT.ownerOf(id) != msg.sender) revert NotOwner();\n   │                 ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:145:80\n    │\n145 │             PoolKey(CurrencyLibrary.ADDRESS_ZERO, Currency.wrap(imd_), imdFee, int24(imdTickSpacing), IHooks(imdHooks));\n    │                                                                                ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' 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[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/ImdoClaim.sol:73:29\n   │\n73 │         if (id >= seatSize) revert InvalidToken();\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/ImdoClaim.sol:98:52\n   │\n98 │             if (seatNFT.ownerOf(id) != msg.sender) revert NotOwner();\n   │                                                    ━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:149:36\n    │\n149 │             _imdLeg.checkpointAt = 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/ImdoClaim.sol:115:13\n    │\n115 │         if (block.timestamp >= deadline) revert ClaimClosed();\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/ImdoClaim.sol:131:9\n    │\n131 │         emit HolderClaimed(msg.sender, amount, stake);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoHook.sol:170:31\n    │\n170 │             launchTimestamp = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoHook.sol:178:42\n    │\n178 │             params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:178:77\n    │\n178 │             params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:193:46\n    │\n193 │                 params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:193:81\n    │\n193 │                 params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:196:47\n    │\n196 │                 params.amountSpecified < 0 ? -int256(requested - prepaidFee) : int256(requested + prepaidFee);\n    │                                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/ImdoHook.sol:196:80\n    │\n196 │                 params.amountSpecified < 0 ? -int256(requested - prepaidFee) : int256(requested + prepaidFee);\n    │                                                                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/ImdoHook.sol:201:44\n    │\n201 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:201:52\n    │\n201 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\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/ImdoHook.sol:201:74\n    │\n201 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\n    │                                                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:201:82\n    │\n201 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\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/ImdoClaim.sol:147:13\n    │\n147 │         if (block.timestamp < deadline) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/ImdoHook.sol:281:9\n    │\n281 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoHook.sol:283:9\n    │\n283 │         emit FeesRedeemed(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/ImdoHook.sol:281:9\n    │\n281 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_imdLeg` is updated\n    ╭▸ src/ImdoTreasury.sol:346:13\n    │\n346 │         try this.executeBuy(ethIn) returns (uint256 out) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/ImdoTreasury.sol:213:13\n    │\n213 │         if (bounty != 0) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/ImdoTreasury.sol:165:13\n    │\n165 │         if (block.timestamp < availableAt) revert CooldownActive(availableAt);\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/ImdoTreasury.sol:198:13\n    │\n198 │             emit Split(amount, ops, offsets, regen, buy);\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/ImdoTreasury.sol:351:13\n    │\n351 │             emit LegBought(0, imd, ethIn, out);\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/ImdoTreasury.sol:353:13\n    │\n353 │             emit LegFailed(0, ethIn, reason);\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/ImdoTreasury.sol:206:17\n    │\n206 │                 emit RegenSent(toSend);\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/ImdoTreasury.sol:209:17\n    │\n209 │                 emit RegenDeferred(toSend, reason);\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/ImdoTreasury.sol:218:17\n    │\n218 │                 emit KeeperPaid(msg.sender, bounty);\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/ImdoTreasury.sol:222:17\n    │\n222 │                 emit KeeperPaymentDeferred(msg.sender, bounty);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:175:36\n    │\n175 │             _imdLeg.checkpointAt = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:178:25\n    │\n178 │         lastProcessed = 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/ImdoHook.sol:299:9\n    │\n299 │         emit FeeTaken(params.zeroForOne, ethAmount, fee, bps);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[return-bomb]: external call with a gas limit may copy unbounded return data\n    ╭▸ src/ImdoTreasury.sol:216:26\n    │\n216 │             (bool ok,) = msg.sender.call{value: bounty, gas: 100_000}(\"\");\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/return-bomb\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoStaking.sol:115:9\n    │\n115 │         emit Staked(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/ImdoStaking.sol:127:9\n    │\n127 │         emit RewardClaimed(msg.sender, token, 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/ImdoStaking.sol:134:9\n    │\n134 │         emit RegenNotified(msg.sender, msg.value, distributed);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `_status` is updated\n    ╭▸ src/ImdoTreasury.sol:256:22\n    │\n256 │         (bool ok,) = recipient.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoTreasury.sol:233:9\n    │\n233 │         emit OpsPaid(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/ImdoTreasury.sol:242:9\n    │\n242 │         emit OffsetsPaid(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/ImdoTreasury.sol:252:9\n    │\n252 │         emit KeeperPaid(msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `_status` is updated\n    ╭▸ src/ImdoStaking.sol:146:22\n    │\n146 │         (bool ok,) = regenSafe.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoStaking.sol:148:9\n    │\n148 │         emit RegenWithdrawn(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/ImdoStaking.sol:245:13\n    │\n245 │             emit RewardClaimed(account, token, 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/ImdoStaking.sol:192:9\n    │\n192 │         emit RewardNotified(token, msg.sender, received, distributed);\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/ImdoStaking.sol:227:13\n    │\n227 │         if (block.timestamp < unlockTime[msg.sender]) revert StakeLocked(unlockTime[msg.sender]);\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/ImdoStaking.sol:232:9\n    │\n232 │         emit Unstaked(msg.sender, amount);\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/ImdoStaking.sol:278:13\n    │\n278 │         if (elapsed > BACKLOG_DURATION) elapsed = BACKLOG_DURATION;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/ImdoTreasury.sol:256:22\n    │\n256 │         (bool ok,) = recipient.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoStaking.sol:300:77\n    │\n300 │                 backlogStream[token] = BacklogStream(unallocated[token], 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-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/ImdoTreasury.sol:270:9\n    │\n270 │         staking.notifyReward(imd, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoTreasury.sol:264:13\n    │\n264 │             emit RewardsFlushed(imd, amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:291:61\n    │\n291 │             poolManager.swap(_imdLeg.key, SwapParams(true, -int256(ethIn), TickMath.MIN_SQRT_PRICE + 1), \"\");\n    │                                                             ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/ImdoTreasury.sol:292:37\n    │\n292 │         if (delta.amount0() >= 0 || uint256(-int256(delta.amount0())) != ethIn) revert PartialFill();\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoTreasury.sol:294:23\n    │\n294 │         uint256 out = uint256(uint128(delta.amount1()));\n    │                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoTreasury.sol:294:31\n    │\n294 │         uint256 out = uint256(uint128(delta.amount1()));\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[unused-return]: return value of an external call is not used\n    ╭▸ src/ImdoTreasury.sol:295:9\n    │\n295 │         poolManager.settle{value: ethIn}();\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/ImdoTreasury.sol:317:13\n    │\n317 │         if (elapsed <= CHECKPOINT_HOLD) return uint160(anchor);\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/ImdoTreasury.sol:319:13\n    │\n319 │         if (elapsed >= CHECKPOINT_DECAY) return 0;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:317:48\n    │\n317 │         if (elapsed <= CHECKPOINT_HOLD) return uint160(anchor);\n    │                                                ━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' 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/ImdoTreasury.sol:320:16\n    │\n320 │         return uint160(FullMath.mulDiv(anchor, CHECKPOINT_DECAY - elapsed, CHECKPOINT_DECAY));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' 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/ImdoTreasury.sol:339:32\n    │\n339 │         if (l.failures != 0 && block.timestamp > uint256(l.lastFailure) + gap) _resetFailures();\n    │                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:350:30\n    │\n350 │             l.checkpointAt = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:354:51\n    │\n354 │             if (l.failures == 0) l.failingSince = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:355:29\n    │\n355 │             l.lastFailure = 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[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/unit/ImdoStaking.t.sol:356:25\n    │\n356 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n357 │         assertEq(dist.earned(alice, address(imd)), 0, \"no instant jackpot at threshold\");\n358 │         vm.warp(start + 1 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/unit/ImdoStaking.t.sol:378:25\n    │\n378 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n379 │         vm.warp(start + 1 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\n","passed":true},{"durationMs":1352,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 6 tests for test/unit/LaunchToken.t.sol:LaunchTokenTest\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 79966, ~: 80289)\n[PASS] test_metadata() (gas: 24431)\n[PASS] test_mintsFixedSupplyToDeployer() (gas: 23278)\n[PASS] test_noMintOrAdminEntryPoints() (gas: 143123)\n[PASS] test_transferMovesExactAmount() (gas: 74094)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 34859)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 7.20ms (7.81ms CPU time)\n\nRan 1 test for test/unit/ProjectShape.t.sol:ProjectShapeTest\n[PASS] test_offlineManifestConstructionPreservesSupplyAndHasNoEscapeOpcodes() (gas: 3824968)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 14.86ms (14.69ms CPU time)\n\nRan 7 tests for test/unit/ImdoClaim.t.sol:ImdoClaimTest\n[PASS] test_claimAndStakeResetsLockAndRevokesApproval() (gas: 616947)\n[PASS] test_constructorMustMatchPredictedClaim() (gas: 14781)\n[PASS] test_holderProofDoubleClaimZeroRootAndCap() (gas: 531626)\n[PASS] test_seatBoundaryDuplicateAndTiming() (gas: 261150)\n[PASS] test_seatOwnerTransferAndTenTranches() (gas: 559786)\n[PASS] test_unclaimedBurnAtDeadlineAndNoClaimAfter() (gas: 284337)\n[PASS] test_unfundedClaimRollsBackAccounting() (gas: 294368)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 25.70ms (1.54ms CPU time)\n\nRan 2 tests for test/unit/DeployImdo.t.sol:DeployImdoTest\n[PASS] test_manualDeploymentAndBurnedSinglePosition() (gas: 30224949)\n[PASS] test_wrongChainNoCodePoolAndTickRejectedBeforeMint() (gas: 19336327)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 31.85ms (15.44ms CPU time)\n\nRan 11 tests for test/unit/ImdoTreasury.t.sol:ImdoTreasuryTest\n[PASS] testFuzz_splitConservesEveryWei(uint96) (runs: 256, μ: 412594, ~: 355945)\n[PASS] test_authCooldownAndCallbacks() (gas: 964176)\n[PASS] test_capBoundsAuthorizationAndReductionDoesNotUndoAccrual() (gas: 984839)\n[PASS] test_capOverflowEpochAndPendingReservation() (gas: 1157570)\n[PASS] test_checkpointProtectsImmediateManipulationAndEventuallyRecovers() (gas: 835911)\n[PASS] test_exactSplitBountyAndPulls() (gas: 814159)\n[PASS] test_failedIMDNotificationRollsBackBuyAndHalvesRetry() (gas: 860875)\n[PASS] test_failedKeeperPaymentCanBePulledToAnotherRecipient() (gas: 848833)\n[PASS] test_failedRegenRetriedWithoutDoubleBountyOrDoubleCap() (gas: 652885)\n[PASS] test_missingPoolCanBeInitializedLaterWithoutPermanentZeroCheckpoint() (gas: 5875603)\n[PASS] test_noCodeDependencyCannotProcessOrPayBounty() (gas: 73254)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 44.50ms (42.51ms CPU time)\n\nRan 3 tests for test/unit/FreshManager.t.sol:HookFeeTakeFreshManagerTest\n[PASS] test_callbackRequiresManagerAndActiveRedemption() (gas: 63355)\n[PASS] test_firstBuyOnTokenOnlyManagerMustSucceed() (gas: 375947)\n[PASS] test_rejectingTreasuryCannotBlockTradingAndKeepsClaim() (gas: 527123)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 55.96ms (1.04ms CPU time)\n\nRan 8 tests for test/unit/Revision.t.sol:RevisionTest\n[PASS] testFuzz_exactOutputFeesAreGrossRate(uint32,uint128) (runs: 256, μ: 444457, ~: 444625)\n[PASS] test_constructorCheckpointAlsoProtectsFirstProcess() (gas: 607718)\n[PASS] test_emptySellCannotChargeFeeOrStartClock() (gas: 402186)\n[PASS] test_exactOutputAndInputBuySameGrossFeeAtLaunch() (gas: 550885)\n[PASS] test_firstFilledSwapStartsFullDecayAfterDeploymentDelay() (gas: 312697)\n[PASS] test_hookAndTreasuryHaveNoStakingEntryPoint() (gas: 70536)\n[PASS] test_partialInputBuyRevertsAndReturnsEntireFee() (gas: 577254)\n[PASS] test_sandwichCannotLowerCheckpointFloor() (gas: 1388459)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 84.71ms (78.41ms CPU time)\n\nRan 22 tests for test/unit/ImdoHook.t.sol:ImdoHookTest\n[PASS] testFuzz_buyFeeMatchesSchedule(uint256,uint32) (runs: 256, μ: 436349, ~: 436568)\n[PASS] testFuzz_decayIsMonotoneAndBounded(uint32,uint32) (runs: 256, μ: 206286, ~: 206279)\n[PASS] test_addressCarriesExactlyTheRequiredFlags() (gas: 6313)\n[PASS] test_antiSnipeFeeAtLaunchIs20Percent() (gas: 278667)\n[PASS] test_buyExactInput_takes1_5PercentOfEthIn() (gas: 445603)\n[PASS] test_buyExactOutput_feeIsAddedOnTopOfEthIn() (gas: 449204)\n[PASS] test_callbacksRejectNonPoolManager() (gas: 90785)\n[PASS] test_constructorRejectsUnflaggedAddress() (gas: 12526)\n[PASS] test_decaySchedule() (gas: 233188)\n[PASS] test_feeCannotBeRaisedOrKept() (gas: 122078)\n[PASS] test_feeEventsAreEmitted() (gas: 430588)\n[PASS] test_initialState() (gas: 43201)\n[PASS] test_initializeOnlyOnce() (gas: 46337)\n[PASS] test_initializeRejectsWrongPair() (gas: 3257495)\n[PASS] test_initializeRequiresOwner() (gas: 3368374)\n[PASS] test_lowerFee() (gas: 247946)\n[PASS] test_lowerFeeDuringDecayKeepsDecaying() (gas: 229879)\n[PASS] test_lowerFeeToZeroDisablesFee() (gas: 379610)\n[PASS] test_onlyOwnerLowersFee() (gas: 35224)\n[PASS] test_sellExactInput_takes1_5PercentOfEthOut() (gas: 679971)\n[PASS] test_sellExactOutput_feeIsAddedOnTopOfEthOut() (gas: 671117)\n[PASS] test_twoStepOwnershipTransfer() (gas: 157199)\nSuite result: ok. 22 passed; 0 failed; 0 skipped; finished in 172.80ms (269.08ms CPU time)\n\nRan 1 test for test/fuzz/Treasury.fuzz.t.sol:TreasuryFuzzTest\n[PASS] testFuzz_retriesDoNotDuplicateAnyAllocation(uint96,uint8) (runs: 256, μ: 1478700, ~: 1280438)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 175.28ms (165.31ms CPU time)\n\nRan 4 tests for test/unit/TreasuryBoundaries.t.sol:TreasuryBoundariesTest\n[PASS] testFuzz_directionalProtocolFeesPreserveExecutableOutputFloors(uint16,uint16) (runs: 1000, μ: 685050, ~: 685067)\n[PASS] test_failureGapAndDustAreNotRerouting() (gas: 1477026)\n[PASS] test_pullPaymentRollbackAndReentrancy() (gas: 1347332)\n[PASS] test_regenWithdrawalReentrancy() (gas: 855039)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 180.85ms (159.08ms CPU time)\n\nRan 11 tests for test/unit/StakingAdversarial.t.sol:StakingAdversarialTest\n[PASS] testFuzz_taxedRewardCreditsOnlyReceivedTokens(uint256,uint16) (runs: 1000, μ: 547032, ~: 551040)\n[PASS] testFuzz_twoEpochsFollowStakeOwnership(uint256,uint256,uint256,uint256) (runs: 1000, μ: 959741, ~: 967369)\n[PASS] test_allFixedExclusionsRejectStakeBeforeTokenInteraction() (gas: 367373)\n[PASS] test_claimCallbackCannotReenterAnyHolderOrRewardAction() (gas: 554050)\n[PASS] test_failedStakeRollsBackBacklogAndShares() (gas: 613400)\n[PASS] test_falseReturningIntakeCannotCreateCredit() (gas: 276382)\n[PASS] test_fullSupplyCanStakeAndExit() (gas: 634250)\n[PASS] test_noReturnTokenCanNotifyAndClaim() (gas: 506679)\n[PASS] test_notifyCallbackCannotReenterAnyHolderOrRewardAction() (gas: 376857)\n[PASS] test_oneWeiAndCompleteTransferTax() (gas: 552516)\n[PASS] test_pausedRewardCannotTrapUnstakedPrincipal() (gas: 670575)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 184.50ms (359.12ms CPU time)\n\nRan 4 tests for test/unit/HookAdversarial.t.sol:HookAdversarialTest\n[PASS] testFuzz_exactInputSellChargesGrossEthAtAnyDecayTime(uint256,uint32,uint16) (runs: 1000, μ: 564246, ~: 567798)\n[PASS] testFuzz_exactOutputSellDeliversRequestedNetAndGrossFee(uint256,uint32) (runs: 1000, μ: 533084, ~: 533247)\n[PASS] test_decayBoundariesAndNoFeeResurrection() (gas: 655821)\n[PASS] test_partialExactOutputSellRevertsFeeAndPoolMovement() (gas: 699873)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 194.53ms (315.84ms CPU time)\n\nRan 29 tests for test/unit/Regen.t.sol:RegenTest\n[PASS] testFuzz_backlogConservation(uint128,uint32) (runs: 256, μ: 654210, ~: 658065)\n[PASS] testFuzz_claimsNeverExceedDistributed(uint256,uint256,uint256,uint256,uint256) (runs: 256, μ: 1156226, ~: 1160544)\n[PASS] testFuzz_creditNeverDecreasesOnSettlement(uint96,uint96,uint96) (runs: 256, μ: 634776, ~: 636197)\n[PASS] testFuzz_stakeUnstakeRoundTrip(uint256,uint256) (runs: 256, μ: 287241, ~: 287631)\n[PASS] test_backlogPausesBelowThresholdWithoutRetroactiveVesting() (gas: 985971)\n[PASS] test_backlogStreamsAndNewcomerCannotClaimPastTime() (gas: 1216584)\n[PASS] test_claimIsReentrancySafe() (gas: 1443096)\n[PASS] test_claimWithNothingIsANoop() (gas: 76696)\n[PASS] test_constructorState() (gas: 72226)\n[PASS] test_constructorValidation() (gas: 8202)\n[PASS] test_dustCannotCaptureBacklogEvenAfterWaiting() (gas: 548667)\n[PASS] test_emptyStakeCreditsStreamAndWithdrawDoesNotChangeBacklog() (gas: 1008217)\n[PASS] test_exitAfterUnstakeStillClaims() (gas: 626909)\n[PASS] test_exitWithdrawsAndClaims() (gas: 511901)\n[PASS] test_extraExclusionIsApplied() (gas: 16000)\n[PASS] test_noOwnerOrAdminEntryPoints() (gas: 119818)\n[PASS] test_notificationAndLifetimeCreditNeverPaidAsEth() (gas: 979275)\n[PASS] test_notifyRejectsUnknownTokenAndZero() (gas: 1127840)\n[PASS] test_notifyRequiresAllowance() (gas: 51065)\n[PASS] test_rewardsBeforeAnyStakeWaitForMeaningfulStake() (gas: 434788)\n[PASS] test_rewardsOnlyAccrueWhileStaked() (gas: 737392)\n[PASS] test_rewardsProRata() (gas: 795700)\n[PASS] test_stakeAndUnstake() (gas: 311954)\n[PASS] test_stakeForAuthAndBothStakePathsResetLock() (gas: 716192)\n[PASS] test_stakeRejectsZeroAndExcluded() (gas: 205144)\n[PASS] test_tokensSentDirectlyAreNotStakedButRewardsFromBalanceDiff() (gas: 251740)\n[PASS] test_unstakeKeepsAccruedRewardsClaimable() (gas: 589829)\n[PASS] test_unstakeMoreThanStakedReverts() (gas: 236466)\n[PASS] test_withdrawAuthorizationBoundAndFailureRollback() (gas: 385394)\nSuite result: ok. 29 passed; 0 failed; 0 skipped; finished in 194.59ms (352.64ms CPU time)\n\nRan 24 tests for test/unit/ImdoStaking.t.sol:ImdoStakingTest\n[PASS] testFuzz_backlogConservation(uint128,uint32) (runs: 256, μ: 654209, ~: 658058)\n[PASS] testFuzz_claimsNeverExceedDistributed(uint256,uint256,uint256,uint256,uint256) (runs: 256, μ: 1155336, ~: 1160426)\n[PASS] testFuzz_stakeUnstakeRoundTrip(uint256,uint256) (runs: 256, μ: 287310, ~: 287587)\n[PASS] test_backlogPausesBelowThresholdWithoutRetroactiveVesting() (gas: 985883)\n[PASS] test_backlogStreamsAndNewcomerCannotClaimPastTime() (gas: 1216496)\n[PASS] test_claimIsReentrancySafe() (gas: 1442986)\n[PASS] test_claimWithNothingIsANoop() (gas: 76674)\n[PASS] test_constructorState() (gas: 72182)\n[PASS] test_constructorValidation() (gas: 8202)\n[PASS] test_dustCannotCaptureBacklogEvenAfterWaiting() (gas: 548610)\n[PASS] test_exitAfterUnstakeStillClaims() (gas: 626887)\n[PASS] test_exitWithdrawsAndClaims() (gas: 511791)\n[PASS] test_extraExclusionIsApplied() (gas: 15978)\n[PASS] test_noOwnerOrAdminEntryPoints() (gas: 119774)\n[PASS] test_notifyRejectsUnknownTokenAndZero() (gas: 1127818)\n[PASS] test_notifyRequiresAllowance() (gas: 51065)\n[PASS] test_rewardsBeforeAnyStakeWaitForMeaningfulStake() (gas: 434744)\n[PASS] test_rewardsOnlyAccrueWhileStaked() (gas: 737348)\n[PASS] test_rewardsProRata() (gas: 795653)\n[PASS] test_stakeAndUnstake() (gas: 311932)\n[PASS] test_stakeRejectsZeroAndExcluded() (gas: 205056)\n[PASS] test_tokensSentDirectlyAreNotStakedButRewardsFromBalanceDiff() (gas: 251630)\n[PASS] test_unstakeKeepsAccruedRewardsClaimable() (gas: 589741)\n[PASS] test_unstakeMoreThanStakedReverts() (gas: 236356)\nSuite result: ok. 24 passed; 0 failed; 0 skipped; finished in 194.61ms (253.55ms CPU time)\n\nRan 1 test for test/invariant/Accounting.invariant.t.sol:AccountingInvariantTest\n[PASS] invariant_balancesAndLifetimeCredits() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------------+----------+-------+---------+----------╮\n| Contract          | Selector | Calls | Reverts | Discards |\n+===========================================================+\n| AccountingHandler | claim    | 1006  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| AccountingHandler | exit     | 1002  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| AccountingHandler | notify   | 1025  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| AccountingHandler | process  | 1055  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| AccountingHandler | pull     | 1033  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| AccountingHandler | stake    | 1100  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| AccountingHandler | warp     | 989   | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| AccountingHandler | withdraw | 982   | 0       | 0        |\n╰-------------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.14s (1.13s CPU time)\n\nRan 15 test suites in 1.14s (2.70s CPU time): 134 tests passed, 0 failed, 0 skipped (134 total tests)\n","passed":true},{"durationMs":101,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDOToken.approve(address,uint256)\",\"IMDOToken.transfer(address,uint256)\",\"IMDOToken.transferFrom(address,address,uint256)\",\"ImdoClaim.burnUnclaimed()\",\"ImdoClaim.claimHolder(uint256,bytes32[],bool)\",\"ImdoClaim.claimSeat(uint256[],bool)\",\"ImdoHook.acceptOwnership()\",\"ImdoHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"ImdoHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"ImdoHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"ImdoHook.lowerFee(uint16)\",\"ImdoHook.redeemFees()\",\"ImdoHook.renounceOwnership()\",\"ImdoHook.transferOwnership(address)\",\"ImdoHook.unlockCallback(bytes)\",\"ImdoStaking.claim()\",\"ImdoStaking.claimReward(address)\",\"ImdoStaking.exit()\",\"ImdoStaking.notifyRegen()\",\"ImdoStaking.notifyReward(address,uint256)\",\"ImdoStaking.stake(uint256)\",\"ImdoStaking.stakeFor(address,uint256)\",\"ImdoStaking.unstake(uint256)\",\"ImdoStaking.withdrawRegen(uint256)\",\"ImdoTreasury.claimKeeper(address)\",\"ImdoTreasury.executeBuy(uint256)\",\"ImdoTreasury.flushRewards()\",\"ImdoTreasury.payOffsets()\",\"ImdoTreasury.payOps()\",\"ImdoTreasury.process()\",\"ImdoTreasury.receive()\",\"ImdoTreasury.setRegenCap(uint256)\",\"ImdoTreasury.unlockCallback(bytes)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"README.md\":72,\"docs/DEPLOYMENT.md\":46,\"docs/REVIEW_NOTES.md\":26,\"foundry.toml\":32,\"launch.json\":49,\"remappings.txt\":9,\"script/DeployImdo.s.sol\":156,\"script/utils/HookMiner.sol\":66,\"src/IMDOToken.sol\":6,\"src/ImdoClaim.sol\":152,\"src/ImdoHook.sol\":301,\"src/ImdoStaking.sol\":304,\"src/ImdoTreasury.sol\":368,\"src/LaunchToken.sol\":17,\"src/interfaces/IImdoStaking.sol\":7,\"test/REVISION.md\":7,\"test/TESTING.md\":48,\"test/fork/MainnetFork.t.sol\":118,\"test/fuzz/Treasury.fuzz.t.sol\":42,\"test/invariant/Accounting.invariant.t.sol\":137,\"test/unit/DeployImdo.t.sol\":122,\"test/unit/FreshManager.t.sol\":142,\"test/unit/HookAdversarial.t.sol\":101,\"test/unit/ImdoClaim.t.sol\":154,\"test/unit/ImdoHook.t.sol\":278,\"test/unit/ImdoStaking.t.sol\":408,\"test/unit/ImdoTreasury.t.sol\":261,\"test/unit/LaunchToken.t.sol\":64,\"test/unit/ProjectShape.t.sol\":53,\"test/unit/Regen.t.sol\":146,\"test/unit/Revision.t.sol\":126,\"test/unit/StakingAdversarial.t.sol\":278,\"test/unit/TreasuryBoundaries.t.sol\":145,\"test/utils/ClaimFixture.sol\":67,\"test/utils/LocalV4.sol\":173},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":5571,"exitCode":0,"name":"slither","output":"[high/medium] arbitrary-send-eth at src/ImdoTreasury.sol:255: ImdoTreasury._pay(address,uint256) (src/ImdoTreasury.sol#255-258) sends eth to arbitrary user\n[high/medium] weak-prng at src/ImdoStaking.sol:251: ImdoStaking._settle(address) (src/ImdoStaking.sol#251-263) uses a weak PRNG: \"_remainder[token][account] = fractional % MAGNITUDE (src/ImdoStaking.sol#259)\"\n[high/medium] reentrancy-balance at src/ImdoTreasury.sol:284: Reentrancy in ImdoTreasury.unlockCallback(bytes) (src/ImdoTreasury.sol#284-301):\n[high/medium] reentrancy-eth at src/ImdoTreasury.sol:163: Reentrancy in ImdoTreasury.process() (src/ImdoTreasury.sol#163-225):\n[high/medium] reentrancy-eth at src/ImdoTreasury.sol:163: Reentrancy in ImdoTreasury.process() (src/ImdoTreasury.sol#163-225):\n[medium/high] incorrect-equality at src/ImdoTreasury.sol:163: ImdoTreasury.process() (src/ImdoTreasury.sol#163-225) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoClaim.sol:90: ImdoClaim.claimSeat(uint256[],bool) (src/ImdoClaim.sol#90-112) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoTreasury.sol:323: ImdoTreasury.quoteMinOut(uint8,uint256) (src/ImdoTreasury.sol#323-334) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoStaking.sol:283: ImdoStaking._releaseBacklog(address) (src/ImdoStaking.sol#283-290) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoTreasury.sol:163: ImdoTreasury.process() (src/ImdoTreasury.sol#163-225) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoHook.sol:111: ImdoHook.currentFeeBps() (src/ImdoHook.sol#111-119) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoTreasury.sol:163: ImdoTreasury.process() (src/ImdoTreasury.sol#163-225) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoTreasury.sol:275: ImdoTreasury.executeBuy(uint256) (src/ImdoTreasury.sol#275-282) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoStaking.sol:294: ImdoStaking._syncBacklogStreams() (src/ImdoStaking.sol#294-303) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoStaking.sol:182: ImdoStaking.notifyReward(address,uint256) (src/ImdoStaking.sol#182-193) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/ImdoTreasury.sol:163: Reentrancy in ImdoTreasury.process() (src/ImdoTreasury.sol#163-225):\n[medium/medium] reentrancy-no-eth at src/ImdoTreasury.sol:336: Reentrancy in ImdoTreasury._executeLeg() (src/ImdoTreasury.sol#336-360):\n[medium/medium] uninitialized-local at src/ImdoTreasury.sol:179: ImdoTreasury.process().bounty (src/ImdoTreasury.sol#179) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ImdoClaim.sol:93: ImdoClaim.claimSeat(uint256[],bool).amount (src/ImdoClaim.sol#93) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ImdoClaim.sol:94: ImdoClaim.claimSeat(uint256[],bool).changed (src/ImdoClaim.sol#94) is a local variable never initialized\n[medium/medium] unused-return at src/ImdoTreasury.sol:104: ImdoTreasury.constructor(address,address,address,address,address,address,uint24,uint24,address,uint256,uint16,uint32,uint256,uint256,uint256) (src/ImdoTreasury.sol#104-151) ignores return value by (_imdLeg.checkpointSqrtPriceX96,None,None,None) = poolManager.getSlot0(_imdLeg.key.toId()) (src/ImdoTreasury.sol#148)\n[medium/medium] unused-return at src/ImdoTreasury.sol:323: ImdoTreasury.quoteMinOut(uint8,uint256) (src/ImdoTreasury.sol#323-334) ignores return value by (sqrtPrice,None,protocolFee,lpFee) = poolManager.getSlot0(_imdLeg.key.toId()) (src/ImdoTreasury.sol#325)\n[medium/medium] unused-return at src/ImdoHook.sol:277: ImdoHook.redeemFees() (src/ImdoHook.sol#277-284) ignores return value by poolManager.unlock(abi.encode(amount)) (src/ImdoHook.sol#281)\n[medium/medium] unused-return at src/ImdoTreasury.sol:336: ImdoTreasury._executeLeg() (src/ImdoTreasury.sol#336-360) ignores return value by (l.checkpointSqrtPriceX96,None,None,None) = poolManager.getSlot0(l.key.toId()) (src/ImdoTreasury.sol#349)\n[medium/medium] unused-return at src/ImdoTreasury.sol:163: ImdoTreasury.process() (src/ImdoTreasury.sol#163-225) ignores return value by (spot,None,None,None) = poolManager.getSlot0(_imdLeg.key.toId()) (src/ImdoTreasury.sol#170)\n[medium/medium] unused-return at src/ImdoTreasury.sol:284: ImdoTreasury.unlockCallback(bytes) (src/ImdoTreasury.sol#284-301) ignores return value by poolManager.settle{value: ethIn}() (src/ImdoTreasury.sol#295)\n[low/medium] calls-loop at src/ImdoClaim.sol:90: ImdoClaim.claimSeat(uint256[],bool) (src/ImdoClaim.sol#90-112) has external calls inside a loop: seatNFT.ownerOf(id) != msg.sender (src/ImdoClaim.sol#98)\n[low/medium] reentrancy-benign at src/ImdoTreasury.sol:275: Reentrancy in ImdoTreasury.executeBuy(uint256) (src/ImdoTreasury.sol#275-282):\n[low/medium] reentrancy-benign at src/ImdoHook.sol:277: Reentrancy in ImdoHook.redeemFees() (src/ImdoHook.sol#277-284):\n[low/medium] reentrancy-benign at src/ImdoTreasury.sol:163: Reentrancy in ImdoTreasury.process() (src/ImdoTreasury.sol#163-225):\n[low/medium] reentrancy-events at src/ImdoHook.sol:295: Reentrancy in ImdoHook._takeFee(SwapParams,uint256) (src/ImdoHook.sol#295-300):\n[low/medium] return-bomb at src/ImdoTreasury.sol:163: ImdoTreasury.process() (src/ImdoTreasury.sol#163-225) tries to limit the gas of an external call that controls implicit decoding\n[low/medium] timestamp at src/ImdoClaim.sol:77: ImdoClaim.claimableSeat(uint256) (src/ImdoClaim.sol#77-81) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoStaking.sol:223: ImdoStaking._unstake(uint256) (src/ImdoStaking.sol#223-234) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoTreasury.sol:314: ImdoTreasury.effectiveCheckpoint() (src/ImdoTreasury.sol#314-321) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoClaim.sol:114: ImdoClaim.claimHolder(uint256,bytes32[],bool) (src/ImdoClaim.sol#114-133) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoStaking.sol:274: ImdoStaking._backlogDue(address) (src/ImdoStaking.sol#274-280) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoHook.sol:295: ImdoHook._takeFee(SwapParams,uint256) (src/ImdoHook.sol#295-300) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoTreasury.sol:284: ImdoTreasury.unlockCallback(bytes) (src/ImdoTreasury.sol#284-301) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoTreasury.sol:336: ImdoTreasury._executeLeg() (src/ImdoTreasury.sol#336-360) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoHook.sol:164: ImdoHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/ImdoHook.sol#164-181) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoClaim.sol:49: ImdoClaim.constructor(address,address,address,uint256,bytes32,uint256) (src/ImdoClaim.sol#49-70) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoClaim.sol:83: ImdoClaim._vested(uint256) (src/ImdoClaim.sol#83-88) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoHook.sol:111: ImdoHook.currentFeeBps() (src/ImdoHook.sol#111-119) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoClaim.sol:90: ImdoClaim.claimSeat(uint256[],bool) (src/ImdoClaim.sol#90-112) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoStaking.sol:251: ImdoStaking._settle(address) (src/ImdoStaking.sol#251-263) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoStaking.sol:294: ImdoStaking._syncBacklogStreams() (src/ImdoStaking.sol#294-303) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoClaim.sol:146: ImdoClaim.burnUnclaimed() (src/ImdoClaim.sol#146-151) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoTreasury.sol:275: ImdoTreasury.executeBuy(uint256) (src/ImdoTreasury.sol#275-282) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoStaking.sol:283: ImdoStaking._releaseBacklog(address) (src/ImdoStaking.sol#283-290) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoTreasury.sol:323: ImdoTreasury.quoteMinOut(uint8,uint256) (src/ImdoTreasury.sol#323-334) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoHook.sol:183: ImdoHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/ImdoHook.sol#183-204) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoTreasury.sol:163: ImdoTreasury.process() (src/ImdoTreasury.sol#163-225) uses timestamp for comparisons","passed":true},{"durationMs":634,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/ImdoTreasury.sol:163: ETH transferred without address checks\n[high] reentrancy-state-change at src/ImdoClaim.sol:58: Reentrancy: State change after external call (7 places)\n[high] unsafe-casting at src/ImdoTreasury.sol:317: Unsafe Casting of integers\n[low] centralization-risk at src/ImdoHook.sol:40: Centralization Risk (2 places)\n[low] costly-loop at src/ImdoClaim.sol:95: Costly operations inside loop (4 places)\n[low] large-numeric-literal at src/ImdoHook.sol:44: Large Numeric Literal (2 places)\n[low] literal-instead-of-constant at src/ImdoClaim.sol:86: Literal Instead of Constant (3 places)\n[low] require-revert-in-loop at src/ImdoClaim.sol:95: Loop Contains `require`/`revert` (3 places)\n[low] state-change-without-event at src/ImdoHook.sol:286: State Change Without Event (3 places)\n[low] state-variable-could-be-immutable at src/ImdoStaking.sol:24: State Variable Could Be Immutable\n[low] storage-array-length-not-cached at src/ImdoStaking.sol:295: Storage Array Length not Cached\n[low] unchecked-return at src/ImdoHook.sol:281: Unchecked Return\n[low] uninitialized-local-variable at src/ImdoClaim.sol:95: Uninitialized Local Variable (4 places)","passed":true}],"detail":"launch.json is not a valid launch manifest: (root): Unrecognized key: \"chainId\"","evaluation":"checks","profile":"foundry","status":"rejected","submissionHash":"12306b54129c537470008e7ab930885d429903296387c0029c052307514e85cb","verifiedTreeHash":"7082b8eba57d26acd98955ef346285d3068a11df","verifierVersion":"0.1.0+94826a22"},{"checks":[{"durationMs":76254,"exitCode":0,"name":"build","output":"Compiling 167 files with Solc 0.8.26\nSolc 0.8.26 finished in 76.07s\nCompiler run successful!\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/ImdoClaim.sol:56:51\n   │\n56 │                 || seatSize_ > SEAT_ALLOCATION || launch_ < block.timestamp\n   │                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[missing-events-arithmetic]: `regenCap` is changed without an event but is used in arithmetic\n    ╭▸ src/ImdoTreasury.sol:160:9\n    │\n160 │         regenCap = cap;\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/ImdoClaim.sol:76:13\n   │\n76 │         if (block.timestamp < launch || block.timestamp >= deadline) 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/ImdoClaim.sol:76:41\n   │\n76 │         if (block.timestamp < launch || block.timestamp >= deadline) 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/ImdoClaim.sol:78:13\n   │\n78 │         if (tranches > 10) tranches = 10;\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/ImdoHook.sol:121:13\n    │\n121 │         if (elapsed >= DECAY_DURATION) return base;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:127:64\n    │\n127 │             maxEthPerBuy_ < MIN_ETH_PER_BUY || maxEthPerBuy_ > uint256(uint128(type(int128).max))\n    │                                                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoTreasury.sol:127:72\n    │\n127 │             maxEthPerBuy_ < MIN_ETH_PER_BUY || maxEthPerBuy_ > uint256(uint128(type(int128).max))\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/ImdoTreasury.sol:129:37\n    │\n129 │                 || imdTickSpacing > uint24(TickMath.MAX_TICK_SPACING) || imdFee > LPFeeLibrary.MAX_LP_FEE\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint24' 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-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/ImdoClaim.sol:128:13\n    │\n128 │             staking.stakeFor(msg.sender, amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:144:79\n    │\n144 │             PoolKey(CurrencyLibrary.ADDRESS_ZERO, Currency.wrap(imd), imdFee, int24(imdTickSpacing), IHooks(imdHooks));\n    │                                                                               ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' 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/ImdoTreasury.sol:149:77\n    │\n149 │             if (_imdLeg.checkpointSqrtPriceX96 != 0) _imdLeg.checkpointAt = 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[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/ImdoClaim.sol:98:13\n   │\n98 │             amount += due;\n   │             ━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/ImdoClaim.sol:101:29\n    │\n101 │         if (amount == 0 && !changed) revert NothingUnlocked();\n    │                             ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/ImdoClaim.sol:83:13\n   │\n83 │         if (block.timestamp >= deadline) revert ClaimClosed();\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/ImdoClaim.sol:84:13\n   │\n84 │         if (block.timestamp < launch) revert NothingUnlocked();\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/ImdoClaim.sol:90:17\n   │\n90 │             if (seatNFT.ownerOf(id) != msg.sender) revert NotOwner();\n   │                 ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/ImdoClaim.sol:70:29\n   │\n70 │         if (id >= seatSize) revert InvalidToken();\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/ImdoClaim.sol:90:52\n   │\n90 │             if (seatNFT.ownerOf(id) != msg.sender) revert NotOwner();\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/ImdoClaim.sol:106:13\n    │\n106 │         if (block.timestamp >= deadline) revert ClaimClosed();\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/ImdoClaim.sol:121:9\n    │\n121 │         emit HolderClaimed(msg.sender, amount, stake);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoHook.sol:175:31\n    │\n175 │             launchTimestamp = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoHook.sol:183:42\n    │\n183 │             params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:183:77\n    │\n183 │             params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoStaking.sol:116:9\n    │\n116 │         emit Staked(msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoHook.sol:198:46\n    │\n198 │                 params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:198:81\n    │\n198 │                 params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:201:47\n    │\n201 │                 params.amountSpecified < 0 ? -int256(requested - prepaidFee) : int256(requested + prepaidFee);\n    │                                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/ImdoHook.sol:201:80\n    │\n201 │                 params.amountSpecified < 0 ? -int256(requested - prepaidFee) : int256(requested + prepaidFee);\n    │                                                                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/ImdoHook.sol:206:44\n    │\n206 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:206:52\n    │\n206 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\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/ImdoHook.sol:206:74\n    │\n206 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\n    │                                                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:206:82\n    │\n206 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\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-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/ImdoHook.sol:266:9\n    │\n266 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoHook.sol:268:9\n    │\n268 │         emit FeesRedeemed(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/ImdoHook.sol:266:9\n    │\n266 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_imdLeg` is updated\n    ╭▸ src/ImdoTreasury.sol:329:13\n    │\n329 │         try this.executeImd(ethIn) returns (uint256 out) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/ImdoClaim.sol:137:13\n    │\n137 │         if (block.timestamp < deadline) revert TooEarly();\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/ImdoTreasury.sol:174:22\n    │\n174 │             bounty = amount * KEEPER_BPS / BPS;\n    │                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/ImdoTreasury.sol:208:13\n    │\n208 │         if (bounty != 0) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/ImdoTreasury.sol:165:13\n    │\n165 │         if (block.timestamp < availableAt) revert CooldownActive(availableAt);\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/ImdoTreasury.sol:201:17\n    │\n201 │                 emit RegenSent(regenPending);\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/ImdoTreasury.sol:204:17\n    │\n204 │                 emit RegenDeferred(regenPending, reason);\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/ImdoTreasury.sol:213:17\n    │\n213 │                 emit KeeperPaid(msg.sender, bounty);\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/ImdoTreasury.sol:217:17\n    │\n217 │                 emit KeeperPaymentDeferred(msg.sender, bounty);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:171:25\n    │\n171 │         lastProcessed = 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/ImdoHook.sol:302:17\n    │\n302 │                 emit FeesDeferred(fee);\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/ImdoHook.sol:305:9\n    │\n305 │         emit FeeTaken(params.zeroForOne, ethAmount, fee, bps);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[return-bomb]: external call with a gas limit may copy unbounded return data\n    ╭▸ src/ImdoTreasury.sol:211:26\n    │\n211 │             (bool ok,) = msg.sender.call{value: bounty, gas: 100_000}(\"\");\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/return-bomb\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoStaking.sol:257:13\n    │\n257 │             emit RewardClaimed(account, token, 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/ImdoStaking.sol:157:9\n    │\n157 │         emit RewardNotified(token, msg.sender, received, distributed);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `_status` is updated\n    ╭▸ src/ImdoTreasury.sol:251:22\n    │\n251 │         (bool ok,) = recipient.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoTreasury.sol:228:9\n    │\n228 │         emit OpsPaid(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/ImdoTreasury.sol:237:9\n    │\n237 │         emit OffsetsPaid(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/ImdoTreasury.sol:247:9\n    │\n247 │         emit KeeperPaid(msg.sender, 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/ImdoStaking.sol:171:9\n    │\n171 │         emit Staked(beneficiary, 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/ImdoStaking.sol:182:9\n    │\n182 │         emit RewardClaimed(msg.sender, token, 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/ImdoStaking.sol:190:9\n    │\n190 │         emit RegenNotified(msg.sender, msg.value, distributed);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/ImdoTreasury.sol:251:22\n    │\n251 │         (bool ok,) = recipient.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/ImdoTreasury.sol:260:9\n    │\n260 │         staking.notifyReward(token, balance);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoTreasury.sol:262:9\n    │\n262 │         emit RewardsFlushed(token, balance);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:284:60\n    │\n284 │         if (sqrtPriceX96 < checkpointFloor) sqrtPriceX96 = uint160(checkpointFloor);\n    │                                                            ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' 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-eth]: uncapped ETH transfer can be reentered before `_status` is updated\n    ╭▸ src/ImdoStaking.sol:202:22\n    │\n202 │         (bool ok,) = regenSafe.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoStaking.sol:204:9\n    │\n204 │         emit RegenWithdrawn(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:299:61\n    │\n299 │             poolManager.swap(_imdLeg.key, SwapParams(true, -int256(ethIn), TickMath.MIN_SQRT_PRICE + 1), \"\");\n    │                                                             ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/ImdoTreasury.sol:300:37\n    │\n300 │         if (delta.amount0() >= 0 || uint256(-int256(delta.amount0())) != ethIn) revert PartialFill();\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoTreasury.sol:302:29\n    │\n302 │         uint256 amountOut = uint256(uint128(delta.amount1()));\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoTreasury.sol:302:37\n    │\n302 │         uint256 amountOut = uint256(uint128(delta.amount1()));\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[unused-return]: return value of an external call is not used\n    ╭▸ src/ImdoTreasury.sol:304:9\n    │\n304 │         poolManager.settle{value: ethIn}();\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/ImdoTreasury.sol:322:32\n    │\n322 │         if (l.failures != 0 && block.timestamp > uint256(l.lastFailure) + gap) _resetFailures();\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/ImdoTreasury.sol:334:13\n    │\n334 │             emit LegBought(LEG_IMD, Currency.unwrap(l.key.currency1), ethIn, out);\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/ImdoTreasury.sol:336:13\n    │\n336 │             emit LegFailed(LEG_IMD, ethIn, reason);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:333:30\n    │\n333 │             l.checkpointAt = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:337:51\n    │\n337 │             if (l.failures == 0) l.failingSince = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:338:29\n    │\n338 │             l.lastFailure = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:361:36\n    │\n361 │             _imdLeg.checkpointAt = 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/ImdoStaking.sol:239:13\n    │\n239 │         if (block.timestamp < unlockTime[msg.sender]) revert StakeLocked(unlockTime[msg.sender]);\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/ImdoStaking.sol:244:9\n    │\n244 │         emit Unstaked(msg.sender, amount);\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/ImdoStaking.sol:286:13\n    │\n286 │         if (elapsed > BACKLOG_DURATION) elapsed = BACKLOG_DURATION;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoStaking.sol:308:77\n    │\n308 │                 backlogStream[token] = BacklogStream(unallocated[token], 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\n","passed":true},{"durationMs":1717,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 6 tests for test/unit/LaunchToken.t.sol:IMDOTokenTest\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 80063, ~: 80349)\n[PASS] test_metadata() (gas: 24431)\n[PASS] test_mintsFixedSupplyToDeployer() (gas: 23278)\n[PASS] test_noMintOrAdminEntryPoints() (gas: 143123)\n[PASS] test_transferMovesExactAmount() (gas: 74094)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 34859)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 49.72ms (50.30ms CPU time)\n\nRan 5 tests for test/unit/ImdoClaim.t.sol:ImdoClaimTest\n[PASS] test_duplicateIdsAndOwnerChecks() (gas: 250140)\n[PASS] test_holderProofPartialDoubleClaimAndStake() (gas: 529656)\n[PASS] test_launchDeadlineAndBurnBoundaries() (gas: 335174)\n[PASS] test_seatTransferMovesOnlyRemainingEntitlement() (gas: 522259)\n[PASS] test_zeroRootDisablesHolderClaims() (gas: 219423)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 51.49ms (1.64ms CPU time)\n\nRan 7 tests for test/unit/ImdoStaking.t.sol:ImdoStakingTest\n[PASS] testFuzz_lifetimeCreditSurvivesClaimsAndExit(uint128,uint128,uint96) (runs: 256, μ: 742750, ~: 750464)\n[PASS] test_backlogThresholdStreamPauseAndNoPastCredit() (gas: 1742638)\n[PASS] test_failedOrReentrantWithdrawalRollsBack() (gas: 718463)\n[PASS] test_invalidIntakeAndClaims() (gas: 188271)\n[PASS] test_rewardsProRataAndEthIsNeverClaimed() (gas: 898336)\n[PASS] test_stakeForRestrictedAndEveryStakeResetsLock() (gas: 566570)\n[PASS] test_withdrawOnlySafeBoundedEvenWithForcedEthAndNoStake() (gas: 296995)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 51.53ms (53.70ms CPU time)\n\nRan 11 tests for test/unit/DistributorAdversarial.t.sol:DistributorAdversarialTest\n[PASS] testFuzz_taxedRewardCreditsOnlyReceivedTokens(uint256,uint16) (runs: 1000, μ: 519625, ~: 522476)\n[PASS] testFuzz_twoEpochsFollowStakeOwnership(uint256,uint256,uint256,uint256) (runs: 1000, μ: 890629, ~: 898008)\n[PASS] test_allFixedExclusionsRejectStakeBeforeTokenInteraction() (gas: 418142)\n[PASS] test_claimCallbackCannotReenterAnyHolderOrRewardAction() (gas: 523169)\n[PASS] test_failedStakeRollsBackBacklogAndShares() (gas: 600805)\n[PASS] test_falseReturningIntakeCannotCreateCredit() (gas: 267523)\n[PASS] test_fullSupplyCanStakeAndExit() (gas: 601080)\n[PASS] test_noReturnTokenCanNotifyAndClaim() (gas: 478149)\n[PASS] test_notifyCallbackCannotReenterAnyHolderOrRewardAction() (gas: 370405)\n[PASS] test_oneWeiAndCompleteTransferTax() (gas: 543864)\n[PASS] test_pausedRewardCannotTrapUnstakedPrincipal() (gas: 633583)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 72.32ms (145.46ms CPU time)\n\nRan 1 test for test/unit/Integration.t.sol:IntegrationTest\n[PASS] test_buyStakeProcessClaimAndWithdraw() (gas: 1555567)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 713.70ms (722.90µs CPU time)\n\nRan 3 tests for test/unit/ImdoDeploy.t.sol:ImdoDeployTest\n[PASS] test_manualSignerRunUsesArgumentsRegardlessOfCaller() (gas: 32656773)\n[PASS] test_scriptDeploysFundsSeedsBurnsLpAndStartsOwnershipTransfer() (gas: 32845340)\n[PASS] test_scriptRejectsMissingCodeWrongChainAndBadTickBeforeCreation() (gas: 19359033)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 777.87ms (69.13ms CPU time)\n\nRan 1 test for test/fuzz/Treasury.fuzz.t.sol:TreasuryFuzzTest\n[PASS] testFuzz_capAndConservation(uint96) (runs: 256, μ: 524665, ~: 524685)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 797.88ms (59.27ms CPU time)\n\nRan 4 tests for test/unit/HookAdversarial.t.sol:HookAdversarialTest\n[PASS] testFuzz_exactInputSellChargesGrossEthAtAnyDecayTime(uint256,uint32,uint16) (runs: 1000, μ: 386499, ~: 387154)\n[PASS] testFuzz_exactOutputSellDeliversRequestedNetAndGrossFee(uint256,uint32) (runs: 1000, μ: 352458, ~: 352659)\n[PASS] test_decayBoundariesAndNoFeeResurrection() (gas: 526585)\n[PASS] test_partialExactOutputSellRevertsFeeAndPoolMovement() (gas: 482068)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 820.40ms (201.81ms CPU time)\n\nRan 12 tests for test/unit/ImdoTreasury.t.sol:ImdoTreasuryTest\n[PASS] testFuzz_splitConservesEveryWei(uint96) (runs: 256, μ: 796439, ~: 737132)\n[PASS] test_capOverflowAndEpochRoll() (gas: 1503255)\n[PASS] test_capSetterBoundsAuthAndNoRetroactiveResplit() (gas: 981747)\n[PASS] test_cooldownAndCallbackAuthorization() (gas: 897644)\n[PASS] test_dustAndFlush() (gas: 528446)\n[PASS] test_exactSplitBountyAndAuthorizedPulls() (gas: 671458)\n[PASS] test_failedRegenRetryDoesNotChargeAgainOrBlockBuy() (gas: 1012551)\n[PASS] test_failedRewardNotifyRollsBackBuyAndHalvesRetry() (gas: 1203028)\n[PASS] test_missingDependenciesDoNotSeedPermanentZeroCheckpoint() (gas: 6083833)\n[PASS] test_rejectedAndReentrantPullPaymentsPreserveOwed() (gas: 1149245)\n[PASS] test_rejectingKeeperCanPullToAlternateRecipient() (gas: 886966)\n[PASS] test_staleCheckpointRecoversWithoutRerouting() (gas: 4483731)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 823.85ms (29.84ms CPU time)\n\nRan 5 tests for test/unit/Revision.t.sol:RevisionTest\n[PASS] testFuzz_exactOutputFeesAreGrossRate(uint32,uint128) (runs: 256, μ: 339691, ~: 339848)\n[PASS] test_emptySellCannotChargeFeeOrStartClock() (gas: 372929)\n[PASS] test_exactOutputAndInputBuySameGrossFeeAtLaunch() (gas: 372223)\n[PASS] test_firstFilledSwapStartsFullDecayAfterDeploymentDelay() (gas: 227125)\n[PASS] test_partialInputBuyRevertsAndReturnsEntireFee() (gas: 458441)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 856.32ms (28.46ms CPU time)\n\nRan 4 tests for test/unit/HookDeferredFees.t.sol:HookDeferredFeesTest\n[PASS] test_failedRedemptionPreservesClaimAndCannotRedirect() (gas: 488920)\n[PASS] test_firstBuyOnTokenOnlyManagerThenPermissionlessRedemption() (gas: 354136)\n[PASS] test_freshManagerExactOutputBuyAlsoDefersFee() (gas: 278888)\n[PASS] test_fundedManagerUsesOriginalDirectPayment() (gas: 195363)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.01s (1.46ms CPU time)\n\nRan 1 test for test/invariant/Treasury.invariant.t.sol:TreasuryInvariantTest\n[PASS] invariant_ethEqualsOwedPlusPendingAndUnsplit() (runs: 128, calls: 6144, reverts: 0)\n\n╭-----------------+----------+-------+---------+----------╮\n| Contract        | Selector | Calls | Reverts | Discards |\n+=========================================================+\n| TreasuryHandler | cap      | 1002  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | fund     | 1015  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | keeper   | 1071  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | pay      | 1050  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | process  | 993   | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | withdraw | 1013  | 0       | 0        |\n╰-----------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.23s (418.46ms CPU time)\n\nRan 1 test for test/invariant/Staking.invariant.t.sol:StakingInvariantTest\n[PASS] invariant_principalAndRewardConservation() (runs: 128, calls: 8192, reverts: 0)\n\n╭----------------+----------+-------+---------+----------╮\n| Contract       | Selector | Calls | Reverts | Discards |\n+========================================================+\n| StakingHandler | advance  | 1362  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | claim    | 1350  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | notify   | 1341  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | stake    | 1338  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | unstake  | 1357  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | withdraw | 1444  | 0       | 0        |\n╰----------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.37s (1.37s CPU time)\n\nRan 22 tests for test/unit/ImdoHook.t.sol:ImdoHookTest\n[PASS] testFuzz_buyFeeMatchesSchedule(uint256,uint32) (runs: 256, μ: 331722, ~: 331953)\n[PASS] testFuzz_decayIsMonotoneAndBounded(uint32,uint32) (runs: 256, μ: 196707, ~: 196697)\n[PASS] test_addressCarriesExactlyTheRequiredFlags() (gas: 6313)\n[PASS] test_antiSnipeFeeAtLaunchIs20Percent() (gas: 186101)\n[PASS] test_buyExactInput_takes1_5PercentOfEthIn() (gas: 345952)\n[PASS] test_buyExactOutput_feeIsAddedOnTopOfEthIn() (gas: 347053)\n[PASS] test_callbacksRejectNonPoolManager() (gas: 90785)\n[PASS] test_constructorRejectsUnflaggedAddress() (gas: 12526)\n[PASS] test_decaySchedule() (gas: 223606)\n[PASS] test_feeCannotBeRaisedOrKept() (gas: 122078)\n[PASS] test_feeEventsAreEmitted() (gas: 334937)\n[PASS] test_initialState() (gas: 43201)\n[PASS] test_initializeOnlyOnce() (gas: 46337)\n[PASS] test_initializeRejectsWrongPair() (gas: 159603622)\n[PASS] test_initializeRequiresOwner() (gas: 159715886)\n[PASS] test_lowerFee() (gas: 240864)\n[PASS] test_lowerFeeDuringDecayKeepsDecaying() (gas: 220297)\n[PASS] test_lowerFeeToZeroDisablesFee() (gas: 358443)\n[PASS] test_onlyOwnerLowersFee() (gas: 35224)\n[PASS] test_sellExactInput_takes1_5PercentOfEthOut() (gas: 465838)\n[PASS] test_sellExactOutput_feeIsAddedOnTopOfEthOut() (gas: 467897)\n[PASS] test_twoStepOwnershipTransfer() (gas: 157199)\nSuite result: ok. 22 passed; 0 failed; 0 skipped; finished in 1.51s (1.71s CPU time)\n\nRan 14 test suites in 1.51s (10.14s CPU time): 83 tests passed, 0 failed, 0 skipped (83 total tests)\n","passed":true},{"durationMs":101,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDOToken.approve(address,uint256)\",\"IMDOToken.transfer(address,uint256)\",\"IMDOToken.transferFrom(address,address,uint256)\",\"ImdoClaim.burnUnclaimed()\",\"ImdoClaim.claimHolder(uint256,bytes32[],bool)\",\"ImdoClaim.claimSeat(uint256[],bool)\",\"ImdoHook.acceptOwnership()\",\"ImdoHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"ImdoHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"ImdoHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"ImdoHook.lowerFee(uint16)\",\"ImdoHook.redeemFees()\",\"ImdoHook.renounceOwnership()\",\"ImdoHook.transferOwnership(address)\",\"ImdoHook.unlockCallback(bytes)\",\"ImdoStaking.claim()\",\"ImdoStaking.claimReward(address)\",\"ImdoStaking.exit()\",\"ImdoStaking.notifyRegen()\",\"ImdoStaking.notifyReward(address,uint256)\",\"ImdoStaking.stake(uint256)\",\"ImdoStaking.stakeFor(address,uint256)\",\"ImdoStaking.unstake(uint256)\",\"ImdoStaking.withdrawRegen(uint256)\",\"ImdoTreasury.claimKeeper(address)\",\"ImdoTreasury.executeImd(uint256)\",\"ImdoTreasury.flushRewards()\",\"ImdoTreasury.payOffsets()\",\"ImdoTreasury.payOps()\",\"ImdoTreasury.process()\",\"ImdoTreasury.receive()\",\"ImdoTreasury.setRegenCap(uint256)\",\"ImdoTreasury.unlockCallback(bytes)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"LICENSE\":21,\"README.md\":90,\"SELF_AUDIT.md\":7,\"docs/DEPLOYMENT.md\":42,\"docs/REVIEW.md\":49,\"foundry.toml\":32,\"launch.json\":48,\"remappings.txt\":9,\"script/DeployImdo.s.sol\":168,\"script/utils/HookMiner.sol\":60,\"src/IMDOToken.sol\":6,\"src/ImdoClaim.sol\":142,\"src/ImdoHook.sol\":307,\"src/ImdoStaking.sol\":312,\"src/ImdoTreasury.sol\":365,\"src/LaunchToken.sol\":17,\"src/interfaces/IImdoStaking.sol\":7,\"test/REVISION.md\":7,\"test/TESTING.md\":33,\"test/fork/MainnetFork.t.sol\":56,\"test/fuzz/Treasury.fuzz.t.sol\":28,\"test/invariant/Staking.invariant.t.sol\":145,\"test/invariant/Treasury.invariant.t.sol\":102,\"test/unit/DistributorAdversarial.t.sol\":284,\"test/unit/HookAdversarial.t.sol\":101,\"test/unit/HookDeferredFees.t.sol\":123,\"test/unit/ImdoClaim.t.sol\":163,\"test/unit/ImdoDeploy.t.sol\":129,\"test/unit/ImdoHook.t.sol\":277,\"test/unit/ImdoStaking.t.sol\":214,\"test/unit/ImdoTreasury.t.sol\":338,\"test/unit/Integration.t.sol\":43,\"test/unit/LaunchToken.t.sol\":64,\"test/unit/Revision.t.sol\":78,\"test/utils/LocalV4.sol\":159,\"test/utils/Permit2Mock.sol\":23},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":5128,"exitCode":0,"name":"slither","output":"[high/medium] arbitrary-send-eth at src/ImdoTreasury.sol:250: ImdoTreasury._pay(address,uint256) (src/ImdoTreasury.sol#250-253) sends eth to arbitrary user\n[high/medium] reentrancy-eth at src/ImdoTreasury.sol:163: Reentrancy in ImdoTreasury.process() (src/ImdoTreasury.sol#163-220):\n[high/medium] reentrancy-eth at src/ImdoTreasury.sol:163: Reentrancy in ImdoTreasury.process() (src/ImdoTreasury.sol#163-220):\n[medium/high] incorrect-equality at src/ImdoStaking.sol:302: ImdoStaking._syncBacklogStreams() (src/ImdoStaking.sol#302-311) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoClaim.sol:82: ImdoClaim.claimSeat(uint256[],bool) (src/ImdoClaim.sol#82-103) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoTreasury.sol:255: ImdoTreasury.flushRewards() (src/ImdoTreasury.sol#255-263) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoTreasury.sol:163: ImdoTreasury.process() (src/ImdoTreasury.sol#163-220) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoTreasury.sol:352: ImdoTreasury._ensurePool() (src/ImdoTreasury.sol#352-364) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoTreasury.sol:352: ImdoTreasury._ensurePool() (src/ImdoTreasury.sol#352-364) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoHook.sol:116: ImdoHook.currentFeeBps() (src/ImdoHook.sol#116-124) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoTreasury.sol:352: ImdoTreasury._ensurePool() (src/ImdoTreasury.sol#352-364) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoTreasury.sol:292: ImdoTreasury.unlockCallback(bytes) (src/ImdoTreasury.sol#292-307) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoStaking.sol:147: ImdoStaking.notifyReward(address,uint256) (src/ImdoStaking.sol#147-158) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/ImdoStaking.sol:291: ImdoStaking._releaseBacklog(address) (src/ImdoStaking.sol#291-298) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/ImdoTreasury.sol:163: Reentrancy in ImdoTreasury.process() (src/ImdoTreasury.sol#163-220):\n[medium/medium] reentrancy-no-eth at src/ImdoTreasury.sol:319: Reentrancy in ImdoTreasury._executeImd() (src/ImdoTreasury.sol#319-343):\n[medium/medium] uninitialized-local at src/ImdoTreasury.sol:172: ImdoTreasury.process().bounty (src/ImdoTreasury.sol#172) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ImdoClaim.sol:85: ImdoClaim.claimSeat(uint256[],bool).amount (src/ImdoClaim.sol#85) is a local variable never initialized\n[medium/medium] uninitialized-local at src/ImdoClaim.sol:86: ImdoClaim.claimSeat(uint256[],bool).changed (src/ImdoClaim.sol#86) is a local variable never initialized\n[medium/medium] unused-return at src/ImdoTreasury.sol:105: ImdoTreasury.constructor(address,address,address,address,address,address,uint24,uint24,address,uint256,uint16,uint32,uint256,uint256,uint256) (src/ImdoTreasury.sol#105-151) ignores return value by (_imdLeg.checkpointSqrtPriceX96,None,None,None) = poolManager.getSlot0(_imdLeg.key.toId()) (src/ImdoTreasury.sol#148)\n[medium/medium] unused-return at src/ImdoTreasury.sol:352: ImdoTreasury._ensurePool() (src/ImdoTreasury.sol#352-364) ignores return value by (price,None,None,None) = poolManager.getSlot0(_imdLeg.key.toId()) (src/ImdoTreasury.sol#357)\n[medium/medium] unused-return at src/ImdoHook.sol:262: ImdoHook.redeemFees() (src/ImdoHook.sol#262-269) ignores return value by poolManager.unlock(abi.encode(amount)) (src/ImdoHook.sol#266)\n[medium/medium] unused-return at src/ImdoTreasury.sol:319: ImdoTreasury._executeImd() (src/ImdoTreasury.sol#319-343) ignores return value by (l.checkpointSqrtPriceX96,None,None,None) = poolManager.getSlot0(l.key.toId()) (src/ImdoTreasury.sol#332)\n[medium/medium] unused-return at src/ImdoTreasury.sol:276: ImdoTreasury.quoteMinOut(uint8,uint256) (src/ImdoTreasury.sol#276-290) ignores return value by (sqrtPriceX96,None,protocolFee,lpFee) = poolManager.getSlot0(l.key.toId()) (src/ImdoTreasury.sol#279)\n[medium/medium] unused-return at src/ImdoTreasury.sol:292: ImdoTreasury.unlockCallback(bytes) (src/ImdoTreasury.sol#292-307) ignores return value by poolManager.settle{value: ethIn}() (src/ImdoTreasury.sol#304)\n[low/medium] calls-loop at src/ImdoClaim.sol:82: ImdoClaim.claimSeat(uint256[],bool) (src/ImdoClaim.sol#82-103) has external calls inside a loop: seatNFT.ownerOf(id) != msg.sender (src/ImdoClaim.sol#90)\n[low/medium] reentrancy-benign at src/ImdoHook.sol:262: Reentrancy in ImdoHook.redeemFees() (src/ImdoHook.sol#262-269):\n[low/medium] reentrancy-benign at src/ImdoTreasury.sol:163: Reentrancy in ImdoTreasury.process() (src/ImdoTreasury.sol#163-220):\n[low/medium] reentrancy-events at src/ImdoHook.sol:294: Reentrancy in ImdoHook._takeFee(SwapParams,uint256) (src/ImdoHook.sol#294-306):\n[low/medium] reentrancy-events at src/ImdoHook.sol:294: Reentrancy in ImdoHook._takeFee(SwapParams,uint256) (src/ImdoHook.sol#294-306):\n[low/medium] return-bomb at src/ImdoTreasury.sol:163: ImdoTreasury.process() (src/ImdoTreasury.sol#163-220) tries to limit the gas of an external call that controls implicit decoding\n[low/medium] timestamp at src/ImdoClaim.sol:136: ImdoClaim.burnUnclaimed() (src/ImdoClaim.sol#136-141) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoTreasury.sol:319: ImdoTreasury._executeImd() (src/ImdoTreasury.sol#319-343) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoClaim.sol:46: ImdoClaim.constructor(address,address,address,uint256,bytes32,uint256) (src/ImdoClaim.sol#46-67) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoClaim.sol:105: ImdoClaim.claimHolder(uint256,bytes32[],bool) (src/ImdoClaim.sol#105-123) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoTreasury.sol:276: ImdoTreasury.quoteMinOut(uint8,uint256) (src/ImdoTreasury.sol#276-290) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoStaking.sol:291: ImdoStaking._releaseBacklog(address) (src/ImdoStaking.sol#291-298) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoHook.sol:169: ImdoHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/ImdoHook.sol#169-186) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoStaking.sol:235: ImdoStaking._unstake(uint256) (src/ImdoStaking.sol#235-246) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoClaim.sol:69: ImdoClaim.seatClaimable(uint256) (src/ImdoClaim.sol#69-73) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoStaking.sol:263: ImdoStaking._settle(address) (src/ImdoStaking.sol#263-272) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoTreasury.sol:292: ImdoTreasury.unlockCallback(bytes) (src/ImdoTreasury.sol#292-307) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoHook.sol:188: ImdoHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/ImdoHook.sol#188-209) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoTreasury.sol:105: ImdoTreasury.constructor(address,address,address,address,address,address,uint24,uint24,address,uint256,uint16,uint32,uint256,uint256,uint256) (src/ImdoTreasury.sol#105-151) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoTreasury.sol:352: ImdoTreasury._ensurePool() (src/ImdoTreasury.sol#352-364) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoStaking.sol:302: ImdoStaking._syncBacklogStreams() (src/ImdoStaking.sol#302-311) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoClaim.sol:75: ImdoClaim._vested(uint256) (src/ImdoClaim.sol#75-80) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoClaim.sol:82: ImdoClaim.claimSeat(uint256[],bool) (src/ImdoClaim.sol#82-103) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoHook.sol:294: ImdoHook._takeFee(SwapParams,uint256) (src/ImdoHook.sol#294-306) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoHook.sol:116: ImdoHook.currentFeeBps() (src/ImdoHook.sol#116-124) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoTreasury.sol:163: ImdoTreasury.process() (src/ImdoTreasury.sol#163-220) uses timestamp for comparisons\n[low/medium] timestamp at src/ImdoStaking.sol:282: ImdoStaking._backlogDue(address) (src/ImdoStaking.sol#282-288) uses timestamp for comparisons","passed":true},{"durationMs":632,"exitCode":0,"name":"aderyn","output":"[high] eth-send-unchecked-address at src/ImdoTreasury.sol:163: ETH transferred without address checks\n[high] reentrancy-state-change at src/ImdoClaim.sol:55: Reentrancy: State change after external call (6 places)\n[low] centralization-risk at src/ImdoHook.sol:40: Centralization Risk (2 places)\n[low] costly-loop at src/ImdoClaim.sol:87: Costly operations inside loop (4 places)\n[low] large-numeric-literal at src/ImdoHook.sol:44: Large Numeric Literal (2 places)\n[low] literal-instead-of-constant at src/ImdoClaim.sol:78: Literal Instead of Constant (3 places)\n[low] require-revert-in-loop at src/ImdoClaim.sol:87: Loop Contains `require`/`revert` (3 places)\n[low] state-variable-could-be-immutable at src/ImdoStaking.sol:40: State Variable Could Be Immutable\n[low] storage-array-length-not-cached at src/ImdoStaking.sol:303: Storage Array Length not Cached\n[low] unchecked-return at src/ImdoHook.sol:266: Unchecked Return\n[low] uninitialized-local-variable at src/ImdoClaim.sol:87: Uninitialized Local Variable (4 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"2d8d683f9eb8c55210be2874a2a3a976af54d68c23fa60cc5a89bc9bbab47cc6","verifiedTreeHash":"0f261f75efea907000d7e9de2de9b3e9d88d8ab3","verifierVersion":"0.1.0+94826a22"},{"checks":[{"durationMs":109856,"exitCode":0,"name":"build","output":"Compiling 172 files with Solc 0.8.26\nSolc 0.8.26 finished in 109.61s\nCompiler run successful!\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/ImdoClaim.sol:56:51\n   │\n56 │                 || seatSize_ > SEAT_ALLOCATION || launch_ < block.timestamp\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/ImdoClaim.sol:76:13\n   │\n76 │         if (block.timestamp < launch || block.timestamp >= deadline) 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/ImdoClaim.sol:76:41\n   │\n76 │         if (block.timestamp < launch || block.timestamp >= deadline) 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/ImdoClaim.sol:78:13\n   │\n78 │         if (tranches > 10) tranches = 10;\n   │             ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/ImdoClaim.sol:128:13\n    │\n128 │             staking.stakeFor(msg.sender, amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[uninitialized-local]: local variable is read before being initialized\n   ╭▸ src/ImdoClaim.sol:98:13\n   │\n98 │             amount += due;\n   │             ━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/ImdoClaim.sol:101:29\n    │\n101 │         if (amount == 0 && !changed) revert NothingUnlocked();\n    │                             ━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/ImdoClaim.sol:83:13\n   │\n83 │         if (block.timestamp >= deadline) revert ClaimClosed();\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/ImdoClaim.sol:84:13\n   │\n84 │         if (block.timestamp < launch) revert NothingUnlocked();\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[calls-loop]: external call inside a loop\n   ╭▸ src/ImdoClaim.sol:90:17\n   │\n90 │             if (seatNFT.ownerOf(id) != msg.sender) revert NotOwner();\n   │                 ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n   ╭▸ src/ImdoClaim.sol:70:29\n   │\n70 │         if (id >= seatSize) revert InvalidToken();\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/ImdoClaim.sol:90:52\n   │\n90 │             if (seatNFT.ownerOf(id) != msg.sender) revert NotOwner();\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/ImdoClaim.sol:106:13\n    │\n106 │         if (block.timestamp >= deadline) revert ClaimClosed();\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/ImdoClaim.sol:121:9\n    │\n121 │         emit HolderClaimed(msg.sender, amount, stake);\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/ImdoHook.sol:121:13\n    │\n121 │         if (elapsed >= DECAY_DURATION) return base;\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/ImdoStaking.sol:116:9\n    │\n116 │         emit Staked(msg.sender, 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/ImdoStaking.sol:257:13\n    │\n257 │             emit RewardClaimed(account, token, amount);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoHook.sol:175:31\n    │\n175 │             launchTimestamp = 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/ImdoStaking.sol:157:9\n    │\n157 │         emit RewardNotified(token, msg.sender, received, distributed);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoHook.sol:183:42\n    │\n183 │             params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:183:77\n    │\n183 │             params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:198:46\n    │\n198 │                 params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:198:81\n    │\n198 │                 params.amountSpecified < 0 ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:201:47\n    │\n201 │                 params.amountSpecified < 0 ? -int256(requested - prepaidFee) : int256(requested + prepaidFee);\n    │                                               ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/ImdoHook.sol:201:80\n    │\n201 │                 params.amountSpecified < 0 ? -int256(requested - prepaidFee) : int256(requested + prepaidFee);\n    │                                                                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/ImdoHook.sol:206:44\n    │\n206 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\n    │                                            ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:206:52\n    │\n206 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\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/ImdoHook.sol:206:74\n    │\n206 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\n    │                                                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoHook.sol:206:82\n    │\n206 │         uint256 ethAmount = ethDelta < 0 ? uint256(uint128(-ethDelta)) : uint256(uint128(ethDelta));\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-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/ImdoHook.sol:266:9\n    │\n266 │         poolManager.unlock(abi.encode(amount));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoHook.sol:268:9\n    │\n268 │         emit FeesRedeemed(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/ImdoHook.sol:266:9\n    │\n266 │         poolManager.unlock(abi.encode(amount));\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/ImdoClaim.sol:137:13\n    │\n137 │         if (block.timestamp < deadline) revert TooEarly();\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/ImdoStaking.sol:171:9\n    │\n171 │         emit Staked(beneficiary, 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/ImdoStaking.sol:182:9\n    │\n182 │         emit RewardClaimed(msg.sender, token, 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/ImdoStaking.sol:190:9\n    │\n190 │         emit RegenNotified(msg.sender, msg.value, distributed);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `_status` is updated\n    ╭▸ src/ImdoStaking.sol:202:22\n    │\n202 │         (bool ok,) = regenSafe.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoStaking.sol:204:9\n    │\n204 │         emit RegenWithdrawn(amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-events-arithmetic]: `regenCap` is changed without an event but is used in arithmetic\n    ╭▸ src/ImdoTreasury.sol:160:9\n    │\n160 │         regenCap = cap;\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoHook.sol:302:17\n    │\n302 │                 emit FeesDeferred(fee);\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/ImdoHook.sol:305:9\n    │\n305 │         emit FeeTaken(params.zeroForOne, ethAmount, fee, bps);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:127:64\n    │\n127 │             maxEthPerBuy_ < MIN_ETH_PER_BUY || maxEthPerBuy_ > uint256(uint128(type(int128).max))\n    │                                                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoTreasury.sol:127:72\n    │\n127 │             maxEthPerBuy_ < MIN_ETH_PER_BUY || maxEthPerBuy_ > uint256(uint128(type(int128).max))\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/ImdoTreasury.sol:129:37\n    │\n129 │                 || imdTickSpacing > uint24(TickMath.MAX_TICK_SPACING) || imdFee > LPFeeLibrary.MAX_LP_FEE\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint24' 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/ImdoTreasury.sol:144:79\n    │\n144 │             PoolKey(CurrencyLibrary.ADDRESS_ZERO, Currency.wrap(imd), imdFee, int24(imdTickSpacing), IHooks(imdHooks));\n    │                                                                               ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' 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/ImdoTreasury.sol:149:77\n    │\n149 │             if (_imdLeg.checkpointSqrtPriceX96 != 0) _imdLeg.checkpointAt = 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/ImdoStaking.sol:239:13\n    │\n239 │         if (block.timestamp < unlockTime[msg.sender]) revert StakeLocked(unlockTime[msg.sender]);\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/ImdoStaking.sol:244:9\n    │\n244 │         emit Unstaked(msg.sender, amount);\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/ImdoStaking.sol:286:13\n    │\n286 │         if (elapsed > BACKLOG_DURATION) elapsed = BACKLOG_DURATION;\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoStaking.sol:308:77\n    │\n308 │                 backlogStream[token] = BacklogStream(unallocated[token], 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-no-eth]: external call can be reentered before `_imdLeg` is updated\n    ╭▸ src/ImdoTreasury.sol:329:13\n    │\n329 │         try this.executeImd(ethIn) returns (uint256 out) {\n    │             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/ImdoTreasury.sol:174:22\n    │\n174 │             bounty = amount * KEEPER_BPS / BPS;\n    │                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/ImdoTreasury.sol:208:13\n    │\n208 │         if (bounty != 0) {\n    │             ━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/ImdoTreasury.sol:165:13\n    │\n165 │         if (block.timestamp < availableAt) revert CooldownActive(availableAt);\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/ImdoTreasury.sol:201:17\n    │\n201 │                 emit RegenSent(regenPending);\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/ImdoTreasury.sol:204:17\n    │\n204 │                 emit RegenDeferred(regenPending, reason);\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/ImdoTreasury.sol:213:17\n    │\n213 │                 emit KeeperPaid(msg.sender, bounty);\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/ImdoTreasury.sol:217:17\n    │\n217 │                 emit KeeperPaymentDeferred(msg.sender, bounty);\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:171:25\n    │\n171 │         lastProcessed = 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[return-bomb]: external call with a gas limit may copy unbounded return data\n    ╭▸ src/ImdoTreasury.sol:211:26\n    │\n211 │             (bool ok,) = msg.sender.call{value: bounty, gas: 100_000}(\"\");\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/return-bomb\n\nwarning[reentrancy-eth]: uncapped ETH transfer can be reentered before `_status` is updated\n    ╭▸ src/ImdoTreasury.sol:251:22\n    │\n251 │         (bool ok,) = recipient.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoTreasury.sol:228:9\n    │\n228 │         emit OpsPaid(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/ImdoTreasury.sol:237:9\n    │\n237 │         emit OffsetsPaid(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/ImdoTreasury.sol:247:9\n    │\n247 │         emit KeeperPaid(msg.sender, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/ImdoTreasury.sol:251:22\n    │\n251 │         (bool ok,) = recipient.call{value: amount}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `_status` is updated\n    ╭▸ src/ImdoTreasury.sol:260:9\n    │\n260 │         staking.notifyReward(token, balance);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/ImdoTreasury.sol:262:9\n    │\n262 │         emit RewardsFlushed(token, balance);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:284:60\n    │\n284 │         if (sqrtPriceX96 < checkpointFloor) sqrtPriceX96 = uint160(checkpointFloor);\n    │                                                            ━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' 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/ImdoTreasury.sol:299:61\n    │\n299 │             poolManager.swap(_imdLeg.key, SwapParams(true, -int256(ethIn), TickMath.MIN_SQRT_PRICE + 1), \"\");\n    │                                                             ━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int256' 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/ImdoTreasury.sol:300:37\n    │\n300 │         if (delta.amount0() >= 0 || uint256(-int256(delta.amount0())) != ethIn) revert PartialFill();\n    │                                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoTreasury.sol:302:29\n    │\n302 │         uint256 amountOut = uint256(uint128(delta.amount1()));\n    │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' 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/ImdoTreasury.sol:302:37\n    │\n302 │         uint256 amountOut = uint256(uint128(delta.amount1()));\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[unused-return]: return value of an external call is not used\n    ╭▸ src/ImdoTreasury.sol:304:9\n    │\n304 │         poolManager.settle{value: ethIn}();\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/ImdoTreasury.sol:322:32\n    │\n322 │         if (l.failures != 0 && block.timestamp > uint256(l.lastFailure) + gap) _resetFailures();\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/ImdoTreasury.sol:334:13\n    │\n334 │             emit LegBought(LEG_IMD, Currency.unwrap(l.key.currency1), ethIn, out);\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/ImdoTreasury.sol:336:13\n    │\n336 │             emit LegFailed(LEG_IMD, ethIn, reason);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:333:30\n    │\n333 │             l.checkpointAt = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:337:51\n    │\n337 │             if (l.failures == 0) l.failingSince = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:338:29\n    │\n338 │             l.lastFailure = 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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/ImdoTreasury.sol:361:36\n    │\n361 │             _imdLeg.checkpointAt = 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\n","passed":true},{"durationMs":8702,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 5 tests for test/unit/ImdoClaim.t.sol:ImdoClaimTest\n[PASS] test_duplicateIdsAndOwnerChecks() (gas: 250140)\n[PASS] test_holderProofPartialDoubleClaimAndStake() (gas: 529656)\n[PASS] test_launchDeadlineAndBurnBoundaries() (gas: 335174)\n[PASS] test_seatTransferMovesOnlyRemainingEntitlement() (gas: 522259)\n[PASS] test_zeroRootDisablesHolderClaims() (gas: 219423)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 1.94ms (1.41ms CPU time)\n\nRan 7 tests for test/unit/ImdoStaking.t.sol:ImdoStakingTest\n[PASS] testFuzz_lifetimeCreditSurvivesClaimsAndExit(uint128,uint128,uint96) (runs: 256, μ: 742737, ~: 750458)\n[PASS] test_backlogThresholdStreamPauseAndNoPastCredit() (gas: 1742638)\n[PASS] test_failedOrReentrantWithdrawalRollsBack() (gas: 718463)\n[PASS] test_invalidIntakeAndClaims() (gas: 188271)\n[PASS] test_rewardsProRataAndEthIsNeverClaimed() (gas: 898336)\n[PASS] test_stakeForRestrictedAndEveryStakeResetsLock() (gas: 566570)\n[PASS] test_withdrawOnlySafeBoundedEvenWithForcedEthAndNoStake() (gas: 296995)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 97.95ms (99.85ms CPU time)\n\nRan 6 tests for test/unit/LaunchToken.t.sol:IMDOTokenTest\n[PASS] testFuzz_transferConservesSupply(address,uint256) (runs: 256, μ: 80092, ~: 80325)\n[PASS] test_metadata() (gas: 24431)\n[PASS] test_mintsFixedSupplyToDeployer() (gas: 23278)\n[PASS] test_noMintOrAdminEntryPoints() (gas: 143123)\n[PASS] test_transferMovesExactAmount() (gas: 74094)\n[PASS] test_transferRevertsOnInsufficientBalance() (gas: 34859)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 106.17ms (106.88ms CPU time)\n\nRan 11 tests for test/unit/DistributorAdversarial.t.sol:DistributorAdversarialTest\n[PASS] testFuzz_taxedRewardCreditsOnlyReceivedTokens(uint256,uint16) (runs: 1000, μ: 519258, ~: 522500)\n[PASS] testFuzz_twoEpochsFollowStakeOwnership(uint256,uint256,uint256,uint256) (runs: 1000, μ: 890856, ~: 898005)\n[PASS] test_allFixedExclusionsRejectStakeBeforeTokenInteraction() (gas: 418142)\n[PASS] test_claimCallbackCannotReenterAnyHolderOrRewardAction() (gas: 523169)\n[PASS] test_failedStakeRollsBackBacklogAndShares() (gas: 600805)\n[PASS] test_falseReturningIntakeCannotCreateCredit() (gas: 267523)\n[PASS] test_fullSupplyCanStakeAndExit() (gas: 601080)\n[PASS] test_noReturnTokenCanNotifyAndClaim() (gas: 478149)\n[PASS] test_notifyCallbackCannotReenterAnyHolderOrRewardAction() (gas: 370405)\n[PASS] test_oneWeiAndCompleteTransferTax() (gas: 543864)\n[PASS] test_pausedRewardCannotTrapUnstakedPrincipal() (gas: 633583)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 286.87ms (572.08ms CPU time)\n\nRan 9 tests for test/unit/ClaimAdversarial.t.sol:ClaimAdversarialTest\n[PASS] testFuzz_eachDailyTrancheIsPaidOnlyOnceAcrossWalletAndStake(uint16) (runs: 1000, μ: 3272145, ~: 3304165)\n[PASS] test_batchWithUnauthorizedLastSeatRollsBackEarlierEntitlement() (gas: 229421)\n[PASS] test_emptyBatchNeverChangesBalancesOrLocks() (gas: 56074)\n[PASS] test_excludedNftOwnerCanTakeWalletClaimAfterFailedStake() (gas: 352548)\n[PASS] test_holderRejectsSingleHashLeafAndReorderedProof() (gas: 86101)\n[PASS] test_operatorApprovalDoesNotGiveClaimRights() (gas: 101937)\n[PASS] test_overcommittedHolderTreeCannotSpendSeatReserve() (gas: 354193)\n[PASS] test_recordingNewOwnerWithoutNewTrancheDoesNotResetExistingStakeLock() (gas: 493375)\n[PASS] test_underfundedHolderPaymentRollsBackAndCanRetryAfterFunding() (gas: 281686)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 287.29ms (287.71ms CPU time)\n\nRan 4 tests for test/unit/TreasuryBoundaries.t.sol:TreasuryBoundariesTest\n[PASS] test_failedFlushRetainsTokensAndClearsApprovalOnSuccessfulRetry() (gas: 680942)\n[PASS] test_keeperDebtsAreIsolatedAndFailedPullPreservesBoth() (gas: 1540690)\n[PASS] test_loweringCapBelowUsedCapacityCannotResplitPendingFunds() (gas: 1505127)\n[PASS] test_oldPendingRegenSurvivesEpochRollAndDoesNotConsumeNewCap() (gas: 1483236)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 874.65ms (2.90ms CPU time)\n\nRan 3 tests for test/unit/ImdoDeploy.t.sol:ImdoDeployTest\n[PASS] test_manualSignerRunUsesArgumentsRegardlessOfCaller() (gas: 32656773)\n[PASS] test_scriptDeploysFundsSeedsBurnsLpAndStartsOwnershipTransfer() (gas: 32845340)\n[PASS] test_scriptRejectsMissingCodeWrongChainAndBadTickBeforeCreation() (gas: 19359033)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 888.96ms (95.48ms CPU time)\n\nRan 1 test for test/unit/Integration.t.sol:IntegrationTest\n[PASS] test_buyStakeProcessClaimAndWithdraw() (gas: 1555567)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 946.45ms (1.03ms CPU time)\n\nRan 5 tests for test/unit/Revision.t.sol:RevisionTest\n[PASS] testFuzz_exactOutputFeesAreGrossRate(uint32,uint128) (runs: 256, μ: 339743, ~: 339938)\n[PASS] test_emptySellCannotChargeFeeOrStartClock() (gas: 372929)\n[PASS] test_exactOutputAndInputBuySameGrossFeeAtLaunch() (gas: 372223)\n[PASS] test_firstFilledSwapStartsFullDecayAfterDeploymentDelay() (gas: 227125)\n[PASS] test_partialInputBuyRevertsAndReturnsEntireFee() (gas: 458441)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 1.01s (77.95ms CPU time)\n\nRan 1 test for test/fuzz/Treasury.fuzz.t.sol:TreasuryFuzzTest\n[PASS] testFuzz_capAndConservation(uint96) (runs: 256, μ: 524676, ~: 524685)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.02s (18.47ms CPU time)\n\nRan 12 tests for test/unit/ImdoTreasury.t.sol:ImdoTreasuryTest\n[PASS] testFuzz_splitConservesEveryWei(uint96) (runs: 256, μ: 816577, ~: 879977)\n[PASS] test_capOverflowAndEpochRoll() (gas: 1503255)\n[PASS] test_capSetterBoundsAuthAndNoRetroactiveResplit() (gas: 981747)\n[PASS] test_cooldownAndCallbackAuthorization() (gas: 897644)\n[PASS] test_dustAndFlush() (gas: 528446)\n[PASS] test_exactSplitBountyAndAuthorizedPulls() (gas: 671458)\n[PASS] test_failedRegenRetryDoesNotChargeAgainOrBlockBuy() (gas: 1012551)\n[PASS] test_failedRewardNotifyRollsBackBuyAndHalvesRetry() (gas: 1203028)\n[PASS] test_missingDependenciesDoNotSeedPermanentZeroCheckpoint() (gas: 6083833)\n[PASS] test_rejectedAndReentrantPullPaymentsPreserveOwed() (gas: 1149245)\n[PASS] test_rejectingKeeperCanPullToAlternateRecipient() (gas: 886966)\n[PASS] test_staleCheckpointRecoversWithoutRerouting() (gas: 4483731)\nSuite result: ok. 12 passed; 0 failed; 0 skipped; finished in 1.06s (48.05ms CPU time)\n\nRan 4 tests for test/unit/HookDeferredFees.t.sol:HookDeferredFeesTest\n[PASS] test_failedRedemptionPreservesClaimAndCannotRedirect() (gas: 488920)\n[PASS] test_firstBuyOnTokenOnlyManagerThenPermissionlessRedemption() (gas: 354136)\n[PASS] test_freshManagerExactOutputBuyAlsoDefersFee() (gas: 278888)\n[PASS] test_fundedManagerUsesOriginalDirectPayment() (gas: 195363)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.19s (1.26ms CPU time)\n\nRan 1 test for test/invariant/Treasury.invariant.t.sol:TreasuryInvariantTest\n[PASS] invariant_ethEqualsOwedPlusPendingAndUnsplit() (runs: 128, calls: 6144, reverts: 0)\n\n╭-----------------+----------+-------+---------+----------╮\n| Contract        | Selector | Calls | Reverts | Discards |\n+=========================================================+\n| TreasuryHandler | cap      | 1033  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | fund     | 1009  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | keeper   | 1031  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | pay      | 1027  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | process  | 1018  | 0       | 0        |\n|-----------------+----------+-------+---------+----------|\n| TreasuryHandler | withdraw | 1026  | 0       | 0        |\n╰-----------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.42s (464.41ms CPU time)\n\nRan 1 test for test/invariant/Staking.invariant.t.sol:StakingInvariantTest\n[PASS] invariant_principalAndRewardConservation() (runs: 128, calls: 8192, reverts: 0)\n\n╭----------------+----------+-------+---------+----------╮\n| Contract       | Selector | Calls | Reverts | Discards |\n+========================================================+\n| StakingHandler | advance  | 1343  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | claim    | 1380  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | notify   | 1296  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | stake    | 1365  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | unstake  | 1425  | 0       | 0        |\n|----------------+----------+-------+---------+----------|\n| StakingHandler | withdraw | 1383  | 0       | 0        |\n╰----------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.60s (1.59s CPU time)\n\nRan 22 tests for test/unit/ImdoHook.t.sol:ImdoHookTest\n[PASS] testFuzz_buyFeeMatchesSchedule(uint256,uint32) (runs: 256, μ: 331686, ~: 331921)\n[PASS] testFuzz_decayIsMonotoneAndBounded(uint32,uint32) (runs: 256, μ: 196675, ~: 196684)\n[PASS] test_addressCarriesExactlyTheRequiredFlags() (gas: 6313)\n[PASS] test_antiSnipeFeeAtLaunchIs20Percent() (gas: 186101)\n[PASS] test_buyExactInput_takes1_5PercentOfEthIn() (gas: 345952)\n[PASS] test_buyExactOutput_feeIsAddedOnTopOfEthIn() (gas: 347053)\n[PASS] test_callbacksRejectNonPoolManager() (gas: 90785)\n[PASS] test_constructorRejectsUnflaggedAddress() (gas: 12526)\n[PASS] test_decaySchedule() (gas: 223606)\n[PASS] test_feeCannotBeRaisedOrKept() (gas: 122078)\n[PASS] test_feeEventsAreEmitted() (gas: 334937)\n[PASS] test_initialState() (gas: 43201)\n[PASS] test_initializeOnlyOnce() (gas: 46337)\n[PASS] test_initializeRejectsWrongPair() (gas: 159603622)\n[PASS] test_initializeRequiresOwner() (gas: 159715886)\n[PASS] test_lowerFee() (gas: 240864)\n[PASS] test_lowerFeeDuringDecayKeepsDecaying() (gas: 220297)\n[PASS] test_lowerFeeToZeroDisablesFee() (gas: 358443)\n[PASS] test_onlyOwnerLowersFee() (gas: 35224)\n[PASS] test_sellExactInput_takes1_5PercentOfEthOut() (gas: 465838)\n[PASS] test_sellExactOutput_feeIsAddedOnTopOfEthOut() (gas: 467897)\n[PASS] test_twoStepOwnershipTransfer() (gas: 157199)\nSuite result: ok. 22 passed; 0 failed; 0 skipped; finished in 1.84s (1.69s CPU time)\n\nRan 4 tests for test/unit/HookAdversarial.t.sol:HookAdversarialTest\n[PASS] testFuzz_exactInputSellChargesGrossEthAtAnyDecayTime(uint256,uint32,uint16) (runs: 1000, μ: 386614, ~: 387161)\n[PASS] testFuzz_exactOutputSellDeliversRequestedNetAndGrossFee(uint256,uint32) (runs: 1000, μ: 352457, ~: 352640)\n[PASS] test_decayBoundariesAndNoFeeResurrection() (gas: 526585)\n[PASS] test_partialExactOutputSellRevertsFeeAndPoolMovement() (gas: 482068)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 1.84s (1.05s CPU time)\n\nRan 1 test for test/invariant/Hook.invariant.t.sol:HookInvariantTest\n[PASS] invariant_allChargedFeesRemainRedeemableOnlyToTreasury() (runs: 256, calls: 16384, reverts: 0)\n\n╭-------------+----------+-------+---------+----------╮\n| Contract    | Selector | Calls | Reverts | Discards |\n+=====================================================+\n| HookHandler | advance  | 3313  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | buy      | 3205  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | lower    | 3278  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | redeem   | 3317  | 0       | 0        |\n|-------------+----------+-------+---------+----------|\n| HookHandler | sell     | 3271  | 0       | 0        |\n╰-------------+----------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.20s (2.02s CPU time)\n\nRan 1 test for test/invariant/Claim.invariant.t.sol:ClaimInvariantTest\n[PASS] invariant_entitlementsAndCustodyMatchIndependentModel() (runs: 256, calls: 24576, reverts: 0)\n\n╭--------------+--------------+-------+---------+----------╮\n| Contract     | Selector     | Calls | Reverts | Discards |\n+==========================================================+\n| ClaimHandler | advance      | 3460  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| ClaimHandler | burn         | 3459  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| ClaimHandler | claimHolder  | 3507  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| ClaimHandler | claimSeat    | 3469  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| ClaimHandler | exit         | 3574  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| ClaimHandler | stakeAgain   | 3569  | 0       | 0        |\n|--------------+--------------+-------+---------+----------|\n| ClaimHandler | transferSeat | 3538  | 0       | 0        |\n╰--------------+--------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 8.41s (8.41s CPU time)\n\nRan 18 test suites in 8.41s (26.09s CPU time): 98 tests passed, 0 failed, 0 skipped (98 total tests)\n","passed":true},{"durationMs":125,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMDOToken.approve(address,uint256)\",\"IMDOToken.transfer(address,uint256)\",\"IMDOToken.transferFrom(address,address,uint256)\",\"ImdoClaim.burnUnclaimed()\",\"ImdoClaim.claimHolder(uint256,bytes32[],bool)\",\"ImdoClaim.claimSeat(uint256[],bool)\",\"ImdoHook.acceptOwnership()\",\"ImdoHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"ImdoHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"ImdoHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"ImdoHook.lowerFee(uint16)\",\"ImdoHook.redeemFees()\",\"ImdoHook.renounceOwnership()\",\"ImdoHook.transferOwnership(address)\",\"ImdoHook.unlockCallback(bytes)\",\"ImdoStaking.claim()\",\"ImdoStaking.claimReward(address)\",\"ImdoStaking.exit()\",\"ImdoStaking.notifyRegen()\",\"ImdoStaking.notifyReward(address,uint256)\",\"ImdoStaking.stake(uint256)\",\"ImdoStaking.stakeFor(address,uint256)\",\"ImdoStaking.unstake(uint256)\",\"ImdoStaking.withdrawRegen(uint256)\",\"ImdoTreasury.claimKeeper(address)\",\"ImdoTreasury.executeImd(uint256)\",\"ImdoTreasury.flushRewards()\",\"ImdoTreasury.payOffsets()\",\"ImdoTreasury.payOps()\",\"ImdoTreasury.process()\",\"ImdoTreasury.receive()\",\"ImdoTreasury.setRegenCap(uint256)\",\"ImdoTreasury.unlockCallback(bytes)\",\"LaunchToken.approve(address,uint256)\",\"LaunchToken.transfer(address,uint256)\",\"LaunchToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"LICENSE\":21,\"README.md\":90,\"SELF_AUDIT.md\":7,\"docs/DEPLOYMENT.md\":42,\"docs/REVIEW.md\":49,\"foundry.toml\":32,\"launch.json\":48,\"remappings.txt\":9,\"script/DeployImdo.s.sol\":168,\"script/utils/HookMiner.sol\":60,\"src/IMDOToken.sol\":6,\"src/ImdoClaim.sol\":142,\"src/ImdoHook.sol\":307,\"src/ImdoStaking.sol\":312,\"src/ImdoTreasury.sol\":365,\"src/LaunchToken.sol\":17,\"src/interfaces/IImdoStaking.sol\":7,\"test/REVISION.md\":7,\"test/TESTING.md\":48,\"test/fork/MainnetFork.t.sol\":56,\"test/fuzz/Treasury.fuzz.t.sol\":28,\"test/invariant/Claim.invariant.t.sol\":229,\"test/invariant/Hook.invariant.t.sol\":175,\"test/invariant/Staking.invariant.t.sol\":145,\"test/invariant/Treasury.invariant.t.sol\":102,\"test/unit/ClaimAdversarial.t.sol\":175,\"test/unit/DistributorAdversarial.t.sol\":284,\"test/unit/HookAdversarial.t.sol\":101,\"test/unit/HookDeferredFees.t.sol\":125,\"test/unit/ImdoClaim.t.sol\":163,\"test/unit/ImdoDeploy.t.sol\":129,\"test/unit/ImdoHook.t.sol\":277,\"test/unit/ImdoStaking.t.sol\":214,\"test/unit/ImdoTreasury.t.sol\":338,\"test/unit/Integration.t.sol\":43,\"test/unit/LaunchToken.t.sol\":64,\"test/unit/Revision.t.sol\":78,\"test/unit/TreasuryBoundaries.t.sol\":115,\"test/utils/ClaimHarness.sol\":76,\"test/utils/LocalV4.sol\":159,\"test/utils/Permit2Mock.sol\":23},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"f2e4c8ec27bda2cebc061bfdefe828b506da1e93546dd182e5171084fe1a55c4","verifiedTreeHash":"846fd077dab87ad10303465038a6b703dcce810a","verifierVersion":"0.1.0+94826a22"}]}