{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"6154a55e-4637-4f4b-8197-d29f9f552eff","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"d16db66ea91a00665edecc6cdbbd969d160e3470f4dd4c45f05fc0d15f11044c","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"2d214f95ef23fc7623adc046190513c8c2a936a0747e73a14d6fac4fca3bde8a","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"ba246e3097131c9684ebf085427e38e1ae30c35da7f62d0535f6a1e9b0ef62bc","dependsOn":["build_contract_project","write_foundry_tests","manifest","audit_math","audit_permissions","audit_economics","audit_flow"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","tools":[]},"key":"audit_judge","kind":"code","role":"review","skillHash":"3014f1ea5961918ca059453a484bf4c8bcbbfc2248dbe31d94ac7c5cdf8f50bd","skillId":"audit-judge","state":"accepted"},{"acceptedSubmissionHash":"bd21d932e28b59b274b589227dac631243330a818589972674d338ef79aeffeb","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"170f74eaa93c95c8f547cae4267b233f950f4384b795bb6f52580f1182a838c3","dependsOn":["build_contract_project","write_foundry_tests","manifest"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"fd0395228c39c844a699c5128fb5b714e9456250c63a5c2ac85c2a8c32c37034","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"587567a5ef71546b5973ceaf6ef1dbbd590f49503b5ac9a6973ed86b89584857","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"47e321b646a0773e708e9837743829bd47fd7ff2bf7fc99321a092c3680d3d80","dependsOn":["build_contract_project","write_foundry_tests"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"b66347e61a29f0c19c3755ecd3a3508ae49d40d772bd9e27bbbbaf0b0513203a","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","tools":[]},"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"73431852439ad3a343f3d2b7db1c43cd363b9f48f9bb51497374a0cf6d50b223","skillId":"write-foundry-tests","state":"accepted"}],"objective":"[SIMD-LAUNCH]\nToken name: IMD RISK\nToken symbol: RISK\n\nIMD RISK (RISK) is minted at 1,000,000,000 tokens with 18 decimals, with 10% allocated to the swarm by the factory and 90% seeded into the Uniswap v4 pool with IMD (0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7). The pool fee is fixed at 1.25%, per SIMD launchpad rules.\n\nCONTRACT: RISKHook (the launch pool's hook)\n1.\n\nMechanics specification (authoritative: where anything above differs, follow these exactly):\nM1. Buyback and burn in batches: on EVERY swap (buys and sells) the hook takes an extra 1% in afterSwap from the actual BalanceDelta, on the unspecified currency (afterSwapReturnDelta), so the fee always matches what filled. On a buy that currency is usually the launched token; on a sell it is usually the paired currency. Launched-token fees are burned: an anyone-callable sweep() sends them to 0x000000000000000000000000000000000000dEaD. Paired-currency fees accrue in the hook to fund buybacks and are never spent inside a swap callback. executeBatch(), callable by anyone in its own transaction, runs at most once every 3600 seconds; its budget is up to 25% of the accrued paired-currency balance, and it swaps exact-input through the PoolManager (unlock and swap) with a sqrtPriceLimit 300 bps beyond a time-weighted reference price kept by the hook, accepting a partial fill: whatever does not fit inside that limit stays accrued for the next batch, so the buyback can never deadlock. The price limit is the only slippage guard (no minimum-output check that hardcodes the LP fee). Batch swaps are made by the hook itself and pay no hook fee. Bought tokens go to 0x000000000000000000000000000000000000dEaD. Immutable constants; views pending(), pendingBurn(), lastBatch(), referencePrice().\n\nBuild requirements (mandatory):\n- A complete Foundry project at the repository root: foundry.toml with solc 0.8.26, evm_version cancun, optimizer on and bytecode_hash = \"none\", so the build is reproducible and verifiable: deployed contracts live in src/ and every import resolves to a committed file.\n- Contracts: RISKHook. The hook is the hook of this launch's pool; keep its creation code within the EIP-3860 size limit.\n- No selfdestruct and no delegatecall anywhere in runtime code. No proxies, no owner, no upgradeability.\n- Chain: Ethereum mainnet (set by the order, not a launch.json field). Uniswap v4 PoolManager: 0x000000000004444c5dc75cB358380D2e3dE08A90 (pass it to the hook constructor).\n- launch.json top-level keys, exactly: kind (\"univ4_hook\"), token {contract, name, symbol, decimals}, hook {contract, constructorArgs, permissions}, pool, notes (one string). No chainId, economics or other keys.\n- Paired currency: IMD, the ERC-20 at 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7 on Ethereum mainnet (18 decimals).\n- Every address the hook needs is known now and fixed at deployment; nothing may require an owner or a setter after launch.\n- Supply distribution is done by the launch factory: it mints the supply, seeds the pool, sends the swarm's 10% through its Merkle distributor and any remainder to remainderTo. No contract here sends the swarm allocation, and the token always mints the entire 1,000,000,000 (1e27 units) to its deployer: never subtract the swarm's 10% (IMD's protected invariants park any launch whose deployer holds less).\n- Hook fees are collected through beforeSwap/afterSwap return deltas, on top of the pool's static 1.25% LP fee (fee tier 12500). Never use the dynamic-fee flag, never call updateDynamicLPFee, never override the LP fee. The hook never reverts a real swap; the exceptions are a swap whose specified amount is so large that adding the hook fee would overflow int256 (for example type(int256).max requests): it may revert with UnrepresentableFee. That is the accepted swap domain.\n- The hook is a plain immutable contract deployed directly at a CREATE2-mined address with the right permission bits, and launch.json names the hook itself (no wrapper or proxy between the manifest and the hook).\n- Tests: Foundry unit, fuzz and mainnet-fork tests that swap through the real PoolManager with the hook (exact-input and exact-output, buys and sells), plus permission bits matching the hook address.\n- Every hook fee is proportional to what actually filled. Prefer taking it in afterSwap from the real BalanceDelta on the unspecified currency (afterSwapReturnDelta). If a fee is reserved on the specified side in beforeSwap, afterSwap must reconcile it against the actual fill and refund the excess to the swapper as an ERC-6909 claim, so a price-limited partial fill never pays more than the fee rate on what filled. Test exact-input and exact-output partial fills with a price limit.\n- launch.json pool: pairedCurrency 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7, fee 12500, tickSpacing 60, initialPrice \"79228162514264337593543950336\" (provenance only; the launch factory sets the opening price from the economics). Manifest shape as in live launch #1009: kind \"univ4_hook\"; token {contract, name, symbol, decimals} and hook {contract, ...} where each contract is a bare Solidity contract name like \"RISK\" or \"RISKHook\" (never a path or \"File.sol:Name\"); hook {contract, constructorArgs (e.g. [\"$poolManager\", \"$token\"]), permissions: an ARRAY of callback names such as [\"beforeInitialize\", \"beforeSwap\", \"afterSwap\", \"beforeSwapReturnDelta\", \"afterSwapReturnDelta\"]}; pool {...}; notes: a string explaining constructor args, permission bits and fees.","parentJobId":null,"planHash":"f5c35502de82bf04992e856395bf305679a84b2b81825e33c5d9bc39581002cf","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"6154a55e-4637-4f4b-8197-d29f9f552eff","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-1113-imd-risk"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52420","feedbackHash":"8de56c3b47406d0f05974b98be9f7144834f541a4c15a6933a1aaf835c0e522d","nodeKey":"audit_economics","submissionHash":"d16db66ea91a00665edecc6cdbbd969d160e3470f4dd4c45f05fc0d15f11044c","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51886","feedbackHash":"f8cab7bfd805b597d5d593b8589084c8785b691bc194a133c4beabe1bd7932e7","nodeKey":"audit_flow","submissionHash":"2d214f95ef23fc7623adc046190513c8c2a936a0747e73a14d6fac4fca3bde8a","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"50970","feedbackHash":"497e8006884929a1493e12743d9aa6ebf7d8c67c9e93b46007dd169e1f08dc06","nodeKey":"audit_judge","submissionHash":"ba246e3097131c9684ebf085427e38e1ae30c35da7f62d0535f6a1e9b0ef62bc","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52250","feedbackHash":"abab57b45ec8b24cd5e23c320a233f6d5ead1f8f42eb4f6b2d17a5a9653939df","nodeKey":"audit_judge","submissionHash":"ceea07d16a03730a554646a300f89b2704a99f0add548540f9f5036e7caadaba","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51883","feedbackHash":"fc9e834edfa178343d1265041ae1f0c0f13c277a222fcc4e6ab8f92cfb2d20b3","nodeKey":"audit_math","submissionHash":"bd21d932e28b59b274b589227dac631243330a818589972674d338ef79aeffeb","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51885","feedbackHash":"a95fa9dba8f330ab0c32e268a568b656a2e70d9d42666c817a25c3a83a726f21","nodeKey":"audit_permissions","submissionHash":"170f74eaa93c95c8f547cae4267b233f950f4384b795bb6f52580f1182a838c3","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52119","feedbackHash":"3ec69d7b685c86f1113f44cfe386f3b7d79a3b1bc1d43fc30ce0ab2a393bec17","nodeKey":"build_contract_project","submissionHash":"fd0395228c39c844a699c5128fb5b714e9456250c63a5c2ac85c2a8c32c37034","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52487","feedbackHash":"49d25440287efa6f658b35d9bf22dad5011c9c5ffd68abd8118f1dcaac274426","nodeKey":"build_contract_project","submissionHash":"4d9bfae1b8db9ff94d9f2a557d938170df1c64598829ac18384983e5bf6bb99a","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51446","feedbackHash":"6900ccf90f634d355c680a5017efc1bb3a36e4c8824b2a496fb9a68ebc1e973b","nodeKey":"manifest","submissionHash":"a38b0b6529ad13611900a05a449df192260b020947ef8e020580a4034cdb3345","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51981","feedbackHash":"9f8726c87f7094e8dcf3cae28338a93ec0d34e7ca9a72256396b0d955f850466","nodeKey":"manifest","submissionHash":"47e321b646a0773e708e9837743829bd47fd7ff2bf7fc99321a092c3680d3d80","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51046","feedbackHash":"ed30f5a8082c873383cc27ddaba7120dec1959be2993ea02e2fefd8447ec8125","nodeKey":"write_foundry_tests","submissionHash":"0a2dfaf9373d4ca1d53eb4ee09b32c3c98befabe0bda88dd6abefb2294b03ddd","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51983","feedbackHash":"6970c0195a148e0bb4ee7f7544e85150e25ce628dab1dba263c5a8bfe3d7aa36","nodeKey":"write_foundry_tests","submissionHash":"b66347e61a29f0c19c3755ecd3a3508ae49d40d772bd9e27bbbbaf0b0513203a","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"72280b8d182fa96770db364d7eb52cdb98bcce013a6724386a30b9c742092309","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"e040f670ca3cb380f6ec82f376616ac35c9f7a117cc96e45bb25e55e72fe6df7","device":"02ae6543274731ab","findings":[],"hash":"0a2dfaf9373d4ca1d53eb4ee09b32c3c98befabe0bda88dd6abefb2294b03ddd","nodeId":"d26fb80e-e827-4ff5-b79b-72789208b1dd","outcome":"completed","summary":"Added adversarial unit, fuzz, invariant, and mainnet-fork tests under `test/`.\n\nValidation:\n- `forge build` passed.\n- Offline: 52 passed; 2 fork suites skipped.\n- Mainnet block 26,151,212: all 10 fork tests passed.\n- New invariant: 16,384 randomized actions, no unexpected reverts.\n\nNo reproducible implementation defects found.","treeHash":"cb0727284ec27ee355527a65456f08692540b67f","usage":{"cachedInputTokens":2051200,"inputTokens":104067,"model":"gpt-6-astra","outputTokens":22134,"runtime":"codex","turns":7,"wallClockMs":696547}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"aca5d7170d77c721","findings":[{"citation":"resolved","description":"beforeInitialize restricts the caller to the PoolManager and pins the exact RISK/IMD key (fee 12500, spacing 60), but it ignores the sender argument and accepts any sqrtPriceX96. The launch design says the factory deploys the hook and calls PoolManager.initialize in the same transaction, and the reference explicitly allows a hook that does not gate on the factory, so this is a trust assumption on the factory flow rather than a code defect. It is recorded so the operator verifies atomicity: if the CREATE2 deployment of RISKHook and the factory's initialize were ever split into two transactions, an outsider could initialize the one pool this hook will ever accept at an extreme opening price. The factory's own initialize would then revert (PoolAlreadyInitialized), the hook address would be burned for this launch (initialized is sticky), and a new salt would be required. No funds are at risk; the hook's referencePrice would start from the outsider's price. Nothing else in the access-control, trust-gap and asymmetry passes produced a defect: every callback and unlockCallback checks the PoolManager, unlockCallback additionally requires the operating flag so only the hook's own sweep/executeBatch unlocks reach it, the afterSwap claim mint is matched exactly by the returned unspecified delta, the hook's own batch swap is exempt through v4's noSelfCall, and the permissionless executeBatch was tested for same-block sandwiches and zero-fill griefing (see coverage rows) without finding a profitable path.","line":92,"path":"src/RISKHook.sol","reproduction":"State: RISKHook deployed at a mined 0x..20c4 address against a PoolManager, pool NOT yet initialized (deployment and initialize in separate transactions). Call, from any EOA: manager.initialize(hook.poolKey(), TickMath.MAX_SQRT_PRICE - 1). Expected under the atomic factory flow: unreachable. Actual when reachable: the call succeeds, hook.initialized() == true, hook.referencePrice() == getSqrtPriceAtTick(MAX_TICK - 1), and the factory's subsequent manager.initialize(key, 79228162514264337593543950336) reverts. Verified in test/scratch/Init.t.sol test_thirdPartyInitializesBeforeFactoryWhenNotAtomic (passes against the current code, i.e. the sequence is accepted).","severity":"info","snippet":"    function beforeInitialize(address, PoolKey calldata key, uint160 sqrtPriceX96)\n        external\n        onlyManager\n        returns (bytes4)\n    {\n        PoolKey memory expected = poolKey();\n        if (initialized || PoolId.unwrap(key.toId()) != PoolId.unwrap(expected.toId())) revert InvalidPool();","title":"beforeInitialize is sender-agnostic: pool can be opened by anyone at any price if hook deployment and initialization are not in one transaction"}],"hash":"170f74eaa93c95c8f547cae4267b233f950f4384b795bb6f52580f1182a838c3","nodeId":"82e2507d-52d0-4c3b-b1e1-6013779a5e6e","outcome":"completed","summary":"Review complete. The findings file is written and validated against the tree.\n\n**Outcome**\n\nNo defect in the Access Control, Trust Gap or Asymmetry areas. The file `.imd-findings.json` holds one informational entry and a 13-row coverage record answering all 9 listed entry points plus four invariants.\n\n**What I checked, with results**\n\n- **Caller guards.** Every callback and `unlockCallback` requires the PoolManager. The unlock callback additionally requires the hook's own `operating` flag, so its decoded bool can never come from an outsider. A nested unlock from a third party's callback reverts with AlreadyUnlocked, and the suite already tests this.\n- **Initialization.** The hook pins the one RISK/IMD key with fee 12500 and spacing 60, so no second pool can ever route through it and corrupt the TWAP or mint foreign-currency claims. The sender and opening price are unchecked. Under the stated atomic deploy-and-initialize factory flow this is unreachable, so it is recorded as info with a reproduction showing an outsider can open the pool first only if the two steps are split.\n- **Fee symmetry.** The afterSwap claim mint is matched exactly by the returned unspecified delta in all four swap modes and both currency orderings. The hook's own batch swap is exempt through v4's noSelfCall rather than the dead `sender == this` check.\n- **Permissionless executeBatch economics.** Using scratch tests on a 1e24-liquidity pool I measured the two seams the Trust Gap guide targets. A same-block push past the price band to force a zero fill costs the attacker about 876 IMD to delay a 225 IMD budget. A buy, batch, sell sandwich loses 20 IMD at spot equal to reference and 37 IMD after a 30% dump. The single price guard is not extractable by the caller.\n- **Claims.** Nobody can burn or transfer the hook's ERC-6909 claims; outsiders can only donate to the burn or buyback.\n- **Manifest.** Permission bits, constructor argument order, pool fields and the fixed IMD address all agree with the code.\n\n**Static analysis leads** were all traced and dropped: the weak-PRNG line is a floor-division remainder check, the reentrancy lines only reach the PoolManager and the plain OZ RISK token, and the int24 cast is a bounded tick mean.\n\n**Not reached.** The live mainnet IMD token and the real factory's seeding could not be exercised offline. The hook never transfers IMD itself, so IMD transfer quirks cannot block sweep or batch, but the operator's fork rehearsal still owes that run.","treeHash":null,"usage":{"cachedInputTokens":2100747,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":50208,"runtime":"claude","turns":37,"wallClockMs":668880}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"dd3018ab6b18e7bc","findings":[{"citation":"resolved","description":"referencePrice() returns the mean tick of the last COMPLETED observation period whenever the current period is younger than INTERVAL (lines 155-160). A period only completes on an observation (swap or batch) at least 3600 s after periodStart, so when executeBatch() runs less than an hour after the previous boundary, the limit at line 197 is derived from a price that ended up to ~1 h earlier and averaged the hour before that. The 300 bps bound is therefore relative to that old price, not to the pool's spot immediately before the batch. This matches the brief (\"sqrtPriceLimit 300 bps beyond a time-weighted reference price\" and \"the price limit is the only slippage guard\") and the README documents it, so it is not a specification defect. It is reported because the measured gap between the guard and the pre-batch spot is much wider than 300 bps after an ordinary move, which is the state an MEV searcher would look at. In the measured scenario the searcher's sandwich and a cooldown-wasting grief were both unprofitable in the test pool (1e24 full-range liquidity, 2.25% round-trip fees), so no loss of funds is claimed; the note is for the author to decide whether a trailing window ending at batch time (which referencePrice() already computes once the period has matured) should be used for every batch.","line":193,"path":"src/RISKHook.sol","reproduction":"test/scratch/StaleReference.t.sol (probe, imports the project's HookFixture) on the default fixture (IMD = currency0, full-range liquidity 1e24, opening price 1:1). t0: sell 1e23 RISK (accrues ~1290 IMD of fees). t0+3600: a 1e18 buy completes period 1; referencePrice() = ratio 1.2072e18 (RISK per IMD). t0+4200: a legitimate 5e22 RISK sell moves spot to ratio 1.3182e18 (RISK ~9% cheaper). t0+4800: executeBatch(). Observed: referencePrice() unchanged at 1.2072e18 (period 2 is only 1200 s old); the batch limit is ratio 1.1710e18, i.e. 574 bps below the pre-batch spot in sqrt-price terms (~11.8% in price), not 300 bps; the batch spent its full 322.5 IMD budget inside that window. Sandwich attempt (front-run buy to 95% of the way to the limit, batch, back-run sell): attacker net -2162 IMD against a 322 IMD batch, so unprofitable here. Cooldown grief (push spot beyond the limit, executeBatch zero-fills and advances lastBatch, sell back): griefer net -622 IMD to delay 899 IMD of pending for one hour; pending is retained, not lost. Expected per README: the bound applies to the reference, which is what happens; the finding only quantifies how far that reference can sit from spot.","severity":"info","snippet":"        uint160 ref = referencePrice();","title":"Batch price guard is anchored to a reference that can be up to two hours stale, so the 300 bps bound is measured from an old price, not from the spot at batch time"}],"hash":"2d214f95ef23fc7623adc046190513c8c2a936a0747e73a14d6fac4fca3bde8a","nodeId":"9930be64-c5b1-4fcc-b803-c152281966f4","outcome":"completed","summary":"The findings file is in place and excluded from git by the harness's own `.git/info/exclude`, so no tracked file changed. The review is complete.\n\n## Outcome\n\n**No loss-of-funds or breakage defect found in the assigned area.** The report at `.imd-findings.json` carries one informational finding and a 14-row coverage record. All 9 entry points are answered: 8 hold, executeBatch is linked to the informational note, and only the offline-skipped mainnet fork suites are marked unreached.\n\n**What I traced (Execution Trace, Periphery, First Principles):**\n\n- **Fee path.** The unspecified-side selection in afterSwap matches the PoolManager's own delta positioning in all four swap modes. The claim mint plus the returned delta net the hook's balance to zero, and the swapper pays the fee. The self-swap exemption is enforced by the manager, not the hook.\n- **Batch path.** Limit sign, rounding and clamping are correct for both currency orderings. The zero-fill guard prevents the manager's price-limit revert, spent can never exceed pending, and every suite ends with zero nonzero deltas. Probes at the minimum and maximum opening price, after a century of inactivity, and with the period completing inside the batch all behaved correctly.\n- **Periphery.** All 86 vendored files are plain blobs, no submodules. I fetched upstream v4-core at the pinned commit and OpenZeppelin v5.1.0: the only differences are forge-fmt whitespace in ten v4-core files. IMD on mainnet is a LayerZero OFT ERC-20 with 18 decimals and no pause or blocklist selectors, and the hook never transfers it. The sqrt constants round in the conservative direction. The manifest satisfies every schema constraint and matches the constructor and permission bits.\n\n**The one finding (info).** The batch's 300 bps guard is anchored to a reference that can be up to two hours stale. In a measured scenario the guard sat 574 bps in sqrt-price terms from the pre-batch spot. This matches the brief and README, and both a sandwich and a cooldown-wasting grief were unprofitable in the test pool, so no loss is claimed. It is there for the author to decide whether to use the trailing window that referencePrice already computes once a period matures.\n\n**Not covered.** The mainnet fork rehearsal at block 26151185 was not re-run. Scratch probes live under test/scratch, which is ignored and will not be kept.","treeHash":null,"usage":{"cachedInputTokens":2952436,"inputTokens":578,"model":"claude-fable-5-1","outputTokens":59759,"runtime":"claude","turns":41,"wallClockMs":872805}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"93652ca7a1cbb0e8","findings":[],"hash":"4035c112b866c202328a4eb3a39df389dce411065fa76e38f4b3180ba49b794e","nodeId":"5760976b-5db3-40ea-9cc1-6f0be32ee65c","outcome":"failed","summary":"could not restore accepted work: accepted work conflicts with a dependency, which plan validation should have prevented: ","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"codex","turns":0,"wallClockMs":590}},{"artifacts":[],"attempt":1,"bundleHash":"7c5a08c2315b1e5fa312ff25455f6f828ecb4974824b0fa37c18f109288a3a39","device":"93678b4d542094a9","findings":[],"hash":"47e321b646a0773e708e9837743829bd47fd7ff2bf7fc99321a092c3680d3d80","nodeId":"5760976b-5db3-40ea-9cc1-6f0be32ee65c","outcome":"completed","summary":"Updated [launch.json](/home/imd/.identitymd/work/6154a55e-4637-4f4b-8197-d29f9f552eff/5760976b-5db3-40ea-9cc1-6f0be32ee65c/launch.json) with verified constructor arguments, permissions, pool settings, and technical notes.\n\nSchema validation and `forge build` passed. `forge test`: 66 passed, 0 failed; 2 fork suites skipped without mainnet state.\n\nOnly `launch.json` changed.","treeHash":"1d06cd1338e14ddede969df03042fdd4ded931f4","usage":{"cachedInputTokens":471296,"inputTokens":64491,"model":"gpt-6-astra","outputTokens":4452,"runtime":"codex","turns":3,"wallClockMs":155648}},{"artifacts":[],"attempt":1,"bundleHash":"313a1164ba604358c671a2aff10c2ae0cbac5d6c96795e4e3bd26bcf016ef5dd","device":"4fa6ac0d62fd2e01","findings":[],"hash":"4d9bfae1b8db9ff94d9f2a557d938170df1c64598829ac18384983e5bf6bb99a","nodeId":"8faa1d5e-280c-42b6-8354-c09d1b02c63c","outcome":"completed","summary":"Implemented RISK, RISKHook, launch.json, vendored dependencies, deployment helper, tests, and documentation.\n\nValidation passed:\n\n- `forge build`\n- `forge test`: 28 passed; fork suite skips offline\n- `forge fmt --check`\n- Mainnet fork at block 26151185: passed\n- 8,192 invariant actions: zero reverts\n\nDeployment parameters and operational responsibilities are documented in README.md. No transactions were broadcast.","treeHash":"b7454ffe5dc6424504ee63e12ed6e08acb488409","usage":{"cachedInputTokens":2284672,"inputTokens":115781,"model":"gpt-6.1-sol","outputTokens":27168,"runtime":"codex","turns":7,"wallClockMs":564976}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"129decb3e5ee9f5f","findings":[],"hash":"9c8295368c5a506ba7cade20e2f8881bc9169dd148cbeaceb8983d0e63871298","nodeId":"8faa1d5e-280c-42b6-8354-c09d1b02c63c","outcome":"failed","summary":"spawn ENAMETOOLONG","treeHash":null,"usage":{"cachedInputTokens":0,"inputTokens":0,"model":null,"outputTokens":0,"runtime":"codex","turns":0,"wallClockMs":1874}},{"artifacts":[],"attempt":1,"bundleHash":"946422f3907ecb2e17b69db51ab102b17f341bdacf7d3b652c7c6042088e6ff3","device":"16b70f563303d8e7","findings":[],"hash":"a38b0b6529ad13611900a05a449df192260b020947ef8e020580a4034cdb3345","nodeId":"5760976b-5db3-40ea-9cc1-6f0be32ee65c","outcome":"completed","summary":"Updated only [launch.json](/Users/gmoney/.identitymd/work/6154a55e-4637-4f4b-8197-d29f9f552eff/5760976b-5db3-40ea-9cc1-6f0be32ee65c/launch.json), clarifying deployment, fees, buybacks, and price provenance.\n\nValidated schema constraints, constructor arguments, and permissions against the implementation.\n\n`forge build` passed. `forge test`: 52 passed, 0 failed, 2 skipped.","treeHash":"1b4a79ee1721d9c0381d8a52520b8db00099e667","usage":{"cachedInputTokens":511232,"inputTokens":61838,"model":"gpt-6-astra","outputTokens":4462,"runtime":"codex","turns":3,"wallClockMs":135223}},{"artifacts":[],"attempt":1,"bundleHash":"397b4b2f533d88064ff8a3457d1aea27228af2f5da0016c13c814060138c8e0f","device":"90675b3128fe9096","findings":[],"hash":"b66347e61a29f0c19c3755ecd3a3508ae49d40d772bd9e27bbbbaf0b0513203a","nodeId":"d26fb80e-e827-4ff5-b79b-72789208b1dd","outcome":"completed","summary":"Updated the partial-buyback test to verify both reference and spot price limits and require the fill to reach the tighter bound.\n\nValidation:\n- `forge build` passed.\n- Offline suite: 66 passed, 2 fork skips.\n- Mainnet fork: 10 passed at block 26,151,445.\n- Invariants: 24,576 calls, zero reverts.\n\nOnly files under `test/` changed.","treeHash":"c22e6b5a44fa14e0fe955642fbf8ce2c3c3eeb3c","usage":{"cachedInputTokens":777600,"inputTokens":86829,"model":"gpt-6-astra","outputTokens":4685,"runtime":"codex","turns":5,"wallClockMs":273331}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"c2a0c6e1f08b5974","findings":[],"hash":"ba246e3097131c9684ebf085427e38e1ae30c35da7f62d0535f6a1e9b0ef62bc","nodeId":"14ff8de4-1a41-46b3-b6c7-de5380cefaf3","outcome":"completed","summary":"Wrote [.imd-findings.json](/home/imd/.identitymd/work/6154a55e-4637-4f4b-8197-d29f9f552eff/14ff8de4-1a41-46b3-b6c7-de5380cefaf3/.imd-findings.json): no substantiated defects, all nine entry points covered.\n\nThe prior medium finding is fixed. Both advisories reproduce but remain documented design choices consistent with the brief.\n\nValidation: 66 offline tests passed; 10 mainnet-fork tests passed at block 26,151,445. Build and formatting checks passed.","treeHash":null,"usage":{"cachedInputTokens":1380864,"inputTokens":138653,"model":"gpt-6-astra","outputTokens":5105,"runtime":"codex","turns":5,"wallClockMs":227943}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"707624586655cf74","findings":[{"citation":"resolved","description":"The limit multiplies the reference sqrt price by sqrt(0.97) when the hook sells currency0 (IMD < RISK) and by sqrt(1.03) otherwise. Both bound the SORTED price currency1/currency0 to +/-3.00%. The economic quantity the spec protects is the price the hook pays for RISK in IMD. When IMD is currency1 (RISK < IMD, ~82% of random RISK addresses since IMD is 0xD34a...) the sorted price IS the IMD price of RISK and the bound is exactly +3.00%. When IMD is currency0 (RISK address > 0xD34a99bc..., ~18% of addresses) the sorted price is RISK-per-IMD and the hook pushes it down to 0.97x; the IMD price of RISK then rises to 1/0.97 = 1.0309278x, i.e. 309.3 bps, not 300. The accepted overpayment therefore depends on which address the factory happens to mint RISK at. Seam: boundary x precision (the band is defined on a ratio whose orientation is a deployment accident). The fix is orientation-aware: when zeroForOne use sqrt(1/1.03) = 0.985329278164292...e18 (rounded up) so that the IMD price of RISK may rise at most 3.00% in both orderings.","line":195,"path":"src/RISKHook.sol","reproduction":"Deploy with IMD as currency0 (the default RISKHookTest fixture does this: RISK is created by the test contract at an address above 0xD34a..., so hook.poolKey().currency0 == IMD). Seed full-range liquidity 1e24, sell 1e23 RISK so IMD claims accrue, thin liquidity to 1e20 so the budget exceeds the band, warp lastBatch()+3600, ref = referencePrice(), call executeBatch(). Observed: slot0 sqrtPrice P with (P/ref)^2 = 0.970000000000000001 (sorted ratio hit the limit) and the IMD price of RISK = 1/0.97 = 1.030927835051546390x ref. Expected per spec 'sqrtPriceLimit 300 bps beyond the reference': at most 1.0300x. In the reverse ordering (RISKHookReverseOrderingTest, RISK at 0xffff...ff9b so IMD is currency1) the same sequence gives exactly 1.03x, showing the asymmetry between orderings.","severity":"low","snippet":"        uint256 product = uint256(ref) * (zeroForOne ? 984885780179610473 : 1014889156509221946);","title":"Buyback price band is 300 bps on the sorted ratio, which is 309 bps on RISK's IMD price when IMD sorts as currency0"},{"citation":"resolved","description":"A period only completes on the first observation at least 3600 s after it started, and the reference used by executeBatch is the mean tick of the last COMPLETED period (or the completing one). Nothing bounds a period from above: in a quiet pool a single period spans every idle second. After a long idle stretch the first swap that moves the price completes the stale period with the OLD tick (beforeSwap observes the pre-swap tick for the whole elapsed time), resets the period, and for the next 3600 s the reference equals that old price. executeBatch in that hour builds its limit as old_price * 1.03, which can be tens of percent away from the current market. The hook itself still buys at spot (budget is small relative to liquidity), so the direct loss is nil, but the only slippage guard is then nominal: a sandwicher may push the price up to the stale limit before the batch and sell back after it, capturing up to (1 - spot/limit) of the batch budget rather than at most ~3%. Seam: boundary x invariant (the invariant 'the hook never buys more than 3% above a recent price' fails exactly at the quiet-period boundary). A rolling window, or capping the integration span (e.g. weight the idle tick for at most N hours), keeps the reference recent.","line":223,"path":"src/RISKHook.sol","reproduction":"Default fixture (IMD is currency0, liquidity 1e24). (1) sell 5e22 RISK exact-input; (2) vm.warp(+2 days) with no swaps; referencePrice() = 83136132562774675878473923604, spot = 83140053038406139262225182883; (3) sell 3e23 RISK: spot becomes 106611396183256949274312578170 (RISK price in IMD falls from 0.908 to 0.552), referencePrice() stays 83136132562774675878473923604 because the 2-day period just completed with the old tick; (4) vm.warp(+1 s), executeBatch(): limit = ceil(ref*984885780179610473/1e18) = 81879594780203855709808637186, i.e. the hook is authorised to pay up to 0.9363 IMD per RISK while the market is 0.5523 (+69.5%). Expected from 'sqrtPriceLimit 300 bps beyond a time-weighted reference price': the permitted price stays within a few percent of recent trading, not 69% above it. In this run the hook spent its full budget 642127264282396655828 for 1147191445748234314207 RISK at 0.5597 avg (spot was 0.5523) only because nobody front-ran it.","severity":"low","snippet":"        if (duration >= INTERVAL) {","title":"Observation window is unbounded, so the 'time-weighted reference' can be days old and the 3% guard can sit far above the market"},{"citation":"resolved","description":"v4-core Hooks.beforeSwap and Hooks.afterSwap return early when msg.sender == address(self), so neither RISKHook.beforeSwap nor RISKHook.afterSwap ever executes for the hook's own batch swap. The branch at line 131 is dead code and the comment in launch.json/README ('Batch swaps ... pay no hook fee') holds for a different reason. This matters for the TWAP: the _observe() and spotTick refresh inside afterSwap do not run for the batch, and correctness depends entirely on the explicit re-read of slot0 at line 213 after the swap. That re-read is present (and tested by test_batchPriceFeedsNextObservationDespiteSelfCallbackExemption), so there is no defect today; the note is to prevent a future edit that moves the spot update back into afterSwap, or that removes line 213 believing afterSwap covers it.","line":131,"path":"src/RISKHook.sol","reproduction":"Default fixture: sell 1e22 RISK, warp lastBatch()+3600, then vm.mockCallRevert(address(hook), RISKHook.afterSwap.selector, ...) and vm.mockCallRevert(address(hook), RISKHook.beforeSwap.selector, ...). executeBatch() succeeds and pending() decreases, proving neither callback is invoked on the hook's own swap; the sender == address(this) branch can never be taken.","severity":"info","snippet":"        if (sender == address(this)) return (IHooks.afterSwap.selector, 0);","title":"afterSwap's self-exemption branch is unreachable; the batch's fee exemption and TWAP update rely on v4's self-call skip and line 213 instead"}],"hash":"bd21d932e28b59b274b589227dac631243330a818589972674d338ef79aeffeb","nodeId":"a585d3d5-c60d-4e2b-8e7a-fe1e05a26f39","outcome":"completed","summary":"The review is complete. The findings file is written, snippets match the source lines, and the scratch tests are removed so the tree is clean apart from `.imd-findings.json` (which git ignores).\n\n**Outcome: no critical or high defects in the math.** The hook's arithmetic holds up under the Math Precision, Boundary and Numerical Gap checklists. I recorded two low findings and one informational note.\n\n**What I verified as sound**\n\n- The 1% fee is computed from the actual `int128` fill on the unspecified side, with the sign conventions matching v4's `Hooks.afterSwap` in all four swap modes and both currency orderings. It is widened before negation, so `int128.min` is safe, and the `int128` return cannot overflow.\n- The `UnrepresentableFee` guard is overflow-safe for `type(int256).min` and only rejects the nominal domain the spec explicitly allows.\n- The limit multipliers are correct to 18 digits and rounded toward a tighter band. The product fits `uint256` with room to spare, and the clamps plus early-return conditions mirror v4's strict price-limit checks exactly, so the batch has no revert path.\n- The time-weighted mean floors toward negative infinity, stays inside the tick range, and the view matches what `_observe` commits. The slither \"weak PRNG\" and reentrancy leads are false positives.\n- Dust behaviour is benign: sub-100-wei fills pay no fee (documented), and a 98-wei batch budget still bought 95 wei of RISK.\n\n**Findings**\n\n1. **Low.** The 300 bps band is defined on the sorted price ratio. When IMD happens to sort as currency0 (RISK minted above `0xD34a...`, roughly 18% of addresses), the IMD price of RISK may rise 309.3 bps instead of 300. Reproduced in the default fixture: the sorted ratio hit 0.970000000000000001 and the RISK price was 1.0309x the reference.\n2. **Low.** Observation periods have no upper bound, so after a quiet stretch the reference can be days old. Reproduced: two idle days, then a large sell, and one second later the batch was authorised to pay 0.936 IMD per RISK against a 0.552 market, 69.5% above spot. The hook itself still bought near spot, so the exposure is a widened sandwich window, not direct loss.\n3. **Info.** The `sender == address(this)` branch in `afterSwap` is unreachable because v4 skips a hook's own callbacks. The TWAP depends on the explicit slot0 re-read after the batch swap, which is present and tested.\n\n**Coverage.** All nine entry points have a row, plus four invariant and lead rows. No proof files were attached since nothing reached high severity.","treeHash":null,"usage":{"cachedInputTokens":1729010,"inputTokens":450,"model":"claude-fable-5-1","outputTokens":47662,"runtime":"claude","turns":35,"wallClockMs":810056}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"fc87c05048aca47d","findings":[{"citation":"resolved","description":"Merged from audit_economics #1 (medium), audit_math #2 (low) and audit_flow #1 (info): one root cause. The batch swap's only slippage guard is sqrtPriceLimit = referencePrice() * sqrt(0.97) (or sqrt(1.03)). referencePrice() (lines 153-162) is the mean tick of the last COMPLETED observation period; a period completes only on the first observation at least 3600 s after it started (lines 222-228), and nothing bounds a period from above. So the reference lags spot by up to ~2 h in an active pool and by arbitrarily long in a quiet one: after an idle stretch, the first swap that moves the price completes the stale period with the OLD tick (beforeSwap observes the pre-swap tick for the whole idle span) and the reference then equals the old price for the next 3600 s. The limit at line 197 is never tightened against the pre-batch spot read at line 202. Consequences, both reproduced: (a) the hook is authorised to pay up to ~70% above the current market (the 300 bps bound is only relative to the stale reference), and (b) because executeBatch() is permissionless and its eligibility (lastBatch + 3600, pending()/4) is public, a searcher can in one transaction buy RISK up to just short of the limit, call executeBatch() so the hook spends its budget into the inflated price, then sell back. The attacker's 4.5% round-trip fee burden (2 x (1.25% LP + 1% hook)) makes this unprofitable when spot == reference (window 3%), but once spot has moved more than ~10-15% below the reference and the budget is a few percent of in-range IMD depth, 11-22% of the batch budget goes to the searcher and the burn shrinks accordingly. The budget grows naturally in exactly that situation: during a rally batches zero-fill (limit below spot) and pending() accumulates, so the first batch after a sharp drop meets both a wide window and a large budget. Who loses: RISK holders, whose buyback IMD is paid to the searcher instead of being burned. M1 is met literally ('300 bps beyond a time-weighted reference') but the reference the author built can be days old, so the guard does not do what M1 uses it for. Fix compatible with M1: anchor the band to the tighter of reference and pre-batch spot, i.e. anchor = zeroForOne ? max(ref, current) : min(ref, current) before multiplying (a partial fill already carries the unspent budget forward), and/or cap the time weight of an idle tick so a period cannot integrate days of silence.","line":193,"path":"src/RISKHook.sol","reproduction":"All runs use the vendored PoolManager, the fixture of test/RISKHook.t.sol (RISK deployed by the test, so IMD sorts as currency0 and the buyback is zeroForOne), opening price 1:1. (A) Thin pool, 1e22 full-range liquidity: 320 rounds of exact-input sell 1e21 RISK then buy back with the IMD received (pending() = 1773997648044519196547); vm.warp(t0+3599); exact-input sell 5e21 RISK; vm.warp(t0+3600). Now referencePrice() ~= opening tick (TWAP of the hour) while the IMD price of RISK at spot is 0.594x the reference, and the hook's limit sits at 1.736x spot. Honest executeBatch(): spent 446958676121681606680 IMD, bought (burned) 1866626874985864239689 RISK. Instead, from one EOA in one block: router exact-input buy of RISK with sqrtPriceLimit set 30% of the way (in sqrt-price terms) from the hook's limit back toward spot, then hook.executeBatch(), then sell all RISK received: attacker IMD balance +97845989370565726735 (21.9% of the budget); the batch spent the full budget but burned only 1308994178807658680757 RISK (30% less). Same sequence without the t0+3599 dump (spot == reference): attacker loses IMD at every front-run size tried (2%..80% of the window). (B) Deep pool, 1e24 liquidity: 320 rounds of 1e23, dump 3e23 at t0+3599: budget 44578470998019561858544 IMD (4.45% of depth), spot 0.72x reference; best sandwich +4995984770845785129357 IMD (11.2% of budget), hook burns 15% less. With budget 0.69% of depth the sandwich is unprofitable. (C) Staleness, default fixture: sell 5e22 RISK; vm.warp(+2 days) with no swaps (referencePrice() 83136132562774675878473923604, spot 83140053038406139262225182883); sell 3e23 RISK: spot becomes 106611396183256949274312578170 (IMD price of RISK -39%) while referencePrice() is unchanged because the 2-day period just completed with the old tick; vm.warp(+1 s); executeBatch() limit = 81879594780203855709808637186, which in IMD-per-RISK terms is 1.695x the pre-batch spot. The hook spent its full 642127264282396655828 IMD budget only ~1.3% above spot because nobody front-ran it; the window stays open for 3600 s (reference only moves to 106518323703303335197250488984 once the next period completes). Expected: a slippage guard of 300 bps beyond a time-weighted reference keeps the batch within a few percent of recent trading and cannot be sandwiched against a 4.5% fee burden. Actual: the permitted adverse move is up to ~70% of market and 11-22% of the budget is extractable.","severity":"medium","snippet":"        uint160 ref = referencePrice();\n        // sqrt(0.97), rounded UP; sqrt(1.03), rounded DOWN: conservative price bounds.\n        uint256 product = uint256(ref) * (zeroForOne ? 984885780179610473 : 1014889156509221946);","title":"Buyback price limit is anchored only to a lagging, unbounded-age reference, so executeBatch() can buy far above market and be sandwiched for profit"},{"citation":"resolved","description":"From audit_math #1, reproduced; recalibrated to info because the README documents that 'the bound applies to the sorted pair's price ratio' and the brief's 'sqrtPriceLimit 300 bps beyond a time-weighted reference price' reads naturally as Uniswap's currency1/currency0 price. When IMD < token (zeroForOne) the hook lowers the sorted ratio (RISK per IMD) to 0.97x, so the IMD price of RISK may rise to 1/0.97 = 1.0309x, i.e. 309.3 bps, not 300. When token < IMD the sorted ratio is the IMD price of RISK and the bound is exactly 300 bps. Which case applies depends on the address the factory mints RISK at (IMD is 0xD34a..., so roughly 18% of addresses sort above it). If the author wants exactly 300 bps in the paired currency in both orderings, use sqrt(1/1.03) = 985329278164293152 (rounded up) for the zeroForOne multiplier.","line":195,"path":"src/RISKHook.sol","reproduction":"Default fixture of test/RISKHook.t.sol (hook.poolKey().currency0 == IMD). vm.warp(+3600) so the reference is the opening price; ref = referencePrice(); limit = ceil(ref * 984885780179610473 / 1e18). In IMD-per-RISK terms (ref/limit)^2 = 1030927835051546390e-18, i.e. +309.3 bps. In test/RISKHook.t.sol's reverse ordering (RISK at 0xffff...ff9b, IMD is currency1) the same computation with 1014889156509221946 gives (limit/ref)^2 = 1.0300x exactly. Expected per 'sqrtPriceLimit 300 bps beyond': 1.0300x in both orderings; actual: 1.0309x in the zeroForOne ordering.","severity":"info","snippet":"        uint256 product = uint256(ref) * (zeroForOne ? 984885780179610473 : 1014889156509221946);","title":"The 300 bps band is applied to the sorted price ratio, which is 309 bps on RISK's IMD price when IMD sorts as currency0"},{"citation":"resolved","description":"From audit_economics #2, reproduced; recalibrated to info. executeBatch() writes lastBatch = block.timestamp (line 172) before unlocking, and unlockCallback returns successfully with zero spend when spot is already beyond the limit. Anyone can therefore, in one transaction, push spot past referencePrice() * sqrt(0.97), call executeBatch() (zero fill, cooldown consumed), and sell back; the honest keeper's batch then reverts TooSoon for 3600 s. No funds are lost: pending() is retained in full and 1% of the griefer's own round trip accrues to the hook. The README documents that an empty or zero-fill batch advances the cooldown, M1's 'at most once every 3600 seconds' is satisfied, and the griefer pays about 4.5% of a ~1.5%-of-depth excursion every hour (702.6e18 IMD per hour against 1e24 liquidity) for no gain beyond delay, so this is a design note rather than a defect. If the author wants to close it: do not advance lastBatch when spent == 0 (or advance it by a short anti-spam period), or combine with the spot-anchored limit in finding 1 which makes the push explicit.","line":204,"path":"src/RISKHook.sol","reproduction":"Default fixture (1e24 full-range liquidity, IMD = currency0). (1) exact-input sell 1e22 RISK so pending() > 0; vm.warp(lastBatch()+3600) (reference == spot). (2) Griefer exact-input buys RISK with 1.6e22 IMD, which moves spot below referencePrice()*sqrt(0.97). (3) Griefer calls executeBatch(): succeeds, BatchExecuted(budget, 0, 0, limit), pending() unchanged, DEAD balance unchanged, lastBatch() == block.timestamp. (4) Griefer sells the RISK back: net cost 702614111395279495460 IMD. (5) executeBatch() reverts TooSoon in the same block and again at +3599 s. Expected: a batch that bought nothing need not lock the buyback for an hour; actual: cooldown consumed, repeatable each hour at ~0.07% of depth per hour.","severity":"info","snippet":"        if (budget == 0 || (zeroForOne ? current <= limit : current >= limit)) {\n            emit BatchExecuted(budget, 0, 0, limit);\n            return \"\";\n        }","title":"A zero-fill batch still consumes the hourly cooldown, so a payer can defer the buyback hour by hour with a same-block price push"}],"hash":"ceea07d16a03730a554646a300f89b2704a99f0add548540f9f5036e7caadaba","nodeId":"14ff8de4-1a41-46b3-b6c7-de5380cefaf3","outcome":"completed","summary":"The findings file is in place at the repository root, excluded from git by the harness's own rule, and no tracked file changed. The review is complete.\n\n**Outcome.** The RISKHook code is sound on access control, delta accounting and fee proportionality. One medium finding survives, built from three specialist reports that share a root cause. Two specialist reports are kept as informational notes, and two are dropped as non-defects.\n\n**Finding 1, medium.** The batch's only slippage guard is anchored to a reference that can lag spot by hours, or by days in a quiet pool, and is never tightened against the pre-batch spot. I reproduced three consequences against the real PoolManager:\n\n- Thin pool, spot 40% below the reference: a one-block sandwich around executeBatch nets the attacker 21.9% of the batch budget, and the burn shrinks by 30%.\n- Deep pool, budget at 4.45% of depth, spot 28% below reference: the attacker takes 11.2% of the budget.\n- Two idle days followed by a sell: the hook is authorised to pay up to 1.695 times the market price for one full hour.\n\nThe sandwich is a loss for the attacker whenever spot equals the reference, so the 4.5% fee burden works as designed in the normal case. The exposure arises naturally after a rally, when zero-fill batches let pending accumulate before a sharp drop. The suggested fix keeps M1 intact: anchor the band to the tighter of reference and spot, and cap how long an idle tick can be weighted.\n\n**Informational notes.** The 300 bps band measures 309 bps in IMD-per-RISK terms when IMD sorts as currency0, which the README documents as a sorted-ratio bound. A zero-fill batch still consumes the hourly cooldown, which is documented and costs a griefer about 0.07% of depth per hour for no gain.\n\n**Dropped.** The sender-agnostic beforeInitialize is explicitly permitted by the launch rules, and the unreachable self-call branch in afterSwap is dead code with no effect.\n\n**Coverage.** All nine entry points are answered, seven hold and two carry finding 1, plus four invariant and manifest rows. The existing suite passes with 52 tests and 2 offline fork skips. The manifest validates against the schema, and all static-analysis leads traced to non-issues.","treeHash":null,"usage":{"cachedInputTokens":1784652,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":46840,"runtime":"claude","turns":39,"wallClockMs":600485}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"1559912e747bbcd4","findings":[{"citation":"resolved","description":"The batch swap's only slippage guard is sqrtPriceLimit = referencePrice() * sqrt(0.97) (zeroForOne) or * sqrt(1.03). referencePrice() is the time-weighted mean tick of the last completed period of at least 3600 s, so it lags the pool by up to an hour or more. The limit is never tightened against the pre-batch spot price read on line 202. When the pool has moved against the reference, which happens naturally after a sell-off late in the hour, or in any trending market, or after one period of held manipulation, the hook may move the price all the way from spot to ref +/- 300 bps, an allowance that is unbounded in spot terms (73% in the reproduction). executeBatch() is permissionless and its eligibility (lastBatch + 3600) is public, so an MEV actor runs, in one transaction: buy RISK (push spot toward the limit), executeBatch() (the hook spends its full budget at the inflated price up to the limit), sell the RISK back. The attacker's round trip costs 2 x (1.25% LP + 1% hook fee) = 4.5%, which is why the same sandwich is unprofitable when spot == reference (hook impact capped at 3%). Once the budget is a few percent of in-range depth the hook's own impact exceeds 4.5% and the sandwich captures a large share of the IMD that M1 earmarks for buyback and burn; the burn shrinks accordingly. The guarantee of M1 ('sqrtPriceLimit 300 bps beyond a time-weighted reference price ... the price limit is the only slippage guard') is met literally but does not bound the batch's adverse move relative to the market the batch actually trades in. Who profits: any searcher. Who loses: RISK holders, whose buyback IMD is paid to the searcher instead of being burned as RISK. Proposed fix compatible with M1: keep the 300 bps bound relative to the reference but also never allow more than 300 bps adverse movement from the pre-batch spot, i.e. anchor = zeroForOne ? max(ref, current) : min(ref, current) before computing the limit; partial fills already carry the unspent budget forward. Verified on a scratch copy of the hook: the same scenario yields zero attacker profit and the batch fills 325e18 RISK at <= 3% from spot, retaining the rest.","line":193,"path":"src/RISKHook.sol","reproduction":"State: local PoolManager, RISK/IMD pool, 1e22 full-range liquidity opened at 1:1, IMD is currency0 in this ordering (zeroForOne buyback). 1) Ordinary churn at t0: 320 rounds of sell 1e21 RISK exact-input then buy back with the IMD received -> hook.pending() ~= 1.774e21 IMD, budget pending/4 = 443499412011129799136 (about 4.4% of IMD depth). 2) warp t0+3599, one exact-input sell of 5e21 RISK (late-hour sell-off). warp t0+3600: referencePrice() ~= opening tick (TWAP of the hour) while spot sqrtPrice is ~1.49x higher (RISK ~2.2x cheaper than the reference). 3) Honest executeBatch(): spends the full 443.5e18 IMD budget and burns 1866626874985864239689 RISK. 4) Instead, in one block from one account: router buy RISK with 5e21 IMD -> hook.executeBatch() -> sell all RISK received. Attacker IMD balance rises by 100892415492029109580 (22.7% of the budget) and the batch burns only 1289271164971926029477 RISK (31% less). Expected: a 300 bps slippage guard cannot be sandwiched for profit against a 4.5% round-trip fee burden (attacker profit == 0, as it is when spot == reference). Actual: profit 100.9e18 IMD per batch. Proof file: test/scratch/RISKHookSandwichProof.t.sol, test_batchCannotBeSandwichedForProfit fails with 'sandwich of executeBatch() is profitable: 100892415492029109580 != 0'. Scaling (claims funded directly, same pool): budget 2.5e20 -> profit 19.8e18; 5e20 -> 141e18 (hook buys 51% less); 1e21 -> 368e18 (37% of budget).","severity":"medium","snippet":"        uint160 ref = referencePrice();\n        // sqrt(0.97), rounded UP; sqrt(1.03), rounded DOWN: conservative price bounds.\n        uint256 product = uint256(ref) * (zeroForOne ? 984885780179610473 : 1014889156509221946);\n        uint256 scaled = zeroForOne ? (product + 1e18 - 1) / 1e18 : product / 1e18;","title":"Buyback price limit is anchored only to the lagging TWAP, so executeBatch() can be sandwiched for profit whenever spot has moved away from the reference"},{"citation":"resolved","description":"executeBatch() sets lastBatch = block.timestamp (line 172) before unlocking, and unlockCallback returns successfully with zero spend when the current price is already at or beyond the 300 bps limit. Because executeBatch() is permissionless and the limit depends only on the public TWAP reference, a griefer can, in one transaction: buy RISK to push spot beyond referencePrice() * sqrt(0.97) (for the zeroForOne ordering), call executeBatch() (zero fill, cooldown consumed), then sell the RISK back. The honest keeper's batch now reverts TooSoon for the next 3600 s, and the griefer can repeat every hour. Cost is only the fees on a ~3% price excursion (about 4.5% of roughly 1.5% of in-range depth, i.e. ~0.07% of depth per hour; 702.6e18 IMD per hour against 1e24 liquidity, ~7e18 IMD per hour against a 1e22 pool). No funds are lost and part of the griefer's fees even flow back into pending()/burn, so this is low severity, but M1's 'the buyback can never deadlock' holds only against a passive adversary. Suggested fix: do not advance lastBatch (or advance it by a short anti-spam period) when spent == 0, so the batch runs as soon as the price is back inside the guard; or compute the limit against min/max(reference, spot) as in finding 1, which does not remove this vector but makes the required push explicit.","line":204,"path":"src/RISKHook.sol","reproduction":"State: HookFixture (1e24 full-range liquidity at 1:1, IMD = currency0). 1) sell 1e22 RISK exact-input so pending() > 0; warp to lastBatch()+3600 (reference == spot). 2) Griefer buys RISK with 1.6e22 IMD exact-input (spot sqrtPrice falls below referencePrice()*sqrt(0.97)). 3) Griefer calls executeBatch(): succeeds, emits BatchExecuted(budget,0,0,limit), pending() unchanged, lastBatch() == block.timestamp. 4) Griefer sells the RISK back; net cost 702614111395279495460 IMD (fees). 5) Honest executeBatch() in the same block or within 3600 s reverts TooSoon. Expected: a batch that bought nothing should not lock the buyback for an hour. Actual: cooldown consumed; repeatable each hour for ~0.07% of depth.","severity":"low","snippet":"        if (budget == 0 || (zeroForOne ? current <= limit : current >= limit)) {\n            emit BatchExecuted(budget, 0, 0, limit);\n            return \"\";\n        }","title":"A zero-fill batch still consumes the hourly cooldown, so anyone can defer the buyback indefinitely with a same-block price push"}],"hash":"d16db66ea91a00665edecc6cdbbd969d160e3470f4dd4c45f05fc0d15f11044c","nodeId":"5219b9c4-361e-4c71-a0bf-4f8fdb97c110","outcome":"completed","summary":"Review complete. The findings file is written and validated.\n\n**Outcome.** Two defects in the buyback economics, none in fee collection or claim accounting. Written to `.imd-findings.json` with 12 coverage rows (all 9 entry points plus three invariants).\n\n**Finding 1, medium, with proof.** The batch's only slippage guard is 300 bps beyond the hook's TWAP reference, and the limit is never tightened against the pre-batch spot. Whenever spot has drifted from the lagging reference, which a late-hour sell-off produces naturally, the hook may move the price from spot all the way to the reference bound. Since `executeBatch()` is permissionless and its timing is public, a searcher buys RISK, calls the batch, and sells back in one block. With a 1e22 pool and a budget of about 4.4% of depth, the attacker nets 100.9 IMD of the 443.5 IMD budget and the burn shrinks 31%. The same sandwich is unprofitable when spot equals the reference, because the 4.5% round-trip fee burden exceeds the 3% cap. A spot-anchored cap (anchor to the tighter of reference and spot) makes it unprofitable in every tested scenario, verified on a scratch copy. Proof test fails on the current code at `test/scratch/RISKHookSandwichProof.t.sol`.\n\n**Finding 2, low.** A zero-fill batch still consumes the hourly cooldown. Pushing spot past the limit, calling `executeBatch()`, and selling back costs about 0.07% of pool depth per hour (702 IMD against 1e24 liquidity) and defers the buyback indefinitely.\n\n**Covered and holding.** Fee direction logic for all four swap modes, proportionality on partial fills, ERC-6909 claim accounting netting to zero, sweep and batch settlement, TWAP time-weighting and view/write consistency, sqrt constants and rounding direction, and the zero-fill guard matching v4's revert condition. Both stateful invariant suites re-ran clean.\n\n**Not reached.** No mainnet fork run (no network), so the real IMD token's transfer behaviour and the live PoolManager remain unverified here. The factory's actual liquidity range, which sets how large a budget is relative to depth and therefore how soon finding 1 becomes exploitable, is outside this repository.","treeHash":null,"usage":{"cachedInputTokens":3485627,"inputTokens":706,"model":"claude-fable-5-1","outputTokens":57964,"runtime":"claude","turns":43,"wallClockMs":963920}},{"artifacts":[],"attempt":2,"bundleHash":"3cb5c93d386df30e0b26c1d1601360533f2d62629b9f0813d878e666ca451007","device":"1369de7c9398881a","findings":[],"hash":"fd0395228c39c844a699c5128fb5b714e9456250c63a5c2ac85c2a8c32c37034","nodeId":"8faa1d5e-280c-42b6-8354-c09d1b02c63c","outcome":"completed","summary":"Fixed the stale-reference issue by tightening the buyback limit against pre-batch spot. The reproduced sandwich now loses money.\n\nRecorded all three findings in `.imd-responses.json`; retained and documented the two advisory behaviors.\n\nValidation passed: `forge build`, `forge fmt --check`, 42 local tests, and the mainnet-fork test at block 26151445.","treeHash":"389045dc04bf4cbe9cd56fd40fd82db4d59ea695","usage":{"cachedInputTokens":2057856,"inputTokens":103240,"model":"gpt-6-astra","outputTokens":15389,"runtime":"codex","turns":6,"wallClockMs":451163}}],"verification":[{"checks":[{"durationMs":5932,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.69s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `spotTick` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:100:9\n    │\n100 │         spotTick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `periodStart` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:102:9\n    │\n102 │         periodStart = block.timestamp;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:103:9\n    │\n103 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:104:9\n    │\n104 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `tickSeconds` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:220:9\n    │\n220 │         tickSeconds += int256(spotTick) * int256(now_ - observedAt);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:221:9\n    │\n221 │         observedAt = now_;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `spotTick` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:130:9\n    │\n130 │         (, spotTick,,) = poolManager.getSlot0(key.toId());\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/RISKHook.sol:54:39\n   │\n54 │     constructor(IPoolManager manager, address launchToken) {\n   │                                       ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:116:42\n    │\n116 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:116:87\n    │\n116 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : uint256(params.amountSpecified);\n    │                                                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:117:25\n    │\n117 │         if (magnitude > uint256(type(int256).max) - magnitude / 100) revert UnrepresentableFee();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RISKHook.sol:139:13\n    │\n139 │             emit FeeAccrued(Currency.unwrap(currency), fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:134:23\n    │\n134 │         uint256 fee = uint256(filled < 0 ? -filled : filled) / 100;\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/RISKHook.sol:141:44\n    │\n141 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                            ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe 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/RISKHook.sol:141:51\n    │\n141 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/RISKHook.sol:157:13\n    │\n157 │         if (duration >= INTERVAL) {\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:158:66\n    │\n158 │             int256 cumulative = tickSeconds + int256(spotTick) * int256(block.timestamp - observedAt);\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[reentrancy-no-eth]: external call can be reentered before `operating` is updated\n    ╭▸ src/RISKHook.sol:166:9\n    │\n166 │         poolManager.unlock(abi.encode(false));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/RISKHook.sol:166:9\n    │\n166 │         poolManager.unlock(abi.encode(false));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `operating` is updated\n    ╭▸ src/RISKHook.sol:174:9\n    │\n174 │         poolManager.unlock(abi.encode(true));\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/RISKHook.sol:171:13\n    │\n171 │         if (block.timestamp - lastBatch < INTERVAL) revert TooSoon();\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/RISKHook.sol:174:9\n    │\n174 │         poolManager.unlock(abi.encode(true));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:208:30\n    │\n208 │         BalanceDelta delta = poolManager.swap(key, SwapParams(zeroForOne, -int256(budget), limit), \"\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:211:25\n    │\n211 │         if (spent != 0) poolManager.burn(address(this), Currency.wrap(IMD).toId(), spent);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:212:26\n    │\n212 │         if (bought != 0) poolManager.take(Currency.wrap(token), DEAD, bought);\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/RISKHook.sol:185:13\n    │\n185 │             emit Swept(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/RISKHook.sol:205:13\n    │\n205 │             emit BatchExecuted(budget, 0, 0, limit);\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/RISKHook.sol:214:9\n    │\n214 │         emit BatchExecuted(budget, spent, bought, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:22\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:30\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:67\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:75\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:197:25\n    │\n197 │           uint160 limit = uint160(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━┛\n198 │ ┃             scaled < TickMath.MIN_SQRT_PRICE + 1\n199 │ ┃                 ? TickMath.MIN_SQRT_PRICE + 1\n200 │ ┃                 : scaled > TickMath.MAX_SQRT_PRICE - 1 ? TickMath.MAX_SQRT_PRICE - 1 : scaled\n201 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:208:76\n    │\n208 │         BalanceDelta delta = poolManager.swap(key, SwapParams(zeroForOne, -int256(budget), limit), \"\");\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/RISKHook.sol:209:25\n    │\n209 │         uint256 spent = uint256(-int256(zeroForOne ? delta.amount0() : delta.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:210:26\n    │\n210 │         uint256 bought = uint256(int256(zeroForOne ? delta.amount1() : delta.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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/RISKHook.sol:223:13\n    │\n223 │         if (duration >= INTERVAL) {\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:220:43\n    │\n220 │         tickSeconds += int256(spotTick) * int256(now_ - observedAt);\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/RISKHook.sol:232:36\n    │\n232 │         int256 mean = cumulative / int256(duration);\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/RISKHook.sol:233:44\n    │\n233 │         if (cumulative < 0 && cumulative % int256(duration) != 0) --mean;\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/RISKHook.sol:234:16\n    │\n234 │         return int24(mean);\n    │                ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/RISKHook.t.sol:272:25\n    │\n272 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n273 │         vm.warp(start + 1800);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/RISKHook.t.sol:288:17\n    │\n288 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":3814,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RISKHook.t.sol:RISKHookMainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 401.32µs (0.00ns CPU time)\n\nRan 1 test for test/RISKHookAdversarial.t.sol:RISKHookAdversarialMainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 485.23µs (0.00ns CPU time)\n\nRan 2 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorRefusesInvalidInputsAndPermissionAddress() (gas: 4387985)\n[PASS] test_mineAndDeployDirectlyAtPredictedAddress() (gas: 48853616)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 68.34ms (51.73ms CPU time)\n\nRan 11 tests for test/RISKHook.t.sol:RISKHookTest\n[PASS] testFuzz_feeTracksActualPartialFill(bool,bool,uint256,uint24) (runs: 1000, μ: 339070, ~: 338205)\nLogs:\n  Bound result 99999999999991313373042\n  Bound result 1\n\n[PASS] test_allSwapModesAndSweep() (gas: 1433651)\n[PASS] test_batchCooldownAndBudget() (gas: 590616)\n[PASS] test_batchPartialFillRetainsClaimsAndCanRunAgain() (gas: 778289)\n[PASS] test_creationSizeAndRuntimeNoEscapeHatches() (gas: 2389610)\n[PASS] test_dustAndUnrepresentableRequests() (gas: 349197)\n[PASS] test_emptyBatchAndEmptySweep() (gas: 140397)\n[PASS] test_exactInputAndOutputPartialFillsBothDirections() (gas: 1347597)\n[PASS] test_permissionsInitAndAuthorization() (gas: 234365)\n[PASS] test_spotBeyondLimitDoesNotDeadlock() (gas: 721495)\n[PASS] test_timeWeightedReferenceAndSameBlockResistance() (gas: 497940)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 135.16ms (133.46ms CPU time)\n\nRan 1 test for test/RISKHookAdversarial.t.sol:RISKHookFreshManagerTest\n[PASS] testFuzz_firstBuyWorksWithZeroPairedCurrencyReserves(bool,uint96) (runs: 1000, μ: 413381, ~: 413239)\nLogs:\n  Bound result 2514264345516352027517\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 135.94ms (105.19ms CPU time)\n\nRan 3 tests for test/RISK.t.sol:RISKTest\n[PASS] testFuzz_transferAndAllowance(uint256) (runs: 256, μ: 172014, ~: 172267)\nLogs:\n  Bound result 723135071823688642836594649\n\n[PASS] test_noMintAndInvalidTransfer() (gas: 80953)\n[PASS] test_supplyAndFactoryDistribution() (gas: 186413)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 136.63ms (136.82ms CPU time)\n\nRan 13 tests for test/RISKHookAdversarial.t.sol:RISKHookAdversarialTest\n[PASS] testFuzz_dustFeesUseFilledAmount(bool,bool,uint16) (runs: 1000, μ: 310725, ~: 312729)\nLogs:\n  Bound result 8\n\n[PASS] testFuzz_referenceWeightsIrregularDurations(uint16,uint16,bool) (runs: 1000, μ: 726795, ~: 724966)\nLogs:\n  Bound result 427\n  Bound result 898\n\n[PASS] test_batchAcceptsProtocolFeesAndStillUsesOnlyPriceLimit() (gas: 767704)\n[PASS] test_batchPriceFeedsNextObservationDespiteSelfCallbackExemption() (gas: 428033)\n[PASS] test_batchWithoutLiquidityPreservesBudgetAndRecovers() (gas: 1011417)\n[PASS] test_dustBudgetRetainsClaimsAndStillEnforcesCooldown() (gas: 355809)\n[PASS] test_failedBatchRestoresCooldownPriceAndClaims() (gas: 720791)\n[PASS] test_failedSweepRestoresClaimsAndCanBeRetried() (gas: 453425)\n[PASS] test_hugeRepresentableRequestsOnlyPayForSmallPartialFill() (gas: 1364502)\n[PASS] test_nestedMaintenanceCannotConsumeCooldownOrClaims() (gas: 1121623)\n[PASS] test_overdueSwapsNeverRunMaintenanceInsideCallback() (gas: 1430304)\n[PASS] test_unrepresentableRequestsRevertThroughRealManagerAndLeaveNoFees() (gas: 582801)\n[PASS] test_zeroLiquiditySwapsChargeNothingInEveryMode() (gas: 1690103)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 136.66ms (254.54ms CPU time)\n\nRan 9 tests for test/RISKHookAdversarial.t.sol:RISKHookAdversarialReverseTest\n[PASS] testFuzz_dustFeesUseFilledAmount(bool,bool,uint16) (runs: 1000, μ: 310664, ~: 312721)\nLogs:\n  Bound result 30\n\n[PASS] test_batchWithoutLiquidityPreservesBudgetAndRecovers() (gas: 1011405)\n[PASS] test_failedBatchRestoresCooldownPriceAndClaims() (gas: 720824)\n[PASS] test_failedSweepRestoresClaimsAndCanBeRetried() (gas: 453458)\n[PASS] test_hugeRepresentableRequestsOnlyPayForSmallPartialFill() (gas: 1364392)\n[PASS] test_nestedMaintenanceCannotConsumeCooldownOrClaims() (gas: 1121601)\n[PASS] test_overdueSwapsNeverRunMaintenanceInsideCallback() (gas: 1430281)\n[PASS] test_unrepresentableRequestsRevertThroughRealManagerAndLeaveNoFees() (gas: 582823)\n[PASS] test_zeroLiquiditySwapsChargeNothingInEveryMode() (gas: 1690136)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 136.68ms (45.59ms CPU time)\n\nRan 11 tests for test/RISKHook.t.sol:RISKHookReverseOrderingTest\n[PASS] testFuzz_feeTracksActualPartialFill(bool,bool,uint256,uint24) (runs: 256, μ: 339226, ~: 341929)\nLogs:\n  Bound result 99999999999990129563199\n  Bound result 1\n\n[PASS] test_allSwapModesAndSweep() (gas: 1433651)\n[PASS] test_batchCooldownAndBudget() (gas: 590616)\n[PASS] test_batchPartialFillRetainsClaimsAndCanRunAgain() (gas: 778289)\n[PASS] test_creationSizeAndRuntimeNoEscapeHatches() (gas: 2389610)\n[PASS] test_dustAndUnrepresentableRequests() (gas: 349197)\n[PASS] test_emptyBatchAndEmptySweep() (gas: 140397)\n[PASS] test_exactInputAndOutputPartialFillsBothDirections() (gas: 1347597)\n[PASS] test_permissionsInitAndAuthorization() (gas: 234365)\n[PASS] test_spotBeyondLimitDoesNotDeadlock() (gas: 721495)\n[PASS] test_timeWeightedReferenceAndSameBlockResistance() (gas: 497940)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 140.53ms (49.23ms CPU time)\n\nRan 1 test for test/RISKInvariant.t.sol:RISKInvariantTest\n[PASS] invariant_conservationAndClaimAccounting() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------------+----------+-------+---------+----------╮\n| Contract          | Selector | Calls | Reverts | Discards |\n+===========================================================+\n| RISKInvariantTest | actBatch | 2708  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| RISKInvariantTest | actSwap  | 2775  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| RISKInvariantTest | actSweep | 2709  | 0       | 0        |\n╰-------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 4318\n  Bound result 95\n  Bound result 999999999990000014259\n  Bound result 3603\n  Bound result 5764\n  Bound result 6301\n  Bound result 6601\n  Bound result 226\n  Bound result 999999999990000019864\n  Bound result 238\n  Bound result 999999999990000000065\n  Bound result 543\n  Bound result 999999999990000000513\n  Bound result 525\n  Bound result 76327324913288460190\n  Bound result 575\n  Bound result 999999999990000016249\n  Bound result 295\n  Bound result 999999999990000003524\n  Bound result 3600\n  Bound result 358\n  Bound result 999999999990000007246\n  Bound result 5019\n  Bound result 1\n  Bound result 999999999991018396345\n  Bound result 3614\n  Bound result 301\n  Bound result 999999999990000000012\n  Bound result 195\n  Bound result 999999999990599290589\n  Bound result 4298\n  Bound result 3728\n  Bound result 156\n  Bound result 100000000000000000000\n  Bound result 3601\n  Bound result 300\n  Bound result 9223372036854775808\n  Bound result 4345\n  Bound result 512\n  Bound result 999999999990000007200\n  Bound result 5216\n  Bound result 4096\n  Bound result 7200\n  Bound result 594\n  Bound result 999999999990000000245\n  Bound result 150\n  Bound result 999999999990000006317\n  Bound result 7037\n  Bound result 575\n  Bound result 999999999990000021098\n  Bound result 17\n  Bound result 10000000001\n  Bound result 6311\n  Bound result 5049\n  Bound result 161\n  Bound result 515056619213464722174\n  Bound result 496\n  Bound result 656008723758966195308\n  Bound result 357\n  Bound result 999999999990000000836\n  Bound result 244\n  Bound result 999999999990000000256\n  Bound result 3661\n  Bound result 4601\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.25s (2.22s CPU time)\n\nRan 1 test for test/RISKLifecycleInvariant.t.sol:RISKLifecycleInvariantTest\n[PASS] invariant_claimsAndAssetsSurviveLiquidityAndMaintenanceSequences() (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------------+-----------------+-------+---------+----------╮\n| Contract             | Selector        | Calls | Reverts | Discards |\n+=====================================================================+\n| RISKLifecycleHandler | advance         | 3249  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | batch           | 3254  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | changeLiquidity | 3305  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | swap            | 3314  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | sweep           | 3262  | 0       | 0        |\n╰----------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 203\n  Bound result 2024546467199252955135\n  Bound result 13\n  Bound result 14723\n  Bound result 53\n  Bound result 1917\n  Bound result 999990000000000000013744\n  Bound result 14099\n  Bound result 29\n  Bound result 999990000000001064470261\n  Bound result 12992\n  Bound result 14\n  Bound result 9471\n  Bound result 4\n  Bound result 16999\n  Bound result 5\n  Bound result 2920\n  Bound result 154\n  Bound result 17\n  Bound result 16661\n  Bound result 19\n  Bound result 3746\n  Bound result 41\n  Bound result 14892\n  Bound result 45\n  Bound result 4215\n  Bound result 739\n  Bound result 40\n  Bound result 11572\n  Bound result 34\n  Bound result 16777214\n  Bound result 16\n  Bound result 905\n  Bound result 18\n  Bound result 1804619\n  Bound result 35\n  Bound result 2864\n  Bound result 3223\n  Bound result 13293\n  Bound result 7\n  Bound result 3933\n  Bound result 53617914\n  Bound result 11\n  Bound result 60\n  Bound result 32\n  Bound result 12\n  Bound result 6146\n  Bound result 3441\n  Bound result 18\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.70s (3.67s CPU time)\n\nRan 11 test suites in 3.71s (6.84s CPU time): 52 tests passed, 0 failed, 2 skipped (54 total tests)\n","passed":true},{"durationMs":62,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"RISK.approve(address,uint256)\",\"RISK.transfer(address,uint256)\",\"RISK.transferFrom(address,address,uint256)\",\"RISKHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"RISKHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"RISKHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"RISKHook.executeBatch()\",\"RISKHook.sweep()\",\"RISKHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":5,\"DEPENDENCIES.md\":12,\"README.md\":65,\"SECURITY_REVIEW.md\":33,\"foundry.toml\":11,\"launch.json\":16,\"script/MineRISKHook.s.sol\":29,\"src/HookFlags.sol\":29,\"src/RISK.sol\":11,\"src/RISKHook.sol\":236,\"test/Deployment.t.sol\":74,\"test/PoolRouter.sol\":49,\"test/README.md\":38,\"test/RISK.t.sol\":53,\"test/RISKHook.t.sol\":369,\"test/RISKHookAdversarial.t.sol\":380,\"test/RISKInvariant.t.sol\":73,\"test/RISKLifecycleInvariant.t.sol\":206},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"0a2dfaf9373d4ca1d53eb4ee09b32c3c98befabe0bda88dd6abefb2294b03ddd","verifiedTreeHash":"cb0727284ec27ee355527a65456f08692540b67f","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":6222,"exitCode":0,"name":"build","output":"Compiling 83 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.06s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `spotTick` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:100:9\n    │\n100 │         spotTick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `periodStart` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:102:9\n    │\n102 │         periodStart = block.timestamp;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:103:9\n    │\n103 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:104:9\n    │\n104 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `tickSeconds` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:222:9\n    │\n222 │         tickSeconds += int256(spotTick) * int256(now_ - observedAt);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:223:9\n    │\n223 │         observedAt = now_;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `spotTick` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:130:9\n    │\n130 │         (, spotTick,,) = poolManager.getSlot0(key.toId());\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/RISKHook.sol:54:39\n   │\n54 │     constructor(IPoolManager manager, address launchToken) {\n   │                                       ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:116:42\n    │\n116 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:116:87\n    │\n116 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : uint256(params.amountSpecified);\n    │                                                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:117:25\n    │\n117 │         if (magnitude > uint256(type(int256).max) - magnitude / 100) revert UnrepresentableFee();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RISKHook.sol:139:13\n    │\n139 │             emit FeeAccrued(Currency.unwrap(currency), fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:134:23\n    │\n134 │         uint256 fee = uint256(filled < 0 ? -filled : filled) / 100;\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/RISKHook.sol:141:44\n    │\n141 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                            ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe 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/RISKHook.sol:141:51\n    │\n141 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/RISKHook.sol:157:13\n    │\n157 │         if (duration >= INTERVAL) {\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:158:66\n    │\n158 │             int256 cumulative = tickSeconds + int256(spotTick) * int256(block.timestamp - observedAt);\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[reentrancy-no-eth]: external call can be reentered before `operating` is updated\n    ╭▸ src/RISKHook.sol:166:9\n    │\n166 │         poolManager.unlock(abi.encode(false));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/RISKHook.sol:166:9\n    │\n166 │         poolManager.unlock(abi.encode(false));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `operating` is updated\n    ╭▸ src/RISKHook.sol:174:9\n    │\n174 │         poolManager.unlock(abi.encode(true));\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/RISKHook.sol:171:13\n    │\n171 │         if (block.timestamp - lastBatch < INTERVAL) revert TooSoon();\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/RISKHook.sol:174:9\n    │\n174 │         poolManager.unlock(abi.encode(true));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:210:30\n    │\n210 │         BalanceDelta delta = poolManager.swap(key, SwapParams(zeroForOne, -int256(budget), limit), \"\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:213:25\n    │\n213 │         if (spent != 0) poolManager.burn(address(this), Currency.wrap(IMD).toId(), spent);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:214:26\n    │\n214 │         if (bought != 0) poolManager.take(Currency.wrap(token), DEAD, bought);\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/RISKHook.sol:185:13\n    │\n185 │             emit Swept(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/RISKHook.sol:207:13\n    │\n207 │             emit BatchExecuted(budget, 0, 0, limit);\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/RISKHook.sol:216:9\n    │\n216 │         emit BatchExecuted(budget, spent, bought, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:22\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:30\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:67\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:75\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:200:25\n    │\n200 │           uint160 limit = uint160(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━┛\n201 │ ┃             scaled < TickMath.MIN_SQRT_PRICE + 1\n202 │ ┃                 ? TickMath.MIN_SQRT_PRICE + 1\n203 │ ┃                 : scaled > TickMath.MAX_SQRT_PRICE - 1 ? TickMath.MAX_SQRT_PRICE - 1 : scaled\n204 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:210:76\n    │\n210 │         BalanceDelta delta = poolManager.swap(key, SwapParams(zeroForOne, -int256(budget), limit), \"\");\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/RISKHook.sol:211:25\n    │\n211 │         uint256 spent = uint256(-int256(zeroForOne ? delta.amount0() : delta.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:212:26\n    │\n212 │         uint256 bought = uint256(int256(zeroForOne ? delta.amount1() : delta.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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/RISKHook.sol:225:13\n    │\n225 │         if (duration >= INTERVAL) {\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:222:43\n    │\n222 │         tickSeconds += int256(spotTick) * int256(now_ - observedAt);\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/RISKHook.sol:234:36\n    │\n234 │         int256 mean = cumulative / int256(duration);\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/RISKHook.sol:235:44\n    │\n235 │         if (cumulative < 0 && cumulative % int256(duration) != 0) --mean;\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/RISKHook.sol:236:16\n    │\n236 │         return int24(mean);\n    │                ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/RISKHook.t.sol:274:25\n    │\n274 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n275 │         vm.warp(start + 1800);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/RISKHook.t.sol:290:17\n    │\n290 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":3703,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RISKHook.t.sol:RISKHookMainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 254.52µs (0.00ns CPU time)\n\nRan 1 test for test/RISKHookAdversarial.t.sol:RISKHookAdversarialMainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 317.57µs (0.00ns CPU time)\n\nRan 3 tests for test/RISK.t.sol:RISKTest\n[PASS] testFuzz_transferAndAllowance(uint256) (runs: 256, μ: 171487, ~: 172207)\nLogs:\n  Bound result 6555\n\n[PASS] test_noMintAndInvalidTransfer() (gas: 80953)\n[PASS] test_supplyAndFactoryDistribution() (gas: 186413)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 13.97ms (14.21ms CPU time)\n\nRan 11 tests for test/RISKHook.t.sol:RISKHookReverseOrderingTest\n[PASS] testFuzz_feeTracksActualPartialFill(bool,bool,uint256,uint24) (runs: 256, μ: 339142, ~: 342228)\nLogs:\n  Bound result 95537021973121\n  Bound result 54\n\n[PASS] test_allSwapModesAndSweep() (gas: 1433651)\n[PASS] test_batchCooldownAndBudget() (gas: 590702)\n[PASS] test_batchPartialFillRetainsClaimsAndCanRunAgain() (gas: 794965)\n[PASS] test_creationSizeAndRuntimeNoEscapeHatches() (gas: 2405366)\n[PASS] test_dustAndUnrepresentableRequests() (gas: 349197)\n[PASS] test_emptyBatchAndEmptySweep() (gas: 140459)\n[PASS] test_exactInputAndOutputPartialFillsBothDirections() (gas: 1347597)\n[PASS] test_permissionsInitAndAuthorization() (gas: 234365)\n[PASS] test_spotBeyondLimitDoesNotDeadlock() (gas: 721659)\n[PASS] test_timeWeightedReferenceAndSameBlockResistance() (gas: 497940)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 147.23ms (113.84ms CPU time)\n\nRan 11 tests for test/RISKHook.t.sol:RISKHookTest\n[PASS] testFuzz_feeTracksActualPartialFill(bool,bool,uint256,uint24) (runs: 1000, μ: 339270, ~: 342065)\nLogs:\n  Bound result 17142451900\n  Bound result 26\n\n[PASS] test_allSwapModesAndSweep() (gas: 1433651)\n[PASS] test_batchCooldownAndBudget() (gas: 590702)\n[PASS] test_batchPartialFillRetainsClaimsAndCanRunAgain() (gas: 794965)\n[PASS] test_creationSizeAndRuntimeNoEscapeHatches() (gas: 2405366)\n[PASS] test_dustAndUnrepresentableRequests() (gas: 349197)\n[PASS] test_emptyBatchAndEmptySweep() (gas: 140459)\n[PASS] test_exactInputAndOutputPartialFillsBothDirections() (gas: 1347597)\n[PASS] test_permissionsInitAndAuthorization() (gas: 234365)\n[PASS] test_spotBeyondLimitDoesNotDeadlock() (gas: 721659)\n[PASS] test_timeWeightedReferenceAndSameBlockResistance() (gas: 497940)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 152.23ms (145.55ms CPU time)\n\nRan 2 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorRefusesInvalidInputsAndPermissionAddress() (gas: 4387985)\n[PASS] test_mineAndDeployDirectlyAtPredictedAddress() (gas: 38446323)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 210.87ms (36.31ms CPU time)\n\nRan 7 tests for test/RISKBatchLimit.t.sol:RISKBatchLimitReverseOrderingTest\n[PASS] testFuzz_idleReferenceCannotWidenBatchLimit(uint32,uint96) (runs: 256, μ: 726973, ~: 727132)\nLogs:\n  Bound result 3600\n  Bound result 590000000000000000001414\n\n[PASS] test_idleReferenceCannotWidenBatchLimit() (gas: 719636)\n[PASS] test_reportedDeepPoolSandwichLosesMoney() (gas: 135918561)\n[PASS] test_reportedThinPoolSandwichLosesMoney() (gas: 136048899)\n[PASS] test_sortedRatioConvention() (gas: 219855)\n[PASS] test_staleBandPartialFillRetainsBudgetAndNextBatchProgresses() (gas: 105590724)\n[PASS] test_zeroFillStillConsumesCooldownAndRetainsClaims() (gas: 1177748)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 210.79ms (584.88ms CPU time)\n\nRan 9 tests for test/RISKHookAdversarial.t.sol:RISKHookAdversarialReverseTest\n[PASS] testFuzz_dustFeesUseFilledAmount(bool,bool,uint16) (runs: 1000, μ: 310258, ~: 312721)\nLogs:\n  Bound result 170\n\n[PASS] test_batchWithoutLiquidityPreservesBudgetAndRecovers() (gas: 1011577)\n[PASS] test_failedBatchRestoresCooldownPriceAndClaims() (gas: 720996)\n[PASS] test_failedSweepRestoresClaimsAndCanBeRetried() (gas: 453458)\n[PASS] test_hugeRepresentableRequestsOnlyPayForSmallPartialFill() (gas: 1364392)\n[PASS] test_nestedMaintenanceCannotConsumeCooldownOrClaims() (gas: 1121687)\n[PASS] test_overdueSwapsNeverRunMaintenanceInsideCallback() (gas: 1430281)\n[PASS] test_unrepresentableRequestsRevertThroughRealManagerAndLeaveNoFees() (gas: 582823)\n[PASS] test_zeroLiquiditySwapsChargeNothingInEveryMode() (gas: 1690136)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 237.39ms (184.62ms CPU time)\n\nRan 1 test for test/RISKHookAdversarial.t.sol:RISKHookFreshManagerTest\n[PASS] testFuzz_firstBuyWorksWithZeroPairedCurrencyReserves(bool,uint96) (runs: 1000, μ: 413382, ~: 413239)\nLogs:\n  Bound result 9999999999999999013978\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 276.04ms (251.86ms CPU time)\n\nRan 7 tests for test/RISKBatchLimit.t.sol:RISKBatchLimitTest\n[PASS] testFuzz_idleReferenceCannotWidenBatchLimit(uint32,uint96) (runs: 256, μ: 720902, ~: 721043)\nLogs:\n  Bound result 20021593\n  Bound result 590131768121354464302294\n\n[PASS] test_idleReferenceCannotWidenBatchLimit() (gas: 713558)\n[PASS] test_reportedDeepPoolSandwichLosesMoney() (gas: 135908936)\n[PASS] test_reportedThinPoolSandwichLosesMoney() (gas: 136049028)\n[PASS] test_sortedRatioConvention() (gas: 220027)\n[PASS] test_staleBandPartialFillRetainsBudgetAndNextBatchProgresses() (gas: 105599771)\n[PASS] test_zeroFillStillConsumesCooldownAndRetainsClaims() (gas: 1172049)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 276.20ms (517.69ms CPU time)\n\nRan 13 tests for test/RISKHookAdversarial.t.sol:RISKHookAdversarialTest\n[PASS] testFuzz_dustFeesUseFilledAmount(bool,bool,uint16) (runs: 1000, μ: 310138, ~: 312729)\nLogs:\n  Bound result 1\n\n[PASS] testFuzz_referenceWeightsIrregularDurations(uint16,uint16,bool) (runs: 1000, μ: 726900, ~: 729034)\nLogs:\n  Bound result 204\n  Bound result 39\n\n[PASS] test_batchAcceptsProtocolFeesAndStillUsesOnlyPriceLimit() (gas: 784023)\n[PASS] test_batchPriceFeedsNextObservationDespiteSelfCallbackExemption() (gas: 428119)\n[PASS] test_batchWithoutLiquidityPreservesBudgetAndRecovers() (gas: 1011589)\n[PASS] test_dustBudgetRetainsClaimsAndStillEnforcesCooldown() (gas: 355878)\n[PASS] test_failedBatchRestoresCooldownPriceAndClaims() (gas: 720975)\n[PASS] test_failedSweepRestoresClaimsAndCanBeRetried() (gas: 453421)\n[PASS] test_hugeRepresentableRequestsOnlyPayForSmallPartialFill() (gas: 1364486)\n[PASS] test_nestedMaintenanceCannotConsumeCooldownOrClaims() (gas: 1121705)\n[PASS] test_overdueSwapsNeverRunMaintenanceInsideCallback() (gas: 1430292)\n[PASS] test_unrepresentableRequestsRevertThroughRealManagerAndLeaveNoFees() (gas: 582793)\n[PASS] test_zeroLiquiditySwapsChargeNothingInEveryMode() (gas: 1690087)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 276.37ms (521.34ms CPU time)\n\nRan 1 test for test/RISKInvariant.t.sol:RISKInvariantTest\n[PASS] invariant_conservationAndClaimAccounting() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------------+----------+-------+---------+----------╮\n| Contract          | Selector | Calls | Reverts | Discards |\n+===========================================================+\n| RISKInvariantTest | actBatch | 2754  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| RISKInvariantTest | actSwap  | 2707  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| RISKInvariantTest | actSweep | 2731  | 0       | 0        |\n╰-------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 379\n  Bound result 10000000001\n  Bound result 64\n  Bound result 999999999990000013162\n  Bound result 97\n  Bound result 501580684605396940749\n  Bound result 593\n  Bound result 999999999990000000012\n  Bound result 9\n  Bound result 9007199254740993\n  Bound result 9\n  Bound result 999999999990000200001\n  Bound result 6345\n  Bound result 128\n  Bound result 999999999990016635279\n  Bound result 379\n  Bound result 8999999999100000\n  Bound result 64\n  Bound result 999999999900000\n  Bound result 4601\n  Bound result 0\n  Bound result 999999999990113549768\n  Bound result 120\n  Bound result 999999999990001088739\n  Bound result 4119\n  Bound result 300\n  Bound result 3705638798429310474\n  Bound result 4104\n  Bound result 588\n  Bound result 999999999990000002609\n  Bound result 5757\n  Bound result 3696\n  Bound result 5581\n  Bound result 4391\n  Bound result 4601\n  Bound result 11\n  Bound result 999999999990000004422\n  Bound result 76\n  Bound result 970000000000000000\n  Bound result 10\n  Bound result 10000000000000000000\n  Bound result 153\n  Bound result 999999999990000009346\n  Bound result 38\n  Bound result 999999999990000016384\n  Bound result 3601\n  Bound result 3911\n  Bound result 7199\n  Bound result 226\n  Bound result 999999999990000007058\n  Bound result 3626\n  Bound result 405\n  Bound result 999999999990000004429\n  Bound result 3696\n  Bound result 13\n  Bound result 999999999990000003600\n  Bound result 6938\n  Bound result 6842\n  Bound result 6661\n  Bound result 80\n  Bound result 501580684605396940749\n  Bound result 505\n  Bound result 999999999990000000009\n  Bound result 96\n  Bound result 16936635263097937066\n  Bound result 3697\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.20s (2.15s CPU time)\n\nRan 1 test for test/RISKLifecycleInvariant.t.sol:RISKLifecycleInvariantTest\n[PASS] invariant_claimsAndAssetsSurviveLiquidityAndMaintenanceSequences() (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------------+-----------------+-------+---------+----------╮\n| Contract             | Selector        | Calls | Reverts | Discards |\n+=====================================================================+\n| RISKLifecycleHandler | advance         | 3322  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | batch           | 3235  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | changeLiquidity | 3274  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | swap            | 3265  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | sweep           | 3288  | 0       | 0        |\n╰----------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 6029\n  Bound result 51\n  Bound result 5751\n  Bound result 6167\n  Bound result 3848\n  Bound result 1187\n  Bound result 999990000000000000005454\n  Bound result 991\n  Bound result 999990000000000000011715\n  Bound result 3\n  Bound result 3797\n  Bound result 4\n  Bound result 2729953055865610363306\n  Bound result 3\n  Bound result 999990000000000000008671\n  Bound result 5419\n  Bound result 18\n  Bound result 1000\n  Bound result 518\n  Bound result 999990000000000000017065\n  Bound result 32\n  Bound result 3882\n  Bound result 9151\n  Bound result 7\n  Bound result 840\n  Bound result 999990000000000016777217\n  Bound result 999990000000000000008773\n  Bound result 991\n  Bound result 6958\n  Bound result 15774\n  Bound result 56\n  Bound result 27216951113984272969\n  Bound result 46\n  Bound result 999990000000000000015084\n  Bound result 254\n  Bound result 4914\n  Bound result 11747\n  Bound result 23\n  Bound result 999990000000000000000581\n  Bound result 4231\n  Bound result 10\n  Bound result 1179\n  Bound result 2678\n  Bound result 15132\n  Bound result 48\n  Bound result 999990000000000000084692\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.61s (3.59s CPU time)\n\nRan 13 test suites in 3.61s (7.61s CPU time): 66 tests passed, 0 failed, 2 skipped (68 total tests)\n","passed":true},{"durationMs":60,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"RISK.approve(address,uint256)\",\"RISK.transfer(address,uint256)\",\"RISK.transferFrom(address,address,uint256)\",\"RISKHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"RISKHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"RISKHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"RISKHook.executeBatch()\",\"RISKHook.sweep()\",\"RISKHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":5,\"DEPENDENCIES.md\":12,\"README.md\":67,\"SECURITY_REVIEW.md\":41,\"foundry.toml\":11,\"launch.json\":16,\"script/MineRISKHook.s.sol\":29,\"src/HookFlags.sol\":29,\"src/RISK.sol\":11,\"src/RISKHook.sol\":238,\"test/Deployment.t.sol\":74,\"test/PoolRouter.sol\":49,\"test/README.md\":47,\"test/RISK.t.sol\":53,\"test/RISKBatchLimit.t.sol\":204,\"test/RISKHook.t.sol\":371,\"test/RISKHookAdversarial.t.sol\":395,\"test/RISKInvariant.t.sol\":73,\"test/RISKLifecycleInvariant.t.sol\":206},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"47e321b646a0773e708e9837743829bd47fd7ff2bf7fc99321a092c3680d3d80","verifiedTreeHash":"1d06cd1338e14ddede969df03042fdd4ded931f4","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":3224,"exitCode":0,"name":"build","output":"Compiling 80 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.07s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `spotTick` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:100:9\n    │\n100 │         spotTick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `periodStart` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:102:9\n    │\n102 │         periodStart = block.timestamp;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:103:9\n    │\n103 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:104:9\n    │\n104 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `tickSeconds` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:220:9\n    │\n220 │         tickSeconds += int256(spotTick) * int256(now_ - observedAt);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:221:9\n    │\n221 │         observedAt = now_;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `spotTick` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:130:9\n    │\n130 │         (, spotTick,,) = poolManager.getSlot0(key.toId());\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/RISKHook.sol:54:39\n   │\n54 │     constructor(IPoolManager manager, address launchToken) {\n   │                                       ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:116:42\n    │\n116 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:116:87\n    │\n116 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : uint256(params.amountSpecified);\n    │                                                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:117:25\n    │\n117 │         if (magnitude > uint256(type(int256).max) - magnitude / 100) revert UnrepresentableFee();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RISKHook.sol:139:13\n    │\n139 │             emit FeeAccrued(Currency.unwrap(currency), fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:134:23\n    │\n134 │         uint256 fee = uint256(filled < 0 ? -filled : filled) / 100;\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/RISKHook.sol:141:44\n    │\n141 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                            ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe 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/RISKHook.sol:141:51\n    │\n141 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/RISKHook.sol:157:13\n    │\n157 │         if (duration >= INTERVAL) {\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:158:66\n    │\n158 │             int256 cumulative = tickSeconds + int256(spotTick) * int256(block.timestamp - observedAt);\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[reentrancy-no-eth]: external call can be reentered before `operating` is updated\n    ╭▸ src/RISKHook.sol:166:9\n    │\n166 │         poolManager.unlock(abi.encode(false));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/RISKHook.sol:166:9\n    │\n166 │         poolManager.unlock(abi.encode(false));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `operating` is updated\n    ╭▸ src/RISKHook.sol:174:9\n    │\n174 │         poolManager.unlock(abi.encode(true));\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/RISKHook.sol:171:13\n    │\n171 │         if (block.timestamp - lastBatch < INTERVAL) revert TooSoon();\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/RISKHook.sol:174:9\n    │\n174 │         poolManager.unlock(abi.encode(true));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:208:30\n    │\n208 │         BalanceDelta delta = poolManager.swap(key, SwapParams(zeroForOne, -int256(budget), limit), \"\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:211:25\n    │\n211 │         if (spent != 0) poolManager.burn(address(this), Currency.wrap(IMD).toId(), spent);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:212:26\n    │\n212 │         if (bought != 0) poolManager.take(Currency.wrap(token), DEAD, bought);\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/RISKHook.sol:185:13\n    │\n185 │             emit Swept(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/RISKHook.sol:205:13\n    │\n205 │             emit BatchExecuted(budget, 0, 0, limit);\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/RISKHook.sol:214:9\n    │\n214 │         emit BatchExecuted(budget, spent, bought, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:22\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:30\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:67\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:75\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:197:25\n    │\n197 │           uint160 limit = uint160(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━┛\n198 │ ┃             scaled < TickMath.MIN_SQRT_PRICE + 1\n199 │ ┃                 ? TickMath.MIN_SQRT_PRICE + 1\n200 │ ┃                 : scaled > TickMath.MAX_SQRT_PRICE - 1 ? TickMath.MAX_SQRT_PRICE - 1 : scaled\n201 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:208:76\n    │\n208 │         BalanceDelta delta = poolManager.swap(key, SwapParams(zeroForOne, -int256(budget), limit), \"\");\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/RISKHook.sol:209:25\n    │\n209 │         uint256 spent = uint256(-int256(zeroForOne ? delta.amount0() : delta.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:210:26\n    │\n210 │         uint256 bought = uint256(int256(zeroForOne ? delta.amount1() : delta.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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/RISKHook.sol:223:13\n    │\n223 │         if (duration >= INTERVAL) {\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:220:43\n    │\n220 │         tickSeconds += int256(spotTick) * int256(now_ - observedAt);\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/RISKHook.sol:232:36\n    │\n232 │         int256 mean = cumulative / int256(duration);\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/RISKHook.sol:233:44\n    │\n233 │         if (cumulative < 0 && cumulative % int256(duration) != 0) --mean;\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/RISKHook.sol:234:16\n    │\n234 │         return int24(mean);\n    │                ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/RISKHook.t.sol:268:25\n    │\n268 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n269 │         vm.warp(start + 1800);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/RISKHook.t.sol:284:17\n    │\n284 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":2078,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RISKHook.t.sol:RISKHookMainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 264.74µs (0.00ns CPU time)\n\nRan 3 tests for test/RISK.t.sol:RISKTest\n[PASS] testFuzz_transferAndAllowance(uint256) (runs: 256, μ: 171778, ~: 172195)\nLogs:\n  Bound result 8388608\n\n[PASS] test_noMintAndInvalidTransfer() (gas: 80953)\n[PASS] test_supplyAndFactoryDistribution() (gas: 186413)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 12.19ms (12.42ms CPU time)\n\nRan 2 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorRefusesInvalidInputsAndPermissionAddress() (gas: 4387985)\n[PASS] test_mineAndDeployDirectlyAtPredictedAddress() (gas: 48853616)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 49.92ms (36.81ms CPU time)\n\nRan 11 tests for test/RISKHook.t.sol:RISKHookTest\n[PASS] testFuzz_feeTracksActualPartialFill(bool,bool,uint256,uint24) (runs: 1000, μ: 339402, ~: 342623)\nLogs:\n  Bound result 99999999999990000000017\n  Bound result 6\n\n[PASS] test_allSwapModesAndSweep() (gas: 1433651)\n[PASS] test_batchCooldownAndBudget() (gas: 590616)\n[PASS] test_batchPartialFillRetainsClaimsAndCanRunAgain() (gas: 778289)\n[PASS] test_creationSizeAndRuntimeNoEscapeHatches() (gas: 2389610)\n[PASS] test_dustAndUnrepresentableRequests() (gas: 349197)\n[PASS] test_emptyBatchAndEmptySweep() (gas: 140397)\n[PASS] test_exactInputAndOutputPartialFillsBothDirections() (gas: 1347597)\n[PASS] test_permissionsInitAndAuthorization() (gas: 234365)\n[PASS] test_spotBeyondLimitDoesNotDeadlock() (gas: 721495)\n[PASS] test_timeWeightedReferenceAndSameBlockResistance() (gas: 497940)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 50.37ms (46.02ms CPU time)\n\nRan 11 tests for test/RISKHook.t.sol:RISKHookReverseOrderingTest\n[PASS] testFuzz_feeTracksActualPartialFill(bool,bool,uint256,uint24) (runs: 256, μ: 339544, ~: 342174)\nLogs:\n  Bound result 22606746449495248868942\n  Bound result 63\n\n[PASS] test_allSwapModesAndSweep() (gas: 1433651)\n[PASS] test_batchCooldownAndBudget() (gas: 590616)\n[PASS] test_batchPartialFillRetainsClaimsAndCanRunAgain() (gas: 778289)\n[PASS] test_creationSizeAndRuntimeNoEscapeHatches() (gas: 2389610)\n[PASS] test_dustAndUnrepresentableRequests() (gas: 349197)\n[PASS] test_emptyBatchAndEmptySweep() (gas: 140397)\n[PASS] test_exactInputAndOutputPartialFillsBothDirections() (gas: 1347597)\n[PASS] test_permissionsInitAndAuthorization() (gas: 234365)\n[PASS] test_spotBeyondLimitDoesNotDeadlock() (gas: 721495)\n[PASS] test_timeWeightedReferenceAndSameBlockResistance() (gas: 497940)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 84.22ms (31.29ms CPU time)\n\nRan 1 test for test/RISKInvariant.t.sol:RISKInvariantTest\n[PASS] invariant_conservationAndClaimAccounting() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------------+----------+-------+---------+----------╮\n| Contract          | Selector | Calls | Reverts | Discards |\n+===========================================================+\n| RISKInvariantTest | actBatch | 2759  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| RISKInvariantTest | actSwap  | 2704  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| RISKInvariantTest | actSweep | 2729  | 0       | 0        |\n╰-------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 4859\n  Bound result 3881\n  Bound result 5\n  Bound result 654768811181472532724\n  Bound result 400\n  Bound result 999999999990008388609\n  Bound result 7179\n  Bound result 508\n  Bound result 999999999990000002742\n  Bound result 5922\n  Bound result 4911\n  Bound result 3610\n  Bound result 3603\n  Bound result 6653\n  Bound result 212\n  Bound result 99518287065365269445\n  Bound result 6411\n  Bound result 6816\n  Bound result 386\n  Bound result 999999999990000000031\n  Bound result 157\n  Bound result 10000000000\n  Bound result 3600\n  Bound result 600\n  Bound result 999999999990000010159\n  Bound result 18\n  Bound result 999999999990000021434\n  Bound result 3729\n  Bound result 3617\n  Bound result 255\n  Bound result 999999999990000000015\n  Bound result 2\n  Bound result 999999999990000001024\n  Bound result 5010\n  Bound result 96\n  Bound result 999999999990000000307\n  Bound result 3729\n  Bound result 7083\n  Bound result 456\n  Bound result 655860891252342090481\n  Bound result 584\n  Bound result 999999999990000000128\n  Bound result 1\n  Bound result 100000000000000000000\n  Bound result 2\n  Bound result 999999999990000000008\n  Bound result 32\n  Bound result 999999999990000000033\n  Bound result 14\n  Bound result 999999999990000013857\n  Bound result 6063\n  Bound result 3610\n  Bound result 3697\n  Bound result 2\n  Bound result 384406336978568786467\n  Bound result 4055\n  Bound result 130\n  Bound result 1000000000000000000000\n  Bound result 4285\n  Bound result 433\n  Bound result 999999999900000\n  Bound result 4195\n  Bound result 16\n  Bound result 999999999990000000017\n  Bound result 5800\n  Bound result 128\n  Bound result 999999999990000013973\n  Bound result 487\n  Bound result 999999999990000000112\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.99s (1.98s CPU time)\n\nRan 6 test suites in 2.00s (2.19s CPU time): 28 tests passed, 0 failed, 1 skipped (29 total tests)\n","passed":true},{"durationMs":37,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"RISK.approve(address,uint256)\",\"RISK.transfer(address,uint256)\",\"RISK.transferFrom(address,address,uint256)\",\"RISKHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"RISKHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"RISKHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"RISKHook.executeBatch()\",\"RISKHook.sweep()\",\"RISKHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":5,\"DEPENDENCIES.md\":12,\"README.md\":65,\"SECURITY_REVIEW.md\":33,\"foundry.toml\":11,\"launch.json\":16,\"script/MineRISKHook.s.sol\":29,\"src/HookFlags.sol\":29,\"src/RISK.sol\":11,\"src/RISKHook.sol\":236,\"test/Deployment.t.sol\":74,\"test/PoolRouter.sol\":49,\"test/RISK.t.sol\":53,\"test/RISKHook.t.sol\":365,\"test/RISKInvariant.t.sol\":73},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1364,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/RISKHook.sol:231: RISKHook._meanTick(int256,uint256) (src/RISKHook.sol#231-235) uses a weak PRNG: \"cumulative < 0 && cumulative % int256(duration) != 0 (src/RISKHook.sol#233)\"\n[medium/medium] reentrancy-no-eth at src/RISKHook.sol:177: Reentrancy in RISKHook.unlockCallback(bytes) (src/RISKHook.sol#177-216):\n[medium/medium] unused-return at src/RISKHook.sol:169: RISKHook.executeBatch() (src/RISKHook.sol#169-175) ignores return value by poolManager.unlock(abi.encode(true)) (src/RISKHook.sol#174)\n[medium/medium] unused-return at src/RISKHook.sol:177: RISKHook.unlockCallback(bytes) (src/RISKHook.sol#177-216) ignores return value by (current,None,None,None) = poolManager.getSlot0(key.toId()) (src/RISKHook.sol#202)\n[medium/medium] unused-return at src/RISKHook.sol:122: RISKHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/RISKHook.sol#122-142) ignores return value by (None,spotTick,None,None) = poolManager.getSlot0(key.toId()) (src/RISKHook.sol#130)\n[medium/medium] unused-return at src/RISKHook.sol:177: RISKHook.unlockCallback(bytes) (src/RISKHook.sol#177-216) ignores return value by (None,spotTick,None,None) = poolManager.getSlot0(key.toId()) (src/RISKHook.sol#213)\n[medium/medium] unused-return at src/RISKHook.sol:164: RISKHook.sweep() (src/RISKHook.sol#164-167) ignores return value by poolManager.unlock(abi.encode(false)) (src/RISKHook.sol#166)\n[low/medium] events-maths at src/RISKHook.sol:92: RISKHook.beforeInitialize(address,PoolKey,uint160) (src/RISKHook.sol#92-106) should emit an event for: \n[low/medium] reentrancy-events at src/RISKHook.sol:177: Reentrancy in RISKHook.unlockCallback(bytes) (src/RISKHook.sol#177-216):\n[low/medium] reentrancy-events at src/RISKHook.sol:177: Reentrancy in RISKHook.unlockCallback(bytes) (src/RISKHook.sol#177-216):\n[low/medium] reentrancy-events at src/RISKHook.sol:122: Reentrancy in RISKHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/RISKHook.sol#122-142):\n[low/medium] timestamp at src/RISKHook.sol:169: RISKHook.executeBatch() (src/RISKHook.sol#169-175) uses timestamp for comparisons\n[low/medium] timestamp at src/RISKHook.sol:218: RISKHook._observe() (src/RISKHook.sol#218-229) uses timestamp for comparisons\n[low/medium] timestamp at src/RISKHook.sol:153: RISKHook.referencePrice() (src/RISKHook.sol#153-162) uses timestamp for comparisons\n[low/medium] timestamp at src/RISKHook.sol:231: RISKHook._meanTick(int256,uint256) (src/RISKHook.sol#231-235) uses timestamp for comparisons\n[low/medium] timestamp at src/RISKHook.sol:177: RISKHook.unlockCallback(bytes) (src/RISKHook.sol#177-216) uses timestamp for comparisons","passed":true},{"durationMs":383,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/RISKHook.sol:208: Reentrancy: State change after external call (3 places)\n[high] unsafe-casting at src/RISKHook.sol:234: Unsafe Casting of integers\n[low] internal-function-used-once at src/HookFlags.sol:22: Internal Function Used Only Once\n[low] large-numeric-literal at src/RISK.sol:9: Large Numeric Literal\n[low] literal-instead-of-constant at src/RISKHook.sol:117: Literal Instead of Constant (5 places)\n[low] state-change-without-event at src/RISKHook.sol:92: State Change Without Event (2 places)\n[low] unchecked-return at src/RISKHook.sol:166: Unchecked Return (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"4d9bfae1b8db9ff94d9f2a557d938170df1c64598829ac18384983e5bf6bb99a","verifiedTreeHash":"b7454ffe5dc6424504ee63e12ed6e08acb488409","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":5588,"exitCode":0,"name":"build","output":"Compiling 82 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.41s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `spotTick` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:100:9\n    │\n100 │         spotTick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `periodStart` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:102:9\n    │\n102 │         periodStart = block.timestamp;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:103:9\n    │\n103 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:104:9\n    │\n104 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `tickSeconds` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:220:9\n    │\n220 │         tickSeconds += int256(spotTick) * int256(now_ - observedAt);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:221:9\n    │\n221 │         observedAt = now_;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `spotTick` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:130:9\n    │\n130 │         (, spotTick,,) = poolManager.getSlot0(key.toId());\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/RISKHook.sol:54:39\n   │\n54 │     constructor(IPoolManager manager, address launchToken) {\n   │                                       ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:116:42\n    │\n116 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:116:87\n    │\n116 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : uint256(params.amountSpecified);\n    │                                                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:117:25\n    │\n117 │         if (magnitude > uint256(type(int256).max) - magnitude / 100) revert UnrepresentableFee();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RISKHook.sol:139:13\n    │\n139 │             emit FeeAccrued(Currency.unwrap(currency), fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:134:23\n    │\n134 │         uint256 fee = uint256(filled < 0 ? -filled : filled) / 100;\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/RISKHook.sol:141:44\n    │\n141 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                            ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe 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/RISKHook.sol:141:51\n    │\n141 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/RISKHook.sol:157:13\n    │\n157 │         if (duration >= INTERVAL) {\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:158:66\n    │\n158 │             int256 cumulative = tickSeconds + int256(spotTick) * int256(block.timestamp - observedAt);\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[reentrancy-no-eth]: external call can be reentered before `operating` is updated\n    ╭▸ src/RISKHook.sol:166:9\n    │\n166 │         poolManager.unlock(abi.encode(false));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/RISKHook.sol:166:9\n    │\n166 │         poolManager.unlock(abi.encode(false));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `operating` is updated\n    ╭▸ src/RISKHook.sol:174:9\n    │\n174 │         poolManager.unlock(abi.encode(true));\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/RISKHook.sol:171:13\n    │\n171 │         if (block.timestamp - lastBatch < INTERVAL) revert TooSoon();\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/RISKHook.sol:174:9\n    │\n174 │         poolManager.unlock(abi.encode(true));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:208:30\n    │\n208 │         BalanceDelta delta = poolManager.swap(key, SwapParams(zeroForOne, -int256(budget), limit), \"\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:211:25\n    │\n211 │         if (spent != 0) poolManager.burn(address(this), Currency.wrap(IMD).toId(), spent);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:212:26\n    │\n212 │         if (bought != 0) poolManager.take(Currency.wrap(token), DEAD, bought);\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/RISKHook.sol:185:13\n    │\n185 │             emit Swept(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/RISKHook.sol:205:13\n    │\n205 │             emit BatchExecuted(budget, 0, 0, limit);\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/RISKHook.sol:214:9\n    │\n214 │         emit BatchExecuted(budget, spent, bought, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:22\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:30\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:67\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:75\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:197:25\n    │\n197 │           uint160 limit = uint160(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━┛\n198 │ ┃             scaled < TickMath.MIN_SQRT_PRICE + 1\n199 │ ┃                 ? TickMath.MIN_SQRT_PRICE + 1\n200 │ ┃                 : scaled > TickMath.MAX_SQRT_PRICE - 1 ? TickMath.MAX_SQRT_PRICE - 1 : scaled\n201 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:208:76\n    │\n208 │         BalanceDelta delta = poolManager.swap(key, SwapParams(zeroForOne, -int256(budget), limit), \"\");\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/RISKHook.sol:209:25\n    │\n209 │         uint256 spent = uint256(-int256(zeroForOne ? delta.amount0() : delta.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:210:26\n    │\n210 │         uint256 bought = uint256(int256(zeroForOne ? delta.amount1() : delta.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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/RISKHook.sol:223:13\n    │\n223 │         if (duration >= INTERVAL) {\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:220:43\n    │\n220 │         tickSeconds += int256(spotTick) * int256(now_ - observedAt);\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/RISKHook.sol:232:36\n    │\n232 │         int256 mean = cumulative / int256(duration);\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/RISKHook.sol:233:44\n    │\n233 │         if (cumulative < 0 && cumulative % int256(duration) != 0) --mean;\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/RISKHook.sol:234:16\n    │\n234 │         return int24(mean);\n    │                ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/RISKHook.t.sol:272:25\n    │\n272 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n273 │         vm.warp(start + 1800);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/RISKHook.t.sol:288:17\n    │\n288 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":3743,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RISKHook.t.sol:RISKHookMainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 319.37µs (0.00ns CPU time)\n\nRan 1 test for test/RISKHookAdversarial.t.sol:RISKHookAdversarialMainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 296.43µs (0.00ns CPU time)\n\nRan 3 tests for test/RISK.t.sol:RISKTest\n[PASS] testFuzz_transferAndAllowance(uint256) (runs: 256, μ: 171413, ~: 172147)\nLogs:\n  Bound result 24576\n\n[PASS] test_noMintAndInvalidTransfer() (gas: 80953)\n[PASS] test_supplyAndFactoryDistribution() (gas: 186413)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 9.93ms (10.13ms CPU time)\n\nRan 11 tests for test/RISKHook.t.sol:RISKHookReverseOrderingTest\n[PASS] testFuzz_feeTracksActualPartialFill(bool,bool,uint256,uint24) (runs: 256, μ: 338291, ~: 338198)\nLogs:\n  Bound result 10000000001\n  Bound result 88\n\n[PASS] test_allSwapModesAndSweep() (gas: 1433651)\n[PASS] test_batchCooldownAndBudget() (gas: 590616)\n[PASS] test_batchPartialFillRetainsClaimsAndCanRunAgain() (gas: 778289)\n[PASS] test_creationSizeAndRuntimeNoEscapeHatches() (gas: 2389610)\n[PASS] test_dustAndUnrepresentableRequests() (gas: 349197)\n[PASS] test_emptyBatchAndEmptySweep() (gas: 140397)\n[PASS] test_exactInputAndOutputPartialFillsBothDirections() (gas: 1347597)\n[PASS] test_permissionsInitAndAuthorization() (gas: 234365)\n[PASS] test_spotBeyondLimitDoesNotDeadlock() (gas: 721495)\n[PASS] test_timeWeightedReferenceAndSameBlockResistance() (gas: 497940)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 138.20ms (41.51ms CPU time)\n\nRan 2 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorRefusesInvalidInputsAndPermissionAddress() (gas: 4387985)\n[PASS] test_mineAndDeployDirectlyAtPredictedAddress() (gas: 48853616)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 138.35ms (36.74ms CPU time)\n\nRan 1 test for test/RISKHookAdversarial.t.sol:RISKHookFreshManagerTest\n[PASS] testFuzz_firstBuyWorksWithZeroPairedCurrencyReserves(bool,uint96) (runs: 1000, μ: 413435, ~: 413575)\nLogs:\n  Bound result 9999999999999999003598\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 140.85ms (126.46ms CPU time)\n\nRan 11 tests for test/RISKHook.t.sol:RISKHookTest\n[PASS] testFuzz_feeTracksActualPartialFill(bool,bool,uint256,uint24) (runs: 1000, μ: 338952, ~: 342198)\nLogs:\n  Bound result 99999999999990000006907\n  Bound result 100\n\n[PASS] test_allSwapModesAndSweep() (gas: 1433651)\n[PASS] test_batchCooldownAndBudget() (gas: 590616)\n[PASS] test_batchPartialFillRetainsClaimsAndCanRunAgain() (gas: 778289)\n[PASS] test_creationSizeAndRuntimeNoEscapeHatches() (gas: 2389610)\n[PASS] test_dustAndUnrepresentableRequests() (gas: 349197)\n[PASS] test_emptyBatchAndEmptySweep() (gas: 140397)\n[PASS] test_exactInputAndOutputPartialFillsBothDirections() (gas: 1347597)\n[PASS] test_permissionsInitAndAuthorization() (gas: 234365)\n[PASS] test_spotBeyondLimitDoesNotDeadlock() (gas: 721495)\n[PASS] test_timeWeightedReferenceAndSameBlockResistance() (gas: 497940)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 140.82ms (144.75ms CPU time)\n\nRan 9 tests for test/RISKHookAdversarial.t.sol:RISKHookAdversarialReverseTest\n[PASS] testFuzz_dustFeesUseFilledAmount(bool,bool,uint16) (runs: 1000, μ: 310966, ~: 312721)\nLogs:\n  Bound result 4262\n\n[PASS] test_batchWithoutLiquidityPreservesBudgetAndRecovers() (gas: 1011405)\n[PASS] test_failedBatchRestoresCooldownPriceAndClaims() (gas: 720824)\n[PASS] test_failedSweepRestoresClaimsAndCanBeRetried() (gas: 453458)\n[PASS] test_hugeRepresentableRequestsOnlyPayForSmallPartialFill() (gas: 1364392)\n[PASS] test_nestedMaintenanceCannotConsumeCooldownOrClaims() (gas: 1121601)\n[PASS] test_overdueSwapsNeverRunMaintenanceInsideCallback() (gas: 1430281)\n[PASS] test_unrepresentableRequestsRevertThroughRealManagerAndLeaveNoFees() (gas: 582823)\n[PASS] test_zeroLiquiditySwapsChargeNothingInEveryMode() (gas: 1690136)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 146.37ms (47.25ms CPU time)\n\nRan 13 tests for test/RISKHookAdversarial.t.sol:RISKHookAdversarialTest\n[PASS] testFuzz_dustFeesUseFilledAmount(bool,bool,uint16) (runs: 1000, μ: 310689, ~: 312248)\nLogs:\n  Bound result 731\n\n[PASS] testFuzz_referenceWeightsIrregularDurations(uint16,uint16,bool) (runs: 1000, μ: 726775, ~: 724938)\nLogs:\n  Bound result 97\n  Bound result 2339\n\n[PASS] test_batchAcceptsProtocolFeesAndStillUsesOnlyPriceLimit() (gas: 767704)\n[PASS] test_batchPriceFeedsNextObservationDespiteSelfCallbackExemption() (gas: 428033)\n[PASS] test_batchWithoutLiquidityPreservesBudgetAndRecovers() (gas: 1011417)\n[PASS] test_dustBudgetRetainsClaimsAndStillEnforcesCooldown() (gas: 355809)\n[PASS] test_failedBatchRestoresCooldownPriceAndClaims() (gas: 720791)\n[PASS] test_failedSweepRestoresClaimsAndCanBeRetried() (gas: 453425)\n[PASS] test_hugeRepresentableRequestsOnlyPayForSmallPartialFill() (gas: 1364502)\n[PASS] test_nestedMaintenanceCannotConsumeCooldownOrClaims() (gas: 1121623)\n[PASS] test_overdueSwapsNeverRunMaintenanceInsideCallback() (gas: 1430304)\n[PASS] test_unrepresentableRequestsRevertThroughRealManagerAndLeaveNoFees() (gas: 582801)\n[PASS] test_zeroLiquiditySwapsChargeNothingInEveryMode() (gas: 1690103)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 146.41ms (272.72ms CPU time)\n\nRan 1 test for test/RISKInvariant.t.sol:RISKInvariantTest\n[PASS] invariant_conservationAndClaimAccounting() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------------+----------+-------+---------+----------╮\n| Contract          | Selector | Calls | Reverts | Discards |\n+===========================================================+\n| RISKInvariantTest | actBatch | 2786  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| RISKInvariantTest | actSwap  | 2744  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| RISKInvariantTest | actSweep | 2662  | 0       | 0        |\n╰-------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 595\n  Bound result 999999999990000008389\n  Bound result 130\n  Bound result 988017077291248909870\n  Bound result 497\n  Bound result 999999999990000000256\n  Bound result 3667\n  Bound result 6101\n  Bound result 0\n  Bound result 219898233426205802745\n  Bound result 259\n  Bound result 824588066402809556799\n  Bound result 5202\n  Bound result 589\n  Bound result 999999999990000007770\n  Bound result 3701\n  Bound result 183\n  Bound result 999999999990000001001\n  Bound result 2\n  Bound result 5604359107230\n  Bound result 223\n  Bound result 999999999990000014676\n  Bound result 558\n  Bound result 999999999990000008545\n  Bound result 101\n  Bound result 999999999990000000968\n  Bound result 270\n  Bound result 999999999990004097001\n  Bound result 64\n  Bound result 1014889156509221946\n  Bound result 5003\n  Bound result 6752\n  Bound result 516\n  Bound result 999999999990000002501\n  Bound result 64\n  Bound result 999999999990000001348\n  Bound result 3804\n  Bound result 3804\n  Bound result 432\n  Bound result 999999999990000000008\n  Bound result 3940\n  Bound result 6571\n  Bound result 3\n  Bound result 716976202377251879666\n  Bound result 108\n  Bound result 999999999999999999997\n  Bound result 20\n  Bound result 984885780179610473\n  Bound result 5783\n  Bound result 6764\n  Bound result 3614\n  Bound result 190\n  Bound result 999999999990002629944\n  Bound result 384\n  Bound result 999999999990000002157\n  Bound result 5492\n  Bound result 6101\n  Bound result 242\n  Bound result 999999999990000016385\n  Bound result 135\n  Bound result 999999999990000010543\n  Bound result 30\n  Bound result 32179413295\n  Bound result 4371\n  Bound result 6243\n  Bound result 6310\n  Bound result 245\n  Bound result 569590465975755214954\n  Bound result 6101\n  Bound result 11\n  Bound result 999999999990000007863\n  Bound result 4302\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.11s (2.08s CPU time)\n\nRan 1 test for test/RISKLifecycleInvariant.t.sol:RISKLifecycleInvariantTest\n[PASS] invariant_claimsAndAssetsSurviveLiquidityAndMaintenanceSequences() (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------------+-----------------+-------+---------+----------╮\n| Contract             | Selector        | Calls | Reverts | Discards |\n+=====================================================================+\n| RISKLifecycleHandler | advance         | 3322  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | batch           | 3230  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | changeLiquidity | 3247  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | swap            | 3275  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | sweep           | 3310  | 0       | 0        |\n╰----------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 15\n  Bound result 16008\n  Bound result 33\n  Bound result 999990000000000000003664\n  Bound result 7199\n  Bound result 1676417\n  Bound result 1\n  Bound result 2065\n  Bound result 2973\n  Bound result 10000000000000034879\n  Bound result 48\n  Bound result 30\n  Bound result 999990000000000000001193\n  Bound result 3265\n  Bound result 17974\n  Bound result 1\n  Bound result 254\n  Bound result 51\n  Bound result 4096\n  Bound result 13518\n  Bound result 20\n  Bound result 1982\n  Bound result 2\n  Bound result 6\n  Bound result 17439\n  Bound result 58\n  Bound result 4100096\n  Bound result 16\n  Bound result 5\n  Bound result 16773119\n  Bound result 4\n  Bound result 999990000000000000005929\n  Bound result 5992\n  Bound result 53\n  Bound result 1982\n  Bound result 957625571\n  Bound result 45\n  Bound result 999990000000001383437907\n  Bound result 13202\n  Bound result 18\n  Bound result 485834934582535861072\n  Bound result 4\n  Bound result 999990000000001029410238\n  Bound result 2514264337593543950086\n  Bound result 44\n  Bound result 4390\n  Bound result 9\n  Bound result 1575\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.66s (3.63s CPU time)\n\nRan 11 test suites in 3.66s (6.62s CPU time): 52 tests passed, 0 failed, 2 skipped (54 total tests)\n","passed":true},{"durationMs":49,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"RISK.approve(address,uint256)\",\"RISK.transfer(address,uint256)\",\"RISK.transferFrom(address,address,uint256)\",\"RISKHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"RISKHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"RISKHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"RISKHook.executeBatch()\",\"RISKHook.sweep()\",\"RISKHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":5,\"DEPENDENCIES.md\":12,\"README.md\":65,\"SECURITY_REVIEW.md\":33,\"foundry.toml\":11,\"launch.json\":16,\"script/MineRISKHook.s.sol\":29,\"src/HookFlags.sol\":29,\"src/RISK.sol\":11,\"src/RISKHook.sol\":236,\"test/Deployment.t.sol\":74,\"test/PoolRouter.sol\":49,\"test/README.md\":38,\"test/RISK.t.sol\":53,\"test/RISKHook.t.sol\":369,\"test/RISKHookAdversarial.t.sol\":380,\"test/RISKInvariant.t.sol\":73,\"test/RISKLifecycleInvariant.t.sol\":206},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"a38b0b6529ad13611900a05a449df192260b020947ef8e020580a4034cdb3345","verifiedTreeHash":"1b4a79ee1721d9c0381d8a52520b8db00099e667","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":6626,"exitCode":0,"name":"build","output":"Compiling 83 files with Solc 0.8.26\nSolc 0.8.26 finished in 6.39s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `spotTick` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:100:9\n    │\n100 │         spotTick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `periodStart` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:102:9\n    │\n102 │         periodStart = block.timestamp;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:103:9\n    │\n103 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:104:9\n    │\n104 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `tickSeconds` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:222:9\n    │\n222 │         tickSeconds += int256(spotTick) * int256(now_ - observedAt);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:223:9\n    │\n223 │         observedAt = now_;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `spotTick` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:130:9\n    │\n130 │         (, spotTick,,) = poolManager.getSlot0(key.toId());\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/RISKHook.sol:54:39\n   │\n54 │     constructor(IPoolManager manager, address launchToken) {\n   │                                       ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:116:42\n    │\n116 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:116:87\n    │\n116 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : uint256(params.amountSpecified);\n    │                                                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:117:25\n    │\n117 │         if (magnitude > uint256(type(int256).max) - magnitude / 100) revert UnrepresentableFee();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RISKHook.sol:139:13\n    │\n139 │             emit FeeAccrued(Currency.unwrap(currency), fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:134:23\n    │\n134 │         uint256 fee = uint256(filled < 0 ? -filled : filled) / 100;\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/RISKHook.sol:141:44\n    │\n141 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                            ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe 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/RISKHook.sol:141:51\n    │\n141 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/RISKHook.sol:157:13\n    │\n157 │         if (duration >= INTERVAL) {\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:158:66\n    │\n158 │             int256 cumulative = tickSeconds + int256(spotTick) * int256(block.timestamp - observedAt);\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[reentrancy-no-eth]: external call can be reentered before `operating` is updated\n    ╭▸ src/RISKHook.sol:166:9\n    │\n166 │         poolManager.unlock(abi.encode(false));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/RISKHook.sol:166:9\n    │\n166 │         poolManager.unlock(abi.encode(false));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `operating` is updated\n    ╭▸ src/RISKHook.sol:174:9\n    │\n174 │         poolManager.unlock(abi.encode(true));\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/RISKHook.sol:171:13\n    │\n171 │         if (block.timestamp - lastBatch < INTERVAL) revert TooSoon();\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/RISKHook.sol:174:9\n    │\n174 │         poolManager.unlock(abi.encode(true));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:210:30\n    │\n210 │         BalanceDelta delta = poolManager.swap(key, SwapParams(zeroForOne, -int256(budget), limit), \"\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:213:25\n    │\n213 │         if (spent != 0) poolManager.burn(address(this), Currency.wrap(IMD).toId(), spent);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:214:26\n    │\n214 │         if (bought != 0) poolManager.take(Currency.wrap(token), DEAD, bought);\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/RISKHook.sol:185:13\n    │\n185 │             emit Swept(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/RISKHook.sol:207:13\n    │\n207 │             emit BatchExecuted(budget, 0, 0, limit);\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/RISKHook.sol:216:9\n    │\n216 │         emit BatchExecuted(budget, spent, bought, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:22\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:30\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:67\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:75\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:200:25\n    │\n200 │           uint160 limit = uint160(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━┛\n201 │ ┃             scaled < TickMath.MIN_SQRT_PRICE + 1\n202 │ ┃                 ? TickMath.MIN_SQRT_PRICE + 1\n203 │ ┃                 : scaled > TickMath.MAX_SQRT_PRICE - 1 ? TickMath.MAX_SQRT_PRICE - 1 : scaled\n204 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:210:76\n    │\n210 │         BalanceDelta delta = poolManager.swap(key, SwapParams(zeroForOne, -int256(budget), limit), \"\");\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/RISKHook.sol:211:25\n    │\n211 │         uint256 spent = uint256(-int256(zeroForOne ? delta.amount0() : delta.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:212:26\n    │\n212 │         uint256 bought = uint256(int256(zeroForOne ? delta.amount1() : delta.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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/RISKHook.sol:225:13\n    │\n225 │         if (duration >= INTERVAL) {\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:222:43\n    │\n222 │         tickSeconds += int256(spotTick) * int256(now_ - observedAt);\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/RISKHook.sol:234:36\n    │\n234 │         int256 mean = cumulative / int256(duration);\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/RISKHook.sol:235:44\n    │\n235 │         if (cumulative < 0 && cumulative % int256(duration) != 0) --mean;\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/RISKHook.sol:236:16\n    │\n236 │         return int24(mean);\n    │                ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/RISKHook.t.sol:274:25\n    │\n274 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n275 │         vm.warp(start + 1800);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/RISKHook.t.sol:290:17\n    │\n290 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":3827,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RISKHook.t.sol:RISKHookMainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 426.56µs (0.00ns CPU time)\n\nRan 1 test for test/RISKHookAdversarial.t.sol:RISKHookAdversarialMainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 428.79µs (0.00ns CPU time)\n\nRan 3 tests for test/RISK.t.sol:RISKTest\n[PASS] testFuzz_transferAndAllowance(uint256) (runs: 256, μ: 171576, ~: 172267)\nLogs:\n  Bound result 526629943356224140125953832\n\n[PASS] test_noMintAndInvalidTransfer() (gas: 80953)\n[PASS] test_supplyAndFactoryDistribution() (gas: 186413)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 18.41ms (18.56ms CPU time)\n\nRan 1 test for test/RISKHookAdversarial.t.sol:RISKHookFreshManagerTest\n[PASS] testFuzz_firstBuyWorksWithZeroPairedCurrencyReserves(bool,uint96) (runs: 1000, μ: 413419, ~: 413476)\nLogs:\n  Bound result 9999999999999999010661\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 161.43ms (137.54ms CPU time)\n\nRan 2 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorRefusesInvalidInputsAndPermissionAddress() (gas: 4387985)\n[PASS] test_mineAndDeployDirectlyAtPredictedAddress() (gas: 38446323)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 186.00ms (37.17ms CPU time)\n\nRan 7 tests for test/RISKBatchLimit.t.sol:RISKBatchLimitReverseOrderingTest\n[PASS] testFuzz_idleReferenceCannotWidenBatchLimit(uint32,uint96) (runs: 256, μ: 726976, ~: 727132)\nLogs:\n  Bound result 3600\n  Bound result 590000000000001219117320\n\n[PASS] test_idleReferenceCannotWidenBatchLimit() (gas: 719636)\n[PASS] test_reportedDeepPoolSandwichLosesMoney() (gas: 135918561)\n[PASS] test_reportedThinPoolSandwichLosesMoney() (gas: 136048899)\n[PASS] test_sortedRatioConvention() (gas: 219855)\n[PASS] test_staleBandPartialFillRetainsBudgetAndNextBatchProgresses() (gas: 105590724)\n[PASS] test_zeroFillStillConsumesCooldownAndRetainsClaims() (gas: 1177748)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 187.05ms (698.87ms CPU time)\n\nRan 9 tests for test/RISKHookAdversarial.t.sol:RISKHookAdversarialReverseTest\n[PASS] testFuzz_dustFeesUseFilledAmount(bool,bool,uint16) (runs: 1000, μ: 309056, ~: 312240)\nLogs:\n  Bound result 4440\n\n[PASS] test_batchWithoutLiquidityPreservesBudgetAndRecovers() (gas: 1011577)\n[PASS] test_failedBatchRestoresCooldownPriceAndClaims() (gas: 720996)\n[PASS] test_failedSweepRestoresClaimsAndCanBeRetried() (gas: 453458)\n[PASS] test_hugeRepresentableRequestsOnlyPayForSmallPartialFill() (gas: 1364392)\n[PASS] test_nestedMaintenanceCannotConsumeCooldownOrClaims() (gas: 1121687)\n[PASS] test_overdueSwapsNeverRunMaintenanceInsideCallback() (gas: 1430281)\n[PASS] test_unrepresentableRequestsRevertThroughRealManagerAndLeaveNoFees() (gas: 582823)\n[PASS] test_zeroLiquiditySwapsChargeNothingInEveryMode() (gas: 1690136)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 248.23ms (209.13ms CPU time)\n\nRan 13 tests for test/RISKHookAdversarial.t.sol:RISKHookAdversarialTest\n[PASS] testFuzz_dustFeesUseFilledAmount(bool,bool,uint16) (runs: 1000, μ: 309054, ~: 312248)\nLogs:\n  Bound result 4576\n\n[PASS] testFuzz_referenceWeightsIrregularDurations(uint16,uint16,bool) (runs: 1000, μ: 726983, ~: 729090)\nLogs:\n  Bound result 255\n  Bound result 1700\n\n[PASS] test_batchAcceptsProtocolFeesAndStillUsesOnlyPriceLimit() (gas: 784023)\n[PASS] test_batchPriceFeedsNextObservationDespiteSelfCallbackExemption() (gas: 428119)\n[PASS] test_batchWithoutLiquidityPreservesBudgetAndRecovers() (gas: 1011589)\n[PASS] test_dustBudgetRetainsClaimsAndStillEnforcesCooldown() (gas: 355878)\n[PASS] test_failedBatchRestoresCooldownPriceAndClaims() (gas: 720975)\n[PASS] test_failedSweepRestoresClaimsAndCanBeRetried() (gas: 453421)\n[PASS] test_hugeRepresentableRequestsOnlyPayForSmallPartialFill() (gas: 1364486)\n[PASS] test_nestedMaintenanceCannotConsumeCooldownOrClaims() (gas: 1121705)\n[PASS] test_overdueSwapsNeverRunMaintenanceInsideCallback() (gas: 1430292)\n[PASS] test_unrepresentableRequestsRevertThroughRealManagerAndLeaveNoFees() (gas: 582793)\n[PASS] test_zeroLiquiditySwapsChargeNothingInEveryMode() (gas: 1690087)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 274.27ms (306.88ms CPU time)\n\nRan 11 tests for test/RISKHook.t.sol:RISKHookReverseOrderingTest\n[PASS] testFuzz_feeTracksActualPartialFill(bool,bool,uint256,uint24) (runs: 256, μ: 338475, ~: 338143)\nLogs:\n  Bound result 10000000001\n  Bound result 3\n\n[PASS] test_allSwapModesAndSweep() (gas: 1433651)\n[PASS] test_batchCooldownAndBudget() (gas: 590702)\n[PASS] test_batchPartialFillRetainsClaimsAndCanRunAgain() (gas: 794965)\n[PASS] test_creationSizeAndRuntimeNoEscapeHatches() (gas: 2405366)\n[PASS] test_dustAndUnrepresentableRequests() (gas: 349197)\n[PASS] test_emptyBatchAndEmptySweep() (gas: 140459)\n[PASS] test_exactInputAndOutputPartialFillsBothDirections() (gas: 1347597)\n[PASS] test_permissionsInitAndAuthorization() (gas: 234365)\n[PASS] test_spotBeyondLimitDoesNotDeadlock() (gas: 721659)\n[PASS] test_timeWeightedReferenceAndSameBlockResistance() (gas: 497940)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 274.29ms (245.82ms CPU time)\n\nRan 11 tests for test/RISKHook.t.sol:RISKHookTest\n[PASS] testFuzz_feeTracksActualPartialFill(bool,bool,uint256,uint24) (runs: 1000, μ: 338856, ~: 338198)\nLogs:\n  Bound result 99999999999990000003202\n  Bound result 100\n\n[PASS] test_allSwapModesAndSweep() (gas: 1433651)\n[PASS] test_batchCooldownAndBudget() (gas: 590702)\n[PASS] test_batchPartialFillRetainsClaimsAndCanRunAgain() (gas: 794965)\n[PASS] test_creationSizeAndRuntimeNoEscapeHatches() (gas: 2405366)\n[PASS] test_dustAndUnrepresentableRequests() (gas: 349197)\n[PASS] test_emptyBatchAndEmptySweep() (gas: 140459)\n[PASS] test_exactInputAndOutputPartialFillsBothDirections() (gas: 1347597)\n[PASS] test_permissionsInitAndAuthorization() (gas: 234365)\n[PASS] test_spotBeyondLimitDoesNotDeadlock() (gas: 721659)\n[PASS] test_timeWeightedReferenceAndSameBlockResistance() (gas: 497940)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 290.68ms (270.48ms CPU time)\n\nRan 7 tests for test/RISKBatchLimit.t.sol:RISKBatchLimitTest\n[PASS] testFuzz_idleReferenceCannotWidenBatchLimit(uint32,uint96) (runs: 256, μ: 720876, ~: 721043)\nLogs:\n  Bound result 3600\n  Bound result 590000000000000000000219\n\n[PASS] test_idleReferenceCannotWidenBatchLimit() (gas: 713558)\n[PASS] test_reportedDeepPoolSandwichLosesMoney() (gas: 135908936)\n[PASS] test_reportedThinPoolSandwichLosesMoney() (gas: 136049028)\n[PASS] test_sortedRatioConvention() (gas: 220027)\n[PASS] test_staleBandPartialFillRetainsBudgetAndNextBatchProgresses() (gas: 105599771)\n[PASS] test_zeroFillStillConsumesCooldownAndRetainsClaims() (gas: 1172049)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 290.59ms (517.65ms CPU time)\n\nRan 1 test for test/RISKInvariant.t.sol:RISKInvariantTest\n[PASS] invariant_conservationAndClaimAccounting() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------------+----------+-------+---------+----------╮\n| Contract          | Selector | Calls | Reverts | Discards |\n+===========================================================+\n| RISKInvariantTest | actBatch | 2707  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| RISKInvariantTest | actSwap  | 2755  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| RISKInvariantTest | actSweep | 2730  | 0       | 0        |\n╰-------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 348\n  Bound result 970000000000000000\n  Bound result 3756\n  Bound result 13\n  Bound result 999999999990000005643\n  Bound result 144\n  Bound result 999999999999999999999\n  Bound result 6697\n  Bound result 3960\n  Bound result 3843\n  Bound result 6968\n  Bound result 3601\n  Bound result 60\n  Bound result 10000000000000000000\n  Bound result 3939\n  Bound result 6208\n  Bound result 4254\n  Bound result 598\n  Bound result 1030927835051546391\n  Bound result 3600\n  Bound result 3603\n  Bound result 4704\n  Bound result 423\n  Bound result 999999999990000000007\n  Bound result 512\n  Bound result 999999999990000004415\n  Bound result 4964\n  Bound result 5940\n  Bound result 7\n  Bound result 999999999990000000033\n  Bound result 3845\n  Bound result 3\n  Bound result 999999999990000006699\n  Bound result 5590\n  Bound result 4978\n  Bound result 328\n  Bound result 723830115443659212518\n  Bound result 3602\n  Bound result 4845\n  Bound result 3602\n  Bound result 3696\n  Bound result 156\n  Bound result 5999999999400\n  Bound result 7199\n  Bound result 3663\n  Bound result 6095\n  Bound result 60\n  Bound result 10000000000\n  Bound result 501\n  Bound result 999999999990000000007\n  Bound result 32\n  Bound result 999999999993101934471\n  Bound result 60\n  Bound result 999999999990000172801\n  Bound result 4380\n  Bound result 3609\n  Bound result 3845\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.22s (2.19s CPU time)\n\nRan 1 test for test/RISKLifecycleInvariant.t.sol:RISKLifecycleInvariantTest\n[PASS] invariant_claimsAndAssetsSurviveLiquidityAndMaintenanceSequences() (runs: 256, calls: 16384, reverts: 0)\n\n╭----------------------+-----------------+-------+---------+----------╮\n| Contract             | Selector        | Calls | Reverts | Discards |\n+=====================================================================+\n| RISKLifecycleHandler | advance         | 3196  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | batch           | 3241  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | changeLiquidity | 3297  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | swap            | 3383  | 0       | 0        |\n|----------------------+-----------------+-------+---------+----------|\n| RISKLifecycleHandler | sweep           | 3267  | 0       | 0        |\n╰----------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 999990000000000000008426\n  Bound result 15962\n  Bound result 15\n  Bound result 13836\n  Bound result 11\n  Bound result 2903\n  Bound result 19\n  Bound result 1\n  Bound result 1\n  Bound result 11\n  Bound result 2514264337593543950305\n  Bound result 7\n  Bound result 999990000000000000010416\n  Bound result 954794264337593543871107\n  Bound result 1150\n  Bound result 2500\n  Bound result 331\n  Bound result 1\n  Bound result 13\n  Bound result 4202047189\n  Bound result 12\n  Bound result 89232972745271124057\n  Bound result 2\n  Bound result 1854\n  Bound result 53\n  Bound result 2484\n  Bound result 9\n  Bound result 96\n  Bound result 956\n  Bound result 41\n  Bound result 7\n  Bound result 13877\n  Bound result 17\n  Bound result 999990000000000016777199\n  Bound result 1219117319\n  Bound result 10\n  Bound result 4440\n  Bound result 10404\n  Bound result 6\n  Bound result 2013\n  Bound result 6580\n  Bound result 31\n  Bound result 4373\n  Bound result 999990000000006536898931\n  Bound result 13333\n  Bound result 36\n  Bound result 18041\n  Bound result 16\n  Bound result 999990000000000000002599\n  Bound result 13759\n  Bound result 18\n  Bound result 7719\n  Bound result 14\n  Bound result 9950\n  Bound result 10\n  Bound result 679971968514891425420\n  Bound result 15\n  Bound result 15537\n  Bound result 32\n  Bound result 16\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 3.71s (3.67s CPU time)\n\nRan 13 test suites in 3.71s (7.86s CPU time): 66 tests passed, 0 failed, 2 skipped (68 total tests)\n","passed":true},{"durationMs":62,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"RISK.approve(address,uint256)\",\"RISK.transfer(address,uint256)\",\"RISK.transferFrom(address,address,uint256)\",\"RISKHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"RISKHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"RISKHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"RISKHook.executeBatch()\",\"RISKHook.sweep()\",\"RISKHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":5,\"DEPENDENCIES.md\":12,\"README.md\":67,\"SECURITY_REVIEW.md\":41,\"foundry.toml\":11,\"launch.json\":16,\"script/MineRISKHook.s.sol\":29,\"src/HookFlags.sol\":29,\"src/RISK.sol\":11,\"src/RISKHook.sol\":238,\"test/Deployment.t.sol\":74,\"test/PoolRouter.sol\":49,\"test/README.md\":47,\"test/RISK.t.sol\":53,\"test/RISKBatchLimit.t.sol\":204,\"test/RISKHook.t.sol\":371,\"test/RISKHookAdversarial.t.sol\":395,\"test/RISKInvariant.t.sol\":73,\"test/RISKLifecycleInvariant.t.sol\":206},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"b66347e61a29f0c19c3755ecd3a3508ae49d40d772bd9e27bbbbaf0b0513203a","verifiedTreeHash":"c22e6b5a44fa14e0fe955642fbf8ce2c3c3eeb3c","verifierVersion":"0.1.0+ad90ce4c"},{"checks":[{"durationMs":3747,"exitCode":0,"name":"build","output":"Compiling 81 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.59s\nCompiler run successful!\nwarning[missing-events-arithmetic]: `spotTick` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:100:9\n    │\n100 │         spotTick = TickMath.getTickAtSqrtPrice(sqrtPriceX96);\n    │         ━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `periodStart` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:102:9\n    │\n102 │         periodStart = block.timestamp;\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:103:9\n    │\n103 │         observedAt = block.timestamp;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `lastBatch` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:104:9\n    │\n104 │         lastBatch = block.timestamp;\n    │         ━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `tickSeconds` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:222:9\n    │\n222 │         tickSeconds += int256(spotTick) * int256(now_ - observedAt);\n    │         ━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `observedAt` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:223:9\n    │\n223 │         observedAt = now_;\n    │         ━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-events-arithmetic]: `spotTick` is changed without an event but is used in arithmetic\n    ╭▸ src/RISKHook.sol:130:9\n    │\n130 │         (, spotTick,,) = poolManager.getSlot0(key.toId());\n    │         ━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/missing-events-arithmetic\n\nwarning[missing-zero-check]: address parameter is used in a state write or value transfer without a zero-address check\n   ╭▸ src/RISKHook.sol:54:39\n   │\n54 │     constructor(IPoolManager manager, address launchToken) {\n   │                                       ━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/missing-zero-check\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:116:42\n    │\n116 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : uint256(params.amountSpecified);\n    │                                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:116:87\n    │\n116 │             params.amountSpecified < 0 ? uint256(-(params.amountSpecified + 1)) + 1 : uint256(params.amountSpecified);\n    │                                                                                       ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:117:25\n    │\n117 │         if (magnitude > uint256(type(int256).max) - magnitude / 100) revert UnrepresentableFee();\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/RISKHook.sol:139:13\n    │\n139 │             emit FeeAccrued(Currency.unwrap(currency), fee);\n    │             ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:134:23\n    │\n134 │         uint256 fee = uint256(filled < 0 ? -filled : filled) / 100;\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/RISKHook.sol:141:44\n    │\n141 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\n    │                                            ━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int128' is safe 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/RISKHook.sol:141:51\n    │\n141 │         return (IHooks.afterSwap.selector, int128(int256(fee)));\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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/RISKHook.sol:157:13\n    │\n157 │         if (duration >= INTERVAL) {\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:158:66\n    │\n158 │             int256 cumulative = tickSeconds + int256(spotTick) * int256(block.timestamp - observedAt);\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[reentrancy-no-eth]: external call can be reentered before `operating` is updated\n    ╭▸ src/RISKHook.sol:166:9\n    │\n166 │         poolManager.unlock(abi.encode(false));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/RISKHook.sol:166:9\n    │\n166 │         poolManager.unlock(abi.encode(false));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `operating` is updated\n    ╭▸ src/RISKHook.sol:174:9\n    │\n174 │         poolManager.unlock(abi.encode(true));\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/RISKHook.sol:171:13\n    │\n171 │         if (block.timestamp - lastBatch < INTERVAL) revert TooSoon();\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/RISKHook.sol:174:9\n    │\n174 │         poolManager.unlock(abi.encode(true));\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:210:30\n    │\n210 │         BalanceDelta delta = poolManager.swap(key, SwapParams(zeroForOne, -int256(budget), limit), \"\");\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:213:25\n    │\n213 │         if (spent != 0) poolManager.burn(address(this), Currency.wrap(IMD).toId(), spent);\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-no-eth]: external call can be reentered before `spotTick` is updated\n    ╭▸ src/RISKHook.sol:214:26\n    │\n214 │         if (bought != 0) poolManager.take(Currency.wrap(token), DEAD, bought);\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/RISKHook.sol:185:13\n    │\n185 │             emit Swept(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/RISKHook.sol:207:13\n    │\n207 │             emit BatchExecuted(budget, 0, 0, limit);\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/RISKHook.sol:216:9\n    │\n216 │         emit BatchExecuted(budget, spent, bought, limit);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:22\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:30\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                              ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:67\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:190:75\n    │\n190 │         if (budget > uint256(uint128(type(int128).max))) budget = uint256(uint128(type(int128).max));\n    │                                                                           ━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint128' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:200:25\n    │\n200 │           uint160 limit = uint160(\n    │ ┏━━━━━━━━━━━━━━━━━━━━━━━━━┛\n201 │ ┃             scaled < TickMath.MIN_SQRT_PRICE + 1\n202 │ ┃                 ? TickMath.MIN_SQRT_PRICE + 1\n203 │ ┃                 : scaled > TickMath.MAX_SQRT_PRICE - 1 ? TickMath.MAX_SQRT_PRICE - 1 : scaled\n204 │ ┃         );\n    │ ┗━━━━━━━━━┛\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint160' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:210:76\n    │\n210 │         BalanceDelta delta = poolManager.swap(key, SwapParams(zeroForOne, -int256(budget), limit), \"\");\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/RISKHook.sol:211:25\n    │\n211 │         uint256 spent = uint256(-int256(zeroForOne ? delta.amount0() : delta.amount1()));\n    │                         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:212:26\n    │\n212 │         uint256 bought = uint256(int256(zeroForOne ? delta.amount1() : delta.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[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n    ╭▸ src/RISKHook.sol:225:13\n    │\n225 │         if (duration >= INTERVAL) {\n    │             ━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/RISKHook.sol:222:43\n    │\n222 │         tickSeconds += int256(spotTick) * int256(now_ - observedAt);\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/RISKHook.sol:234:36\n    │\n234 │         int256 mean = cumulative / int256(duration);\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/RISKHook.sol:235:44\n    │\n235 │         if (cumulative < 0 && cumulative % int256(duration) != 0) --mean;\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/RISKHook.sol:236:16\n    │\n236 │         return int24(mean);\n    │                ━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'int24' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/RISKHook.t.sol:270:25\n    │\n270 │         uint256 start = block.timestamp;\n    │                         ━━━━━━━━━━━━━━━\n271 │         vm.warp(start + 1800);\n    │         ───────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/RISKHook.t.sol:286:17\n    │\n286 │         vm.warp(block.timestamp + 3600);\n    │         ────────━━━━━━━━━━━━━━━──────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\n","passed":true},{"durationMs":2106,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/RISKHook.t.sol:RISKHookMainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 330.32µs (0.00ns CPU time)\n\nRan 3 tests for test/RISK.t.sol:RISKTest\n[PASS] testFuzz_transferAndAllowance(uint256) (runs: 256, μ: 171883, ~: 172195)\nLogs:\n  Bound result 3600\n\n[PASS] test_noMintAndInvalidTransfer() (gas: 80953)\n[PASS] test_supplyAndFactoryDistribution() (gas: 186413)\nSuite result: ok. 3 passed; 0 failed; 0 skipped; finished in 13.74ms (14.02ms CPU time)\n\nRan 11 tests for test/RISKHook.t.sol:RISKHookReverseOrderingTest\n[PASS] testFuzz_feeTracksActualPartialFill(bool,bool,uint256,uint24) (runs: 256, μ: 339060, ~: 342437)\nLogs:\n  Bound result 99999999999990000000006\n  Bound result 66\n\n[PASS] test_allSwapModesAndSweep() (gas: 1433651)\n[PASS] test_batchCooldownAndBudget() (gas: 590702)\n[PASS] test_batchPartialFillRetainsClaimsAndCanRunAgain() (gas: 794965)\n[PASS] test_creationSizeAndRuntimeNoEscapeHatches() (gas: 2405366)\n[PASS] test_dustAndUnrepresentableRequests() (gas: 349197)\n[PASS] test_emptyBatchAndEmptySweep() (gas: 140459)\n[PASS] test_exactInputAndOutputPartialFillsBothDirections() (gas: 1347597)\n[PASS] test_permissionsInitAndAuthorization() (gas: 234365)\n[PASS] test_spotBeyondLimitDoesNotDeadlock() (gas: 721659)\n[PASS] test_timeWeightedReferenceAndSameBlockResistance() (gas: 497940)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 51.91ms (38.41ms CPU time)\n\nRan 11 tests for test/RISKHook.t.sol:RISKHookTest\n[PASS] testFuzz_feeTracksActualPartialFill(bool,bool,uint256,uint24) (runs: 1000, μ: 339252, ~: 342595)\nLogs:\n  Bound result 99271564458\n  Bound result 58\n\n[PASS] test_allSwapModesAndSweep() (gas: 1433651)\n[PASS] test_batchCooldownAndBudget() (gas: 590702)\n[PASS] test_batchPartialFillRetainsClaimsAndCanRunAgain() (gas: 794965)\n[PASS] test_creationSizeAndRuntimeNoEscapeHatches() (gas: 2405366)\n[PASS] test_dustAndUnrepresentableRequests() (gas: 349197)\n[PASS] test_emptyBatchAndEmptySweep() (gas: 140459)\n[PASS] test_exactInputAndOutputPartialFillsBothDirections() (gas: 1347597)\n[PASS] test_permissionsInitAndAuthorization() (gas: 234365)\n[PASS] test_spotBeyondLimitDoesNotDeadlock() (gas: 721659)\n[PASS] test_timeWeightedReferenceAndSameBlockResistance() (gas: 497940)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 54.85ms (46.94ms CPU time)\n\nRan 2 tests for test/Deployment.t.sol:DeploymentTest\n[PASS] test_constructorRefusesInvalidInputsAndPermissionAddress() (gas: 4387985)\n[PASS] test_mineAndDeployDirectlyAtPredictedAddress() (gas: 38446323)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 151.52ms (29.45ms CPU time)\n\nRan 7 tests for test/RISKBatchLimit.t.sol:RISKBatchLimitReverseOrderingTest\n[PASS] testFuzz_idleReferenceCannotWidenBatchLimit(uint32,uint96) (runs: 256, μ: 727001, ~: 727132)\nLogs:\n  Bound result 31532406\n  Bound result 590000000000000000004066\n\n[PASS] test_idleReferenceCannotWidenBatchLimit() (gas: 719636)\n[PASS] test_reportedDeepPoolSandwichLosesMoney() (gas: 135918561)\n[PASS] test_reportedThinPoolSandwichLosesMoney() (gas: 136048899)\n[PASS] test_sortedRatioConvention() (gas: 219855)\n[PASS] test_staleBandPartialFillRetainsBudgetAndNextBatchProgresses() (gas: 105590724)\n[PASS] test_zeroFillStillConsumesCooldownAndRetainsClaims() (gas: 1177748)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 151.55ms (473.71ms CPU time)\n\nRan 7 tests for test/RISKBatchLimit.t.sol:RISKBatchLimitTest\n[PASS] testFuzz_idleReferenceCannotWidenBatchLimit(uint32,uint96) (runs: 256, μ: 720934, ~: 721043)\nLogs:\n  Bound result 31532406\n  Bound result 590000000000000000016774\n\n[PASS] test_idleReferenceCannotWidenBatchLimit() (gas: 713558)\n[PASS] test_reportedDeepPoolSandwichLosesMoney() (gas: 135908936)\n[PASS] test_reportedThinPoolSandwichLosesMoney() (gas: 136049028)\n[PASS] test_sortedRatioConvention() (gas: 220027)\n[PASS] test_staleBandPartialFillRetainsBudgetAndNextBatchProgresses() (gas: 105599771)\n[PASS] test_zeroFillStillConsumesCooldownAndRetainsClaims() (gas: 1172049)\nSuite result: ok. 7 passed; 0 failed; 0 skipped; finished in 200.07ms (442.35ms CPU time)\n\nRan 1 test for test/RISKInvariant.t.sol:RISKInvariantTest\n[PASS] invariant_conservationAndClaimAccounting() (runs: 128, calls: 8192, reverts: 0)\n\n╭-------------------+----------+-------+---------+----------╮\n| Contract          | Selector | Calls | Reverts | Discards |\n+===========================================================+\n| RISKInvariantTest | actBatch | 2743  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| RISKInvariantTest | actSwap  | 2752  | 0       | 0        |\n|-------------------+----------+-------+---------+----------|\n| RISKInvariantTest | actSweep | 2697  | 0       | 0        |\n╰-------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 201\n  Bound result 851841397728469666299\n  Bound result 577\n  Bound result 999999999990000000008\n  Bound result 2\n  Bound result 9111768533045318626\n  Bound result 7041\n  Bound result 145\n  Bound result 999999999990000003600\n  Bound result 320\n  Bound result 999999999990000000642\n  Bound result 3607\n  Bound result 3600\n  Bound result 111\n  Bound result 999999999990000014676\n  Bound result 3917\n  Bound result 32\n  Bound result 999999999990000004917\n  Bound result 100\n  Bound result 999999999990000024577\n  Bound result 64\n  Bound result 10000000000\n  Bound result 3\n  Bound result 999999999990000009346\n  Bound result 467\n  Bound result 281474372730864\n  Bound result 595\n  Bound result 999999999990000000256\n  Bound result 5298\n  Bound result 423\n  Bound result 999999999990000008389\n  Bound result 300\n  Bound result 999999999990000006796\n  Bound result 3843\n  Bound result 6791\n  Bound result 114\n  Bound result 999999999990000002049\n  Bound result 3602\n  Bound result 4017\n  Bound result 5298\n  Bound result 3797\n  Bound result 4329\n  Bound result 8\n  Bound result 999999999990000010409\n  Bound result 4318\n  Bound result 98\n  Bound result 1030927835051546391\n  Bound result 506\n  Bound result 726925184864626663726\n  Bound result 6101\n  Bound result 5346\n  Bound result 464\n  Bound result 943839315070286937892\n  Bound result 590\n  Bound result 999999999990000008608\n  Bound result 50\n  Bound result 70978410576623436002\n  Bound result 363\n  Bound result 10000000000\n  Bound result 3601\n  Bound result 4159\n  Bound result 3613\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.03s (1.99s CPU time)\n\nRan 8 test suites in 2.03s (2.65s CPU time): 42 tests passed, 0 failed, 1 skipped (43 total tests)\n","passed":true},{"durationMs":43,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"RISK.approve(address,uint256)\",\"RISK.transfer(address,uint256)\",\"RISK.transferFrom(address,address,uint256)\",\"RISKHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"RISKHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"RISKHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"RISKHook.executeBatch()\",\"RISKHook.sweep()\",\"RISKHook.unlockCallback(bytes)\"],\"files\":{\".gitignore\":5,\"DEPENDENCIES.md\":12,\"README.md\":67,\"SECURITY_REVIEW.md\":41,\"foundry.toml\":11,\"launch.json\":16,\"script/MineRISKHook.s.sol\":29,\"src/HookFlags.sol\":29,\"src/RISK.sol\":11,\"src/RISKHook.sol\":238,\"test/Deployment.t.sol\":74,\"test/PoolRouter.sol\":49,\"test/RISK.t.sol\":53,\"test/RISKBatchLimit.t.sol\":204,\"test/RISKHook.t.sol\":367,\"test/RISKInvariant.t.sol\":73},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1350,"exitCode":0,"name":"slither","output":"[high/medium] weak-prng at src/RISKHook.sol:233: RISKHook._meanTick(int256,uint256) (src/RISKHook.sol#233-237) uses a weak PRNG: \"cumulative < 0 && cumulative % int256(duration) != 0 (src/RISKHook.sol#235)\"\n[medium/medium] reentrancy-no-eth at src/RISKHook.sol:177: Reentrancy in RISKHook.unlockCallback(bytes) (src/RISKHook.sol#177-218):\n[medium/medium] unused-return at src/RISKHook.sol:169: RISKHook.executeBatch() (src/RISKHook.sol#169-175) ignores return value by poolManager.unlock(abi.encode(true)) (src/RISKHook.sol#174)\n[medium/medium] unused-return at src/RISKHook.sol:122: RISKHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/RISKHook.sol#122-142) ignores return value by (None,spotTick,None,None) = poolManager.getSlot0(key.toId()) (src/RISKHook.sol#130)\n[medium/medium] unused-return at src/RISKHook.sol:177: RISKHook.unlockCallback(bytes) (src/RISKHook.sol#177-218) ignores return value by (None,spotTick,None,None) = poolManager.getSlot0(key.toId()) (src/RISKHook.sol#215)\n[medium/medium] unused-return at src/RISKHook.sol:164: RISKHook.sweep() (src/RISKHook.sol#164-167) ignores return value by poolManager.unlock(abi.encode(false)) (src/RISKHook.sol#166)\n[medium/medium] unused-return at src/RISKHook.sol:177: RISKHook.unlockCallback(bytes) (src/RISKHook.sol#177-218) ignores return value by (current,None,None,None) = poolManager.getSlot0(key.toId()) (src/RISKHook.sol#194)\n[low/medium] events-maths at src/RISKHook.sol:92: RISKHook.beforeInitialize(address,PoolKey,uint160) (src/RISKHook.sol#92-106) should emit an event for: \n[low/medium] reentrancy-events at src/RISKHook.sol:177: Reentrancy in RISKHook.unlockCallback(bytes) (src/RISKHook.sol#177-218):\n[low/medium] reentrancy-events at src/RISKHook.sol:177: Reentrancy in RISKHook.unlockCallback(bytes) (src/RISKHook.sol#177-218):\n[low/medium] reentrancy-events at src/RISKHook.sol:122: Reentrancy in RISKHook.afterSwap(address,PoolKey,SwapParams,BalanceDelta,bytes) (src/RISKHook.sol#122-142):\n[low/medium] timestamp at src/RISKHook.sol:169: RISKHook.executeBatch() (src/RISKHook.sol#169-175) uses timestamp for comparisons\n[low/medium] timestamp at src/RISKHook.sol:220: RISKHook._observe() (src/RISKHook.sol#220-231) uses timestamp for comparisons\n[low/medium] timestamp at src/RISKHook.sol:177: RISKHook.unlockCallback(bytes) (src/RISKHook.sol#177-218) uses timestamp for comparisons\n[low/medium] timestamp at src/RISKHook.sol:153: RISKHook.referencePrice() (src/RISKHook.sol#153-162) uses timestamp for comparisons\n[low/medium] timestamp at src/RISKHook.sol:233: RISKHook._meanTick(int256,uint256) (src/RISKHook.sol#233-237) uses timestamp for comparisons","passed":true},{"durationMs":403,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/RISKHook.sol:210: Reentrancy: State change after external call (3 places)\n[high] unsafe-casting at src/RISKHook.sol:236: Unsafe Casting of integers\n[low] internal-function-used-once at src/HookFlags.sol:22: Internal Function Used Only Once\n[low] large-numeric-literal at src/RISK.sol:9: Large Numeric Literal\n[low] literal-instead-of-constant at src/RISKHook.sol:117: Literal Instead of Constant (5 places)\n[low] state-change-without-event at src/RISKHook.sol:92: State Change Without Event (2 places)\n[low] unchecked-return at src/RISKHook.sol:166: Unchecked Return (2 places)","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"fd0395228c39c844a699c5128fb5b714e9456250c63a5c2ac85c2a8c32c37034","verifiedTreeHash":"389045dc04bf4cbe9cd56fd40fd82db4d59ea695","verifierVersion":"0.1.0+ad90ce4c"}]}