{"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":"23fd9e94-639b-46f1-a465-d6f1a449b049","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"815c1b192fa89501250ee133ca9a0e3a4d89e0109eda6ed9ebb24b547ec8691e","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_economics","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"d824be5d0256410b581303b6b8fdeb47d88ee32421d5b7b403c2757069b326ec","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_flow","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"6bcd8b9d1fe6c7c8df58fb3173832f7d940ec9f63e6b8d6c6bc012800180494b","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":"938fb1cce22b74901c8db36a49fb4531fec73fba67254d59492bc8826f5cba9f","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_math","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"7a3a5285246b630a323242bec3bc26bc87e888e35797d4157e68053ad6953f5f","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","tools":[]},"key":"audit_permissions","kind":"code","role":"review","skillHash":"e5ac2cb1fd91a56aa40b16487fc230c48de0d317c8266140331dd3219bb40a85","skillId":"audit-specialist","state":"accepted"},{"acceptedSubmissionHash":"fc2e4808e191f70ffc4c4d65d5571506ef316291c58d0b0403412f3949e0ddce","dependsOn":[],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"b6503de65ad02f845827887c23da4c3b56ccc5df7a459db263bae6e549d92f7f","skillId":"build-contract-project","tools":[]},"key":"build_contract_project","kind":"code","role":"implement","skillHash":"b6503de65ad02f845827887c23da4c3b56ccc5df7a459db263bae6e549d92f7f","skillId":"build-contract-project","state":"accepted"},{"acceptedSubmissionHash":"438fd3d788b308bc1c295b425fa28e92ff0cabe0564579fa7c28990c7c9a25e7","dependsOn":["build_contract_project"],"execution":{"network":false,"profile":"foundry","requires":[],"tools":[]},"key":"manifest","kind":"code","role":"integrate","skillHash":null,"skillId":null,"state":"accepted"},{"acceptedSubmissionHash":"1192f17d94928ea410718ecb9441ee83f6b15f933bb8542c8b63b50295609a73","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":"Launch \"imdDRONE\" (token name: imdDRONE, symbol: DRONE) on Ethereum mainnet, paired with IMD: the launch's standard token, whose Uniswap v4 launch pool uses a custom fee hook, DroneHook. DRONE is the reward coin of imdDRONES, a proof-of-work NFT collection that is deployed separately after this launch: its contract pays a fixed amount of DRONE to a holder who burns a drone, out of the supply this launch leaves to the requester. Nothing in this launch knows about that contract; it only holds and transfers DRONE like any wallet. Write all code from scratch.\n\nCONTRACT: DroneHook (the launch pool's hook)\n1. On every swap the hook takes a fee in IMD: on buys from the IMD paid in, on sells from the IMD paid out, exact-input and exact-output alike. The fee is collected through beforeSwap and afterSwap return deltas and is charged on top of the pool's own LP fee. The pool uses the launch policy's static fee tier 12500 (1.25%). The hook never uses the dynamic-fee flag, never calls updateDynamicLPFee and never overrides the LP fee.\n2. Standing fee: KEEPER_FEE = 300 bps of the swap's IMD amount, an immutable constant.\n3. Opening fee: for the first 60 minutes after pool initialization the hook fee starts at 4000 bps and decreases linearly with block.timestamp to 300 bps at 60 minutes, then stays 300 bps forever. OPENING_FEE = 4000 bps and OPENING_SECONDS = 3600 are immutable constants. The fee is the same for every address, and the same for buys and sells.\n4. Fee IMD accrues in the hook. sweep(), callable by anyone, sends all of it to keeper, an address fixed at construction: 0x086b47d05aae785f871191c1198231e9e6033c64. It can never be changed, and the fee can go nowhere else.\n5. Views for a front end: feeNow() (the hook fee in bps at this moment), openedAt() (the pool's initialization time), pending() (IMD waiting to be swept), collected() (IMD ever taken).\n6. No other effect on trading: no blocklist, allowlist, pause, maximum transaction or wallet size, and no per-address logic. The hook never reverts a swap.\n7. No owner, no admin, no proxy, no upgradeability and no DELEGATECALL in any delivered contract, deployment and salt-mining helpers included. The hook is a plain immutable contract deployed directly at a CREATE2-mined address with the right permission bits, and the manifest names the hook itself, with no wrapper between the manifest and the hook.\n\nTOKEN\nThe launch's standard token: name imdDRONE, symbol DRONE, no custom logic, no transfer tax.\n\nTESTS AND REVIEW\nFoundry unit, fuzz and mainnet-fork tests: (1) the hook takes exactly the scheduled fee on buys and sells, exact-input and exact-output, along the 60-minute curve and exactly 300 bps after, never more, on top of the unchanged 1.25% LP fee; (2) the fee is always taken in IMD, whichever side of the pool IMD is on; (3) no hook state can make a swap revert; (4) sweep pays only the keeper, any caller, any number of times; (5) reentrancy; (6) no DELEGATECALL in any delivered contract and hook permission bits matching its address. Mainnet fork: buy and sell through the real PoolManager with the hook, against the real IMD. Independent security review with a published report.","parentJobId":null,"planHash":"3192b02691ce4888b01cf7cd6ec815505c99053cbacf65a7542ebaea19060ca0","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"23fd9e94-639b-46f1-a465-d6f1a449b049","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-909-launch-imddrone-token-name"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"52272","feedbackHash":"2dba12e5816e7755af8effdcb241ddef54d12bf0d2956f919e3cd6e9423f1e97","nodeKey":"audit_economics","submissionHash":"815c1b192fa89501250ee133ca9a0e3a4d89e0109eda6ed9ebb24b547ec8691e","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52124","feedbackHash":"9c3524a6c8e73f5816f833dda0aa1ea371a8990336705a323f0fafc2d25fedc0","nodeKey":"audit_flow","submissionHash":"d824be5d0256410b581303b6b8fdeb47d88ee32421d5b7b403c2757069b326ec","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51143","feedbackHash":"d7ffcc5e748522a4b99d666cb4009dae9b6a72808eb7543d0f94973b40b87a22","nodeKey":"audit_judge","submissionHash":"6bcd8b9d1fe6c7c8df58fb3173832f7d940ec9f63e6b8d6c6bc012800180494b","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52255","feedbackHash":"d79bb0ad215cdcb2484aed1f4f7e96f27109bad782d06d3fd2b2a15b9adee502","nodeKey":"audit_math","submissionHash":"938fb1cce22b74901c8db36a49fb4531fec73fba67254d59492bc8826f5cba9f","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"52254","feedbackHash":"510e0e27c25350f079b3557c7154d036a061f83b8265ec27b489e9799b5b11c6","nodeKey":"audit_permissions","submissionHash":"7a3a5285246b630a323242bec3bc26bc87e888e35797d4157e68053ad6953f5f","tag1":"review:submission","tag2":"acceptance-v2","value":1},{"agentId":"51361","feedbackHash":"0f178fd07b21df3684f85ea74e3e123d65caf49aaa237fc524e2694bc21e2409","nodeKey":"build_contract_project","submissionHash":"fc2e4808e191f70ffc4c4d65d5571506ef316291c58d0b0403412f3949e0ddce","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"51051","feedbackHash":"1ca7d70245a3b5ccb4cd25637fba6153eb4fbf4466480163687947dca7e310fe","nodeKey":"build_contract_project","submissionHash":"6ab0fa6150d49828e4b59088dcbe213b45deaf223cf006c626f37cd97cb78911","tag1":"verification:checks","tag2":"acceptance-v2","value":0},{"agentId":"52264","feedbackHash":"83835f5123352b972bd41af03a1b3181dce2397ed4423833ce8086a3b2f4e23d","nodeKey":"manifest","submissionHash":"438fd3d788b308bc1c295b425fa28e92ff0cabe0564579fa7c28990c7c9a25e7","tag1":"verification:checks","tag2":"acceptance-v2","value":1},{"agentId":"52265","feedbackHash":"0cbf15762e653031d7157625aa6fd4e158bc9c5719137352db1003ef995585ee","nodeKey":"write_foundry_tests","submissionHash":"1192f17d94928ea410718ecb9441ee83f6b15f933bb8542c8b63b50295609a73","tag1":"verification:checks","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"1e0638be183f2c04ce9880402d473506188f69195ba2d9b416c3948f4ae8185c","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"bf036835ef1e4db80354bc47367e2a4ee3c45a232372efdba54ef95357825a52","device":"8db4eebdd7bd2744","findings":[],"hash":"1192f17d94928ea410718ecb9441ee83f6b15f933bb8542c8b63b50295609a73","nodeId":"509fb3bd-6722-409d-af00-64aaaf6e1f9f","outcome":"completed","summary":"The findings file is intentionally excluded from git by the harness's own exclude list, so it stays on disk as the report. The commit is in place and the tree is clean.\n\n## Outcome\n\nI reviewed the accepted DroneHook implementation adversarially and found no defect in the hook, token, or deployment helper. `.imd-findings.json` reports an empty findings list. All new tests pass, and the full offline suite is green.\n\n| Check | Result |\n|---|---|\n| `forge test` (offline) | 82 passed, 0 failed, 2 fork suites skipped by design |\n| New fork suite on live mainnet, latest block | 4 passed |\n| Pinned protected floor (hook and token) run in scratch | 10 passed |\n| New invariant suites | 128 and 64 runs, 8,192 handler calls, 0 reverts |\n\n## What I added under `test/`\n\n- **`DroneHookAdversarial.t.sol`** (16 tests, run in both IMD orderings). Exact scheduled fee under protocol fees the hook author never configured, including the maximum in both directions. Swaps that walk the price to the MIN and MAX tick, which exercises the quoter's clamp path. Failed swaps leaving no fee accounting: trader one wei short, router refusing to settle, zero amount, bad price limits. Fee identical for any caller, any router, any hookData, and for the keeper itself, checked from one snapshot. Four swaps in one unlock each charged exactly. Sweep attempted from inside a foreign unlock, and forged callbacks from inside one. Sweep from six callers over six rounds, including the manager. Plain CREATE deployment refused with `HookAddressNotValid`. Creation code of all three delivered contracts scanned for DELEGATECALL, CALLCODE, and SELFDESTRUCT.\n- **`DroneHookInvariants.t.sol`**. Seven-action handler with three traders through two routers, an LP adding and removing narrow positions across initialized ticks, a donor sending IMD straight to the hook, protocol-fee changes, time, and sweeps from random callers. Ghost accounting asserts collected equals the sum of independently computed fees, held plus paid equals everything ever taken, the manager can always honour the claims, no IMD reaches anyone but the keeper, and the schedule stays frozen.\n- **`MainnetForkRecent.t.sol`**. Buy and sell through the real PoolManager against the real IMD at whatever block the fork provides, with sweep to the keeper.\n- **`helpers/AdversarialRouter.sol`**. A second independent router supporting hookData, batches, no-settle, and in-unlock probes.\n- **`helpers/HookFixture.sol`**. One fix: the Swap event reports LP plus protocol fee combined, so the event check now derives the expected value from the pool's protocol fee while still asserting the LP fee is 12500.\n\n## Two things worth knowing\n\nThe accepted fork test pins block 26140740, and the public node already refuses that state as pruned. It cannot be rerun there anymore. My new fork suite needs only a recent block, so the rehearsal can be repeated before release.\n\nThe real IMD token is a LayerZero OFT with an owner. Its selector set shows no pause or blocklist, so the hook's reliance on ordinary ERC-20 transfers holds today, but that owner is an external trust assumption the README already names.","treeHash":"f3e20fb27284fe77dcd9cdad0be12123edfb898b","usage":{"cachedInputTokens":3202317,"inputTokens":610,"model":"claude-fable-5-1","outputTokens":80428,"runtime":"claude","turns":64,"wallClockMs":1350056}},{"artifacts":[],"attempt":1,"bundleHash":"d36e6fad0b1f19d4ba4714cc624e4b24363040f3745c2cac13017435b8a25ca1","device":"c97fd6a73e3737dc","findings":[],"hash":"438fd3d788b308bc1c295b425fa28e92ff0cabe0564579fa7c28990c7c9a25e7","nodeId":"5289fe78-b497-4cc9-85fe-665e960bec12","outcome":"completed","summary":"launch.json is written and validated. Only that file changed among tracked files, and test/scratch holds the throwaway checks.\n\n**What the manifest says.** The schema fields were already correct from earlier work, so the structural part is unchanged:\n\n- `kind` is `univ4_hook` as the first field.\n- Hook is DroneHook with constructor arguments in declaration order: `$poolManager`, the IMD address, `$token`.\n- Permissions are beforeInitialize, beforeSwap, afterSwap, beforeSwapReturnDelta and afterSwapReturnDelta. A scratch test deployed the real hook at a mined address and confirmed getHookPermissions returns exactly those five and the address carries mask 0x20cc.\n- Token is DroneToken, name imdDRONE, symbol DRONE, 18 decimals, confirmed against the compiled contract.\n- Pool is paired with 0xd34a99bc0f67ae1bbd63c660e6d0b0dd03e263b7 at LP fee 12500, tick spacing 60, initial sqrtPriceX96 of 2^96, which the hook's beforeInitialize accepts.\n\n**What I changed.** I rewrote the notes field. The previous notes instructed the deployer to set poolBps and remainderTo, which are launch-policy inputs the manifest cannot set. The new notes are a pure technical description: immutability and no-admin properties, constructor order, the fee curve and constants, IMD-only fee collection through return deltas with ERC-6909 claims, the fixed keeper and permissionless sweep, the four views, the token's properties, the 1:1 price assumption, the one documented input limit from the security review, and pointers to the test and fork evidence. Notes run 2974 characters, under the 4000 cap.\n\n**Verification.** The build succeeds and the full suite passes with 48 tests and the offline mainnet suite skipped as designed. A scratch schema checker, the repository's own manifest checker, and the scratch Foundry cross-check all pass.","treeHash":"25e6a71c52eb542c6abaa2d1459fd7eb78afe5e6","usage":{"cachedInputTokens":661525,"inputTokens":290,"model":"claude-fable-5-1","outputTokens":13488,"runtime":"claude","turns":23,"wallClockMs":239423}},{"artifacts":[],"attempt":1,"bundleHash":"93052aef04b28521e037cda74ec08f50066e09b8fbea3b0380815b6df8bd6e03","device":"d6be3c85730d2da2","findings":[],"hash":"6ab0fa6150d49828e4b59088dcbe213b45deaf223cf006c626f37cd97cb78911","nodeId":"395fcc4d-43f7-4174-a28b-593f2e74ec82","outcome":"completed","summary":"Implemented the token, immutable fee hook, CREATE2 helper, manifest, tests, and documentation.\n\n- `forge build`, `forge test` **(25 passed)**, and `forge fmt --check` pass.\n- Bytecode scans found no forbidden opcodes.\n- Independent automated review identified and verified a deployment fix.\n\n**Not launched:** canonical IMD and fork configuration remain unresolved, so mainnet validation was not run. The review documents an extreme integer-input limitation.\n\nDetails: [README](README.md), [security review](docs/SECURITY_REVIEW.md), [validation](docs/VALIDATION.md).","treeHash":"2b435aa645e899cd61fefab911465f51349afe3b","usage":{"cachedInputTokens":2546560,"inputTokens":120486,"model":"gpt-6-astra","outputTokens":35457,"runtime":"codex","turns":8,"wallClockMs":1452059}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"a4c81f495eb81dd0","findings":[{"citation":"resolved","description":"For a sell that specifies an exact IMD output, beforeSwap returns a positive specified-currency delta equal to the fee quoted from the price-limited fill. The vendored v4 Hooks.beforeSwap then executes the checked addition `amountToSwap += hookDeltaSpecified` on the caller's int256 amountSpecified (lib/v4-core/src/libraries/Hooks.sol line 296). Any request with amountSpecified > type(int256).max - fee overflows and the whole swap reverts before Pool.swap runs, while the identical price-limited request against the same pool with hooks = address(0) partially fills. This deviates from the brief's 'the hook never reverts a swap' only for sentinel-style inputs (the window is [int256.max - fee, int256.max]); no hook storage influences it, no funds are at risk, and routers do not use int256.max as an unlimited-output sentinel in v4. It is disclosed in README.md 'Scope of guarantees' and docs/SECURITY_REVIEW.md R-01 and covered by a regression test. Three specialists reported the same root cause; merged into one finding at low. No code change is required if the requester accepts the documented limitation; the only in-hook alternative is to clamp the returned fee so amountSpecified + fee <= int256.max, which under-collects the fee on those absurd requests.","line":124,"path":"src/DroneHook.sol","reproduction":"State: pool initialized at 1:1 with 1e27 liquidity over ticks -600..600, hook attached (test/helpers/HookFixture.sol). Input: SwapParams with zeroForOne chosen so IMD is the output, amountSpecified = type(int256).max (exact output), sqrtPriceLimitX96 two ticks from the current price. Expected under a strict reading of the brief: partial fill to the limit and fee = floor(fill * feeNow() / 10_000). Actual: revert (arithmetic overflow in Hooks.beforeSwap). The same request with key.hooks = address(0) returns a positive IMD delta. Verified by running `forge test --match-test test_nativeExactOutputInt256MaxCanPartiallyFillButHookedRequestOverflows` (passes = reproduces the revert) and by a scratch probe showing amountSpecified = type(int256).max - 1e30 does not revert and accrues a fee, which locates the failing window at amountSpecified > int256.max - fee.","severity":"low","snippet":"        return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);","title":"Exact-output IMD requests within one hook fee of int256.max revert on the hooked pool where the bare pool partially fills (merged: audit_math, audit_flow, audit_permissions; documented as R-01)"},{"citation":"resolved","description":"Every fee formula (lines 112, 118, 120, 141) floors. At the standing 300 bps a swap of 33 wei IMD or less pays zero hook fee; at the opening 4000 bps, 2 wei or less. This matches the brief's 'never more' wording and is stated in the README ('a tiny swap may pay zero hook fee'). Splitting a trade into sub-threshold pieces costs orders of magnitude more gas than the fee avoided with an 18-decimal token, so there is no economic impact. Recorded so the floor is acknowledged as the accepted rounding direction; no change recommended.","line":112,"path":"src/DroneHook.sol","reproduction":"State: pool initialized, vm.warp(openedAt + 3600) so feeNow() == 300, liquidity present. Call: exact-input buy of 33 wei IMD (amountSpecified = -33). Expected under a round-up convention: 1 wei fee. Actual: reserve = 33 * 300 / 10_000 = 0, beforeSwap returns a zero specified delta, collected() unchanged. A 34 wei buy charges exactly 1 wei. Verified in a scratch test against HookFixture: _checkedSwap(true, true, 33, false) returned 0 and _checkedSwap(true, true, 34, false) returned 1.","severity":"info","snippet":"        uint256 reserve = exactIn ? budget * rate / 10_000 : budget * rate / (10_000 - rate);","title":"Hook fee rounds down, so swaps whose gross IMD is below 10_000/rate wei pay no hook fee (audit_math; documented design choice)"},{"citation":"resolved","description":"The single accepted pool key (IMD/DRONE, fee 12500, spacing 60, this hook) can be initialized by whoever reaches PoolManager.initialize first, at any sqrtPriceX96, and that call fixes openedAt and starts the 4000->300 bps decay. The hook cannot know the factory and the manifest's initialPrice is not a constructor argument, so it cannot restrict either without hardcoding. In the IMD launch flow the factory deploys the hook and initializes the pool in one transaction, which closes the window, and the required beforeInitialize permission blocks initialization before the hook has code (test_cannotInitializePredictedHookWithoutCode). The window exists only if the hook is deployed through the optional DeployDrone helper (or any separate transaction) and initialized later; README step 5 and SECURITY_REVIEW R-02 already say not to do this. No code change recommended; the deployer must keep the atomic sequence.","line":81,"path":"src/DroneHook.sol","reproduction":"Non-atomic sequence only. Tx1: DeployDrone.deploy(manager, IMD, DRONE, minedSalt) deploys the hook. Tx2 from 0xBAD at timestamp 50_000: manager.initialize({IMD/DRONE ordered, 12500, 60, hooks = fresh}, 3 * 2^96). Result: fresh.initialized() == true, fresh.openedAt() == 50_000, pool price 3x the intended 1:1. Tx3 (launch): manager.initialize(sameKey, 2^96) reverts (hook AlreadyInitialized / manager PoolAlreadyInitialized). At 50_000 + 3600 feeNow() == 300 before any launch liquidity exists. Verified in a scratch test (test_strangerInitializesWhenNotAtomic passed, i.e. the state is reachable).","severity":"info","snippet":"        openedAt = block.timestamp;","title":"beforeInitialize accepts any initializer and any starting price; only the atomic factory deploy+initialize keeps the opening price and 60-minute clock under the launch's control (audit_permissions; do"},{"citation":"resolved","description":"The brief's economics depend on this launch leaving DRONE with the requester: the later imdDRONES contract pays burn rewards 'out of the supply this launch leaves to the requester'. DroneToken mints the whole 10^27 supply to its deployer (the factory) and has no other path; the split is a launch-policy matter (poolBps/remainderTo), by design not a manifest field, and the tree expresses the intended 80/10/10 split only as prose in README step 2 and launch.json notes. If the policy row applied to this launch uses a default pool/network split with no requester remainder, the requester ends the launch holding zero DRONE and the reward contract can only be funded by buying DRONE back through the hooked pool. This is a configuration gap, not a code defect: no change to src/, script/ or launch.json can fix it. Evidence needed before admission: the launch request or policy row showing the approved pool/network/requester split and the requester address. The initialPrice 2^96 (1 IMD per DRONE) is likewise an assumption the notes ask the deployer to confirm.","line":22,"path":"src/DroneToken.sol","reproduction":"State: launch policy row for this launch uses a 9000/1000 pool/network split (or any split without a requester remainder). Deploy per manifest: DroneToken constructor executes balanceOf[factory] = 1e27; factory seeds 90% to the pool and 10% to the network. Expected per brief: requester holds 100,000,000e18 DRONE to fund imdDRONES. Actual: requester balanceOf == 0; grep of src/, script/ and launch.json shows no transfer, mint or allocation to any requester address. Check: compare the policy row / launch request used for admission against README step 2 (poolBps = 8000, remainderTo = requester) before freezing allocations.","severity":"info","snippet":"        balanceOf[msg.sender] = totalSupply;","title":"Requester DRONE allocation that funds imdDRONES rewards is not enforced by any delivered artifact; needs launch-request/policy evidence (audit_economics)"}],"hash":"6bcd8b9d1fe6c7c8df58fb3173832f7d940ec9f63e6b8d6c6bc012800180494b","nodeId":"97da9a53-20dd-4caf-af4b-66f6cf4a7145","outcome":"completed","summary":"The findings file is valid JSON with 4 findings and 12 coverage rows, every quoted snippet matches the tree, the scratch directory is gone, and the working tree shows no stray files.\n\n## Review outcome\n\n**No critical, high or medium defect was found.** The delivered DroneHook and DroneToken implement the brief correctly. The findings file at `.imd-findings.json` holds one low and three informational items, all reproduced against the code, with the specialists' three reports of the same overflow merged into one.\n\n**What I verified myself**\n\n- The full offline suite passes: 82 tests, 0 failures, across both IMD currency orderings, protocol-fee variants, partial fills, tick-range extremes, batch swaps, reentrancy, failed settlement, and stateful invariants.\n- The mainnet fork rehearsal passes at the current block against the real PoolManager and real IMD. I confirmed the IMD address on mainnet returns symbol IMD with 18 decimals, and the manager has code.\n- I traced the fee accounting by hand against the vendored Hooks and Pool libraries. The gross/net identity holds in all four swap modes, the partial-fill fee never exceeds the full-fill reserve, and the grossed-up exact-output amount never undershoots the quoted fill. The read-only quote library matches Pool.swap step for step, including the tick-bitmap walk, protocol-fee composition and liquidity transitions.\n- Every callback and the unlock callback are restricted to the PoolManager. The sweep guard, claim burn and take sequence pay only the constant keeper. Permission bits, manifest constructor order and the absence of DELEGATECALL in runtime and creation code all check out.\n\n**Findings kept**\n\n1. **Low.** An exact-output IMD request within one hook fee of `int256.max` reverts in the manager's checked addition, where the bare pool would partially fill. It is input-driven, disclosed as R-01, and covered by a regression test. Three specialists reported it; merged.\n2. **Info.** Fees floor, so swaps under 34 wei at 300 bps pay nothing. Documented design choice, verified 33 wei pays 0 and 34 wei pays 1.\n3. **Info.** Pool initialization is permissionless once the hook has code, so the launch must deploy and initialize atomically. Reproduced with a stranger fixing the price and clock in a non-atomic sequence.\n4. **Info.** The requester's retained DRONE allocation exists only in prose. The token mints everything to the factory, so the policy row must carry the 80/10/10 split before allocations are frozen.\n\n**Dropped or rejected leads.** All static-analysis lines were checked: the strict equalities and timestamp comparisons are intended, the sweep reentrancy warning is covered by the storage guard and the manager's unlock state, and the unsafe-ERC20 warning is wrong because the transfer goes through v4's reverting Currency library.","treeHash":null,"usage":{"cachedInputTokens":1754523,"inputTokens":388,"model":"claude-fable-5-1","outputTokens":36472,"runtime":"claude","turns":39,"wallClockMs":1244215}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"584bd638e7e022f0","findings":[{"citation":"resolved","description":"For a sell that specifies an exact IMD output, beforeSwap returns a positive specified-currency delta (the fee). The canonical PoolManager then performs the checked addition amountToSwap = params.amountSpecified + hookDeltaSpecified before the pool walk. When a router uses int256.max (or anything within fee of it) as an 'unlimited output' sentinel together with a price limit, the same request partially fills on an unhooked pool but overflows and reverts on the hooked pool. The hook itself does not revert; the revert is induced by the hook's return delta in the manager. This is a deviation from the brief's 'the hook never reverts a swap' guarantee, is caller-input driven rather than state driven, and is already disclosed in README.md and docs/SECURITY_REVIEW.md as R-01 with a regression test (test/SpecifiedAmount.t.sol: test_nativeExactOutputInt256MaxCanPartiallyFillButHookedRequestOverflows). Reported here so the judge has it with a concrete input; no fix is possible without charging the specified-side fee differently (for example only in afterSwap on the unspecified side, which changes the agreed accounting), so it is an accepted limitation to keep in the front-end/router guidance.","line":124,"path":"src/DroneHook.sol","reproduction":"State: initialized DRONE/IMD pool with liquidity (HookFixture: 1e27 liquidity over ticks -600..600), opening or standing fee. Input: a sell with zeroForOne chosen so IMD is the output, amountSpecified = type(int256).max (exact output), sqrtPriceLimitX96 one or two ticks from the current price. Expected (per brief): the swap partially fills to the limit and the hook takes feeNow() of the gross IMD output. Actual: PoolManager reverts with an arithmetic overflow in Hooks.beforeSwap (amountToSwap += hookDeltaSpecified) because fee > 0; the same request without the hook returns a positive IMD delta. Run: forge test --match-test test_nativeExactOutputInt256MaxCanPartiallyFillButHookedRequestOverflows.","severity":"low","snippet":"        return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);","title":"Exact-output IMD requests near int256.max revert only when the hook is attached (documented limitation R-01)"},{"citation":"resolved","description":"The one accepted pool key (IMD/DRONE, fee 12500, spacing 60, this hook) can be initialized by whoever reaches PoolManager.initialize first, at any sqrtPriceX96, and that call fixes openedAt and starts the 4000->300 bps decay. The hook has no way to know the factory, so it cannot restrict the initializer, and the manifest's initialPrice is not a constructor argument, so it cannot pin the price. In the IMD launch flow the factory deploys the hook and initializes the pool in one transaction, which closes the window; the required beforeInitialize permission already blocks initialization before the hook has code. The window only exists if someone deploys the hook through the optional DeployDrone helper (or any separate transaction) and initializes later. README.md step 5 and docs/SECURITY_REVIEW.md R-02 already say not to do this. Recorded as information so the deployer keeps the atomic sequence; no code change is recommended because any in-hook restriction would either hardcode a factory or the price.","line":67,"path":"src/DroneHook.sol","reproduction":"Non-atomic sequence only. Tx1: DeployDrone.deploy(manager, IMD, DRONE, salt) deploys the hook. Tx2 (attacker 0xBAD): manager.initialize(key, 10 * 2^96) with key = {IMD/DRONE ordered, fee 12500, tickSpacing 60, hooks = hook}. Result: hook.initialized() == true, hook.openedAt() == Tx2 timestamp, pool price = 10x the intended 1:1. Tx3 (launch): manager.initialize(key, 2^96) reverts (hook AlreadyInitialized / manager PoolAlreadyInitialized), and one hour after Tx2 feeNow() is already 300 bps before any launch liquidity exists. Reproduced in test/scratch/Probe.t.sol test_thirdPartyCanInitializeBeforeLaunchWhenNotAtomic (passes, i.e. the state is reachable). Expected under the brief: the launch controls opening price and clock; this holds only with the atomic factory path.","severity":"info","snippet":"    function beforeInitialize(address, PoolKey calldata key, uint160)","title":"beforeInitialize accepts any initializer and any starting price; only an atomic deploy+initialize keeps the opening price and 60-minute clock under the launch's control (documented R-02)"}],"hash":"7a3a5285246b630a323242bec3bc26bc87e888e35797d4157e68053ad6953f5f","nodeId":"be2461ec-577e-4c83-aac1-943e3c9956ff","outcome":"completed","summary":"The review is complete. `.imd-findings.json` is written in the repository root with two findings and a 12-row coverage record. No tracked file was changed.\n\n**Outcome: no access-control, trust-gap, or asymmetry defect of medium or higher severity.** The permission model is as small as the brief demands and every path I traced behaves as specified.\n\nWhat I verified in my area:\n\n- **Access control.** All four callbacks are gated to the PoolManager, and the manager can only reach beforeSwap and afterSwap for the single pool that beforeInitialize accepts. The unlock callback additionally requires the hook's own sweep flag. Nobody but the hook can burn or transfer its ERC-6909 fee claims, confirmed with a scratch test. Sweep pays only the constant keeper, which matches the brief's address byte for byte, and the caller receives nothing. No owner, setter, proxy, or DELEGATECALL exists in hook, token, or helper.\n- **Asymmetry.** Buy and sell formulas mirror each other across beforeSwap and afterSwap, both currency orderings pay in IMD only, and the rate is identical in both directions at every second of the curve, including the 3599 and 3600 second boundary.\n- **Trust gap.** The hook never transfers IMD during a swap, so a fresh manager holding no IMD cannot block trades. The mainnet IMD token is a LayerZero OFT with an owner but no pause or blocklist selectors and no proxy slot, so sweep cannot be frozen by it.\n- **Evidence run.** Existing suite 48/48, fuzz tests at 1500 runs, the protected hook and token floor tests against the real creation bytecode 10/10, the manifest and bytecode checkers, and the mainnet fork rehearsal against the real PoolManager and real IMD at block 26140906 (the pinned block's state is no longer served by the public RPC).\n\nFindings recorded, both already disclosed by the author:\n\n1. **Low.** An exact-output IMD request within the fee of int256.max partially fills unhooked but reverts hooked, because the manager's checked add of the hook's specified delta overflows. Concrete input and the repo's own regression test are cited.\n2. **Info.** beforeInitialize cannot restrict the initializer or the starting price, so the opening price and 60-minute clock are under launch control only on the atomic factory path. A scratch test shows the reachable state when the optional helper is used non-atomically.\n\nNot reached: nothing in the assigned entry points. The remaining IMD trust assumption, the owner's cross-chain mint authority, is outside this launch and is noted in the coverage record rather than as a finding.","treeHash":null,"usage":{"cachedInputTokens":1986986,"inputTokens":354,"model":"claude-fable-5-1","outputTokens":52641,"runtime":"claude","turns":43,"wallClockMs":747783}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"ef31844bb462de78","findings":[{"citation":"resolved","description":"The brief's economics depend on this launch leaving DRONE with the requester: the later imdDRONES contract pays burn rewards 'out of the supply this launch leaves to the requester'. The delivered tree expresses that only as prose in launch.json notes and README step 2 (poolBps=8000, remainderTo=requester, 100,000,000 DRONE retained). Notes are explanatory text, not deployment authority, and supply/split fields are by design not manifest fields; the split comes from the launch policy row and frozen allocations. DroneToken mints the whole 1e27 supply to the factory and has no other path, so if the policy applies its default pool/network split with no requester remainder, the requester ends the launch holding zero DRONE and the reward contract cannot be funded without buying DRONE back through the hooked pool (paying the 1.25% LP fee plus 3% to 40% hook fee). This is a configuration gap, not a code defect: no change to the contracts fixes it. Evidence needed before admission: the launch request or policy row showing the approved pool/network/requester split and the requester address, since neither is in the repository. Related assumption also only in notes: initialPrice 79228162514264337593543950336 is 1 IMD per DRONE; with IMD total supply about 4.1e24 (read on mainnet 2026-10-07) an 800,000,000 DRONE pool at that price implies a DRONE valuation far above IMD's whole supply, so the price needs the same deployment-time confirmation the notes request.","line":30,"path":"launch.json","reproduction":"State: launch policy row for this launch uses a 9000/1000 pool/network split (or any split with no requester remainder) and no remainderTo. Deploy per manifest: DroneToken constructor mints 1,000,000,000e18 to the factory; factory seeds 90% to the pool and 10% to the network. Expected per brief: requester holds 100,000,000e18 DRONE to fund imdDRONES. Actual: requester balanceOf == 0; nothing in src/, script/ or launch.json can change that. Check: compare the policy row / launch request used for admission against README step 2 (poolBps=8000, remainderTo=requester) before freezing allocations.","severity":"info","snippet":"  \"notes\": \"Ethereum mainnet. Direct CREATE2 DroneHook; permission mask 8396. Static 1.25% LP fee plus IMD-only hook fee; 4000 to 300 bps over 3600 seconds. Initial sqrtPriceX96 assumes 1:1 minor units. Configure the launch request with poolBps=8000 and remainderTo=requester, assuming 1000 bps network allocation, leaving 1000 bps (100 million DRONE) with the requester for the separately deployed imdDRONES reward contract. No NFT integration or token mint authority. Requester address, allocation and economic opening price require deployment-time confirmation. See README.md and docs/SECURITY_REVIEW.md for accounting, verification and release responsibilities.\"","title":"Requester DRONE allocation that funds imdDRONES rewards is not enforced by any delivered artifact; needs launch-request/policy evidence"}],"hash":"815c1b192fa89501250ee133ca9a0e3a4d89e0109eda6ed9ebb24b547ec8691e","nodeId":"7ba936a3-fe2f-4f3a-b662-06ba92a6294f","outcome":"completed","summary":"Review complete. The findings file is written at the repository root, no tracked file was changed, and the scratch tests were removed.\n\n**Outcome: no code defect found in the economic area.** The hook's fee mechanics, custody and invariants held against every failure mode in the Economic Security, Invariant and Flow Gap guides. One informational configuration gap is recorded.\n\n**What I verified**\n\n- **Fee exactness.** The hook charges exactly floor(gross IMD × rate / 10000) in all four swap modes, with IMD on either side, for full and price-limited fills. I proved the quoted fill always equals the executed fill (the adjusted amount never undershoots the quote), so the beforeSwap path cannot under- or over-charge. The delivered hooked tests never enable PoolManager protocol fees, so I fuzzed those too (up to 1000 pips each direction, 256 runs, plus fixed partial-fill cases): still exact. That matters because mainnet's protocol fee controller is set.\n- **Custody.** Fees accrue as ERC-6909 claims, so no transfer happens mid-swap and a fresh manager with DRONE-only liquidity works. Sweep by any caller, repeated, reentrant, or with donated claims or loose IMD, pays only the constant keeper, which is an EOA on mainnet. The claim burn's int128 bound is unreachable because IMD's whole supply is about 4.1e24.\n- **No hook-induced reverts.** The only writes are a counter and a bounded claim mint. The quote library mirrors the v4 swap loop line for line.\n- **Mainnet fork.** The pinned block is now outside the public node's archive window, so I re-ran the rehearsal logic at the latest block against the real PoolManager and real IMD. All modes, limited fills and keeper payout passed.\n- **Gas griefing.** The read-only tick walk adds about 6.5k gas per crossed tick (21% on a 300-tick swap). It scales with the pool's own cost and cannot revert, so it stays a note.\n\n**The one finding (info).** The brief's economics depend on the launch leaving DRONE with the requester to fund imdDRONES rewards. That split exists only in the manifest notes and README. If the policy row applies a default split with no requester remainder, the requester ends with zero DRONE. Nothing in the tree can enforce it, so the judge needs the policy row or launch request as evidence before allocations freeze. The 1:1 initial price is flagged in the same note as a confirmation item.\n\n**Not reached.** IMD's own admin surface (it exposes an owner) was not audited. A blocklist on the hook or keeper would strand swept fees but not swaps. That sits outside this launch's control.\n\nCoverage rows answer all eight entry points plus eight invariant and economic rows.","treeHash":null,"usage":{"cachedInputTokens":2286603,"inputTokens":514,"model":"claude-fable-5-1","outputTokens":64777,"runtime":"claude","turns":51,"wallClockMs":1002873}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"e06554fd2816f9d7","findings":[{"citation":"resolved","description":"Every fee formula in the hook (lines 112, 118, 120, 141) floors. The Math Precision guide's convention is that fees round up; here a swap whose gross IMD amount is below 10_000/rate wei pays no hook fee at all. At the standing 300 bps that is any swap of 33 wei IMD or less; at the opening 4000 bps it is 2 wei or less. This is consistent with the brief's 'never more' wording and is stated in the README ('a tiny swap may pay zero hook fee'). Splitting a trade into sub-threshold pieces costs far more gas than the fee avoided with an 18-decimal token, so there is no economic impact; recorded only so the gross-floor definition is acknowledged as the accepted rounding direction. No change recommended unless the requester wants fee rounding up (replace the floor with a round-up division; the gross/net identity floor((P+F)*r/D)==F then needs re-verification).","line":112,"path":"src/DroneHook.sol","reproduction":"State: pool initialized, elapsed >= 3600 s so feeNow()==300, liquidity present. Call: exact-input buy of 33 wei IMD (SwapParams zeroForOne = IMD is currency0, amountSpecified = -33). Expected under a round-up convention: 1 wei fee. Actual: beforeSwap computes reserve = 33*300/10_000 = 0, returns a zero specified delta, collected() unchanged. Verified in a scratch test: 33 wei -> charged 0; 34 wei -> charged 1 (34*300/10_000 = 1).","severity":"info","snippet":"        uint256 reserve = exactIn ? budget * rate / 10_000 : budget * rate / (10_000 - rate);","title":"Hook fee rounds down, so sub-threshold swaps pay zero hook fee (documented design choice)"},{"citation":"resolved","description":"For a sell with exact IMD output the hook returns a positive specified delta `fee`. Canonical v4 then executes `amountToSwap += hookDeltaSpecified` with checked arithmetic in Hooks.beforeSwap, so any request A with A + fee > type(int256).max reverts before the swap runs, although the same price-limited request on a pool without this hook partially fills. The window is only [int256.max - fee, int256.max], i.e. sentinel-style 'unlimited output' inputs; any fill above int128.max would revert in the manager's toInt128 anyway, so no economically meaningful request is affected. The repository documents this (README 'Scope of guarantees', SECURITY_REVIEW R-01) and tests it (test/SpecifiedAmount.t.sol). The only alternative that keeps 'never reverts' is to clamp the returned delta to int256.max - A for those inputs, which undercharges the fee on them; that trades acceptance criterion (1) for (3) on absurd inputs, so the author's choice is reasonable. Recorded for completeness of the boundary review; routers must not use int256.max as an unlimited-output sentinel against this pool.","line":124,"path":"src/DroneHook.sol","reproduction":"State: initialized pool with liquidity. Call: sell with exact IMD output, amountSpecified = type(int256).max, sqrtPriceLimitX96 one tick past the current price. Expected (per 'the hook never reverts a swap'): partial fill up to the limit, fee = floor(fill*rate/10_000). Actual: beforeSwap computes a non-zero fee from the quoted partial fill and returns it; Hooks.beforeSwap's `amountToSwap += hookDeltaSpecified` overflows and the transaction reverts (panic 0x11). Confirmed by the repository test test_nativeExactOutputInt256MaxCanPartiallyFillButHookedRequestOverflows, and in a scratch probe that type(int256).max - 1e30 does NOT revert (fee < 1e30), locating the failing window at A > int256.max - fee.","severity":"info","snippet":"        return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);","title":"Exact-output IMD requests within one hook fee of int256.max revert in the manager's checked addition, where the unhooked pool would partially fill (already documented as R-01)"}],"hash":"938fb1cce22b74901c8db36a49fb4531fec73fba67254d59492bc8826f5cba9f","nodeId":"9f6ef268-4145-41fe-bbbe-f5a6ba668b92","outcome":"completed","summary":"The review is complete. The findings file is written and validated, the scratch probes are removed, and the only working-tree change is `.imd-findings.json`.\n\n**Result: no medium or higher defect in the math.** The hook's fee arithmetic holds under every state I could reach. Two informational notes are recorded, both already acknowledged by the author.\n\n**What I verified**\n\n- **Fee identity.** All four swap modes charge exactly `floor(gross IMD × rate / 10000)`. I proved the gross-up lemma the exact-output and unspecified-input paths rely on: if F = floor(P·r/(D−r)) then floor((P+F)·r/D) = F, so the fee is never one wei high or low.\n- **Quote versus execution.** The read-only fill quote cannot diverge from the real swap. For exact-input, the pool's budget after the fee is never below the quoted fill. For exact-output, the grossed-up request is never below the quoted fill. Either the pool hits the same price limit or fills exactly the quoted amount.\n- **Library mirror.** `SpecifiedAmount.filled` matches the vendored `Pool.swap` loop step for step, including bitmap masks, tick clamps, unchecked remaining updates, liquidity-net crossing and the directional protocol fee. Its checked int24 arithmetic cannot overflow at tick spacing 60.\n- **Casts and bounds.** Every int128 cast is bounded by about 0.67 × int128.max. No hook state can make a swap revert.\n- **Schedule.** `feeNow()` is 4000 at open, 301 at 3599 seconds, 300 from 3600 on, monotone, no overflow.\n- **Extra probing.** A scratch fuzz of 3000 runs with protocol fees on, scattered and gapped liquidity, limits landing on initialized ticks, an extreme price region near tick −880000, and requests between the int128 and int256 ceilings all passed. The repository's own fuzz and invariant suites passed at 3000 and 256 runs.\n- **Mainnet.** The fork suite passed at the pinned block via an archive RPC, and the IMD address returns symbol IMD with 18 decimals.\n\n**Informational findings reported**\n\n1. Fees floor, so swaps of 33 wei or less at 300 bps pay nothing. Dust-level, documented in the README.\n2. An exact-output IMD request within one fee of int256.max reverts in the manager's checked addition. The window only covers sentinel inputs, is documented as R-01, and fixing it would undercharge instead.\n\nCoverage rows are filed for all eight ABI entry points plus four invariants, all `holds`.","treeHash":null,"usage":{"cachedInputTokens":2123208,"inputTokens":482,"model":"claude-fable-5-1","outputTokens":54694,"runtime":"claude","turns":47,"wallClockMs":974298}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"03845cacb54c3a73","findings":[{"citation":"resolved","description":"For a sell that specifies IMD output (specified currency = IMD, exact-output), beforeSwap returns a positive specified-currency delta `fee`. The vendored v4 Hooks.beforeSwap then executes the checked addition `amountToSwap += hookDeltaSpecified` on the caller's int256 amountSpecified. When amountSpecified > type(int256).max - fee the addition overflows and the swap reverts before Pool.swap runs, whereas the same request on an unhooked pool partially fills to the price limit. This is an input-domain deviation from the brief's 'the hook never reverts a swap' wording, not a hook-state condition: no hook storage (initialized, openedAt, collected, sweeping) influences it, no funds are at risk, and routers do not use int256.max as an unlimited-output sentinel in v4. The repository already documents it (README 'Scope of guarantees', docs/SECURITY_REVIEW.md R-01) and has a regression test. Recorded here so the judge has the cross-reference; no code change is required unless the requester wants a strict reading of the no-revert guarantee, in which case the hook could cap the returned fee so that amountSpecified + fee <= type(int256).max (the fee would then be under-collected only for such absurd requests).","line":124,"path":"src/DroneHook.sol","reproduction":"Pool at 1:1 with liquidity, hook attached. Call PoolManager.swap with zeroForOne chosen so IMD is the output, amountSpecified = type(int256).max (exact output), sqrtPriceLimitX96 two ticks away. Expected under a strict 'never reverts' reading: partial fill to the limit and the scheduled fee on the filled IMD. Actual: revert (checked arithmetic overflow in Hooks.beforeSwap `amountToSwap += hookDeltaSpecified`). The same request against the same pool with hooks = address(0) succeeds with a positive partial fill. Existing reproduction: test/SpecifiedAmount.t.sol test_nativeExactOutputInt256MaxCanPartiallyFillButHookedRequestOverflows (passes = reproduces the revert).","severity":"info","snippet":"        return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);","title":"Exact-output IMD requests within one hook fee of int256.max revert in the manager where the bare pool would partially fill (already disclosed as R-01)"}],"hash":"d824be5d0256410b581303b6b8fdeb47d88ee32421d5b7b403c2757069b326ec","nodeId":"ef71791c-176d-4c21-8de6-d6cc88ef992e","outcome":"completed","summary":"Review complete. The findings file is written and validated, and the scratch probes are removed.\n\n**Outcome.** I found no exploitable defect in my area (Execution Trace, Periphery, First Principles). The file `.imd-findings.json` holds one info-level entry and twelve coverage rows, all `holds`.\n\n**What I verified**\n\n- **Fee path end to end.** I traced beforeSwap and afterSwap against the vendored v4 Hooks and PoolManager code. The orientation logic, the gross-up identity, the int128 fits, and the mint-then-credit netting are all correct. The hook cannot trigger HookDeltaExceedsSwapAmount because the fee is always below the budget.\n- **SpecifiedAmount library.** Compared line by line with Pool.swap, SwapMath, TickBitmap and the protocol-fee library. The one textual difference in the tick-recompute condition is unreachable.\n- **Differential fuzzing beyond the shipped suite.** 2000 runs with asymmetric positions, protocol fees in both directions, shifted prices and limits on tick boundaries, plus 1500 runs of wei-sized swaps at deep liquidity, in both currency orderings. Every run charged exactly the scheduled fee on actual gross IMD and never touched the DRONE delta.\n- **Sweep and callbacks.** Keeper is a constant, reentrant and in-unlock sweeps are harmless no-ops, and the unlock callback is reachable only through the hook's own sweep. The slither and aderyn reentrancy and unsafe-ERC20 leads are dismissed with reasons in the coverage rows.\n- **Manifest and helpers.** Constructor order, permissions, fee tier and tick spacing match the code. The mainnet IMD address has code, symbol IMD and 18 decimals. The repo's manifest and bytecode checkers pass.\n- **Mainnet fork.** The documented block is no longer served by the public RPC, so I ran a scratch copy of the fork suite at block 26140915. Both tests passed against the real PoolManager and real IMD.\n\n**The one recorded item.** Exact-output IMD requests within one fee of int256.max revert in the manager's checked addition where a bare pool would partially fill. It is input-domain only, carries no fund risk, and is already disclosed as R-01 with a regression test. I recorded it as info so the judge has the cross-reference.\n\n**Not reached.** Nothing in the listed entry points. The fork rehearsal at the exact documented block could not be reproduced for lack of archive state, which is noted in its coverage row.","treeHash":null,"usage":{"cachedInputTokens":1981636,"inputTokens":418,"model":"claude-fable-5-1","outputTokens":53374,"runtime":"claude","turns":43,"wallClockMs":1086207}},{"artifacts":[],"attempt":2,"bundleHash":"0c6ffd645d3e9fd49f939c61ed3f02543ec4cfc16c4b81cb7ef5ced33e7a9321","device":"20a3efdc039f2089","findings":[],"hash":"fc2e4808e191f70ffc4c4d65d5571506ef316291c58d0b0403412f3949e0ddce","nodeId":"395fcc4d-43f7-4174-a28b-593f2e74ec82","outcome":"completed","summary":"Implemented DRONE, DroneHook, CREATE2 tooling, tests, and the corrected `univ4_hook` manifest.\n\nPassed:\n- `forge build`, `forge test`, `forge fmt --check`\n- 48 local tests, 10 protected checks, 2 mainnet-fork tests\n\nDeployment assumptions and limitations are documented in README.md; the separate AI review is in docs/SECURITY_REVIEW.md.\n\nNo mainnet transactions were broadcast. Final price/allocation confirmation and independent network review remain release steps.","treeHash":"59bfe45cd9db55c49410ebf693d0ed037e04db37","usage":{"cachedInputTokens":2753024,"inputTokens":128926,"model":"gpt-6-astra","outputTokens":37757,"runtime":"codex","turns":8,"wallClockMs":1565986}}],"verification":[{"checks":[{"durationMs":117184,"exitCode":0,"name":"build","output":"Compiling 83 files with Solc 0.8.26\nSolc 0.8.26 finished in 116.98s\nCompiler run successful!\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/SpecifiedAmount.sol:58:51\n   │\n58 │                 remaining = exactIn ? remaining + int256(input + lpAmount) : remaining - int256(output);\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/SpecifiedAmount.sol:58:90\n   │\n58 │                 remaining = exactIn ? remaining + int256(input + lpAmount) : remaining - int256(output);\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/SpecifiedAmount.sol:72:19\n   │\n72 │                 ? uint256(remaining - 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/SpecifiedAmount.sol:73:19\n   │\n73 │                 : uint256(params.amountSpecified - remaining);\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/DroneHook.sol:87:29\n   │\n87 │         if (!initialized || block.timestamp <= openedAt) return OPENING_FEE;\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/DroneHook.sol:89:13\n   │\n89 │         if (elapsed >= OPENING_SECONDS) return KEEPER_FEE;\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DroneHook.sol:106:35\n    │\n106 │             requested = exactIn ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DroneHook.sol:106:70\n    │\n106 │             requested = exactIn ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DroneHook.sol:111:25\n    │\n111 │             requested > uint256(uint128(type(int128).max)) ? uint256(uint128(type(int128).max)) : requested;\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/DroneHook.sol:111:33\n    │\n111 │             requested > uint256(uint128(type(int128).max)) ? uint256(uint128(type(int128).max)) : requested;\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/DroneHook.sol:111:62\n    │\n111 │             requested > uint256(uint128(type(int128).max)) ? uint256(uint128(type(int128).max)) : requested;\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/DroneHook.sol:111:70\n    │\n111 │             requested > uint256(uint128(type(int128).max)) ? uint256(uint128(type(int128).max)) : requested;\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/DroneHook.sol:114:44\n    │\n114 │         quote.amountSpecified = exactIn ? -int256(budget - reserve) : int256(budget + reserve);\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/DroneHook.sol:114:71\n    │\n114 │         quote.amountSpecified = exactIn ? -int256(budget - reserve) : int256(budget + reserve);\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/DroneHook.sol:124:63\n    │\n124 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\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/DroneHook.sol:124:70\n    │\n124 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\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/DroneHook.sol:141:36\n    │\n141 │         uint256 fee = amount < 0 ? uint256(-amount) * rate / (10_000 - rate) : uint256(amount) * rate / 10_000;\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/DroneHook.sol:141:80\n    │\n141 │         uint256 fee = amount < 0 ? uint256(-amount) * rate / (10_000 - rate) : uint256(amount) * rate / 10_000;\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/DroneHook.sol:143:44\n    │\n143 │         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/DroneHook.sol:143:51\n    │\n143 │         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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DroneHook.sol:150:9\n    │\n150 │         emit FeeAccrued(fee);\n    │         ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `sweeping` is updated\n    ╭▸ src/DroneHook.sol:162:29\n    │\n162 │         amount = abi.decode(poolManager.unlock(\"\"), (uint256));\n    │                             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DroneHook.sol:167:9\n    │\n167 │         emit Swept(amount);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/DroneHookAdversarial.t.sol:141:45\n    │\n141 │             charged, gross * _scheduledRate(block.timestamp) / 10_000, \"exact scheduled fee at the extreme\"\n    │                                             ━━━━━━━━━━━━━━━\n    ‡\n162 │         vm.warp(10_000 + 1234);\n    │         ────────────────────── `vm.warp` changes this environment here\n    │\n    ├ help: capture it with `vm.getBlockTimestamp()` instead\n    ╰ help: https://getfoundry.sh/forge/linting/environment-read-across-mutation\n\nwarning[environment-read-across-mutation]: `block.timestamp` may be reused across `vm.warp`\n    ╭▸ test/DroneHookAdversarial.t.sol:141:45\n    │\n141 │             charged, gross * _scheduledRate(block.timestamp) / 10_000, \"exact scheduled fee at the extreme\"\n    │                                             ━━━━━━━━━━━━━━━\n    ‡\n171 │         vm.warp(10_000 + 4000);\n    │         ────────────────────── `vm.warp` changes 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":1939,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 595.12µs (0.00ns CPU time)\n\nRan 1 test for test/MainnetForkRecent.t.sol:MainnetForkRecentTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.38ms (0.00ns CPU time)\n\nRan 5 tests for test/DroneToken.t.sol:DroneTokenTest\n[PASS] testFuzz_ordinaryTransfersAndAllowances(uint96) (runs: 256, μ: 185722, ~: 186144)\nLogs:\n  Bound result 505513266363752282279\n\n[PASS] test_failurePaths() (gas: 104110)\n[PASS] test_fixedSupplyAndMetadata() (gas: 37510)\n[PASS] test_noAdminOrMintEvenForDeployer() (gas: 361287)\n[PASS] test_zeroSelfTransfersAndInfiniteAllowance() (gas: 178878)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 7.64ms (7.83ms CPU time)\n\nRan 20 tests for test/DroneHook.t.sol:DroneHookTest\n[PASS] testFuzz_crossTicksAndEmptyLiquidity(bool,bool,uint96,uint16) (runs: 256, μ: 859305, ~: 853891)\nLogs:\n  Bound result 18\n  Bound result 899000000000000000000000721\n\n[PASS] testFuzz_feeAndPartialFills(bool,bool,uint96,uint32,bool) (runs: 256, μ: 361654, ~: 363807)\nLogs:\n  Bound result 6280\n  Bound result 19516681861673905\n\n[PASS] test_allModesAtOpeningMidpointAndAfterHour() (gas: 5995323)\n[PASS] test_callbacksOnlyManager() (gas: 150063)\n[PASS] test_cannotInitializePredictedHookWithoutCode() (gas: 45784)\n[PASS] test_constructorRejectsZeroOrSameCurrencies() (gas: 8425)\n[PASS] test_failedSweepRollsBackAndDoesNotPoisonTrading() (gas: 920917)\n[PASS] test_freshManagerWithDroneOnlyLiquidity() (gas: 30998724)\n[PASS] test_initializationRejectsWrongPairFeeAndSpacing() (gas: 6159570)\n[PASS] test_invalidSaltRefused() (gas: 69849)\n[PASS] test_noLiquidityCollectsNoFee() (gas: 1083365)\n[PASS] test_openingCannotBeResetOrReusedForAnotherPool() (gas: 63908)\n[PASS] test_partialFillsAllModes() (gas: 2527369)\n[PASS] test_permissionBitsAndNoEscapeOpcodes() (gas: 4446012)\n[PASS] test_reentrancyFromSwapTransferDefersSweep() (gas: 874896)\n[PASS] test_reentrantSweepCannotRedirectOrDoublePay() (gas: 556827)\n[PASS] test_scheduleEndpointsAndEverySecond() (gas: 49000935)\n[PASS] test_sweepAnyoneRepeatedAndDirectDonations() (gas: 1294671)\n[PASS] test_sweepDuringUnlockDefersWithoutReverting() (gas: 543577)\n[PASS] test_tinyAmountsAndHugePartialRequests() (gas: 7793471)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 170.49ms (334.60ms CPU time)\n\nRan 2 tests for test/SpecifiedAmount.t.sol:IndependentQuoteReview\n[PASS] testFuzz_independentSpecifiedFillMatchesManager(bool,bool,uint96,uint16,uint16,uint16) (runs: 256, μ: 263075, ~: 248613)\nLogs:\n  Bound result 15\n  Bound result 1\n  Bound result 31051356666719719\n  Bound result 23\n\n[PASS] test_nativeExactOutputInt256MaxCanPartiallyFillButHookedRequestOverflows() (gas: 308710)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 171.04ms (139.98ms CPU time)\n\nRan 20 tests for test/DroneHook.t.sol:DroneHookIMDSecondTest\n[PASS] testFuzz_crossTicksAndEmptyLiquidity(bool,bool,uint96,uint16) (runs: 256, μ: 859831, ~: 856081)\nLogs:\n  Bound result 18\n  Bound result 899000000000000000000000721\n\n[PASS] testFuzz_feeAndPartialFills(bool,bool,uint96,uint32,bool) (runs: 256, μ: 361029, ~: 363975)\nLogs:\n  Bound result 4345\n  Bound result 10800\n\n[PASS] test_allModesAtOpeningMidpointAndAfterHour() (gas: 5998393)\n[PASS] test_callbacksOnlyManager() (gas: 150778)\n[PASS] test_cannotInitializePredictedHookWithoutCode() (gas: 46012)\n[PASS] test_constructorRejectsZeroOrSameCurrencies() (gas: 8425)\n[PASS] test_failedSweepRollsBackAndDoesNotPoisonTrading() (gas: 908237)\n[PASS] test_freshManagerWithDroneOnlyLiquidity() (gas: 15112051)\n[PASS] test_initializationRejectsWrongPairFeeAndSpacing() (gas: 9450226)\n[PASS] test_invalidSaltRefused() (gas: 70077)\n[PASS] test_noLiquidityCollectsNoFee() (gas: 1074905)\n[PASS] test_openingCannotBeResetOrReusedForAnotherPool() (gas: 64136)\n[PASS] test_partialFillsAllModes() (gas: 2521347)\n[PASS] test_permissionBitsAndNoEscapeOpcodes() (gas: 4446012)\n[PASS] test_reentrancyFromSwapTransferDefersSweep() (gas: 865030)\n[PASS] test_reentrantSweepCannotRedirectOrDoublePay() (gas: 546274)\n[PASS] test_scheduleEndpointsAndEverySecond() (gas: 49000935)\n[PASS] test_sweepAnyoneRepeatedAndDirectDonations() (gas: 1285903)\n[PASS] test_sweepDuringUnlockDefersWithoutReverting() (gas: 533252)\n[PASS] test_tinyAmountsAndHugePartialRequests() (gas: 7761860)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 187.77ms (383.91ms CPU time)\n\nRan 16 tests for test/DroneHookAdversarial.t.sol:DroneHookAdversarialIMDSecondTest\n[PASS] testFuzz_extremeLimitsNeverLeaveTheFeeWrong(bool,bool,uint96,uint16) (runs: 256, μ: 853713, ~: 823035)\nLogs:\n  Bound result 5\n  Bound result 457948\n\n[PASS] testFuzz_feeExactWithProtocolFeeOnHookedPool(bool,bool,uint96,uint32,bool,uint16,uint16) (runs: 256, μ: 880263, ~: 883057)\nLogs:\n  Bound result 0\n  Bound result 12\n  Bound result 6\n  Bound result 475\n\n[PASS] testFuzz_feeIndependentOfCallerRouterAndHookData(bool,bool,uint96,uint32,bytes) (runs: 256, μ: 1343724, ~: 1351274)\nLogs:\n  Bound result 0\n  Bound result 2286623813\n\n[PASS] testFuzz_scheduleIsMonotoneBoundedAndFlatAfterOneHour(uint32,uint32) (runs: 256, μ: 46475, ~: 46445)\nLogs:\n  Bound result 10966\n  Bound result 3\n\n[PASS] test_batchOfSwapsInOneUnlockChargesEachExactly() (gas: 463198)\n[PASS] test_constructorRefusesAnAddressWithoutTheFlags() (gas: 12070)\n[PASS] test_creationCodeHasNoDelegatecallEither() (gas: 3926700)\n[PASS] test_deployHelperIsNotAWrapperAndHoldsNothing() (gas: 640446)\n[PASS] test_exactOutputSellWithPoolTooShallowStopsAtLimitNotRevert() (gas: 2531234)\n[PASS] test_failedSwapsLeaveNoFeeAccounting() (gas: 1908571)\n[PASS] test_liquidityChangesNeverAccrueFees() (gas: 895434)\n[PASS] test_minedAddressCarriesExactlyTheManifestMask() (gas: 10255)\n[PASS] test_protocolFeeMaximumBothDirectionsAllModes() (gas: 5736726)\n[PASS] test_sweepInsideAnotherUnlockDefersAndCallbacksRefuseIt() (gas: 888585)\n[PASS] test_sweepManyCallersManyRoundsOnlyKeeperGains() (gas: 3156382)\n[PASS] test_walkToMaxAndMinPriceInBothDirections() (gas: 5194106)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 192.85ms (316.17ms CPU time)\n\nRan 16 tests for test/DroneHookAdversarial.t.sol:DroneHookAdversarialTest\n[PASS] testFuzz_extremeLimitsNeverLeaveTheFeeWrong(bool,bool,uint96,uint16) (runs: 300, μ: 862415, ~: 823621)\nLogs:\n  Bound result 2811\n  Bound result 3\n\n[PASS] testFuzz_feeExactWithProtocolFeeOnHookedPool(bool,bool,uint96,uint32,bool,uint16,uint16) (runs: 600, μ: 879193, ~: 883061)\nLogs:\n  Bound result 368\n  Bound result 675\n  Bound result 314\n  Bound result 16028\n\n[PASS] testFuzz_feeIndependentOfCallerRouterAndHookData(bool,bool,uint96,uint32,bytes) (runs: 300, μ: 1340126, ~: 1345072)\nLogs:\n  Bound result 1\n  Bound result 111360844\n\n[PASS] testFuzz_scheduleIsMonotoneBoundedAndFlatAfterOneHour(uint32,uint32) (runs: 1000, μ: 46463, ~: 46445)\nLogs:\n  Bound result 62\n  Bound result 7446\n\n[PASS] test_batchOfSwapsInOneUnlockChargesEachExactly() (gas: 464214)\n[PASS] test_constructorRefusesAnAddressWithoutTheFlags() (gas: 12070)\n[PASS] test_creationCodeHasNoDelegatecallEither() (gas: 3926700)\n[PASS] test_deployHelperIsNotAWrapperAndHoldsNothing() (gas: 650992)\n[PASS] test_exactOutputSellWithPoolTooShallowStopsAtLimitNotRevert() (gas: 2516947)\n[PASS] test_failedSwapsLeaveNoFeeAccounting() (gas: 1886431)\n[PASS] test_liquidityChangesNeverAccrueFees() (gas: 905296)\n[PASS] test_minedAddressCarriesExactlyTheManifestMask() (gas: 10255)\n[PASS] test_protocolFeeMaximumBothDirectionsAllModes() (gas: 5726682)\n[PASS] test_sweepInsideAnotherUnlockDefersAndCallbacksRefuseIt() (gas: 897355)\n[PASS] test_sweepManyCallersManyRoundsOnlyKeeperGains() (gas: 3152810)\n[PASS] test_walkToMaxAndMinPriceInBothDirections() (gas: 5185477)\nSuite result: ok. 16 passed; 0 failed; 0 skipped; finished in 192.91ms (508.43ms CPU time)\n\nRan 1 test for test/DroneInvariant.t.sol:DroneInvariantTest\n[PASS]\nDroneInvariantTest invariants:\n[PASS] invariant_allCollectedIMDEitherPendingOrPaidOnlyToKeeper\n[PASS] invariant_tokenSupplyAndOpeningCannotChange\n DroneInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| DroneHandler | passTime | 700   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| DroneHandler | sweep    | 637   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| DroneHandler | trade    | 711   | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 909\n  Bound result 394\n  Bound result 470512093\n  Bound result 634\n  Bound result 3056\n  Bound result 985253656262010797\n  Bound result 46\n  Bound result 2\n  Bound result 3280739443\n  Bound result 1675\n  Bound result 1651952575\n  Bound result 3746\n  Bound result 501\n  Bound result 594\n  Bound result 4\n  Bound result 512\n  Bound result 255\n  Bound result 302\n  Bound result 506\n  Bound result 56\n  Bound result 295\n  Bound result 83\n  Bound result 127\n  Bound result 1678\n  Bound result 372\n  Bound result 5496\n  Bound result 1861\n  Bound result 25158400798\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 432.78ms (404.21ms CPU time)\n\nRan 1 test for test/DroneHookInvariants.t.sol:DroneHookInvariantsIMDSecondTest\n[PASS]\nDroneHookInvariantsIMDSecondTest invariants:\n[PASS] invariant_collectedIsExactlyTheSumOfScheduledFees\n[PASS] invariant_managerCanAlwaysHonourTheHooksClaims\n[PASS] invariant_noIMDLeaksToAnyoneButTheKeeper\n[PASS] invariant_scheduleAndPoolFeeAreFrozen\n[PASS] invariant_whatTheHookHoldsPlusWhatTheKeeperGotIsEverythingEverTaken\n DroneHookInvariantsIMDSecondTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭------------------------+-----------------+-------+---------+----------╮\n| Contract               | Selector        | Calls | Reverts | Discards |\n+=======================================================================+\n| DroneMultiActorHandler | addLiquidity    | 258   | 0       | 0        |\n|------------------------+-----------------+-------+---------+----------|\n| DroneMultiActorHandler | donate          | 300   | 0       | 0        |\n|------------------------+-----------------+-------+---------+----------|\n| DroneMultiActorHandler | passTime        | 256   | 0       | 0        |\n|------------------------+-----------------+-------+---------+----------|\n| DroneMultiActorHandler | removeLiquidity | 296   | 0       | 0        |\n|------------------------+-----------------+-------+---------+----------|\n| DroneMultiActorHandler | setProtocolFee  | 309   | 0       | 0        |\n|------------------------+-----------------+-------+---------+----------|\n| DroneMultiActorHandler | sweep           | 295   | 0       | 0        |\n|------------------------+-----------------+-------+---------+----------|\n| DroneMultiActorHandler | trade           | 334   | 0       | 0        |\n╰------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 4503599627370496\n  Bound result 999999000000000000016380\n  Bound result 11\n  Bound result 2\n  Bound result 4584\n  Bound result 469\n  Bound result 5\n  Bound result 13087\n  Bound result 911758331207199776782\n  Bound result 1931\n  Bound result 14439\n  Bound result 300\n  Bound result 813\n  Bound result 7050\n  Bound result 114349986\n  Bound result 88\n  Bound result 4\n  Bound result 413\n  Bound result 190\n  Bound result 807\n  Bound result 999999000000000000011085\n  Bound result 351\n  Bound result 223\n  Bound result 5\n  Bound result 255\n  Bound result 95\n  Bound result 165\n  Bound result 815\n  Bound result 470\n  Bound result 496711331825316925193\n  Bound result 9270\n  Bound result 484\n  Bound result 700\n  Bound result 16440\n  Bound result 1747\n  Bound result 639\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 791.90ms (742.11ms CPU time)\n\nRan 1 test for test/DroneHookInvariants.t.sol:DroneHookInvariantsTest\n[PASS]\nDroneHookInvariantsTest invariants:\n[PASS] invariant_collectedIsExactlyTheSumOfScheduledFees\n[PASS] invariant_managerCanAlwaysHonourTheHooksClaims\n[PASS] invariant_noIMDLeaksToAnyoneButTheKeeper\n[PASS] invariant_scheduleAndPoolFeeAreFrozen\n[PASS] invariant_whatTheHookHoldsPlusWhatTheKeeperGotIsEverythingEverTaken\n DroneHookInvariantsTest invariants (runs: 128, calls: 6144, reverts: 0)\n\n╭------------------------+-----------------+-------+---------+----------╮\n| Contract               | Selector        | Calls | Reverts | Discards |\n+=======================================================================+\n| DroneMultiActorHandler | addLiquidity    | 872   | 0       | 0        |\n|------------------------+-----------------+-------+---------+----------|\n| DroneMultiActorHandler | donate          | 891   | 0       | 0        |\n|------------------------+-----------------+-------+---------+----------|\n| DroneMultiActorHandler | passTime        | 834   | 0       | 0        |\n|------------------------+-----------------+-------+---------+----------|\n| DroneMultiActorHandler | removeLiquidity | 868   | 0       | 0        |\n|------------------------+-----------------+-------+---------+----------|\n| DroneMultiActorHandler | setProtocolFee  | 884   | 0       | 0        |\n|------------------------+-----------------+-------+---------+----------|\n| DroneMultiActorHandler | sweep           | 880   | 0       | 0        |\n|------------------------+-----------------+-------+---------+----------|\n| DroneMultiActorHandler | trade           | 915   | 0       | 0        |\n╰------------------------+-----------------+-------+---------+----------╯\n\nLogs:\n  Bound result 380\n  Bound result 2\n  Bound result 1652\n  Bound result 244\n  Bound result 15\n  Bound result 999999000000000000001473\n  Bound result 7295\n  Bound result 186\n  Bound result 571\n  Bound result 3886\n  Bound result 600782947137124163248\n  Bound result 88\n  Bound result 512\n  Bound result 13\n  Bound result 10879137169680829072\n  Bound result 57\n  Bound result 646436997065849521658\n  Bound result 368\n  Bound result 915\n  Bound result 8549\n  Bound result 5255\n  Bound result 650\n  Bound result 1631\n  Bound result 999999000000000000012135\n  Bound result 230917542063125411\n  Bound result 791\n  Bound result 64\n  Bound result 184\n  Bound result 1000000000000000000\n  Bound result 2230\n  Bound result 452809228\n  Bound result 1\n  Bound result 299\n  Bound result 534653\n  Bound result 199\n  Bound result 799\n  Bound result 12159\n  Bound result 14\n  Bound result 0\n  Bound result 10\n  Bound result 492\n  Bound result 2137\n  Bound result 256\n  Bound result 10757\n  Bound result 5111\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.82s (1.79s CPU time)\n\nRan 11 test suites in 1.82s (3.97s CPU time): 82 tests passed, 0 failed, 2 skipped (84 total tests)\n","passed":true},{"durationMs":73,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DroneHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DroneHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DroneHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"DroneHook.sweep()\",\"DroneHook.unlockCallback(bytes)\",\"DroneToken.approve(address,uint256)\",\"DroneToken.transfer(address,uint256)\",\"DroneToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"LICENSE\":24,\"README.md\":104,\"docs/DEPENDENCIES.md\":13,\"docs/SECURITY_REVIEW.md\":92,\"docs/VERIFICATION.md\":52,\"foundry.toml\":24,\"launch.json\":31,\"remappings.txt\":3,\"script/DeployDrone.sol\":47,\"src/DroneHook.sol\":179,\"src/DroneToken.sol\":60,\"src/HookFlags.sol\":30,\"src/SpecifiedAmount.sol\":101,\"test/DroneHook.t.sol\":272,\"test/DroneHookAdversarial.t.sol\":513,\"test/DroneHookInvariants.t.sol\":303,\"test/DroneInvariant.t.sol\":81,\"test/DroneToken.t.sol\":77,\"test/MainnetFork.t.sol\":53,\"test/MainnetForkRecent.t.sol\":108,\"test/SpecifiedAmount.t.sol\":74,\"test/helpers/AdversarialRouter.sol\":125,\"test/helpers/HookFixture.sol\":155,\"test/helpers/TestRouter.sol\":70,\"test/mocks/MockERC20.sol\":58,\"tools/check_bytecode.py\":18,\"tools/check_manifest.py\":17},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"1192f17d94928ea410718ecb9441ee83f6b15f933bb8542c8b63b50295609a73","verifiedTreeHash":"f3e20fb27284fe77dcd9cdad0be12123edfb898b","verifierVersion":"0.1.0+be003835"},{"checks":[{"durationMs":49153,"exitCode":0,"name":"build","output":"Compiling 79 files with Solc 0.8.26\nSolc 0.8.26 finished in 48.99s\nCompiler run successful!\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/SpecifiedAmount.sol:58:51\n   │\n58 │                 remaining = exactIn ? remaining + int256(input + lpAmount) : remaining - int256(output);\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/SpecifiedAmount.sol:58:90\n   │\n58 │                 remaining = exactIn ? remaining + int256(input + lpAmount) : remaining - int256(output);\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/SpecifiedAmount.sol:72:19\n   │\n72 │                 ? uint256(remaining - 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/SpecifiedAmount.sol:73:19\n   │\n73 │                 : uint256(params.amountSpecified - remaining);\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/DroneHook.sol:87:29\n   │\n87 │         if (!initialized || block.timestamp <= openedAt) return OPENING_FEE;\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/DroneHook.sol:89:13\n   │\n89 │         if (elapsed >= OPENING_SECONDS) return KEEPER_FEE;\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DroneHook.sol:106:35\n    │\n106 │             requested = exactIn ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DroneHook.sol:106:70\n    │\n106 │             requested = exactIn ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DroneHook.sol:111:25\n    │\n111 │             requested > uint256(uint128(type(int128).max)) ? uint256(uint128(type(int128).max)) : requested;\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/DroneHook.sol:111:33\n    │\n111 │             requested > uint256(uint128(type(int128).max)) ? uint256(uint128(type(int128).max)) : requested;\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/DroneHook.sol:111:62\n    │\n111 │             requested > uint256(uint128(type(int128).max)) ? uint256(uint128(type(int128).max)) : requested;\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/DroneHook.sol:111:70\n    │\n111 │             requested > uint256(uint128(type(int128).max)) ? uint256(uint128(type(int128).max)) : requested;\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/DroneHook.sol:114:44\n    │\n114 │         quote.amountSpecified = exactIn ? -int256(budget - reserve) : int256(budget + reserve);\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/DroneHook.sol:114:71\n    │\n114 │         quote.amountSpecified = exactIn ? -int256(budget - reserve) : int256(budget + reserve);\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/DroneHook.sol:124:63\n    │\n124 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\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/DroneHook.sol:124:70\n    │\n124 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\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/DroneHook.sol:141:36\n    │\n141 │         uint256 fee = amount < 0 ? uint256(-amount) * rate / (10_000 - rate) : uint256(amount) * rate / 10_000;\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/DroneHook.sol:141:80\n    │\n141 │         uint256 fee = amount < 0 ? uint256(-amount) * rate / (10_000 - rate) : uint256(amount) * rate / 10_000;\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/DroneHook.sol:143:44\n    │\n143 │         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/DroneHook.sol:143:51\n    │\n143 │         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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DroneHook.sol:150:9\n    │\n150 │         emit FeeAccrued(fee);\n    │         ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `sweeping` is updated\n    ╭▸ src/DroneHook.sol:162:29\n    │\n162 │         amount = abi.decode(poolManager.unlock(\"\"), (uint256));\n    │                             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DroneHook.sol:167:9\n    │\n167 │         emit Swept(amount);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":456,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 418.97µs (0.00ns CPU time)\n\nRan 5 tests for test/DroneToken.t.sol:DroneTokenTest\n[PASS] testFuzz_ordinaryTransfersAndAllowances(uint96) (runs: 256, μ: 184564, ~: 186090)\nLogs:\n  Bound result 177362147\n\n[PASS] test_failurePaths() (gas: 104110)\n[PASS] test_fixedSupplyAndMetadata() (gas: 37510)\n[PASS] test_noAdminOrMintEvenForDeployer() (gas: 361287)\n[PASS] test_zeroSelfTransfersAndInfiniteAllowance() (gas: 178878)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 5.59ms (5.98ms CPU time)\n\nRan 2 tests for test/SpecifiedAmount.t.sol:IndependentQuoteReview\n[PASS] testFuzz_independentSpecifiedFillMatchesManager(bool,bool,uint96,uint16,uint16,uint16) (runs: 256, μ: 257273, ~: 248255)\nLogs:\n  Bound result 997\n  Bound result 4\n  Bound result 4227\n  Bound result 13\n\n[PASS] test_nativeExactOutputInt256MaxCanPartiallyFillButHookedRequestOverflows() (gas: 308710)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 47.53ms (27.99ms CPU time)\n\nRan 20 tests for test/DroneHook.t.sol:DroneHookTest\n[PASS] testFuzz_crossTicksAndEmptyLiquidity(bool,bool,uint96,uint16) (runs: 256, μ: 849749, ~: 842101)\nLogs:\n  Bound result 5\n  Bound result 899000000000000000000014948\n\n[PASS] testFuzz_feeAndPartialFills(bool,bool,uint96,uint32,bool) (runs: 256, μ: 351765, ~: 352093)\nLogs:\n  Bound result 7123\n  Bound result 25048574003\n\n[PASS] test_allModesAtOpeningMidpointAndAfterHour() (gas: 5755050)\n[PASS] test_callbacksOnlyManager() (gas: 150001)\n[PASS] test_cannotInitializePredictedHookWithoutCode() (gas: 45781)\n[PASS] test_constructorRejectsZeroOrSameCurrencies() (gas: 8422)\n[PASS] test_failedSweepRollsBackAndDoesNotPoisonTrading() (gas: 897116)\n[PASS] test_freshManagerWithDroneOnlyLiquidity() (gas: 30974992)\n[PASS] test_initializationRejectsWrongPairFeeAndSpacing() (gas: 6159565)\n[PASS] test_invalidSaltRefused() (gas: 69845)\n[PASS] test_noLiquidityCollectsNoFee() (gas: 1035745)\n[PASS] test_openingCannotBeResetOrReusedForAnotherPool() (gas: 63905)\n[PASS] test_partialFillsAllModes() (gas: 2431907)\n[PASS] test_permissionBitsAndNoEscapeOpcodes() (gas: 4446006)\n[PASS] test_reentrancyFromSwapTransferDefersSweep() (gas: 851075)\n[PASS] test_reentrantSweepCannotRedirectOrDoublePay() (gas: 544932)\n[PASS] test_scheduleEndpointsAndEverySecond() (gas: 49000918)\n[PASS] test_sweepAnyoneRepeatedAndDirectDonations() (gas: 1258907)\n[PASS] test_sweepDuringUnlockDefersWithoutReverting() (gas: 531670)\n[PASS] test_tinyAmountsAndHugePartialRequests() (gas: 7480219)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 93.79ms (181.90ms CPU time)\n\nRan 20 tests for test/DroneHook.t.sol:DroneHookIMDSecondTest\n[PASS] testFuzz_crossTicksAndEmptyLiquidity(bool,bool,uint96,uint16) (runs: 256, μ: 850107, ~: 844195)\nLogs:\n  Bound result 3343\n  Bound result 899000000087153340529498924\n\n[PASS] testFuzz_feeAndPartialFills(bool,bool,uint96,uint32,bool) (runs: 256, μ: 351552, ~: 352864)\nLogs:\n  Bound result 2853\n  Bound result 428506261498577067033\n\n[PASS] test_allModesAtOpeningMidpointAndAfterHour() (gas: 5758120)\n[PASS] test_callbacksOnlyManager() (gas: 150716)\n[PASS] test_cannotInitializePredictedHookWithoutCode() (gas: 46009)\n[PASS] test_constructorRejectsZeroOrSameCurrencies() (gas: 8422)\n[PASS] test_failedSweepRollsBackAndDoesNotPoisonTrading() (gas: 884436)\n[PASS] test_freshManagerWithDroneOnlyLiquidity() (gas: 15088319)\n[PASS] test_initializationRejectsWrongPairFeeAndSpacing() (gas: 9450221)\n[PASS] test_invalidSaltRefused() (gas: 70073)\n[PASS] test_noLiquidityCollectsNoFee() (gas: 1027285)\n[PASS] test_openingCannotBeResetOrReusedForAnotherPool() (gas: 64133)\n[PASS] test_partialFillsAllModes() (gas: 2425885)\n[PASS] test_permissionBitsAndNoEscapeOpcodes() (gas: 4446006)\n[PASS] test_reentrancyFromSwapTransferDefersSweep() (gas: 841209)\n[PASS] test_reentrantSweepCannotRedirectOrDoublePay() (gas: 534379)\n[PASS] test_scheduleEndpointsAndEverySecond() (gas: 49000918)\n[PASS] test_sweepAnyoneRepeatedAndDirectDonations() (gas: 1250139)\n[PASS] test_sweepDuringUnlockDefersWithoutReverting() (gas: 521345)\n[PASS] test_tinyAmountsAndHugePartialRequests() (gas: 7448608)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 114.94ms (182.62ms CPU time)\n\nRan 1 test for test/DroneInvariant.t.sol:DroneInvariantTest\n[PASS]\nDroneInvariantTest invariants:\n[PASS] invariant_allCollectedIMDEitherPendingOrPaidOnlyToKeeper\n[PASS] invariant_tokenSupplyAndOpeningCannotChange\n DroneInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| DroneHandler | passTime | 671   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| DroneHandler | sweep    | 652   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| DroneHandler | trade    | 725   | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 7200\n  Bound result 54567\n  Bound result 25\n  Bound result 2\n  Bound result 396\n  Bound result 35\n  Bound result 256\n  Bound result 2086\n  Bound result 11935\n  Bound result 839\n  Bound result 69\n  Bound result 3348\n  Bound result 669609419082\n  Bound result 2487586\n  Bound result 1729\n  Bound result 78\n  Bound result 29\n  Bound result 256\n  Bound result 394\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 348.09ms (321.05ms CPU time)\n\nRan 6 test suites in 349.62ms (610.36ms CPU time): 48 tests passed, 0 failed, 1 skipped (49 total tests)\n","passed":true},{"durationMs":75,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DroneHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DroneHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DroneHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"DroneHook.sweep()\",\"DroneHook.unlockCallback(bytes)\",\"DroneToken.approve(address,uint256)\",\"DroneToken.transfer(address,uint256)\",\"DroneToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"LICENSE\":24,\"README.md\":104,\"docs/DEPENDENCIES.md\":13,\"docs/SECURITY_REVIEW.md\":92,\"docs/VERIFICATION.md\":52,\"foundry.toml\":24,\"launch.json\":31,\"remappings.txt\":3,\"script/DeployDrone.sol\":47,\"src/DroneHook.sol\":179,\"src/DroneToken.sol\":60,\"src/HookFlags.sol\":30,\"src/SpecifiedAmount.sol\":101,\"test/DroneHook.t.sol\":272,\"test/DroneInvariant.t.sol\":81,\"test/DroneToken.t.sol\":77,\"test/MainnetFork.t.sol\":53,\"test/SpecifiedAmount.t.sol\":74,\"test/helpers/HookFixture.sol\":133,\"test/helpers/TestRouter.sol\":70,\"test/mocks/MockERC20.sol\":58,\"tools/check_bytecode.py\":18,\"tools/check_manifest.py\":17},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"438fd3d788b308bc1c295b425fa28e92ff0cabe0564579fa7c28990c7c9a25e7","verifiedTreeHash":"25e6a71c52eb542c6abaa2d1459fd7eb78afe5e6","verifierVersion":"0.1.0+be003835"},{"checks":[{"durationMs":3275,"exitCode":0,"name":"build","output":"Compiling 79 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.09s\nCompiler run successful!\nwarning[encode-packed-collision]: `abi.encodePacked()` called with multiple dynamic type arguments; hash collisions possible\n   ╭▸ src/DroneDeployer.sol:39:26\n   │\n39 │         return keccak256(abi.encodePacked(type(DroneHook).creationCode, abi.encode(manager, imd, token)));\n   │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/encode-packed-collision\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n   ╭▸ src/DroneDeployer.sol:79:9\n   │\n79 │         emit Deployed(address(token), address(hook), config.requester);\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n   ╭▸ src/DroneDeployer.sol:71:9\n   │\n71 │ ┏         config.manager\n72 │ ┃             .initialize(\n73 │ ┃                 PoolKey(\n74 │ ┃                     Currency.wrap(a), Currency.wrap(b), 12_500, config.tickSpacing, IHooks(address(hook))\n75 │ ┃                 ),\n76 │ ┃                 config.sqrtPriceX96\n77 │ ┃             );\n   │ ┗━━━━━━━━━━━━━┛\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\nwarning[erc20-unchecked-transfer]: ERC20 `transfer` or `transferFrom` call does not check the return value\n   ╭▸ src/DroneDeployer.sol:78:9\n   │\n78 │         token.transfer(config.requester, token.totalSupply());\n   │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/erc20-unchecked-transfer\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/DroneHook.sol:86:29\n   │\n86 │         if (!initialized || block.timestamp <= openedAt) return OPENING_FEE;\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/DroneHook.sol:88:13\n   │\n88 │         if (elapsed >= OPENING_SECONDS) return KEEPER_FEE;\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DroneHook.sol:184:9\n    │\n184 │         emit FeeCollected(fee);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DroneHook.sol:102:63\n    │\n102 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\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/DroneHook.sol:102:70\n    │\n102 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\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/DroneHook.sol:111:23\n    │\n111 │         uint256 cap = 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/DroneHook.sol:111:31\n    │\n111 │         uint256 cap = 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/DroneHook.sol:115:42\n    │\n115 │             simulated.amountSpecified += int256(reserve);\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/DroneHook.sol:117:28\n    │\n117 │             uint256 room = uint256(type(int256).max) - requested;\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/DroneHook.sol:118:42\n    │\n118 │             simulated.amountSpecified += int256(reserve > room ? room : reserve);\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/DroneHook.sol:142:44\n    │\n142 │         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/DroneHook.sol:142:51\n    │\n142 │         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[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DroneHook.sol:157:40\n    │\n157 │             if (reason.length == 36 && bytes4(reason) == QuoteResult.selector) {\n    │                                        ━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'bytes4' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DroneHook.sol:172:32\n    │\n172 │             return value < 0 ? uint256(-(value + 1)) + 1 : uint256(value);\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/DroneHook.sol:172:60\n    │\n172 │             return value < 0 ? uint256(-(value + 1)) + 1 : uint256(value);\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[divide-before-multiply]: division before multiplication may lose precision\n    ╭▸ src/DroneHook.sol:177:16\n    │\n177 │         return (amount / denominator) * rate + (amount % denominator) * rate / denominator;\n    │                ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/divide-before-multiply\n\nwarning[reentrancy-no-eth]: external call can be reentered before `sweeping` is updated\n    ╭▸ src/DroneHook.sol:197:26\n    │\n197 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\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/DroneHook.sol:202:9\n    │\n202 │         emit Swept(amount);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[unused-return]: return value of an external call is not used\n    ╭▸ src/DroneHook.sol:197:26\n    │\n197 │         if (claims != 0) poolManager.unlock(abi.encode(claims));\n    │                          ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/unused-return\n\n","passed":true},{"durationMs":722,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 5 tests for test/DroneToken.t.sol:DroneTokenTest\n[PASS] testFuzz_transfersConserveSupply(uint256) (runs: 256, μ: 110275, ~: 111072)\n[PASS] test_allowanceAndUnlimitedApproval() (gas: 201093)\n[PASS] test_invalidTransfersAtomic() (gas: 175299)\n[PASS] test_noMintAdminOrUpgradeSelectors() (gas: 184994)\n[PASS] test_standardMetadataAndEntireSupplyToDeployer() (gas: 36670)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 6.93ms (10.52ms CPU time)\n\nRan 4 tests for test/DroneDeployer.t.sol:DroneDeployerTest\n[PASS] test_directMinedDeploymentAndSupplyRecipient() (gas: 8216622)\n[PASS] test_frontRunnerCannotReplaceRecipientOrReserveTokenAddress() (gas: 11636535)\n[PASS] test_invalidParametersRollBackDeployment() (gas: 1885742)\n[PASS] test_minerIsBoundedAndWrongFlagsRejected() (gas: 476836)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 29.03ms (28.35ms CPU time)\n\nRan 2 tests for test/NumericBoundaries.t.sol:NumericBoundariesTest\n[PASS] test_reviewHookMaximumInt256ExactOutputOverflowsManagerAddition() (gas: 171474)\n[PASS] test_reviewVanillaCanPartiallyFillMaximumInt256ExactOutput() (gas: 444358)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 53.68ms (797.17µs CPU time)\n\nRan 13 tests for test/DroneHook.t.sol:DroneHookTest\n[PASS] testFuzz_curveMonotonic(uint32) (runs: 256, μ: 33855, ~: 33415)\n[PASS] testFuzz_feeAllModes(uint8,bool,bool,uint96,uint32) (runs: 256, μ: 367855, ~: 348342)\n[PASS] testFuzz_partialFillNeverChargesUnspentIMD(uint8,bool,bool,uint32) (runs: 256, μ: 375336, ~: 394574)\n[PASS] test_allDirectionsAndOpeningTimes() (gas: 12200056)\n[PASS] test_callbacksAndQuoteRefuseUnauthorizedCalls() (gas: 178072)\n[PASS] test_curveBoundaries() (gas: 101063)\n[PASS] test_failedSweepDoesNotBlockSwapsOrLoseClaims() (gas: 726721)\n[PASS] test_freshManagerTokenOnlyLiquidityBuy() (gas: 39780283)\n[PASS] test_hugeRequestsPartialFillAndZeroLiquidity() (gas: 467919)\n[PASS] test_initializationRejectsWrongPairFeeAndReset() (gas: 10188321)\n[PASS] test_permissionlessSweepRepeatedAndDonations() (gas: 925973)\n[PASS] test_permissionsAndInitialization() (gas: 62037)\n[PASS] test_reentrantSweepPaysOnlyOnce() (gas: 533092)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 99.64ms (153.88ms CPU time)\n\nRan 1 test for test/DroneInvariant.t.sol:DroneInvariantTest\n[PASS] invariant_feesAndDonationsConservedOnlyToKeeper() (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+------------+-------+---------+----------╮\n| Contract     | Selector   | Calls | Reverts | Discards |\n+========================================================+\n| DroneHandler | donate     | 499   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| DroneHandler | sweep      | 506   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| DroneHandler | timePasses | 515   | 0       | 0        |\n|--------------+------------+-------+---------+----------|\n| DroneHandler | trade      | 528   | 0       | 0        |\n╰--------------+------------+-------+---------+----------╯\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 576.65ms (513.28ms CPU time)\n\nRan 5 test suites in 578.35ms (765.94ms CPU time): 25 tests passed, 0 failed, 0 skipped (25 total tests)\n","passed":true},{"durationMs":55,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DroneDeployer.deploy((address,address,address,bytes32,bytes32,uint160,int24))\",\"DroneHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DroneHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DroneHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"DroneHook.quoteAndRevert((address,address,uint24,int24,address),(bool,int256,uint160))\",\"DroneHook.sweep()\",\"DroneHook.unlockCallback(bytes)\",\"DroneToken.approve(address,uint256)\",\"DroneToken.transfer(address,uint256)\",\"DroneToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":3,\"README.md\":99,\"docs/SECURITY_REVIEW.md\":61,\"docs/VALIDATION.md\":30,\"foundry.toml\":25,\"launch.json\":30,\"script/MainnetRehearsal.s.sol\":133,\"src/DroneDeployer.sol\":81,\"src/DroneHook.sol\":212,\"src/DroneToken.sol\":53,\"src/HookFlags.sol\":30,\"test/DroneDeployer.t.sol\":109,\"test/DroneHook.t.sol\":281,\"test/DroneInvariant.t.sol\":112,\"test/DroneToken.t.sol\":81,\"test/NumericBoundaries.t.sol\":29,\"test/helpers/HookFixture.sol\":82,\"test/helpers/MockIMD.sol\":54,\"test/helpers/PoolActor.sol\":61,\"tools/check_bytecode.py\":20},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":1467,"exitCode":0,"name":"slither","output":"[high/medium] unchecked-transfer at src/DroneDeployer.sol:60: DroneDeployer.deploy(DroneDeployer.Config) (src/DroneDeployer.sol#60-80) ignores return value by token.transfer(config.requester,token.totalSupply()) (src/DroneDeployer.sol#78)\n[medium/medium] divide-before-multiply at src/DroneHook.sol:176: DroneHook._fraction(uint256,uint256,uint256) (src/DroneHook.sol#176-178) performs a multiplication on the result of a division:\n[medium/high] incorrect-equality at src/DroneHook.sol:180: DroneHook._collect(uint256) (src/DroneHook.sol#180-185) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/DroneHook.sol:193: Reentrancy in DroneHook.sweep() (src/DroneHook.sol#193-203):\n[medium/medium] unused-return at src/DroneDeployer.sol:60: DroneDeployer.deploy(DroneDeployer.Config) (src/DroneDeployer.sol#60-80) ignores return value by config.manager.initialize(PoolKey(Currency.wrap(a),Currency.wrap(b),12_500,config.tickSpacing,IHooks(address(hook))),config.sqrtPriceX96) (src/DroneDeployer.sol#71-77)\n[medium/medium] unused-return at src/DroneHook.sol:193: DroneHook.sweep() (src/DroneHook.sol#193-203) ignores return value by poolManager.unlock(abi.encode(claims)) (src/DroneHook.sol#197)\n[low/medium] reentrancy-events at src/DroneHook.sol:92: Reentrancy in DroneHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/DroneHook.sol#92-103):\n[low/medium] reentrancy-events at src/DroneDeployer.sol:60: Reentrancy in DroneDeployer.deploy(DroneDeployer.Config) (src/DroneDeployer.sol#60-80):\n[low/medium] reentrancy-events at src/DroneHook.sol:180: Reentrancy in DroneHook._collect(uint256) (src/DroneHook.sol#180-185):\n[low/medium] reentrancy-events at src/DroneHook.sol:193: Reentrancy in DroneHook.sweep() (src/DroneHook.sol#193-203):\n[low/medium] timestamp at src/DroneHook.sol:105: DroneHook._specifiedFee(PoolKey,SwapParams) (src/DroneHook.sol#105-127) uses timestamp for comparisons\n[low/medium] timestamp at src/DroneHook.sol:180: DroneHook._collect(uint256) (src/DroneHook.sol#180-185) uses timestamp for comparisons\n[low/medium] timestamp at src/DroneHook.sol:85: DroneHook.feeNow() (src/DroneHook.sol#85-90) uses timestamp for comparisons","passed":true},{"durationMs":500,"exitCode":0,"name":"aderyn","output":"[high] abi-encode-packed-hash-collision at src/DroneDeployer.sol:39: `abi.encodePacked()` Hash Collision\n[high] reentrancy-state-change at src/DroneHook.sol:196: Reentrancy: State change after external call (2 places)\n[low] internal-function-used-once at src/HookFlags.sol:23: Internal Function Used Only Once\n[low] large-numeric-literal at src/DroneHook.sol:20: Large Numeric Literal (2 places)\n[low] missing-inheritance at src/DroneToken.sol:5: Missing Inheritance\n[low] unchecked-return at src/DroneDeployer.sol:71: Unchecked Return (2 places)\n[low] uninitialized-local-variable at src/DroneDeployer.sol:48: Uninitialized Local Variable\n[low] unsafe-erc20-operation at src/DroneDeployer.sol:78: Unsafe ERC20 Operation (2 places)\n[low] unused-public-function at src/DroneDeployer.sol:34: Public Function Not Used Internally (3 places)","passed":true}],"detail":"launch.json is not a valid launch manifest: kind: Invalid discriminator value. Expected 'univ4_hook' | 'evm_project' | 'custom_token' | 'evm_contracts'","evaluation":"checks","profile":"foundry","status":"rejected","submissionHash":"6ab0fa6150d49828e4b59088dcbe213b45deaf223cf006c626f37cd97cb78911","verifiedTreeHash":"2b435aa645e899cd61fefab911465f51349afe3b","verifierVersion":"0.1.0+be003835"},{"checks":[{"durationMs":49712,"exitCode":0,"name":"build","output":"Compiling 79 files with Solc 0.8.26\nSolc 0.8.26 finished in 49.52s\nCompiler run successful!\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/DroneHook.sol:87:29\n   │\n87 │         if (!initialized || block.timestamp <= openedAt) return OPENING_FEE;\n   │                             ━━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[block-timestamp]: usage of `block.timestamp` in a comparison may be manipulated by validators\n   ╭▸ src/DroneHook.sol:89:13\n   │\n89 │         if (elapsed >= OPENING_SECONDS) return KEEPER_FEE;\n   │             ━━━━━━━━━━━━━━━━━━━━━━━━━━\n   │\n   ╰ help: https://getfoundry.sh/forge/linting/block-timestamp\n\nwarning[unsafe-typecast]: typecast can truncate values\n   ╭▸ src/SpecifiedAmount.sol:58:51\n   │\n58 │                 remaining = exactIn ? remaining + int256(input + lpAmount) : remaining - int256(output);\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/SpecifiedAmount.sol:58:90\n   │\n58 │                 remaining = exactIn ? remaining + int256(input + lpAmount) : remaining - int256(output);\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/SpecifiedAmount.sol:72:19\n   │\n72 │                 ? uint256(remaining - 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/SpecifiedAmount.sol:73:19\n   │\n73 │                 : uint256(params.amountSpecified - remaining);\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/DroneHook.sol:106:35\n    │\n106 │             requested = exactIn ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                   ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DroneHook.sol:106:70\n    │\n106 │             requested = exactIn ? uint256(-params.amountSpecified) : uint256(params.amountSpecified);\n    │                                                                      ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ├ note: consider disabling this lint if you're certain the cast is safe\n    │       \n    │       // casting to 'uint256' is safe because [explain why]\n    │       // forge-lint: disable-next-line(unsafe-typecast)\n    │       \n    │       \n    ╰ help: https://getfoundry.sh/forge/linting/unsafe-typecast\n\nwarning[unsafe-typecast]: typecast can truncate values\n    ╭▸ src/DroneHook.sol:111:25\n    │\n111 │             requested > uint256(uint128(type(int128).max)) ? uint256(uint128(type(int128).max)) : requested;\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/DroneHook.sol:111:33\n    │\n111 │             requested > uint256(uint128(type(int128).max)) ? uint256(uint128(type(int128).max)) : requested;\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/DroneHook.sol:111:62\n    │\n111 │             requested > uint256(uint128(type(int128).max)) ? uint256(uint128(type(int128).max)) : requested;\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/DroneHook.sol:111:70\n    │\n111 │             requested > uint256(uint128(type(int128).max)) ? uint256(uint128(type(int128).max)) : requested;\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/DroneHook.sol:114:44\n    │\n114 │         quote.amountSpecified = exactIn ? -int256(budget - reserve) : int256(budget + reserve);\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/DroneHook.sol:114:71\n    │\n114 │         quote.amountSpecified = exactIn ? -int256(budget - reserve) : int256(budget + reserve);\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/DroneHook.sol:124:63\n    │\n124 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\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/DroneHook.sol:124:70\n    │\n124 │         return (IHooks.beforeSwap.selector, toBeforeSwapDelta(int128(int256(fee)), 0), 0);\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/DroneHook.sol:141:36\n    │\n141 │         uint256 fee = amount < 0 ? uint256(-amount) * rate / (10_000 - rate) : uint256(amount) * rate / 10_000;\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/DroneHook.sol:141:80\n    │\n141 │         uint256 fee = amount < 0 ? uint256(-amount) * rate / (10_000 - rate) : uint256(amount) * rate / 10_000;\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/DroneHook.sol:143:44\n    │\n143 │         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/DroneHook.sol:143:51\n    │\n143 │         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[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DroneHook.sol:150:9\n    │\n150 │         emit FeeAccrued(fee);\n    │         ━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\nwarning[reentrancy-no-eth]: external call can be reentered before `sweeping` is updated\n    ╭▸ src/DroneHook.sol:162:29\n    │\n162 │         amount = abi.decode(poolManager.unlock(\"\"), (uint256));\n    │                             ━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-no-eth\n\nwarning[reentrancy-events]: event emitted after an external call; reentrancy can reorder or fabricate logs that off-chain consumers rely on\n    ╭▸ src/DroneHook.sol:167:9\n    │\n167 │         emit Swept(amount);\n    │         ━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://getfoundry.sh/forge/linting/reentrancy-events\n\n","passed":true},{"durationMs":564,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 1 test for test/MainnetFork.t.sol:MainnetForkTest\n[SKIP: skipped] setUp() (gas: 0)\nSuite result: ok. 0 passed; 0 failed; 1 skipped; finished in 1.07ms (0.00ns CPU time)\n\nRan 5 tests for test/DroneToken.t.sol:DroneTokenTest\n[PASS] testFuzz_ordinaryTransfersAndAllowances(uint96) (runs: 256, μ: 185400, ~: 186144)\nLogs:\n  Bound result 134400999546548980161702\n\n[PASS] test_failurePaths() (gas: 104110)\n[PASS] test_fixedSupplyAndMetadata() (gas: 37510)\n[PASS] test_noAdminOrMintEvenForDeployer() (gas: 361287)\n[PASS] test_zeroSelfTransfersAndInfiniteAllowance() (gas: 178878)\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 8.88ms (9.63ms CPU time)\n\nRan 2 tests for test/SpecifiedAmount.t.sol:IndependentQuoteReview\n[PASS] testFuzz_independentSpecifiedFillMatchesManager(bool,bool,uint96,uint16,uint16,uint16) (runs: 256, μ: 255593, ~: 248028)\nLogs:\n  Bound result 35\n  Bound result 8\n  Bound result 636375930930087447\n  Bound result 1666\n\n[PASS] test_nativeExactOutputInt256MaxCanPartiallyFillButHookedRequestOverflows() (gas: 308710)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 75.77ms (29.64ms CPU time)\n\nRan 20 tests for test/DroneHook.t.sol:DroneHookTest\n[PASS] testFuzz_crossTicksAndEmptyLiquidity(bool,bool,uint96,uint16) (runs: 256, μ: 848758, ~: 841903)\nLogs:\n  Bound result 258\n  Bound result 899000000000000000000001915\n\n[PASS] testFuzz_feeAndPartialFills(bool,bool,uint96,uint32,bool) (runs: 256, μ: 352389, ~: 356457)\nLogs:\n  Bound result 1800\n  Bound result 23083\n\n[PASS] test_allModesAtOpeningMidpointAndAfterHour() (gas: 5755050)\n[PASS] test_callbacksOnlyManager() (gas: 150001)\n[PASS] test_cannotInitializePredictedHookWithoutCode() (gas: 45781)\n[PASS] test_constructorRejectsZeroOrSameCurrencies() (gas: 8422)\n[PASS] test_failedSweepRollsBackAndDoesNotPoisonTrading() (gas: 897116)\n[PASS] test_freshManagerWithDroneOnlyLiquidity() (gas: 30974992)\n[PASS] test_initializationRejectsWrongPairFeeAndSpacing() (gas: 6159565)\n[PASS] test_invalidSaltRefused() (gas: 69845)\n[PASS] test_noLiquidityCollectsNoFee() (gas: 1035745)\n[PASS] test_openingCannotBeResetOrReusedForAnotherPool() (gas: 63905)\n[PASS] test_partialFillsAllModes() (gas: 2431907)\n[PASS] test_permissionBitsAndNoEscapeOpcodes() (gas: 4446006)\n[PASS] test_reentrancyFromSwapTransferDefersSweep() (gas: 851075)\n[PASS] test_reentrantSweepCannotRedirectOrDoublePay() (gas: 544932)\n[PASS] test_scheduleEndpointsAndEverySecond() (gas: 49000918)\n[PASS] test_sweepAnyoneRepeatedAndDirectDonations() (gas: 1258907)\n[PASS] test_sweepDuringUnlockDefersWithoutReverting() (gas: 531670)\n[PASS] test_tinyAmountsAndHugePartialRequests() (gas: 7480219)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 130.35ms (186.43ms CPU time)\n\nRan 20 tests for test/DroneHook.t.sol:DroneHookIMDSecondTest\n[PASS] testFuzz_crossTicksAndEmptyLiquidity(bool,bool,uint96,uint16) (runs: 256, μ: 847851, ~: 843852)\nLogs:\n  Bound result 258\n  Bound result 899000000000000000000001915\n\n[PASS] testFuzz_feeAndPartialFills(bool,bool,uint96,uint32,bool) (runs: 256, μ: 351351, ~: 352444)\nLogs:\n  Bound result 4429\n  Bound result 4980\n\n[PASS] test_allModesAtOpeningMidpointAndAfterHour() (gas: 5758120)\n[PASS] test_callbacksOnlyManager() (gas: 150716)\n[PASS] test_cannotInitializePredictedHookWithoutCode() (gas: 46009)\n[PASS] test_constructorRejectsZeroOrSameCurrencies() (gas: 8422)\n[PASS] test_failedSweepRollsBackAndDoesNotPoisonTrading() (gas: 884436)\n[PASS] test_freshManagerWithDroneOnlyLiquidity() (gas: 15088319)\n[PASS] test_initializationRejectsWrongPairFeeAndSpacing() (gas: 9450221)\n[PASS] test_invalidSaltRefused() (gas: 70073)\n[PASS] test_noLiquidityCollectsNoFee() (gas: 1027285)\n[PASS] test_openingCannotBeResetOrReusedForAnotherPool() (gas: 64133)\n[PASS] test_partialFillsAllModes() (gas: 2425885)\n[PASS] test_permissionBitsAndNoEscapeOpcodes() (gas: 4446006)\n[PASS] test_reentrancyFromSwapTransferDefersSweep() (gas: 841209)\n[PASS] test_reentrantSweepCannotRedirectOrDoublePay() (gas: 534379)\n[PASS] test_scheduleEndpointsAndEverySecond() (gas: 49000918)\n[PASS] test_sweepAnyoneRepeatedAndDirectDonations() (gas: 1250139)\n[PASS] test_sweepDuringUnlockDefersWithoutReverting() (gas: 521345)\n[PASS] test_tinyAmountsAndHugePartialRequests() (gas: 7448608)\nSuite result: ok. 20 passed; 0 failed; 0 skipped; finished in 130.43ms (201.12ms CPU time)\n\nRan 1 test for test/DroneInvariant.t.sol:DroneInvariantTest\n[PASS]\nDroneInvariantTest invariants:\n[PASS] invariant_allCollectedIMDEitherPendingOrPaidOnlyToKeeper\n[PASS] invariant_tokenSupplyAndOpeningCannotChange\n DroneInvariantTest invariants (runs: 64, calls: 2048, reverts: 0)\n\n╭--------------+----------+-------+---------+----------╮\n| Contract     | Selector | Calls | Reverts | Discards |\n+======================================================+\n| DroneHandler | passTime | 678   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| DroneHandler | sweep    | 687   | 0       | 0        |\n|--------------+----------+-------+---------+----------|\n| DroneHandler | trade    | 683   | 0       | 0        |\n╰--------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 208\n  Bound result 1996316571\n  Bound result 16\n  Bound result 7293\n  Bound result 7216\n  Bound result 173\n  Bound result 502\n  Bound result 12261\n  Bound result 15705\n  Bound result 4194\n  Bound result 512\n  Bound result 89120862830319172580\n  Bound result 3\n  Bound result 57585003334618531534\n  Bound result 580\n  Bound result 5\n  Bound result 472\n  Bound result 16945\n  Bound result 6\n  Bound result 384\n  Bound result 490\n  Bound result 6133\n  Bound result 114\n  Bound result 2\n  Bound result 2305843009213693951\n\nSuite result: ok. 1 passed; 0 failed; 0 skipped; finished in 435.89ms (398.43ms CPU time)\n\nRan 6 test suites in 437.93ms (782.39ms CPU time): 48 tests passed, 0 failed, 1 skipped (49 total tests)\n","passed":true},{"durationMs":64,"exitCode":0,"name":"source-index","output":"{\"v\":1,\"entryPoints\":[\"DroneHook.afterSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),int256,bytes)\",\"DroneHook.beforeInitialize(address,(address,address,uint24,int24,address),uint160)\",\"DroneHook.beforeSwap(address,(address,address,uint24,int24,address),(bool,int256,uint160),bytes)\",\"DroneHook.sweep()\",\"DroneHook.unlockCallback(bytes)\",\"DroneToken.approve(address,uint256)\",\"DroneToken.transfer(address,uint256)\",\"DroneToken.transferFrom(address,address,uint256)\"],\"files\":{\".gitignore\":4,\"LICENSE\":24,\"README.md\":104,\"docs/DEPENDENCIES.md\":13,\"docs/SECURITY_REVIEW.md\":92,\"docs/VERIFICATION.md\":52,\"foundry.toml\":24,\"launch.json\":31,\"remappings.txt\":3,\"script/DeployDrone.sol\":47,\"src/DroneHook.sol\":179,\"src/DroneToken.sol\":60,\"src/HookFlags.sol\":30,\"src/SpecifiedAmount.sol\":101,\"test/DroneHook.t.sol\":272,\"test/DroneInvariant.t.sol\":81,\"test/DroneToken.t.sol\":77,\"test/MainnetFork.t.sol\":53,\"test/SpecifiedAmount.t.sol\":74,\"test/helpers/HookFixture.sol\":133,\"test/helpers/TestRouter.sol\":70,\"test/mocks/MockERC20.sol\":58,\"tools/check_bytecode.py\":18,\"tools/check_manifest.py\":17},\"excluded\":[\"lib/\",\"node_modules/\"],\"truncated\":false}","passed":true},{"durationMs":2877,"exitCode":0,"name":"slither","output":"[medium/high] incorrect-equality at src/DroneHook.sol:93: DroneHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/DroneHook.sol#93-125) uses a dangerous strict equality:\n[medium/high] incorrect-equality at src/DroneHook.sol:146: DroneHook._accrue(uint256) (src/DroneHook.sol#146-151) uses a dangerous strict equality:\n[medium/medium] reentrancy-no-eth at src/DroneHook.sol:159: Reentrancy in DroneHook.sweep() (src/DroneHook.sol#159-168):\n[medium/medium] unused-return at src/SpecifiedAmount.sol:24: SpecifiedAmount.filled(IPoolManager,PoolKey,SwapParams) (src/SpecifiedAmount.sol#24-75) ignores return value by (None,net) = manager.getTickLiquidity(id,next) (src/SpecifiedAmount.sol#62)\n[low/medium] reentrancy-events at src/DroneHook.sol:159: Reentrancy in DroneHook.sweep() (src/DroneHook.sol#159-168):\n[low/medium] reentrancy-events at src/DroneHook.sol:146: Reentrancy in DroneHook._accrue(uint256) (src/DroneHook.sol#146-151):\n[low/medium] timestamp at src/DroneHook.sol:146: DroneHook._accrue(uint256) (src/DroneHook.sol#146-151) uses timestamp for comparisons\n[low/medium] timestamp at src/DroneHook.sol:93: DroneHook.beforeSwap(address,PoolKey,SwapParams,bytes) (src/DroneHook.sol#93-125) uses timestamp for comparisons\n[low/medium] timestamp at src/DroneHook.sol:86: DroneHook.feeNow() (src/DroneHook.sol#86-91) uses timestamp for comparisons","passed":true},{"durationMs":468,"exitCode":0,"name":"aderyn","output":"[high] reentrancy-state-change at src/DroneHook.sol:162: Reentrancy: State change after external call\n[low] internal-function-used-once at src/HookFlags.sol:23: Internal Function Used Only Once\n[low] large-numeric-literal at src/DroneHook.sol:112: Large Numeric Literal (7 places)\n[low] literal-instead-of-constant at src/DroneHook.sol:112: Literal Instead of Constant (8 places)\n[low] missing-inheritance at src/DroneToken.sol:5: Missing Inheritance\n[low] require-revert-in-loop at src/SpecifiedAmount.sol:44: Loop Contains `require`/`revert`\n[low] unsafe-erc20-operation at src/DroneHook.sol:164: Unsafe ERC20 Operation","passed":true}],"detail":"all checks passed","evaluation":"checks","profile":"foundry","status":"accepted","submissionHash":"fc2e4808e191f70ffc4c4d65d5571506ef316291c58d0b0403412f3949e0ddce","verifiedTreeHash":"59bfe45cd9db55c49410ebf693d0ed037e04db37","verifierVersion":"0.1.0+be003835"}]}