{"assessments":[],"deployments":[{"attestationHash":"50544ae35bb1652dc18ac362567ca0e2b39da9c26a5c83f036ba5d02ccf59099","chainId":4663,"contracts":[{"address":"0x71e712b90df0ad4dc67ce81677052996dca3ec09","blockNumber":87443371,"name":"IMD6900RobinhoodLaunch","role":"other","txHash":"0x4486068893dcaea0904b6533601ed8940952f3edc1165615a323dc9edab6ad63"}],"id":"f4be01e2-765e-48c2-9ea6-b51c9cae9d53","manifestHash":"0c4f38d98a74f407fd6c68093e5fcc2195b0ef447c87d468759ef9e7bd48886c","status":"live"}],"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":"7c57438f-1cad-4f00-9863-7a62d8accdee","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"1a02fa00131f183fd32e0ca857689a5ea67f217ccb6baece2314a609c8a4b144","dependsOn":["audit_imported_code"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","tools":[]},"key":"adapt_contract_project","kind":"code","role":"implement","skillHash":"2d7d84a1a9972f1497e5bc68a704cbf42294d08c62d2868f102ffe3b909b9b51","skillId":"adapt-contract-project","state":"accepted"},{"acceptedSubmissionHash":"17023cb877158aedd1855e9c1f173eb0e37a83354fdf8281df5d0ede7c264aaa","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"48355bd8aef7422798d17bbd921e3268a8bcf9692a7e4814c1f22fb804d2adaa","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"74157f60ff6f1b89711bc9e56d09974bce5299c9a778fd3ad3930537e5f9a739","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":"86c79ab52d2e4cb28664135ef6e421e7359c2fced784671133cd9c66b7b8eca0","dependsOn":["adapt_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"af3aa01159bbf354f621cafa5c0006f6169e0938a8b015be508b7377dbf165bc","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"c9a96a8b06e94ecfcfd2fccc93dda9ac123be2b2983645eb0df844c59a884e9b","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"15d92ebea65db0f5ef43e2fd63eb0110efcadb7365df7773b55a01e278c78228","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"25ba2f8b7a4fe4888c39ff54887fd9764547ce06cf18903101ca9642139bc89a","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"7205cd92613020fc8338f321d5d742a9b53c46c84bb50d8540f593b18de2af4c","dependsOn":["adapt_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"Deploy IMD6900RobinhoodLaunch (src/IMD6900RobinhoodLaunch.sol) on Robinhood Chain exactly as it is in the repository: one contract, four constructor arguments in this order: 0x35da9c0303507ddf708e87f2568eddf12c47a059 (owner: the team wallet), 0x8366a39CC670B4001A1121B8F6A443A643e40951 (Uniswap v4's PoolManager on Robinhood Chain), 0x0000198C940D8cD70Cb9ACeC5E3af8216ac57d2F (Robinhood's IMDSTR, the old token that claims the new coin 1:1), 0x16D3f65B708883DF042d98E1C7a49B32A33E2A14 (the Robinhood timelock: owns the hook and recovers ETH). Its constructor creates IMD6900LockedPool and nothing else (no salt mining: about 6.4M gas from any deployer address). After the deploy, createHook(salt) (anyone, once) creates IMD6900Hook with CREATE2 from the launch at a salt mined off-chain for its address (script/launch.sh hook: its address carries exactly the hook's permission flags and doesn't start with 0x91; the hook is owned by the timelock); the team calls it, the deploy does not need to. The pool's fee is static: 4.44% (POOL_FEE 44_400), the same for every trade, so Uniswap's router routes the pool on its own (no dynamic fee, no return deltas); it accrues to the locked position, whose fees anyone can collect. A v4 pool's fee can't change, so IMD6900LockedPool.lowerFee (the timelock only, lower only) moves the whole position in one unlock to a new pool at the lower fee and the same price. It calls nothing that existed before it, so it deploys the same on a fresh chain (test_DeploysOnAFreshChain); it fits one transaction (test_FitsOneTransaction, ~6.4M gas, the same from any deployer); no contract shows CALLCODE, DELEGATECALL or SELFDESTRUCT, creation code included (test_PassesTheAdmissionScan). Later, its owner launches the coin IMD6900 at a 0x6900… address mined for this contract's address (launch(): the coin with CREATE2, the pool opened with the whole 1B supply as coin-only liquidity, locked for good, and the opening buy, in one transaction), through Programmable's custom launch, which binds every component to the exact source: do not change the source, the compiler settings (solc 0.8.26, via-IR, 200 runs, cancun, bytecode_hash none), the constructor, the hook's permissions, the 4.44% static fee and lowerFee's lower-only move, the pool's lack of any liquidity removal (only lowerFee's move from one pool into the next), the coin's plain ERC20 (no owner, no mint, no tax) and launch()'s refusal of any coin address not starting with 0x6900, the fee split's defaults (90% the pot bridge, which feeds IMD6900's NFT floor pot on Ethereum; 10% $IMD bought for IMD's canonical Robinhood burn executor 0x4D6537ec564aFf82eABb1Ce5f355E817cBEA1e07, fixed in the code), the perps engine's fees (addPerpFees: all of it as ETH to the pot bridge), the split and every landing address being the timelock's alone to change (setPayees, setPerpFeeTo, setImdBurn, setImdPool), or the timelock's role (the hook's owner; it alone recovers ETH, releases the excess or sells the coins the launch holds back into the pool for the opening buy's ETH (sellHeld), ends the claims early (endClaims), repoints $IMD's pool and sets the split). test/RobinhoodFork.t.sol runs the whole launch on a Robinhood fork when ROBINHOOD_RPC_URL is set (claims, trades, buys through Uniswap's Universal Router, the fee split, the $IMD burn, a fee move to 3.33%) and skips otherwise. Everything builds offline (lib/ is vendored; v4-core's PoolManager is used by the tests only).","parentJobId":null,"planHash":"e285e1ea8a4c36a4a891aa7301e626ea6217bf8283bbc2b6bd5f134c8aa1dd58","previousHash":"b958bb1c18227b263b9c1558f4002368195d38744cb5d41cf7db74c4347594f4","projectId":"7c57438f-1cad-4f00-9863-7a62d8accdee","publication":{"commit":"5e5906ebc32bab9fe730ef872e0f81261d8f5f69","deliveredAt":"2026-10-13T10:30:43.817Z","repoUrl":"https://github.com/identity-md-launches/launch-1542-imd6900robinhoodlaunch"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[],"site":null,"snapshotHash":"e59071105814890753e1a646ed43ef122dbe79be944a5ed734885a2c753da873","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"797dadf3f0881394","findings":[{"citation":"resolved","description":"Trust gap (access x economics). burnImd is permissionless (\"Anyone\") and its only slippage protection is `minImd`, which the caller chooses; the attacker passes 0. The cap IMD_BUY_MAX (0.1 ETH, src/IMD6900RobinhoodLaunch.sol:64, documented as 'under what sandwiching it would be worth through two 1% fees') is applied per call, not per transaction or per block, and burnImd has no other rate limit. A searcher therefore front-runs with a large $IMD buy on the ETH/$IMD pool, calls burnImd(0) repeatedly in the same transaction until imdBucket is empty (each call spends 0.1 ETH at the inflated price), then sells the $IMD back. The per-call cap gives no protection because the attacker controls the number of calls, so the economic claim behind IMD_BUY_MAX does not hold. The 10% $IMD share of every fee (and 100% of it if the timelock lists only the executor) is thus extractable by any third party with capital; the burn executor receives a fraction of the $IMD it should. Protocol-destined value is paid to the wrong party. Suggested minimal fix that keeps the design: enforce the cap per block (e.g. `if (block.number == lastBurnBlock) revert; lastBurnBlock = block.number;`) so a sandwich can only wrap 0.1 ETH at a time, and/or let the timelock set a minimum $IMD-per-ETH price (or read a TWAP) that `minImd` cannot go below.","line":488,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: launched; $IMD pool ETH/$IMD 1% fee, tick spacing 100, full-range liquidity L=1e21 at ~261 $IMD/ETH (~62 ETH deep); imdBucket = 3 ETH after split() of 30 ETH of fees under the default 90/10 payees. Attack, one transaction from a contract: (1) swap 40 ETH -> $IMD on the $IMD pool; (2) call L.burnImd(0) 30 times (all 30 succeed, spending the whole 3 ETH bucket); (3) sell all $IMD back. Expected (the code's own comment): sandwiching burnImd is not worth it, attacker profit <= 0, the executor receives ~3*261*0.99 = ~775 $IMD. Actual: attacker ends with +1.2268 ETH net of both 1% fees, burn executor receives 280.10 $IMD for the 3 ETH spent (about 36% of fair). test/scratch/BurnImdSandwich.t.sol reproduces it: `forge test --match-path test/scratch/BurnImdSandwich.t.sol -vv` fails with 'a searcher must not profit from sandwiching the $IMD burn: 1226795087220999675 != 0'.","severity":"high","snippet":"        uint256 spend = imdBucket < IMD_BUY_MAX ? imdBucket : IMD_BUY_MAX;\n        if (spend == 0) revert NoEth();\n        imdBucket -= spend;\n        imd = abi.decode(poolManager.unlock(abi.encode(uint8(1), spend)), (uint256));\n        if (imd < minImd) revert Short(imd);","title":"burnImd: anyone picks minImd=0 and the IMD_BUY_MAX cap is per call, so a searcher batches calls in one transaction and sandwiches the whole $IMD bucket"},{"citation":"resolved","description":"Asymmetry between the two fee-landing paths. split() pays a payee with a low-level call and, on failure, books the share as owed (src/IMD6900RobinhoodLaunch.sol:538-545) so a paused or reverting pot bridge never blocks the pool's fee flow (the README calls this out: 'A payee that refuses its share (the bridge paused, say) keeps it as owed'). addPerpFees forwards the perps engine's fees to the same default landing address, POT_BRIDGE, but with SafeTransferLib.safeTransferETH, which reverts the whole call when the recipient refuses. Whatever in the perps engine calls flushFees (and anything that depends on it) then reverts until the timelock repoints perpFeeTo behind its delay. The engine is not in this repository, so whether a trade path is blocked cannot be confirmed here; the defect is the inconsistent failure handling between the two paths to the same recipient. Suggested fix: reuse the owed mechanism (`_pay(to, msg.value)`) or document that the engine must tolerate a reverting sink.","line":211,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: perpFeeTo refuses ETH (e.g. the timelock set it to a contract whose receive() reverts, or the pot bridge is paused). Call: any account calls addPerpFees{value: 1 ether}(). Expected (by symmetry with split()): the ETH is kept as owed for the recipient and the call succeeds. Actual: the call reverts (ETHTransferFailed), so the engine's flush reverts. test/scratch/LowLeads.t.sol::test_PerpFeesRevertWhenThePotRefuses shows the revert with a Refuser as perpFeeTo.","severity":"low","snippet":"    function addPerpFees() external payable {\n        address to = perpFeeTo;\n        SafeTransferLib.safeTransferETH(to, msg.value);\n        emit PerpFeesForwarded(to, msg.value);\n    }","title":"addPerpFees reverts outright when perpFeeTo refuses ETH, while split() tolerates a refusing payee (owed): a paused pot bridge makes every perps fee flush revert"},{"citation":"resolved","description":"Access-control asymmetry between the two Ownable contracts. IMD6900Hook overrides renounceOwnership to revert (src/IMD6900Hook.sol:127-129, 'Ownership never goes to nobody'). IMD6900RobinhoodLaunch inherits solady's renounceOwnership unchanged, so the team wallet can set the owner to address(0). Before launch(), that makes the launch impossible for good (launch, setClaims and withdrawEth are onlyOwner), and any ETH already sent for the opening buy can only leave through the timelock's recoverEth. After launch(), releaseImdstr is the only way the IMDSTR surrendered by claimants leaves the contract and releaseUnclaimed the only owner-side release of the unclaimed reserve; both become unreachable, so the IMDSTR is locked forever (the timelock's endClaims still covers the reserve). This is a self-inflicted owner action, so the severity is low, but the hook shows the authors' intent that these roles never go to nobody. Fix: override renounceOwnership in the launch to revert, as the hook does.","line":44,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"Sequence: owner calls L.renounceOwnership(). Expected (by the hook's rule): revert. Actual: owner() becomes address(0); a following owner call such as L.setClaims(0,0,0) reverts Unauthorized, and after a launch L.releaseImdstr(0, team) can never succeed. test/scratch/LowLeads.t.sol::test_LaunchOwnerCanRenounce shows the hook reverting NoRenounce while the launch renounces.","severity":"low","snippet":"contract IMD6900RobinhoodLaunch is Ownable, ReentrancyGuard {","title":"IMD6900RobinhoodLaunch does not block renounceOwnership (the hook does): a renounce strands the launch before it, and the collected IMDSTR after it"},{"citation":"resolved","description":"Trust gap (access x asymmetry), reported as a documented-but-unenforced guarantee rather than a permission bypass. MIN_CLAIM_WINDOW is documented as 'Claims stay open at least this long after the launch before the unclaimed reserve can be released' (src/IMD6900RobinhoodLaunch.sol:66-67), launch() enforces it against the owner's claimDeadline (line 284) and releaseUnclaimed() is gated on that deadline (line 311). endClaims() has no time gate at all: the timelock can call it in the block after launch(), which sets unclaimedReleased, stops every claim (claim() reverts AlreadyReleased) and moves the entire unclaimed reserve into excessHeld, from where sellHeld or releaseExcess (timelock only) dispose of it. The brief lists 'ends the claims early' as the timelock's role, so this is an intentional power; it is recorded here because the 90-day notice to holders is not something the code guarantees, only the timelock's public delay. If the guarantee is meant, add `if (block.timestamp < launchTime + MIN_CLAIM_WINDOW) revert ClaimsOpen();` to endClaims (recording the launch time in launch()).","line":382,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: launched at time T with claimTotal = 63,935,747e18 and claimDeadline = T + 100 days, no claims yet. At T + 1 second the timelock calls endClaims(). Expected (per MIN_CLAIM_WINDOW's notice): revert, claims open until at least T + 90 days. Actual: succeeds; claimReserve() == 0, excessHeld == claimTotal, holder A's claim(AA, AA, proof) reverts AlreadyReleased, and the timelock's sellHeld(0, 0, to) sells the whole reserve into the pool (test_EndingTheClaimsBringsMostOfTheBuyBack in the project's own suite performs this immediately after the launch).","severity":"low","snippet":"        unclaimedReleased = true;\n        claimDeadline = block.timestamp;\n        excessHeld += unclaimed;","title":"endClaims lets the timelock end the claims at any moment after the launch, bypassing the MIN_CLAIM_WINDOW guarantee that the owner-side release honours"}],"hash":"15d92ebea65db0f5ef43e2fd63eb0110efcadb7365df7773b55a01e278c78228","nodeId":"ca253bea-ed41-4599-97d2-ed6c8e28fd91","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds four findings and a 56-row coverage record. No tracked file was changed; the only writes are the findings file and two scratch tests.\n\n**Findings**\n\n1. **High: `burnImd` can be sandwiched for the whole $IMD bucket.** The function is permissionless, the caller sets `minImd` (so 0), and the 0.1 ETH cap is per call rather than per transaction or block. A searcher buys $IMD, calls `burnImd(0)` thirty times in the same transaction, and sells back. In the proof test the attacker nets 1.23 ETH from a 3 ETH bucket on a 62 ETH deep pool, and the burn executor receives about a third of the fair $IMD. The proof at `test/scratch/BurnImdSandwich.t.sol` fails on the current code and passes with a per-block cap.\n2. **Low: `addPerpFees` reverts when the landing address refuses ETH**, while `split` books a refused share as owed. A paused pot bridge would make every perps fee flush revert until the timelock repoints it. Confirmed with a refusing recipient.\n3. **Low: the launch contract allows `renounceOwnership`** while the hook blocks it. A renounce before launch strands the launch; after launch it locks the surrendered IMDSTR forever.\n4. **Low: `endClaims` has no time gate.** The timelock can end claims in the block after launch and move the whole reserve to `excessHeld`, which the 90-day notice in the code does not guarantee. The brief sanctions this power, so it is recorded as an unenforced guarantee and trust assumption.\n\n**Coverage**\n\nAll 56 verifier entry points have a row. 52 hold and 4 point to findings. Access guards traced as intended: owner-only launch and claims setup, timelock-only payees, landing addresses, fee lowering, ETH recovery and held-coin sale, pool-manager-only callbacks, and hook callbacks that admit only the locked pool. Trust assumptions documented rather than flagged: the timelock's `setImdPool` accepting any hooks address, `setPerpEngine` giving a timelock-chosen engine control mid-swap, and `recoverEth` reaching owed ETH.\n\n**Not verified here**: the real $IMD pool's depth on Robinhood and the perps engine's behaviour on a reverting fee sink, since neither is in the repository. The existing 44-test suite passes unchanged.","treeHash":null,"usage":{"cachedInputTokens":1861218,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":53725,"runtime":"claude","turns":35,"wallClockMs":833593}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"559cfaaab2c0d013","findings":[{"citation":"resolved","description":"The 10% $IMD share of every fee split accumulates in imdBucket and is spent by burnImd, which anyone may call with minImd = 0, at the pool's MIN_SQRT_PRICE limit (no slippage bound of its own). The only protection the code gives the buy is IMD_BUY_MAX (line 64: 'The most one {burnImd} spends: under what sandwiching it would be worth through two 1% fees'). That cap is per call, and nothing limits calls per transaction or per block, so the premise is void: an attacker needs no mempool (Robinhood Chain is a sequenced Orbit chain with none) because the 'victim' trades are its own calls. In one transaction it buys $IMD on the ETH/$IMD 1% pool, calls burnImd(0) until the bucket is empty (every call buys at the pushed-up price), and sells the $IMD back. The attacker keeps the difference; the burn executor (IMD's canonical burn, fixed in the code) receives a fraction of the $IMD the bucket should have bought. Against the live pool (sqrtPriceX96 0x107a0482598252c831e5d61b7b, ~269 $IMD/ETH, liquidity 0x508c449711d4021e3d, ~90 ETH full-range depth, read from PoolManager 0x8366a39CC670B4001A1121B8F6A443A643e40951 on 2026-10-13) the attack is profitable once the bucket passes about 1 ETH (about 225 ETH of pool volume at 4.44%); with a 3 ETH bucket and 100 ETH of attacker capital the attacker nets ~0.82 ETH and the burn gets ~180 instead of ~780 $IMD (-77%). Smaller buckets suffice if the live liquidity is concentrated rather than full range. The team's own `script/launch.sh pot` calls burnImd in a loop too, so a cap that only holds per call cannot be what keeps the burn honest. Minimal fix: make the cap hold per block (e.g. `if (block.number == lastBurnBlock) revert; lastBurnBlock = block.number;`), or restrict burnImd to the owner/timelock with a required minImd, or bound minImd by a price the attacker cannot set in the same transaction.","line":487,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: launched; fees split so that imdBucket = 3 ether (30 ETH of fees through addFees + split, i.e. ~675 ETH of pool volume); $IMD pool with the live depth above. Attacker contract with 100 ETH, one transaction: (1) swap 100 ETH -> $IMD on imdPoolKey(); (2) call L.burnImd(0) 30 times (each spends 0.1 ETH of the bucket at the inflated price); (3) swap all $IMD back to ETH. Expected (the code's stated guarantee): sandwiching the burn is not worth it, the executor receives what 3 ETH buys (~780 $IMD). Actual: attacker balance 100.819 ETH (+0.819 ETH), burn executor receives 180.38 $IMD. Run: forge test --match-path test/scratch/BurnImdSandwich.t.sol -vv","severity":"medium","snippet":"    function burnImd(uint256 minImd) external nonReentrant returns (uint256 imd) {\n        uint256 spend = imdBucket < IMD_BUY_MAX ? imdBucket : IMD_BUY_MAX;\n        if (spend == 0) revert NoEth();\n        imdBucket -= spend;\n        imd = abi.decode(poolManager.unlock(abi.encode(uint8(1), spend)), (uint256));\n        if (imd < minImd) revert Short(imd);","title":"burnImd's per-call cap does not bound a transaction: anyone self-sandwiches the whole $IMD burn bucket in one tx"},{"citation":"resolved","description":"burnImd subtracts `spend` (up to 0.1 ETH) from imdBucket before the swap, but unlockCallback only settles `ethIn = -d.amount0()`, which is what the $IMD pool actually took (an exact-input v4 swap stops when the pool runs out of liquidity in that direction and returns zero amounts for the rest). The unspent ETH stays in the contract's balance outside imdBucket, so the next split() treats it as fresh fees and pays 90% of it to the pot bridge; burnImd's event and the accounting invariant 'imdBucket is the 10% share' both break. Anyone can call burnImd(0) repeatedly whenever the $IMD pool has no $IMD-side liquidity at its price (its LPs out of range, or the pool drained after a large buy), draining the burn share into the pot 0.09 ETH per call with 0 $IMD bought. No external attacker profits; the $IMD burn is shortchanged in favour of the pot. Minimal fix: have unlockCallback return ethIn too and debit `imdBucket -= ethIn` (and revert when `out == 0`), or compute the debit from the balance change.","line":490,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: launched; imdBucket = 1 ether (10 ETH through addFees + split). imdPoolKey() initialized at sqrtPriceX96 0x107a0482598252c831e5d61b7b with its only liquidity in ticks [57000, 60000], above the price, so an ETH -> $IMD buy finds no $IMD. Call burnImd(0) (anyone). Expected: either revert (nothing bought) or imdBucket unchanged. Actual: returns 0 $IMD, imdBucket 1.0 -> 0.9 ETH, the contract's ETH balance unchanged at 1.0; the next split() sends 0.09 ETH to POT_BRIDGE and 0.01 back to the bucket. Ten calls plus a split move 0.9 ETH of the burn share to the pot. Verified with test/scratch/Leads.t.sol test_PartialFillMovesTheBurnShareToThePot (logs: '$IMD bought: 0', 'bucket after: 0.9', 'to the pot on the next split: 0.09').","severity":"low","snippet":"        imdBucket -= spend;\n        imd = abi.decode(poolManager.unlock(abi.encode(uint8(1), spend)), (uint256));","title":"burnImd debits the full spend from imdBucket even when the swap consumes less, moving the burn share to the pot"},{"citation":"resolved","description":"The split's payment path tolerates a payee that refuses ETH (the README names 'the bridge paused, say'): _pay records it as owed and retries later. addPerpFees, which the perps engine calls as its fee sink, forwards straight to perpFeeTo (the same POT_BRIDGE by default) with safeTransferETH and reverts if the transfer fails. While the bridge refuses ETH, every PerpEngine.flushFees that pays into this sink reverts; if the engine flushes as part of its open/close/liquidation flow those operations fail with it, and no fee reaches anyone until the timelock repoints perpFeeTo behind its 12h delay. Economic dependency failure (Economic Security: break dependencies), bounded by the timelock's ability to repoint. Minimal fix: on a failed transfer keep the ETH as owed[to] (reuse _pay) instead of reverting.","line":213,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: any; POT_BRIDGE (0xc39e650a24985C2bBA9FD83C81041B8f3E3f9EDd) has code whose receive() reverts (vm.etch a Refuser there in the test; live: the bridge paused). Call addPerpFees{value: 1 ether}() from any address (the engine). Expected: the fee is kept for the pot and forwarded later, as the split does for a refused share. Actual: the call reverts (and so does the engine's flushFees that made it). Verified with test/scratch/Leads.t.sol test_PerpFeesRevertWhenTheBridgeRefuses.","severity":"low","snippet":"        SafeTransferLib.safeTransferETH(to, msg.value);","title":"addPerpFees reverts when perpFeeTo refuses ETH, so a paused pot bridge halts the perps engine's fee flush"}],"hash":"17023cb877158aedd1855e9c1f173eb0e37a83354fdf8281df5d0ede7c264aaa","nodeId":"4d663c36-8fb6-4419-ad2c-1e2de35d429e","outcome":"completed","summary":"The review is complete. The findings file is at `.imd-findings.json` with three findings and a 60-row coverage record (all 57 listed entry points plus four invariants), and the proof test is under `test/scratch/`.\n\n**Findings**\n\n1. **Medium: burnImd's per-call cap does not bound a transaction, so anyone self-sandwiches the whole $IMD burn bucket.** The code's only defense for the burn buy is the 0.1 ETH cap, justified in a comment as \"under what sandwiching it would be worth through two 1% fees\". Nothing limits calls per transaction, and burnImd is public with minImd chosen by the caller, so the attacker needs no mempool: it buys $IMD, calls burnImd(0) in a loop until the bucket is empty, and sells back. Against the live $IMD pool's depth read from Robinhood's PoolManager today, a 3 ETH bucket nets the attacker 0.82 ETH and the burn executor receives 180 instead of 780 $IMD. The proof test in `test/scratch/BurnImdSandwich.t.sol` fails on the current code with those numbers.\n2. **Low: burnImd debits the full spend from imdBucket even when the swap consumes less.** When the $IMD pool has no $IMD-side liquidity at its price, the call buys nothing but still moves 0.1 ETH out of the bucket, and the next split pays 90% of it to the pot bridge. Reproduced in `test/scratch/Leads.t.sol`.\n3. **Low: addPerpFees reverts when the pot bridge refuses ETH.** The split tolerates a refusing payee via the owed path, but the perps fee sink forwards with a reverting transfer, so a paused bridge makes the engine's fee flush fail until the timelock repoints it. Reproduced in the same scratch file.\n\n**What held**\n\nI traced the launch, claims, split, owed, recovery, sellHeld and lowerFee flows against the Economic Security, Invariant and Flow Gap guides. The coin-balance and ETH-balance conservation invariants hold through every writer. The lowerFee move held in a 150-run fuzz across opening caps and trade sizes, including right after a collect leaves no fees to absorb rounding. Hook ownership, the perps sweep's economics and the Solady two-step ownership functions are outside my area and are marked holds with that reason.","treeHash":null,"usage":{"cachedInputTokens":2788521,"inputTokens":706,"model":"claude-fable-5-1","outputTokens":59779,"runtime":"claude","turns":38,"wallClockMs":1042863}},{"artifacts":[],"attempt":1,"bundleHash":"00be93d6a262d3dbfd60072e01f127d7fe795d5c37282d24dae81be5107bc236","device":"9506ecec995bdbf9","findings":[],"hash":"1a02fa00131f183fd32e0ca857689a5ea67f217ccb6baece2314a609c8a4b144","nodeId":"4f4637eb-8a36-4982-803a-8b68fc0c2465","outcome":"completed","summary":"Revision complete. Both reopening findings reproduced and are fixed, the reviewer's proof passes, and the advisory findings are each answered in `.imd-responses.json`.\n\n**What reproduced and what changed**\n\n- **burnImd sandwich (medium):** reproduced exactly as reported, with the searcher ending 1.23 ETH up and the burn executor getting 280 instead of 740 $IMD. Fix: one buy per minute (`IMD_BUY_GAP`, `lastImdBuy`, `TooSoon`), so a transaction or block wraps at most one 0.1 ETH buy, which the two 1% fees make a loss. The same attack now makes one call and loses 0.58 ETH. A time gap rather than a block gate because Robinhood Chain is Arbitrum-based, where block number semantics and sub-second shared timestamps make \"per block\" weak.\n- **Bucket debited by the request, not what the pool took (medium):** reproduced with the supplied proof. Fix: the buy callback returns what the pool took along with what it gave, the bucket is charged that, and a buy returning nothing reverts. The proof passes.\n- **addPerpFees reverting on a refusing sink (low):** fixed by routing through the existing owed mechanism, so a paused bridge never fails the engine's flush.\n- **Launch renounceOwnership (low):** now reverts, matching the hook.\n- **ETH stuck in the pool or hook (low):** the pool forwards its whole balance on collect or lowerFee, and the hook's payable Ownable entry points refuse value.\n- **sellHeld floor and endClaims time gate:** reproduced but left alone, answered as disputed. Both are the timelock's bound role per the brief, and the mitigations are operational. Reasoning is in ADAPTATION.md.\n\n**Verification**\n\n- `forge build` clean. `forge test`: 49 tests in the main suite pass, both reviewer proofs pass in scratch copies, the fork test skips offline.\n- The fork test was also run against the live Robinhood RPC with the revised source and passed.\n- The protected floor probe ran on chain id 4663 with the four real constructor arguments: constructor succeeds on an empty chain, init code 33,254 bytes, runtime 22,708 bytes, no forbidden opcodes.\n\nFiles changed: the three source files, the test suite (five new tests plus two stand-in contracts), README.md, ADAPTATION.md (sections 6 through 12 and the files table), and `.imd-responses.json` with all twelve findings answered.","treeHash":"aafed39f0bf206ab3913c05a9fced9534d6175da","usage":{"cachedInputTokens":3396573,"inputTokens":1026,"model":"claude-fable-5-1","outputTokens":48104,"runtime":"claude","turns":76,"wallClockMs":850935}},{"artifacts":[],"attempt":1,"bundleHash":"3e7daffb4ef52d97f8ded7be9b7e7a1c0e7ea6410a77b96d007614d1f8371537","device":"91d3f9816008ce9f","findings":[],"hash":"25ba2f8b7a4fe4888c39ff54887fd9764547ce06cf18903101ca9642139bc89a","nodeId":"b4d40d33-bfee-46a9-abf1-795612e82df9","outcome":"completed","summary":"The revision is complete. The one advisory finding reproduced and I fixed it.\n\n**What the finding said and what I found.** The reviewer reported that the first constructor argument in launch.json was the symbolic `$owner`, while the brief states the owner as a literal address. Reading the manifest confirmed it. The recipe for this launch mode says an owner the brief itself names is written as that static address, so the finding holds.\n\n**What I changed.** Only launch.json, in two places:\n\n- The first constructor argument is now the literal team wallet address 0x35da9c0303507ddf708e87f2568eddf12c47a059 instead of `$owner`.\n- The notes no longer claim that `$owner` resolves to the team wallet. They now say the first argument is the owner the brief states.\n\nThe other three arguments and their order are untouched and still match the constructor signature of owner, pool manager, IMDSTR and timelock.\n\n**Verification.** The manifest parses as JSON with exactly the keys kind, contracts and notes, one contract with four string arguments, and notes under the 4000 character limit. The build compiles and the full Foundry suite passes with 50 tests and no failures. The fork test skips as designed since no Robinhood RPC is set.\n\n**Response file.** I wrote `.imd-responses.json` with a verdict of fixed for finding 078a7f49 and the details above. That file is excluded from git by the harness's own exclude list, so it does not appear in the diff, which touches launch.json alone.","treeHash":"382fd6c5b144bb6ec7abd387edf13361b084747b","usage":{"cachedInputTokens":281320,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":3042,"runtime":"claude","turns":10,"wallClockMs":72903}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"dfa08759ca0424de","findings":[{"citation":"resolved","description":"IMD_BUY_MAX (line 64) is documented as the bound 'under what sandwiching it would be worth through two 1% fees'. The bound is applied to one call: burnImd is callable by anyone, nonReentrant only blocks nesting, nothing limits calls per transaction or per block, the swap's price limit is MIN_SQRT_PRICE + 1 and the only floor is the caller's own minImd, which the attacker passes as 0. A searcher therefore buys $IMD on the ETH/$IMD pool, calls burnImd(0) floor(imdBucket / 0.1 ETH) times in the same transaction (every call buys at the pushed-up price), and sells the $IMD back: the protected quantity is the whole bucket, the attacker pays the pool's two 1% fees once, and IMD's burn executor receives a fraction of the $IMD the bucket should have bought. No mempool is needed, the 'victim' trades are the attacker's own calls, so Robinhood Chain's private sequencing does not help. The bucket accumulates between calls (nobody is paid to call burnImd), so the profitable state (bucket above roughly 2% of the $IMD pool's ETH depth, about 1.2 ETH at the 62 ETH depth tested) is the normal operating state. Merged from audit_permissions (high), audit_economics (medium) and audit_math (medium): same root cause, one finding. Severity medium: the ETH is still spent on $IMD, but a third party extracts part of the 10% burn share whenever the bucket has accumulated. Minimal fix that keeps the design: enforce the cap per block (if (block.number == lastBurnBlock) revert; lastBurnBlock = block.number;) so one sandwich can wrap only 0.1 ETH, and/or bound minImd below by a reference the caller cannot set in the same transaction (a timelock-set floor price or the pool's TWAP).","line":488,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: launched; $IMD pool ETH/$IMD 1% fee, tick spacing 100, no hooks, full-range liquidity L = 1e21 at ~261 $IMD/ETH (about 62 ETH deep); 30 ETH sent through addFees() and split() under the default 90/10 payees, so imdBucket = 3 ETH. Honest baseline (30 burnImd(0) calls, nobody in between): the burn executor receives 739.68 $IMD. Attack, one transaction from a contract holding 100 ETH: (1) swap 40 ETH -> $IMD on imdPoolKey(); (2) call burnImd(0) until imdBucket == 0 (30 calls, all succeed); (3) sell all the $IMD back. Expected (the code's own comment): the two 1% fees make the sandwich unprofitable, searcher balance <= 100 ETH, executor gets ~740 $IMD. Actual: searcher balance 101.2268 ETH (+1.2268 ETH), the burn executor receives 280.10 $IMD (38% of the honest amount). test/scratch/BurnImdSandwich.t.sol fails on this tree with 'a searcher must not profit from sandwiching the $IMD burn: 101226795087220999675 > 100000000000000000000'.","severity":"medium","snippet":"        uint256 spend = imdBucket < IMD_BUY_MAX ? imdBucket : IMD_BUY_MAX;","title":"burnImd's 0.1 ETH cap holds per call, not per transaction: anyone sandwiches the whole $IMD bucket with minImd = 0 and keeps the difference"},{"citation":"resolved","description":"burnImd subtracts spend = min(imdBucket, 0.1 ETH) from imdBucket before the swap. unlockCallback (what == 1, lines 526-532) settles only ethIn = -d.amount0(), the ETH the $IMD pool actually consumed, and returns only out; the caller never learns ethIn. Uniswap v4 fills an exact-input swap only as far as there is liquidity before the price limit, so whenever the pool at imdPoolKey() has no $IMD-side liquidity at or below its price (LPs out of range or withdrawn, a repointed key whose pool is initialized but empty, or a concentrated range the 0.1 ETH buy runs past), ethIn < spend. The unspent remainder stays in the contract's balance but is no longer counted in imdBucket, so split() (amount = balance - imdBucket - owedTotal, line 454) treats it as fresh fees and pays 90% of it to POT_BRIDGE and 10% back to the bucket. burnImd(0) is callable by anyone and imd == 0 passes the minImd check, so in that state the whole $IMD share is walked out of the bucket in bucket/0.1 calls and the next split() routes 90% of it to the pot: the 10% share fixed in the code for IMD's canonical burn is paid to the wrong party. sellHeld's callback (what == 2) handles its own partial fill correctly (excessHeld -= sold), which shows the omission. Merged from audit_math (medium), audit_flow (medium), audit_economics (low) and write_foundry_tests (low). Fix: return ethIn from the callback and debit imdBucket by ethIn (or add spend - ethIn back), and/or revert when out == 0.","line":490,"path":"src/IMD6900RobinhoodLaunch.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IPoolManager} from \"@uniswap/v4-core/src/interfaces/IPoolManager.sol\";\nimport {PoolManager} from \"@uniswap/v4-core/src/PoolManager.sol\";\nimport {Hooks} from \"@uniswap/v4-core/src/libraries/Hooks.sol\";\nimport {PoolKey} from \"@uniswap/v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"@uniswap/v4-core/src/types/Currency.sol\";\nimport {BalanceDelta} from \"@uniswap/v4-core/src/types/BalanceDelta.sol\";\nimport {SwapParams} from \"@uniswap/v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"@uniswap/v4-core/src/libraries/TickMath.sol\";\nimport {FullMath} from \"@uniswap/v4-core/src/libraries/FullMath.sol\";\nimport {IMD6900Hook} from \"src/IMD6900Hook.sol\";\nimport {IMD6900LockedPool} from \"src/IMD6900LockedPool.sol\";\nimport {IMD6900RobinhoodLaunch} from \"src/IMD6900RobinhoodLaunch.sol\";\n\ninterface IERC20 {\n    function balanceOf(address) external view returns (uint256);\n    function transfer(address, uint256) external returns (bool);\n}\n\n/// A plain ERC20 stand-in (IMDSTR, and $IMD etched at its Robinhood address)\ncontract MockToken {\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n    function mint(address to, uint256 a) external { balanceOf[to] += a; totalSupply += a; }\n    function approve(address s, uint256 a) external returns (bool) { allowance[msg.sender][s] = a; return true; }\n    function transfer(address to, uint256 a) external returns (bool) { balanceOf[msg.sender] -= a; balanceOf[to] += a; return true; }\n    function transferFrom(address f, address to, uint256 a) external returns (bool) {\n        allowance[f][msg.sender] -= a; balanceOf[f] -= a; balanceOf[to] += a; return true;\n    }\n}\n\n/// A plain trader through the PoolManager\ncontract Trader {\n    IPoolManager immutable pm;\n    PoolKey key;\n    constructor(IPoolManager pm_) { pm = pm_; }\n    function swap(PoolKey calldata k, bool buy, int256 amountSpecified) external returns (BalanceDelta d) {\n        key = k;\n        d = abi.decode(pm.unlock(abi.encode(buy, amountSpecified)), (BalanceDelta));\n    }\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (bool buy, int256 amt) = abi.decode(data, (bool, int256));\n        BalanceDelta d = pm.swap(key, SwapParams(buy, amt, buy ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1), \"\");\n        _settle(key.currency0, d.amount0());\n        _settle(key.currency1, d.amount1());\n        return abi.encode(d);\n    }\n    function _settle(Currency c, int128 v) internal {\n        if (v < 0) {\n            if (c.isAddressZero()) pm.settle{value: uint128(-v)}();\n            else { pm.sync(c); IERC20(Currency.unwrap(c)).transfer(address(pm), uint128(-v)); pm.settle(); }\n        } else if (v > 0) pm.take(c, address(this), uint128(v));\n    }\n    receive() external payable {}\n}\n\n/// burnImd charges the $IMD bucket the requested `spend`, not the ETH the swap took. On an $IMD pool with no\n/// liquidity at (or below) its price the swap takes nothing, the 0.1 ETH stays in the launch, leaves the bucket,\n/// and the next split sends 90% of it to the pot bridge: the $IMD burn's share is paid to the wrong party.\ncontract BurnImdBucketTest is Test {\n    address constant OWNER = address(0xA11CE);\n    address constant TL = address(0x71E10C);\n    address constant POT = 0xc39e650a24985C2bBA9FD83C81041B8f3E3f9EDd;\n    uint256 constant SUPPLY = 1_000_000_000 ether;\n\n    IPoolManager PM;\n    IMD6900RobinhoodLaunch L;\n    IMD6900LockedPool pool;\n    Trader trader;\n\n    function setUp() public {\n        PM = IPoolManager(address(new PoolManager(address(this))));\n        MockToken imdstr = new MockToken();\n        L = new IMD6900RobinhoodLaunch(OWNER, PM, address(imdstr), TL);\n        L.createHook(_mineHook(L));\n        pool = L.pool();\n        trader = new Trader(PM);\n        vm.deal(address(trader), 1000 ether);\n        // $IMD and its pool, initialized but with no liquidity (LPs gone, or a repointed key)\n        vm.etch(L.IMD(), address(new MockToken()).code);\n        PM.initialize(L.imdPoolKey(), uint160(_sqrt(261 << 192)));\n        // the launch, then 150 ETH of buys: 6.66 ETH of fees, 0.666 ETH set aside for $IMD\n        bytes32 salt = _mineCoin(L);\n        address expected = L.tokenAddress(salt);\n        vm.deal(OWNER, 2.81 ether);\n        vm.prank(OWNER);\n        L.launch{value: 2.81 ether}(salt, expected, _sqrtPrice(19.18 ether), 0, address(0));\n        trader.swap(pool.poolKey(), true, -150 ether);\n        L.harvest();\n    }\n\n    function test_TheBucketIsChargedOnlyWhatTheBuySpent() public {\n        uint256 bucket = L.imdBucket();\n        uint256 balance = address(L).balance;\n        assertGt(bucket, 0.1 ether);\n        uint256 potBefore = POT.balance;\n        // anyone; a refusal (reverting when the pool returns nothing) is an acceptable fix too\n        try L.burnImd(0) returns (uint256 got) {\n            uint256 spent = balance - address(L).balance;\n            emit log_named_decimal_uint(\"imd bought\", got, 18);\n            emit log_named_decimal_uint(\"eth the swap took\", spent, 18);\n            assertEq(bucket - L.imdBucket(), spent, \"the bucket must be charged exactly what the swap took\");\n        } catch {}\n        L.split();\n        assertEq(POT.balance - potBefore, 0, \"none of the $IMD share may reach the pot bridge\");\n    }\n\n    function _mineHook(IMD6900RobinhoodLaunch l) internal view returns (bytes32 s) {\n        bytes32 initHash = l.hookInitCodeHash();\n        uint160 flags = l.FLAGS();\n        for (uint256 i;; ++i) {\n            address a = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(l), i, initHash)))));\n            if (uint160(a) & Hooks.ALL_HOOK_MASK == flags && uint160(a) >> 152 != 0x91) return bytes32(i);\n        }\n    }\n\n    function _mineCoin(IMD6900RobinhoodLaunch l) internal view returns (bytes32 s) {\n        bytes32 initHash = l.tokenInitCodeHash();\n        for (uint256 i;; ++i) {\n            address a = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(l), i, initHash)))));\n            if (uint160(a) >> 144 == 0x6900) return bytes32(i);\n        }\n    }\n\n    function _sqrtPrice(uint256 mc) internal pure returns (uint160) {\n        return uint160(_sqrt(FullMath.mulDiv(SUPPLY, 1 << 192, mc)));\n    }\n\n    function _sqrt(uint256 x) internal pure returns (uint256 y) {\n        if (x == 0) return 0;\n        y = x;\n        uint256 z = (x + 1) / 2;\n        while (z < y) (y, z) = (z, (x / z + z) / 2);\n    }\n}","reproduction":"State: launched (2.81 ETH opening buy at a 19.18 ETH market cap, no claims); $IMD etched as a plain ERC20 at 0x5F7B…7127; its pool (ETH/$IMD, fee 10_000, spacing 100, no hooks) initialized at ~261 $IMD/ETH with zero liquidity; a trader buys 150 ETH of IMD6900 and harvest() runs, so imdBucket = 0.6784764 ETH and address(L).balance == imdBucket (+1 wei of split dust). Call burnImd(0) from any address. Expected: revert (nothing bought) or imdBucket reduced by exactly the ETH the swap took (0). Actual: returns 0 $IMD, imdBucket falls by 0.1 ETH to 0.5784764 ETH, the contract's balance is unchanged; the next split() sends 0.09 ETH to POT_BRIDGE (0xc39e…9EDd) and 0.01 ETH back to the bucket. Six more burnImd(0) calls and a split() move 90% of the entire $IMD share to the pot. The attached test fails on this tree with 'the bucket must be charged exactly what the swap took: 100000000000000000 != 0'. With a thin concentrated range instead of an empty pool (write_foundry_tests' proof) the swap takes 0.000000464 ETH and the bucket still loses 0.1 ETH.","severity":"medium","snippet":"        imdBucket -= spend;","title":"burnImd debits imdBucket by the requested spend, not what the $IMD pool took: an unfilled or partly filled buy moves the burn's share into the 90/10 split"},{"citation":"resolved","description":"split() pays each payee with a low-level call and books a refused share as owed (lines 538-545), so a paused or reverting pot bridge never blocks the pool's fee flow (the README names 'the bridge paused' as the case). addPerpFees forwards the perps engine's fees to the same default landing address, POT_BRIDGE, with safeTransferETH, which reverts the whole call when the recipient refuses. The perps engine is not in this repository, so what in it depends on a successful flush cannot be confirmed here; the defect is the inconsistent failure handling between the two paths to the same recipient: while the bridge refuses ETH, every flush into this sink reverts until the timelock repoints perpFeeTo behind its delay. Merged from audit_permissions and audit_economics. Fix: reuse the owed mechanism (_pay(to, msg.value)) or document that the engine must tolerate a reverting sink.","line":213,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: the timelock has set perpFeeTo to a contract whose receive() reverts (vm.prank(TL); L.setPerpFeeTo(address(new Refuser()))), standing in for the pot bridge paused. Call addPerpFees{value: 1 ether}() from any address. Expected (by symmetry with split): the ETH is kept as owed for the recipient and the call succeeds. Actual: the call reverts with ETHTransferFailed. test/scratch/Lows.t.sol::test_PerpFeesRevertWhenTheSinkRefuses passes with vm.expectRevert on this tree.","severity":"low","snippet":"        SafeTransferLib.safeTransferETH(to, msg.value);","title":"addPerpFees reverts when perpFeeTo refuses ETH, while split() keeps a refused share as owed: a paused pot bridge makes every perps fee flush revert"},{"citation":"resolved","description":"IMD6900Hook overrides renounceOwnership to revert NoRenounce (src/IMD6900Hook.sol:127-129, 'Ownership never goes to nobody'). The launch does not, so the team wallet can set owner() to address(0). Before launch() that ends the launch for good (launch, setClaims and withdrawEth are onlyOwner; ETH already sent for the opening buy leaves only through the timelock's recoverEth). After launch(), releaseImdstr is the only way the IMDSTR surrendered by claimants leaves the contract and releaseUnclaimed the only owner-side release of the unclaimed reserve; both become unreachable, so the IMDSTR is locked forever (the timelock's endClaims still covers the reserve). Self-inflicted, so low. Fix: override renounceOwnership to revert, as the hook does.","line":44,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"Sequence: vm.prank(TL); hook.renounceOwnership() reverts NoRenounce. vm.prank(OWNER); L.renounceOwnership() succeeds and L.owner() == address(0); a following vm.prank(OWNER); L.setClaims(0, 0, 0) reverts Unauthorized, and after a launch L.releaseImdstr(0, team) can never succeed. test/scratch/Lows.t.sol::test_LaunchOwnerCanRenounceButHookCannot passes on this tree.","severity":"low","snippet":"contract IMD6900RobinhoodLaunch is Ownable, ReentrancyGuard {","title":"IMD6900RobinhoodLaunch inherits solady's renounceOwnership unchanged (the hook blocks it): one owner call makes launch() impossible before, and strands the collected IMDSTR after"},{"citation":"resolved","description":"IMD6900LockedPool needs receive() so PoolManager.take(currency0) can pay it, but receive() accepts ETH from anyone, and _payOut(eth, tokens) (lines 240-244) forwards only the eth the unlock returned, never address(this).balance. ETH sent to the pool by mistake (a user confusing the address shown as pool() with the launch's addFees/receive) can never leave: no function reads the pool's own ETH balance. IMD6900Hook likewise: solady's Ownable functions (transferOwnership, requestOwnershipHandover, cancelOwnershipHandover, completeOwnershipHandover) are payable and the hook has no way to move ETH out. Fix: forward address(this).balance in _payOut (the pool holds no ETH between unlocks), and make the hook's payable overrides revert on msg.value.","line":186,"path":"src/IMD6900LockedPool.sol","reproduction":"After a launch: address(pool).call{value: 1 ether}('') succeeds. pool.collect() then forwards only the fees the PoolManager paid in that call and address(pool).balance stays 1 ether; after vm.prank(TL); pool.lowerFee(33_300) it is still 1 ether. vm.prank(TL); hook.transferOwnership{value: 1 ether}(TL) succeeds and address(hook).balance == 1 ether; no entry point of IMD6900LockedPool, IMD6900Hook or IMD6900RobinhoodLaunch can move either. test/scratch/Lows.t.sol::test_EthSentToThePoolIsStuck passes on this tree.","severity":"low","snippet":"    receive() external payable {}","title":"ETH sent straight to IMD6900LockedPool, or with the hook's payable Ownable functions, is locked forever: collect and lowerFee forward only what the PoolManager just paid"},{"citation":"resolved","description":"sellHeld sells amount coins exact-input to MAX_SQRT_PRICE - 1 with minEth as the only floor. The timelock schedules the call behind a public delay, so amount, minEth and the execution window are known in advance; a minEth tight to the price at scheduling reverts after any ordinary move, so in practice it is loose (the project's own tests pass 0). The coin's pool charges 4.44% each way, so a sandwich pays about 8.9% on the searcher's own size, but the held excess is large against the pool's ETH side (about 58.9M coins on the test launch, against a pool holding the opening buy's 2.7 ETH), so the sale's own price impact exceeds that and the searcher who sells coins just before and buys back just after takes the difference out of the ETH returned to `to`. Low: the actor who sets minEth is trusted and can choose a tight floor at the cost of reverts. Fix: let the executor pass the floor at execution time (a quote with a maximum age) or derive it from the pool's TWAP.","line":398,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: claims set (63,935,747e18), launched with 2.81 ETH at a 19.18 ETH market cap and excessTo = address(L), so excessHeld = 58,411,743.87 coins; a searcher bought coins with 1 ETH earlier; the timelock's queued call is sellHeld(0, 0, R). Without a sandwich that call returns 1.425449964289975147 ETH to R. Sandwich: (1) the searcher sells all its coins into the pool; (2) the timelock's sellHeld(0, 0, R) executes and returns 1.317120039484028716 ETH to R (0.1083 ETH less); (3) the searcher buys exactly the same coins back. Searcher ETH: 9.0 before, 9.030036582295679085 after, a 0.030 ETH profit through the 4.44% fee both ways, with a 1 ETH position; a larger position takes more of the 0.108 ETH R lost. Expected: the sale's floor protects the ETH returned to R; actual: minEth = 0 accepts any amount. Run: test/scratch/SellHeld.t.sol::test_SellHeldSandwich logs these values on this tree.","severity":"low","snippet":"        if (eth < minEth) revert Short(eth);","title":"sellHeld's only slippage floor is minEth, fixed days ahead in the timelock's public queue, so the timelock's sale into the coin's pool can be sandwiched"},{"citation":"resolved","description":"The brief fixes the first constructor argument as the static address 0x35da9c0303507ddf708e87f2568eddf12c47a059 (owner: the team wallet). The recipe for this launch mode says an owner address the brief itself states is written as that static address, and $owner is used only when the owner is the paying wallet or the request's owner field. The manifest writes $owner and relies on the notes ('$owner resolves to the team wallet') to say what it should resolve to; notes are explanatory text, not deployment authority. If the request's owner field is empty or the paying wallet is not the team wallet, the launch, the claim list, withdrawEth and releaseImdstr belong to whoever paid, not the named owner. The deployer's service evidence cannot be checked from this tree, so this is reported as a manifest defect against the brief, not as a confirmed misdeployment. Fix: write the static address in constructorArgs[0].","line":7,"path":"launch.json","reproduction":"Read launch.json: contracts[0].constructorArgs == ['$owner', '0x8366a39CC670B4001A1121B8F6A443A643e40951', '0x0000198C940D8cD70Cb9ACeC5E3af8216ac57d2F', '0x16D3f65B708883DF042d98E1C7a49B32A33E2A14']. Expected per the brief: ['0x35da9c0303507ddf708e87f2568eddf12c47a059', …]. Actual: a symbolic $owner whose resolution depends on the request's owner field or the paying wallet, outside the manifest's control. The other three arguments and their order match the brief and the constructor (owner_, poolManager_, imdstr_, timelock_).","severity":"low","snippet":"        \"$owner\",","title":"launch.json writes $owner where the brief states the owner's static address"},{"citation":"resolved","description":"MIN_CLAIM_WINDOW (lines 66-67) says 'Claims stay open at least this long after the launch before the unclaimed reserve can be released'; launch() enforces it against the owner's claimDeadline (line 284) and releaseUnclaimed is gated on that deadline (line 311). endClaims has no time gate: it sets unclaimedReleased, every later claim() reverts AlreadyReleased, and the whole unclaimed reserve moves to excessHeld, from where sellHeld or releaseExcess dispose of it. The brief lists 'ends the claims early' as the timelock's intended role and the function's own comment relies on the timelock's public delay, so this is a trust assumption, recorded as info rather than a defect: the 90-day notice to IMDSTR holders is kept by the timelock's governance, not by the code. If the code is meant to guarantee it, record the launch time in launch() and revert endClaims before launchTime + MIN_CLAIM_WINDOW.","line":377,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: claims set (total 1,000,000e18, deadline T + 100 days), launched at T, no claims yet. At T + 1 the timelock calls endClaims(). Expected if MIN_CLAIM_WINDOW were enforced: revert ClaimsOpen until T + 90 days. Actual: succeeds; claimReserve() == 0, excessHeld == 1,000,000e18, any claim() now reverts AlreadyReleased. test/scratch/Lows.t.sol::test_EndClaimsRightAfterLaunch passes on this tree; the project's own test_EndingTheClaimsBringsMostOfTheBuyBack performs the same sequence immediately after the launch.","severity":"info","snippet":"    function endClaims() external {","title":"endClaims has no time gate: the timelock can end the claims the block after launch(), so MIN_CLAIM_WINDOW is a guarantee only against the owner's release, not the timelock's"}],"hash":"286a2a76b8a609ed4b91da03971f38a28a261925e7c925a0f01985685cd08980","nodeId":"66771256-0624-4ddc-8fb8-27f666dd7aaa","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds 8 findings and 59 coverage rows (all 56 entry points plus three invariants), with every snippet verified against the tree. No file outside `.imd-findings.json` and `test/scratch/` was touched.\n\n**What I kept, after reproducing each against the code**\n\n- **Medium, burnImd sandwich (merged from 3 specialists).** The 0.1 ETH cap holds per call only. My test sandwiches a 3 ETH bucket in one transaction: the searcher nets 1.23 ETH and the burn executor gets 280 instead of 740 $IMD. Proof attached, written so it passes once a per-block cap or price floor is added.\n- **Medium, burnImd debits the requested spend, not the fill (merged from 4 specialists).** On an empty or thin $IMD pool the bucket loses 0.1 ETH per call while the swap takes nothing, and the next split pays 90% of it to the pot bridge. The attached proof fails on this tree exactly as its finding states.\n- **Low, addPerpFees reverts when the sink refuses ETH** while split books it as owed (merged from 2 specialists).\n- **Low, the launch does not block renounceOwnership** though the hook does. One owner call strands the collected IMDSTR.\n- **Low, ETH sent straight to the pool or with the hook's payable Ownable functions is stuck.** Verified after collect, lowerFee and transferOwnership.\n- **Low, sellHeld's minEth is fixed in the public timelock queue.** I ran it rather than trusting the trace: the recipient receives 1.317 instead of 1.425 ETH and the searcher profits through the 4.44% fee both ways.\n- **Low, launch.json writes $owner** where the brief states the team wallet as a static address. The other three arguments match.\n- **Info, endClaims has no time gate.** Kept as a documented trust assumption since the brief lists it as the timelock's role.\n\n**What I checked myself and found sound:** the deployment model (constructor stores only, runtime 22,609 B, init code 32,701 B, admission scan passes), createHook's address checks, launch's vanity and CREATE2 binding, the claim Merkle path, split's accounting against underflow, lowerFee's one-unlock move, the hook's sender and range guards, and unlockCallback's caller restriction on both contracts. The project's own 77 tests pass.","treeHash":null,"usage":{"cachedInputTokens":1285840,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":43700,"runtime":"claude","turns":31,"wallClockMs":884268}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"3a720671678111bc","findings":[{"citation":"resolved","description":"burnImd() picks spend = min(imdBucket, 0.1 ether), subtracts the whole spend from imdBucket, then runs an exact-input swap on $IMD's pool with sqrtPriceLimitX96 = MIN_SQRT_PRICE + 1. unlockCallback() (lines 526-532) settles only what the swap actually consumed (ethIn = -d.amount0()) and returns only `out`; the caller never learns ethIn. When the $IMD pool's active liquidity runs out before 0.1 ETH is absorbed (a concentrated LP range the price leaves, LPs withdrawing, or the pool being repointed to a thin one), ethIn < spend: the contract keeps spend - ethIn in its balance but imdBucket no longer counts it. split() computes amount = balance - imdBucket - owedTotal, so the next split() / harvest() pays that remainder 90% to the pot bridge (or to whatever payees the timelock set) instead of to $IMD's burn. burnImd() is callable by anyone and each call debits another 0.1 ETH regardless of fill, so while the pool is thin the whole $IMD bucket can be walked into the other payees' share in a handful of calls. This breaks the fee split's stated 10% to $IMD's canonical burn (and the pot bridge receives ETH it is not owed). Fix: have unlockCallback return (out, ethIn) for what == 1 and debit imdBucket by ethIn (or refund spend - ethIn to the bucket after the swap).","line":490,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: launched; $IMD pool (ETH/$IMD, 1%, spacing 100, no hooks) initialized at ~261 $IMD/ETH with 1e18 liquidity only in the 200-tick band just below the price (test/scratch/BurnImdBucket.t.sol setUp). 1) trader buys 150 ETH of IMD6900, harvest(): imdBucket ~0.678 ETH, contract balance == imdBucket. 2) burnImd(0): the swap consumes 0.000629851210013456 ETH; imdBucket falls by 0.100000000000000000 ETH (expected: by 0.000629851210013456). 3) split(): 0.089433133910987890 ETH of the $IMD share is sent to POT_BRIDGE (expected: 0, nothing to split). Repeating burnImd(0) moves another 0.1 ETH out of the bucket each call.","severity":"medium","snippet":"        imdBucket -= spend;\n        imd = abi.decode(poolManager.unlock(abi.encode(uint8(1), spend)), (uint256));","title":"burnImd debits imdBucket by the full spend even when the $IMD pool only fills part of the buy; the unspent rest is re-split to the other payees"},{"citation":"resolved","description":"IMD6900LockedPool needs receive() so PoolManager.take(currency0) can pay it, but it is open to anyone, and _payOut(eth, tokens) (lines 240-244) forwards only the `eth` the unlock returned, never address(this).balance. Any ETH sent to the pool by mistake (a user confusing the pool contract with the launch's addFees/receive, a wallet sending to the address shown as `pool()`) can never leave: no function reads the pool's own ETH balance. The same holds for IMD6900Hook, whose inherited solady Ownable functions (transferOwnership, requestOwnershipHandover, cancelOwnershipHandover, completeOwnershipHandover, renounceOwnership) are payable and the hook has no way to move ETH out. Fix: forward address(this).balance in _payOut (the pool holds no ETH between unlocks), and/or make the hook's payable overrides revert on msg.value.","line":186,"path":"src/IMD6900LockedPool.sol","reproduction":"After any launch: vm.deal(alice, 1 ether); alice.call{value: 1 ether}(address(pool), \"\") succeeds (receive accepts it). pool.collect() then forwards only the fees the PoolManager paid in that call; address(pool).balance stays 1 ether after collect() and after lowerFee(); no entry point of IMD6900LockedPool, IMD6900RobinhoodLaunch or IMD6900Hook can move it. Expected: a way to recover it (or refusal of unsolicited ETH); actual: permanently locked.","severity":"low","snippet":"    receive() external payable {}","title":"ETH sent straight to IMD6900LockedPool (or with the hook's payable Ownable functions) is locked forever: collect/lowerFee only forward the amounts the PoolManager just paid"}],"hash":"48355bd8aef7422798d17bbd921e3268a8bcf9692a7e4814c1f22fb804d2adaa","nodeId":"7d3e5520-c49f-4107-bd66-f8b96c424565","outcome":"completed","summary":"The project's own 44 tests pass on the baseline, the exploratory probe is removed, and the findings file is in place with the proof test still under `test/scratch/`.\n\n## Review result\n\nArea covered: Execution Trace, Periphery and First Principles across all four contracts, the hook-mining step of the launch script, the claims leaf encoding and the solady/BaseHook surfaces. All 56 entry points have a coverage row. Two findings are recorded in `.imd-findings.json`.\n\n**Finding 1, medium, with a proof test.** `burnImd` at `src/IMD6900RobinhoodLaunch.sol:490` debits the $IMD bucket by the full spend before the swap, but the callback settles only what the pool actually consumed and never reports it back. When the $IMD pool's active liquidity runs out inside 0.1 ETH, the unspent remainder stays in the balance uncounted, and the next `split()` pays it to the other payees. Measured in the proof test:\n\n| step | value |\n|---|---|\n| ETH the swap took | 0.00063 ETH |\n| imdBucket debited | 0.1 ETH |\n| $IMD share sent to the pot bridge on the next split | 0.0894 ETH |\n\nThe function is public, so repeated calls walk the whole bucket across while the pool is thin. The proof `test/scratch/BurnImdBucket.t.sol` fails now and passes once the debit equals the consumed ETH.\n\n**Finding 2, low.** ETH sent directly to the locked pool's `receive()` at `src/IMD6900LockedPool.sol:186`, or with the hook's payable solady Ownable functions, is locked forever. The pool only forwards the fee amount the PoolManager just paid.\n\n**Leads traced and rejected.** `lowerFee` with zero accrued fees after a public `collect()` succeeds, so the rounding does not go negative. The rounding hazard near the top of the range, where the ETH principal would be a few wei, is unreachable while the fee is nonzero since sellers can never return enough principal to push the price back there. The static-analysis lines on arbitrary ETH sends, reentrancy and `encodePacked` collisions all have guards or fixed-width operands and are not findings. The hook's permission bits match the script's mined suffix `0x2ac0`.\n\n**Not reached.** The fork test against live Robinhood contracts and the real `$IMD` pool's liquidity shape, which decides how easily finding 1 triggers in production.","treeHash":null,"usage":{"cachedInputTokens":3013633,"inputTokens":802,"model":"claude-fable-5-1","outputTokens":62097,"runtime":"claude","turns":37,"wallClockMs":1124320}},{"artifacts":[],"attempt":1,"bundleHash":"58a10fc182c0d8e48f9334d3665dce2c0d3936556f42e8401d32d381d6ea5954","device":"fc0a1efa39657191","findings":[],"hash":"6a0bc1761a71cc23332005924990b1fdbef91184fe712d8e3e6edc8a6e530755","nodeId":"b4d40d33-bfee-46a9-abf1-795612e82df9","outcome":"completed","summary":"Created [launch.json](/home/imd10/.identitymd/work/7c57438f-1cad-4f00-9863-7a62d8accdee/b4d40d33-bfee-46a9-abf1-795612e82df9/launch.json) with one contract, `$owner`, and the three supplied addresses in constructor order.\n\nValidation passed: manifest structure, compiled ABI, offline `forge build`, all 44 application tests, and the live Robinhood fork test.\n\nOnly `launch.json` changes in the submission.","treeHash":"16cf0ae0f27c8fa657b6b7cbc7e474046f5e0630","usage":{"cachedInputTokens":403840,"inputTokens":38148,"model":"gpt-6-astra","outputTokens":4247,"runtime":"codex","turns":4,"wallClockMs":212909}},{"artifacts":[],"attempt":1,"bundleHash":"184fcaac08fd3f165ecdd2a3e9f6fa8a0f6a15fed1304964f37045c514fcbe78","device":"96ece23c63b791a1","findings":[{"description":"addPerpFees() is callable by anyone at any time, and since the latest revision a refused flush (the pot bridge reverting on receive) is booked as owed[perpFeeTo] / owedTotal and the ETH stays in the launch contract. launch() then takes `spend = address(this).balance`, which includes that owed ETH, and puts all of it into the opening buy, while owedTotal is left unchanged (withdrawEth() before the launch has the same gap: it sends the whole balance without touching owed/owedTotal). After the launch, split() computes `address(this).balance - imdBucket - owedTotal` unchecked for ordering, so it reverts with an arithmetic underflow (and harvest() with it, since it ends in split()) until the ETH fees held again reach owedTotal; from then on the first owedTotal of fees is reserved for the perp sink (paid out whole by payOwed or the next split that lists it) instead of being split to the payees and the $IMD bucket. The timelock can clear it afterwards with recoverEth(to, 1 wei), which clamps owedTotal to what is left, so nothing is lost for good; but until it acts, fee collection is stuck for anyone. Only a sender who gives up the ETH (it funds the opening buy) and a refusing pot bridge make it happen, hence low. A fix is to make launch() (and withdrawEth()) reconcile: either refuse to spend `owedTotal` (spend = balance - owedTotal) or reset owed/owedTotal when the balance is spent, and/or guard split() with `if (balance <= imdBucket + owedTotal) return 0`.","line":320,"path":"src/IMD6900RobinhoodLaunch.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IPoolManager} from \"@uniswap/v4-core/src/interfaces/IPoolManager.sol\";\nimport {PoolManager} from \"@uniswap/v4-core/src/PoolManager.sol\";\nimport {FullMath} from \"@uniswap/v4-core/src/libraries/FullMath.sol\";\nimport {IMD6900RobinhoodLaunch} from \"src/IMD6900RobinhoodLaunch.sol\";\nimport {IMD6900Hook} from \"src/IMD6900Hook.sol\";\n\ninterface IERC20 {\n    function balanceOf(address) external view returns (uint256);\n}\n\ncontract Refuser {\n    receive() external payable {\n        revert(\"no\");\n    }\n}\n\ncontract Imdstr {\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n    uint256 public totalSupply;\n\n    function mint(address to, uint256 a) external {\n        balanceOf[to] += a;\n        totalSupply += a;\n    }\n\n    function approve(address s, uint256 a) external returns (bool) {\n        allowance[msg.sender][s] = a;\n        return true;\n    }\n\n    function transfer(address to, uint256 a) external returns (bool) {\n        balanceOf[msg.sender] -= a;\n        balanceOf[to] += a;\n        return true;\n    }\n\n    function transferFrom(address f, address t, uint256 a) external returns (bool) {\n        allowance[f][msg.sender] -= a;\n        balanceOf[f] -= a;\n        balanceOf[t] += a;\n        return true;\n    }\n}\n\n/// A refused perps flush before launch() is kept as owed, but launch() spends every wei held (the owed included) in\n/// the opening buy and leaves owedTotal standing. Afterwards split() (and so harvest()) underflow until the fees\n/// held reach owedTotal again, and the first owedTotal of fees is reserved for the sink instead of being split.\ncontract OwedBeforeLaunchTest is Test {\n    address constant OWNER = address(0xA11CE);\n    address constant TL = address(0x71E10C);\n    address constant POT = 0xc39e650a24985C2bBA9FD83C81041B8f3E3f9EDd;\n    uint256 constant SUPPLY = 1_000_000_000 ether;\n\n    IPoolManager PM;\n    IMD6900RobinhoodLaunch L;\n\n    function setUp() public {\n        PM = IPoolManager(address(new PoolManager(address(this))));\n        Imdstr imdstr = new Imdstr();\n        L = new IMD6900RobinhoodLaunch(OWNER, PM, address(imdstr), TL);\n        L.createHook(_mineHook());\n    }\n\n    function test_OwedBeforeLaunchIsSpentByTheOpeningBuyAndBlocksTheSplit() public {\n        vm.etch(POT, address(new Refuser()).code);\n        vm.deal(address(this), 1 ether);\n        L.addPerpFees{value: 1 ether}();\n        assertEq(L.owedTotal(), 1 ether);\n        assertEq(address(L).balance, 1 ether);\n\n        bytes32 salt = _mineCoin();\n        address expected = L.tokenAddress(salt);\n        uint160 price = _sqrtPrice(19.18 ether);\n        vm.deal(OWNER, 2 ether);\n        vm.prank(OWNER);\n        L.launch{value: 2 ether}(salt, expected, price, 0, address(L));\n        // the whole 3 ether, the pot's 1 ether included, went into the opening buy\n        assertEq(L.launchEth(), 3 ether, \"the owed ETH was spent as the opening buy\");\n        assertEq(address(L).balance, 0);\n        assertEq(L.owedTotal(), 1 ether, \"but the debt still stands\");\n\n        // expected: the fees are split as usual. actual: split() underflows until the balance reaches owedTotal\n        vm.etch(POT, \"\");\n        vm.deal(address(this), 0.5 ether);\n        L.addFees{value: 0.5 ether}();\n        L.split();\n    }\n\n    function _sqrtPrice(uint256 mc) internal pure returns (uint160) {\n        return uint160(_sqrt(FullMath.mulDiv(SUPPLY, 1 << 192, mc)));\n    }\n\n    function _sqrt(uint256 x) internal pure returns (uint256 y) {\n        if (x == 0) return 0;\n        y = x;\n        uint256 z = (x + 1) / 2;\n        while (z < y) (y, z) = (z, (x / z + z) / 2);\n    }\n\n    function _mineHook() internal view returns (bytes32) {\n        bytes32 h = L.hookInitCodeHash();\n        uint160 flags = L.FLAGS();\n        for (uint256 i;; ++i) {\n            bytes32 salt = bytes32(i);\n            address a = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(L), salt, h)))));\n            if (uint160(a) & 0x3FFF == flags && uint160(a) >> 152 != 0x91) return salt;\n        }\n    }\n\n    function _mineCoin() internal view returns (bytes32) {\n        for (uint256 i;; ++i) {\n            bytes32 salt = bytes32(i);\n            if (uint160(L.tokenAddress(salt)) >> 144 == 0x6900) return salt;\n        }\n    }\n}","reproduction":"Fresh deploy, hook created, pot bridge 0xc39e650a24985C2bBA9FD83C81041B8f3E3f9EDd etched with a contract whose receive() reverts. 1) anyone: addPerpFees{value: 1 ether}() -> owed[POT] = 1 ether, owedTotal = 1 ether, balance 1 ether. 2) owner: launch{value: 2 ether}(salt, expected, price, 0, address(this)) -> launchEth = 3 ether (the owed ether went into the opening buy), balance 0, owedTotal still 1 ether. 3) pot bridge takes ETH again; anyone: addFees{value: 0.5 ether}(); split(). Expected: 0.5 ether split 90/10 (0.45 to the pot, 0.05 to the $IMD bucket). Actual: split() reverts with panic 0x11 (arithmetic underflow), and so does harvest(); they keep reverting until the balance reaches 1 ether, after which the next split pays the first 1 ether whole to the pot as 'owed' rather than splitting it.","severity":"low","title":"A perps-fee flush refused before launch() leaves owedTotal standing while launch() spends that ETH in the opening buy; split() and harvest() then underflow until the fees held reach owedTotal"}],"hash":"7205cd92613020fc8338f321d5d742a9b53c46c84bb50d8540f593b18de2af4c","nodeId":"7c45c3b5-d567-4925-a485-72b85533e5b7","outcome":"completed","summary":"The suite is green again and one defect is reported. Nothing else is pending.\n\n**What was wrong.** The implementer's latest source commit landed under my accepted tests and changed three behaviours they relied on. A refused perps-fee flush is now kept as `owed` instead of reverting. `burnImd` now enforces a one-minute gap and reverts with `Short(0)` when the buy returns nothing. Two tests and the invariant handler still assumed the old behaviour, so the tree already failed at runtime before I touched it.\n\n**What I changed, in test files only:**\n- `test/IMD6900Edges.t.sol`: replaced the stale revert test with two tests of the new bookkeeping. A refused flush is owed, adds up, is retried whole by `payOwed`, and stays out of the split until the pot takes ETH again.\n- `test/IMD6900Invariants.t.sol`: the handler now asserts a refused flush lands in `owed` and `owedTotal`. It expects `TooSoon` inside the buy gap. It accepts `Short` only for a dust bucket and checks nothing was spent. The liquidity invariant is now price-aware, since the coin-only position is legitimately out of range once a sale drains the pool's ETH. A partial `sellHeld` must leave the pool dry.\n\n**Verification.** The delivered suite passes with 83 tests. The invariant suite passed under ten different seeds with the failure cache cleared each time. A scratch copy at 400 runs of depth 120 ran 144,000 handler calls across three seeds with zero reverts.\n\n**Finding reported** in `.imd-findings.json`, severity low, with a self-contained proof that fails on the current code. A perps-fee flush refused before `launch()` leaves `owedTotal` standing while `launch()` spends that ETH in the opening buy. Afterwards `split()` and `harvest()` underflow until the fees held reach `owedTotal`, and the first `owedTotal` of fees goes whole to the pot instead of being split. The timelock can clear it with a one-wei `recoverEth`, so nothing is lost for good.\n\n**Still owed.** The Robinhood fork test skips here as designed; its live-state run needs an RPC the verifier does not have.","treeHash":"ece3712065592f430703fd48893219e75a31d76f","usage":{"cachedInputTokens":2100337,"inputTokens":802,"model":"claude-fable-5-1","outputTokens":35851,"runtime":"claude","turns":47,"wallClockMs":978799}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"bc1e515ca5c78ac4","findings":[{"citation":"resolved","description":"When a payee's ETH transfer fails, split() leaves that part in the contract's general balance and does not record it as owed to that payee. The next split() computes amount = address(this).balance - imdBucket (line 444) and divides it pro rata again, so each call moves 10% of the pot's refused 90% into imdBucket. split() is public, so anyone can call it repeatedly while the pot bridge (POT_BRIDGE 0xc39e650a…) refuses ETH, for example while it is paused or reverting on Across, and send nearly all of the pot's fees to the $IMD buy. This breaks the 90/10 split guarantee, and the ETH is paid to the wrong party. The timelock's recoverEth can only intervene after its delay.","line":455,"path":"src/IMD6900RobinhoodLaunch.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\nimport {Test, console} from \"forge-std/Test.sol\";\nimport {IPoolManager} from \"@uniswap/v4-core/src/interfaces/IPoolManager.sol\";\nimport {PoolManager} from \"@uniswap/v4-core/src/PoolManager.sol\";\nimport {Hooks} from \"@uniswap/v4-core/src/libraries/Hooks.sol\";\nimport {PoolKey} from \"@uniswap/v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"@uniswap/v4-core/src/types/Currency.sol\";\nimport {BalanceDelta} from \"@uniswap/v4-core/src/types/BalanceDelta.sol\";\nimport {SwapParams} from \"@uniswap/v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"@uniswap/v4-core/src/libraries/TickMath.sol\";\nimport {FullMath} from \"@uniswap/v4-core/src/libraries/FullMath.sol\";\nimport {IMD6900LockedPool} from \"src/IMD6900LockedPool.sol\";\nimport {IMD6900RobinhoodLaunch} from \"src/IMD6900RobinhoodLaunch.sol\";\n\ninterface IERC20 { function transfer(address, uint256) external returns (bool); }\n\ncontract Trader {\n    IPoolManager immutable pm; PoolKey key;\n    constructor(IPoolManager pm_) { pm = pm_; }\n    function swap(PoolKey calldata k, bool buy, int256 amt) external returns (BalanceDelta d) {\n        key = k; d = abi.decode(pm.unlock(abi.encode(buy, amt)), (BalanceDelta));\n    }\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (bool buy, int256 amt) = abi.decode(data, (bool, int256));\n        BalanceDelta d = pm.swap(key, SwapParams(buy, amt, buy ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1), \"\");\n        _s(key.currency0, d.amount0()); _s(key.currency1, d.amount1());\n        return abi.encode(d);\n    }\n    function _s(Currency c, int128 v) internal {\n        if (v < 0) {\n            if (c.isAddressZero()) pm.settle{value: uint128(-v)}();\n            else { pm.sync(c); IERC20(Currency.unwrap(c)).transfer(address(pm), uint128(-v)); pm.settle(); }\n        } else if (v > 0) pm.take(c, address(this), uint128(v));\n    }\n    receive() external payable {}\n}\n\ncontract SplitResplitTest is Test {\n    address constant OWNER = address(0xA11CE);\n    address constant TL = address(0x71E10C);\n    IPoolManager PM; IMD6900RobinhoodLaunch L; IMD6900LockedPool pool; Trader trader;\n\n    function _mine(uint160 mask, uint160 want, bytes32 h, bool hook) internal view returns (bytes32) {\n        for (uint256 i;; ++i) {\n            address a = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(L), i, h)))));\n            if (hook) { if (uint160(a) & mask == want && uint160(a) >> 152 != 0x91) return bytes32(i); }\n            else if (uint160(a) >> 144 == want) return bytes32(i);\n        }\n    }\n\n    function setUp() public {\n        PM = IPoolManager(address(new PoolManager(address(this))));\n        L = new IMD6900RobinhoodLaunch(OWNER, PM, address(0x1357), TL);\n        L.createHook(_mine(Hooks.ALL_HOOK_MASK, L.FLAGS(), L.hookInitCodeHash(), true));\n        pool = L.pool();\n        bytes32 salt = _mine(0, 0x6900, L.tokenInitCodeHash(), false);\n        vm.deal(OWNER, 2.81 ether);\n        uint160 sq = uint160(FixedSqrt.sqrt(FullMath.mulDiv(1e27, 1 << 192, 19.18 ether)));\n        address exp = L.tokenAddress(salt);\n        vm.prank(OWNER);\n        L.launch{value: 2.81 ether}(salt, exp, sq, 0, address(0));\n        trader = new Trader(PM);\n        vm.deal(address(trader), 1000 ether);\n    }\n\n    /// The pot bridge refuses ETH for a while; anyone then calls split() repeatedly. The pot's refused 90% must stay\n    /// the pot's, not be re-split until $IMD's 10% bucket has nearly all of it.\n    function test_RefusedPotShareIsNotResplitToImd() public {\n        vm.etch(L.POT_BRIDGE(), address(new Refuser()).code);\n        vm.deal(address(this), 1 ether);\n        (bool ok,) = address(L).call{value: 1 ether}(\"\");\n        require(ok);\n        for (uint256 i; i < 60; ++i) L.split();\n        assertLe(L.imdBucket(), 0.1 ether, \"the $IMD share is 10% of the fees, not the pot's refused 90%\");\n    }\n}\n\ncontract Refuser {\n    receive() external payable { revert(); }\n}\n\nlibrary FixedSqrt {\n    function sqrt(uint256 x) internal pure returns (uint256 y) {\n        if (x == 0) return 0; y = x; uint256 z = (x + 1) / 2;\n        while (z < y) { y = z; z = (x / z + z) / 2; }\n    }\n}","reproduction":"After launch() with the default payees (90% POT_BRIDGE, 10% IMD_BURN), make POT_BRIDGE revert on receive (vm.etch with a contract whose receive() reverts). Send 1 ETH to the launch contract and call split() 60 times from any address. Expected: imdBucket == 0.1 ETH and 0.9 ETH stays reserved for the pot. Actual: imdBucket == 0.998202989700085564 ETH. The pot's share has gone to the $IMD burn. Fix: keep an owed[payee] balance for refused shares, or exclude refused amounts from the next split, and pay them on retry.","severity":"medium","snippet":"            if (!ok) emit PayFailed(to[i], part);","title":"split() re-splits a payee's refused share, so anyone can move the NFT pot's ETH to the $IMD burn bucket"},{"citation":"resolved","description":"_beforeSwap calls perpEngine.sweepLiquidations on every swap that is not the engine's own and fails closed: if the call reverts or returns fewer than 32 bytes and sweepFailOpen is false, the swap reverts. setPerpEngine (line 107) accepts any address, with no code or interface check. A mistaken or malicious engine therefore pauses all buying and selling of IMD6900 in its only liquidity, the pool that holds the locked position. That freezes holders' exit. Nothing else gives the code a pause: this is a pause the launch's rules forbid, reachable through configuration after deployment. The same engine is also called inside the unlock on every trade, and its own swaps skip the sweep, so it can trade ahead of each user swap. Both are guarded only by the timelock's delay.","line":203,"path":"src/IMD6900Hook.sol","reproduction":"After launch(), as the timelock call hook.setPerpEngine(address(new BadEngine())), where BadEngine has a fallback() that reverts. Any swap through PoolManager on pool.poolKey() then reverts with SweepUnavailable, for example a Trader swapping 1 ETH for coins, or any holder selling. Verified in a local test (trader.swap(k, true, -1 ether) reverts). Expected per the coin's guarantees: holders can always trade the locked pool. Actual: trading is halted until the timelock sets sweepFailOpen or changes the engine, each behind its delay.","severity":"medium","snippet":"                if (!sweepFailOpen) revert SweepUnavailable();","title":"The hook's owner (the timelock) can halt every trade in the coin's only pool by setting a perps engine that reverts"},{"citation":"resolved","description":"setClaims stores root and total independently and never checks that the leaves sum to total. claim() checks only each account's own allowance. If the root's leaves sum to more than claimTotal (a snapshot or script error), claims beyond the reserve are paid from coins counted in excessHeld. claimReserve(), endClaims() and releaseUnclaimed() then compute claimTotal - claimedTotal, which underflows and reverts, so neither can ever run. sellHeld/releaseExcess can also fail for lack of balance.","line":433,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"Before launch, call setClaims(root over leaves {A: 40M, B: 20M}, total = 50M, deadline = now + 100 days), then launch with excessTo = address(this). A claims 40M and B claims 20M: both succeed, and claimedTotal = 60M > claimTotal = 50M. B's last 10M came from excessHeld's coins. After that, endClaims() (timelock) and releaseUnclaimed() (owner, after the deadline) revert with an arithmetic underflow, and claimReserve() reverts. Expected: claim reverts once claimedTotal would exceed claimTotal.","severity":"low","snippet":"        claimedTotal += amount;","title":"claim() does not bound claimedTotal by claimTotal: a claim list summing above claimTotal draws on the timelock's held coins and bricks endClaims/releaseUnclaimed"},{"citation":"resolved","description":"The pool cannot open until someone calls createHook(salt) with an off-chain mined salt. The claim list, the payees and the perps engine are also set after deployment (setClaims, setPayees, hook.setPerpEngine). The evm_contracts recipe asks for configuration in constructors. The brief explicitly approves this flow (\"the team calls it, the deploy does not need to\") and says the source is bound, so this is recorded for the adapter and admission as a known deviation, not a change to make. createHook is open to anyone, and every salt it accepts deploys the same hook with the same owner, so front-running it does no harm.","line":176,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"Deploy IMD6900RobinhoodLaunch(owner, PM, IMDSTR, TL) and call launch(...) as the owner before createHook: pool.open reverts with NoHook. The launch depends on a post-deploy createHook(salt) transaction.","severity":"info","snippet":"    function createHook(bytes32 salt) external returns (IMD6900Hook) {","title":"Configured after deployment: createHook and setClaims are post-deploy steps the contracts-only recipe forbids, but the approved brief requires them"},{"citation":"resolved","description":"Read in full: src/IMD6900RobinhoodLaunch.sol, src/IMD6900LockedPool.sol, src/IMD6900Hook.sol and src/IMD6900.sol, plus test/IMD6900RobinhoodLaunch.t.sol and test/HookSalt.sol. The local suite passes, 40 tests with the fork test skipped. Checked locally: lowerFee's rounding right after collect(), 256 fuzz runs with no revert; the opening range and buy; claims and Merkle leaves; the timelock gating; the unlock callbacks. The external contracts it depends on could not be reached and were not reviewed: POT_BRIDGE 0xc39e650a24985C2bBA9FD83C81041B8f3E3f9EDd, IMD_BURN 0x4D6537ec564aFf82eABb1Ce5f355E817cBEA1e07, $IMD 0x5F7Bb59365ce557C26dbcAa4EE9d39A4b95B7127 and its pool, IMDSTR 0x0000198C940D8cD70Cb9ACeC5E3af8216ac57d2F, the timelock 0x16D3f65B708883DF042d98E1C7a49B32A33E2A14, the future PerpEngine, and the Robinhood PoolManager deployment. test/RobinhoodFork.t.sol was not run: no ROBINHOOD_RPC_URL.","line":44,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"Not a defect: this entry records the review's scope.","severity":"info","snippet":"contract IMD6900RobinhoodLaunch is Ownable, ReentrancyGuard {","title":"Scope: contracts read, and those not reachable"}],"hash":"74157f60ff6f1b89711bc9e56d09974bce5299c9a778fd3ad3930537e5f9a739","nodeId":"70dbeead-bc28-48bc-bd68-84562f0c6d2a","outcome":"completed","summary":"I found two medium defects and one low one in the four contracts, and wrote them to `.imd-findings.json`. The file also has two info entries: one on the setup done after deployment, one on review scope. The repository's 40 tests pass (the Robinhood fork test skipped itself because no RPC URL is set). The scratch tests are deleted and no other files were changed.\n\n**Findings**\n\n1. **Medium: one payee's failed payment can be moved to the other payee** (`src/IMD6900RobinhoodLaunch.sol:455`). When a payee's ETH payment fails, `split()` leaves that share in the general balance. The next `split()` divides it 90/10 again, and anyone can call `split()`. So while the pot bridge is refusing ETH, repeated calls move the pot's share into the $IMD burn bucket. In a test, the bridge was made to always refuse ETH, 1 ETH was sent in and `split()` was called 60 times. The $IMD bucket ended at 0.998 ETH instead of 0.1 ETH. The finding includes a self-contained Foundry test that fails on the current code for this reason.\n\n2. **Medium: the timelock can stop all trading in the coin's only pool** (`src/IMD6900Hook.sol:203`). The hook calls the perps engine before every swap, and the swap reverts if that call fails. `setPerpEngine` accepts any address. If the engine is set to a contract that reverts, every buy and sell reverts until the timelock changes the engine or turns on fail-open, each behind its delay. I confirmed this locally. The launch rules treat pausing as forbidden, so I rated it medium. The same engine can also trade ahead of each user's swap, because its own swaps skip the check.\n\n3. **Low: claims can exceed `claimTotal`** (`src/IMD6900RobinhoodLaunch.sol:433`). Nothing checks that the claim list adds up to the total the owner sets. For example, allowances of 40M and 20M under a 50M total can both be claimed in full, which pays 10M out of the coins held for the timelock. After that, `endClaims`, `releaseUnclaimed` and `claimReserve` always revert. This needs the owner to submit a wrong list, hence low.\n\n4. **Info: setup after deployment.** The launch depends on `createHook`, and on setting the claims, payees and perps engine after deployment. The contracts-only launch rules ask for configuration in the constructor, but your brief approves this flow, so I recorded it as a known deviation, not a defect.\n\nI also checked whether `lowerFee` could fail on rounding when `collect()` runs just before it. It never failed in 256 randomized trade sizes, so I left it out.\n\n**Scope:** I read the four contracts in `src/` (`IMD6900RobinhoodLaunch.sol`, `IMD6900LockedPool.sol`, `IMD6900Hook.sol`, `IMD6900.sol`) in full, plus the local tests. These outside contracts were not reachable and weren't reviewed:\n- the pot bridge\n- IMD's burn executor\n- $IMD and its pool\n- IMDSTR\n- the timelock\n- the future perps engine\n- Uniswap v4's PoolManager on Robinhood Chain","treeHash":null,"usage":{"cachedInputTokens":984730,"inputTokens":30,"model":"claude-opus-5-5","outputTokens":24923,"runtime":"claude","turns":18,"wallClockMs":384499}},{"artifacts":[],"attempt":2,"bundleHash":"e69096c9f36cfa9ccc27f8984744239ed5441a71cd4144b1ca604ef3eabd6d26","device":"797dadf3f0881394","findings":[{"description":"burnImd() does `imdBucket -= spend` (spend = min(imdBucket, IMD_BUY_MAX)) and then runs an exact-input swap on $IMD's pool with the price limit MIN_SQRT_PRICE + 1. If the pool's liquidity cannot absorb the whole input (a concentrated position the price leaves, or an empty range), Uniswap v4 consumes only part of it: unlockCallback settles `ethIn = -d.amount0()`, which is less than `spend`, and the difference stays in the contract as ordinary balance. Nothing adds it back to imdBucket, so split() counts it as fresh fees (`balance - imdBucket - owedTotal`) and pays 90% of it to the pot bridge and 10% back to the bucket. The $IMD burn loses up to 0.1 ETH per burnImd call while the $IMD pool is thin; anyone can call burnImd, so the drain can be repeated once per call until the bucket is empty. The ETH goes to the configured payees, not to the caller, so this is accounting, not theft. The opening buy and sellHeld on the coin's own pool are not affected: that position spans the whole range below the opening price.","line":490,"path":"src/IMD6900RobinhoodLaunch.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IPoolManager} from \"@uniswap/v4-core/src/interfaces/IPoolManager.sol\";\nimport {PoolManager} from \"@uniswap/v4-core/src/PoolManager.sol\";\nimport {PoolKey} from \"@uniswap/v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"@uniswap/v4-core/src/types/Currency.sol\";\nimport {BalanceDelta} from \"@uniswap/v4-core/src/types/BalanceDelta.sol\";\nimport {ModifyLiquidityParams} from \"@uniswap/v4-core/src/types/PoolOperation.sol\";\nimport {Hooks} from \"@uniswap/v4-core/src/libraries/Hooks.sol\";\nimport {TickMath} from \"@uniswap/v4-core/src/libraries/TickMath.sol\";\nimport {FullMath} from \"@uniswap/v4-core/src/libraries/FullMath.sol\";\nimport {IMD6900RobinhoodLaunch} from \"src/IMD6900RobinhoodLaunch.sol\";\n\ninterface IERC20 {\n    function balanceOf(address) external view returns (uint256);\n    function transfer(address, uint256) external returns (bool);\n}\n\ncontract MockImd {\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n    function mint(address to, uint256 a) external { balanceOf[to] += a; totalSupply += a; }\n    function approve(address s, uint256 a) external returns (bool) { allowance[msg.sender][s] = a; return true; }\n    function transfer(address to, uint256 a) external returns (bool) { balanceOf[msg.sender] -= a; balanceOf[to] += a; return true; }\n    function transferFrom(address f, address to, uint256 a) external returns (bool) {\n        allowance[f][msg.sender] -= a; balanceOf[f] -= a; balanceOf[to] += a; return true;\n    }\n}\n\n/// Puts liquidity in a narrow range of a plain pool: a thin $IMD pool\ncontract NarrowAdder {\n    IPoolManager immutable pm; PoolKey key; uint256 liq; int24 lo; int24 hi;\n    constructor(IPoolManager pm_) { pm = pm_; }\n    function add(PoolKey calldata k, int24 lo_, int24 hi_, uint256 l) external { (key, liq, lo, hi) = (k, l, lo_, hi_); pm.unlock(\"\"); }\n    function unlockCallback(bytes calldata) external returns (bytes memory) {\n        (BalanceDelta d,) = pm.modifyLiquidity(key, ModifyLiquidityParams(lo, hi, int256(liq), 0), \"\");\n        if (d.amount0() < 0) pm.settle{value: uint128(-d.amount0())}();\n        if (d.amount1() < 0) { pm.sync(key.currency1); IERC20(Currency.unwrap(key.currency1)).transfer(address(pm), uint128(-d.amount1())); pm.settle(); }\n        return \"\";\n    }\n    receive() external payable {}\n}\n\n/// burnImd takes IMD_BUY_MAX out of the $IMD bucket before it knows how much the pool took; what the pool could not\n/// take stays in the launch as ordinary ETH and is split 90/10 by the next split, so it leaves the burn's share\ncontract BurnImdPartialFillProof is Test {\n    address constant OWNER = address(0xA11CE);\n    address constant TL = address(0x71E10C);\n    uint256 constant SUPPLY = 1_000_000_000 ether;\n\n    function _sqrt(uint256 x) internal pure returns (uint256 y) {\n        if (x == 0) return 0;\n        y = x;\n        uint256 z = (x + 1) / 2;\n        while (z < y) (y, z) = (z, (x / z + z) / 2);\n    }\n\n    function _hookSalt(IMD6900RobinhoodLaunch l) internal view returns (bytes32) {\n        bytes32 initHash = l.hookInitCodeHash();\n        for (uint256 i;; ++i) {\n            address a = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(l), i, initHash)))));\n            if (uint160(a) & Hooks.ALL_HOOK_MASK == l.FLAGS() && uint160(a) >> 152 != 0x91) return bytes32(i);\n        }\n    }\n\n    function _coinSalt(IMD6900RobinhoodLaunch l) internal view returns (bytes32) {\n        bytes32 initHash = l.tokenInitCodeHash();\n        for (uint256 i;; ++i) {\n            address a = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(l), i, initHash)))));\n            if (uint160(a) >> 144 == 0x6900) return bytes32(i);\n        }\n    }\n\n    function test_AnUnfilledImdBuyLeaksTheBucketIntoTheSplit() public {\n        IPoolManager PM = IPoolManager(address(new PoolManager(address(this))));\n        MockImd imdstr = new MockImd();\n        IMD6900RobinhoodLaunch L = new IMD6900RobinhoodLaunch(OWNER, PM, address(imdstr), TL);\n        L.createHook(_hookSalt(L));\n        // $IMD's pool, thin: liquidity only in one band around the price, so 0.1 ETH drains it\n        address imd = L.IMD();\n        vm.etch(imd, address(new MockImd()).code);\n        NarrowAdder adder = new NarrowAdder(PM);\n        vm.deal(address(adder), 10 ether);\n        MockImd(imd).mint(address(adder), 1e30);\n        PoolKey memory ik = L.imdPoolKey();\n        uint160 imdPrice = uint160(_sqrt(261 << 192)); // ~261 $IMD per ETH\n        PM.initialize(ik, imdPrice);\n        int24 cur = TickMath.getTickAtSqrtPrice(imdPrice);\n        int24 lo = (cur / 100) * 100 - 100;\n        adder.add(ik, lo, lo + 200, 1e15);\n        // the launch, without claims\n        vm.prank(OWNER);\n        L.setClaims(bytes32(0), 0, 0);\n        bytes32 salt = _coinSalt(L);\n        address expected = L.tokenAddress(salt);\n        vm.deal(OWNER, 2.81 ether);\n        vm.prank(OWNER);\n        L.launch{value: 2.81 ether}(salt, expected, uint160(_sqrt(FullMath.mulDiv(SUPPLY, 1 << 192, 19.18 ether))), 0, address(0));\n        // 1 ETH of fees: 0.1 set aside for $IMD's burn\n        vm.deal(address(this), 1 ether);\n        L.addFees{value: 1 ether}();\n        L.split();\n        assertEq(L.imdBucket(), 0.1 ether);\n        uint256 balBefore = address(L).balance;\n        L.burnImd(0);\n        uint256 spent = balBefore - address(L).balance;\n        emit log_named_decimal_uint(\"ETH the $IMD pool took\", spent, 18);\n        assertLt(spent, 0.1 ether, \"the thin pool took less than the call's 0.1 ETH\");\n        // expected: the unspent part is still the burn's (imdBucket == 0.1 ether - spent), and no split can move it\n        // actual: imdBucket is 0; the unspent ETH is ordinary balance and the next split sends 90% of it to the pot\n        assertEq(L.imdBucket(), 0.1 ether - spent, \"what the pool could not take should stay set aside for the burn\");\n        assertEq(L.split(), 0, \"nothing of the burn's share should be splittable\");\n    }\n}","reproduction":"Deploy the launch with a PoolManager; initialize the ETH/$IMD pool (1%, spacing 100, no hooks) at ~261 $IMD per ETH with liquidity 1e15 only in the band [tick-100, tick+100]; launch the coin with 2.81 ETH and no claims; send 1 ETH to addFees() and split() (imdBucket = 0.1 ETH); call burnImd(0). Expected: the pool takes what it can (here 0.000000464 ETH) and imdBucket == 0.1 ether - taken, with split() returning 0. Actual: imdBucket == 0, the launch's balance still holds 0.0999995 ETH of the burn's share, and the next split() pays 0.09 ETH of it to the pot bridge. Fix: after the swap, `imdBucket += spend - ethIn` (return ethIn from the callback), or swap with the unspent part refunded to the bucket.","severity":"low","title":"burnImd debits the $IMD bucket by the full spend before the swap, so an unfilled buy leaks the burn's ETH into the 90/10 split"}],"hash":"80fcca689c2003d9c80c50fd38503dd58ae13c25b062ce06c2fa7701f4f01b30","nodeId":"7c45c3b5-d567-4925-a485-72b85533e5b7","outcome":"completed","summary":"The findings file is excluded from git by the harness itself, so it stays in the working tree as intended. The two test files are committed and the tree is clean. The work is done.\n\n**Outcome.** The suite now has 77 tests across four files, all passing, including 9 invariants checked over 4,800 random handler calls with zero reverts. The pre-existing 44 unit tests and the fork test are untouched. One low-severity defect is reported in `.imd-findings.json` with a self-contained failing proof.\n\n**What I added.**\n\n- `test/IMD6900Invariants.t.sol`: a handler drives the launched system from every role with bounded inputs. Traders buy and sell through the PoolManager, snapshot holders claim, the timelock lowers the fee, ends claims, sells or releases held coins, recovers ETH and resets payees, the owner releases the unclaimed rest, anyone harvests, splits, retries a refused share and buys $IMD, and the pot bridge goes down and up. The invariants: coins held equal the claims' reserve plus the timelock's; ETH held covers the bucket plus the owed; the pool contract holds nothing; all liquidity is the one locked position and every pool left behind is empty; the fee only falls and the hook serves the newest pool; supply never rises; claims are bounded and one-for-one; ended claims stay ended; the launch is final. It runs with fail-on-revert so a handler call that unexpectedly reverts is a failure, not noise.\n- `test/IMD6900Edges.t.sol`: 31 tests on the paths the happy suite skipped. Every guard's failure path before and after the launch, zero and one-wei claims, wrong proofs, claims without IMDSTR approval, releaseUnclaimed twice, payee lists that are empty, nine long, mismatched, zero-addressed or off by one, callbacks from strangers to both contracts and to the hook, a stranger initializing a pool on the hook, a sweep that reverts, reports too-large or gas-starved, ownership handover. Fuzzed: payee sums, the exact 90/10 split with rounding dust, recoverEth's bookkeeping with a refusing pot, lowerFee after arbitrary trades at any fee, launches from 0.01 ETH to 10M ETH market cap, sellHeld never touching the reserve.\n\n**The finding (low).** burnImd subtracts the full 0.1 ETH from the $IMD bucket before the swap. When $IMD's pool cannot absorb the whole input, the unspent ETH stays as plain balance and the next split sends 90% of it to the pot bridge. Reproduced with a thin pool: it took under a millionth of an ETH, the bucket went to zero, and 0.0999995 ETH became splittable. The fix is to credit the unspent part back to the bucket.\n\n**Checked and ruled out.** I suspected lowerFee could be blocked by parking the price just under the opening tick, where the rounding in the liquidity move might fail. With any nonzero fee the market cannot get within half a percent of that tick, since the fee comes out of every sell's input, and lowerFee succeeded at every distance tried. Not a finding.\n\n**Still owed.** The fork test against live Robinhood state only runs with ROBINHOOD_RPC_URL set, which this machine does not have. The real $IMD pool's liquidity shape, which decides whether the reported defect bites in practice, is unverified offline.","treeHash":"4e2dfb3dcf733ab14c204eca931c2f6cf1845938","usage":{"cachedInputTokens":3828026,"inputTokens":962,"model":"claude-fable-5-1","outputTokens":69893,"runtime":"claude","turns":38,"wallClockMs":1365507}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"f47f953f8e35921e","findings":[{"citation":"resolved","description":"The revision routes addPerpFees through _pay, so a flush the pot bridge refuses is kept as owed[perpFeeTo] and summed in owedTotal (lines 573-580). addPerpFees is callable by anyone at any time, including before launch(). launch() then takes spend = address(this).balance (line 320), the owed ETH included, and puts all of it into the opening buy; withdrawEth (line 291) likewise sends the whole balance. Neither touches owed or owedTotal, so after the ETH is gone the debt still stands. After the launch, split() computes amount = address(this).balance - imdBucket - owedTotal (line 481) with checked arithmetic, so it reverts with panic 0x11 whenever the fees held are below owedTotal, and harvest() reverts with it (it ends in split()). Fee collection is stuck for everyone until either the balance reaches owedTotal, at which point the next split() with any free amount pays that whole tranche to the sink as owed rather than 90% to the pot and 10% to the $IMD bucket, or the timelock clears it with recoverEth (which clamps owedTotal) behind its delay. Nothing is lost for good, and it needs a refusing pot bridge plus a pre-launch flush (the perps engine does not exist before the launch, so this is a stray or hostile call), hence low. Reported by write_foundry_tests (6a7b7f96); reproduced with its proof, and the withdrawEth variant reproduced locally. Fix: have launch() and withdrawEth() reconcile the owed ETH (spend = balance - owedTotal, or reset owed/owedTotal when the balance is spent), and/or make split() return 0 when balance <= imdBucket + owedTotal.","line":320,"path":"src/IMD6900RobinhoodLaunch.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IPoolManager} from \"@uniswap/v4-core/src/interfaces/IPoolManager.sol\";\nimport {PoolManager} from \"@uniswap/v4-core/src/PoolManager.sol\";\nimport {FullMath} from \"@uniswap/v4-core/src/libraries/FullMath.sol\";\nimport {IMD6900RobinhoodLaunch} from \"src/IMD6900RobinhoodLaunch.sol\";\nimport {IMD6900Hook} from \"src/IMD6900Hook.sol\";\n\ninterface IERC20 {\n    function balanceOf(address) external view returns (uint256);\n}\n\ncontract Refuser {\n    receive() external payable {\n        revert(\"no\");\n    }\n}\n\ncontract Imdstr {\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n    uint256 public totalSupply;\n\n    function mint(address to, uint256 a) external {\n        balanceOf[to] += a;\n        totalSupply += a;\n    }\n\n    function approve(address s, uint256 a) external returns (bool) {\n        allowance[msg.sender][s] = a;\n        return true;\n    }\n\n    function transfer(address to, uint256 a) external returns (bool) {\n        balanceOf[msg.sender] -= a;\n        balanceOf[to] += a;\n        return true;\n    }\n\n    function transferFrom(address f, address t, uint256 a) external returns (bool) {\n        allowance[f][msg.sender] -= a;\n        balanceOf[f] -= a;\n        balanceOf[t] += a;\n        return true;\n    }\n}\n\n/// A refused perps flush before launch() is kept as owed, but launch() spends every wei held (the owed included) in\n/// the opening buy and leaves owedTotal standing. Afterwards split() (and so harvest()) underflow until the fees\n/// held reach owedTotal again, and the first owedTotal of fees is reserved for the sink instead of being split.\ncontract OwedBeforeLaunchTest is Test {\n    address constant OWNER = address(0xA11CE);\n    address constant TL = address(0x71E10C);\n    address constant POT = 0xc39e650a24985C2bBA9FD83C81041B8f3E3f9EDd;\n    uint256 constant SUPPLY = 1_000_000_000 ether;\n\n    IPoolManager PM;\n    IMD6900RobinhoodLaunch L;\n\n    function setUp() public {\n        PM = IPoolManager(address(new PoolManager(address(this))));\n        Imdstr imdstr = new Imdstr();\n        L = new IMD6900RobinhoodLaunch(OWNER, PM, address(imdstr), TL);\n        L.createHook(_mineHook());\n    }\n\n    function test_OwedBeforeLaunchIsSpentByTheOpeningBuyAndBlocksTheSplit() public {\n        vm.etch(POT, address(new Refuser()).code);\n        vm.deal(address(this), 1 ether);\n        L.addPerpFees{value: 1 ether}();\n        assertEq(L.owedTotal(), 1 ether);\n        assertEq(address(L).balance, 1 ether);\n\n        bytes32 salt = _mineCoin();\n        address expected = L.tokenAddress(salt);\n        uint160 price = _sqrtPrice(19.18 ether);\n        vm.deal(OWNER, 2 ether);\n        vm.prank(OWNER);\n        L.launch{value: 2 ether}(salt, expected, price, 0, address(L));\n        // the whole 3 ether, the pot's 1 ether included, went into the opening buy\n        assertEq(L.launchEth(), 3 ether, \"the owed ETH was spent as the opening buy\");\n        assertEq(address(L).balance, 0);\n        assertEq(L.owedTotal(), 1 ether, \"but the debt still stands\");\n\n        // expected: the fees are split as usual. actual: split() underflows until the balance reaches owedTotal\n        vm.etch(POT, \"\");\n        vm.deal(address(this), 0.5 ether);\n        L.addFees{value: 0.5 ether}();\n        L.split();\n    }\n\n    function _sqrtPrice(uint256 mc) internal pure returns (uint160) {\n        return uint160(_sqrt(FullMath.mulDiv(SUPPLY, 1 << 192, mc)));\n    }\n\n    function _sqrt(uint256 x) internal pure returns (uint256 y) {\n        if (x == 0) return 0;\n        y = x;\n        uint256 z = (x + 1) / 2;\n        while (z < y) (y, z) = (z, (x / z + z) / 2);\n    }\n\n    function _mineHook() internal view returns (bytes32) {\n        bytes32 h = L.hookInitCodeHash();\n        uint160 flags = L.FLAGS();\n        for (uint256 i;; ++i) {\n            bytes32 salt = bytes32(i);\n            address a = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(L), salt, h)))));\n            if (uint160(a) & 0x3FFF == flags && uint160(a) >> 152 != 0x91) return salt;\n        }\n    }\n\n    function _mineCoin() internal view returns (bytes32) {\n        for (uint256 i;; ++i) {\n            bytes32 salt = bytes32(i);\n            if (uint160(L.tokenAddress(salt)) >> 144 == 0x6900) return salt;\n        }\n    }\n}","reproduction":"Fresh deploy, hook created, POT_BRIDGE 0xc39e650a24985C2bBA9FD83C81041B8f3E3f9EDd etched with a contract whose receive() reverts. (1) anyone: addPerpFees{value: 1 ether}() -> owed[POT] = 1 ether, owedTotal = 1 ether, balance 1 ether. (2) owner: launch{value: 2 ether}(salt, expected, price, 0, address(L)) -> launchEth == 3 ether (the owed ether funded the opening buy), balance 0, owedTotal still 1 ether. (3) the pot bridge takes ETH again; anyone: addFees{value: 0.5 ether}(); split(). Expected: 0.5 ether split 90/10 (0.45 to the pot, 0.05 to the $IMD bucket). Actual: split() reverts with panic 0x11 (arithmetic underflow), harvest() reverts the same; after another 0.5 ether of fees the next split with any free amount pays the first 1 ether whole to the pot as owed and the $IMD bucket gets nothing of it. The attached proof fails on this tree with '[FAIL: panic: arithmetic underflow or overflow (0x11)]'. Variant: owner withdrawEth(R, 0) after step (1) instead of launching: balance 0, owedTotal still 1 ether, and after a later launch the same split()/harvest() revert (test/scratch/Round2.t.sol::test_WithdrawEthLeavesOwedStanding on this tree).","severity":"low","snippet":"        uint256 spend = address(this).balance;","title":"launch() and withdrawEth() spend ETH that is booked as owed for a refused perps flush, leaving owedTotal standing: split() and harvest() then underflow until the fees held reach owedTotal, and that fi"},{"citation":"resolved","description":"The fix for f5033c14 holds for what it states: a second burnImd in the same transaction or block reverts TooSoon, so an atomic sandwich wraps at most one 0.1 ETH buy and loses to the pool's two 1% fees (test_TheImdBurnCantBeSandwiched). What remains is the non-atomic form the author's answer names: a searcher buys $IMD, calls burnImd(0) once a minute until the bucket is empty, and sells back. Nothing in the code limits that run; what stands against it is that the searcher holds a mispriced position in the open for the whole run (30 minutes for a 3 ETH bucket), where any other trader can arbitrage the pushed-up price and take the searcher's margin. This is an economic mitigation, not a guarantee, and whether it holds depends on how actively $IMD's Robinhood pool is arbitraged. Recorded as information so the residual is on the record; it does not reopen the fixed finding. If a guarantee is wanted, the remaining lever is a floor the caller cannot set in the same run: a timelock-set minimum $IMD per ETH for the buy (setImdPool already takes the timelock's parameters), checked against the swap's realised price.","line":518,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State as in test_TheImdBurnCantBeSandwiched: launched; $IMD pool ETH/$IMD 1%, spacing 100, full-range liquidity 1e21 at ~261 $IMD/ETH (~62 ETH deep); 30 ETH through addFees() and split(), imdBucket = 3 ETH. Honest baseline, one burnImd(0) a minute: the burn executor receives 739.68 $IMD. Searcher with 100 ETH: (1) swap 40 ETH -> $IMD; (2) burnImd(0), vm.warp(+60s), repeated 30 times, every call succeeds; (3) sell all the $IMD back. Nobody else trades. Actual: searcher ends with 101.2268 ETH (+1.2268 ETH), the executor receives 280.10 $IMD, the same numbers as the atomic attack before the fix. test/scratch/Round2.t.sol::test_GapResidualOverThirtyMinutes logs 'calls: 30', 'searcher pnl: 1.226795087220999675', 'executor got: 280.099234121262814770' on this tree.","severity":"info","snippet":"        if (last != 0 && block.timestamp < last + IMD_BUY_GAP) revert TooSoon();","title":"Residual of the burnImd sandwich fix: the one-minute gap bounds a transaction and a block, but a searcher who holds its $IMD position for bucket/0.1 ETH minutes in a quiet market still takes the same "}],"hash":"86c79ab52d2e4cb28664135ef6e421e7359c2fced784671133cd9c66b7b8eca0","nodeId":"66771256-0624-4ddc-8fb8-27f666dd7aaa","outcome":"completed","summary":"The review is complete. `.imd-findings.json` holds one low finding, one info finding, and a coverage row for every one of the 56 entry points plus two invariants. No repository file was changed; the scratch tests were removed.\n\n**Settlement of the prior findings**\n\n- **burnImd per-call cap (medium, f5033c14): fixed.** A second call in the same transaction or block reverts TooSoon, and the author's test shows the atomic sandwich now loses 0.58 ETH. The non-atomic form, one call per minute with the searcher holding its position for thirty minutes in a quiet market, still yields the same 1.2268 ETH profit. I recorded that as an info-level residual that does not reopen the fix, since the exposure to arbitrage during the run is a real economic deterrent the author chose deliberately.\n- **burnImd bucket debit (medium, 60984bf7): fixed.** The callback returns what the pool took, the bucket is debited by that, and an empty buy reverts Short(0). The attached proof passes on this tree.\n- **addPerpFees reverting on a refused sink (low): fixed** through the owed mechanism, confirmed by the author's test.\n- **renounceOwnership on the launch (low): fixed.** It reverts NoRenounce and transferOwnership still works.\n- **ETH stuck in the pool or hook (low): fixed.** The pool forwards its whole balance on collect and lowerFee, and the hook's payable Ownable functions refuse value.\n- **launch.json $owner (low): fixed.** The first argument is the brief's static team wallet, the other three match the constructor order.\n- **sellHeld sandwich (low) and endClaims time gate (info): disputed, accepted.** Both are the timelock's role as the brief binds it, documented in ADAPTATION.md as trust assumptions. I did not restate them.\n\n**New finding, low, reported by the independent tester and reproduced**\n\nA perps flush refused by the pot bridge before the launch is booked as owed, but launch() spends the whole balance including that ETH, and withdrawEth does the same. The debt stays recorded, so after the launch split() and harvest() revert with an arithmetic underflow until the fees held reach the owed amount, and that first tranche is then paid whole to the sink rather than split 90/10. The tester's proof fails on this tree for exactly that reason, and I reproduced the withdrawEth variant locally. The timelock can clear it with recoverEth, so nothing is lost for good, and it needs a refusing bridge plus a stray pre-launch flush. The minimal fix is to reconcile owedTotal in launch() and withdrawEth(), or to make split() return zero when the balance is at or below the bucket plus the owed.\n\nThe full project suite passes: 83 tests offline, the fork test skipped without an RPC URL.","treeHash":null,"usage":{"cachedInputTokens":1651356,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":23866,"runtime":"claude","turns":28,"wallClockMs":536607}},{"artifacts":[],"attempt":2,"bundleHash":"9e6ac574d2405a6072ce245d282022b9dd2f225d7803aae44b16397f4999394c","device":"f7df22fc14d72d4e","findings":[],"hash":"93b6dc0740913da974126deade2fd0a095e9ceaebb32775a917ddf398664aae0","nodeId":"4f4637eb-8a36-4982-803a-8b68fc0c2465","outcome":"completed","summary":"The work is done and committed on branch `adapt-robinhood-launch` (commit 92fcc7e). The build and the offline suite pass with an empty environment, and the fork test passed once against live Robinhood state.\n\n**What the launch rules needed: nothing.** The project already fits the `evm_contracts` factory as it arrived. The constructor is nonpayable, takes the four address arguments in the brief's order, sets the owner from its first argument, and calls nothing that exists before it. I rehearsed the protected floor's probe offline with the real arguments on chain id 4663: the constructor runs on an empty chain, the runtime fits EIP-170 with about 2 KB to spare, and the opcode scan is clean. Configuration, remappings and lib/ are untouched.\n\n**All three audit findings reproduced, so each is fixed with a test.**\n\n- **split re-splitting a refused share (medium).** The supplied proof reproduced exactly. A refused share is now kept as `owed` to its payee and excluded from every later split. The next split that lists the payee pays it, and `payOwed` retries for a payee the timelock has since removed. The timelock's `recoverEth` keeps its behaviour and also clamps the owed total, so recovery never strands split. Three tests cover the proof's scenario, the retry path, and recovery.\n- **A perps engine that can't run halts trading (medium).** Reproduced. The fail-closed sweep is the project's bound design and the timelock's role, so the sweep is unchanged. `setPerpEngine` now refuses an address with no code or one that cannot answer `openCount`, so a mistaken engine can no longer freeze the pool. A deliberately malicious engine stays the timelock's documented trust assumption, with its existing escape hatches. One existing test that used a bare address as the engine now uses the stand-in engine.\n- **claim not bounded by the reserve (low).** Reproduced. `claim` now reverts once the claimed total would pass `claimTotal`, so the timelock's held coins are never drawn on and `endClaims` keeps working.\n\nThe two info findings are recorded in ADAPTATION.md as the brief requires: the post-deploy `createHook` and `setClaims` steps stay, and the scope note needed nothing.\n\n**Results**\n\n| Check | Result |\n|---|---|\n| forge build | clean |\n| offline suite | 44 tests pass, fork test skips |\n| fork test with the public RPC | passes |\n| floor rehearsal (scratch) | passes, ~7.1M gas |\n\nADAPTATION.md lists every change, its file, and the finding or rule behind it. No launch.json was written. The hook's permissions, the 4.44% static fee, the lower-only fee move, the coin, the fee split defaults and every timelock power are as they arrived.","treeHash":"6ba44e83da4919868ed6c37ec19bacf697f19411","usage":{"cachedInputTokens":2166061,"inputTokens":674,"model":"claude-fable-5-1","outputTokens":32945,"runtime":"claude","turns":46,"wallClockMs":622659}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"4dd74fd7c315da80","findings":[{"citation":"resolved","description":"burnImd deducts `spend` (0.1 ETH or the whole bucket) from imdBucket before the swap, and unlockCallback (what == 1) settles only `ethIn = -d.amount0()`, the ETH the $IMD pool actually took, and returns only `out`. The two differ whenever the $IMD pool at imdPoolKey cannot absorb the full exact-input: a pool that is initialized but has no liquidity at or below its price (LPs withdrew, or the timelock repointed setImdPool to a key whose pool exists but is empty), or whose concentrated range the 0.1 ETH buy runs past (v4 keeps walking with zero liquidity to the limit and fills partially). The unspent remainder stays in the contract's balance but is no longer counted in imdBucket, so `split()`'s `amount = address(this).balance - imdBucket - owedTotal` treats it as fresh fees and pays 90% of it to POT_BRIDGE. Anyone can call burnImd(0) repeatedly (minImd is caller-chosen and 0 is accepted; `imd == 0` passes), so in the empty-pool state the whole $IMD share is drained into the general balance in bucket/0.1 calls and the next harvest/split routes 90% of it to the pot. Boundary x invariant seam: the invariant 'imdBucket == ETH reserved for $IMD' is kept by the body only when the swap consumes exactly `spend`, and the zero/partial-fill edge of the swap breaks it. Fix: return ethIn from the callback and deduct it (`imdBucket -= ethIn`), and/or revert when `out == 0`.","line":490,"path":"src/IMD6900RobinhoodLaunch.sol","proof":"// SPDX-License-Identifier: MIT\npragma solidity ^0.8.26;\n\nimport {Test} from \"forge-std/Test.sol\";\nimport {IPoolManager} from \"@uniswap/v4-core/src/interfaces/IPoolManager.sol\";\nimport {PoolManager} from \"@uniswap/v4-core/src/PoolManager.sol\";\nimport {Hooks} from \"@uniswap/v4-core/src/libraries/Hooks.sol\";\nimport {PoolKey} from \"@uniswap/v4-core/src/types/PoolKey.sol\";\nimport {Currency} from \"@uniswap/v4-core/src/types/Currency.sol\";\nimport {BalanceDelta} from \"@uniswap/v4-core/src/types/BalanceDelta.sol\";\nimport {SwapParams} from \"@uniswap/v4-core/src/types/PoolOperation.sol\";\nimport {TickMath} from \"@uniswap/v4-core/src/libraries/TickMath.sol\";\nimport {FullMath} from \"@uniswap/v4-core/src/libraries/FullMath.sol\";\nimport {IMD6900Hook} from \"src/IMD6900Hook.sol\";\nimport {IMD6900LockedPool} from \"src/IMD6900LockedPool.sol\";\nimport {IMD6900RobinhoodLaunch} from \"src/IMD6900RobinhoodLaunch.sol\";\n\ninterface IERC20 {\n    function balanceOf(address) external view returns (uint256);\n    function transfer(address, uint256) external returns (bool);\n}\n\n/// A plain ERC20 stand-in (IMDSTR, and $IMD etched at its Robinhood address)\ncontract MockToken {\n    uint256 public totalSupply;\n    mapping(address => uint256) public balanceOf;\n    mapping(address => mapping(address => uint256)) public allowance;\n    function mint(address to, uint256 a) external { balanceOf[to] += a; totalSupply += a; }\n    function approve(address s, uint256 a) external returns (bool) { allowance[msg.sender][s] = a; return true; }\n    function transfer(address to, uint256 a) external returns (bool) { balanceOf[msg.sender] -= a; balanceOf[to] += a; return true; }\n    function transferFrom(address f, address to, uint256 a) external returns (bool) {\n        allowance[f][msg.sender] -= a; balanceOf[f] -= a; balanceOf[to] += a; return true;\n    }\n}\n\n/// A plain trader through the PoolManager\ncontract Trader {\n    IPoolManager immutable pm;\n    PoolKey key;\n    constructor(IPoolManager pm_) { pm = pm_; }\n    function swap(PoolKey calldata k, bool buy, int256 amountSpecified) external returns (BalanceDelta d) {\n        key = k;\n        d = abi.decode(pm.unlock(abi.encode(buy, amountSpecified)), (BalanceDelta));\n    }\n    function unlockCallback(bytes calldata data) external returns (bytes memory) {\n        (bool buy, int256 amt) = abi.decode(data, (bool, int256));\n        BalanceDelta d = pm.swap(key, SwapParams(buy, amt, buy ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1), \"\");\n        _settle(key.currency0, d.amount0());\n        _settle(key.currency1, d.amount1());\n        return abi.encode(d);\n    }\n    function _settle(Currency c, int128 v) internal {\n        if (v < 0) {\n            if (c.isAddressZero()) pm.settle{value: uint128(-v)}();\n            else { pm.sync(c); IERC20(Currency.unwrap(c)).transfer(address(pm), uint128(-v)); pm.settle(); }\n        } else if (v > 0) pm.take(c, address(this), uint128(v));\n    }\n    receive() external payable {}\n}\n\n/// burnImd charges the $IMD bucket the requested `spend`, not the ETH the swap took. On an $IMD pool with no\n/// liquidity at (or below) its price the swap takes nothing, the 0.1 ETH stays in the launch, leaves the bucket,\n/// and the next split sends 90% of it to the pot bridge: the $IMD burn's share is paid to the wrong party.\ncontract BurnImdBucketTest is Test {\n    address constant OWNER = address(0xA11CE);\n    address constant TL = address(0x71E10C);\n    address constant POT = 0xc39e650a24985C2bBA9FD83C81041B8f3E3f9EDd;\n    uint256 constant SUPPLY = 1_000_000_000 ether;\n\n    IPoolManager PM;\n    IMD6900RobinhoodLaunch L;\n    IMD6900LockedPool pool;\n    Trader trader;\n\n    function setUp() public {\n        PM = IPoolManager(address(new PoolManager(address(this))));\n        MockToken imdstr = new MockToken();\n        L = new IMD6900RobinhoodLaunch(OWNER, PM, address(imdstr), TL);\n        L.createHook(_mineHook(L));\n        pool = L.pool();\n        trader = new Trader(PM);\n        vm.deal(address(trader), 1000 ether);\n        // $IMD and its pool, initialized but with no liquidity (LPs gone, or a repointed key)\n        vm.etch(L.IMD(), address(new MockToken()).code);\n        PM.initialize(L.imdPoolKey(), uint160(_sqrt(261 << 192)));\n        // the launch, then 150 ETH of buys: 6.66 ETH of fees, 0.666 ETH set aside for $IMD\n        bytes32 salt = _mineCoin(L);\n        address expected = L.tokenAddress(salt);\n        vm.deal(OWNER, 2.81 ether);\n        vm.prank(OWNER);\n        L.launch{value: 2.81 ether}(salt, expected, _sqrtPrice(19.18 ether), 0, address(0));\n        trader.swap(pool.poolKey(), true, -150 ether);\n        L.harvest();\n    }\n\n    function test_TheBucketIsChargedOnlyWhatTheBuySpent() public {\n        uint256 bucket = L.imdBucket();\n        uint256 balance = address(L).balance;\n        assertGt(bucket, 0.1 ether);\n        uint256 potBefore = POT.balance;\n        // anyone; a refusal (reverting when the pool returns nothing) is an acceptable fix too\n        try L.burnImd(0) returns (uint256 got) {\n            uint256 spent = balance - address(L).balance;\n            emit log_named_decimal_uint(\"imd bought\", got, 18);\n            emit log_named_decimal_uint(\"eth the swap took\", spent, 18);\n            assertEq(bucket - L.imdBucket(), spent, \"the bucket must be charged exactly what the swap took\");\n        } catch {}\n        L.split();\n        assertEq(POT.balance - potBefore, 0, \"none of the $IMD share may reach the pot bridge\");\n    }\n\n    function _mineHook(IMD6900RobinhoodLaunch l) internal view returns (bytes32 s) {\n        bytes32 initHash = l.hookInitCodeHash();\n        uint160 flags = l.FLAGS();\n        for (uint256 i;; ++i) {\n            address a = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(l), i, initHash)))));\n            if (uint160(a) & Hooks.ALL_HOOK_MASK == flags && uint160(a) >> 152 != 0x91) return bytes32(i);\n        }\n    }\n\n    function _mineCoin(IMD6900RobinhoodLaunch l) internal view returns (bytes32 s) {\n        bytes32 initHash = l.tokenInitCodeHash();\n        for (uint256 i;; ++i) {\n            address a = address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(l), i, initHash)))));\n            if (uint160(a) >> 144 == 0x6900) return bytes32(i);\n        }\n    }\n\n    function _sqrtPrice(uint256 mc) internal pure returns (uint160) {\n        return uint160(_sqrt(FullMath.mulDiv(SUPPLY, 1 << 192, mc)));\n    }\n\n    function _sqrt(uint256 x) internal pure returns (uint256 y) {\n        if (x == 0) return 0;\n        y = x;\n        uint256 z = (x + 1) / 2;\n        while (z < y) (y, z) = (z, (x / z + z) / 2);\n    }\n}","reproduction":"State: launch done; $IMD's pool key (ETH/$IMD, fee 10_000, spacing 100, no hooks) initialized at sqrtPrice for ~261 $IMD/ETH with zero liquidity; 150 ETH of buys harvested so imdBucket = 0.6784764 ETH and address(L).balance = imdBucket (+1 wei split dust). Call burnImd(0) from any address. Expected: either revert (nothing to buy) or imdBucket reduced by exactly the ETH spent (0). Actual: returns 0 $IMD, imdBucket = 0.5784764 ETH, balance unchanged at 0.6784764 ETH; then split() pays 0.09 ETH to POT_BRIDGE (0xc39e...9EDd) and moves 0.01 ETH back into the bucket. Repeating burnImd(0) six more times and split() sends 90% of the entire $IMD share to the pot. test/scratch/BurnImdBucket.t.sol fails on this tree with 'the bucket must be charged exactly what the swap took: 100000000000000000 != 0'.","severity":"medium","snippet":"        imdBucket -= spend;\n        imd = abi.decode(poolManager.unlock(abi.encode(uint8(1), spend)), (uint256));","title":"burnImd charges the $IMD bucket the requested spend, not what the swap took: unspent ETH is re-split 90% to the pot bridge"},{"citation":"resolved","description":"IMD_BUY_MAX is documented as the sandwich bound ('under what sandwiching it would be worth through two 1% fees'). The bound applies to one call: nothing limits calls per block or per transaction (nonReentrant only blocks nesting), burnImd is callable by anyone, the swap's price limit is MIN_SQRT_PRICE + 1, and the only floor is the caller's own minImd, which the attacker sets to 0. A searcher therefore front-runs the $IMD pool, calls burnImd(0) floor(imdBucket / 0.1 ETH) times in the same bundle, and sells back: the victim size is the whole bucket, the attacker pays the pool's two 1% fees once, and the burn executor receives a fraction of the $IMD it would have. Because nobody but a searcher is paid to call burnImd, the bucket accumulates until a sandwich is profitable, so the condition is the normal operating state. Boundary x invariant seam: the per-call cap is checked against a per-call quantity while the protected quantity (ETH exposed between two attacker swaps) is per bundle. Fix: bound burnImd per block (one call per block, or a per-block budget) and/or require a floor derived from an on-chain reference (e.g. the pool's TWAP) rather than the caller's minImd.","line":488,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: $IMD pool ETH/$IMD 1% with full-range liquidity worth ~62 ETH on the ETH side at ~261 $IMD/ETH; launch done; 1500 ETH of buys harvested so imdBucket = 6.6724764 ETH (66 calls' worth). Honest: 66 burnImd(0) calls buy 1542.54 $IMD for the burn executor. Attack bundle from one searcher with 100 ETH: swap 40 ETH into $IMD on that pool, then 66 x burnImd(0), then sell all the $IMD back. Expected (the cap's intent): no profitable sandwich, as for a single 0.1 ETH call (profit 0 at every front-run size tried). Actual: the burn executor gets 595.89 $IMD instead of 1542.54 (61% lost) and the searcher ends with 103.50 ETH, a 3.502 ETH profit. test/scratch/BurnImdSandwich.t.sol fails on this tree with 'a bundle of capped calls is sandwiched for a profit: 3502053457686053030 != 0'.","severity":"medium","snippet":"        uint256 spend = imdBucket < IMD_BUY_MAX ? imdBucket : IMD_BUY_MAX;","title":"burnImd's 0.1 ETH cap is per call, so a bundle of calls with minImd = 0 exposes the whole $IMD bucket to one sandwich"},{"citation":"resolved","description":"sellHeld sells `amount` coins exact-input to MAX_SQRT_PRICE - 1 with `minEth` as the only floor. The timelock schedules the call behind a public delay, so the amount, minEth and execution window are known in advance; a minEth tight to the price at scheduling reverts the sale after any ordinary move, so in practice it is loose (the project's own tests use 0). A searcher sells coins into the pool just before execution, lets the timelock's sale go through at the depressed price, and buys back after. With minEth = 0 and the held excess sold (~58.9M coins on the test launch, ~1.3 ETH), the ETH returned to `to` falls by the sandwich's price impact. Low: the actor who sets minEth is trusted and can choose a tight floor; reported because the parameter cannot be set at execution time. Fix: let the executor pass the floor at execution (e.g. a maximum age for a quoted price) or derive it from the pool's TWAP.","line":397,"path":"src/IMD6900RobinhoodLaunch.sol","reproduction":"State: launch with excessTo = address(L) (excessHeld ~58.9M coins); the timelock's queued call is sellHeld(0, 0, R). Searcher holding 10M coins: (1) swap 10M coins into the pool (price up in coins/ETH), (2) the timelock's sellHeld executes: eth received is lower than without step 1, (3) searcher buys 10M coins back. Expected: the sale's floor protects the ETH returned to R; actual: minEth = 0 accepts any amount, the difference goes to the searcher.","severity":"low","snippet":"        (eth, sold) = abi.decode(poolManager.unlock(abi.encode(uint8(2), amount)), (uint256, uint256));\n        if (eth < minEth) revert Short(eth);","title":"sellHeld's only slippage floor is minEth fixed days ahead in the timelock's public queue, so the sale can be sandwiched"}],"hash":"c9a96a8b06e94ecfcfd2fccc93dda9ac123be2b2983645eb0df844c59a884e9b","nodeId":"7fc1668d-97b5-4226-bf48-bdafe2ed8051","outcome":"completed","summary":"The review is complete and the findings file is written and validated.\n\n**Outcome.** The arithmetic in the launch, the pool opening and the fee move holds up. The `burnImd` path has two accounting defects in my area, plus one low-severity slippage note. All are in `.imd-findings.json` with coverage rows for all 58 entry points (55 hold, 4 point at findings) and three invariant rows.\n\n**Findings**\n\n1. **Medium, `burnImd` charges the bucket what it asked for, not what the swap took.** The bucket is reduced by 0.1 ETH before the swap, but the callback settles only the ETH the $IMD pool actually consumed. On an initialized $IMD pool with no liquidity at or below its price (or a partial fill past a concentrated range), the ETH never leaves, drops out of the bucket, and the next split pays 90% of it to the pot bridge. Anyone can trigger it with `burnImd(0)` repeatedly. Proof test fails on this tree with the bucket charged 0.1 ETH for a swap that spent nothing.\n\n2. **Medium, the 0.1 ETH cap is per call and does not bound a bundle.** With a caller-chosen `minImd` of 0 and no per-block limit, a searcher front-runs, calls `burnImd` as many times as the bucket allows, and sells back. In the simulation the burn executor received 596 $IMD instead of 1543, and the searcher cleared 3.5 ETH on a 6.67 ETH bucket against a 62 ETH pool. A single capped call was never profitable to sandwich, which is the cap's stated rationale. Proof test attached.\n\n3. **Low, `sellHeld` relies on a `minEth` fixed days ahead in the public timelock queue.** The sale can be sandwiched when the floor is loose, which the project's own tests model with 0.\n\n**What held under test.** `lowerFee` was fuzzed over 256 runs after random buys and sells with a prior `collect`. The moved liquidity stayed within one part in a billion of the original, the coins burned on the move were dust, and the ETH re-deposit never exceeded the principal removed. The near-opening-tick edge, where the position's ETH would round to a few wei and the re-deposit could lose a large share, is unreachable because the sell fee keeps the price below the opening tick. `open`'s tick floor is correct for negative ticks, and its liquidity-minus-one keeps the rounded-up coin amount within the approved supply. The split's floor rounding only leaves re-split dust, and the balance always covers the bucket plus owed shares.\n\n**Not reached.** IMDSTR's decimals on Robinhood could not be verified offline; the 1:1 claim is in raw units. The uint128 truncation in `open` was not reported because it only triggers below about 1e-23 coins per ETH, where v4 reverts anyway.\n\nThe scratch tests live under `test/scratch/` and the working tree has no stray files.","treeHash":null,"usage":{"cachedInputTokens":2664755,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":64967,"runtime":"claude","turns":33,"wallClockMs":951363}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"6a67f34b10f5ad79","findings":[],"hash":"cf1e5302fda7567b6bf29bfdad50312ff1a5feb3a07decbde42b272add99d156","nodeId":"7c45c3b5-d567-4925-a485-72b85533e5b7","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/… before retrying.","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":"gpt-6-astra","outputTokens":0,"runtime":"codex","turns":6,"wallClockMs":1385303}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"59006bbb4a8a46a3","findings":[],"hash":"fea2042c9f550f2a70ef843cbfb5b7fc69e0bac1a4618c1404f157bb82441fa8","nodeId":"4f4637eb-8a36-4982-803a-8b68fc0c2465","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/… before retrying.","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":"gpt-6-astra","outputTokens":0,"runtime":"codex","turns":2,"wallClockMs":301995}}],"verification":[{"checks":[{"durationMs":21202,"exitCode":0,"name":"build","output":"Compiling 83 files with Solc 0.8.26\nSolc 0.8.26 finished in 21.06s\nCompiler run successful!\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900Hook.sol:76:44\n   │\n76 │     constructor(IPoolManager poolManager_, address pool_, address owner_) BaseHook(poolManager_) {\n   │                                            ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:79:57\n   │\n79 │     constructor(IPoolManager poolManager_, uint24 fee_, address launcher_, address timelock_) {\n   │                                                         ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:79:76\n   │\n79 │     constructor(IPoolManager poolManager_, uint24 fee_, address launcher_, address timelock_) {\n   │                                                                            ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900Hook.sol:112:28\n    │\n112 │     function setPerpEngine(address engine) external onlyOwner {\n    │                            ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900Hook.sol:119:9\n    │\n119 │         emit PerpEngineSet(engine);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-events-access-control]: `claimRoot` is changed without an event but is used for access control\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:283:9\n    │\n283 │         (claimRoot, claimTotal, claimDeadline) = (root, total, deadline);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900Hook.sol:124:9\n    │\n124 │         emit SweepFailOpenSet(on);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:96:22\n   │\n96 │     function setHook(address hook_) external {\n   │                      ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:181:60\n    │\n181 │     constructor(address owner_, IPoolManager poolManager_, address imdstr_, address timelock_) {\n    │                                                            ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:181:77\n    │\n181 │     constructor(address owner_, IPoolManager poolManager_, address imdstr_, address timelock_) {\n    │                                                                             ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-no-eth]: external call can be reentered before `hook` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:205:9\n    │\n205 │         pool.setHook(h);\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/IMD6900RobinhoodLaunch.sol:208:9\n    │\n208 │         emit HookCreated(h, salt);\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/IMD6900RobinhoodLaunch.sol:231:9\n    │\n231 │         emit PerpFeesForwarded(to, msg.value);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:246:27\n    │\n246 │                 keccak256(abi.encodePacked(type(IMD6900).creationCode, abi.encode(address(this))))\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:254:26\n    │\n254 │         return keccak256(abi.encodePacked(type(IMD6900).creationCode, abi.encode(address(this))));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900LockedPool.sol:106:19\n    │\n106 │     function open(address token_, uint160 sqrtPriceX96, uint256 amount) external nonReentrant returns (uint128 liq) {\n    │                   ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900LockedPool.sol:126:9\n    │\n126 │         emit Opened(start, upper, used, liq);\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/IMD6900LockedPool.sol:118:9\n    │\n118 │         poolManager.initialize(poolKey(), start);\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/IMD6900RobinhoodLaunch.sol:284:9\n    │\n284 │         emit ClaimsSet(root, total, deadline);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:122:15\n    │\n122 │         liq = uint128(l - 1);\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[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:293:9\n    │\n293 │         SafeTransferLib.safeTransferETH(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:289:73\n    │\n289 │     function withdrawEth(address to, uint256 amount) external onlyOwner nonReentrant {\n    │                                                                         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:294:9\n    │\n294 │         emit EthWithdrawn(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:307:9\n    │\n307 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:311:32\n    │\n311 │         if (claimTotal != 0 && claimDeadline < block.timestamp + MIN_CLAIM_WINDOW) revert BadClaims();\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/IMD6900RobinhoodLaunch.sol:332:9\n    │\n332 │         emit Launched(t, sqrtPriceX96, pool.upperTick(), spend, bought, excess, excessTo);\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/IMD6900RobinhoodLaunch.sol:318:9\n    │\n318 │         pool.open(t, sqrtPriceX96, SUPPLY);\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/IMD6900LockedPool.sol:246:9\n    │\n246 │         emit Collected(eth, tokens);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:336:62\n    │\n336 │     function releaseUnclaimed(address to) external onlyOwner nonReentrant {\n    │                                                              ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:338:13\n    │\n338 │         if (block.timestamp < claimDeadline) revert ClaimsOpen();\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/IMD6900LockedPool.sol:148:9\n    │\n148 │         emit FeeLowered(from, newFee, poolKey().toId(), liq);\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/IMD6900RobinhoodLaunch.sol:346:9\n    │\n346 │         emit UnclaimedReleased(to, 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/IMD6900LockedPool.sol:142:9\n    │\n142 │         poolManager.initialize(poolKey(), sqrtPriceX96); // the hook serves the new pool from here on\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:350:75\n    │\n350 │     function releaseImdstr(uint256 amount, address to) external onlyOwner nonReentrant {\n    │                                                                           ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:355:9\n    │\n355 │         emit ImdstrReleased(to, amount);\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/IMD6900Hook.sol:230:43\n    │\n230 │               (bool ok, bytes memory out) = engine.call{gas: g - reserve}(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n231 │ ┃                 abi.encodeCall(IPerpSweep.sweepLiquidations, (tx.origin, spec, isBuy, limit))\n232 │ ┃             );\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/return-bomb\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:193:23\n    │\n193 │         uint256 eth = uint256(int256(out.amount0() - earned.amount0()));\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/IMD6900LockedPool.sol:194:25\n    │\n194 │         uint256 coins = uint256(int256(out.amount1() - earned.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[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:370:13\n    │\n370 │             sum += bps[i];\n    │             ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/IMD6900LockedPool.sol:211:21\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:368:38\n    │\n368 │             if (to[i] == address(0)) revert BadPayees();\n    │                                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:211:47\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\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/IMD6900LockedPool.sol:211:55\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\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/IMD6900LockedPool.sol:212:73\n    │\n212 │         else if (a0 > 0) poolManager.take(key.currency0, address(this), uint256(uint128(a0)));\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/IMD6900LockedPool.sol:212:81\n    │\n212 │         else if (a0 > 0) poolManager.take(key.currency0, address(this), uint256(uint128(a0)));\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/IMD6900RobinhoodLaunch.sol:373:23\n    │\n373 │         _payeeCount = uint8(n);\n    │                       ━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:215:71\n    │\n215 │             SafeTransferLib.safeTransfer(token, address(poolManager), uint256(uint128(-a1)));\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/IMD6900LockedPool.sol:215:79\n    │\n215 │             SafeTransferLib.safeTransfer(token, address(poolManager), uint256(uint128(-a1)));\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/IMD6900LockedPool.sol:216:13\n    │\n216 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:218:60\n    │\n218 │             poolManager.take(key.currency1, address(this), uint256(uint128(a1)));\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/IMD6900LockedPool.sol:218:68\n    │\n218 │             poolManager.take(key.currency1, address(this), uint256(uint128(a1)));\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/IMD6900LockedPool.sol:237:60\n    │\n237 │         return l > type(uint128).max ? type(uint128).max : uint128(l);\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/IMD6900LockedPool.sol:250:24\n    │\n250 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:250:32\n    │\n250 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:250:47\n    │\n250 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:250:55\n    │\n250 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\n    │                                                       ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:387:9\n    │\n387 │         emit EthRecovered(to, 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/IMD6900RobinhoodLaunch.sol:399:9\n    │\n399 │         emit ExcessReleased(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `excessHeld` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:424:34\n    │\n424 │         (eth, sold) = abi.decode(poolManager.unlock(abi.encode(uint8(2), amount)), (uint256, uint256));\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/IMD6900RobinhoodLaunch.sol:428:9\n    │\n428 │         emit HeldSold(sold, eth, to);\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/IMD6900RobinhoodLaunch.sol:472:9\n    │\n472 │         emit Claimed(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 `owed` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:574:22\n    │\n574 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:574:22\n    │\n574 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:578:13\n    │\n578 │             emit PayFailed(payee, part);\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/IMD6900RobinhoodLaunch.sol:498:9\n    │\n498 │         emit Split(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/IMD6900RobinhoodLaunch.sol:507:9\n    │\n507 │         SafeTransferLib.safeTransferETH(payee, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-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/IMD6900RobinhoodLaunch.sol:508:9\n    │\n508 │         emit OwedPaid(payee, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `imdBucket` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:522:35\n    │\n522 │         (imd, ethIn) = abi.decode(poolManager.unlock(abi.encode(uint8(1), spend)), (uint256, uint256));\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/IMD6900RobinhoodLaunch.sol:518:26\n    │\n518 │         if (last != 0 && block.timestamp < last + IMD_BUY_GAP) revert TooSoon();\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/IMD6900RobinhoodLaunch.sol:528:9\n    │\n528 │         emit ImdBurned(ethIn, imd);\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/IMD6900RobinhoodLaunch.sol:538:9\n    │\n538 │         pool.collect();\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:552:69\n    │\n552 │             BalanceDelta s = poolManager.swap(k, SwapParams(false, -int256(spend), TickMath.MAX_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/IMD6900RobinhoodLaunch.sol:553:28\n    │\n553 │             uint256 sold = uint256(uint128(-s.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/IMD6900RobinhoodLaunch.sol:553:36\n    │\n553 │             uint256 sold = uint256(uint128(-s.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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:554:30\n    │\n554 │             uint256 ethOut = uint256(uint128(s.amount0()));\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/IMD6900RobinhoodLaunch.sol:554:38\n    │\n554 │             uint256 ethOut = uint256(uint128(s.amount0()));\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/IMD6900RobinhoodLaunch.sol:557:13\n    │\n557 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:562:66\n    │\n562 │         BalanceDelta d = poolManager.swap(key, SwapParams(true, -int256(spend), 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/IMD6900RobinhoodLaunch.sol:563:25\n    │\n563 │         uint256 ethIn = uint256(uint128(-d.amount0()));\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/IMD6900RobinhoodLaunch.sol:563:33\n    │\n563 │         uint256 ethIn = uint256(uint128(-d.amount0()));\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/IMD6900RobinhoodLaunch.sol:564:23\n    │\n564 │         uint256 out = uint256(uint128(d.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/IMD6900RobinhoodLaunch.sol:564:31\n    │\n564 │         uint256 out = uint256(uint128(d.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/IMD6900RobinhoodLaunch.sol:565:9\n    │\n565 │         poolManager.settle{value: ethIn}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:574:22\n    │\n574 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:583:16\n    │\n583 │         return abi.encodePacked(type(IMD6900Hook).creationCode, abi.encode(poolManager, address(pool), timelock));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\n","passed":true},{"durationMs":268,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[PASS] test_TheWholeLaunchOnRobinhood() (gas: 2554)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 291.11µs (20.74µs CPU time)\n\nRan 49 tests for test/IMD6900RobinhoodLaunch.t.sol:IMD6900RobinhoodLaunchTest\n[PASS] test_ARefusedShareIsKeptForItsPayee() (gas: 3525788)\n[PASS] test_ARefusedShareIsPaidOnRetry() (gas: 1764817)\n[PASS] test_BurnsImdThroughItsExecutor() (gas: 2965075)\n[PASS] test_ClaimsCantChangeAfterTheLaunch() (gas: 1256947)\n[PASS] test_ClaimsNeedTheSnapshot() (gas: 1458855)\n[PASS] test_ClaimsNeverPassTheReserve() (gas: 1809422)\n[PASS] test_ClaimsOneForOne() (gas: 1581495)\n[PASS] test_ClaimsStayOpenNinetyDays() (gas: 208717)\n[PASS] test_DeploysOnAFreshChain() (gas: 1985555)\n[PASS] test_EachDeployerMinesItsOwnHook() (gas: 36172616)\n[PASS] test_EndingTheClaimsBringsMostOfTheBuyBack() (gas: 2138647)\nLogs:\n  back of the 2.81 ETH buy (C's claim kept by C): 2.631273447654979998\n\n[PASS] test_EthComesBackBeforeTheLaunchOnly() (gas: 1298217)\n[PASS] test_EthSentToThePoolIsForwarded() (gas: 1438923)\n[PASS] test_FitsOneTransaction() (gas: 36151292)\nLogs:\n  launch deploy gas (the same for every deployer: no mining): 8088\n  createHook gas: 1161390\n\n[PASS] test_HarvestSplitsTheEthAndBurnsTheCoin() (gas: 1840609)\nLogs:\n  pot bridge: 0.511887600000000000\n\n[PASS] test_KeepsTheExcessForTheTimelock() (gas: 1428185)\n[PASS] test_LaunchesAtEthereumsPrice() (gas: 1306261)\nLogs:\n  opening buy, coins: 122347490.873046793208740802\n  market cap after the buy, ETH on 1B: 25.007184312087223619\n\n[PASS] test_LaunchesOnce() (gas: 1297827)\n[PASS] test_LowerFeeMovesTheLiquidity() (gas: 2183455)\n[PASS] test_NoClaimsAllOfItBack() (gas: 1770289)\nLogs:\n  back of the 2.81 ETH buy, nobody claimed: 2.704790817050839653\n\n[PASS] test_OnlyTheOwnerLaunches() (gas: 178941)\n[PASS] test_OnlyTheTimelockLowersTheFeeAndOnlyDown() (gas: 1955717)\n[PASS] test_OnlyTheTimelockRecoversEth() (gas: 1298348)\n[PASS] test_OnlyTheTimelockRepointsImdsPool() (gas: 68121)\n[PASS] test_OnlyTheTimelockRepointsTheBurnExecutor() (gas: 91713)\n[PASS] test_OnlyTheTimelockRunsTheHook() (gas: 667778)\n[PASS] test_OnlyTheTimelockSetsTheSplit() (gas: 1793753)\n[PASS] test_PassesTheAdmissionScan() (gas: 13850462)\n[PASS] test_PerpFeesGoAllToThePot() (gas: 1449197)\n[PASS] test_PerpFeesRefusedAreOwed() (gas: 1565400)\n[PASS] test_RefusesAnAddressThatIsnt0x6900() (gas: 702428)\n[PASS] test_RefusesTheWrongAddress() (gas: 709897)\n[PASS] test_SendsTheExcessWhereTold() (gas: 1272855)\n[PASS] test_TheBucketIsChargedWhatTheBuyTook() (gas: 3543321)\nLogs:\n  the thin range took, ETH: 0.000158651623865091\n\n[PASS] test_TheBuyMustCoverTheClaims() (gas: 1214473)\n[PASS] test_TheCoinIsAt0x6900() (gas: 1231408)\nLogs:\n  the coin: 0x6900d11750039d7Ef0f8262b5a62152BB906B8E8\n\n[PASS] test_TheCoinIsPlain() (gas: 1294790)\n[PASS] test_TheFeeIsStatic() (gas: 1572960)\n[PASS] test_TheHookIsCreatedOnceAtAFittingSalt() (gas: 187929720)\n[PASS] test_TheHookRefusesAnEngineThatCantAnswer() (gas: 2451560)\n[PASS] test_TheHooksAddressAndPermissions() (gas: 21800)\n[PASS] test_TheImdBurnCantBeSandwiched() (gas: 3766615)\nLogs:\n  the searcher's loss: 0.580539316843367753\n\n[PASS] test_TheLaunchOwnerCantRenounce() (gas: 70390)\n[PASS] test_TheLiquidityIsLocked() (gas: 2147433)\n[PASS] test_ThePerpsEngineTradesThroughTheHook() (gas: 2308414)\n[PASS] test_TheTimelockCanRecoverARefusedShare() (gas: 1723479)\n[PASS] test_TheTimelockSellsTheExcessBack() (gas: 1648906)\nLogs:\n  the excess alone, sold back: ETH: 1.312390213060687826\n\n[PASS] test_TheUnclaimedRestAfterTheDeadline() (gas: 1648928)\n[PASS] test_TradesBothWays() (gas: 1553220)\nSuite result: ok. 49 passed; 0 failed; 0 skipped; finished in 170.90ms (239.75ms CPU time)\n\nRan 2 test suites in 172.05ms (171.19ms CPU time): 50 tests passed, 0 failed, 0 skipped (50 total tests)\n","passed":true},{"durationMs":60,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMD6900.approve(address,uint256)\",\"IMD6900.burn(uint256)\",\"IMD6900.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"IMD6900.transfer(address,uint256)\",\"IMD6900.transferFrom(address,address,uint256)\",\"IMD6900Hook.afterAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"IMD6900Hook.afterDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"IMD6900Hook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMD6900Hook.afterRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"IMD6900Hook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMD6900Hook.beforeAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"IMD6900Hook.beforeDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"IMD6900Hook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"IMD6900Hook.beforeRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"IMD6900Hook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMD6900Hook.cancelOwnershipHandover()\",\"IMD6900Hook.completeOwnershipHandover(address)\",\"IMD6900Hook.renounceOwnership()\",\"IMD6900Hook.requestOwnershipHandover()\",\"IMD6900Hook.setPerpEngine(address)\",\"IMD6900Hook.setSweepFailOpen(bool)\",\"IMD6900Hook.transferOwnership(address)\",\"IMD6900LockedPool.collect()\",\"IMD6900LockedPool.lowerFee(uint24)\",\"IMD6900LockedPool.open(address,uint160,uint256)\",\"IMD6900LockedPool.receive()\",\"IMD6900LockedPool.setHook(address)\",\"IMD6900LockedPool.unlockCallback(bytes)\",\"IMD6900RobinhoodLaunch.addFees()\",\"IMD6900RobinhoodLaunch.addPerpFees()\",\"IMD6900RobinhoodLaunch.burnImd(uint256)\",\"IMD6900RobinhoodLaunch.cancelOwnershipHandover()\",\"IMD6900RobinhoodLaunch.claim(uint256,uint256,bytes32[])\",\"IMD6900RobinhoodLaunch.completeOwnershipHandover(address)\",\"IMD6900RobinhoodLaunch.createHook(bytes32)\",\"IMD6900RobinhoodLaunch.endClaims()\",\"IMD6900RobinhoodLaunch.harvest()\",\"IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address)\",\"IMD6900RobinhoodLaunch.payOwed(address)\",\"IMD6900RobinhoodLaunch.receive()\",\"IMD6900RobinhoodLaunch.recoverEth(address,uint256)\",\"IMD6900RobinhoodLaunch.releaseExcess(uint256,address)\",\"IMD6900RobinhoodLaunch.releaseImdstr(uint256,address)\",\"IMD6900RobinhoodLaunch.releaseUnclaimed(address)\",\"IMD6900RobinhoodLaunch.renounceOwnership()\",\"IMD6900RobinhoodLaunch.requestOwnershipHandover()\",\"IMD6900RobinhoodLaunch.sellHeld(uint256,uint256,address)\",\"IMD6900RobinhoodLaunch.setClaims(bytes32,uint256,uint256)\",\"IMD6900RobinhoodLaunch.setImdBurn(address)\",\"IMD6900RobinhoodLaunch.setImdPool(uint24,int24,address)\",\"IMD6900RobinhoodLaunch.setPayees(address[],uint16[])\",\"IMD6900RobinhoodLaunch.setPerpFeeTo(address)\",\"IMD6900RobinhoodLaunch.split()\",\"IMD6900RobinhoodLaunch.transferOwnership(address)\",\"IMD6900RobinhoodLaunch.unlockCallback(bytes)\",\"IMD6900RobinhoodLaunch.withdrawEth(address,uint256)\"],\"files\":{\".gitignore\":14,\"ADAPTATION.md\":185,\"README.md\":117,\"foundry.toml\":25,\"jobs/swarm-launch.json\":27,\"script/claims.py\":88,\"script/launch.sh\":222,\"script/mine.sh\":12,\"script/params.py\":64,\"script/programmable_plan.py\":205,\"snapshots/rh-community-2026-10-09.json\":56,\"src/IMD6900.sol\":29,\"src/IMD6900Hook.sol\":252,\"src/IMD6900LockedPool.sol\":252,\"src/IMD6900RobinhoodLaunch.sol\":595,\"test/HookSalt.sol\":35,\"test/IMD6900RobinhoodLaunch.t.sol\":1202,\"test/RobinhoodFork.t.sol\":217},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":6626,"exitCode":0,"name":"slither","output":"[high/medium] arbitrary-send-eth at src/IMD6900LockedPool.sol:242: IMD6900LockedPool._payOut(uint256,uint256) (src/IMD6900LockedPool.sol#242-247) sends eth to arbitrary user\n[high/medium] reentrancy-eth at src/IMD6900RobinhoodLaunch.sol:479: Reentrancy in IMD6900RobinhoodLaunch.split() (src/IMD6900RobinhoodLaunch.sol#479-499):\n[medium/high] incorrect-equality at src/IMD6900RobinhoodLaunch.sol:379: IMD6900RobinhoodLaunch.recoverEth(address,uint256) (src/IMD6900RobinhoodLaunch.sol#379-388) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMD6900RobinhoodLaunch.sol:303: IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address) (src/IMD6900RobinhoodLaunch.sol#303-333) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMD6900RobinhoodLaunch.sol:479: IMD6900RobinhoodLaunch.split() (src/IMD6900RobinhoodLaunch.sol#479-499) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMD6900RobinhoodLaunch.sol:289: IMD6900RobinhoodLaunch.withdrawEth(address,uint256) (src/IMD6900RobinhoodLaunch.sol#289-295) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/IMD6900RobinhoodLaunch.sol:195: Reentrancy in IMD6900RobinhoodLaunch.createHook(bytes32) (src/IMD6900RobinhoodLaunch.sol#195-210):\n[medium/medium] reentrancy-no-eth at src/IMD6900RobinhoodLaunch.sol:419: Reentrancy in IMD6900RobinhoodLaunch.sellHeld(uint256,uint256,address) (src/IMD6900RobinhoodLaunch.sol#419-429):\n[medium/medium] reentrancy-no-eth at src/IMD6900RobinhoodLaunch.sol:516: Reentrancy in IMD6900RobinhoodLaunch.burnImd(uint256) (src/IMD6900RobinhoodLaunch.sol#516-529):\n[medium/medium] uninitialized-local at src/IMD6900RobinhoodLaunch.sol:366: IMD6900RobinhoodLaunch.setPayees(address[],uint16[]).sum (src/IMD6900RobinhoodLaunch.sol#366) is a local variable never initialized\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:209: IMD6900LockedPool._settle(int128,int128) (src/IMD6900LockedPool.sol#209-220) ignores return value by poolManager.settle() (src/IMD6900LockedPool.sol#216)\n[medium/medium] unused-return at src/IMD6900RobinhoodLaunch.sol:537: IMD6900RobinhoodLaunch.harvest() (src/IMD6900RobinhoodLaunch.sol#537-540) ignores return value by pool.collect() (src/IMD6900RobinhoodLaunch.sol#538)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:134: IMD6900LockedPool.lowerFee(uint24) (src/IMD6900LockedPool.sol#134-149) ignores return value by (sqrtPriceX96,None,None,None) = poolManager.getSlot0(old.toId()) (src/IMD6900LockedPool.sol#139)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:134: IMD6900LockedPool.lowerFee(uint24) (src/IMD6900LockedPool.sol#134-149) ignores return value by poolManager.initialize(poolKey(),sqrtPriceX96) (src/IMD6900LockedPool.sol#142)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:106: IMD6900LockedPool.open(address,uint160,uint256) (src/IMD6900LockedPool.sol#106-127) ignores return value by poolManager.initialize(poolKey(),start) (src/IMD6900LockedPool.sol#118)\n[medium/medium] unused-return at src/IMD6900RobinhoodLaunch.sol:303: IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address) (src/IMD6900RobinhoodLaunch.sol#303-333) ignores return value by pool.open(t,sqrtPriceX96,SUPPLY) (src/IMD6900RobinhoodLaunch.sol#318)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:209: IMD6900LockedPool._settle(int128,int128) (src/IMD6900LockedPool.sol#209-220) ignores return value by poolManager.settle{value: uint256(uint128(- a0))}() (src/IMD6900LockedPool.sol#211)\n[medium/medium] unused-return at src/IMD6900RobinhoodLaunch.sol:547: IMD6900RobinhoodLaunch.unlockCallback(bytes) (src/IMD6900RobinhoodLaunch.sol#547-568) ignores return value by poolManager.settle{value: ethIn}() (src/IMD6900RobinhoodLaunch.sol#565)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:223: IMD6900LockedPool._liquidityFor(uint256,uint256) (src/IMD6900LockedPool.sol#223-238) ignores return value by (p,None,None,None) = poolManager.getSlot0(poolKey().toId()) (src/IMD6900LockedPool.sol#224)\n[medium/medium] unused-return at src/IMD6900RobinhoodLaunch.sol:547: IMD6900RobinhoodLaunch.unlockCallback(bytes) (src/IMD6900RobinhoodLaunch.sol#547-568) ignores return value by poolManager.settle() (src/IMD6900RobinhoodLaunch.sol#557)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:161: IMD6900LockedPool.state() (src/IMD6900LockedPool.sol#161-165) ignores return value by (sqrtPriceX96,tick,None,None) = poolManager.getSlot0(id) (src/IMD6900LockedPool.sol#163)\n[low/medium] missing-zero-check at src/IMD6900LockedPool.sol:79: IMD6900LockedPool.constructor(IPoolManager,uint24,address,address).timelock_ (src/IMD6900LockedPool.sol#79) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900LockedPool.sol:79: IMD6900LockedPool.constructor(IPoolManager,uint24,address,address).launcher_ (src/IMD6900LockedPool.sol#79) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900RobinhoodLaunch.sol:181: IMD6900RobinhoodLaunch.constructor(address,IPoolManager,address,address).timelock_ (src/IMD6900RobinhoodLaunch.sol#181) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900Hook.sol:76: IMD6900Hook.constructor(IPoolManager,address,address).pool_ (src/IMD6900Hook.sol#76) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900RobinhoodLaunch.sol:181: IMD6900RobinhoodLaunch.constructor(address,IPoolManager,address,address).imdstr_ (src/IMD6900RobinhoodLaunch.sol#181) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900RobinhoodLaunch.sol:448: IMD6900RobinhoodLaunch.setImdPool(uint24,int24,address).hooks (src/IMD6900RobinhoodLaunch.sol#448) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900LockedPool.sol:106: IMD6900LockedPool.open(address,uint160,uint256).token_ (src/IMD6900LockedPool.sol#106) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900LockedPool.sol:96: IMD6900LockedPool.setHook(address).hook_ (src/IMD6900LockedPool.sol#96) lacks a zero-check on :\n[low/medium] calls-loop at src/IMD6900RobinhoodLaunch.sol:573: IMD6900RobinhoodLaunch._pay(address,uint256) (src/IMD6900RobinhoodLaunch.sol#573-580) has external calls inside a loop: (ok,None) = payee.call{value: part}() (src/IMD6900RobinhoodLaunch.sol#574)\n[low/medium] calls-loop at src/IMD6900RobinhoodLaunch.sol:573: IMD6900RobinhoodLaunch._pay(address,uint256) (src/IMD6900RobinhoodLaunch.sol#573-580) has external calls inside a loop: (ok,None) = payee.call{value: part}() (src/IMD6900RobinhoodLaunch.sol#574)\n[low/medium] reentrancy-benign at src/IMD6900RobinhoodLaunch.sol:195: Reentrancy in IMD6900RobinhoodLaunch.createHook(bytes32) (src/IMD6900RobinhoodLaunch.sol#195-210):\n[low/medium] reentrancy-benign at src/IMD6900RobinhoodLaunch.sol:516: Reentrancy in IMD6900RobinhoodLaunch.burnImd(uint256) (src/IMD6900RobinhoodLaunch.sol#516-529):\n[low/medium] reentrancy-benign at src/IMD6900RobinhoodLaunch.sol:573: Reentrancy in IMD6900RobinhoodLaunch._pay(address,uint256) (src/IMD6900RobinhoodLaunch.sol#573-580):\n[low/medium] reentrancy-benign at src/IMD6900RobinhoodLaunch.sol:303: Reentrancy in IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address) (src/IMD6900RobinhoodLaunch.sol#303-333):\n[low/medium] reentrancy-benign at src/IMD6900LockedPool.sol:106: Reentrancy in IMD6900LockedPool.open(address,uint160,uint256) (src/IMD6900LockedPool.sol#106-127):\n[low/medium] reentrancy-benign at src/IMD6900LockedPool.sol:134: Reentrancy in IMD6900LockedPool.lowerFee(uint24) (src/IMD6900LockedPool.sol#134-149):\n[low/medium] reentrancy-benign at src/IMD6900RobinhoodLaunch.sol:303: Reentrancy in IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address) (src/IMD6900RobinhoodLaunch.sol#303-333):\n[low/medium] reentrancy-events at src/IMD6900RobinhoodLaunch.sol:228: Reentrancy in IMD6900RobinhoodLaunch.addPerpFees() (src/IMD6900RobinhoodLaunch.sol#228-232):\n[low/medium] reentrancy-events at src/IMD6900RobinhoodLaunch.sol:537: Reentrancy in IMD6900RobinhoodLaunch.harvest() (src/IMD6900RobinhoodLaunch.sol#537-540):\n[low/medium] reentrancy-events at src/IMD6900RobinhoodLaunch.sol:573: Reentrancy in IMD6900RobinhoodLaunch._pay(address,uint256) (src/IMD6900RobinhoodLaunch.sol#573-580):\n[low/medium] reentrancy-events at src/IMD6900RobinhoodLaunch.sol:195: Reentrancy in IMD6900RobinhoodLaunch.createHook(bytes32) (src/IMD6900RobinhoodLaunch.sol#195-210):\n[low/medium] return-bomb at src/IMD6900Hook.sol:224: IMD6900Hook._sweep(address,int256,bool,uint160) (src/IMD6900Hook.sol#224-251) tries to limit the gas of an external call that controls implicit decoding\n[low/medium] timestamp at src/IMD6900RobinhoodLaunch.sol:303: IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address) (src/IMD6900RobinhoodLaunch.sol#303-333) uses timestamp for comparisons\n[low/medium] timestamp at src/IMD6900RobinhoodLaunch.sol:516: IMD6900RobinhoodLaunch.burnImd(uint256) (src/IMD6900RobinhoodLaunch.sol#516-529) uses timestamp for comparisons\n[low/medium] timestamp at src/IMD6900RobinhoodLaunch.sol:336: IMD6900RobinhoodLaunch.releaseUnclaimed(address) (src/IMD6900RobinhoodLaunch.sol#336-347) uses timestamp for comparisons","passed":true},{"durationMs":578,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/IMD6900RobinhoodLaunch.sol:246: `abi.encodePacked()` Hash Collision (3 places)\n[high] contract-locks-ether at src/IMD6900Hook.sol:40: Contract locks Ether without a withdraw function (2 places)\n[high] eth-send-unchecked-address at src/IMD6900RobinhoodLaunch.sol:228: ETH transferred without address checks\n[high] reentrancy-state-change at src/IMD6900LockedPool.sol:118: Reentrancy: State change after external call (9 places)\n[low] centralization-risk at src/IMD6900Hook.sol:40: Centralization Risk (11 places)\n[low] costly-loop at src/IMD6900RobinhoodLaunch.sol:367: Costly operations inside loop (2 places)\n[low] large-numeric-literal at src/IMD6900.sol:11: Large Numeric Literal (7 places)\n[low] literal-instead-of-constant at src/IMD6900Hook.sol:116: Literal Instead of Constant (9 places)\n[low] non-reentrant-not-first at src/IMD6900RobinhoodLaunch.sol:289: `nonReentrant` is Not the First Modifier (4 places)\n[low] push-zero-opcode at src/IMD6900.sol:2: PUSH0 Opcode (4 places)\n[low] require-revert-in-loop at src/IMD6900RobinhoodLaunch.sol:367: Loop Contains `require`/`revert`\n[low] state-no-address-check at src/IMD6900LockedPool.sol:99: Address State Variable Set Without Checks (3 places)\n[low] unchecked-return at src/IMD6900LockedPool.sol:118: Unchecked Return (6 places)\n[low] uninitialized-local-variable at src/IMD6900RobinhoodLaunch.sol:589: Uninitialized Local Variable\n[low] unspecific-solidity-pragma at src/IMD6900.sol:2: Unspecific Solidity Pragma (4 places)\n[low] unused-public-function at src/IMD6900Hook.sol:102: Public Function Not Used Internally (3 places)","passed":true},{"durationMs":10363,"exitCode":0,"name":"proof 60984bf755ca","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 9.72s\nCompiler run successful!\n\nRan 1 test for test/imd-proof-cb34e168/Proof_60984bf755ca.t.sol:BurnImdBucketTest\n[PASS] test_TheBucketIsChargedOnlyWhatTheBuySpent() (gas: 343877)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 69.86ms (395.68µs CPU time)\n\nRan 1 test suite in 70.69ms (69.86ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"1a02fa00131f183fd32e0ca857689a5ea67f217ccb6baece2314a609c8a4b144","verifiedTreeHash":"aafed39f0bf206ab3913c05a9fced9534d6175da","verifierVersion":"0.1.0+327e1e9f"},{"checks":[{"durationMs":24211,"exitCode":0,"name":"build","output":"Compiling 83 files with Solc 0.8.26\nSolc 0.8.26 finished in 24.02s\nCompiler run successful!\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:79:76\n   │\n79 │     constructor(IPoolManager poolManager_, uint24 fee_, address launcher_, address timelock_) {\n   │                                                                            ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900Hook.sol:76:44\n   │\n76 │     constructor(IPoolManager poolManager_, address pool_, address owner_) BaseHook(poolManager_) {\n   │                                            ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:79:57\n   │\n79 │     constructor(IPoolManager poolManager_, uint24 fee_, address launcher_, address timelock_) {\n   │                                                         ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-events-access-control]: `claimRoot` is changed without an event but is used for access control\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:283:9\n    │\n283 │         (claimRoot, claimTotal, claimDeadline) = (root, total, deadline);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900Hook.sol:112:28\n    │\n112 │     function setPerpEngine(address engine) external onlyOwner {\n    │                            ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900Hook.sol:119:9\n    │\n119 │         emit PerpEngineSet(engine);\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/IMD6900Hook.sol:124:9\n    │\n124 │         emit SweepFailOpenSet(on);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:96:22\n   │\n96 │     function setHook(address hook_) external {\n   │                      ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:181:60\n    │\n181 │     constructor(address owner_, IPoolManager poolManager_, address imdstr_, address timelock_) {\n    │                                                            ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:181:77\n    │\n181 │     constructor(address owner_, IPoolManager poolManager_, address imdstr_, address timelock_) {\n    │                                                                             ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-no-eth]: external call can be reentered before `hook` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:205:9\n    │\n205 │         pool.setHook(h);\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/IMD6900RobinhoodLaunch.sol:208:9\n    │\n208 │         emit HookCreated(h, salt);\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/IMD6900RobinhoodLaunch.sol:231:9\n    │\n231 │         emit PerpFeesForwarded(to, msg.value);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900LockedPool.sol:106:19\n    │\n106 │     function open(address token_, uint160 sqrtPriceX96, uint256 amount) external nonReentrant returns (uint128 liq) {\n    │                   ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:246:27\n    │\n246 │                 keccak256(abi.encodePacked(type(IMD6900).creationCode, abi.encode(address(this))))\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900LockedPool.sol:126:9\n    │\n126 │         emit Opened(start, upper, used, liq);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:254:26\n    │\n254 │         return keccak256(abi.encodePacked(type(IMD6900).creationCode, abi.encode(address(this))));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/IMD6900LockedPool.sol:118:9\n    │\n118 │         poolManager.initialize(poolKey(), start);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:122:15\n    │\n122 │         liq = uint128(l - 1);\n    │               ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:284:9\n    │\n284 │         emit ClaimsSet(root, total, deadline);\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/IMD6900RobinhoodLaunch.sol:293:9\n    │\n293 │         SafeTransferLib.safeTransferETH(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:289:73\n    │\n289 │     function withdrawEth(address to, uint256 amount) external onlyOwner nonReentrant {\n    │                                                                         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:294:9\n    │\n294 │         emit EthWithdrawn(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:307:9\n    │\n307 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900LockedPool.sol:246:9\n    │\n246 │         emit Collected(eth, tokens);\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/IMD6900RobinhoodLaunch.sol:311:32\n    │\n311 │         if (claimTotal != 0 && claimDeadline < block.timestamp + MIN_CLAIM_WINDOW) revert BadClaims();\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/IMD6900LockedPool.sol:148:9\n    │\n148 │         emit FeeLowered(from, newFee, poolKey().toId(), liq);\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/IMD6900LockedPool.sol:142:9\n    │\n142 │         poolManager.initialize(poolKey(), sqrtPriceX96); // the hook serves the new pool from here on\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/IMD6900RobinhoodLaunch.sol:332:9\n    │\n332 │         emit Launched(t, sqrtPriceX96, pool.upperTick(), spend, bought, excess, excessTo);\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/IMD6900RobinhoodLaunch.sol:318:9\n    │\n318 │         pool.open(t, sqrtPriceX96, SUPPLY);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:336:62\n    │\n336 │     function releaseUnclaimed(address to) external onlyOwner nonReentrant {\n    │                                                              ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:338:13\n    │\n338 │         if (block.timestamp < claimDeadline) revert ClaimsOpen();\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/IMD6900RobinhoodLaunch.sol:346:9\n    │\n346 │         emit UnclaimedReleased(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:350:75\n    │\n350 │     function releaseImdstr(uint256 amount, address to) external onlyOwner nonReentrant {\n    │                                                                           ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:355:9\n    │\n355 │         emit ImdstrReleased(to, amount);\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/IMD6900Hook.sol:230:43\n    │\n230 │               (bool ok, bytes memory out) = engine.call{gas: g - reserve}(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n231 │ ┃                 abi.encodeCall(IPerpSweep.sweepLiquidations, (tx.origin, spec, isBuy, limit))\n232 │ ┃             );\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/return-bomb\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:193:23\n    │\n193 │         uint256 eth = uint256(int256(out.amount0() - earned.amount0()));\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/IMD6900LockedPool.sol:194:25\n    │\n194 │         uint256 coins = uint256(int256(out.amount1() - earned.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[unused-return]: return value of an external call is not used\n    ╭▸ src/IMD6900LockedPool.sol:211:21\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:211:47\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\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/IMD6900LockedPool.sol:211:55\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\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/IMD6900LockedPool.sol:212:73\n    │\n212 │         else if (a0 > 0) poolManager.take(key.currency0, address(this), uint256(uint128(a0)));\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/IMD6900LockedPool.sol:212:81\n    │\n212 │         else if (a0 > 0) poolManager.take(key.currency0, address(this), uint256(uint128(a0)));\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/IMD6900LockedPool.sol:215:71\n    │\n215 │             SafeTransferLib.safeTransfer(token, address(poolManager), uint256(uint128(-a1)));\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/IMD6900LockedPool.sol:215:79\n    │\n215 │             SafeTransferLib.safeTransfer(token, address(poolManager), uint256(uint128(-a1)));\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/IMD6900LockedPool.sol:216:13\n    │\n216 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:218:60\n    │\n218 │             poolManager.take(key.currency1, address(this), uint256(uint128(a1)));\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/IMD6900LockedPool.sol:218:68\n    │\n218 │             poolManager.take(key.currency1, address(this), uint256(uint128(a1)));\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/IMD6900LockedPool.sol:237:60\n    │\n237 │         return l > type(uint128).max ? type(uint128).max : uint128(l);\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/IMD6900LockedPool.sol:250:24\n    │\n250 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:250:32\n    │\n250 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:250:47\n    │\n250 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:250:55\n    │\n250 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\n    │                                                       ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:370:13\n    │\n370 │             sum += bps[i];\n    │             ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:368:38\n    │\n368 │             if (to[i] == address(0)) revert BadPayees();\n    │                                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:373:23\n    │\n373 │         _payeeCount = uint8(n);\n    │                       ━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:387:9\n    │\n387 │         emit EthRecovered(to, 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/IMD6900RobinhoodLaunch.sol:399:9\n    │\n399 │         emit ExcessReleased(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `excessHeld` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:424:34\n    │\n424 │         (eth, sold) = abi.decode(poolManager.unlock(abi.encode(uint8(2), amount)), (uint256, uint256));\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/IMD6900RobinhoodLaunch.sol:428:9\n    │\n428 │         emit HeldSold(sold, eth, to);\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/IMD6900RobinhoodLaunch.sol:472:9\n    │\n472 │         emit Claimed(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 `owed` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:574:22\n    │\n574 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:574:22\n    │\n574 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:578:13\n    │\n578 │             emit PayFailed(payee, part);\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/IMD6900RobinhoodLaunch.sol:498:9\n    │\n498 │         emit Split(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/IMD6900RobinhoodLaunch.sol:507:9\n    │\n507 │         SafeTransferLib.safeTransferETH(payee, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-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/IMD6900RobinhoodLaunch.sol:508:9\n    │\n508 │         emit OwedPaid(payee, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `imdBucket` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:522:35\n    │\n522 │         (imd, ethIn) = abi.decode(poolManager.unlock(abi.encode(uint8(1), spend)), (uint256, uint256));\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/IMD6900RobinhoodLaunch.sol:518:26\n    │\n518 │         if (last != 0 && block.timestamp < last + IMD_BUY_GAP) revert TooSoon();\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/IMD6900RobinhoodLaunch.sol:528:9\n    │\n528 │         emit ImdBurned(ethIn, imd);\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/IMD6900RobinhoodLaunch.sol:538:9\n    │\n538 │         pool.collect();\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:552:69\n    │\n552 │             BalanceDelta s = poolManager.swap(k, SwapParams(false, -int256(spend), TickMath.MAX_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/IMD6900RobinhoodLaunch.sol:553:28\n    │\n553 │             uint256 sold = uint256(uint128(-s.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/IMD6900RobinhoodLaunch.sol:553:36\n    │\n553 │             uint256 sold = uint256(uint128(-s.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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:554:30\n    │\n554 │             uint256 ethOut = uint256(uint128(s.amount0()));\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/IMD6900RobinhoodLaunch.sol:554:38\n    │\n554 │             uint256 ethOut = uint256(uint128(s.amount0()));\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/IMD6900RobinhoodLaunch.sol:557:13\n    │\n557 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:562:66\n    │\n562 │         BalanceDelta d = poolManager.swap(key, SwapParams(true, -int256(spend), 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/IMD6900RobinhoodLaunch.sol:563:25\n    │\n563 │         uint256 ethIn = uint256(uint128(-d.amount0()));\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/IMD6900RobinhoodLaunch.sol:563:33\n    │\n563 │         uint256 ethIn = uint256(uint128(-d.amount0()));\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/IMD6900RobinhoodLaunch.sol:564:23\n    │\n564 │         uint256 out = uint256(uint128(d.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/IMD6900RobinhoodLaunch.sol:564:31\n    │\n564 │         uint256 out = uint256(uint128(d.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/IMD6900RobinhoodLaunch.sol:565:9\n    │\n565 │         poolManager.settle{value: ethIn}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:574:22\n    │\n574 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:583:16\n    │\n583 │         return abi.encodePacked(type(IMD6900Hook).creationCode, abi.encode(poolManager, address(pool), timelock));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\n","passed":true},{"durationMs":264,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[PASS] test_TheWholeLaunchOnRobinhood() (gas: 2554)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 318.53µs (32.42µs CPU time)\n\nRan 49 tests for test/IMD6900RobinhoodLaunch.t.sol:IMD6900RobinhoodLaunchTest\n[PASS] test_ARefusedShareIsKeptForItsPayee() (gas: 3525788)\n[PASS] test_ARefusedShareIsPaidOnRetry() (gas: 1764817)\n[PASS] test_BurnsImdThroughItsExecutor() (gas: 2965075)\n[PASS] test_ClaimsCantChangeAfterTheLaunch() (gas: 1256947)\n[PASS] test_ClaimsNeedTheSnapshot() (gas: 1458855)\n[PASS] test_ClaimsNeverPassTheReserve() (gas: 1809422)\n[PASS] test_ClaimsOneForOne() (gas: 1581495)\n[PASS] test_ClaimsStayOpenNinetyDays() (gas: 208717)\n[PASS] test_DeploysOnAFreshChain() (gas: 1985555)\n[PASS] test_EachDeployerMinesItsOwnHook() (gas: 36172616)\n[PASS] test_EndingTheClaimsBringsMostOfTheBuyBack() (gas: 2138647)\nLogs:\n  back of the 2.81 ETH buy (C's claim kept by C): 2.631273447654979998\n\n[PASS] test_EthComesBackBeforeTheLaunchOnly() (gas: 1298217)\n[PASS] test_EthSentToThePoolIsForwarded() (gas: 1438923)\n[PASS] test_FitsOneTransaction() (gas: 36151292)\nLogs:\n  launch deploy gas (the same for every deployer: no mining): 8088\n  createHook gas: 1161390\n\n[PASS] test_HarvestSplitsTheEthAndBurnsTheCoin() (gas: 1840609)\nLogs:\n  pot bridge: 0.511887600000000000\n\n[PASS] test_KeepsTheExcessForTheTimelock() (gas: 1428185)\n[PASS] test_LaunchesAtEthereumsPrice() (gas: 1306261)\nLogs:\n  opening buy, coins: 122347490.873046793208740802\n  market cap after the buy, ETH on 1B: 25.007184312087223619\n\n[PASS] test_LaunchesOnce() (gas: 1297827)\n[PASS] test_LowerFeeMovesTheLiquidity() (gas: 2183455)\n[PASS] test_NoClaimsAllOfItBack() (gas: 1770289)\nLogs:\n  back of the 2.81 ETH buy, nobody claimed: 2.704790817050839653\n\n[PASS] test_OnlyTheOwnerLaunches() (gas: 178941)\n[PASS] test_OnlyTheTimelockLowersTheFeeAndOnlyDown() (gas: 1955717)\n[PASS] test_OnlyTheTimelockRecoversEth() (gas: 1298348)\n[PASS] test_OnlyTheTimelockRepointsImdsPool() (gas: 68121)\n[PASS] test_OnlyTheTimelockRepointsTheBurnExecutor() (gas: 91713)\n[PASS] test_OnlyTheTimelockRunsTheHook() (gas: 667778)\n[PASS] test_OnlyTheTimelockSetsTheSplit() (gas: 1793753)\n[PASS] test_PassesTheAdmissionScan() (gas: 13850462)\n[PASS] test_PerpFeesGoAllToThePot() (gas: 1449197)\n[PASS] test_PerpFeesRefusedAreOwed() (gas: 1565400)\n[PASS] test_RefusesAnAddressThatIsnt0x6900() (gas: 702428)\n[PASS] test_RefusesTheWrongAddress() (gas: 709897)\n[PASS] test_SendsTheExcessWhereTold() (gas: 1272855)\n[PASS] test_TheBucketIsChargedWhatTheBuyTook() (gas: 3543321)\nLogs:\n  the thin range took, ETH: 0.000158651623865091\n\n[PASS] test_TheBuyMustCoverTheClaims() (gas: 1214473)\n[PASS] test_TheCoinIsAt0x6900() (gas: 1231408)\nLogs:\n  the coin: 0x6900d11750039d7Ef0f8262b5a62152BB906B8E8\n\n[PASS] test_TheCoinIsPlain() (gas: 1294790)\n[PASS] test_TheFeeIsStatic() (gas: 1572960)\n[PASS] test_TheHookIsCreatedOnceAtAFittingSalt() (gas: 187929720)\n[PASS] test_TheHookRefusesAnEngineThatCantAnswer() (gas: 2451560)\n[PASS] test_TheHooksAddressAndPermissions() (gas: 21800)\n[PASS] test_TheImdBurnCantBeSandwiched() (gas: 3766615)\nLogs:\n  the searcher's loss: 0.580539316843367753\n\n[PASS] test_TheLaunchOwnerCantRenounce() (gas: 70390)\n[PASS] test_TheLiquidityIsLocked() (gas: 2147433)\n[PASS] test_ThePerpsEngineTradesThroughTheHook() (gas: 2308414)\n[PASS] test_TheTimelockCanRecoverARefusedShare() (gas: 1723479)\n[PASS] test_TheTimelockSellsTheExcessBack() (gas: 1648906)\nLogs:\n  the excess alone, sold back: ETH: 1.312390213060687826\n\n[PASS] test_TheUnclaimedRestAfterTheDeadline() (gas: 1648928)\n[PASS] test_TradesBothWays() (gas: 1553220)\nSuite result: ok. 49 passed; 0 failed; 0 skipped; finished in 154.89ms (216.74ms CPU time)\n\nRan 2 test suites in 156.06ms (155.21ms CPU time): 50 tests passed, 0 failed, 0 skipped (50 total tests)\n","passed":true},{"durationMs":67,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMD6900.approve(address,uint256)\",\"IMD6900.burn(uint256)\",\"IMD6900.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"IMD6900.transfer(address,uint256)\",\"IMD6900.transferFrom(address,address,uint256)\",\"IMD6900Hook.afterAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"IMD6900Hook.afterDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"IMD6900Hook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMD6900Hook.afterRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"IMD6900Hook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMD6900Hook.beforeAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"IMD6900Hook.beforeDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"IMD6900Hook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"IMD6900Hook.beforeRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"IMD6900Hook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMD6900Hook.cancelOwnershipHandover()\",\"IMD6900Hook.completeOwnershipHandover(address)\",\"IMD6900Hook.renounceOwnership()\",\"IMD6900Hook.requestOwnershipHandover()\",\"IMD6900Hook.setPerpEngine(address)\",\"IMD6900Hook.setSweepFailOpen(bool)\",\"IMD6900Hook.transferOwnership(address)\",\"IMD6900LockedPool.collect()\",\"IMD6900LockedPool.lowerFee(uint24)\",\"IMD6900LockedPool.open(address,uint160,uint256)\",\"IMD6900LockedPool.receive()\",\"IMD6900LockedPool.setHook(address)\",\"IMD6900LockedPool.unlockCallback(bytes)\",\"IMD6900RobinhoodLaunch.addFees()\",\"IMD6900RobinhoodLaunch.addPerpFees()\",\"IMD6900RobinhoodLaunch.burnImd(uint256)\",\"IMD6900RobinhoodLaunch.cancelOwnershipHandover()\",\"IMD6900RobinhoodLaunch.claim(uint256,uint256,bytes32[])\",\"IMD6900RobinhoodLaunch.completeOwnershipHandover(address)\",\"IMD6900RobinhoodLaunch.createHook(bytes32)\",\"IMD6900RobinhoodLaunch.endClaims()\",\"IMD6900RobinhoodLaunch.harvest()\",\"IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address)\",\"IMD6900RobinhoodLaunch.payOwed(address)\",\"IMD6900RobinhoodLaunch.receive()\",\"IMD6900RobinhoodLaunch.recoverEth(address,uint256)\",\"IMD6900RobinhoodLaunch.releaseExcess(uint256,address)\",\"IMD6900RobinhoodLaunch.releaseImdstr(uint256,address)\",\"IMD6900RobinhoodLaunch.releaseUnclaimed(address)\",\"IMD6900RobinhoodLaunch.renounceOwnership()\",\"IMD6900RobinhoodLaunch.requestOwnershipHandover()\",\"IMD6900RobinhoodLaunch.sellHeld(uint256,uint256,address)\",\"IMD6900RobinhoodLaunch.setClaims(bytes32,uint256,uint256)\",\"IMD6900RobinhoodLaunch.setImdBurn(address)\",\"IMD6900RobinhoodLaunch.setImdPool(uint24,int24,address)\",\"IMD6900RobinhoodLaunch.setPayees(address[],uint16[])\",\"IMD6900RobinhoodLaunch.setPerpFeeTo(address)\",\"IMD6900RobinhoodLaunch.split()\",\"IMD6900RobinhoodLaunch.transferOwnership(address)\",\"IMD6900RobinhoodLaunch.unlockCallback(bytes)\",\"IMD6900RobinhoodLaunch.withdrawEth(address,uint256)\"],\"files\":{\".gitignore\":14,\"ADAPTATION.md\":185,\"README.md\":117,\"foundry.toml\":25,\"jobs/swarm-launch.json\":27,\"launch.json\":15,\"script/claims.py\":88,\"script/launch.sh\":222,\"script/mine.sh\":12,\"script/params.py\":64,\"script/programmable_plan.py\":205,\"snapshots/rh-community-2026-10-09.json\":56,\"src/IMD6900.sol\":29,\"src/IMD6900Hook.sol\":252,\"src/IMD6900LockedPool.sol\":252,\"src/IMD6900RobinhoodLaunch.sol\":595,\"test/HookSalt.sol\":35,\"test/IMD6900RobinhoodLaunch.t.sol\":1202,\"test/RobinhoodFork.t.sol\":217},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"25ba2f8b7a4fe4888c39ff54887fd9764547ce06cf18903101ca9642139bc89a","verifiedTreeHash":"382fd6c5b144bb6ec7abd387edf13361b084747b","verifierVersion":"0.1.0+327e1e9f"},{"checks":[{"durationMs":25873,"exitCode":0,"name":"build","output":"Compiling 83 files with Solc 0.8.26\nSolc 0.8.26 finished in 25.70s\nCompiler run successful!\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:79:57\n   │\n79 │     constructor(IPoolManager poolManager_, uint24 fee_, address launcher_, address timelock_) {\n   │                                                         ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900Hook.sol:75:44\n   │\n75 │     constructor(IPoolManager poolManager_, address pool_, address owner_) BaseHook(poolManager_) {\n   │                                            ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:79:76\n   │\n79 │     constructor(IPoolManager poolManager_, uint24 fee_, address launcher_, address timelock_) {\n   │                                                                            ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-events-access-control]: `claimRoot` is changed without an event but is used for access control\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:256:9\n    │\n256 │         (claimRoot, claimTotal, claimDeadline) = (root, total, deadline);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900Hook.sol:111:28\n    │\n111 │     function setPerpEngine(address engine) external onlyOwner {\n    │                            ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900Hook.sol:118:9\n    │\n118 │         emit PerpEngineSet(engine);\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/IMD6900Hook.sol:123:9\n    │\n123 │         emit SweepFailOpenSet(on);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:96:22\n   │\n96 │     function setHook(address hook_) external {\n   │                      ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:167:60\n    │\n167 │     constructor(address owner_, IPoolManager poolManager_, address imdstr_, address timelock_) {\n    │                                                            ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:167:77\n    │\n167 │     constructor(address owner_, IPoolManager poolManager_, address imdstr_, address timelock_) {\n    │                                                                             ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-no-eth]: external call can be reentered before `hook` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:189:9\n    │\n189 │         pool.setHook(h);\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/IMD6900RobinhoodLaunch.sol:192:9\n    │\n192 │         emit HookCreated(h, salt);\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/IMD6900RobinhoodLaunch.sol:213:9\n    │\n213 │         SafeTransferLib.safeTransferETH(to, msg.value);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-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/IMD6900RobinhoodLaunch.sol:214:9\n    │\n214 │         emit PerpFeesForwarded(to, msg.value);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:221:62\n    │\n221 │                 bytes1(0xff), address(this), salt, keccak256(abi.encodePacked(type(IMD6900).creationCode, abi.encode(address(this))))\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900LockedPool.sol:106:19\n    │\n106 │     function open(address token_, uint160 sqrtPriceX96, uint256 amount) external nonReentrant returns (uint128 liq) {\n    │                   ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:229:26\n    │\n229 │         return keccak256(abi.encodePacked(type(IMD6900).creationCode, abi.encode(address(this))));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900LockedPool.sol:126:9\n    │\n126 │         emit Opened(start, upper, used, liq);\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/IMD6900LockedPool.sol:118:9\n    │\n118 │         poolManager.initialize(poolKey(), start);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:122:15\n    │\n122 │         liq = uint128(l - 1);\n    │               ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:257:9\n    │\n257 │         emit ClaimsSet(root, total, deadline);\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/IMD6900RobinhoodLaunch.sol:266:9\n    │\n266 │         SafeTransferLib.safeTransferETH(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:262:73\n    │\n262 │     function withdrawEth(address to, uint256 amount) external onlyOwner nonReentrant {\n    │                                                                         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:267:9\n    │\n267 │         emit EthWithdrawn(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:280:9\n    │\n280 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:284:32\n    │\n284 │         if (claimTotal != 0 && claimDeadline < block.timestamp + MIN_CLAIM_WINDOW) revert BadClaims();\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/IMD6900LockedPool.sol:243:9\n    │\n243 │         emit Collected(eth, tokens);\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/IMD6900LockedPool.sol:148:9\n    │\n148 │         emit FeeLowered(from, newFee, poolKey().toId(), liq);\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/IMD6900RobinhoodLaunch.sol:305:9\n    │\n305 │         emit Launched(t, sqrtPriceX96, pool.upperTick(), spend, bought, excess, excessTo);\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/IMD6900LockedPool.sol:142:9\n    │\n142 │         poolManager.initialize(poolKey(), sqrtPriceX96); // the hook serves the new pool from here on\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:291:9\n    │\n291 │         pool.open(t, sqrtPriceX96, SUPPLY);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:309:62\n    │\n309 │     function releaseUnclaimed(address to) external onlyOwner nonReentrant {\n    │                                                              ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:311:13\n    │\n311 │         if (block.timestamp < claimDeadline) revert ClaimsOpen();\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/IMD6900RobinhoodLaunch.sol:319:9\n    │\n319 │         emit UnclaimedReleased(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:323:75\n    │\n323 │     function releaseImdstr(uint256 amount, address to) external onlyOwner nonReentrant {\n    │                                                                           ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:328:9\n    │\n328 │         emit ImdstrReleased(to, amount);\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/IMD6900Hook.sol:209:17\n    │\n209 │                 engine.call{gas: g - reserve}(abi.encodeCall(IPerpSweep.sweepLiquidations, (tx.origin, spec, isBuy, limit)));\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/return-bomb\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:193:23\n    │\n193 │         uint256 eth = uint256(int256(out.amount0() - earned.amount0()));\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/IMD6900LockedPool.sol:194:25\n    │\n194 │         uint256 coins = uint256(int256(out.amount1() - earned.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[unused-return]: return value of an external call is not used\n    ╭▸ src/IMD6900LockedPool.sol:211:21\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:211:47\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\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/IMD6900LockedPool.sol:211:55\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\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/IMD6900LockedPool.sol:212:73\n    │\n212 │         else if (a0 > 0) poolManager.take(key.currency0, address(this), uint256(uint128(a0)));\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/IMD6900RobinhoodLaunch.sol:343:13\n    │\n343 │             sum += bps[i];\n    │             ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:212:81\n    │\n212 │         else if (a0 > 0) poolManager.take(key.currency0, address(this), uint256(uint128(a0)));\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/IMD6900LockedPool.sol:215:71\n    │\n215 │             SafeTransferLib.safeTransfer(token, address(poolManager), uint256(uint128(-a1)));\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/IMD6900LockedPool.sol:215:79\n    │\n215 │             SafeTransferLib.safeTransfer(token, address(poolManager), uint256(uint128(-a1)));\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/IMD6900LockedPool.sol:216:13\n    │\n216 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:341:38\n    │\n341 │             if (to[i] == address(0)) revert BadPayees();\n    │                                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:218:60\n    │\n218 │             poolManager.take(key.currency1, address(this), uint256(uint128(a1)));\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/IMD6900RobinhoodLaunch.sol:346:23\n    │\n346 │         _payeeCount = uint8(n);\n    │                       ━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:218:68\n    │\n218 │             poolManager.take(key.currency1, address(this), uint256(uint128(a1)));\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/IMD6900LockedPool.sol:237:60\n    │\n237 │         return l > type(uint128).max ? type(uint128).max : uint128(l);\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/IMD6900LockedPool.sol:247:24\n    │\n247 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:247:32\n    │\n247 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:247:47\n    │\n247 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:247:55\n    │\n247 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\n    │                                                       ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:360:9\n    │\n360 │         emit EthRecovered(to, 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/IMD6900RobinhoodLaunch.sol:372:9\n    │\n372 │         emit ExcessReleased(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `excessHeld` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:397:34\n    │\n397 │         (eth, sold) = abi.decode(poolManager.unlock(abi.encode(uint8(2), amount)), (uint256, uint256));\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/IMD6900RobinhoodLaunch.sol:401:9\n    │\n401 │         emit HeldSold(sold, eth, to);\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/IMD6900RobinhoodLaunch.sol:445:9\n    │\n445 │         emit Claimed(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 `owed` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:539:22\n    │\n539 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:539:22\n    │\n539 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:543:13\n    │\n543 │             emit PayFailed(payee, part);\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/IMD6900RobinhoodLaunch.sol:471:9\n    │\n471 │         emit Split(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/IMD6900RobinhoodLaunch.sol:480:9\n    │\n480 │         SafeTransferLib.safeTransferETH(payee, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-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/IMD6900RobinhoodLaunch.sol:481:9\n    │\n481 │         emit OwedPaid(payee, 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/IMD6900RobinhoodLaunch.sol:495:9\n    │\n495 │         emit ImdBurned(spend, imd);\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/IMD6900RobinhoodLaunch.sol:504:9\n    │\n504 │         pool.collect();\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:517:69\n    │\n517 │             BalanceDelta s = poolManager.swap(k, SwapParams(false, -int256(spend), TickMath.MAX_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/IMD6900RobinhoodLaunch.sol:518:28\n    │\n518 │             uint256 sold = uint256(uint128(-s.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/IMD6900RobinhoodLaunch.sol:518:36\n    │\n518 │             uint256 sold = uint256(uint128(-s.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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:519:30\n    │\n519 │             uint256 ethOut = uint256(uint128(s.amount0()));\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/IMD6900RobinhoodLaunch.sol:519:38\n    │\n519 │             uint256 ethOut = uint256(uint128(s.amount0()));\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/IMD6900RobinhoodLaunch.sol:522:13\n    │\n522 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:527:66\n    │\n527 │         BalanceDelta d = poolManager.swap(key, SwapParams(true, -int256(spend), 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/IMD6900RobinhoodLaunch.sol:528:25\n    │\n528 │         uint256 ethIn = uint256(uint128(-d.amount0()));\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/IMD6900RobinhoodLaunch.sol:528:33\n    │\n528 │         uint256 ethIn = uint256(uint128(-d.amount0()));\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/IMD6900RobinhoodLaunch.sol:529:23\n    │\n529 │         uint256 out = uint256(uint128(d.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/IMD6900RobinhoodLaunch.sol:529:31\n    │\n529 │         uint256 out = uint256(uint128(d.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/IMD6900RobinhoodLaunch.sol:530:9\n    │\n530 │         poolManager.settle{value: ethIn}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:539:22\n    │\n539 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:548:16\n    │\n548 │         return abi.encodePacked(type(IMD6900Hook).creationCode, abi.encode(poolManager, address(pool), timelock));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\n","passed":true},{"durationMs":232,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[PASS] test_TheWholeLaunchOnRobinhood() (gas: 2554)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 333.91µs (28.98µs CPU time)\n\nRan 44 tests for test/IMD6900RobinhoodLaunch.t.sol:IMD6900RobinhoodLaunchTest\n[PASS] test_ARefusedShareIsKeptForItsPayee() (gas: 3522670)\n[PASS] test_ARefusedShareIsPaidOnRetry() (gas: 1764413)\n[PASS] test_BurnsImdThroughItsExecutor() (gas: 2944346)\n[PASS] test_ClaimsCantChangeAfterTheLaunch() (gas: 1256703)\n[PASS] test_ClaimsNeedTheSnapshot() (gas: 1458611)\n[PASS] test_ClaimsNeverPassTheReserve() (gas: 1809178)\n[PASS] test_ClaimsOneForOne() (gas: 1581295)\n[PASS] test_ClaimsStayOpenNinetyDays() (gas: 208607)\n[PASS] test_DeploysOnAFreshChain() (gas: 62991761)\n[PASS] test_EachDeployerMinesItsOwnHook() (gas: 5485771)\n[PASS] test_EndingTheClaimsBringsMostOfTheBuyBack() (gas: 2138352)\nLogs:\n  back of the 2.81 ETH buy (C's claim kept by C): 2.631273447654979998\n\n[PASS] test_EthComesBackBeforeTheLaunchOnly() (gas: 1297973)\n[PASS] test_FitsOneTransaction() (gas: 5464447)\nLogs:\n  launch deploy gas (the same for every deployer: no mining): 8088\n  createHook gas: 1153722\n\n[PASS] test_HarvestSplitsTheEthAndBurnsTheCoin() (gas: 1840273)\nLogs:\n  pot bridge: 0.511887600000000000\n\n[PASS] test_KeepsTheExcessForTheTimelock() (gas: 1427941)\n[PASS] test_LaunchesAtEthereumsPrice() (gas: 1305939)\nLogs:\n  opening buy, coins: 122347490.873046793208740802\n  market cap after the buy, ETH on 1B: 25.007184312087223619\n\n[PASS] test_LaunchesOnce() (gas: 1297627)\n[PASS] test_LowerFeeMovesTheLiquidity() (gas: 2183173)\n[PASS] test_NoClaimsAllOfItBack() (gas: 1769994)\nLogs:\n  back of the 2.81 ETH buy, nobody claimed: 2.704790817050839653\n\n[PASS] test_OnlyTheOwnerLaunches() (gas: 178900)\n[PASS] test_OnlyTheTimelockLowersTheFeeAndOnlyDown() (gas: 1955468)\n[PASS] test_OnlyTheTimelockRecoversEth() (gas: 1298104)\n[PASS] test_OnlyTheTimelockRepointsImdsPool() (gas: 67989)\n[PASS] test_OnlyTheTimelockRepointsTheBurnExecutor() (gas: 91539)\n[PASS] test_OnlyTheTimelockRunsTheHook() (gas: 667668)\n[PASS] test_OnlyTheTimelockSetsTheSplit() (gas: 1793483)\n[PASS] test_PassesTheAdmissionScan() (gas: 13671277)\n[PASS] test_PerpFeesGoAllToThePot() (gas: 1448831)\n[PASS] test_RefusesAnAddressThatIsnt0x6900() (gas: 702318)\n[PASS] test_RefusesTheWrongAddress() (gas: 709787)\n[PASS] test_SendsTheExcessWhereTold() (gas: 1272655)\n[PASS] test_TheBuyMustCoverTheClaims() (gas: 1214259)\n[PASS] test_TheCoinIsAt0x6900() (gas: 1231164)\nLogs:\n  the coin: 0x6900d11750039d7Ef0f8262b5a62152BB906B8E8\n\n[PASS] test_TheCoinIsPlain() (gas: 1294546)\n[PASS] test_TheFeeIsStatic() (gas: 1572689)\n[PASS] test_TheHookIsCreatedOnceAtAFittingSalt() (gas: 6522789)\n[PASS] test_TheHookRefusesAnEngineThatCantAnswer() (gas: 2451301)\n[PASS] test_TheHooksAddressAndPermissions() (gas: 21734)\n[PASS] test_TheLiquidityIsLocked() (gas: 2147229)\n[PASS] test_ThePerpsEngineTradesThroughTheHook() (gas: 2308147)\n[PASS] test_TheTimelockCanRecoverARefusedShare() (gas: 1722913)\n[PASS] test_TheTimelockSellsTheExcessBack() (gas: 1648690)\nLogs:\n  the excess alone, sold back: ETH: 1.312390213060687826\n\n[PASS] test_TheUnclaimedRestAfterTheDeadline() (gas: 1648640)\n[PASS] test_TradesBothWays() (gas: 1552971)\nSuite result: ok. 44 passed; 0 failed; 0 skipped; finished in 115.00ms (129.56ms CPU time)\n\nRan 2 test suites in 116.72ms (115.33ms CPU time): 45 tests passed, 0 failed, 0 skipped (45 total tests)\n","passed":true},{"durationMs":61,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMD6900.approve(address,uint256)\",\"IMD6900.burn(uint256)\",\"IMD6900.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"IMD6900.transfer(address,uint256)\",\"IMD6900.transferFrom(address,address,uint256)\",\"IMD6900Hook.afterAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"IMD6900Hook.afterDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"IMD6900Hook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMD6900Hook.afterRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"IMD6900Hook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMD6900Hook.beforeAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"IMD6900Hook.beforeDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"IMD6900Hook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"IMD6900Hook.beforeRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"IMD6900Hook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMD6900Hook.cancelOwnershipHandover()\",\"IMD6900Hook.completeOwnershipHandover(address)\",\"IMD6900Hook.renounceOwnership()\",\"IMD6900Hook.requestOwnershipHandover()\",\"IMD6900Hook.setPerpEngine(address)\",\"IMD6900Hook.setSweepFailOpen(bool)\",\"IMD6900Hook.transferOwnership(address)\",\"IMD6900LockedPool.collect()\",\"IMD6900LockedPool.lowerFee(uint24)\",\"IMD6900LockedPool.open(address,uint160,uint256)\",\"IMD6900LockedPool.receive()\",\"IMD6900LockedPool.setHook(address)\",\"IMD6900LockedPool.unlockCallback(bytes)\",\"IMD6900RobinhoodLaunch.addFees()\",\"IMD6900RobinhoodLaunch.addPerpFees()\",\"IMD6900RobinhoodLaunch.burnImd(uint256)\",\"IMD6900RobinhoodLaunch.cancelOwnershipHandover()\",\"IMD6900RobinhoodLaunch.claim(uint256,uint256,bytes32[])\",\"IMD6900RobinhoodLaunch.completeOwnershipHandover(address)\",\"IMD6900RobinhoodLaunch.createHook(bytes32)\",\"IMD6900RobinhoodLaunch.endClaims()\",\"IMD6900RobinhoodLaunch.harvest()\",\"IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address)\",\"IMD6900RobinhoodLaunch.payOwed(address)\",\"IMD6900RobinhoodLaunch.receive()\",\"IMD6900RobinhoodLaunch.recoverEth(address,uint256)\",\"IMD6900RobinhoodLaunch.releaseExcess(uint256,address)\",\"IMD6900RobinhoodLaunch.releaseImdstr(uint256,address)\",\"IMD6900RobinhoodLaunch.releaseUnclaimed(address)\",\"IMD6900RobinhoodLaunch.renounceOwnership()\",\"IMD6900RobinhoodLaunch.requestOwnershipHandover()\",\"IMD6900RobinhoodLaunch.sellHeld(uint256,uint256,address)\",\"IMD6900RobinhoodLaunch.setClaims(bytes32,uint256,uint256)\",\"IMD6900RobinhoodLaunch.setImdBurn(address)\",\"IMD6900RobinhoodLaunch.setImdPool(uint24,int24,address)\",\"IMD6900RobinhoodLaunch.setPayees(address[],uint16[])\",\"IMD6900RobinhoodLaunch.setPerpFeeTo(address)\",\"IMD6900RobinhoodLaunch.split()\",\"IMD6900RobinhoodLaunch.transferOwnership(address)\",\"IMD6900RobinhoodLaunch.unlockCallback(bytes)\",\"IMD6900RobinhoodLaunch.withdrawEth(address,uint256)\"],\"files\":{\".gitignore\":14,\"ADAPTATION.md\":108,\"README.md\":114,\"foundry.toml\":25,\"jobs/swarm-launch.json\":27,\"launch.json\":15,\"script/claims.py\":88,\"script/launch.sh\":222,\"script/mine.sh\":12,\"script/params.py\":64,\"script/programmable_plan.py\":205,\"snapshots/rh-community-2026-10-09.json\":56,\"src/IMD6900.sol\":29,\"src/IMD6900Hook.sol\":229,\"src/IMD6900LockedPool.sol\":249,\"src/IMD6900RobinhoodLaunch.sol\":560,\"test/HookSalt.sol\":35,\"test/IMD6900RobinhoodLaunch.t.sol\":953,\"test/RobinhoodFork.t.sol\":217},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"6a0bc1761a71cc23332005924990b1fdbef91184fe712d8e3e6edc8a6e530755","verifiedTreeHash":"16cf0ae0f27c8fa657b6b7cbc7e474046f5e0630","verifierVersion":"0.1.0+327e1e9f"},{"checks":[{"durationMs":45836,"exitCode":0,"name":"build","output":"Compiling 85 files with Solc 0.8.26\nSolc 0.8.26 finished in 45.67s\nCompiler run successful!\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900Hook.sol:76:44\n   │\n76 │     constructor(IPoolManager poolManager_, address pool_, address owner_) BaseHook(poolManager_) {\n   │                                            ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:79:76\n   │\n79 │     constructor(IPoolManager poolManager_, uint24 fee_, address launcher_, address timelock_) {\n   │                                                                            ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900Hook.sol:112:28\n    │\n112 │     function setPerpEngine(address engine) external onlyOwner {\n    │                            ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:79:57\n   │\n79 │     constructor(IPoolManager poolManager_, uint24 fee_, address launcher_, address timelock_) {\n   │                                                         ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900Hook.sol:119:9\n    │\n119 │         emit PerpEngineSet(engine);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-events-access-control]: `claimRoot` is changed without an event but is used for access control\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:283:9\n    │\n283 │         (claimRoot, claimTotal, claimDeadline) = (root, total, deadline);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900Hook.sol:124:9\n    │\n124 │         emit SweepFailOpenSet(on);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:96:22\n   │\n96 │     function setHook(address hook_) external {\n   │                      ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:181:60\n    │\n181 │     constructor(address owner_, IPoolManager poolManager_, address imdstr_, address timelock_) {\n    │                                                            ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:181:77\n    │\n181 │     constructor(address owner_, IPoolManager poolManager_, address imdstr_, address timelock_) {\n    │                                                                             ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-no-eth]: external call can be reentered before `hook` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:205:9\n    │\n205 │         pool.setHook(h);\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/IMD6900RobinhoodLaunch.sol:208:9\n    │\n208 │         emit HookCreated(h, salt);\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/IMD6900RobinhoodLaunch.sol:231:9\n    │\n231 │         emit PerpFeesForwarded(to, msg.value);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:246:27\n    │\n246 │                 keccak256(abi.encodePacked(type(IMD6900).creationCode, abi.encode(address(this))))\n    │                           ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:254:26\n    │\n254 │         return keccak256(abi.encodePacked(type(IMD6900).creationCode, abi.encode(address(this))));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:284:9\n    │\n284 │         emit ClaimsSet(root, total, deadline);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900LockedPool.sol:106:19\n    │\n106 │     function open(address token_, uint160 sqrtPriceX96, uint256 amount) external nonReentrant returns (uint128 liq) {\n    │                   ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:293:9\n    │\n293 │         SafeTransferLib.safeTransferETH(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-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/IMD6900LockedPool.sol:126:9\n    │\n126 │         emit Opened(start, upper, used, liq);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:289:73\n    │\n289 │     function withdrawEth(address to, uint256 amount) external onlyOwner nonReentrant {\n    │                                                                         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:294:9\n    │\n294 │         emit EthWithdrawn(to, 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/IMD6900LockedPool.sol:118:9\n    │\n118 │         poolManager.initialize(poolKey(), start);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:122:15\n    │\n122 │         liq = uint128(l - 1);\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[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:307:9\n    │\n307 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:311:32\n    │\n311 │         if (claimTotal != 0 && claimDeadline < block.timestamp + MIN_CLAIM_WINDOW) revert BadClaims();\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/IMD6900RobinhoodLaunch.sol:332:9\n    │\n332 │         emit Launched(t, sqrtPriceX96, pool.upperTick(), spend, bought, excess, excessTo);\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/IMD6900RobinhoodLaunch.sol:318:9\n    │\n318 │         pool.open(t, sqrtPriceX96, SUPPLY);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:336:62\n    │\n336 │     function releaseUnclaimed(address to) external onlyOwner nonReentrant {\n    │                                                              ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:338:13\n    │\n338 │         if (block.timestamp < claimDeadline) revert ClaimsOpen();\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/IMD6900RobinhoodLaunch.sol:346:9\n    │\n346 │         emit UnclaimedReleased(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:350:75\n    │\n350 │     function releaseImdstr(uint256 amount, address to) external onlyOwner nonReentrant {\n    │                                                                           ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:355:9\n    │\n355 │         emit ImdstrReleased(to, 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/IMD6900LockedPool.sol:246:9\n    │\n246 │         emit Collected(eth, tokens);\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/IMD6900LockedPool.sol:148:9\n    │\n148 │         emit FeeLowered(from, newFee, poolKey().toId(), liq);\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/IMD6900LockedPool.sol:142:9\n    │\n142 │         poolManager.initialize(poolKey(), sqrtPriceX96); // the hook serves the new pool from here on\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[return-bomb]: external call with a gas limit may copy unbounded return data\n    ╭▸ src/IMD6900Hook.sol:230:43\n    │\n230 │               (bool ok, bytes memory out) = engine.call{gas: g - reserve}(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛\n231 │ ┃                 abi.encodeCall(IPerpSweep.sweepLiquidations, (tx.origin, spec, isBuy, limit))\n232 │ ┃             );\n    │ ┗━━━━━━━━━━━━━┛\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/return-bomb\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:370:13\n    │\n370 │             sum += bps[i];\n    │             ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:368:38\n    │\n368 │             if (to[i] == address(0)) revert BadPayees();\n    │                                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:373:23\n    │\n373 │         _payeeCount = uint8(n);\n    │                       ━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:193:23\n    │\n193 │         uint256 eth = uint256(int256(out.amount0() - earned.amount0()));\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/IMD6900LockedPool.sol:194:25\n    │\n194 │         uint256 coins = uint256(int256(out.amount1() - earned.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[unused-return]: return value of an external call is not used\n    ╭▸ src/IMD6900LockedPool.sol:211:21\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:211:47\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\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/IMD6900LockedPool.sol:211:55\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\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/IMD6900LockedPool.sol:212:73\n    │\n212 │         else if (a0 > 0) poolManager.take(key.currency0, address(this), uint256(uint128(a0)));\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/IMD6900LockedPool.sol:212:81\n    │\n212 │         else if (a0 > 0) poolManager.take(key.currency0, address(this), uint256(uint128(a0)));\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/IMD6900LockedPool.sol:215:71\n    │\n215 │             SafeTransferLib.safeTransfer(token, address(poolManager), uint256(uint128(-a1)));\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/IMD6900LockedPool.sol:215:79\n    │\n215 │             SafeTransferLib.safeTransfer(token, address(poolManager), uint256(uint128(-a1)));\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/IMD6900LockedPool.sol:216:13\n    │\n216 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:218:60\n    │\n218 │             poolManager.take(key.currency1, address(this), uint256(uint128(a1)));\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/IMD6900LockedPool.sol:218:68\n    │\n218 │             poolManager.take(key.currency1, address(this), uint256(uint128(a1)));\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/IMD6900LockedPool.sol:237:60\n    │\n237 │         return l > type(uint128).max ? type(uint128).max : uint128(l);\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/IMD6900LockedPool.sol:250:24\n    │\n250 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:250:32\n    │\n250 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:250:47\n    │\n250 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:250:55\n    │\n250 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\n    │                                                       ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:387:9\n    │\n387 │         emit EthRecovered(to, 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/IMD6900RobinhoodLaunch.sol:399:9\n    │\n399 │         emit ExcessReleased(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `excessHeld` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:424:34\n    │\n424 │         (eth, sold) = abi.decode(poolManager.unlock(abi.encode(uint8(2), amount)), (uint256, uint256));\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/IMD6900RobinhoodLaunch.sol:428:9\n    │\n428 │         emit HeldSold(sold, eth, to);\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/IMD6900RobinhoodLaunch.sol:472:9\n    │\n472 │         emit Claimed(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 `owed` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:574:22\n    │\n574 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:574:22\n    │\n574 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:578:13\n    │\n578 │             emit PayFailed(payee, part);\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/IMD6900RobinhoodLaunch.sol:498:9\n    │\n498 │         emit Split(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/IMD6900RobinhoodLaunch.sol:507:9\n    │\n507 │         SafeTransferLib.safeTransferETH(payee, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-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/IMD6900RobinhoodLaunch.sol:508:9\n    │\n508 │         emit OwedPaid(payee, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `imdBucket` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:522:35\n    │\n522 │         (imd, ethIn) = abi.decode(poolManager.unlock(abi.encode(uint8(1), spend)), (uint256, uint256));\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/IMD6900RobinhoodLaunch.sol:518:26\n    │\n518 │         if (last != 0 && block.timestamp < last + IMD_BUY_GAP) revert TooSoon();\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/IMD6900RobinhoodLaunch.sol:528:9\n    │\n528 │         emit ImdBurned(ethIn, imd);\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/IMD6900RobinhoodLaunch.sol:538:9\n    │\n538 │         pool.collect();\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:552:69\n    │\n552 │             BalanceDelta s = poolManager.swap(k, SwapParams(false, -int256(spend), TickMath.MAX_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/IMD6900RobinhoodLaunch.sol:553:28\n    │\n553 │             uint256 sold = uint256(uint128(-s.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/IMD6900RobinhoodLaunch.sol:553:36\n    │\n553 │             uint256 sold = uint256(uint128(-s.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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:554:30\n    │\n554 │             uint256 ethOut = uint256(uint128(s.amount0()));\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/IMD6900RobinhoodLaunch.sol:554:38\n    │\n554 │             uint256 ethOut = uint256(uint128(s.amount0()));\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/IMD6900RobinhoodLaunch.sol:557:13\n    │\n557 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:562:66\n    │\n562 │         BalanceDelta d = poolManager.swap(key, SwapParams(true, -int256(spend), 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/IMD6900RobinhoodLaunch.sol:563:25\n    │\n563 │         uint256 ethIn = uint256(uint128(-d.amount0()));\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/IMD6900RobinhoodLaunch.sol:563:33\n    │\n563 │         uint256 ethIn = uint256(uint128(-d.amount0()));\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/IMD6900RobinhoodLaunch.sol:564:23\n    │\n564 │         uint256 out = uint256(uint128(d.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/IMD6900RobinhoodLaunch.sol:564:31\n    │\n564 │         uint256 out = uint256(uint128(d.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/IMD6900RobinhoodLaunch.sol:565:9\n    │\n565 │         poolManager.settle{value: ethIn}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:574:22\n    │\n574 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:583:16\n    │\n583 │         return abi.encodePacked(type(IMD6900Hook).creationCode, abi.encode(poolManager, address(pool), timelock));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\n","passed":true},{"durationMs":1876,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[PASS] test_TheWholeLaunchOnRobinhood() (gas: 2554)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 285.38µs (24.02µs CPU time)\n\nRan 49 tests for test/IMD6900RobinhoodLaunch.t.sol:IMD6900RobinhoodLaunchTest\n[PASS] test_ARefusedShareIsKeptForItsPayee() (gas: 3525788)\n[PASS] test_ARefusedShareIsPaidOnRetry() (gas: 1764817)\n[PASS] test_BurnsImdThroughItsExecutor() (gas: 2965075)\n[PASS] test_ClaimsCantChangeAfterTheLaunch() (gas: 1256947)\n[PASS] test_ClaimsNeedTheSnapshot() (gas: 1458855)\n[PASS] test_ClaimsNeverPassTheReserve() (gas: 1809422)\n[PASS] test_ClaimsOneForOne() (gas: 1581495)\n[PASS] test_ClaimsStayOpenNinetyDays() (gas: 208717)\n[PASS] test_DeploysOnAFreshChain() (gas: 1985555)\n[PASS] test_EachDeployerMinesItsOwnHook() (gas: 36172616)\n[PASS] test_EndingTheClaimsBringsMostOfTheBuyBack() (gas: 2138647)\nLogs:\n  back of the 2.81 ETH buy (C's claim kept by C): 2.631273447654979998\n\n[PASS] test_EthComesBackBeforeTheLaunchOnly() (gas: 1298217)\n[PASS] test_EthSentToThePoolIsForwarded() (gas: 1438923)\n[PASS] test_FitsOneTransaction() (gas: 36151292)\nLogs:\n  launch deploy gas (the same for every deployer: no mining): 8088\n  createHook gas: 1161390\n\n[PASS] test_HarvestSplitsTheEthAndBurnsTheCoin() (gas: 1840609)\nLogs:\n  pot bridge: 0.511887600000000000\n\n[PASS] test_KeepsTheExcessForTheTimelock() (gas: 1428185)\n[PASS] test_LaunchesAtEthereumsPrice() (gas: 1306261)\nLogs:\n  opening buy, coins: 122347490.873046793208740802\n  market cap after the buy, ETH on 1B: 25.007184312087223619\n\n[PASS] test_LaunchesOnce() (gas: 1297827)\n[PASS] test_LowerFeeMovesTheLiquidity() (gas: 2183455)\n[PASS] test_NoClaimsAllOfItBack() (gas: 1770289)\nLogs:\n  back of the 2.81 ETH buy, nobody claimed: 2.704790817050839653\n\n[PASS] test_OnlyTheOwnerLaunches() (gas: 178941)\n[PASS] test_OnlyTheTimelockLowersTheFeeAndOnlyDown() (gas: 1955717)\n[PASS] test_OnlyTheTimelockRecoversEth() (gas: 1298348)\n[PASS] test_OnlyTheTimelockRepointsImdsPool() (gas: 68121)\n[PASS] test_OnlyTheTimelockRepointsTheBurnExecutor() (gas: 91713)\n[PASS] test_OnlyTheTimelockRunsTheHook() (gas: 667778)\n[PASS] test_OnlyTheTimelockSetsTheSplit() (gas: 1793753)\n[PASS] test_PassesTheAdmissionScan() (gas: 13850462)\n[PASS] test_PerpFeesGoAllToThePot() (gas: 1449197)\n[PASS] test_PerpFeesRefusedAreOwed() (gas: 1565400)\n[PASS] test_RefusesAnAddressThatIsnt0x6900() (gas: 702428)\n[PASS] test_RefusesTheWrongAddress() (gas: 709897)\n[PASS] test_SendsTheExcessWhereTold() (gas: 1272855)\n[PASS] test_TheBucketIsChargedWhatTheBuyTook() (gas: 3543321)\nLogs:\n  the thin range took, ETH: 0.000158651623865091\n\n[PASS] test_TheBuyMustCoverTheClaims() (gas: 1214473)\n[PASS] test_TheCoinIsAt0x6900() (gas: 1231408)\nLogs:\n  the coin: 0x6900d11750039d7Ef0f8262b5a62152BB906B8E8\n\n[PASS] test_TheCoinIsPlain() (gas: 1294790)\n[PASS] test_TheFeeIsStatic() (gas: 1572960)\n[PASS] test_TheHookIsCreatedOnceAtAFittingSalt() (gas: 187929720)\n[PASS] test_TheHookRefusesAnEngineThatCantAnswer() (gas: 2451560)\n[PASS] test_TheHooksAddressAndPermissions() (gas: 21800)\n[PASS] test_TheImdBurnCantBeSandwiched() (gas: 3766615)\nLogs:\n  the searcher's loss: 0.580539316843367753\n\n[PASS] test_TheLaunchOwnerCantRenounce() (gas: 70390)\n[PASS] test_TheLiquidityIsLocked() (gas: 2147433)\n[PASS] test_ThePerpsEngineTradesThroughTheHook() (gas: 2308414)\n[PASS] test_TheTimelockCanRecoverARefusedShare() (gas: 1723479)\n[PASS] test_TheTimelockSellsTheExcessBack() (gas: 1648906)\nLogs:\n  the excess alone, sold back: ETH: 1.312390213060687826\n\n[PASS] test_TheUnclaimedRestAfterTheDeadline() (gas: 1648928)\n[PASS] test_TradesBothWays() (gas: 1553220)\nSuite result: ok. 49 passed; 0 failed; 0 skipped; finished in 178.53ms (263.46ms CPU time)\n\nRan 32 tests for test/IMD6900Edges.t.sol:IMD6900EdgesTest\n[PASS] testFuzz_LowerFeeAfterAnyTrades(uint256,uint256,uint256,bool) (runs: 256, μ: 2247919, ~: 2228696)\nLogs:\n  Bound result 499999999999000000024\n  Bound result 5713\n  Bound result 16800\n\n[PASS] testFuzz_OpensAtAnyReasonablePrice(uint256) (runs: 128, μ: 1249403, ~: 1244833)\nLogs:\n  Bound result 1625585934513726612920634\n\n[PASS] testFuzz_PayeeSharesMustSumToTenThousand(uint16,uint16,uint16) (runs: 512, μ: 144374, ~: 144370)\n[PASS] testFuzz_RecoverEthKeepsTheBooks(uint256,uint256) (runs: 256, μ: 1726681, ~: 1730768)\nLogs:\n  Bound result 58931\n  Bound result 6776\n\n[PASS] testFuzz_SellHeldNeverTouchesTheReserve(uint256,uint256) (runs: 256, μ: 1441604, ~: 1436521)\nLogs:\n  Bound result 58931\n  Bound result 6776\n\n[PASS] testFuzz_TheSplitIsExact(uint256) (runs: 512, μ: 1419711, ~: 1423615)\nLogs:\n  Bound result 2989\n\n[PASS] test_ARefusedShareOutlivesARepointedBurnExecutor() (gas: 1615193)\n[PASS] test_ASweepThatCantRunFailsClosedUnlessTheTimelockOpensIt() (gas: 2328485)\n[PASS] test_BurnImdHoldsItsFloor() (gas: 1417932)\n[PASS] test_ClaimEdges() (gas: 1799183)\n[PASS] test_ClaimsNeedTheImdstr() (gas: 1341407)\n[PASS] test_LaunchesWithoutClaims() (gas: 1215473)\n[PASS] test_LowerFeeWithThePriceAsHighAsItGoes() (gas: 2298784)\n[PASS] test_NobodyElseInitializesAPoolOnTheHook() (gas: 1343288)\n[PASS] test_NothingAfterTheLaunchRunsBeforeIt() (gas: 523931)\n[PASS] test_OnlyTheLauncherRunsThePool() (gas: 1397854)\n[PASS] test_OnlyThePoolManagerCallsBack() (gas: 104118)\n[PASS] test_OwedPerpFeesStayOutOfTheSplitUntilThePotTakesThem() (gas: 1731030)\n[PASS] test_PayeesMustAddUp() (gas: 716322)\n[PASS] test_PerpFeesRefusedByTheirLandingAreOwedNotLost() (gas: 630973)\n[PASS] test_RefusesAPriceOffTheRange() (gas: 1607244)\n[PASS] test_ReleaseImdstrEdges() (gas: 1657502)\n[PASS] test_ReleaseUnclaimedOnceAndWhereTold() (gas: 1438029)\n[PASS] test_SettersRefuseTheObviousMistakes() (gas: 148319)\n[PASS] test_TheClaimListNeedsBothARootAndATotal() (gas: 261297)\n[PASS] test_TheHookRefusesAnEngineThatLies() (gas: 1535621)\n[PASS] test_TheHooksCallbacksAreThePoolManagers() (gas: 1296458)\n[PASS] test_TheLaunchHoldsItsFloor() (gas: 1214296)\n[PASS] test_TheLaunchNeedsEth() (gas: 1050171)\n[PASS] test_TheLaunchNeedsTheHook() (gas: 94716)\n[PASS] test_TheLaunchOwnerCanHandOverTheHookCannotRenounce() (gas: 114914)\n[PASS] test_WithdrawEthEdges() (gas: 226515)\nSuite result: ok. 32 passed; 0 failed; 0 skipped; finished in 190.69ms (464.14ms CPU time)\n\nRan 1 test for test/IMD6900Invariants.t.sol:IMD6900InvariantTest\n[PASS]\nIMD6900InvariantTest invariants:\n[PASS] invariant_AllTheLiquidityIsTheLockedPosition\n[PASS] invariant_ClaimsAreBoundedAndOneForOne\n[PASS] invariant_EndedClaimsStayEnded\n[PASS] invariant_TheCoinsHeldAreTheReserveAndTheTimelocks\n[PASS] invariant_TheEthHeldCoversTheBucketAndTheOwed\n[PASS] invariant_TheFeeOnlyFallsAndTheHookFollows\n[PASS] invariant_TheLaunchIsFinal\n[PASS] invariant_ThePoolContractHoldsNothing\n[PASS] invariant_TheSupplyNeverRises\n IMD6900InvariantTest invariants (runs: 96, calls: 4800, reverts: 0)\n\n╭---------------+------------------+-------+---------+----------╮\n| Contract      | Selector         | Calls | Reverts | Discards |\n+===============================================================+\n| LaunchHandler | burnImd          | 246   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | buy              | 257   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | claim            | 230   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | endClaims        | 231   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | engineTrades     | 234   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | harvest          | 245   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | lowerFee         | 204   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | payOwed          | 238   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | perpFees         | 230   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | recoverEth       | 210   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | releaseExcess    | 215   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | releaseImdstr    | 250   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | releaseUnclaimed | 240   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | sell             | 215   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | sellHeld         | 224   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | sendFees         | 241   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | setEngine        | 220   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | setPayees        | 220   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | setPotRefuses    | 220   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | split            | 202   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | warp             | 228   | 0       | 0        |\n╰---------------+------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 1\n  Bound result 44400\n  Bound result 19494476263398\n  Bound result 13785\n  Bound result 11054\n  Bound result 22460\n  Bound result 2800000000000000000\n  Bound result 2\n  Bound result 7611\n  Bound result 2050770853945402535020468\n  Bound result 999999000000002001\n  Bound result 999998999999000\n  Bound result 6230170410353766\n  Bound result 32\n  Bound result 2488\n  Bound result 57470674509113232386578228\n  Bound result 652009098146765\n  Bound result 4132794462\n  Bound result 1\n  Bound result 1199999999998800000\n  Bound result 15372\n  Bound result 18\n  Bound result 999999000000001976\n  Bound result 0\n  Bound result 5975\n  Bound result 1007\n  Bound result 659918\n  Bound result 416000\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.76s (1.66s CPU time)\n\nRan 4 test suites in 1.76s (2.13s CPU time): 83 tests passed, 0 failed, 0 skipped (83 total tests)\n","passed":true},{"durationMs":73,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMD6900.approve(address,uint256)\",\"IMD6900.burn(uint256)\",\"IMD6900.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"IMD6900.transfer(address,uint256)\",\"IMD6900.transferFrom(address,address,uint256)\",\"IMD6900Hook.afterAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"IMD6900Hook.afterDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"IMD6900Hook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMD6900Hook.afterRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"IMD6900Hook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMD6900Hook.beforeAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"IMD6900Hook.beforeDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"IMD6900Hook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"IMD6900Hook.beforeRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"IMD6900Hook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMD6900Hook.cancelOwnershipHandover()\",\"IMD6900Hook.completeOwnershipHandover(address)\",\"IMD6900Hook.renounceOwnership()\",\"IMD6900Hook.requestOwnershipHandover()\",\"IMD6900Hook.setPerpEngine(address)\",\"IMD6900Hook.setSweepFailOpen(bool)\",\"IMD6900Hook.transferOwnership(address)\",\"IMD6900LockedPool.collect()\",\"IMD6900LockedPool.lowerFee(uint24)\",\"IMD6900LockedPool.open(address,uint160,uint256)\",\"IMD6900LockedPool.receive()\",\"IMD6900LockedPool.setHook(address)\",\"IMD6900LockedPool.unlockCallback(bytes)\",\"IMD6900RobinhoodLaunch.addFees()\",\"IMD6900RobinhoodLaunch.addPerpFees()\",\"IMD6900RobinhoodLaunch.burnImd(uint256)\",\"IMD6900RobinhoodLaunch.cancelOwnershipHandover()\",\"IMD6900RobinhoodLaunch.claim(uint256,uint256,bytes32[])\",\"IMD6900RobinhoodLaunch.completeOwnershipHandover(address)\",\"IMD6900RobinhoodLaunch.createHook(bytes32)\",\"IMD6900RobinhoodLaunch.endClaims()\",\"IMD6900RobinhoodLaunch.harvest()\",\"IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address)\",\"IMD6900RobinhoodLaunch.payOwed(address)\",\"IMD6900RobinhoodLaunch.receive()\",\"IMD6900RobinhoodLaunch.recoverEth(address,uint256)\",\"IMD6900RobinhoodLaunch.releaseExcess(uint256,address)\",\"IMD6900RobinhoodLaunch.releaseImdstr(uint256,address)\",\"IMD6900RobinhoodLaunch.releaseUnclaimed(address)\",\"IMD6900RobinhoodLaunch.renounceOwnership()\",\"IMD6900RobinhoodLaunch.requestOwnershipHandover()\",\"IMD6900RobinhoodLaunch.sellHeld(uint256,uint256,address)\",\"IMD6900RobinhoodLaunch.setClaims(bytes32,uint256,uint256)\",\"IMD6900RobinhoodLaunch.setImdBurn(address)\",\"IMD6900RobinhoodLaunch.setImdPool(uint24,int24,address)\",\"IMD6900RobinhoodLaunch.setPayees(address[],uint16[])\",\"IMD6900RobinhoodLaunch.setPerpFeeTo(address)\",\"IMD6900RobinhoodLaunch.split()\",\"IMD6900RobinhoodLaunch.transferOwnership(address)\",\"IMD6900RobinhoodLaunch.unlockCallback(bytes)\",\"IMD6900RobinhoodLaunch.withdrawEth(address,uint256)\"],\"files\":{\".gitignore\":14,\"ADAPTATION.md\":185,\"README.md\":117,\"foundry.toml\":25,\"jobs/swarm-launch.json\":27,\"script/claims.py\":88,\"script/launch.sh\":222,\"script/mine.sh\":12,\"script/params.py\":64,\"script/programmable_plan.py\":205,\"snapshots/rh-community-2026-10-09.json\":56,\"src/IMD6900.sol\":29,\"src/IMD6900Hook.sol\":252,\"src/IMD6900LockedPool.sol\":252,\"src/IMD6900RobinhoodLaunch.sol\":595,\"test/HookSalt.sol\":35,\"test/IMD6900Edges.t.sol\":814,\"test/IMD6900Invariants.t.sol\":543,\"test/IMD6900RobinhoodLaunch.t.sol\":1202,\"test/RobinhoodFork.t.sol\":217},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"7205cd92613020fc8338f321d5d742a9b53c46c84bb50d8540f593b18de2af4c","verifiedTreeHash":"ece3712065592f430703fd48893219e75a31d76f","verifierVersion":"0.1.0+327e1e9f"},{"checks":[{"durationMs":48569,"exitCode":0,"name":"build","output":"Compiling 85 files with Solc 0.8.26\nSolc 0.8.26 finished in 48.35s\nCompiler run successful!\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900Hook.sol:75:44\n   │\n75 │     constructor(IPoolManager poolManager_, address pool_, address owner_) BaseHook(poolManager_) {\n   │                                            ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-events-access-control]: `claimRoot` is changed without an event but is used for access control\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:256:9\n    │\n256 │         (claimRoot, claimTotal, claimDeadline) = (root, total, deadline);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:79:57\n   │\n79 │     constructor(IPoolManager poolManager_, uint24 fee_, address launcher_, address timelock_) {\n   │                                                         ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:79:76\n   │\n79 │     constructor(IPoolManager poolManager_, uint24 fee_, address launcher_, address timelock_) {\n   │                                                                            ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900Hook.sol:111:28\n    │\n111 │     function setPerpEngine(address engine) external onlyOwner {\n    │                            ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900Hook.sol:118:9\n    │\n118 │         emit PerpEngineSet(engine);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:167:77\n    │\n167 │     constructor(address owner_, IPoolManager poolManager_, address imdstr_, address timelock_) {\n    │                                                                             ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:167:60\n    │\n167 │     constructor(address owner_, IPoolManager poolManager_, address imdstr_, address timelock_) {\n    │                                                            ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900Hook.sol:123:9\n    │\n123 │         emit SweepFailOpenSet(on);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `hook` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:189:9\n    │\n189 │         pool.setHook(h);\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/IMD6900RobinhoodLaunch.sol:192:9\n    │\n192 │         emit HookCreated(h, salt);\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/IMD6900RobinhoodLaunch.sol:213:9\n    │\n213 │         SafeTransferLib.safeTransferETH(to, msg.value);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-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/IMD6900RobinhoodLaunch.sol:214:9\n    │\n214 │         emit PerpFeesForwarded(to, msg.value);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:96:22\n   │\n96 │     function setHook(address hook_) external {\n   │                      ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:221:62\n    │\n221 │                 bytes1(0xff), address(this), salt, keccak256(abi.encodePacked(type(IMD6900).creationCode, abi.encode(address(this))))\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:229:26\n    │\n229 │         return keccak256(abi.encodePacked(type(IMD6900).creationCode, abi.encode(address(this))));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:257:9\n    │\n257 │         emit ClaimsSet(root, total, deadline);\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/IMD6900RobinhoodLaunch.sol:266:9\n    │\n266 │         SafeTransferLib.safeTransferETH(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:262:73\n    │\n262 │     function withdrawEth(address to, uint256 amount) external onlyOwner nonReentrant {\n    │                                                                         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:267:9\n    │\n267 │         emit EthWithdrawn(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900LockedPool.sol:106:19\n    │\n106 │     function open(address token_, uint160 sqrtPriceX96, uint256 amount) external nonReentrant returns (uint128 liq) {\n    │                   ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900LockedPool.sol:126:9\n    │\n126 │         emit Opened(start, upper, used, liq);\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/IMD6900LockedPool.sol:118:9\n    │\n118 │         poolManager.initialize(poolKey(), start);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:122:15\n    │\n122 │         liq = uint128(l - 1);\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[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:280:9\n    │\n280 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:284:32\n    │\n284 │         if (claimTotal != 0 && claimDeadline < block.timestamp + MIN_CLAIM_WINDOW) revert BadClaims();\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/IMD6900RobinhoodLaunch.sol:305:9\n    │\n305 │         emit Launched(t, sqrtPriceX96, pool.upperTick(), spend, bought, excess, excessTo);\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/IMD6900RobinhoodLaunch.sol:291:9\n    │\n291 │         pool.open(t, sqrtPriceX96, SUPPLY);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:309:62\n    │\n309 │     function releaseUnclaimed(address to) external onlyOwner nonReentrant {\n    │                                                              ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:311:13\n    │\n311 │         if (block.timestamp < claimDeadline) revert ClaimsOpen();\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/IMD6900RobinhoodLaunch.sol:319:9\n    │\n319 │         emit UnclaimedReleased(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:323:75\n    │\n323 │     function releaseImdstr(uint256 amount, address to) external onlyOwner nonReentrant {\n    │                                                                           ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:328:9\n    │\n328 │         emit ImdstrReleased(to, 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/IMD6900LockedPool.sol:243:9\n    │\n243 │         emit Collected(eth, tokens);\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/IMD6900LockedPool.sol:148:9\n    │\n148 │         emit FeeLowered(from, newFee, poolKey().toId(), liq);\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/IMD6900LockedPool.sol:142:9\n    │\n142 │         poolManager.initialize(poolKey(), sqrtPriceX96); // the hook serves the new pool from here on\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[return-bomb]: external call with a gas limit may copy unbounded return data\n    ╭▸ src/IMD6900Hook.sol:209:17\n    │\n209 │                 engine.call{gas: g - reserve}(abi.encodeCall(IPerpSweep.sweepLiquidations, (tx.origin, spec, isBuy, limit)));\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/return-bomb\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:343:13\n    │\n343 │             sum += bps[i];\n    │             ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:341:38\n    │\n341 │             if (to[i] == address(0)) revert BadPayees();\n    │                                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:346:23\n    │\n346 │         _payeeCount = uint8(n);\n    │                       ━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:193:23\n    │\n193 │         uint256 eth = uint256(int256(out.amount0() - earned.amount0()));\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/IMD6900LockedPool.sol:194:25\n    │\n194 │         uint256 coins = uint256(int256(out.amount1() - earned.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[unused-return]: return value of an external call is not used\n    ╭▸ src/IMD6900LockedPool.sol:211:21\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:211:47\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\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/IMD6900LockedPool.sol:211:55\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\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/IMD6900LockedPool.sol:212:73\n    │\n212 │         else if (a0 > 0) poolManager.take(key.currency0, address(this), uint256(uint128(a0)));\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/IMD6900LockedPool.sol:212:81\n    │\n212 │         else if (a0 > 0) poolManager.take(key.currency0, address(this), uint256(uint128(a0)));\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/IMD6900LockedPool.sol:215:71\n    │\n215 │             SafeTransferLib.safeTransfer(token, address(poolManager), uint256(uint128(-a1)));\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/IMD6900LockedPool.sol:215:79\n    │\n215 │             SafeTransferLib.safeTransfer(token, address(poolManager), uint256(uint128(-a1)));\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/IMD6900LockedPool.sol:216:13\n    │\n216 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:218:60\n    │\n218 │             poolManager.take(key.currency1, address(this), uint256(uint128(a1)));\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/IMD6900LockedPool.sol:218:68\n    │\n218 │             poolManager.take(key.currency1, address(this), uint256(uint128(a1)));\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/IMD6900LockedPool.sol:237:60\n    │\n237 │         return l > type(uint128).max ? type(uint128).max : uint128(l);\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/IMD6900LockedPool.sol:247:24\n    │\n247 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:247:32\n    │\n247 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:247:47\n    │\n247 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:247:55\n    │\n247 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\n    │                                                       ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:360:9\n    │\n360 │         emit EthRecovered(to, 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/IMD6900RobinhoodLaunch.sol:372:9\n    │\n372 │         emit ExcessReleased(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `excessHeld` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:397:34\n    │\n397 │         (eth, sold) = abi.decode(poolManager.unlock(abi.encode(uint8(2), amount)), (uint256, uint256));\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/IMD6900RobinhoodLaunch.sol:401:9\n    │\n401 │         emit HeldSold(sold, eth, to);\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/IMD6900RobinhoodLaunch.sol:445:9\n    │\n445 │         emit Claimed(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 `owed` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:539:22\n    │\n539 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:539:22\n    │\n539 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:543:13\n    │\n543 │             emit PayFailed(payee, part);\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/IMD6900RobinhoodLaunch.sol:471:9\n    │\n471 │         emit Split(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/IMD6900RobinhoodLaunch.sol:480:9\n    │\n480 │         SafeTransferLib.safeTransferETH(payee, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-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/IMD6900RobinhoodLaunch.sol:481:9\n    │\n481 │         emit OwedPaid(payee, 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/IMD6900RobinhoodLaunch.sol:495:9\n    │\n495 │         emit ImdBurned(spend, imd);\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/IMD6900RobinhoodLaunch.sol:504:9\n    │\n504 │         pool.collect();\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:517:69\n    │\n517 │             BalanceDelta s = poolManager.swap(k, SwapParams(false, -int256(spend), TickMath.MAX_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/IMD6900RobinhoodLaunch.sol:518:28\n    │\n518 │             uint256 sold = uint256(uint128(-s.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/IMD6900RobinhoodLaunch.sol:518:36\n    │\n518 │             uint256 sold = uint256(uint128(-s.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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:519:30\n    │\n519 │             uint256 ethOut = uint256(uint128(s.amount0()));\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/IMD6900RobinhoodLaunch.sol:519:38\n    │\n519 │             uint256 ethOut = uint256(uint128(s.amount0()));\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/IMD6900RobinhoodLaunch.sol:522:13\n    │\n522 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:527:66\n    │\n527 │         BalanceDelta d = poolManager.swap(key, SwapParams(true, -int256(spend), 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/IMD6900RobinhoodLaunch.sol:528:25\n    │\n528 │         uint256 ethIn = uint256(uint128(-d.amount0()));\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/IMD6900RobinhoodLaunch.sol:528:33\n    │\n528 │         uint256 ethIn = uint256(uint128(-d.amount0()));\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/IMD6900RobinhoodLaunch.sol:529:23\n    │\n529 │         uint256 out = uint256(uint128(d.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/IMD6900RobinhoodLaunch.sol:529:31\n    │\n529 │         uint256 out = uint256(uint128(d.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/IMD6900RobinhoodLaunch.sol:530:9\n    │\n530 │         poolManager.settle{value: ethIn}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:539:22\n    │\n539 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:548:16\n    │\n548 │         return abi.encodePacked(type(IMD6900Hook).creationCode, abi.encode(poolManager, address(pool), timelock));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\n","passed":true},{"durationMs":1953,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[PASS] test_TheWholeLaunchOnRobinhood() (gas: 2554)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 358.44µs (26.02µs CPU time)\n\nRan 44 tests for test/IMD6900RobinhoodLaunch.t.sol:IMD6900RobinhoodLaunchTest\n[PASS] test_ARefusedShareIsKeptForItsPayee() (gas: 3522670)\n[PASS] test_ARefusedShareIsPaidOnRetry() (gas: 1764413)\n[PASS] test_BurnsImdThroughItsExecutor() (gas: 2944346)\n[PASS] test_ClaimsCantChangeAfterTheLaunch() (gas: 1256703)\n[PASS] test_ClaimsNeedTheSnapshot() (gas: 1458611)\n[PASS] test_ClaimsNeverPassTheReserve() (gas: 1809178)\n[PASS] test_ClaimsOneForOne() (gas: 1581295)\n[PASS] test_ClaimsStayOpenNinetyDays() (gas: 208607)\n[PASS] test_DeploysOnAFreshChain() (gas: 62991761)\n[PASS] test_EachDeployerMinesItsOwnHook() (gas: 5485771)\n[PASS] test_EndingTheClaimsBringsMostOfTheBuyBack() (gas: 2138352)\nLogs:\n  back of the 2.81 ETH buy (C's claim kept by C): 2.631273447654979998\n\n[PASS] test_EthComesBackBeforeTheLaunchOnly() (gas: 1297973)\n[PASS] test_FitsOneTransaction() (gas: 5464447)\nLogs:\n  launch deploy gas (the same for every deployer: no mining): 8088\n  createHook gas: 1153722\n\n[PASS] test_HarvestSplitsTheEthAndBurnsTheCoin() (gas: 1840273)\nLogs:\n  pot bridge: 0.511887600000000000\n\n[PASS] test_KeepsTheExcessForTheTimelock() (gas: 1427941)\n[PASS] test_LaunchesAtEthereumsPrice() (gas: 1305939)\nLogs:\n  opening buy, coins: 122347490.873046793208740802\n  market cap after the buy, ETH on 1B: 25.007184312087223619\n\n[PASS] test_LaunchesOnce() (gas: 1297627)\n[PASS] test_LowerFeeMovesTheLiquidity() (gas: 2183173)\n[PASS] test_NoClaimsAllOfItBack() (gas: 1769994)\nLogs:\n  back of the 2.81 ETH buy, nobody claimed: 2.704790817050839653\n\n[PASS] test_OnlyTheOwnerLaunches() (gas: 178900)\n[PASS] test_OnlyTheTimelockLowersTheFeeAndOnlyDown() (gas: 1955468)\n[PASS] test_OnlyTheTimelockRecoversEth() (gas: 1298104)\n[PASS] test_OnlyTheTimelockRepointsImdsPool() (gas: 67989)\n[PASS] test_OnlyTheTimelockRepointsTheBurnExecutor() (gas: 91539)\n[PASS] test_OnlyTheTimelockRunsTheHook() (gas: 667668)\n[PASS] test_OnlyTheTimelockSetsTheSplit() (gas: 1793483)\n[PASS] test_PassesTheAdmissionScan() (gas: 13671277)\n[PASS] test_PerpFeesGoAllToThePot() (gas: 1448831)\n[PASS] test_RefusesAnAddressThatIsnt0x6900() (gas: 702318)\n[PASS] test_RefusesTheWrongAddress() (gas: 709787)\n[PASS] test_SendsTheExcessWhereTold() (gas: 1272655)\n[PASS] test_TheBuyMustCoverTheClaims() (gas: 1214259)\n[PASS] test_TheCoinIsAt0x6900() (gas: 1231164)\nLogs:\n  the coin: 0x6900d11750039d7Ef0f8262b5a62152BB906B8E8\n\n[PASS] test_TheCoinIsPlain() (gas: 1294546)\n[PASS] test_TheFeeIsStatic() (gas: 1572689)\n[PASS] test_TheHookIsCreatedOnceAtAFittingSalt() (gas: 6522789)\n[PASS] test_TheHookRefusesAnEngineThatCantAnswer() (gas: 2451301)\n[PASS] test_TheHooksAddressAndPermissions() (gas: 21734)\n[PASS] test_TheLiquidityIsLocked() (gas: 2147229)\n[PASS] test_ThePerpsEngineTradesThroughTheHook() (gas: 2308147)\n[PASS] test_TheTimelockCanRecoverARefusedShare() (gas: 1722913)\n[PASS] test_TheTimelockSellsTheExcessBack() (gas: 1648690)\nLogs:\n  the excess alone, sold back: ETH: 1.312390213060687826\n\n[PASS] test_TheUnclaimedRestAfterTheDeadline() (gas: 1648640)\n[PASS] test_TradesBothWays() (gas: 1552971)\nSuite result: ok. 44 passed; 0 failed; 0 skipped; finished in 167.52ms (213.24ms CPU time)\n\nRan 31 tests for test/IMD6900Edges.t.sol:IMD6900EdgesTest\n[PASS] testFuzz_LowerFeeAfterAnyTrades(uint256,uint256,uint256,bool) (runs: 256, μ: 2244914, ~: 2228846)\nLogs:\n  Bound result 216748040610462160245\n  Bound result 753326662897714568797713647\n  Bound result 44399\n\n[PASS] testFuzz_OpensAtAnyReasonablePrice(uint256) (runs: 128, μ: 1247383, ~: 1244644)\nLogs:\n  Bound result 4785069771039372822391209\n\n[PASS] testFuzz_PayeeSharesMustSumToTenThousand(uint16,uint16,uint16) (runs: 512, μ: 144291, ~: 144338)\n[PASS] testFuzz_RecoverEthKeepsTheBooks(uint256,uint256) (runs: 256, μ: 1727114, ~: 1730122)\nLogs:\n  Bound result 98720242688118647379\n  Bound result 98720242688118647377\n\n[PASS] testFuzz_SellHeldNeverTouchesTheReserve(uint256,uint256) (runs: 256, μ: 1441194, ~: 1436369)\nLogs:\n  Bound result 1407\n  Bound result 145\n\n[PASS] testFuzz_TheSplitIsExact(uint256) (runs: 512, μ: 1419422, ~: 1423319)\nLogs:\n  Bound result 2\n\n[PASS] test_ARefusedShareOutlivesARepointedBurnExecutor() (gas: 1614833)\n[PASS] test_ASweepThatCantRunFailsClosedUnlessTheTimelockOpensIt() (gas: 2328280)\n[PASS] test_BurnImdHoldsItsFloor() (gas: 1418504)\n[PASS] test_ClaimEdges() (gas: 1799027)\n[PASS] test_ClaimsNeedTheImdstr() (gas: 1341229)\n[PASS] test_LaunchesWithoutClaims() (gas: 1215273)\n[PASS] test_LowerFeeWithThePriceAsHighAsItGoes() (gas: 2298521)\n[PASS] test_NobodyElseInitializesAPoolOnTheHook() (gas: 1343127)\n[PASS] test_NothingAfterTheLaunchRunsBeforeIt() (gas: 521694)\n[PASS] test_OnlyTheLauncherRunsThePool() (gas: 1397678)\n[PASS] test_OnlyThePoolManagerCallsBack() (gas: 104112)\n[PASS] test_PayeesMustAddUp() (gas: 716300)\n[PASS] test_PerpFeesRevertWhenTheirLandingRefuses() (gas: 240817)\n[PASS] test_RefusesAPriceOffTheRange() (gas: 1607158)\n[PASS] test_ReleaseImdstrEdges() (gas: 1657258)\n[PASS] test_ReleaseUnclaimedOnceAndWhereTold() (gas: 1437829)\n[PASS] test_SettersRefuseTheObviousMistakes() (gas: 148253)\n[PASS] test_TheClaimListNeedsBothARootAndATotal() (gas: 261275)\n[PASS] test_TheHookRefusesAnEngineThatLies() (gas: 1535465)\n[PASS] test_TheHooksCallbacksAreThePoolManagers() (gas: 1296297)\n[PASS] test_TheLaunchHoldsItsFloor() (gas: 1214148)\n[PASS] test_TheLaunchNeedsEth() (gas: 1050090)\n[PASS] test_TheLaunchNeedsTheHook() (gas: 94653)\n[PASS] test_TheLaunchOwnerCanHandOverTheHookCannotRenounce() (gas: 114877)\n[PASS] test_WithdrawEthEdges() (gas: 226493)\nSuite result: ok. 31 passed; 0 failed; 0 skipped; finished in 211.49ms (506.66ms CPU time)\n\nRan 1 test for test/IMD6900Invariants.t.sol:IMD6900InvariantTest\n[PASS]\nIMD6900InvariantTest invariants:\n[PASS] invariant_AllTheLiquidityIsTheLockedPosition\n[PASS] invariant_ClaimsAreBoundedAndOneForOne\n[PASS] invariant_EndedClaimsStayEnded\n[PASS] invariant_TheCoinsHeldAreTheReserveAndTheTimelocks\n[PASS] invariant_TheEthHeldCoversTheBucketAndTheOwed\n[PASS] invariant_TheFeeOnlyFallsAndTheHookFollows\n[PASS] invariant_TheLaunchIsFinal\n[PASS] invariant_ThePoolContractHoldsNothing\n[PASS] invariant_TheSupplyNeverRises\n IMD6900InvariantTest invariants (runs: 96, calls: 4800, reverts: 0)\n\n╭---------------+------------------+-------+---------+----------╮\n| Contract      | Selector         | Calls | Reverts | Discards |\n+===============================================================+\n| LaunchHandler | burnImd          | 215   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | buy              | 220   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | claim            | 253   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | endClaims        | 220   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | engineTrades     | 221   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | harvest          | 222   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | lowerFee         | 226   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | payOwed          | 210   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | perpFees         | 235   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | recoverEth       | 209   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | releaseExcess    | 238   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | releaseImdstr    | 256   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | releaseUnclaimed | 225   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | sell             | 205   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | sellHeld         | 237   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | sendFees         | 235   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | setEngine        | 233   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | setPayees        | 226   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | setPotRefuses    | 277   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | split            | 219   | 0       | 0        |\n|---------------+------------------+-------+---------+----------|\n| LaunchHandler | warp             | 218   | 0       | 0        |\n╰---------------+------------------+-------+---------+----------╯\n\nLogs:\n  Bound result 39783717287076382185895494\n  Bound result 0\n  Bound result 1000000000002\n  Bound result 480288379487968713\n  Bound result 0\n  Bound result 249999998750000048879\n  Bound result 444\n  Bound result 400000\n  Bound result 2\n  Bound result 7007\n  Bound result 16178\n  Bound result 8726400\n  Bound result 0\n  Bound result 659918\n  Bound result 4099\n  Bound result 0\n  Bound result 4644\n  Bound result 13426\n  Bound result 335\n  Bound result 0\n  Bound result 706703197265189501771155878\n  Bound result 58750878884246479\n  Bound result 0\n  Bound result 2810000000000000001979754\n  Bound result 0\n  Bound result 246293672475909838072\n  Bound result 9771\n  Bound result 3407\n  Bound result 13277\n  Bound result 3915342836732661944\n  Bound result 773\n  Bound result 51\n  Bound result 5030\n  Bound result 1000\n  Bound result 999999000000033301\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.80s (1.72s CPU time)\n\nRan 4 test suites in 1.80s (2.18s CPU time): 77 tests passed, 0 failed, 0 skipped (77 total tests)\n","passed":true},{"durationMs":69,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMD6900.approve(address,uint256)\",\"IMD6900.burn(uint256)\",\"IMD6900.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"IMD6900.transfer(address,uint256)\",\"IMD6900.transferFrom(address,address,uint256)\",\"IMD6900Hook.afterAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"IMD6900Hook.afterDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"IMD6900Hook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMD6900Hook.afterRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"IMD6900Hook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMD6900Hook.beforeAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"IMD6900Hook.beforeDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"IMD6900Hook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"IMD6900Hook.beforeRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"IMD6900Hook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMD6900Hook.cancelOwnershipHandover()\",\"IMD6900Hook.completeOwnershipHandover(address)\",\"IMD6900Hook.renounceOwnership()\",\"IMD6900Hook.requestOwnershipHandover()\",\"IMD6900Hook.setPerpEngine(address)\",\"IMD6900Hook.setSweepFailOpen(bool)\",\"IMD6900Hook.transferOwnership(address)\",\"IMD6900LockedPool.collect()\",\"IMD6900LockedPool.lowerFee(uint24)\",\"IMD6900LockedPool.open(address,uint160,uint256)\",\"IMD6900LockedPool.receive()\",\"IMD6900LockedPool.setHook(address)\",\"IMD6900LockedPool.unlockCallback(bytes)\",\"IMD6900RobinhoodLaunch.addFees()\",\"IMD6900RobinhoodLaunch.addPerpFees()\",\"IMD6900RobinhoodLaunch.burnImd(uint256)\",\"IMD6900RobinhoodLaunch.cancelOwnershipHandover()\",\"IMD6900RobinhoodLaunch.claim(uint256,uint256,bytes32[])\",\"IMD6900RobinhoodLaunch.completeOwnershipHandover(address)\",\"IMD6900RobinhoodLaunch.createHook(bytes32)\",\"IMD6900RobinhoodLaunch.endClaims()\",\"IMD6900RobinhoodLaunch.harvest()\",\"IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address)\",\"IMD6900RobinhoodLaunch.payOwed(address)\",\"IMD6900RobinhoodLaunch.receive()\",\"IMD6900RobinhoodLaunch.recoverEth(address,uint256)\",\"IMD6900RobinhoodLaunch.releaseExcess(uint256,address)\",\"IMD6900RobinhoodLaunch.releaseImdstr(uint256,address)\",\"IMD6900RobinhoodLaunch.releaseUnclaimed(address)\",\"IMD6900RobinhoodLaunch.renounceOwnership()\",\"IMD6900RobinhoodLaunch.requestOwnershipHandover()\",\"IMD6900RobinhoodLaunch.sellHeld(uint256,uint256,address)\",\"IMD6900RobinhoodLaunch.setClaims(bytes32,uint256,uint256)\",\"IMD6900RobinhoodLaunch.setImdBurn(address)\",\"IMD6900RobinhoodLaunch.setImdPool(uint24,int24,address)\",\"IMD6900RobinhoodLaunch.setPayees(address[],uint16[])\",\"IMD6900RobinhoodLaunch.setPerpFeeTo(address)\",\"IMD6900RobinhoodLaunch.split()\",\"IMD6900RobinhoodLaunch.transferOwnership(address)\",\"IMD6900RobinhoodLaunch.unlockCallback(bytes)\",\"IMD6900RobinhoodLaunch.withdrawEth(address,uint256)\"],\"files\":{\".gitignore\":14,\"ADAPTATION.md\":108,\"README.md\":114,\"foundry.toml\":25,\"jobs/swarm-launch.json\":27,\"script/claims.py\":88,\"script/launch.sh\":222,\"script/mine.sh\":12,\"script/params.py\":64,\"script/programmable_plan.py\":205,\"snapshots/rh-community-2026-10-09.json\":56,\"src/IMD6900.sol\":29,\"src/IMD6900Hook.sol\":229,\"src/IMD6900LockedPool.sol\":249,\"src/IMD6900RobinhoodLaunch.sol\":560,\"test/HookSalt.sol\":35,\"test/IMD6900Edges.t.sol\":722,\"test/IMD6900Invariants.t.sol\":507,\"test/IMD6900RobinhoodLaunch.t.sol\":953,\"test/RobinhoodFork.t.sol\":217},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"80fcca689c2003d9c80c50fd38503dd58ae13c25b062ce06c2fa7701f4f01b30","verifiedTreeHash":"4e2dfb3dcf733ab14c204eca931c2f6cf1845938","verifierVersion":"0.1.0+327e1e9f"},{"checks":[{"durationMs":24920,"exitCode":0,"name":"build","output":"Compiling 83 files with Solc 0.8.26\nSolc 0.8.26 finished in 24.74s\nCompiler run successful!\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900Hook.sol:75:44\n   │\n75 │     constructor(IPoolManager poolManager_, address pool_, address owner_) BaseHook(poolManager_) {\n   │                                            ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:79:76\n   │\n79 │     constructor(IPoolManager poolManager_, uint24 fee_, address launcher_, address timelock_) {\n   │                                                                            ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:79:57\n   │\n79 │     constructor(IPoolManager poolManager_, uint24 fee_, address launcher_, address timelock_) {\n   │                                                         ━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-events-access-control]: `claimRoot` is changed without an event but is used for access control\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:256:9\n    │\n256 │         (claimRoot, claimTotal, claimDeadline) = (root, total, deadline);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-access-control\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900Hook.sol:111:28\n    │\n111 │     function setPerpEngine(address engine) external onlyOwner {\n    │                            ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900Hook.sol:118:9\n    │\n118 │         emit PerpEngineSet(engine);\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/IMD6900Hook.sol:123:9\n    │\n123 │         emit SweepFailOpenSet(on);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/IMD6900LockedPool.sol:96:22\n   │\n96 │     function setHook(address hook_) external {\n   │                      ━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:167:60\n    │\n167 │     constructor(address owner_, IPoolManager poolManager_, address imdstr_, address timelock_) {\n    │                                                            ━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:167:77\n    │\n167 │     constructor(address owner_, IPoolManager poolManager_, address imdstr_, address timelock_) {\n    │                                                                             ━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[reentrancy-no-eth]: external call can be reentered before `hook` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:189:9\n    │\n189 │         pool.setHook(h);\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/IMD6900RobinhoodLaunch.sol:192:9\n    │\n192 │         emit HookCreated(h, salt);\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/IMD6900RobinhoodLaunch.sol:213:9\n    │\n213 │         SafeTransferLib.safeTransferETH(to, msg.value);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-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/IMD6900RobinhoodLaunch.sol:214:9\n    │\n214 │         emit PerpFeesForwarded(to, msg.value);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n    ╭▸ src/IMD6900LockedPool.sol:106:19\n    │\n106 │     function open(address token_, uint160 sqrtPriceX96, uint256 amount) external nonReentrant returns (uint128 liq) {\n    │                   ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:221:62\n    │\n221 │                 bytes1(0xff), address(this), salt, keccak256(abi.encodePacked(type(IMD6900).creationCode, abi.encode(address(this))))\n    │                                                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900LockedPool.sol:126:9\n    │\n126 │         emit Opened(start, upper, used, liq);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:229:26\n    │\n229 │         return keccak256(abi.encodePacked(type(IMD6900).creationCode, abi.encode(address(this))));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/IMD6900LockedPool.sol:118:9\n    │\n118 │         poolManager.initialize(poolKey(), start);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:122:15\n    │\n122 │         liq = uint128(l - 1);\n    │               ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:257:9\n    │\n257 │         emit ClaimsSet(root, total, deadline);\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/IMD6900RobinhoodLaunch.sol:266:9\n    │\n266 │         SafeTransferLib.safeTransferETH(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:262:73\n    │\n262 │     function withdrawEth(address to, uint256 amount) external onlyOwner nonReentrant {\n    │                                                                         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:267:9\n    │\n267 │         emit EthWithdrawn(to, 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/IMD6900LockedPool.sol:243:9\n    │\n243 │         emit Collected(eth, tokens);\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/IMD6900LockedPool.sol:148:9\n    │\n148 │         emit FeeLowered(from, newFee, poolKey().toId(), liq);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:280:9\n    │\n280 │         nonReentrant\n    │         ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:284:32\n    │\n284 │         if (claimTotal != 0 && claimDeadline < block.timestamp + MIN_CLAIM_WINDOW) revert BadClaims();\n    │                                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/IMD6900LockedPool.sol:142:9\n    │\n142 │         poolManager.initialize(poolKey(), sqrtPriceX96); // the hook serves the new pool from here on\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/IMD6900RobinhoodLaunch.sol:305:9\n    │\n305 │         emit Launched(t, sqrtPriceX96, pool.upperTick(), spend, bought, excess, excessTo);\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/IMD6900RobinhoodLaunch.sol:291:9\n    │\n291 │         pool.open(t, sqrtPriceX96, SUPPLY);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:309:62\n    │\n309 │     function releaseUnclaimed(address to) external onlyOwner nonReentrant {\n    │                                                              ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:311:13\n    │\n311 │         if (block.timestamp < claimDeadline) revert ClaimsOpen();\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/IMD6900RobinhoodLaunch.sol:319:9\n    │\n319 │         emit UnclaimedReleased(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[non-reentrant-not-first]: `nonReentrant` is not the first modifier\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:323:75\n    │\n323 │     function releaseImdstr(uint256 amount, address to) external onlyOwner nonReentrant {\n    │                                                                           ━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/non-reentrant-not-first\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:328:9\n    │\n328 │         emit ImdstrReleased(to, amount);\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/IMD6900Hook.sol:209:17\n    │\n209 │                 engine.call{gas: g - reserve}(abi.encodeCall(IPerpSweep.sweepLiquidations, (tx.origin, spec, isBuy, limit)));\n    │                 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/return-bomb\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:193:23\n    │\n193 │         uint256 eth = uint256(int256(out.amount0() - earned.amount0()));\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/IMD6900LockedPool.sol:194:25\n    │\n194 │         uint256 coins = uint256(int256(out.amount1() - earned.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[unused-return]: return value of an external call is not used\n    ╭▸ src/IMD6900LockedPool.sol:211:21\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\n    │                     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:211:47\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\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/IMD6900LockedPool.sol:211:55\n    │\n211 │         if (a0 < 0) poolManager.settle{value: uint256(uint128(-a0))}();\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/IMD6900LockedPool.sol:212:73\n    │\n212 │         else if (a0 > 0) poolManager.take(key.currency0, address(this), uint256(uint128(a0)));\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/IMD6900LockedPool.sol:212:81\n    │\n212 │         else if (a0 > 0) poolManager.take(key.currency0, address(this), uint256(uint128(a0)));\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/IMD6900LockedPool.sol:215:71\n    │\n215 │             SafeTransferLib.safeTransfer(token, address(poolManager), uint256(uint128(-a1)));\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/IMD6900LockedPool.sol:215:79\n    │\n215 │             SafeTransferLib.safeTransfer(token, address(poolManager), uint256(uint128(-a1)));\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/IMD6900LockedPool.sol:216:13\n    │\n216 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900LockedPool.sol:218:60\n    │\n218 │             poolManager.take(key.currency1, address(this), uint256(uint128(a1)));\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/IMD6900LockedPool.sol:218:68\n    │\n218 │             poolManager.take(key.currency1, address(this), uint256(uint128(a1)));\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/IMD6900LockedPool.sol:237:60\n    │\n237 │         return l > type(uint128).max ? type(uint128).max : uint128(l);\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/IMD6900LockedPool.sol:247:24\n    │\n247 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:247:32\n    │\n247 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:247:47\n    │\n247 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\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/IMD6900LockedPool.sol:247:55\n    │\n247 │         return v < 0 ? uint256(uint128(-v)) : uint256(uint128(v));\n    │                                                       ━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[uninitialized-local]: local variable is read before being initialized\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:343:13\n    │\n343 │             sum += bps[i];\n    │             ━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/uninitialized-local\n\nwarning[require-revert-in-loop]: `require` or `revert` inside a loop\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:341:38\n    │\n341 │             if (to[i] == address(0)) revert BadPayees();\n    │                                      ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/require-revert-in-loop\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:346:23\n    │\n346 │         _payeeCount = uint8(n);\n    │                       ━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint8' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:360:9\n    │\n360 │         emit EthRecovered(to, 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/IMD6900RobinhoodLaunch.sol:372:9\n    │\n372 │         emit ExcessReleased(to, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `excessHeld` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:397:34\n    │\n397 │         (eth, sold) = abi.decode(poolManager.unlock(abi.encode(uint8(2), amount)), (uint256, uint256));\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/IMD6900RobinhoodLaunch.sol:401:9\n    │\n401 │         emit HeldSold(sold, eth, to);\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/IMD6900RobinhoodLaunch.sol:445:9\n    │\n445 │         emit Claimed(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 `owed` is updated\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:539:22\n    │\n539 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-eth\n\nwarning[calls-loop]: external call inside a loop\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:539:22\n    │\n539 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/calls-loop\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:543:13\n    │\n543 │             emit PayFailed(payee, part);\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/IMD6900RobinhoodLaunch.sol:471:9\n    │\n471 │         emit Split(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/IMD6900RobinhoodLaunch.sol:480:9\n    │\n480 │         SafeTransferLib.safeTransferETH(payee, amount);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-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/IMD6900RobinhoodLaunch.sol:481:9\n    │\n481 │         emit OwedPaid(payee, 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/IMD6900RobinhoodLaunch.sol:495:9\n    │\n495 │         emit ImdBurned(spend, imd);\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/IMD6900RobinhoodLaunch.sol:504:9\n    │\n504 │         pool.collect();\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:517:69\n    │\n517 │             BalanceDelta s = poolManager.swap(k, SwapParams(false, -int256(spend), TickMath.MAX_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/IMD6900RobinhoodLaunch.sol:518:28\n    │\n518 │             uint256 sold = uint256(uint128(-s.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/IMD6900RobinhoodLaunch.sol:518:36\n    │\n518 │             uint256 sold = uint256(uint128(-s.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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:519:30\n    │\n519 │             uint256 ethOut = uint256(uint128(s.amount0()));\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/IMD6900RobinhoodLaunch.sol:519:38\n    │\n519 │             uint256 ethOut = uint256(uint128(s.amount0()));\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/IMD6900RobinhoodLaunch.sol:522:13\n    │\n522 │             poolManager.settle();\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:527:66\n    │\n527 │         BalanceDelta d = poolManager.swap(key, SwapParams(true, -int256(spend), 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/IMD6900RobinhoodLaunch.sol:528:25\n    │\n528 │         uint256 ethIn = uint256(uint128(-d.amount0()));\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/IMD6900RobinhoodLaunch.sol:528:33\n    │\n528 │         uint256 ethIn = uint256(uint128(-d.amount0()));\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/IMD6900RobinhoodLaunch.sol:529:23\n    │\n529 │         uint256 out = uint256(uint128(d.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/IMD6900RobinhoodLaunch.sol:529:31\n    │\n529 │         uint256 out = uint256(uint128(d.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/IMD6900RobinhoodLaunch.sol:530:9\n    │\n530 │         poolManager.settle{value: ethIn}();\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[arbitrary-send-eth]: ETH is sent to a user-controlled destination; restrict the destination or the caller\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:539:22\n    │\n539 │         (bool ok,) = payee.call{value: part}(\"\");\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/arbitrary-send-eth\n\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n    ╭▸ src/IMD6900RobinhoodLaunch.sol:548:16\n    │\n548 │         return abi.encodePacked(type(IMD6900Hook).creationCode, abi.encode(poolManager, address(pool), timelock));\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\n","passed":true},{"durationMs":271,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RobinhoodFork.t.sol:RobinhoodForkTest\n[PASS] test_TheWholeLaunchOnRobinhood() (gas: 2554)\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 476.66µs (74.04µs CPU time)\n\nRan 44 tests for test/IMD6900RobinhoodLaunch.t.sol:IMD6900RobinhoodLaunchTest\n[PASS] test_ARefusedShareIsKeptForItsPayee() (gas: 3522670)\n[PASS] test_ARefusedShareIsPaidOnRetry() (gas: 1764413)\n[PASS] test_BurnsImdThroughItsExecutor() (gas: 2944346)\n[PASS] test_ClaimsCantChangeAfterTheLaunch() (gas: 1256703)\n[PASS] test_ClaimsNeedTheSnapshot() (gas: 1458611)\n[PASS] test_ClaimsNeverPassTheReserve() (gas: 1809178)\n[PASS] test_ClaimsOneForOne() (gas: 1581295)\n[PASS] test_ClaimsStayOpenNinetyDays() (gas: 208607)\n[PASS] test_DeploysOnAFreshChain() (gas: 62991761)\n[PASS] test_EachDeployerMinesItsOwnHook() (gas: 5485771)\n[PASS] test_EndingTheClaimsBringsMostOfTheBuyBack() (gas: 2138352)\nLogs:\n  back of the 2.81 ETH buy (C's claim kept by C): 2.631273447654979998\n\n[PASS] test_EthComesBackBeforeTheLaunchOnly() (gas: 1297973)\n[PASS] test_FitsOneTransaction() (gas: 5464447)\nLogs:\n  launch deploy gas (the same for every deployer: no mining): 8088\n  createHook gas: 1153722\n\n[PASS] test_HarvestSplitsTheEthAndBurnsTheCoin() (gas: 1840273)\nLogs:\n  pot bridge: 0.511887600000000000\n\n[PASS] test_KeepsTheExcessForTheTimelock() (gas: 1427941)\n[PASS] test_LaunchesAtEthereumsPrice() (gas: 1305939)\nLogs:\n  opening buy, coins: 122347490.873046793208740802\n  market cap after the buy, ETH on 1B: 25.007184312087223619\n\n[PASS] test_LaunchesOnce() (gas: 1297627)\n[PASS] test_LowerFeeMovesTheLiquidity() (gas: 2183173)\n[PASS] test_NoClaimsAllOfItBack() (gas: 1769994)\nLogs:\n  back of the 2.81 ETH buy, nobody claimed: 2.704790817050839653\n\n[PASS] test_OnlyTheOwnerLaunches() (gas: 178900)\n[PASS] test_OnlyTheTimelockLowersTheFeeAndOnlyDown() (gas: 1955468)\n[PASS] test_OnlyTheTimelockRecoversEth() (gas: 1298104)\n[PASS] test_OnlyTheTimelockRepointsImdsPool() (gas: 67989)\n[PASS] test_OnlyTheTimelockRepointsTheBurnExecutor() (gas: 91539)\n[PASS] test_OnlyTheTimelockRunsTheHook() (gas: 667668)\n[PASS] test_OnlyTheTimelockSetsTheSplit() (gas: 1793483)\n[PASS] test_PassesTheAdmissionScan() (gas: 13671277)\n[PASS] test_PerpFeesGoAllToThePot() (gas: 1448831)\n[PASS] test_RefusesAnAddressThatIsnt0x6900() (gas: 702318)\n[PASS] test_RefusesTheWrongAddress() (gas: 709787)\n[PASS] test_SendsTheExcessWhereTold() (gas: 1272655)\n[PASS] test_TheBuyMustCoverTheClaims() (gas: 1214259)\n[PASS] test_TheCoinIsAt0x6900() (gas: 1231164)\nLogs:\n  the coin: 0x6900d11750039d7Ef0f8262b5a62152BB906B8E8\n\n[PASS] test_TheCoinIsPlain() (gas: 1294546)\n[PASS] test_TheFeeIsStatic() (gas: 1572689)\n[PASS] test_TheHookIsCreatedOnceAtAFittingSalt() (gas: 6522789)\n[PASS] test_TheHookRefusesAnEngineThatCantAnswer() (gas: 2451301)\n[PASS] test_TheHooksAddressAndPermissions() (gas: 21734)\n[PASS] test_TheLiquidityIsLocked() (gas: 2147229)\n[PASS] test_ThePerpsEngineTradesThroughTheHook() (gas: 2308147)\n[PASS] test_TheTimelockCanRecoverARefusedShare() (gas: 1722913)\n[PASS] test_TheTimelockSellsTheExcessBack() (gas: 1648690)\nLogs:\n  the excess alone, sold back: ETH: 1.312390213060687826\n\n[PASS] test_TheUnclaimedRestAfterTheDeadline() (gas: 1648640)\n[PASS] test_TradesBothWays() (gas: 1552971)\nSuite result: ok. 44 passed; 0 failed; 0 skipped; finished in 131.10ms (140.43ms CPU time)\n\nRan 2 test suites in 132.04ms (131.58ms CPU time): 45 tests passed, 0 failed, 0 skipped (45 total tests)\n","passed":true},{"durationMs":73,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"IMD6900.approve(address,uint256)\",\"IMD6900.burn(uint256)\",\"IMD6900.permit(address,address,uint256,uint256,uint8,bytes32,bytes32)\",\"IMD6900.transfer(address,uint256)\",\"IMD6900.transferFrom(address,address,uint256)\",\"IMD6900Hook.afterAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"IMD6900Hook.afterDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"IMD6900Hook.afterInitialize(address,(address,address,uint24,int24,address),uint160,int24)\",\"IMD6900Hook.afterRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),int256,int256,bytes)\",\"IMD6900Hook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"IMD6900Hook.beforeAddLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"IMD6900Hook.beforeDonate(address,(address,address,uint24,int24,address),uint256,uint256,bytes)\",\"IMD6900Hook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"IMD6900Hook.beforeRemoveLiquidity(address,(address,address,uint24,int24,address),(int24,int24,int256,bytes32),bytes)\",\"IMD6900Hook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"IMD6900Hook.cancelOwnershipHandover()\",\"IMD6900Hook.completeOwnershipHandover(address)\",\"IMD6900Hook.renounceOwnership()\",\"IMD6900Hook.requestOwnershipHandover()\",\"IMD6900Hook.setPerpEngine(address)\",\"IMD6900Hook.setSweepFailOpen(bool)\",\"IMD6900Hook.transferOwnership(address)\",\"IMD6900LockedPool.collect()\",\"IMD6900LockedPool.lowerFee(uint24)\",\"IMD6900LockedPool.open(address,uint160,uint256)\",\"IMD6900LockedPool.receive()\",\"IMD6900LockedPool.setHook(address)\",\"IMD6900LockedPool.unlockCallback(bytes)\",\"IMD6900RobinhoodLaunch.addFees()\",\"IMD6900RobinhoodLaunch.addPerpFees()\",\"IMD6900RobinhoodLaunch.burnImd(uint256)\",\"IMD6900RobinhoodLaunch.cancelOwnershipHandover()\",\"IMD6900RobinhoodLaunch.claim(uint256,uint256,bytes32[])\",\"IMD6900RobinhoodLaunch.completeOwnershipHandover(address)\",\"IMD6900RobinhoodLaunch.createHook(bytes32)\",\"IMD6900RobinhoodLaunch.endClaims()\",\"IMD6900RobinhoodLaunch.harvest()\",\"IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address)\",\"IMD6900RobinhoodLaunch.payOwed(address)\",\"IMD6900RobinhoodLaunch.receive()\",\"IMD6900RobinhoodLaunch.recoverEth(address,uint256)\",\"IMD6900RobinhoodLaunch.releaseExcess(uint256,address)\",\"IMD6900RobinhoodLaunch.releaseImdstr(uint256,address)\",\"IMD6900RobinhoodLaunch.releaseUnclaimed(address)\",\"IMD6900RobinhoodLaunch.renounceOwnership()\",\"IMD6900RobinhoodLaunch.requestOwnershipHandover()\",\"IMD6900RobinhoodLaunch.sellHeld(uint256,uint256,address)\",\"IMD6900RobinhoodLaunch.setClaims(bytes32,uint256,uint256)\",\"IMD6900RobinhoodLaunch.setImdBurn(address)\",\"IMD6900RobinhoodLaunch.setImdPool(uint24,int24,address)\",\"IMD6900RobinhoodLaunch.setPayees(address[],uint16[])\",\"IMD6900RobinhoodLaunch.setPerpFeeTo(address)\",\"IMD6900RobinhoodLaunch.split()\",\"IMD6900RobinhoodLaunch.transferOwnership(address)\",\"IMD6900RobinhoodLaunch.unlockCallback(bytes)\",\"IMD6900RobinhoodLaunch.withdrawEth(address,uint256)\"],\"files\":{\".gitignore\":14,\"ADAPTATION.md\":108,\"README.md\":114,\"foundry.toml\":25,\"jobs/swarm-launch.json\":27,\"script/claims.py\":88,\"script/launch.sh\":222,\"script/mine.sh\":12,\"script/params.py\":64,\"script/programmable_plan.py\":205,\"snapshots/rh-community-2026-10-09.json\":56,\"src/IMD6900.sol\":29,\"src/IMD6900Hook.sol\":229,\"src/IMD6900LockedPool.sol\":249,\"src/IMD6900RobinhoodLaunch.sol\":560,\"test/HookSalt.sol\":35,\"test/IMD6900RobinhoodLaunch.t.sol\":953,\"test/RobinhoodFork.t.sol\":217},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":8399,"exitCode":0,"name":"slither","output":"[high/medium] arbitrary-send-eth at src/IMD6900RobinhoodLaunch.sol:538: IMD6900RobinhoodLaunch._pay(address,uint256) (src/IMD6900RobinhoodLaunch.sol#538-545) sends eth to arbitrary user\n[high/medium] arbitrary-send-eth at src/IMD6900LockedPool.sol:240: IMD6900LockedPool._payOut(uint256,uint256) (src/IMD6900LockedPool.sol#240-244) sends eth to arbitrary user\n[high/medium] reentrancy-eth at src/IMD6900RobinhoodLaunch.sol:452: Reentrancy in IMD6900RobinhoodLaunch.split() (src/IMD6900RobinhoodLaunch.sol#452-472):\n[medium/high] incorrect-equality at src/IMD6900RobinhoodLaunch.sol:262: IMD6900RobinhoodLaunch.withdrawEth(address,uint256) (src/IMD6900RobinhoodLaunch.sol#262-268) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMD6900RobinhoodLaunch.sol:352: IMD6900RobinhoodLaunch.recoverEth(address,uint256) (src/IMD6900RobinhoodLaunch.sol#352-361) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMD6900RobinhoodLaunch.sol:452: IMD6900RobinhoodLaunch.split() (src/IMD6900RobinhoodLaunch.sol#452-472) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/IMD6900RobinhoodLaunch.sol:276: IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address) (src/IMD6900RobinhoodLaunch.sol#276-306) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/IMD6900RobinhoodLaunch.sol:392: Reentrancy in IMD6900RobinhoodLaunch.sellHeld(uint256,uint256,address) (src/IMD6900RobinhoodLaunch.sol#392-402):\n[medium/medium] reentrancy-no-eth at src/IMD6900RobinhoodLaunch.sol:181: Reentrancy in IMD6900RobinhoodLaunch.createHook(bytes32) (src/IMD6900RobinhoodLaunch.sol#181-194):\n[medium/medium] uninitialized-local at src/IMD6900RobinhoodLaunch.sol:339: IMD6900RobinhoodLaunch.setPayees(address[],uint16[]).sum (src/IMD6900RobinhoodLaunch.sol#339) is a local variable never initialized\n[medium/medium] unused-return at src/IMD6900RobinhoodLaunch.sol:512: IMD6900RobinhoodLaunch.unlockCallback(bytes) (src/IMD6900RobinhoodLaunch.sol#512-533) ignores return value by poolManager.settle{value: ethIn}() (src/IMD6900RobinhoodLaunch.sol#530)\n[medium/medium] unused-return at src/IMD6900RobinhoodLaunch.sol:512: IMD6900RobinhoodLaunch.unlockCallback(bytes) (src/IMD6900RobinhoodLaunch.sol#512-533) ignores return value by poolManager.settle() (src/IMD6900RobinhoodLaunch.sol#522)\n[medium/medium] unused-return at src/IMD6900RobinhoodLaunch.sol:276: IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address) (src/IMD6900RobinhoodLaunch.sol#276-306) ignores return value by pool.open(t,sqrtPriceX96,SUPPLY) (src/IMD6900RobinhoodLaunch.sol#291)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:209: IMD6900LockedPool._settle(int128,int128) (src/IMD6900LockedPool.sol#209-220) ignores return value by poolManager.settle() (src/IMD6900LockedPool.sol#216)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:134: IMD6900LockedPool.lowerFee(uint24) (src/IMD6900LockedPool.sol#134-149) ignores return value by (sqrtPriceX96,None,None,None) = poolManager.getSlot0(old.toId()) (src/IMD6900LockedPool.sol#139)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:134: IMD6900LockedPool.lowerFee(uint24) (src/IMD6900LockedPool.sol#134-149) ignores return value by poolManager.initialize(poolKey(),sqrtPriceX96) (src/IMD6900LockedPool.sol#142)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:106: IMD6900LockedPool.open(address,uint160,uint256) (src/IMD6900LockedPool.sol#106-127) ignores return value by poolManager.initialize(poolKey(),start) (src/IMD6900LockedPool.sol#118)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:209: IMD6900LockedPool._settle(int128,int128) (src/IMD6900LockedPool.sol#209-220) ignores return value by poolManager.settle{value: uint256(uint128(- a0))}() (src/IMD6900LockedPool.sol#211)\n[medium/medium] unused-return at src/IMD6900RobinhoodLaunch.sol:503: IMD6900RobinhoodLaunch.harvest() (src/IMD6900RobinhoodLaunch.sol#503-506) ignores return value by pool.collect() (src/IMD6900RobinhoodLaunch.sol#504)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:223: IMD6900LockedPool._liquidityFor(uint256,uint256) (src/IMD6900LockedPool.sol#223-238) ignores return value by (p,None,None,None) = poolManager.getSlot0(poolKey().toId()) (src/IMD6900LockedPool.sol#224)\n[medium/medium] unused-return at src/IMD6900LockedPool.sol:161: IMD6900LockedPool.state() (src/IMD6900LockedPool.sol#161-165) ignores return value by (sqrtPriceX96,tick,None,None) = poolManager.getSlot0(id) (src/IMD6900LockedPool.sol#163)\n[low/medium] missing-zero-check at src/IMD6900LockedPool.sol:79: IMD6900LockedPool.constructor(IPoolManager,uint24,address,address).timelock_ (src/IMD6900LockedPool.sol#79) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900Hook.sol:75: IMD6900Hook.constructor(IPoolManager,address,address).pool_ (src/IMD6900Hook.sol#75) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900LockedPool.sol:79: IMD6900LockedPool.constructor(IPoolManager,uint24,address,address).launcher_ (src/IMD6900LockedPool.sol#79) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900RobinhoodLaunch.sol:167: IMD6900RobinhoodLaunch.constructor(address,IPoolManager,address,address).imdstr_ (src/IMD6900RobinhoodLaunch.sol#167) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900RobinhoodLaunch.sol:421: IMD6900RobinhoodLaunch.setImdPool(uint24,int24,address).hooks (src/IMD6900RobinhoodLaunch.sol#421) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900LockedPool.sol:106: IMD6900LockedPool.open(address,uint160,uint256).token_ (src/IMD6900LockedPool.sol#106) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900RobinhoodLaunch.sol:167: IMD6900RobinhoodLaunch.constructor(address,IPoolManager,address,address).timelock_ (src/IMD6900RobinhoodLaunch.sol#167) lacks a zero-check on :\n[low/medium] missing-zero-check at src/IMD6900LockedPool.sol:96: IMD6900LockedPool.setHook(address).hook_ (src/IMD6900LockedPool.sol#96) lacks a zero-check on :\n[low/medium] calls-loop at src/IMD6900RobinhoodLaunch.sol:538: IMD6900RobinhoodLaunch._pay(address,uint256) (src/IMD6900RobinhoodLaunch.sol#538-545) has external calls inside a loop: (ok,None) = payee.call{value: part}() (src/IMD6900RobinhoodLaunch.sol#539)\n[low/medium] calls-loop at src/IMD6900RobinhoodLaunch.sol:538: IMD6900RobinhoodLaunch._pay(address,uint256) (src/IMD6900RobinhoodLaunch.sol#538-545) has external calls inside a loop: (ok,None) = payee.call{value: part}() (src/IMD6900RobinhoodLaunch.sol#539)\n[low/medium] reentrancy-benign at src/IMD6900RobinhoodLaunch.sol:276: Reentrancy in IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address) (src/IMD6900RobinhoodLaunch.sol#276-306):\n[low/medium] reentrancy-benign at src/IMD6900RobinhoodLaunch.sol:487: Reentrancy in IMD6900RobinhoodLaunch.burnImd(uint256) (src/IMD6900RobinhoodLaunch.sol#487-496):\n[low/medium] reentrancy-benign at src/IMD6900RobinhoodLaunch.sol:276: Reentrancy in IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address) (src/IMD6900RobinhoodLaunch.sol#276-306):\n[low/medium] reentrancy-benign at src/IMD6900RobinhoodLaunch.sol:181: Reentrancy in IMD6900RobinhoodLaunch.createHook(bytes32) (src/IMD6900RobinhoodLaunch.sol#181-194):\n[low/medium] reentrancy-benign at src/IMD6900LockedPool.sol:106: Reentrancy in IMD6900LockedPool.open(address,uint160,uint256) (src/IMD6900LockedPool.sol#106-127):\n[low/medium] reentrancy-benign at src/IMD6900LockedPool.sol:134: Reentrancy in IMD6900LockedPool.lowerFee(uint24) (src/IMD6900LockedPool.sol#134-149):\n[low/medium] reentrancy-benign at src/IMD6900RobinhoodLaunch.sol:538: Reentrancy in IMD6900RobinhoodLaunch._pay(address,uint256) (src/IMD6900RobinhoodLaunch.sol#538-545):\n[low/medium] reentrancy-events at src/IMD6900RobinhoodLaunch.sol:181: Reentrancy in IMD6900RobinhoodLaunch.createHook(bytes32) (src/IMD6900RobinhoodLaunch.sol#181-194):\n[low/medium] reentrancy-events at src/IMD6900RobinhoodLaunch.sol:503: Reentrancy in IMD6900RobinhoodLaunch.harvest() (src/IMD6900RobinhoodLaunch.sol#503-506):\n[low/medium] reentrancy-events at src/IMD6900RobinhoodLaunch.sol:538: Reentrancy in IMD6900RobinhoodLaunch._pay(address,uint256) (src/IMD6900RobinhoodLaunch.sol#538-545):\n[low/medium] return-bomb at src/IMD6900Hook.sol:202: IMD6900Hook._sweep(address,int256,bool,uint160) (src/IMD6900Hook.sol#202-228) tries to limit the gas of an external call that controls implicit decoding\n[low/medium] timestamp at src/IMD6900RobinhoodLaunch.sol:309: IMD6900RobinhoodLaunch.releaseUnclaimed(address) (src/IMD6900RobinhoodLaunch.sol#309-320) uses timestamp for comparisons\n[low/medium] timestamp at src/IMD6900RobinhoodLaunch.sol:276: IMD6900RobinhoodLaunch.launch(bytes32,address,uint160,uint256,address) (src/IMD6900RobinhoodLaunch.sol#276-306) uses timestamp for comparisons","passed":true},{"durationMs":727,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/IMD6900RobinhoodLaunch.sol:221: `abi.encodePacked()` Hash Collision (3 places)\n[high] contract-locks-ether at src/IMD6900Hook.sol:40: Contract locks Ether without a withdraw function (2 places)\n[high] reentrancy-state-change at src/IMD6900LockedPool.sol:118: Reentrancy: State change after external call (9 places)\n[low] centralization-risk at src/IMD6900Hook.sol:40: Centralization Risk (10 places)\n[low] costly-loop at src/IMD6900RobinhoodLaunch.sol:340: Costly operations inside loop (2 places)\n[low] large-numeric-literal at src/IMD6900.sol:11: Large Numeric Literal (7 places)\n[low] literal-instead-of-constant at src/IMD6900Hook.sol:115: Literal Instead of Constant (9 places)\n[low] non-reentrant-not-first at src/IMD6900RobinhoodLaunch.sol:262: `nonReentrant` is Not the First Modifier (4 places)\n[low] push-zero-opcode at src/IMD6900.sol:2: PUSH0 Opcode (4 places)\n[low] require-revert-in-loop at src/IMD6900RobinhoodLaunch.sol:340: Loop Contains `require`/`revert`\n[low] state-no-address-check at src/IMD6900LockedPool.sol:99: Address State Variable Set Without Checks (3 places)\n[low] unchecked-return at src/IMD6900LockedPool.sol:118: Unchecked Return (6 places)\n[low] uninitialized-local-variable at src/IMD6900RobinhoodLaunch.sol:554: Uninitialized Local Variable\n[low] unspecific-solidity-pragma at src/IMD6900.sol:2: Unspecific Solidity Pragma (4 places)\n[low] unused-public-function at src/IMD6900Hook.sol:101: Public Function Not Used Internally (3 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"93b6dc0740913da974126deade2fd0a095e9ceaebb32775a917ddf398664aae0","verifiedTreeHash":"6ba44e83da4919868ed6c37ec19bacf697f19411","verifierVersion":"0.1.0+327e1e9f"}]}