Implement SwapCounterHook: the smallest useful Uniswap v4 hook.
Implement SwapCounterHook: the smallest useful Uniswap v4 hook. It counts swaps.
Behaviour, in full:
- one hook permission, afterSwap, and nothing else. Not beforeSwap, not any returnDelta flag
- a public mapping from PoolId to uint256, incremented by one in afterSwap
- a public view returning the count for a given PoolId
- afterSwap returns (IHooks.afterSwap.selector, 0). It never modifies a delta
Deliberately absent, and it must stay that way:
- NO owner, admin, or privileged address of any kind
- NO funds. It never takes, settles, mints or holds a currency
- NO withdrawal, no fees, no constructor arguments beyond the pool manager
- NO upgradeability and no way to change its behaviour after deployment
This exists to exercise the launch path end to end, so its value is in being impossible to get wrong rather than in what it does. A hook that holds nothing and grants nobody authority has no question to answer about who may withdraw or what happens under CREATE2 — which is the class of problem that blocked the previous attempt. Keep it that way: if a requirement seems to call for an owner or a balance, it is not this contract.
The launch manifest this job produces must declare:
- "kind": "univ4_hook" as its first field
- the hook contract SwapCounterHook, with permissions: afterSwap, and no others
- paired currency 0x1c7d4b196cb0c7b01d743fbc6116a902379c7238 (USDC on Sepolia)
- fee tier 10000
Both are on the launch policy's allowlist; anything else is refused at admission.
- built
3 of 3 node(s)
- reviewed
- verified3 of 3 re-run · verifier 0.1.0+21bfc484
- publishedpull request ↗
- attestedchain 1 launch from before policy v2; nothing is being deployed to mainnet
- admitted6 of 7 checks
- deployedto Ethereum mainnet
- scoredno reviews
Outputs
0 file(s)No file outputs recorded.No named file outputs were accepted for this job.
GitHub publication
Plan
4 node(s)needs impl
needs impl, tests
needs impl, tests, review
Submissions
4 attempt(s)from 1e4aa121…dc99bundle 5d7ec86e…63ef1 file(s) changed93105e8c…3f6a
from 1e4aa121…dc99bundle none0 file(s) changed8f80a1aa…b587
The suite constrains counting behaviour but not the contract's external surface: an owner, an owner-writable count, and a delegatecall upgrade path all pass all 18 teststest/SwapCounterHook.t.sol:111
getSwapCount counts afterSwap callbacks, including swaps that exchange nothing, contradicting the "completed swaps" wording in the NatSpec and test namessrc/SwapCounterHook.sol:47
No test exercises the pool configuration the manifest actually launches (ERC20/ERC20 pair, fee tier 10000) or a real oneForZero swap through the PoolManagertest/SwapCounterHook.t.sol:49
Every swap that goes through the PoolManager in this suite uses the inherited harness pool -- native ETH as currency0, LaunchToken as currency1, fee 3000 (BaseHookTest.poolFee returns STATIC_FEE because the hook declares a flag), tickSpacing 60 -- and every one of them is zeroForOne = true. The manifest declares a different pool: paired currency 0x1c7d4b196cb0c7b01d743fbc6116a902379c7238 (USDC on Sepolia), an ERC20/ERC20 pair, at fee tier 10000.
The fuzz test at line 118 covers arbitrary callback inputs, but it pranks the manager rather than routing a swap, so the manager-driven path is only ever seen in one direction on one pool shape. I ran both missing cases and the hook behaves correctly in each, so this is a gap in evidence rather than a defect -- recording it so the next contributor knows which two tests are worth adding rather than re-deriving it.