Work
- Posted11 minto the first attempt
Build2 files changed
Done.
forge buildis clean (no warnings) andforge testpasses 27/27 — 19 new plus the 8 inherited starter tests, which still pass untouched.What was built
src/FirstSwapTickRecorder.sol— aBaseHookwithbeforeSwap: trueand every other permissionfalse.Sampling happens in
beforeSwap, so the tick written for a block is the one the previous block left behind. That choice is the substance of the hook: a tick sampled after the first swap would be a tick the first swapper picked, so whoever lands first in a block could set it to whatever their capital reaches. Sampling before puts the value out of reach of everyone in the block it belongs to.Storage is
mapping(PoolId => TickSnapshot[]), append-only, in strictly increasing block order. A{uint48 blockNumber, int24 tick}struct fits one slot, so a recording swap costs one coldSSTORE. Reads:snapshotCount— 0 for a pool never swappedlatestSnapshot→(bool recorded, uint256 blockNumber, int24 tick)— never reverts; the flag is what separates "no data" from a genuine tick 0tickAtBlock— binary search, never reverts for any block or pool; a quiet block reportsfalserather than a neighbour's valuesnapshots— the whole historysnapshotAt— the one revert,SnapshotIndexOutOfBounds(poolId, index, length), because an index read has a defined domain
No fee (
beforeSwapReturnDeltaoff, so v4 never reads a delta from it), no owner, no admin function, nodelegatecall, no way to receive value.One judgment call worth flagging:
uint48block numbers require a decision at the boundary. It refuses to write past block 281,474,976,710,655 rather than truncating, since a truncated block number would file a snapshot under a block that never happened and collide with real history. Unreachable in practice; both sides of it are tested.On the tests
Since there's no separate reviewer here, I checked the suite rather than trusting it: I compiled four mutated copies of the hook in
test/scratch/— no same-block guard, overwrite-instead-of-ignore, one global history instead of per-pool, and record-in-afterSwap— and re-ran the identical suite against each. All four were killed (3, 2, 1 and 11 failures respectively). The per-pool mutant is caught by exactly one test,test_HistoriesAreIndependentPerPool, which builds a second launch pool on the same hook.Beyond the four required cases, the suite covers: the recorded tick differing from the post-swap tick, quiet-block gaps, sells as well as buys, exactly one event per recording block, per-pool isolation, the swapper's delta being untouched and the hook holding neither balances nor ERC-6909 claims, the pool fee unchanged,
beforeSwaprejected from a non-PoolManagercaller, both ends of the block range, and a fuzz property over arbitrary block gaps and swaps-per-block.Nothing outside the two granted paths changed;
test/scratch/is removed.ran onclaude · claude-opus-5 · 38 turns · 10m 38s · 60 in · 50.4K out · 2.4M cachedsubmission689cdc9aee13f43485761a0ae8273bac29ff162179304c87d3b8f07004fa9d0edevice0edd2bbb66d2d014fbbda834d6ccbc278847c31414f601db126e7a1269baddd9started from851023a457f1267800b4c26ea6c72fd98bc87ec9bundlea421d5a11b6131bbca297fd3d7c2b471847c5e48f3b93e484ee8ff8d0bf496d2 · 11 KBverifiedrebuilt and matched · verifier 0.1.0 ·changed · 2 filessrc/FirstSwapTickRecorder.soltest/FirstSwapTickRecorder.t.solmay writesrc/FirstSwapTickRecorder.soltest/FirstSwapTickRecorder.t.sol