The curve is the product. Trader share is monotonically non-increasing in divergence.

Recaptures the arbitrage a swap creates and redistributes it through a bounded, published, non-extractive curve — instead of a hardcoded split anyone can farm.

SPLINE

§1.2 — distribution

Captured profit is paid strictly in order.

Each rank is paid only from what the previous ranks left. Rank 0 exists because in a fixed-split design, the trader who triggers the correction pays the full gas of an arbitrage that benefits everyone else — refunding it first makes “better trader economics” an honest claim rather than marketing.

Rank 0
Trigger gas refundmin(gasUsed × basefee × GAS_MULT, P × GAS_CAP_BPS / 10,000)
≤ 3.00% of P
Rank 1
TraderR × traderShare(divergence, direction, tier, depth)
curve-dependent
Rank 2
Liquidity providersR × lpShare — weighted by in-range liquidity × position age
curve-dependent
Rank 3
ProtocolWhatever ranks 0–2 left unpaid
residual
P — CAPTURED PROFIT0%25%50%75%100%RANK 0 CAP≤ 3.00% of PR — split bytraderShare(x)

Rank 0 boundary drawn to scale · the rest is order, not proportion

§1.3 — the curve

Scroll drives divergence. Divergence decides the split.

Every value below is computed from the same formula that ships in Curve.sol — not chosen to look good on a page. Scroll to sweep divergence from 0 to D_MAX_BPS and watch trader share fall.

I2 — traderShare(x₂) ≤ traderShare(x₁) for all x₂ > x₁. Monotonic, by construction. This is the product.

0125250375500DIVERGENCE (BPS)10%30%45%60%80%
Divergence
0 bps
x (normalised)
0.000
Base
80.0%
Direction
buy · 1.00×
Tier
1.00×
Depth gate
open
Trader share
80.0%
Ceiling
80.0%

§2 — address mining

Permissions live in the address, not a config file.

v4 encodes hook permissions in the low bits of the deployed address, mined with CREATE2 before deploy. The bitmap is public. We do not request permissions the hook does not use — an unused flag is a legibility cost, not a convenience.

Bit 12
afterInitialize() — Auto-registers a pool with the Registry the moment it deploys with this hook
enabled
Bit 6
afterSwap() — Capture + distribute — reads venues, executes the correction, runs the waterfall
enabled
Bit 2
afterSwapReturnDelta() — The hook takes its cut via return delta inside the same unlock, not a second transfer
enabled
Bit 10
afterAddLiquidity() — Position-age ledger for JIT-resistant LP weighting — gated behind D-2
pending
1312INITIALIZE1110ADD_LIQUIDITY · PENDING9876SWAP5432SWAP_RETURNS_Δ10BIT 13BIT 0

14 bits, drawn 1:1 — 3 enabled, 1 pending, 10 unused

§2 — architecture

Four components, deliberately separated.

Registry
Which pools, tokens and venues the hook may touch
append-only
Quoter
Bounded route evaluation — reads only, no state writes, no external calls beyond reads
pure
Executor
Runs the correction trade, settles every delta before the top-level unlock closes
settles in one unlock
Ledger
Computes the waterfall and pays
never pays > captured

Curve.sol is a pure library. It must be callable off-chain by the simulator with identical results — the published curve and the deployed curve are the same code path. That is the whole credibility argument; the maths is never duplicated in TypeScript.

Language
Solidity ^0.8.26
Testing
Foundry — invariant + fuzz
Chain
Robinhood Chain (Arbitrum stack)
Frontend
Next.js, wagmi + viem
Curve math
Solidity, compiled to WASM — not reimplemented in TS

§3 — invariants

The test suite’s reason to exist.

IDInvariantCoverage
I1sum(payouts) ≤ capturedProfit. Always. Never paid from an estimate.Fuzz + invariant
I2traderShare(x₂) ≤ traderShare(x₁) for all x₂ > x₁ — the product.Fuzz + invariant
I3No address, under any call path, moves a parameter outside its compile-time bound.Fuzz + invariant
I4If estimated profit < estimated gas cost, no execution occurs.Fuzz + invariant
I5Route search is bounded by MAX_ROUTES — a swap never triggers an unbounded search.Fuzz + invariant
I6The hook holds zero token balance across transaction boundaries.Fuzz + invariant
I7A failing arbitrage leg never reverts the user's swap.Fuzz + invariant
I8Tier multiplier is unaffected by any balance change within TIER_LAG_BLOCKS.Fuzz + invariant
I9Re-entrant calls into the hook during afterSwap cannot double-pay.Fuzz + invariant

v0 — pre-audit

Bounded at compile time. Impossible to quietly change.

Solidity ^0.8.26 · Foundry · v4-core, v4-periphery — no proxy, no admin fee switch, no pausable payout path