{"assessments":[],"deployments":[],"fuzz":[],"identity":null,"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"04e1b70f-5556-4c9c-a275-3457badc9ebc","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"1074bdf426f958328ff7ab83b289539a77617a932e64b58bc4a467e3e1f59ec7","dependsOn":["write_foundry_tests"],"execution":null,"key":"adversarial_review","kind":"code","role":"review","skillHash":"f67f09092064995d5ca9427314dc2ed602731273825a019980e9a8ae5f240e1b","skillId":"adversarial-review","state":"accepted"},{"acceptedSubmissionHash":"2e25642756d08a4622d9fdd8c72bb835bda8d4bf6d0ec905f137d7340e2d97c1","dependsOn":["scaffold_project"],"execution":null,"key":"implement_contract","kind":"code","role":"implement","skillHash":"d37537abb20af46832b24602788d7895021ecaac20c6934313dc4f8aba4ad047","skillId":"implement-contract","state":"accepted"},{"acceptedSubmissionHash":"b432d545bcc417236f75227079886edcd3fc34a790e3162854e5eec2fc4cb82e","dependsOn":[],"execution":null,"key":"scaffold_project","kind":"code","role":"implement","skillHash":"4a09b43734a4e065a2747d46a5f94af939a9b38daffc4cea4670e09a41529c8c","skillId":"scaffold-project","state":"accepted"},{"acceptedSubmissionHash":"cc04a131a854eee9899f18c0abcfe11b152fde90d71a2410f0a5efe9f335bb83","dependsOn":["implement_contract"],"execution":null,"key":"write_foundry_tests","kind":"code","role":"tests","skillHash":"5891004c5c81aca68ad1dcc551c7bc98f3ec10c61fce5561aee8956a26e95588","skillId":"write-foundry-tests","state":"accepted"}],"objective":"A Uniswap v4 hook that charges a fixed 5% fee on every swap.","parentJobId":null,"planHash":"422857d63ba4e6f6d48046089df5fe57598c49e91ebe0bee13ef6d3c3c5158c8","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":null,"publication":{"commit":null,"deliveredAt":null,"repoUrl":null},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[],"site":null,"snapshotHash":"1b52d0e062917ffbd81d0953a49edb02934f2d297211cc169da5188db6372a1d","state":"completed","submissions":[{"artifacts":null,"attempt":2,"bundleHash":null,"deviceKey":"a1c5c6c3e93f5a311d26715fe81382674dca82117134c2e6f97c1bc5faea9f09","findings":[{"description":"deploy() runs HookMiner.find on chain, so it linearly searches salts while repeatedly hashing the hook creation code. Gas depends on the deployer and PoolManager addresses and is not bounded to a deployable transaction. In the repository's own test_ownerIsWhoeverCalledTheDeployer state, HookOwner.deployHook consumes 49,132,282 gas; Forge allows that because the test does not impose a production transaction/block gas cap. The tests therefore pass even though the supplied deployment path is unusable for that concrete address pair on a 30,000,000-gas chain.","line":25,"path":"src/FivePercentFeeHookDeployer.sol","reproduction":"After FivePercentFeeHookWithdrawTest.setUp(), create HookOwner exactly as test_ownerIsWhoeverCalledTheDeployer does and call ownerContract.deployHook{gas: 30_000_000}(manager). Expected: deploy() returns a hook at the required flag address. Actual: the call runs out of gas inside HookMiner.find; running the existing test with --gas-report shows the same deployHook call costs 49,132,282 gas when uncapped.","severity":"medium","title":"Runtime salt mining can make the provided deployer exceed the transaction gas limit"},{"description":"The hook hard-wires both authorization and the withdrawal recipient to owner. Because CREATE2 deployment through the supplied internal library makes the calling contract the owner, a deploying contract without receive()/payable fallback can call withdraw but cannot accept PoolManager.take's native transfer. There is no receiver parameter, ownership transfer, or alternate claim-token withdrawal, so native claims remain locked. The native-withdrawal test misses this by using an ETH-receivable test owner, while HookOwner explicitly includes receive().","line":174,"path":"src/FivePercentFeeHook.sol","reproduction":"Use a NonPayableOwner contract with deployHook(manager) and withdraw(currency) forwarding methods but no receive or fallback. Have it deploy the hook; initialize an address(0)/ERC20 pool at 1:1, add native liquidity, and execute oneForZero exact input -1e14 so accruedFees(address(0)) > 0 (the same swap state as test_withdrawingNativeCurrency). Call NonPayableOwner.withdraw(address(0)). Expected: the deployer receives all accrued native fees. Actual: PoolManager.take calls the nonpayable owner with ETH and reverts NativeTransferFailed; accruedFees and the hook's claims roll back and no other address can withdraw or redirect them.","severity":"medium","title":"Native fees are permanently unwithdrawable when the contract deployer cannot receive ETH"},{"description":"feeOn multiplies before dividing, so checked arithmetic reverts above (type(uint256).max - 99) / 5 even though the 5% result itself fits in uint256. This does not affect current swap deltas, which are int128-sized, but it makes the public helper violate its stated behavior for uint256 amounts. The fuzz tests conceal the boundary by bounding amount to 1e60.","line":97,"path":"src/FivePercentFeeHook.sol","reproduction":"Call hook.feeOn(type(uint256).max). Expected: ceil(type(uint256).max / 20) = 5789604461865809771178549250434395392663499233282028201972879200395656481997. Actual: Solidity panic 0x11 occurs at amount * FEE_NUMERATOR before division.","severity":"low","title":"feeOn reverts for valid uint256 inputs despite documenting the full nonnegative domain"}],"hash":"1074bdf426f958328ff7ab83b289539a77617a932e64b58bc4a467e3e1f59ec7","nodeId":"131959d8-8dc1-4d2d-8997-2cb247169cc9","outcome":"completed","summary":"Review complete. Wrote 3 severity-ranked, reproducible findings to [.imd-findings.json](/root/.identitymd/work/04e1b70f-5556-4c9c-a275-3457badc9ebc/131959d8-8dc1-4d2d-8997-2cb247169cc9/.imd-findings.json):\n\n- 2 medium: deployment gas exhaustion; locked native fees for nonpayable contract deployers.\n- 1 low: `feeOn(type(uint256).max)` overflow.\n\nNo implementation, test, or configuration files were changed.","treeHash":null,"usage":{"cachedInputTokens":1377024,"inputTokens":88356,"model":null,"outputTokens":14278,"runtime":"codex","turns":5,"wallClockMs":441939}},{"artifacts":null,"attempt":1,"bundleHash":"6ac64fe57649c7fba5f364400597be3261488897f725c4515ccd9e67fb24a266","deviceKey":"0edd2bbb66d2d014fbbda834d6ccbc278847c31414f601db126e7a1269baddd9","findings":[],"hash":"2e25642756d08a4622d9fdd8c72bb835bda8d4bf6d0ec905f137d7340e2d97c1","nodeId":"d9d73127-55fb-4a0e-add8-0c693320207f","outcome":"completed","summary":"`forge build` is clean (no warnings from `src/`), and all 20 tests pass.\n\n## What I delivered\n\n**`src/FivePercentFeeHook.sol`** — a v4 hook charging a fixed 5% on every swap, in both directions, on every pool it's attached to. `FEE_NUMERATOR = 5` / `FEE_DENOMINATOR = 100` are `constant`: no constructor arg, no setter, no per-pool value. `owner = msg.sender` in the constructor; only that address can `withdraw(Currency)`.\n\n**`src/FivePercentFeeHookDeployer.sol`** — an `internal` library that mines the salt with v4-periphery's `HookMiner` and CREATE2-deploys. Because it's `internal` it inlines into the caller, so `address(this)` is the CREATE2 deployer the salt is mined against *and* `msg.sender` in the constructor, keeping \"whoever deploys can withdraw\" true.\n\n## The three findings\n\nI could not reproduce them literally — this working tree is the scaffold, not my earlier submission (`HEAD` is an empty commit; only `src/Placeholder.sol` exists). So I treated them as design constraints and wrote the hook to satisfy them, then reproduced each reported *scenario* in `test/scratch/`:\n\n- **Rounding bypass (high)** — real. Fee is now `ceil(amount * 5 / 100)`, so every non-zero swap pays ≥ 1 base unit and slicing an order can only raise the total. Measured: 100 × 19-unit swaps pay **100**, one 1900-unit swap pays **95**. `ceil(5a/100) ≤ a` for all `a`, so the fee can never push the trader's side below zero.\n- **Fee on requested vs filled (high)** — real, and inherent to charging in `beforeSwap`. The fee moved to `afterSwap`, computed from `swapDelta`. Since v4 only lets `afterSwap` keep the *unspecified* currency, it's 5% of the output on exact-input and 5% of the input on exact-output. Measured on the reported repro: exact-output at `sqrtPrice-1` now gives `amount0 = -3, amount1 = 0` with the fee in currency0 — the buyer of currency1 never pays currency1. Exact-input at the same limit gives `amount0 = -2` and zero fee, not `-5e16`.\n- **`take` before settlement (medium, advisory)** — I judged it real. The fee is settled with `poolManager.mint` of an ERC-6909 claim, which only moves the hook's own delta; `withdraw` burns the claim and `take`s real tokens in a separate unlock. Measured on the reported repro (manager holding 3 units of currency0): the swap now partially fills at `amount0 = -5, amount1 = 1` instead of reverting with `Panic(0x11)`.\n\nAlso covered in scratch: native-ETH pools, two pools sharing one hook, `accruedFees[c] == poolManager.balanceOf(hook, c.toId())`, owner-only withdrawal, and every swap size from 1 to 25 units.\n\n## One thing I did not do\n\nI left `src/Placeholder.sol` in place. Deleting it, as \"replace the placeholder\" suggests, breaks `forge build`: `test/Placeholder.t.sol` imports it, and `test/` is outside my writable paths.\n\n```\nError (6275): Source \"src/Placeholder.sol\" not found\n --> test/Placeholder.t.sol:7:1\n```\n\nSince \"leave `forge build` passing\" is an acceptance criterion and touching `test/` is not permitted, I kept the file. If the harness drops `test/Placeholder.t.sol` when it pairs my `src/` with the test author's tests, `src/Placeholder.sol` can be deleted at that point — nothing in the hook references it.","treeHash":"4f77090743cd6f362a83c1143a980587608310df","usage":{"cachedInputTokens":4639448,"inputTokens":98,"model":"claude-opus-5","outputTokens":58936,"runtime":"claude","turns":55,"wallClockMs":789589}},{"artifacts":null,"attempt":1,"bundleHash":"f59c816529bb6b9a60304b32092bf9829cca9ca90f279c7afbfb784b04768b22","deviceKey":"0edd2bbb66d2d014fbbda834d6ccbc278847c31414f601db126e7a1269baddd9","findings":[{"description":"`feeAmount = specifiedAmount * 5 / 100` truncates, and it truncates independently on every `beforeSwap`. Any specified amount below 20 base units therefore costs nothing, and nothing aggregates across calls, so a trader can move unbounded volume through the pool for free by slicing it into 19-unit swaps. The contract's own documentation calls this 'the only case in which no fee is charged', but the case is not confined to dust: it is reachable for any order size at the cost of extra gas, and for a low-decimal or high-value-per-base-unit currency (a 6-decimal stablecoin, a 0-decimal or 2-decimal token, WBTC) 19 base units is not dust. This contradicts the contract's stated invariant of a fixed 5% on every swap. Charging nothing on a single 19-unit swap is ordinary integer truncation; charging nothing on the aggregate is the defect. Fixing it needs accrual of the truncated remainder (carry the sub-unit dust per currency, or round the fee up once it is non-zero), which cannot be done from a test.","line":115,"path":"src/FivePercentFeeHook.sol","reproduction":"Setup: PoolManager, a 1:1 pool (fee tier 3000, tickSpacing 60) with the hook attached and a full-range position of 1e21 liquidity, so neither swap is liquidity-constrained.\n\nA) Execute 100 exact-input zeroForOne swaps with `amountSpecified = -19` and `sqrtPriceLimitX96 = MIN_SQRT_PRICE + 1`. 1,900 base units of currency0 move through the pool. Measured: `accruedFees[currency0] == 0` and `currency0.balanceOf(hook) == 0`.\n\nB) On the same pool, one exact-input zeroForOne swap with `amountSpecified = -1900`. Measured: `accruedFees[currency0] == 95`.\n\nExpected: the same 1,900 units of volume are charged the same fixed 5% however they are sliced, i.e. 95 units in both cases (or at minimum an aggregate that cannot be driven to zero). Actual: 95 units when swapped whole, 0 units when split — a complete bypass, not a rounding difference.","severity":"high","title":"Rounding the fee down on every callback lets an order split into sub-20-unit swaps pay no fee at all"},{"description":"`_beforeSwap` computes the fee from `params.amountSpecified` and `take`s it before the pool has run, so the charge is on what the trader asked for, not on what the pool filled. Whenever `sqrtPriceLimitX96` (or exhausted liquidity) stops the swap short, the fee is unbounded as a fraction of the executed swap. In the exact-output case it inverts the trader's position: the buyer of a currency ends up paying that currency and receiving none of it. `PoolSwapTest`'s own checks do not catch this — a negative `deltaAfter1` still satisfies `deltaAfter1 <= amountSpecified` — so the swap settles and the trader's balance really falls. A price limit is the standard slippage protection every router sets, so this is reachable in normal use rather than by a contrived caller. The fix is to charge on the executed amount (an `afterSwap` fee, or a `beforeSwap` charge reconciled afterwards), which is a change to the hook's design and cannot be tested around.","line":128,"path":"src/FivePercentFeeHook.sol","reproduction":"Setup: PoolManager, a 1:1 pool (fee tier 3000, tickSpacing 60) with the hook attached and a full-range position of 1e21 liquidity. Read `sqrtPriceX96` from `StateLibrary.getSlot0`.\n\nA) Exact output, zeroForOne, `amountSpecified = 1e18`, `sqrtPriceLimitX96 = sqrtPriceX96 - 1`. Measured: `BalanceDelta.amount0() == -2`, `BalanceDelta.amount1() == -50000000000000000`, `accruedFees[currency1] == 5e16`, and the trader's currency1 balance falls by 5e16. Expected: a trader buying currency1 never owes currency1; with the pool delivering none of it, the fee on an executed amount of ~0 is ~0.\n\nB) Exact input, zeroForOne, `amountSpecified = -1e18`, same `sqrtPriceLimitX96 = sqrtPriceX96 - 1`. Measured: `BalanceDelta.amount0() == -50000000000000002`, `BalanceDelta.amount1() == 0`, `accruedFees[currency0] == 5e16` — the trader pays 5.0000000000000002e16 currency0 and receives zero currency1. Expected: 5% of the ~2 base units that actually executed, not 5% of the 1e18 that did not.","severity":"high","title":"The fee is charged on the amount requested rather than the amount filled, so a price-limited swap pays the fee and can receive nothing"},{"description":"`poolManager.take(feeCurrency, address(this), feeAmount)` runs in `beforeSwap`, before the trader has settled anything, so it moves real tokens the PoolManager must already hold. On an exact-input swap the fee currency is the currency the trader is about to supply, so the swap's success depends on unrelated inventory — other pools' reserves in that currency — rather than on the trader's funds. When that inventory is smaller than the fee, the ERC20 transfer underflows and the entire swap reverts, even though the same swap against the same pool without the hook fills partially and settles fine. The contract's comment frames this as binding 'only on swaps far larger than the liquidity they are swapping against', but that includes the first buy into any newly seeded or one-sided pool, which is exactly when a swap most needs to work. `poolManager.mint` of an ERC-6909 claim (redeemed after settlement) would not depend on pre-existing inventory; that is a change to the hook.","line":128,"path":"src/FivePercentFeeHook.sol","reproduction":"Setup: a fresh PoolManager whose only pool is a 1:1 hooked pool (fee tier 3000, tickSpacing 60) with `modifyLiquidity(tickLower: -60, tickUpper: 60, liquidityDelta: 1e3)`. Measured PoolManager balance afterwards: 3 base units of currency0. The trader is funded with far more than 1e18 of currency0 and has approved the router.\n\nSubmit an exact-input zeroForOne swap, `amountSpecified = -1e18`, `sqrtPriceLimitX96 = MIN_SQRT_PRICE + 1`. The hook computes `feeAmount = 5e16` and calls `poolManager.take(currency0, hook, 5e16)`; the manager holds 3, so the ERC20 transfer reverts with `Panic(0x11)`, surfaced as `CustomRevert.WrappedError` (selector 0x90bfb865) and the whole swap reverts.\n\nControl: the identical pool with no hook, same manager shape, same swap. Measured: it partially fills — `BalanceDelta.amount0() == -5`, `BalanceDelta.amount1() == 2`.\n\nExpected: the hooked swap also partially fills, settling out of the input the trader supplies and charging 5% of what executed. Actual: no swap executes at all.","severity":"medium","title":"Taking the fee out of the PoolManager before the trader settles makes otherwise valid swaps revert"}],"hash":"7b3efe8c6cfa399060890a8282232d5017b0ff84df6b8dfd40c64a6375ccef69","nodeId":"3bd4d4b7-7580-4286-bdd0-b5e7d8a1abd5","outcome":"completed","summary":"All three reported defects reproduce. `forge build` and `forge test` pass — 43 tests, 0 failures.\n\n## What was in the tree\n\nThe working tree at HEAD contains only `src/FivePercentFeeHook.sol` and the scaffold's `Placeholder` test — the reopened test files were not present. So the suite is written fresh rather than edited, and none of the three tests the reviewer named (`test_defect_splittingIntoSubThresholdSwapsAvoidsTheFeeEntirely`, the partial-fill tests, `test_swapLargerThanTheManagerCanCoverReverts`) exist in what I'm submitting.\n\n## Tests (`test/**`)\n\n- `test/utils/FeeHookFixture.sol` — deploys the hook from a `HookDeployer` contract, so \"the deployer\" is an identity distinct from the test contract and non-owner withdrawal tests actually mean something. Sets up a deep hooked pool plus an identical hookless pool at the same fee tier as a reference.\n- `test/FivePercentFeeHook.fees.t.sol` (18) — exact input and exact output in both directions, each asserting three things together: the `accruedFees` book, the hook's real token balance, and how much of the swap reached the pool (read off the hookless mirror, so no hardcoded output amounts). Plus cumulative accrual, two pools sharing one pot, the LP fee tier not being overridden, liquidity/donations charging nothing, `beforeSwap` rejecting non-PoolManager callers, zero-amount swaps, a fuzz over amounts and directions, and the rounding-to-zero boundary (19 → 0, 20 → 1).\n- `test/FivePercentFeeHook.withdraw.t.sol` (14) — deployer withdrawal, partial and repeated withdrawal, and the failure paths: a non-deployer (fuzzed over callers), the PoolManager itself, withdrawing more than accrued, withdrawing a currency that never accrued, withdrawing twice, and tokens gifted directly to the hook staying unwithdrawable.\n- `test/FivePercentFeeHook.native.t.sol` (9) — the ETH path: fee arriving via `receive`, native withdrawal, a recipient that rejects ETH reverting the withdrawal, and forced ETH not becoming withdrawable.\n\n## Findings (`.imd-findings.json`)\n\nI reproduced each report before acting on it; all three hold, so none is asserted as correct anywhere in the suite:\n\n1. **high** — Per-call flooring lets 100 swaps of `-19` move 1,900 units for `accruedFees[currency0] == 0`, where one `-1900` swap accrues 95.\n2. **high** — The fee is charged on the requested amount, not the filled one. Exact output `1e18` at `sqrtPriceX96 - 1` returns `amount1() == -5e16` and the buyer's currency1 balance *falls* by 5e16. I also confirmed the exact-input twin the reviewer didn't mention: `-1e18` at the same limit pays 5.0000000000000002e16 currency0 for zero currency1, and included it in the same finding.\n3. **medium** — `take` before settlement. On a fresh manager whose only pool holds 3 base units of currency0, a `-1e18` exact-input swap reverts (`WrappedError` wrapping `Panic(0x11)`); the identical hookless pool fills partially at `-5 / +2`.\n\nThe rounding tests the task asks for are scoped to single-swap truncation and say so in their doc comments — the aggregate consequence is in the findings file, not blessed by an assertion.","treeHash":"ae2a92e1957f6b7d48d49c3b31b1983b3be38c8d","usage":{"cachedInputTokens":4048851,"inputTokens":92,"model":"claude-opus-5","outputTokens":62757,"runtime":"claude","turns":51,"wallClockMs":1241833}},{"artifacts":null,"attempt":1,"bundleHash":null,"deviceKey":"a1c5c6c3e93f5a311d26715fe81382674dca82117134c2e6f97c1bc5faea9f09","findings":[{"description":"The fee is rounded down independently on every callback. An attacker can therefore split arbitrary volume into swaps whose specified amount is 19 base units and pay no hook fee at all. This violates the fixed-5% claim and is economically material for low-decimal or high-value-per-base-unit currencies. `test_defect_splittingIntoSubThresholdSwapsAvoidsTheFeeEntirely` asserts that the bypass succeeds, so the suite passes rather than rejecting this behavior.","line":115,"path":"src/FivePercentFeeHook.sol","reproduction":"In a sufficiently liquid hooked pool, execute 100 exact-input zeroForOne swaps with amountSpecified = -19. For each call, 19 * 5 / 100 truncates to 0, so after moving 1,900 input base units accruedFees[currency0] is 0. On an equivalent fresh pool, one exact-input zeroForOne swap with amountSpecified = -1900 accrues 95 currency0 units. Expected: the split 1,900-unit order is charged the same 95-unit fixed fee (or otherwise cannot reduce the aggregate fee to zero); actual: it pays 0.","severity":"high","title":"Splitting an order into sub-20-unit swaps bypasses the fee entirely"},{"description":"The hook takes 5% of the requested amount before knowing how much the pool fills, and the returned specified delta charges that full amount even when the price limit stops the swap immediately. Consequently the fee can be arbitrarily larger than 5% of the executed swap; for exact output it can reverse the user's output-side delta. The partial-fill tests assert this harmful result (including a negative output delta), so they would not fail the implementation for it.","line":128,"path":"src/FivePercentFeeHook.sol","reproduction":"At a 1:1 initialized hooked pool with deep liquidity, submit an exact-output zeroForOne swap with amountSpecified = 1e18 and sqrtPriceLimitX96 = currentSqrtPriceX96 - 1. Expected: a fixed 5% fee is based on the amount actually filled, and a buyer does not owe currency1 when the pool delivers no currency1. Actual: the hook immediately takes 5e16 currency1, the pool delivers 0 currency1 at that limit, the returned BalanceDelta.amount1 is -5e16, and the trader's currency1 balance falls by 5e16 despite trying to buy it.","severity":"high","title":"A partial exact-output swap can make the buyer pay the output currency while receiving nothing"},{"description":"For exact-input swaps, the fee currency is the token the trader is about to supply, but `poolManager.take` tries to transfer the fee from the manager before that input is settled. The swap therefore depends on unrelated pre-existing manager inventory and can fail even though the trader can fund the full input. This also prevents the first fee-bearing buy when a one-sided pool leaves the manager with no input currency. `test_swapLargerThanTheManagerCanCoverReverts` expects the revert, so it normalizes rather than catches the defect.","line":128,"path":"src/FivePercentFeeHook.sol","reproduction":"Initialize a hooked 1:1 full-range pool with liquidityDelta = 1e3 and no other pool using its currencies, leaving the PoolManager with roughly 1e3 currency0. Submit an exact-input zeroForOne swap with amountSpecified = -1e18 from a trader funded for 1e18. The computed fee is 5e16, so `poolManager.take(currency0, hook, 5e16)` fails because the manager has less than 5e16 before settlement and the whole swap reverts. Expected: as in the same hookless pool, the swap can partially fill and settle from the trader's supplied input while charging 5% of what executes; actual: no swap executes.","severity":"medium","title":"Taking the input fee before settlement makes otherwise valid exact-input swaps revert"}],"hash":"af38a5d7068792acbd78aaff419a4b791e3624869d594f7213239e60828d2766","nodeId":"131959d8-8dc1-4d2d-8997-2cb247169cc9","outcome":"completed","summary":"Completed the adversarial review and wrote 3 ranked, reproducible findings to [.imd-findings.json](/root/.identitymd/work/04e1b70f-5556-4c9c-a275-3457badc9ebc/131959d8-8dc1-4d2d-8997-2cb247169cc9/.imd-findings.json).\n\nFindings: 2 high severity, 1 medium severity. No repository files were modified besides the required findings file.","treeHash":null,"usage":{"cachedInputTokens":576768,"inputTokens":60603,"model":null,"outputTokens":10601,"runtime":"codex","turns":4,"wallClockMs":262613}},{"artifacts":null,"attempt":1,"bundleHash":"0e1dcb23c8eb589b84f10e9d69c30794dc257ad5e485ef3faae39d95920a45e8","deviceKey":"0edd2bbb66d2d014fbbda834d6ccbc278847c31414f601db126e7a1269baddd9","findings":[],"hash":"b432d545bcc417236f75227079886edcd3fc34a790e3162854e5eec2fc4cb82e","nodeId":"133fa867-cb63-4c93-8a1f-814c3345048d","outcome":"completed","summary":"Done. Committed as `a7a69e0`.\n\n## What landed\n\n```\nfoundry.toml        solc 0.8.26 pin, cancun, bytecode_hash=none, explicit remappings\nsrc/Placeholder.sol imports v4-core's Hooks library\ntest/Placeholder.t.sol\nlib/                6 dependencies, 580 files, ordinary committed blobs\nREADME.md\n.gitignore\n```\n\n`forge build` and `forge test` both pass (2 tests). `forge fmt --check` is clean.\n\n## Vendoring\n\nNo submodules, no gitlinks, no manifest — verified with `git ls-tree -r HEAD` finding zero mode-`160000` entries. Pins are the set the Uniswap repos pin *each other* at, so it's a combination upstream tests together:\n\n| lib | pin |\n|---|---|\n| forge-std | `77041d2` (v1.9.7) |\n| v4-core | `59d3ecf` (v1.0.2) |\n| v4-periphery | `3779387` |\n| openzeppelin-contracts | `dbb6104` (v5.0.2) |\n| permit2 | `cc56ad0` |\n| solmate | `4b47a19` |\n\nTwo judgment calls worth flagging:\n\n**v4-periphery is pinned behind `main` deliberately.** Commit `5da22e6` moved `BaseHook` and `HookMiner` out of the repo entirely; `main` has no `src/utils/`. I pinned `3779387`, the last commit that still ships both, since the next task writes a hook. It pins the same v4-core and permit2 as `main` does, so nothing is lost.\n\n**Each library is vendored source-only** — Solidity plus licence and README. Audit PDFs, docs, JS test tooling and broadcast artifacts are ~35MB of the ~41MB upstream and compile to nothing; dropping them puts the bundle at 701K. v4-core's and v4-periphery's `test/` dirs are kept, because hook tests are built on `v4-core/test/utils/Deployers.sol`. Because that removes the nested `remappings.txt` files, `foundry.toml` sets `auto_detect_remappings = false` and lists all nine prefixes explicitly.\n\n## Verification beyond the required commands\n\nThe delivered tree was exported with `git archive` into a non-git directory and built there, which is the verifier's situation. In that clean room I also compiled and ran a full hook: a `BaseHook` subclass deployed to a `HookMiner`-mined CREATE2 address, pool initialized, liquidity added, real swap executed, `beforeSwap` callback observed firing. That exercises v4-core src+test, v4-periphery src, solmate, OZ and forge-std together, and confirms `PoolManager` deploys under these optimizer settings. A second scratch file forced every remapping prefix to resolve. Both proofs lived in `test/scratch/` and `/tmp` and are not in the commit.\n\nsolc 0.8.26 was already cached in this image (Feb 2025), not downloaded by me — so the offline re-run should find it. That's the one external thing the build wants, and the README says so.\n\nOne limitation I did not paper over: `via_ir` is off. Hooks and the v4-core harness are fine, but v4-periphery's `PositionManager` and `PositionDescriptor` only compile with it on. I tried per-file `compilation_restrictions` mirroring upstream's profiles and backed it out — a single file importing both contracts fails with \"incompatible settings restrictions,\" which is a worse failure than the one it fixes. It's documented in the README instead, since a later task can't edit `foundry.toml`.","treeHash":"3276dab8331d7bfab0bf34ddb8dac18485ccc995","usage":{"cachedInputTokens":4620786,"inputTokens":116,"model":"claude-opus-5","outputTokens":56419,"runtime":"claude","turns":72,"wallClockMs":917584}},{"artifacts":null,"attempt":1,"bundleHash":"1207c4661b1920d0c81c7343e55fc600b32b549b372191c606f961b3207cd5f5","deviceKey":"0edd2bbb66d2d014fbbda834d6ccbc278847c31414f601db126e7a1269baddd9","findings":[{"description":"`_beforeSwap` computes `feeAmount` from `params.amountSpecified` and `take`s it out of the PoolManager before `Pool.swap` runs, so the charge is fixed against what the caller asked for rather than against what the pool ended up trading. Any swap that terminates early -- because it reached its `sqrtPriceLimitX96`, or because it is an exact-output order for more than the pool can supply -- still pays 5% of the full request. The effective rate on the trade that actually executed is unbounded, and on the exact-output side the trader's delta in the currency they were trying to *buy* can go negative: they pay that currency out and receive nothing. The NatSpec on lines 95-97 records this as a known consequence, but it contradicts the stated requirement of a fixed 5% fee on every swap, and there is no guard or refund for the unfilled remainder. A correct charge would be 5% of the executed `BalanceDelta`, which requires moving the accounting to `afterSwap` (with `afterSwapReturnDelta`) or refunding the difference. Reachability: this needs a swap that does not fill completely. v4-periphery's `V4Router` passes non-binding `MIN_SQRT_PRICE + 1` / `MAX_SQRT_PRICE - 1` limits and bounds slippage with amountOutMinimum/amountInMaximum afterwards, so through that router an under-fill usually reverts instead; but `sqrtPriceLimitX96` is a first-class parameter of `IPoolManager.swap` and any integrator that binds it (custom routers, arbitrage executors, v4-core's own `PoolSwapTest`) is exposed. Demonstrated by `test_defect_exactInput_feeChargedOnUnfilledAmount`, `test_defect_exactOutput_partialFillLeavesTraderPayingTheOutputCurrency`, `test_defect_ordinarySlippageLimitStillPaysTheFullFee` and `test_defect_exactOutputBeyondPoolCapacity_overchargesWithNoPriceLimit` in test/FivePercentFeeHook.t.sol, which record the current behaviour rather than assert it is correct.","line":115,"path":"src/FivePercentFeeHook.sol","reproduction":"Setup: PoolManager, pool (currency0/currency1, fee 3000, tickSpacing 60) with the hook, initialised at SQRT_PRICE_1_1, full-range liquidity 1e21.\n\n(a) EXACT INPUT WITH A BINDING PRICE LIMIT. Call `manager.swap(key, {zeroForOne: true, amountSpecified: -1e18, sqrtPriceLimitX96: spot - 1}, \"\")`.\n  actual:   trader's BalanceDelta = (amount0 = -50000000000000002, amount1 = 0); hook.accruedFees(currency0) = 50000000000000000. The pool absorbed 2 base units; the trader paid 5e16 and received nothing at all.\n  expected: 5% of the 2 units actually swapped, i.e. 0.\n\n(b) EXACT OUTPUT WITH A BINDING PRICE LIMIT. Same pool, `{zeroForOne: true, amountSpecified: +1e18, sqrtPriceLimitX96: spot - 1}`.\n  actual:   BalanceDelta = (amount0 = -2, amount1 = -50000000000000000); hook.accruedFees(currency1) = 50000000000000000. The trader asked to buy 1e18 of currency1 and instead paid out 5e16 of currency1, receiving nothing.\n  expected: the trader is never made to pay the currency they are buying; fee = 5% of the ~0 that filled.\n\n(c) ORDINARY SLIPPAGE GUARD. Pool with full-range liquidity 1e15 (deep hookless pool alongside it so the manager can cover the take). `{zeroForOne: true, amountSpecified: -1e18, sqrtPriceLimitX96: spot * 995 / 1000}` (~1% below spot).\n  actual:   trader pays 50005040246367243 of currency0, of which 50000000000000000 is the hook fee and 5040246367243 was actually swapped; receives 5000000000000 of currency1. Effective fee rate on the executed trade ~992,000%.\n  expected: 5% of 5040246367243, i.e. 252012318367.\n\n(d) NO PRICE LIMIT AT ALL. Pool with full-range liquidity ~1e15, `sqrtPriceLimitX96 = MIN_SQRT_PRICE + 1` (what v4-periphery's router passes), exact output `amountSpecified: +2e15` -- more currency1 than the pool holds.\n  actual:   pool fills ~999999999999977, hook takes 100000000000000 (5% of the 2e15 requested), trader nets 899999999999977. The fee is ~10% of the swap that executed.\n  expected: 5% of the ~1e15 that executed, i.e. ~5e13.","severity":"medium","title":"Fee is charged on the requested amount, not the amount the swap actually filled, so a partially-filled swap is overcharged without bound"},{"description":"`feeAmount = specifiedAmount * 5 / 100` rounds down, and lines 116-118 then return a zero delta, so every swap specifying fewer than 20 base units of the specified currency is free. Because the threshold is absolute rather than proportional, the same volume routed as N swaps of 19 units pays nothing while one swap of 19N units pays the full 5%. Rounding down in the payer's favour is normal, but the hook has no minimum-fee floor and no `if (feeAmount == 0) revert`, so the 'fixed 5% on every swap' guarantee has a hole in it. For an 18-decimal token gas makes the split uneconomic, which is why this is low rather than higher; for a token with few decimals, or one whose base unit carries real value, it is worth more. Demonstrated by `test_defect_splittingIntoSubThresholdSwapsAvoidsTheFeeEntirely` in test/FivePercentFeeHook.t.sol, which records the behaviour rather than asserts it is correct.","line":115,"path":"src/FivePercentFeeHook.sol","reproduction":"Setup: pool (currency0/currency1, fee 3000, tickSpacing 60) with the hook, SQRT_PRICE_1_1, full-range liquidity 1e21.\n\nIssue 10 exact-input swaps of `amountSpecified = -19` (zeroForOne).\n  actual:   hook.accruedFees(currency0) = 0. 190 base units moved through the pool for no fee.\nThen issue one exact-input swap of `amountSpecified = -190`.\n  actual:   hook.accruedFees(currency0) = 9.\n  expected: the same volume should cost the same either way -- 9 (or 10) in both cases, or the sub-threshold swap should revert rather than be free.\n\nThreshold: any `abs(amountSpecified) <= 19` is free, in both directions and for both exact-input and exact-output; `abs(amountSpecified) == 20` is the first that charges (1 unit).","severity":"low","title":"Fee truncates to zero below 20 base units, so an order split into sub-threshold pieces pays nothing"},{"description":"`poolManager.take(feeCurrency, address(this), feeAmount)` runs in `beforeSwap`, before the swapper has settled anything, so the PoolManager has to already hold `feeAmount` of the fee currency. When 5% of the specified amount exceeds the manager's balance of that currency the transfer fails and the whole swap reverts inside `Hooks.callHook`. Without the hook the same swap simply fills against whatever liquidity exists. This binds only when the manager's total balance of the currency is small -- i.e. a new token whose only pools use this hook -- so it is an availability issue rather than a loss, but it is a behavioural difference integrators would not expect and it is not bounded by anything the caller controls except order size. Charging in `afterSwap`, or clamping the take to the manager's balance, would avoid it. Covered by `test_swapLargerThanTheManagerCanCoverReverts` in test/FivePercentFeeHook.t.sol.","line":128,"path":"src/FivePercentFeeHook.sol","reproduction":"Setup: PoolManager, pool (currency0/currency1, fee 3000, tickSpacing 60) with the hook, SQRT_PRICE_1_1, full-range liquidity 1e3, and no other pool using currency0 -- so the manager holds ~1000 base units of currency0.\n\nCall `swap(key, zeroForOne: true, amountSpecified: -1e18, sqrtPriceLimitX96: MIN_SQRT_PRICE + 1)`.\n  actual:   reverts. `_beforeSwap` computes feeAmount = 5e16 and calls `poolManager.take`, whose ERC20 transfer fails for want of balance; the failure is wrapped and rethrown by `Hooks.callHook`. hook.accruedFees(currency0) stays 0.\n  expected: the same swap against the same pool without the hook succeeds (it fills against the available liquidity at a poor price), so with the hook it should fill and be charged 5%, not revert.","severity":"low","title":"Taking the fee before the swap makes swaps revert that plain v4 would have filled"}],"hash":"bee7bbc8d0f7c74ae362c31ee119b2dbdcc1d159a68520da12ebd05d1a0ae2d9","nodeId":"3bd4d4b7-7580-4286-bdd0-b5e7d8a1abd5","outcome":"completed","summary":"Done. 50 tests, all passing, `forge build` clean. Nothing outside `test/**` was touched (plus the requested `.imd-findings.json`, which the repo's `.git/info/exclude` already anticipates alongside `test/scratch/`).\n\n## Test suites\n\n**`test/FivePercentFeeHookBase.sol`** — shared wiring. The hook is deployed through a separate `HookDeployer` contract, so `owner` is neither the test contract nor an EOA prank; the test contract is itself a non-owner.\n\n**`FivePercentFeeHookSwapTest`** (13) — the fee is proved taken, not just plausible: a second pool over the same currency pair with identical liquidity and *no hook* is the control. An exact-input swap of `n` through the hook pool must produce exactly the output the hookless pool gives for `n − 5%`; an exact-output swap for `n` must cost exactly what the hookless pool charges for `n + 5%`. Both directions, both exactness modes, plus PoolManager/trader/hook balance deltas, event emission, permission bits, and `beforeSwap` rejecting a non-PoolManager caller.\n\n**`FivePercentFeeHookWithdrawTest`** (13) — deployer withdraws (full, partial, zero, then keeps accruing); stranger / the test contract / the PoolManager / any fuzzed non-owner rejected; over-withdraw, double-withdraw and never-accrued currency all revert on the arithmetic; stray tokens transferred in are unreachable; an invariant fuzz that the ledger stays fully backed across mixed swaps and withdrawals.\n\n**`FivePercentFeeHookNativeTest`** (10) — the native path end to end, including withdrawal to an address that reverts on receipt.\n\n**`FivePercentFeeHookRoundingTest`** (6) and **`...ThinPoolTest`** (2) — the sub-20-unit rounding threshold in both directions and both modes, and the take-before-settle revert.\n\n## Three defects reported rather than blessed\n\nThe four `test_defect_*` tests record current behaviour with comments saying so and pointing at the findings file; they do not assert it is correct.\n\n1. **medium** — the fee comes off `params.amountSpecified`, not the executed swap. With `sqrtPriceLimitX96 = spot − 1`, an exact-input swap of `1e18` has the trader pay `50000000000000002` and receive **zero**; the exact-output version gives a delta of `−5e16` in the currency they were trying to *buy*. Under an ordinary ~1% slippage guard the effective rate on the executed trade is ~992,000%. Reachability is stated honestly: v4-periphery's router passes non-binding limits, so this bites callers who bind `sqrtPriceLimitX96` themselves.\n2. **low** — floor rounding: 10 swaps of 19 units pay 0, the same 190 in one swap pays 9.\n3. **low** — `take` before settle makes a thin-pool swap revert that plain v4 fills. I verified the counterfactual: the identical hookless pool fills (`−1e18` in, `995` out) where the hook pool reverts.","treeHash":"db66b688c767be3a01f446eb14af3873ea5ee291","usage":{"cachedInputTokens":6163909,"inputTokens":104,"model":"claude-opus-5","outputTokens":96238,"runtime":"claude","turns":59,"wallClockMs":1200902}},{"artifacts":null,"attempt":2,"bundleHash":"b74ec14ce001f2380a832c2acce572d6e6f6115acff94bf38367e53868ef62e9","deviceKey":"0edd2bbb66d2d014fbbda834d6ccbc278847c31414f601db126e7a1269baddd9","findings":[],"hash":"cc04a131a854eee9899f18c0abcfe11b152fde90d71a2410f0a5efe9f335bb83","nodeId":"3bd4d4b7-7580-4286-bdd0-b5e7d8a1abd5","outcome":"completed","summary":"Done. Final state: `forge build` clean, `forge test` = **58 passed, 0 failed** (56 new + the 2 pre-existing placeholder tests), and `git status` shows only `test/` paths added.\n\n## The three reported defects do not reproduce\n\nThe hook in `src/` was rewritten in commit `bccd9e8` (after the review that produced those reports). The current code charges the fee in `afterSwap` on the **executed** `swapDelta`, rounds it **up**, and settles it by **minting ERC-6909 claims** instead of `poolManager.take`. I reproduced each report's exact scenario before writing anything:\n\n| Report | Reproduction attempt | Result |\n|---|---|---|\n| [high] sub-20-unit swaps bypass the fee | 100 × exact-input `-19` vs one `-1900`, same pool | Split pays **100** base units, single order pays **95**. Slicing costs more, not zero. |\n| [high] partial exact-output makes the buyer pay the output currency | exact-output `1e18`, `sqrtPriceLimit = current - 1` | `delta.amount1() == 0` (never negative), trader's currency1 balance unchanged, fee is 1 unit (5% of the 2 units that executed) — not 5e16 of the request. |\n| [med] `take` before settlement reverts valid swaps | one-sided pool (liquidity all below price, manager holds **0** currency0), exact-output zeroForOne | Swap succeeds, 5% accrues in currency0 — the currency the manager had none of. |\n\nSince none reproduce and I found no new defect, I did **not** write `.imd-findings.json`.\n\n## Tests\n\n`test/utils/FeeHookFixture.sol` runs every test against two identical pools — one hooked, one hookless — so the control pool says what the pool alone would have done and the fee is never measured by the hook's own arithmetic (`_fivePercentRoundedUp` is written out independently rather than calling `hook.feeOn`). Swaps come from a `trader` that is neither the LP nor the owner.\n\n- **`FivePercentFeeHook.t.sol`** (18) — 5% of output on exact input and 5% of input on exact output, both directions, with balances, deltas, events and claim tokens checked; accrual across swaps and pools; the fee comes off the trader, not the LP; fuzzed rate checks; `afterSwap` unreachable except from the PoolManager; zero-amount swaps; liquidity/donate accrue nothing.\n- **`FivePercentFeeHookRounding.t.sol`** (11) — the rounding-toward-zero case: a 1-unit exact-input swap pays no fee **and delivers nothing**, and fuzzing shows fee == 0 ⇔ nothing delivered. Plus the 19-unit swap, and slicing in three shapes.\n- **`FivePercentFeeHookWithdraw.t.sol`** (14) — deployer withdraws (per currency, native included, twice → `NothingToWithdraw`); non-deployer rejected as EOA, as a contract, as the trader/manager/router/hook, with the accrual proven untouched afterwards; ownership follows whoever called the deployer library; `unlockCallback` unreachable directly.\n- **`FivePercentFeeHookPartialFills.t.sol`** (8) — the two high-severity scenarios above, partial fills charged on what executed, thin/empty/one-sided pools.\n- **`FivePercentFeeHookInvariants.t.sol`** (5) — random swap sequences: accrual == claims == what the swaps were seen to pay, and no swap ever pays under the rate.\n\n## Evidence the suite isn't vacuous\n\nI rebuilt the three defective implementations in scratch and ran the suite against each: the truncating-fee version fails **19** tests, the charge-on-the-request version **16**, the `take`-before-settle version **28** — each including the specific regression test written for it.\n\nOne behaviour worth flagging, which I tested rather than reported: rounding up means a swap whose gross output is 1–19 base units pays more than 5% (at output 1, the fee is that whole unit). It is capped at one base unit and is the deliberate cost of closing the slicing bypass, so `test_smallestSwapThatDeliversAnythingIsCharged` documents it instead of treating it as a defect.","treeHash":"32bd9ac8cf2f36e52154bb4026ade84bafbdc62e","usage":{"cachedInputTokens":4061767,"inputTokens":72,"model":"claude-opus-5","outputTokens":91601,"runtime":"claude","turns":43,"wallClockMs":1177029}},{"artifacts":null,"attempt":1,"bundleHash":"238c457e04dc8080343eda4a77d8fa6a5d9aff68c22a1dd0939375e6151ee8c4","deviceKey":"0edd2bbb66d2d014fbbda834d6ccbc278847c31414f601db126e7a1269baddd9","findings":[],"hash":"f206e65336d78c8b4f15fba2e306bfd328a694a59a00bacf7ae6758e64a0cda5","nodeId":"d9d73127-55fb-4a0e-add8-0c693320207f","outcome":"completed","summary":"Done. `src/FivePercentFeeHook.sol` is the hook.\n\n**How it works.** Permissions are `beforeSwap` + `beforeSwapReturnDelta`, so the address must carry `BEFORE_SWAP_FLAG | BEFORE_SWAP_RETURNS_DELTA_FLAG` in its low bits; `BaseHook`'s constructor enforces that. On every swap it computes `feeAmount = |amountSpecified| * 5 / 100`, `take`s that from the PoolManager in the swap's *specified* currency, and returns it as a positive `deltaSpecified` — which nets the hook's currency delta back to zero and shifts the cost onto the swapper. That one expression covers both directions and both swap types:\n\n- exact input — specified currency is the input; the pool swaps 95%, the hook keeps 5%, the swapper still pays the full input.\n- exact output — specified currency is the output; the pool is driven for 105% of the request, the hook keeps the extra, the swapper receives exactly what it asked for.\n\n5% is `FEE_NUMERATOR / FEE_DENOMINATOR`, both `constant` — no setter, no constructor arg, no per-pool storage. `owner` is `msg.sender` at construction (the deployer) and is the only address that can call `withdraw`, which is bounded by the per-currency `accruedFees` ledger so stray transfers aren't withdrawable through it. There's a `receive()` because a native-currency `take` arrives as a bare value transfer.\n\n**Verification.** I wrote 14 tests in `test/scratch/` (deleted, as intended) covering both directions × exact-in/exact-out, accumulation across swaps, dust rounding to zero, owner/non-owner/over-accrual withdrawal, a native-ETH pool, and a fuzz over the specified amount. All passed. Addresses were mined with `HookMiner.find(address(this), flags, type(FivePercentFeeHook).creationCode, abi.encode(manager))`. A clean `forge build` succeeds with no warnings, `forge test` passes, and `forge fmt --check src/` is clean.\n\nTwo behaviours the tests surfaced are documented in the `_beforeSwap` natspec rather than papered over: the 5% is on the *specified* amount, so a partially-filled swap still pays 5% of what it asked for; and because the `take` precedes settlement, a swap whose fee exceeds the PoolManager's balance of that currency reverts — that balance is every v4 pool's reserves in it, so it only binds on swaps far larger than the liquidity they trade against.\n\n**One thing I could not do.** The task says to replace the placeholder, but `test/Placeholder.t.sol` imports `src/Placeholder.sol`, and `test/` is outside my writable paths. I confirmed by deleting it: `forge build` fails with `Source \"src/Placeholder.sol\" not found` from `test/Placeholder.t.sol:7`. Since \"forge build succeeds\" is an acceptance criterion, I left `src/Placeholder.sol` untouched and added the hook alongside it. Deleting `src/Placeholder.sol` and `test/Placeholder.t.sol` together is a one-line follow-up for whoever owns `test/`.","treeHash":"ad410e9f3c88d62816c71314153280a761aa0cd2","usage":{"cachedInputTokens":2399408,"inputTokens":64,"model":"claude-opus-5","outputTokens":39190,"runtime":"claude","turns":39,"wallClockMs":529148}}],"verification":[{"checks":[{"durationMs":889,"exitCode":0,"name":"build","output":"Compiling 46 files with Solc 0.8.26\nSolc 0.8.26 finished in 796.69ms\nCompiler run successful!\n","passed":true},{"durationMs":97,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/Placeholder.t.sol:PlaceholderTest\n[PASS] test_beforeSwapFlag_matchesV4Core() (gas: 8342)\n[PASS] test_hasBeforeSwapPermission_readsAddressBits() (gas: 9545)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 498.10µs (292.39µs CPU time)\n\nRan 1 test suite in 3.84ms (498.10µs CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":null,"profile":null,"status":"accepted","submissionHash":"2e25642756d08a4622d9fdd8c72bb835bda8d4bf6d0ec905f137d7340e2d97c1","verifiedTreeHash":"4f77090743cd6f362a83c1143a980587608310df","verifierVersion":"0.1.0+e28c470a"},{"checks":[{"durationMs":3304,"exitCode":0,"name":"build","output":"Compiling 94 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.20s\nCompiler run successful!\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHook.native.t.sol:73:48\n   │\n73 │         BalanceDelta delta = swapNative(true, -int256(amount), amount);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value\n    ╭▸ test/FivePercentFeeHook.withdraw.t.sol:163:9\n    │\n163 │         MockERC20(Currency.unwrap(currency0)).transfer(address(hook), gift);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHook.native.t.sol:77:36\n   │\n77 │         assertEq(delta.amount0(), -int256(amount), \"swapper paid the full specified input\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHook.native.t.sol:85:28\n   │\n85 │         swapNative(false, -int256(amount), 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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHook.fees.t.sol:72:60\n   │\n72 │         BalanceDelta delta = swapUnlimited(hookKey, true, -int256(amount));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHook.native.t.sol:98:27\n   │\n98 │         swapNative(false, int256(amount), 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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHook.fees.t.sol:82:36\n   │\n82 │         assertEq(delta.amount0(), -int256(amount), \"swapper is charged the full specified input\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHook.fees.t.sol:87:64\n   │\n87 │         BalanceDelta hookless = swapUnlimited(plainKey, true, -int256(amount - 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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:105:61\n    │\n105 │         BalanceDelta delta = swapUnlimited(hookKey, false, -int256(amount));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:111:36\n    │\n111 │         assertEq(delta.amount1(), -int256(amount), \"swapper is charged the full specified input\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:115:65\n    │\n115 │         BalanceDelta hookless = swapUnlimited(plainKey, false, -int256(amount - 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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:123:39\n    │\n123 │         swapUnlimited(hookKey, true, -int256(amount));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:124:40\n    │\n124 │         swapUnlimited(hookKey, false, -int256(amount));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:145:59\n    │\n145 │         BalanceDelta delta = swapUnlimited(hookKey, true, int256(amount));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:151:35\n    │\n151 │         assertEq(delta.amount1(), int256(amount), \"swapper receives exactly the output it asked for\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:156:63\n    │\n156 │         BalanceDelta hookless = swapUnlimited(plainKey, true, int256(amount + 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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:158:39\n    │\n158 │         assertTrue(delta.amount0() < -int256(amount), \"swapper paid more than the output it received\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:167:60\n    │\n167 │         BalanceDelta delta = swapUnlimited(hookKey, false, int256(amount));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:172:35\n    │\n172 │         assertEq(delta.amount0(), int256(amount), \"swapper receives exactly the output it asked for\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:174:64\n    │\n174 │         BalanceDelta hookless = swapUnlimited(plainKey, false, int256(amount + 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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:247:45\n    │\n247 │         swapUnlimited(hookKey, zeroForOne, -int256(amount));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:272:60\n    │\n272 │         BalanceDelta delta = swapUnlimited(hookKey, true, -int256(amount));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.fees.t.sol:278:64\n    │\n278 │         BalanceDelta hookless = swapUnlimited(plainKey, true, -int256(amount));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\n","passed":true},{"durationMs":302,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/Placeholder.t.sol:PlaceholderTest\n[PASS] test_beforeSwapFlag_matchesV4Core() (gas: 8342)\n[PASS] test_hasBeforeSwapPermission_readsAddressBits() (gas: 9545)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 424.03µs (155.06µs CPU time)\n\nRan 9 tests for test/FivePercentFeeHook.native.t.sol:FivePercentFeeHookNativeTest\n[PASS] test_deployerWithdrawsNativeFees() (gas: 194986)\n[PASS] test_ethSentDirectlyToTheHookIsNotWithdrawable() (gas: 182953)\n[PASS] test_exactInputOneForZero_accruesTheErc20Side() (gas: 188894)\n[PASS] test_exactInputZeroForOne_accruesNativeFee() (gas: 172427)\n[PASS] test_exactOutputOneForZero_accruesNativeFee() (gas: 163976)\n[PASS] test_nativeAndErc20FeesAreBookedSeparately() (gas: 312250)\n[PASS] test_nativeSwapBelowTheRoundingThresholdIsNotCharged() (gas: 133334)\n[PASS] test_nonDeployerCannotWithdrawNativeFees() (gas: 169754)\n[PASS] test_withdrawingNativeToARecipientThatRejectsEthReverts() (gas: 238306)\nSuite result: ok. 9 passed; 0 failed; 0 skipped; finished in 156.93ms (3.78ms CPU time)\n\nRan 14 tests for test/FivePercentFeeHook.withdraw.t.sol:FivePercentFeeHookWithdrawTest\n[PASS] testFuzz_onlyTheDeployerCanWithdraw(address,uint256) (runs: 256, μ: 25937, ~: 25937)\n[PASS] test_deployerCannotWithdrawMoreThanAccrued() (gas: 33526)\n[PASS] test_deployerWithdrawsAccruedFees() (gas: 55814)\n[PASS] test_everythingAccruedCanBeWithdrawn() (gas: 92957)\n[PASS] test_feesAccrueAgainAfterAFullWithdrawal() (gas: 181189)\n[PASS] test_nonDeployerCannotWithdraw() (gas: 23120)\n[PASS] test_nonDeployerCannotWithdrawEvenZero() (gas: 10972)\n[PASS] test_partialWithdrawalsLeaveTheRemainderWithdrawable() (gas: 63255)\n[PASS] test_poolManagerCannotWithdraw() (gas: 15326)\n[PASS] test_theSameFeeCannotBeWithdrawnTwice() (gas: 52773)\n[PASS] test_tokensSentDirectlyToTheHookAreNotWithdrawable() (gas: 71772)\n[PASS] test_withdrawalOfOneCurrencyLeavesTheOtherAlone() (gas: 65930)\n[PASS] test_withdrawingFromACurrencyThatNeverAccruedReverts() (gas: 787410)\n[PASS] test_withdrawingZeroIsANoOp() (gas: 35381)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 187.98ms (17.78ms CPU time)\n\nRan 18 tests for test/FivePercentFeeHook.fees.t.sol:FivePercentFeeHookFeesTest\n[PASS] testFuzz_exactInputFeeIsAlwaysFivePercentRoundedDown(uint256,bool) (runs: 256, μ: 194496, ~: 198435)\nLogs:\n  Bound result 38695715580981390069\n\n[PASS] test_beforeSwapRejectsCallersOtherThanThePoolManager() (gas: 28791)\n[PASS] test_bothDirectionsAreChargedTheSameRate() (gas: 338437)\n[PASS] test_exactInputOneForZero_takesFivePercentOfTheInput() (gas: 303488)\n[PASS] test_exactInputZeroForOne_takesFivePercentOfTheInput() (gas: 320281)\n[PASS] test_exactOutputBelowTheRoundingThresholdIsNotCharged() (gas: 148403)\n[PASS] test_exactOutputOneForZero_takesFivePercentOfTheOutput() (gas: 296926)\n[PASS] test_exactOutputZeroForOne_takesFivePercentOfTheOutput() (gas: 309949)\n[PASS] test_feeRateIsAFixedFivePercent() (gas: 9946)\n[PASS] test_feesAccumulateAcrossSwapsAndAreNeverOverstated() (gas: 351479)\n[PASS] test_feesFromDifferentPoolsAccrueToTheSamePot() (gas: 550355)\n[PASS] test_hookAddressCarriesExactlyTheDeclaredPermissions() (gas: 14514)\n[PASS] test_liquidityAndDonationsAreNotCharged() (gas: 257280)\n[PASS] test_ownerIsTheDeployerNotThePoolManager() (gas: 15932)\n[PASS] test_swapBelowTheRoundingThresholdIsNotCharged() (gas: 253069)\n[PASS] test_theHookDoesNotOverrideThePoolsLpFee() (gas: 544779)\n[PASS] test_theRoundingThresholdIsTwentyUnits() (gas: 262413)\n[PASS] test_zeroAmountSwapIsRejectedAndChargesNothing() (gas: 62817)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 204.43ms (35.98ms CPU time)\n\nRan 4 test suites in 205.44ms (549.76ms CPU time): 43 tests passed, 0 failed, 0 skipped (43 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":null,"profile":null,"status":"accepted","submissionHash":"7b3efe8c6cfa399060890a8282232d5017b0ff84df6b8dfd40c64a6375ccef69","verifiedTreeHash":"ae2a92e1957f6b7d48d49c3b31b1983b3be38c8d","verifierVersion":"0.1.0+e28c470a"},{"checks":[],"detail":"paths and tree verified; no suite was run for this kind of work","evaluation":null,"profile":null,"status":"accepted","submissionHash":"b432d545bcc417236f75227079886edcd3fc34a790e3162854e5eec2fc4cb82e","verifiedTreeHash":"3276dab8331d7bfab0bf34ddb8dac18485ccc995","verifierVersion":"0.1.0+e28c470a"},{"checks":[{"durationMs":4092,"exitCode":0,"name":"build","output":"Compiling 94 files with Solc 0.8.26\nSolc 0.8.26 finished in 3.99s\nCompiler run successful!\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookNative.t.sol:45:54\n   │\n45 │         BalanceDelta delta = _swapNative(key, true, -int256(amountIn), 2e18);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[erc20-unchecked-transfer]: ERC20 'transfer' and 'transferFrom' calls should check the return value\n    ╭▸ test/FivePercentFeeHookWithdraw.t.sol:201:9\n    │\n201 │         MockERC20(Currency.unwrap(currency0)).transfer(address(hook), 1e18);\n    │         ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n    │\n    ╰ help: https://book.getfoundry.sh/reference/forge/forge-lint#erc20-unchecked-transfer\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:108:47\n    │\n108 │         BalanceDelta delta = swap(key, true, -int256(amountIn), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:111:36\n    │\n111 │         assertEq(delta.amount0(), -int128(int256(amountIn)), \"swapper did not pay the full specified input\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:111:43\n    │\n111 │         assertEq(delta.amount0(), -int128(int256(amountIn)), \"swapper did not pay the full specified input\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookNative.t.sol:47:36\n   │\n47 │         assertEq(delta.amount0(), -int128(int256(amountIn)), \"trader did not pay the full specified input\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookNative.t.sol:47:43\n   │\n47 │         assertEq(delta.amount0(), -int128(int256(amountIn)), \"trader did not pay the full specified input\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookNative.t.sol:66:54\n   │\n66 │         BalanceDelta delta = _swapNative(key, false, int256(amountOut), 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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookNative.t.sol:68:35\n   │\n68 │         assertEq(delta.amount0(), int128(int256(amountOut)), \"trader did not receive the requested output\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookNative.t.sol:68:42\n   │\n68 │         assertEq(delta.amount0(), int128(int256(amountOut)), \"trader did not receive the requested 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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookNative.t.sol:83:34\n   │\n83 │         _swapNative(key, false, -int256(amountIn), 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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookNative.t.sol:95:32\n   │\n95 │         _swapNative(key, true, int256(amountOut), 5e18);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:127:53\n    │\n127 │         BalanceDelta refDelta = swap(refKey, true, -int256(amountIn - expectedFee), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:142:48\n    │\n142 │         BalanceDelta delta = swap(key, false, -int256(amountIn), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:144:36\n    │\n144 │         assertEq(delta.amount1(), -int128(int256(amountIn)), \"swapper did not pay the full specified input\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:144:43\n    │\n144 │         assertEq(delta.amount1(), -int128(int256(amountIn)), \"swapper did not pay the full specified input\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHookWithdraw.t.sol:227:52\n    │\n227 │             int256 amountSpecified = exactInput ? -int256(amount) : int256(amount);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHookWithdraw.t.sol:227:69\n    │\n227 │             int256 amountSpecified = exactInput ? -int256(amount) : int256(amount);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:157:54\n    │\n157 │         BalanceDelta refDelta = swap(refKey, false, -int256(amountIn - expectedFee), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:175:46\n    │\n175 │         BalanceDelta delta = swap(key, true, int256(amountOut), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:179:35\n    │\n179 │         assertEq(delta.amount1(), int128(int256(amountOut)), \"swapper did not receive the requested output\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:179:42\n    │\n179 │         assertEq(delta.amount1(), int128(int256(amountOut)), \"swapper did not receive the requested 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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:190:52\n    │\n190 │         BalanceDelta refDelta = swap(refKey, true, int256(amountOut + expectedFee), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:205:47\n    │\n205 │         BalanceDelta delta = swap(key, false, int256(amountOut), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:207:35\n    │\n207 │         assertEq(delta.amount0(), int128(int256(amountOut)), \"swapper did not receive the requested output\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:207:42\n    │\n207 │         assertEq(delta.amount0(), int128(int256(amountOut)), \"swapper did not receive the requested 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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:216:53\n    │\n216 │         BalanceDelta refDelta = swap(refKey, false, int256(amountOut + expectedFee), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:244:30\n    │\n244 │             swap(key, true, -int256(i * 1e17), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:268:32\n    │\n268 │         swap(key, zeroForOne, -int256(amountIn), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:280:31\n    │\n280 │         swap(key, zeroForOne, int256(amountOut), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:346:48\n    │\n346 │         int256 amountSpecified = exactInput ? -int256(amount) : int256(amount);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:346:65\n    │\n346 │         int256 amountSpecified = exactInput ? -int256(amount) : int256(amount);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:366:30\n    │\n366 │             swap(key, true, -int256(pieceSize), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:370:26\n    │\n370 │         swap(key, true, -int256(pieces * pieceSize), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:441:36\n    │\n441 │         assertEq(delta.amount1(), -int128(int256(fee)), \"expected the trader to owe the output currency\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:441:43\n    │\n441 │         assertEq(delta.amount1(), -int128(int256(fee)), \"expected the trader to owe the output currency\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:481:29\n    │\n481 │         swap(thinKey, true, int256(requested), ZERO_BYTES);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\n","passed":true},{"durationMs":375,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/Placeholder.t.sol:PlaceholderTest\n[PASS] test_beforeSwapFlag_matchesV4Core() (gas: 8342)\n[PASS] test_hasBeforeSwapPermission_readsAddressBits() (gas: 9545)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 567.17µs (144.34µs CPU time)\n\nRan 10 tests for test/FivePercentFeeHookNative.t.sol:FivePercentFeeHookNativeTest\n[PASS] test_exactInput_nativeIn_takesFivePercentInEther() (gas: 191939)\n[PASS] test_exactInput_oneForZero_takesTheFeeInCurrency1() (gas: 189216)\n[PASS] test_exactOutput_nativeOut_takesFivePercentInEther() (gas: 174956)\n[PASS] test_exactOutput_zeroForOne_takesTheFeeInCurrency1() (gas: 202468)\n[PASS] test_nativeAndErc20FeesAccrueSideBySide() (gas: 390454)\n[PASS] test_nativeSwapUnder20Wei_isFree() (gas: 149525)\n[PASS] test_withdraw_moreNativeThanAccrued_reverts() (gas: 183456)\n[PASS] test_withdraw_nativeFees_byDeployer() (gas: 204216)\n[PASS] test_withdraw_nativeFees_byStranger_reverts() (gas: 183612)\n[PASS] test_withdraw_nativeToARecipientThatRejectsEther_reverts() (gas: 228502)\nSuite result: ok. 10 passed; 0 failed; 0 skipped; finished in 160.26ms (5.71ms CPU time)\n\nRan 2 tests for test/FivePercentFeeHook.t.sol:FivePercentFeeHookThinPoolTest\n[PASS] test_smallSwapOnAThinPoolStillCharges() (gas: 198335)\n[PASS] test_swapLargerThanTheManagerCanCoverReverts() (gas: 71333)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 195.17ms (444.53µs CPU time)\n\nRan 6 tests for test/FivePercentFeeHook.t.sol:FivePercentFeeHookRoundingTest\n[PASS] testFuzz_swapsUnder20BaseUnits_chargeNothing(uint256,bool,bool) (runs: 256, μ: 150830, ~: 149590)\nLogs:\n  Bound result 2\n\n[PASS] test_bothDirections_below20Units_areFree() (gas: 345318)\n[PASS] test_defect_splittingIntoSubThresholdSwapsAvoidsTheFeeEntirely() (gas: 5917111)\n[PASS] test_exactInput_at20Units_chargesOneBaseUnit() (gas: 201902)\n[PASS] test_exactInput_below20Units_isFree() (gas: 161315)\n[PASS] test_exactOutput_below20Units_isFree() (gas: 155102)\nSuite result: ok. 6 passed; 0 failed; 0 skipped; finished in 204.53ms (64.24ms CPU time)\n\nRan 4 tests for test/FivePercentFeeHook.t.sol:FivePercentFeeHookPartialFillTest\n[PASS] test_defect_exactInput_feeChargedOnUnfilledAmount() (gas: 192731)\n[PASS] test_defect_exactOutputBeyondPoolCapacity_overchargesWithNoPriceLimit() (gas: 558576)\n[PASS] test_defect_exactOutput_partialFillLeavesTraderPayingTheOutputCurrency() (gas: 208576)\n[PASS] test_defect_ordinarySlippageLimitStillPaysTheFullFee() (gas: 209219)\nSuite result: ok. 4 passed; 0 failed; 0 skipped; finished in 208.70ms (2.07ms CPU time)\n\nRan 13 tests for test/FivePercentFeeHook.t.sol:FivePercentFeeHookSwapTest\n[PASS] testFuzz_exactInput_feeIsAlwaysFloorOfFivePercent(uint256,bool) (runs: 256, μ: 192161, ~: 197059)\nLogs:\n  Bound result 2\n\n[PASS] testFuzz_exactOutput_feeIsAlwaysFloorOfFivePercent(uint256,bool) (runs: 256, μ: 193330, ~: 197673)\nLogs:\n  Bound result 9045816471738160107\n\n[PASS] test_beforeSwap_revertsWhenCallerIsNotThePoolManager() (gas: 36558)\n[PASS] test_exactInput_oneForZero_takesFivePercentOfInputInCurrency1() (gas: 304295)\n[PASS] test_exactInput_zeroForOne_takesFivePercentOfInputInCurrency0() (gas: 317351)\n[PASS] test_exactOutput_oneForZero_takesFivePercentOfOutputInCurrency0() (gas: 303378)\n[PASS] test_exactOutput_zeroForOne_takesFivePercentOfOutputInCurrency1() (gas: 312783)\n[PASS] test_feeRateIsFivePercentAndConstant() (gas: 9403)\n[PASS] test_feesAccrueAcrossDirectionsAndSwapKinds() (gas: 474437)\n[PASS] test_feesAccrueOverRepeatedSwaps() (gas: 483158)\n[PASS] test_hookAddressEncodesExactlyBeforeSwapPermissions() (gas: 17355)\n[PASS] test_ownerIsTheDeployerAndNotTheTestContract() (gas: 11767)\n[PASS] test_swapsThroughAPoolWithoutTheHookAccrueNothing() (gas: 234201)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 232.16ms (110.54ms CPU time)\n\nRan 13 tests for test/FivePercentFeeHookWithdraw.t.sol:FivePercentFeeHookWithdrawTest\n[PASS] testFuzz_accruedFeesAreAlwaysBackedByTheHooksBalance(uint256) (runs: 256, μ: 700335, ~: 698782)\nLogs:\n  Bound result 3252793207628818519\n  Bound result 7040745206613020906\n  Bound result 5375024004816233492\n  Bound result 986121399214858701\n  Bound result 3158423586193542291\n  Bound result 6989741397677562635\n\n[PASS] testFuzz_withdraw_anyCallerThatIsNotTheDeployer_reverts(address,uint256) (runs: 256, μ: 340636, ~: 340949)\nLogs:\n  Bound result 96644950891\n\n[PASS] test_withdraw_byDeployer_movesTokensAndClearsAccrual() (gas: 337704)\n[PASS] test_withdraw_byStranger_reverts() (gas: 341981)\n[PASS] test_withdraw_byTheDeployersDeployer_reverts() (gas: 334030)\n[PASS] test_withdraw_byThePoolManager_reverts() (gas: 336597)\n[PASS] test_withdraw_cannotReachTokensThatDidNotAccrue() (gas: 356904)\n[PASS] test_withdraw_currencyThatNeverAccrued_reverts() (gas: 834663)\n[PASS] test_withdraw_moreThanAccrued_reverts() (gas: 340838)\n[PASS] test_withdraw_partialThenTheRest() (gas: 341171)\n[PASS] test_withdraw_thenKeepAccruing() (gas: 439851)\n[PASS] test_withdraw_twiceOverTheSameFee_reverts() (gas: 326886)\n[PASS] test_withdraw_zeroAmount_isANoOp() (gas: 347287)\nSuite result: ok. 13 passed; 0 failed; 0 skipped; finished in 278.06ms (169.38ms CPU time)\n\nRan 7 test suites in 279.15ms (1.28s CPU time): 50 tests passed, 0 failed, 0 skipped (50 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":null,"profile":null,"status":"accepted","submissionHash":"bee7bbc8d0f7c74ae362c31ee119b2dbdcc1d159a68520da12ebd05d1a0ae2d9","verifiedTreeHash":"db66b688c767be3a01f446eb14af3873ea5ee291","verifierVersion":"0.1.0+e28c470a"},{"checks":[{"durationMs":5149,"exitCode":0,"name":"build","output":"Compiling 97 files with Solc 0.8.26\nSolc 0.8.26 finished in 5.03s\nCompiler run successful!\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookInvariants.t.sol:55:48\n   │\n55 │         int256 amountSpecified = exactInput ? -int256(amount) : int256(amount);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookPartialFills.t.sol:59:23\n   │\n59 │         assertLt(fee, uint256(requested) * 5 / 100, \"fee was charged on the requested amount\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookPartialFills.t.sol:82:35\n   │\n82 │         assertGe(delta.amount0(), int128(int256(requested)), \"trader paid more than they offered\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHook.t.sol:98:65\n   │\n98 │         assertEq(currency0.balanceOf(trader), traderIn0Before - uint256(-amountIn), \"wrong input debited\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHookPartialFills.t.sol:118:31\n    │\n118 │         assertLt(executedOut, uint256(requested), \"the control pool filled the whole request\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHookPartialFills.t.sol:128:23\n    │\n128 │         assertLt(fee, uint256(requested) * 5 / 100, \"fee was charged on the requested amount\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:130:65\n    │\n130 │         assertEq(currency1.balanceOf(trader), traderIn1Before - uint256(-amountIn), \"wrong input debited\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:162:35\n    │\n162 │         assertEq(delta.amount1(), int128(int256(amountOut)), \"trader did not receive what they asked for\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:166:65\n    │\n166 │         assertEq(currency1.balanceOf(trader), traderIn1Before + uint256(amountOut), \"wrong output credited\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:191:35\n    │\n191 │         assertEq(delta.amount0(), int128(int256(amountOut)), \"trader did not receive what they asked for\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookInvariants.t.sol:55:65\n   │\n55 │         int256 amountSpecified = exactInput ? -int256(amount) : int256(amount);\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookRounding.t.sol:94:59\n   │\n94 │             BalanceDelta control = _swap(plainKey, true, -int256(amountIn));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHookRounding.t.sol:101:54\n    │\n101 │         BalanceDelta delta = _swap(hookedKey, true, -int256(amountIn));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHookPartialFills.t.sol:141:30\n    │\n141 │         assertLt(executedIn, uint256(-requested), \"the control pool consumed the whole request\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookInvariants.t.sol:80:42\n   │\n80 │             uint256 gross = exactInput ? uint256(int256(unspecified)) + fee : uint256(-int256(unspecified)) - fee;\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:195:65\n    │\n195 │         assertEq(currency0.balanceOf(trader), traderIn0Before + uint256(amountOut), \"wrong output credited\");\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n   ╭▸ test/FivePercentFeeHookInvariants.t.sol:80:79\n   │\n80 │             uint256 gross = exactInput ? uint256(int256(unspecified)) + fee : uint256(-int256(unspecified)) - fee;\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHookRounding.t.sol:141:44\n    │\n141 │         _swap(hookedKey, true, sliceSize * int256(slices));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:304:61\n    │\n304 │         BalanceDelta control = _swap(plainKey, zeroForOne, -int256(amountIn));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:307:60\n    │\n307 │         BalanceDelta delta = _swap(hookedKey, zeroForOne, -int256(amountIn));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:321:60\n    │\n321 │         BalanceDelta control = _swap(plainKey, zeroForOne, int256(amountOut));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHook.t.sol:324:59\n    │\n324 │         BalanceDelta delta = _swap(hookedKey, zeroForOne, int256(amountOut));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHookRounding.t.sol:173:55\n    │\n173 │         BalanceDelta control = _swap(plainKey, true, -int256(amountIn));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\nwarning[unsafe-typecast]: typecasts that can truncate values should be checked\n    ╭▸ test/FivePercentFeeHookRounding.t.sol:176:54\n    │\n176 │         BalanceDelta delta = _swap(hookedKey, true, -int256(amountIn));\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://book.getfoundry.sh/reference/forge/forge-lint#unsafe-typecast\n\n","passed":true},{"durationMs":695,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/Placeholder.t.sol:PlaceholderTest\n[PASS] test_beforeSwapFlag_matchesV4Core() (gas: 8342)\n[PASS] test_hasBeforeSwapPermission_readsAddressBits() (gas: 9545)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 636.61µs (280.36µs CPU time)\n\nRan 8 tests for test/FivePercentFeeHookPartialFills.t.sol:FivePercentFeeHookPartialFillsTest\n[PASS] testFuzz_exactOutputNeverReversesTheOutputSide(uint256) (runs: 256, μ: 216122, ~: 216720)\nLogs:\n  Bound result 61600847338969204736841588814\n\n[PASS] test_exactInputStoppedAtThePriceLimitIsNotChargedOnTheRequest() (gas: 235835)\n[PASS] test_exactOutputStoppedAtThePriceLimitIsNotChargedOnTheRequest() (gas: 296552)\n[PASS] test_firstSwapIntoAThinlySeededPoolSucceeds() (gas: 2987447)\n[PASS] test_partialExactInputFillIsChargedOnTheExecutedAmount() (gas: 304117)\n[PASS] test_partialExactOutputFillIsChargedOnTheExecutedAmount() (gas: 304339)\n[PASS] test_swapIntoAPoolWithNoLiquidityChargesNothing() (gas: 2654956)\n[PASS] test_swapSucceedsWhenTheManagerHoldsNoneOfTheFeeCurrency() (gas: 2631766)\nSuite result: ok. 8 passed; 0 failed; 0 skipped; finished in 281.14ms (120.62ms CPU time)\n\nRan 18 tests for test/FivePercentFeeHook.t.sol:FivePercentFeeHookTest\n[PASS] testFuzz_exactInputAlwaysPaysCeilingOfFivePercent(uint256,bool) (runs: 256, μ: 298212, ~: 295801)\nLogs:\n  Bound result 16442129789553820684\n\n[PASS] testFuzz_exactOutputAlwaysPaysCeilingOfFivePercent(uint256,bool) (runs: 256, μ: 300180, ~: 296564)\nLogs:\n  Bound result 16442129789553820684\n\n[PASS] test_afterSwapCannotBeCalledByAnyoneButThePoolManager() (gas: 216321)\n[PASS] test_exactInputOneForZero_takesFivePercentOfTheOutput() (gas: 307673)\n[PASS] test_exactInputZeroForOne_takesFivePercentOfTheOutput() (gas: 320713)\n[PASS] test_exactOutputOneForZero_takesFivePercentOfTheInput() (gas: 309642)\n[PASS] test_exactOutputZeroForOne_takesFivePercentOfTheInput() (gas: 318985)\n[PASS] test_feeDoesNotComeOutOfTheLiquidity() (gas: 695372)\n[PASS] test_feesAccrueOverManySwapsInBothCurrencies() (gas: 938776)\n[PASS] test_feesAreHeldAsClaimsNotTokens() (gas: 214092)\n[PASS] test_feesFromEveryPoolShareOnePerCurrencyBalance() (gas: 558629)\n[PASS] test_hookAddressEncodesAfterSwapAndReturnDeltaAndNothingElse() (gas: 6215)\n[PASS] test_liquidityAndDonateAccrueNothing() (gas: 334402)\n[PASS] test_ownerIsTheDeployer() (gas: 8742)\n[PASS] test_permissionsAreAfterSwapOnly() (gas: 16950)\n[PASS] test_rateIsFivePercent() (gas: 9469)\n[PASS] test_unrequestedHookEntrypointsRevert() (gas: 18535)\n[PASS] test_zeroAmountSwapReverts() (gas: 64752)\nSuite result: ok. 18 passed; 0 failed; 0 skipped; finished in 281.12ms (128.52ms CPU time)\n\nRan 11 tests for test/FivePercentFeeHookRounding.t.sol:FivePercentFeeHookRoundingTest\n[PASS] testFuzz_feeNeverExceedsTheAmountItIsChargedOn(uint256) (runs: 256, μ: 12240, ~: 12113)\nLogs:\n  Bound result 114\n\n[PASS] testFuzz_feeOfAnyRepresentableSwapFitsInInt128(uint256) (runs: 256, μ: 12327, ~: 12094)\nLogs:\n  Bound result 114\n\n[PASS] testFuzz_feeOnIsTheCeilingOfFivePercent(uint256) (runs: 256, μ: 14659, ~: 14549)\nLogs:\n  Bound result 19509670632609410370123143940\n\n[PASS] testFuzz_zeroFeeOnlyWhenNothingWasDelivered(uint256) (runs: 256, μ: 299463, ~: 302585)\nLogs:\n  Bound result 114\n\n[PASS] test_feeOnRoundsUp() (gas: 20517)\n[PASS] test_nineteenUnitSwapIsCharged() (gas: 305286)\n[PASS] test_smallestSwapThatDeliversAnythingIsCharged() (gas: 394713)\n[PASS] test_splittingExactOutputSwapsCannotAvoidTheFee() (gas: 3461754)\n[PASS] test_splittingIntoNineteenUnitSwapsCannotAvoidTheFee() (gas: 6927148)\n[PASS] test_splittingOneForZeroCannotAvoidTheFee() (gas: 3451580)\n[PASS] test_swapTooSmallToDeliverAnythingPaysNoFeeAndReceivesNothing() (gas: 232182)\nSuite result: ok. 11 passed; 0 failed; 0 skipped; finished in 281.14ms (168.65ms CPU time)\n\nRan 5 tests for test/FivePercentFeeHookInvariants.t.sol:FivePercentFeeHookInvariantsTest\n[PASS] invariant_accrualIsExactlyWhatTheSwapsPaid() (runs: 8, calls: 256, reverts: 0)\n\n╭--------------------+----------+-------+---------+----------╮\n| Contract           | Selector | Calls | Reverts | Discards |\n+============================================================+\n| FeeHookSwapHandler | swap     | 256   | 0       | 0        |\n╰--------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 2786\n  Bound result 3659\n  Bound result 2762\n  Bound result 10359994987449670\n  Bound result 4\n  Bound result 1\n  Bound result 735722125971779104\n  Bound result 887200\n  Bound result 60\n  Bound result 680743847645493146\n  Bound result 44343\n  Bound result 999999999999999999\n  Bound result 219047676658003279\n  Bound result 10905\n  Bound result 223967182980544679\n  Bound result 30495963257775203\n  Bound result 7656725545084281\n  Bound result 282877131233321575\n  Bound result 81998358918916928\n  Bound result 10624\n  Bound result 1000000000000000000\n  Bound result 799131311107716507\n  Bound result 1000000000000000000\n  Bound result 75\n  Bound result 1000000\n  Bound result 12391\n  Bound result 23654640991767066\n  Bound result 20968028\n  Bound result 50\n  Bound result 3026\n  Bound result 975932652015417119\n  Bound result 3001\n\n[PASS] invariant_accrualMatchesTheClaimTokensTheHookHolds() (runs: 8, calls: 256, reverts: 0)\n\n╭--------------------+----------+-------+---------+----------╮\n| Contract           | Selector | Calls | Reverts | Discards |\n+============================================================+\n| FeeHookSwapHandler | swap     | 256   | 0       | 0        |\n╰--------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 2786\n  Bound result 3659\n  Bound result 2762\n  Bound result 10359994987449670\n  Bound result 4\n  Bound result 1\n  Bound result 735722125971779104\n  Bound result 887200\n  Bound result 60\n  Bound result 680743847645493146\n  Bound result 44343\n  Bound result 999999999999999999\n  Bound result 219047676658003279\n  Bound result 10905\n  Bound result 995872829490873255\n  Bound result 30495963257775203\n  Bound result 7656725545084281\n  Bound result 282877131233321575\n  Bound result 81998358918916928\n  Bound result 10625\n  Bound result 1000000000000000000\n  Bound result 799131311107716507\n  Bound result 1000000000000000000\n  Bound result 75\n  Bound result 1000000\n  Bound result 12391\n  Bound result 23654640991767066\n  Bound result 20968028\n  Bound result 50\n  Bound result 505944031\n  Bound result 975932652015417119\n  Bound result 3193\n\n[PASS] invariant_everySwapPaidTheFullRate() (runs: 8, calls: 256, reverts: 0)\n\n╭--------------------+----------+-------+---------+----------╮\n| Contract           | Selector | Calls | Reverts | Discards |\n+============================================================+\n| FeeHookSwapHandler | swap     | 256   | 0       | 0        |\n╰--------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 2786\n  Bound result 3659\n  Bound result 2751\n  Bound result 10359994987449670\n  Bound result 4\n  Bound result 1\n  Bound result 1643\n  Bound result 887200\n  Bound result 60\n  Bound result 680743847645493146\n  Bound result 319425529996739948\n  Bound result 999999999999999999\n  Bound result 219047676658003279\n  Bound result 10905\n  Bound result 901682582719075461\n  Bound result 30495963257775203\n  Bound result 7656725545084281\n  Bound result 282877131233321575\n  Bound result 81998358918916928\n  Bound result 10625\n  Bound result 1000000000000000000\n  Bound result 799131311107716507\n  Bound result 1000000000000000000\n  Bound result 75\n  Bound result 1000000\n  Bound result 12392\n  Bound result 23654640991767066\n  Bound result 20968028\n  Bound result 50\n  Bound result 505944031\n  Bound result 975932652015417119\n  Bound result 3193\n\n[PASS] invariant_feesAreAtLeastFivePercentOfEverythingCharged() (runs: 8, calls: 256, reverts: 0)\n\n╭--------------------+----------+-------+---------+----------╮\n| Contract           | Selector | Calls | Reverts | Discards |\n+============================================================+\n| FeeHookSwapHandler | swap     | 256   | 0       | 0        |\n╰--------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 2786\n  Bound result 3659\n  Bound result 767776745657204735\n  Bound result 10359994987449670\n  Bound result 4\n  Bound result 1\n  Bound result 303958227383222271\n  Bound result 887200\n  Bound result 60\n  Bound result 680743847645493146\n  Bound result 255268576510550363\n  Bound result 999999999999999999\n  Bound result 219047676658003279\n  Bound result 10905\n  Bound result 550545094424195106\n  Bound result 30495963257775203\n  Bound result 7656725545084281\n  Bound result 282877131233321575\n  Bound result 81998358918916928\n  Bound result 11246\n  Bound result 1000000000000000000\n  Bound result 799131311107716507\n  Bound result 1000000000000000000\n  Bound result 3852\n  Bound result 1000000\n  Bound result 761\n  Bound result 23654640991767066\n  Bound result 20968028\n  Bound result 50\n  Bound result 2739\n  Bound result 975932652015417119\n  Bound result 4232\n\n[PASS] invariant_hookHoldsNoLooseTokens() (runs: 8, calls: 256, reverts: 0)\n\n╭--------------------+----------+-------+---------+----------╮\n| Contract           | Selector | Calls | Reverts | Discards |\n+============================================================+\n| FeeHookSwapHandler | swap     | 256   | 0       | 0        |\n╰--------------------+----------+-------+---------+----------╯\n\nLogs:\n  Bound result 2786\n  Bound result 3659\n  Bound result 2751\n  Bound result 10359994987449670\n  Bound result 4\n  Bound result 1\n  Bound result 1643\n  Bound result 887200\n  Bound result 60\n  Bound result 680743847645493146\n  Bound result 44409\n  Bound result 999999999999999999\n  Bound result 219047676658003279\n  Bound result 10905\n  Bound result 415411309167588923\n  Bound result 30495963257775203\n  Bound result 7656725545084281\n  Bound result 282877131233321575\n  Bound result 81998358918916928\n  Bound result 12538\n  Bound result 1000000000000000000\n  Bound result 799131311107716507\n  Bound result 1000000000000000000\n  Bound result 76\n  Bound result 1000000\n  Bound result 841\n  Bound result 23654640991767066\n  Bound result 20968028\n  Bound result 50\n  Bound result 10268\n  Bound result 975932652015417119\n  Bound result 3122\n\nSuite result: ok. 5 passed; 0 failed; 0 skipped; finished in 360.96ms (889.93ms CPU time)\n\nRan 14 tests for test/FivePercentFeeHookWithdraw.t.sol:FivePercentFeeHookWithdrawTest\n[PASS] test_deployerWithdrawsEverythingAccrued() (gas: 337589)\n[PASS] test_feesAccrueAgainAfterAWithdrawal() (gas: 412688)\n[PASS] test_noOtherRoleCanWithdraw() (gas: 355666)\n[PASS] test_nonDeployerCannotWithdraw() (gas: 336603)\n[PASS] test_nonDeployerContractCannotWithdraw() (gas: 2101956)\n[PASS] test_ownerIsWhoeverCalledTheDeployer() (gas: 61296437)\n[PASS] test_strangerWithdrawingAnEmptyCurrencyStillReverts() (gas: 13004)\n[PASS] test_unlockCallbackCannotBeCalledDirectly() (gas: 352627)\n[PASS] test_unlockCallbackFromTheManagerOutsideAnUnlockReverts() (gas: 346804)\n[PASS] test_withdrawIsPerCurrency() (gas: 312910)\n[PASS] test_withdrawOfAnUnrelatedCurrencyReverts() (gas: 2448741)\n[PASS] test_withdrawTwiceReverts() (gas: 327022)\n[PASS] test_withdrawWithNothingAccruedReverts() (gas: 12734)\n[PASS] test_withdrawingNativeCurrency() (gas: 441350)\nSuite result: ok. 14 passed; 0 failed; 0 skipped; finished in 560.89ms (380.66ms CPU time)\n\nRan 6 test suites in 562.31ms (1.77s CPU time): 58 tests passed, 0 failed, 0 skipped (58 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":null,"profile":null,"status":"accepted","submissionHash":"cc04a131a854eee9899f18c0abcfe11b152fde90d71a2410f0a5efe9f335bb83","verifiedTreeHash":"32bd9ac8cf2f36e52154bb4026ade84bafbdc62e","verifierVersion":"0.1.0+e28c470a"},{"checks":[{"durationMs":831,"exitCode":0,"name":"build","output":"Compiling 43 files with Solc 0.8.26\nSolc 0.8.26 finished in 737.48ms\nCompiler run successful!\n","passed":true},{"durationMs":120,"exitCode":0,"name":"test","output":"No files changed, compilation skipped\n\nRan 2 tests for test/Placeholder.t.sol:PlaceholderTest\n[PASS] test_beforeSwapFlag_matchesV4Core() (gas: 8342)\n[PASS] test_hasBeforeSwapPermission_readsAddressBits() (gas: 9545)\nSuite result: ok. 2 passed; 0 failed; 0 skipped; finished in 566.08µs (381.76µs CPU time)\n\nRan 1 test suite in 3.27ms (566.08µs CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)\n","passed":true}],"detail":"all checks passed","evaluation":null,"profile":null,"status":"accepted","submissionHash":"f206e65336d78c8b4f15fba2e306bfd328a694a59a00bacf7ae6758e64a0cda5","verifiedTreeHash":"ad410e9f3c88d62816c71314153280a761aa0cd2","verifierVersion":"0.1.0+e28c470a"}]}