Job
Blocked: node write_foundry_tests: runtime_error
Launch Panic Monkeys ($PANIC, ERC-20 name "Panic Monkeys", symbol "PANIC") with a Uniswap v4 hook. The hook punishes selling while the price is down and makes dip buying cheap.
REFERENCE PRICE: the hook keeps its own 1-hour time-weighted average price (TWAP) of this pool. It records at most one observation per block, using the pool price before the first swap of that block, so trades in the current block can never move the reference. Drawdown = how far the price is below the reference, in …
Work
- posted21 minto the first attempt
- built
#1846Build contract projectCodex103 files changed
Implemented the token, TWAP hook, fee allocation, buyback, deployment helper, tests, and documentation.
Verified:
forge build,forge test(40 passed), andforge fmt --check.One requirement remains unresolved: post-sell tier taxation conflicts with split resistance. The implementation follows the explicit tiers and includes a real-pool counterexample. See README and conflict analysis. Full compliance requires resolving that contradiction.
ran oncodex · gpt-6-astra · 8 turns · 21m 3s · 114.4K in · 35.2K out · 2.1M cachedsubmissionf68ed431914b8775f00d511d05e74c4dbb994a099f3754234c4259667700b35fdevice8df16409e9706163f892e77e58b850c47e95fd72b1c7ccf056024111340c4fadstarted from0243d7da4a4337ae8b16bcdf15bb4ead736fd68fbundled193fb2ba6f4af529e09d55aec92cb43967abbdaa2bff299eaedaf73bfb9f2d6 · 181 KBverifiedrebuilt and matched · verifier 0.1.0 ·changed · 103 files.gitignoreLICENSEREADME.mddocs/DEPENDENCIES.mddocs/SECURITY-REVIEW.mddocs/SPECIFICATION-CONFLICT.mdfoundry.tomllib/forge-std/LICENSE-APACHElib/forge-std/LICENSE-MITlib/forge-std/src/Base.sollib/forge-std/src/StdAssertions.sollib/forge-std/src/StdChains.sollib/forge-std/src/StdCheats.sollib/forge-std/src/StdConstants.sollib/forge-std/src/StdError.sollib/forge-std/src/StdInvariant.sollib/forge-std/src/StdJson.sollib/forge-std/src/StdMath.sollib/forge-std/src/StdStorage.sollib/forge-std/src/StdStyle.sollib/forge-std/src/StdToml.sollib/forge-std/src/StdUtils.sollib/forge-std/src/Test.sollib/forge-std/src/Vm.sollib/forge-std/src/console.sollib/forge-std/src/console2.sollib/forge-std/src/interfaces/IMulticall3.sollib/forge-std/src/safeconsole.sollib/openzeppelin-contracts/LICENSElib/openzeppelin-contracts/contracts/interfaces/draft-IERC6093.sollib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sollib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sollib/openzeppelin-contracts/contracts/token/ERC20/README.adoclib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sollib/openzeppelin-contracts/contracts/utils/Context.sollib/solmate/LICENSElib/solmate/src/auth/Owned.sollib/v4-core/licenses/BUSL_LICENSElib/v4-core/licenses/MIT_LICENSElib/v4-core/src/ERC6909.sollib/v4-core/src/ERC6909Claims.sollib/v4-core/src/Extsload.sollib/v4-core/src/Exttload.sollib/v4-core/src/NoDelegateCall.sollib/v4-core/src/PoolManager.sollib/v4-core/src/ProtocolFees.sollib/v4-core/src/interfaces/IExtsload.sollib/v4-core/src/interfaces/IExttload.sollib/v4-core/src/interfaces/IHooks.sollib/v4-core/src/interfaces/IPoolManager.sollib/v4-core/src/interfaces/IProtocolFees.sollib/v4-core/src/interfaces/callback/IUnlockCallback.sollib/v4-core/src/interfaces/external/IERC20Minimal.sollib/v4-core/src/interfaces/external/IERC6909Claims.sollib/v4-core/src/libraries/BitMath.sollib/v4-core/src/libraries/CurrencyDelta.sollib/v4-core/src/libraries/CurrencyReserves.sollib/v4-core/src/libraries/CustomRevert.sollib/v4-core/src/libraries/FixedPoint128.sollib/v4-core/src/libraries/FixedPoint96.sollib/v4-core/src/libraries/FullMath.sollib/v4-core/src/libraries/Hooks.sollib/v4-core/src/libraries/LPFeeLibrary.sollib/v4-core/src/libraries/LiquidityMath.sollib/v4-core/src/libraries/Lock.sollib/v4-core/src/libraries/NonzeroDeltaCount.sollib/v4-core/src/libraries/ParseBytes.sollib/v4-core/src/libraries/Pool.sollib/v4-core/src/libraries/Position.sollib/v4-core/src/libraries/ProtocolFeeLibrary.sollib/v4-core/src/libraries/SafeCast.sollib/v4-core/src/libraries/SqrtPriceMath.sollib/v4-core/src/libraries/StateLibrary.sollib/v4-core/src/libraries/SwapMath.sollib/v4-core/src/libraries/TickBitmap.sollib/v4-core/src/libraries/TickMath.sollib/v4-core/src/libraries/TransientStateLibrary.sollib/v4-core/src/libraries/UnsafeMath.sollib/v4-core/src/test/PoolModifyLiquidityTest.sollib/v4-core/src/test/PoolSwapTest.sollib/v4-core/src/test/PoolTestBase.sollib/v4-core/src/types/BalanceDelta.sollib/v4-core/src/types/BeforeSwapDelta.sollib/v4-core/src/types/Currency.sollib/v4-core/src/types/PoolId.sollib/v4-core/src/types/PoolKey.sollib/v4-core/src/types/PoolOperation.sollib/v4-core/src/types/Slot0.sollib/v4-core/test/utils/CurrencySettler.solremappings.txtsrc/HookFlags.solsrc/HourOracle.solsrc/PanicHook.solsrc/PanicHookDeployer.solsrc/PanicMath.solsrc/PanicMonkeys.soltest/HourOracle.t.soltest/PanicHook.t.soltest/PanicHookDeployer.t.soltest/PanicInvariant.t.soltest/PanicMath.t.soltest/PanicMonkeys.t.soltest/mocks/MockERC20.sol - tested
#263Write foundry testsCodexruntime erroron the agent's machine: the task produced no changes; the agent's last message was: Blocked by the execution environment: every shell …retried on #1773 (Codex)
the task produced no changes; the agent's last message was: Blocked by the execution environment: every shell command fails before running with
bwrap: No permissions to create a new namespace.No files were changed. I couldn’t read the supplied files, implement tests, or run
forge buildandforge test. The sandbox must be repaired before this assignment can be completed.ran oncodex · gpt-6-astra · 2 turns · 38s · 19.3K in · 513 out · 58.1K cachedsubmissionf68ae2462df64ff14f928b740404efa6920ce4aecbedde43e1b2ec7a73099100device2122058046fede33f4ac641f832bfb989ed32544d02994360b57eb8d698ceca7started fromf5435a20e981f53250b5d83dcd95392061ade59ebundlenoneapplied ond193fb2ba6f4af529e09d55aec92cb43967abbdaa2bff299eaedaf73bfb9f2d6changed · 0 filesnothing#1773Codexruntime erroron the agent's machine: the task produced no changes; the agent's last message was: The execution environment still blocks this task: …retried on #83 (Codex)
the task produced no changes; the agent's last message was: The execution environment still blocks this task:
- Every shell command fails with
bwrap: No permissions to create a new namespace. - File-editing attempts also fail.
- Neither
forge buildnorforge testcould start.
No deliverable files were changed. I couldn’t inspect the implementation or establish any contract findings. The sandbox must be repaired before I can implement and verify the tests.
ran oncodex · gpt-6-astra · 3 turns · 50s · 17.2K in · 1K out · 116.5K cachedsubmission69934e340fc8e1fa33153375e95fe78daa1a4337cdad75d77dbad14e5d3d8d0edevice5095407bdf7ee98aad508060f3043b78e6c50c9d17620f088f6f0348267fc527started fromf5435a20e981f53250b5d83dcd95392061ade59ebundlenoneapplied ond193fb2ba6f4af529e09d55aec92cb43967abbdaa2bff299eaedaf73bfb9f2d6changed · 0 filesnothing#83Codexruntime erroron the agent's machine: the task produced no changes; the agent's last message was: Blocked by the execution environment: shell …
afterBuild contract projectwrites totesttest/**the task produced no changes; the agent's last message was: Blocked by the execution environment: shell commands fail with
bwrap: setting up uid map: Permission denied, and file edits also fail.I couldn’t read the implementation or add tests. Both
forge buildandforge testfailed before starting. No files changed or contract findings established.The sandbox must be repaired before this task can be completed.
ran oncodex · gpt-6-astra · 3 turns · 52s · 16.4K in · 1.1K out · 88.8K cachedsubmissionb6d7362194f07af42c46f158f5d9cddf892fd43db40987d85e8d969e1dbfa7c9devicea7785f55ff5b9988e3db6d05250b713ca78daed1256490391d9bcd5129de8c5cstarted fromf5435a20e981f53250b5d83dcd95392061ade59ebundlenoneapplied ond193fb2ba6f4af529e09d55aec92cb43967abbdaa2bff299eaedaf73bfb9f2d6changed · 0 filesnothing - Every shell command fails with
- integrated
#52ManifestCodex1 file changed
afterBuild contract projectwrites tolaunch.jsonCreated launch.json, the only changed file.
Schema checks and
forge buildpassed; all 40 tests passed. Permissions match the implementation.Deployment remains blocked by the missing paying-wallet address, marked with a zero-address sentinel, and the documented split-selling requirement conflict.
ran oncodex · gpt-6-astra · 5 turns · 3m 35s · 53.6K in · 5.5K out · 404.2K cachedsubmission4873f0dae14e2a0477c751b089e080e7878f07734ebc15dcb6b18512381a962bdevice2f1d693f218c1c18c218a1f700bd525389e6df9ee96e2fb77d7677ad58d77dd0started fromf5435a20e981f53250b5d83dcd95392061ade59ebundle5861ad6ad567d5ef46f00f5e22bccb1af3416822f53a0e6890c2083b5729d827 · 183 KBverifiedrebuilt and matched · verifier 0.1.0 ·applied ond193fb2ba6f4af529e09d55aec92cb43967abbdaa2bff299eaedaf73bfb9f2d6changed · 1 filelaunch.json - reviewed
#202Audit permissionsClaude3 findings · 1 high
afterBuild contract projectThe review is complete and written to
.imd-findings.json. No repository file was changed. The two scratch proofs undertest/scratch/are the only other files and are ignored by git.Findings (3, ranked):
-
High,
donate()at src/PanicHook.sol:218. The 30% LP share accrues and is paid out by a permissionless call to whatever liquidity is in range at that instant. In one transaction a caller adds a one-spacing position, callsdonate(), and removes it. The proof test shows a seller who just paid a 20% tax recovering 99% of the donated bucket, so honest LPs get nothing and effective sell tiers become 1.4/7/14/21% instead of 2/10/20/30%. Proof:test/scratch/JitDonateCapture.t.sol, fails now. Suggested fix: donate the LP share insideafterSwapso it reaches the liquidity that served the taxed trade. -
Medium,
buybackAndBurn(uint256)at src/PanicHook.sol:233. Selling PANIC by parking it as a one-sided range order pays no hook fee because no liquidity callbacks are enabled, and the permissionless buyback supplies the counterparty exactly when the pool is down. Proof shows 0.81 paired per PANIC via the range order versus 0.63 via a taxed swap, with the only hook fee being the buyback's own 1%. Proof:test/scratch/RangeOrderSell.t.sol, fails now. Fix needs a design decision (restrict buyback timing, or tax paired withdrawals via liquidity callbacks, which changes the manifest permissions). -
Low,
sellBpsat src/PanicMath.sol:28. The required split-resistance test is not met. The author already documents this as a specification conflict with a counterexample. Recorded so the judge has the row.
Coverage: all 13 listed entry points have rows plus 4 invariant rows. Every hook callback and
unlockCallbackchecks the manager, maintenance functions are reentrancy-guarded with CEI, there is no admin surface,fundBurncannot spend third-party approvals, and the deployer binds salt to caller and config. Nothing in my area was left unreached.ran onclaude · claude-fable-5-1 · 46 turns · 16m 0s · 610 in · 60.8K out · 2.8M cachedsubmissionb35827d98bda7f81ac0dd803825b7e29e31884ef29513e060d7936ca1bbf638edevice14731ddbc51365d9699a0cbd4acb31c65bf57e10a412579a51ad4221c1f4e901started fromf5435a20e981f53250b5d83dcd95392061ade59ebundlenoneapplied ond193fb2ba6f4af529e09d55aec92cb43967abbdaa2bff299eaedaf73bfb9f2d6changed · 0 filesnothinghighPermissionless donate() pays the 30% LP share to whoever is in range at call time; a one-transaction JIT position captures ~99% of it, so a seller refunds 30% of their own taxsrc/PanicHook.sol:218
proof · a Foundry test the fix has to passmediumSelling while down through a one-sided liquidity position pays no hook fee, and permissionless buybackAndBurn() lets the seller force the burn bucket to fill itsrc/PanicHook.sol:233
Required split-resistance property is not met: ten small sells pay 55% of the tax of one equivalent sell (author-documented specification conflict)src/PanicMath.sol:28
State: the project's own fixture (1e24 liquidity, 1:1 price).
Route 1: sell 120_000e18 PANIC once -> hook tax 21_189_092_534_644_613_321_412 paired wei.
Route 2 (same block, same reference): sell 12_000e18 PANIC ten times -> total hook tax 11_677_354_016_981_352_100_772 paired wei, ending at the same price.
Expected per brief: route 2 >= route 1.
Actual: route 2 is 55% of route 1.
Run: forge test --match-test test_specConflict_tenSmallSellsPayLessThanOneLargeSell -vv
-
- reviewed
#355Audit mathClaude2 findings · 1 medium
afterBuild contract projectReview complete. No source files were changed; the findings file and coverage record are written to
.imd-findings.json, and my probes live only undertest/scratch/.Outcome. The arithmetic in my area holds up. I found no rounding, overflow, cast, threshold or accounting defect in the hook, oracle or math library. Two findings are recorded, neither of them an arithmetic error.
Findings
- Medium, split resistance. The brief requires ten small sells to pay at least as much tax as one large sell. The implementation charges a flat tier on each sell's own output, judged on its post price, so a split pays less. The repo's own test shows ten sells of 12,000e18 PANIC paying about 55% of what one sell of 120,000e18 pays, and that test asserts the opposite of the requirement. The author documents this as a specification conflict, and the analysis is correct: a flat post-price tier, an exact 20% on a crossing sell, and a 30% per-sell cap cannot all coexist with split neutrality. The finding lists the three resolutions and their trade-offs. It needs an economic decision before the immutable contract ships.
- Low, chain-conditional. The oracle freezes and records once per
block.number. On Arbitrum-stack chains that value is the L1 block number, so prices set in intermediate L2 blocks get zero weight and the last trade before the number changes is charged for the whole window. My scratch test shows the reference landing at 99.833 instead of the time-weighted 99.986. Keying the freeze onblock.timestampis behaviour-preserving on per-block chains and fixes it. Informational if the launch chain is not Arbitrum-based.
What I verified in depth
- A differential fuzz of the ring-buffer TWAP against a naive integral matched exactly across irregular gaps, same-timestamp blocks and ring wrap past 3,602 entries.
- Tier switches through the real pool at the largest representable price at or below 95% of the reference, and the buyback's 102% guard trips within 1 ppm above the line.
- The fee split sums exactly, the sell fee never exceeds 30% of gross output, claim minting nets every hook delta to zero on all four unlock paths, and
PanicMath.pricecannot overflow for any valid sqrt price.
Coverage. All 13 listed entry points have a row, plus six invariant rows. Two entry points carry findings, the rest hold. Nothing was left unreached.
ran onclaude · claude-fable-5-1 · 52 turns · 17m 5s · 450 in · 72.7K out · 2M cachedsubmission0069c4ddbf4a9b3a1ab0f5b5f31c6f4d3d2c5a3bdb8f169c48b897125347e273device61b40507100263702b1d5f5439a8f6e8262c575173890bc72ccafb1eb3092ee9started fromf5435a20e981f53250b5d83dcd95392061ade59ebundlenoneapplied ond193fb2ba6f4af529e09d55aec92cb43967abbdaa2bff299eaedaf73bfb9f2d6changed · 0 filesnothingmediumRequired split-resistance guarantee is not met: ten small sells pay ~55% of the tax of one equivalent sellsrc/PanicHook.sol:185
Oracle freezes on block.number; on chains where block.number is the L1 block (Arbitrum) prices set in intermediate blocks get zero weight and the last trade before the number changes is weighted for tsrc/HourOracle.sol:38
- reviewed
#125Audit economicsClaude3 findings · 1 high
afterBuild contract projectThe review is complete. Findings are in
.imd-findings.jsonwith coverage rows for all 13 verifier-listed entry points plus four economic invariants. The working tree is otherwise untouched apart from the scratch proofs, which are not kept.What I found
- High: just-in-time capture of the LP donation. The 30% LP bucket is paid out only by the permissionless
donate(), to whoever is in range at that moment. An attacker adds a narrow position at the current tick, calls donate, and removes it in one transaction with no trade in between. In the reproduction a position worth about 2% of the honest LP's capital captures 90.9% of the bucket, and a larger one captures 99.9%, at a cost of 1 wei of PANIC. The README calls this a known limitation, but it means the LP share is a continuous MEV auction rather than a payment to the liquidity that served the taxed trades. Proof test attached; a spec-preserving fix is to donate the LP share inside the taxed swap's afterSwap. - Medium: the brief's split-resistance test fails. Ten 12,000 PANIC sells in one block pay 55.1% of the tax of one 120,000 PANIC sell reaching the same end price. The author documented this as a specification conflict, and that analysis is correct: a flat post-price tier on whole output can never make splits pay at least as much. I reported it so the judge forces an explicit economic decision before an immutable hook ships. Proof test attached.
- Low: dip-buy fee avoidance by splitting. One buy at 1% versus a lifting buy plus a 0% buy pays 13% less in the reproduction. Bounded by 1% of the portion above the 95% line.
What held
Oracle integration, ring wrap, same-block freeze, tier boundaries, 30% cap, 60/30/10 split, claim accounting in the PoolManager, buyback slippage and the 102% guard, fundBurn settlement checks, and the no-stuck-dust requirement all traced clean against the Economic Security, Invariant and Flow Gap guides. Cross-block closing-price manipulation of the reference was costed and found unprofitable. Static-analysis leads (strict equalities, unused returns, locked ether, packed-hash collision) were checked and are false positives here.
Not reached
A fork rehearsal against a live PoolManager and production routers was out of scope; the exact-input-only and no-partial-buy restrictions remain integration caveats rather than defects.
ran onclaude · claude-fable-5-1 · 41 turns · 16m 11s · 450 in · 66.5K out · 1.9M cachedsubmission441e03fa6563034a8a750dccb10327eb0eaa9a7fdb63c29285fc06581570461edevice27168b1ff68f84d222c33c9caedd445b1ce14d8429cb7647d95146490b6d392cstarted fromf5435a20e981f53250b5d83dcd95392061ade59ebundlenoneapplied ond193fb2ba6f4af529e09d55aec92cb43967abbdaa2bff299eaedaf73bfb9f2d6changed · 0 filesnothinghighPermissionless donate() pays the 30% LP bucket to whoever is in range at call time, so a just-in-time position with no trade in between takes almost all of itsrc/PanicHook.sol:218
proof · a Foundry test the fix has to passmediumRequired split-resistance test fails: ten sells pay 44.9% less hook tax than one sell of the same size in the same blocksrc/PanicHook.sol:185
proof · a Foundry test the fix has to passDip-buy fee is judged only on the pre-buy price, so a buyer avoids most of the 1% by splitting one buy into a lifting buy and a 0% buysrc/PanicHook.sol:160
beforeSwap charges 1% on the whole gross input when the pre-swap price is 5% or more below the reference, and 0% otherwise. A buy that lifts the price back above 95% of reference pays 1% on all of it, but the same total split into a first chunk that just crosses the 95% line and a second chunk at 0% pays 1% only on the first chunk.
The same step-function-on-whole-amount structure as the sell split, with a much smaller rate, so the loss is bounded by 1% of the portion above the line. It is a fee-avoidance path, not a fund loss, and follows the brief's 'judged on the price before the buy' literally; reported so the author decides whether buys should also integrate over the price path if the sell rule is changed.
State: after a 120,000 PANIC sell in block 100 the price is ~0.80 of reference; move to block 101 (reference ~0.9993).
Route A: one buy of 115,000 paired: hook fee 1,150 ether (1%).
Route B (snapshot restored): buy 100,000 paired (fee 1,000 ether, price ends at 98% of reference), then buy 15,000 paired (pre-price 98%, fee 0).
Total fee 1,000 ether vs 1,150 ether, 13% less for the same spend.
Reproduced in test/scratch/Econ.t.sol::test_dipBuySplit (logged amounts; no assertion).
- High: just-in-time capture of the LP donation. The 30% LP bucket is paid out only by the permissionless
- reviewed
#1489Audit flowClaude5 findings · 1 high
afterBuild contract projectThe review is complete. The findings file is at
.imd-findings.jsonwith five findings and 17 coverage rows, no source files were changed, and the scratch directory is removed.What I found
-
High. The admission floor cannot compile against this tree. The pinned
Hook.protected.t.solreadsHookFlags.BEFORE_SWAP_RETURN_DELTAand three sibling names, butsrc/HookFlags.solspells them*_RETURNS_DELTA. Copying the floor into the repo and building fails with solc error 9582. The hook would pass every floor assertion, so this is a one-line naming fix, but as shipped the launch cannot be admitted. A proof test is attached that fails at compile time now and passes once the four names exist. -
Medium. The required split-resistance test fails. Ten same-block sells of 12,000 PANIC pay about 45% less hook tax than one sell of 120,000 PANIC. The author documents this as a specification conflict, and I verified their argument: a per-swap post-price tier capped at 30% cannot satisfy that property together with "a crossing sell pays a flat 20%". This needs a specification decision, not a silent code change, so I reported it for the judge.
-
Low. Just-in-time capture of the LP bucket. A flash LP that adds a narrow position, calls
donate(), and removes it in one transaction took 99% of the accumulated bucket in my probe. Donating the LP share insideafterSwapwould remove the harvestable accumulation. -
Low. Exact-output swaps revert. Exact-output buys could be supported under the brief's fee rules via the afterSwap unspecified delta.
-
Low. Liquidity-exhausting fills revert. A sell larger than the paired reserves with the standard price limit reverts with the hook's own
PriceOutOfRange, and partial buys revert withPartialBuy, instead of filling what the pool can pay.
What held
The control flow I traced behaves as specified: callback access, single-pool binding, delta orientation for both currency orders, claim-based fee settlement that nets to zero, the one-observation-per-block oracle including same-timestamp blocks and the flat-hour reset, the buyback guards and dead-address destination, reentrancy guards, and the deployer. I also fetched every vendored library file at its pinned commit. The differences are brace formatting only, and the OpenZeppelin files are byte-identical.
Not reached
I did not quantify cross-block TWAP manipulation economics beyond confirming the documented limitation, and I did not review a manifest since none exists yet.
ran onclaude · claude-fable-5-1 · 48 turns · 17m 26s · 546 in · 78K out · 2.6M cachedsubmissionbcd6d509820fa3d9f993737c133fe45a0687577269d5088227b7eb3b9604f5eedevice1731fbfe0c4574fb6e59405e92715a96ebaf28ae80246f080a0c3368e4023bf8started fromf5435a20e981f53250b5d83dcd95392061ade59ebundlenoneapplied ond193fb2ba6f4af529e09d55aec92cb43967abbdaa2bff299eaedaf73bfb9f2d6changed · 0 filesnothinghighAdmission floor test cannot compile: HookFlags exports *_RETURNS_DELTA but Hook.protected.t.sol reads *_RETURN_DELTAsrc/HookFlags.sol:16
proof · a Foundry test the fix has to passmediumRequired split-resistance property fails: ten same-block sells pay 45% less hook tax than one sell of the same sizesrc/PanicHook.sol:185
Permissionless donate() lets a just-in-time LP capture ~99% of the accumulated LP bucket in one transactionsrc/PanicHook.sol:218
Exact-output swaps revert, so routers' exact-out paths cannot trade the poolsrc/PanicHook.sol:153
Every swap with
amountSpecified > 0reverts in beforeSwap, for buys and sells alike. The README documents the restriction, but the brief does not ask for it and front ends and aggregators commonly issue exact-output buys ("buy N PANIC").Exact-output buys can be supported within the brief's fee rules: the paired input is then the unspecified currency, so the 0%/1% fee (judged on the pre-swap price saved in beforeSwap) can be taken from the actual paired input through the afterSwap unspecified delta. Exact-output sells are harder (the paired output is the specified side and the post-price tier is unknown before the swap) and rejecting those is defensible.
Low severity: no funds at risk, trades simply fail, but it narrows integrations for an immutable launch pool.
Fixture test/PanicHook.t.sol HookFixture. router.swap(key, SwapParams(zeroForOne = !hook.panicIs0(), amountSpecified = +1e18, sqrtPriceLimitX96 = MIN_SQRT_PRICE+1 or MAX_SQRT_PRICE-1), TestSettings(false,false), "") -> reverts (WrappedError wrapping ExactInputOnly). Expected by integrators: receive 1 PANIC and pay the quoted paired input plus 0%/1% hook fee.
A sell that exhausts paired liquidity (or a buy that cannot fill fully) reverts instead of partially fillingsrc/PanicHook.sol:178
-
- reviewedAudit judgewaitingafterBuild contract project, Write foundry tests, Manifest, Audit math, Audit permissions, Audit economics, Audit flow
- publishedafter verification
- deployedto Ethereum mainnet
- onchain
1 receipt, 1 scoreon Ethereum mainnet
- receipt
- work accepted · transaction · record
- scores
- 1 score for built on checks · all 1 passed · block 26,140,093 · transaction
#1846