Security & audit
The threat model, the invariant catalogue, 123 tests, and what you actually trust.
The full self-audit — scope, threat model, the pump/shield math with proofs, the invariant catalogue, findings and trust assumptions — lives in audit/AUDIT.md. This chapter is the shape of it.
What you trust, exactly
| you trust | you do NOT trust |
|---|---|
| the verified bytecode at the canonical address, and Uniswap V4's PoolManager | any oracle, any keeper, any admin key, any upgrade path — none exist |
| the roles YOU configured on YOUR pool (your pot admin, owner, operator) | the hook's deployer (powerless after deployment), other pools' roles (fully isolated) |
The invariants that hold, always
| invariant | meaning |
|---|---|
| pot solvency | the hook's balance of every asset covers obligationOf(asset) — every unit is attributed |
| donation conservation | every donated unit is spent on the market, delivered, parked, or still in the pot — never lost, never skimmed |
| price immobility on a full absorb | a fully-shielded sell leaves the pool's sqrtPrice exactly unchanged |
| pump boundedness | the pump never spends beyond min(pot, fee·depth, buy input) · 80% |
| main attribution | parked + held + carry + owed always reconciles to what entered minus what left |
| monotone program liquidity | a program's position only grows from compounds; only the owner ever removes |
The adversarial campaign
Differential twin-pool parity (a hooked pool must behave wei-identically to a plain one for the trader, including under spot manipulation), self-sandwich unprofitability across every posture, hostile recipients and tokens (reverting receive(), blocklists, fee-on-transfer, no burn function), reentrant donations and reentrant harvest recipients, zero-fee refusal, direction discipline, pot isolation across pools, mixed-decimal pairs (6/18, 8/18, roles both ways) with magnitude proofs, and the buyback split's own never-stop matrix (a refusing recipient, an unburnable main under a burn share, a hostile native recipient, a re-entering recipient — every one fails sideways while the swap lands).
GH-1 — the one informational finding, in full
The audit's single finding walks every sandwich posture around an on-buy buyback, with the complete two-sided accounting. The frame that makes it legible: the pot is not a treasury the hook defends — it is a standing buy order. Its mandate is to convert its entire inventory into bought-and-burned main at the pool's own execution price, as demand arrives. Every "attack" below is an attempt to make the pot trade; the question in each case is not "did the attacker gain?" but "did the pot pay a fair price for real main, capped by real demand?" — and in every posture the answer is yes.
Posture 1 — farming the pump itself: closed
The attacker buys to summon the pump, then dumps the bag into the bump they financed. The sandwich algebra reduces to profitable ⟺ pump spend > f·R — the attacker's own size cancels out — and the pump never spends more than 0.8·f·R, strictly inside the break-even (the full derivation is in the Pump chapter). Proven unprofitable at every size from dust to pool-scale, and refused structurally on zero-fee pools where the bound would not hold.
Posture 2 — sandwiching an unrelated victim's buy: bounded uplift on a pre-existing attack
Any buy that lands behind a separate large buy makes that buy marginally more profitable to sandwich, because the back-runner sells into a price lifted by both. Measured against the fee-ceiling tuning, the uplift is roughly 15–27% over what the same sandwich earned with no hook at all. The victim was sandwichable with or without the hook — the pump does not create the opportunity, it adds a bounded fraction to one that already existed. And the attacker cannot summon the pump for this: it only rides behind the victim's genuine buy, capped by the victim's own input.
Posture 3 — self-sandwiching through a partially-absorbing shield: the attacker buys the pot its burn
The subtlest surface, stated honestly: when a shield quote is tick-bounded, the pot absorbs a slice of a dump at the current (attacker-elevated) price without moving the pool, and the remainder executes from the un-moved price. A large buy followed by a full dump can therefore exit at a better blended price than a hookless pool would give — in the fuzz campaign's worst case, a 60 ETH buy against a ~480 ETH pot netted the attacker ~7 ETH while the pot spent ~13 ETH. Now read the same episode from the hook's side of the ledger:
| what happened | why it's the machine working |
|---|---|
| the pot paid pool-equivalent price for every token it absorbed | the exact terms the pool itself would have demanded at that moment — and ~5,580 tokens were bought and BURNED. The mandate (buy back as much main as possible with the donors' inventory) was executed, not subverted |
| the extraction is never leveraged | the ETH the attacker takes out is strictly LESS than what the pot deliberately spent buying main — with the difference captured by the pool's LPs as fees (fuzzed over 512 runs: profit ≤ pot spend, every pot spend converts into bought main) |
| the attacker's cost is real and at-risk | a pool-scale open position held across two legs, double fees paid to LPs — and while that inventory is open, anyone ELSE can sandwich them. The attacker takes on exactly the MEV risk they hoped to impose |
| the pot cannot be milked idle | with no pot inventory nothing fires, and the pump leg spends only behind real, fee-paying buys |
This is the finding's core inversion: every path that "extracts" ETH from the pot hands the pot the burned main it exists to acquire, at pool-equivalent price, capped by the pot's own spend. An attacker running posture 3 is, from the machine's ledger, a large seller filling the standing buy order — the pot converts inventory into burn faster, at fair price, while the attacker finances the pool's LPs and carries open MEV risk to do it. The residual is inherent to every buyback that trades behind user flow; here it is bounded, measured, and pays for supply destruction.
Resolution: accepted and documented. The fee ceiling V ≤ f·R is the load-bearing bound on posture 1 and is unchanged; the haircut trades buyback aggressiveness against the size of the posture-2/3 residuals.
What a swap actually costs
| circumstance | overhead vs bare V4 |
|---|---|
| hooked pool, pot empty, no program (idle) | +8–12k gas — one pot read + callback plumbing; the only cost every swap pays |
| pump fires on a buy | +88k gas — the pot's own swap + delivery, paid by the buy that triggered it |
| shield fires on a sell | +38k gas — pool-exact quote + fill + delivery |
| auto-harvest + compound inside a swap | +111k gas — collect + split + compound mint, only on the swap that crosses the minimums |
The heavy circumstances only ever run on the swaps that trigger them, each behind a try/catch that skips the work rather than reverting the carrying swap.
Reproduce it
Licence
Business Source License 1.1. Licensed Work: GlueHook, © 2026 gluefinance.eth, owned by Glue Labs Inc. (Delaware). Change Date: the earlier of 2030-08-05 or a date set at gluehook-license-date.gluefinance.eth; Change License: GPL-2.0-or-later. The GluedMath and GluedV4Core libraries are MIT.
Building on the officially deployed hook — pools, donations, integrations, interfaces, tokens adopting it — is authorized and encouraged. Deploying your own copy of the contract is what's restricted until the change date.
FAQ
What's the headline trust model?+
Immutable code, no admin over funds, no oracle, no upgradeability. The pot spends only on its own pool at the pool's price; roles control policy, never other people's money.
How is it tested?+
123 local Foundry tests (128 with live-fork proofs on Ethereum and Tempo): unit, adversarial never-stop matrices, stateful invariants with randomized campaigns, and deterministic scenario walks.
What do the invariants actually check?+
Delivery conservation (everything acquired = compounded + burned + delivered + booked), custody solvency over all ledgers, config validity, and that hostile actors can't stop swaps.
Why can't the pot be used to manipulate the market?+
It only ever trades its own pool at the pool's own price, sized by the carrying trade. Pushing the price first worsens the pusher's own fill — the mechanism taxes its own manipulation.
Has the bytecode been size- and gas-audited?+
Yes — the audit doc tracks runtime sizes (hook + delegatecall library under EIP-170), per-path gas, and the in-swap budget that keeps harvests from griefing trades.