chapters· Security
28 · Reference

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.

123
forge tests, 0 fail
12
stateful invariants
12
fuzzed theorems
5
live-fork proofs

What you trust, exactly

you trustyou do NOT trust
the verified bytecode at the canonical address, and Uniswap V4's PoolManagerany 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

invariantmeaning
pot solvencythe hook's balance of every asset covers obligationOf(asset) — every unit is attributed
donation conservationevery donated unit is spent on the market, delivered, parked, or still in the pot — never lost, never skimmed
price immobility on a full absorba fully-shielded sell leaves the pool's sqrtPrice exactly unchanged
pump boundednessthe pump never spends beyond min(pot, fee·depth, buy input) · 80%
main attributionparked + held + carry + owed always reconciles to what entered minus what left
monotone program liquiditya 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 happenedwhy it's the machine working
the pot paid pool-equivalent price for every token it absorbedthe 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 leveragedthe 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-riska 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 idlewith no pot inventory nothing fires, and the pump leg spends only behind real, fee-paying buys
why every extraction path empowers the burn

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

circumstanceoverhead 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

git clone https://github.com/glue-finance/GlueHook && cd GlueHook forge install OpenZeppelin/openzeppelin-contracts forge clean && forge test // 123 tests, 0 failures FORK_RPC_URL=… forge test // 128: +5 against the LIVE PoolManager

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.

what the licence means for you

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.