Skim
1.5% of the ETH leg of every exact-input sell is taken and parked in the reserve. Buying pays nothing — on an exact-input buy the hook cannot reach ETH at all.
A token whose hook spends its fee income on limit orders it can never cancel, and burns everything they fill.
Nothing here is announced, scheduled or triggered. The order is already in the book at a price it will not move from, and the only question is whether somebody chooses to hit it.
Three moves, all inside afterSwap, in this order.
1.5% of the ETH leg of every exact-input sell is taken and parked in the reserve. Buying pays nothing — on an exact-input buy the hook cannot reach ETH at all.
Once the reserve crosses its threshold the hook mints its own position: one tickSpacing wide, single-sided ETH, placed 19.75% below spot. A real limit bid, visible to every router and depth chart.
When the price trades fully through a rung, that rung has been converted to SHELF by the people who sold into it. The hook pulls the position and burns every token it bought.
The obvious way to spend a buyback reserve is to market-buy inside somebody else's swap. That is the harder design, not the easier one.
A buy triggered by a known event is an announced
price move, which is the exact shape of a sandwich victim: buy in front of it, let it lift the
price, sell behind it. Defending that means bounding the spend against the pool's own fee and
depth, then haircutting the result, then wrapping the whole thing in try/catch
because a nested swap can revert somebody's trade.
A resting bid has none of that surface. The order sits at a fixed price and the counterparty decides whether to take it. There is nothing to front-run, so there is no bound to derive, no haircut to tune, and no gap between the quote and the execution.
| Property | Market buy inside a swap | Resting bid |
|---|---|---|
| Sandwich surface | Real. Needs a spend bound and a haircut. | None. Nothing to front-run. |
| Execution price | Quoted, then may drift before it lands. | Fixed at placement. |
| Visibility | An off-book balance nobody can route through. | Live liquidity on every depth chart. |
| Earns while idle | No. | Collects the pool fee. |
| Failure mode | Nested swap can revert the carrying trade. | Placement is isolated; the trade survives. |
Every rung the protocol places can be claimed as one object, free but for gas, by anyone who wants it.
The water line is the price and the terrace is the rung. As the price falls the water drops toward it; when the water passes below, the rung has been spent and what it bought was burned. The art is read live from the hook, so an object changes state until its rung retires and then holds that state forever. It carries no yield, no governance and no claim on anything — it is a receipt for a moment in the pool's history.
Claiming is deliberately not part of the
swap. An earlier build minted the object inside afterSwap to whoever's trade
tipped the reserve over, which charged that trader about 65,000 gas for a collectible they
never asked for. Now the hook only records who triggered each rung in its event log, and the
only person who pays for an object is the person who wants one.
Two view functions carry the entire claim. Neither is a projection.
shelfEth() ETH resting right now as live, un-withdrawable bid
shelfPrice() the tick of the nearest rung, i.e. the best live bid
lifetimeBurned() SHELF destroyed by rungs that were filled
Which reads: this much ETH is resting as
live bid at or above this price. One eth_call, no trust, no dashboard in
between. These fill in once the pool exists.
| Name | Value | Meaning |
|---|---|---|
| SKIM_BPS | 150 | Share of the ETH leg taken on a qualifying sell. 1.50%. |
| MIN_RUNG_WEI | constructor | ETH that must accumulate before a rung is placed. |
| RUNG_OFFSET | 11 spacings | 2,200 ticks. 19.75% below spot at tickSpacing 200. |
| MAX_RUNGS | 8 | Rungs that may rest at once, held as eight lanes of one word. |
| retire per swap | 1 | Caps the worst case any single trader absorbs. |
| hook flags | 0x2044 | beforeInitialize | afterSwap | afterSwapReturnDelta. |
ETH is currency0, so the pool price
is SHELF per ETH and a higher tick is a lower SHELF price. A rung is therefore
placed above the current tick, where a position holds currency0 and nothing else.
Ticks are logarithmic, so the offset is not linear
in percent. A discount d needs:
ticks = ln(1 / (1 - d)) / ln(1.0001)
d = 20% → 2,231 ticks → 11 spacings of 200 → 19.75% below spot
This is worth stating plainly because it is easy to get wrong by a factor of four: 112 spacings is not 20% below spot, it is 89% below.
Measured on a mainnet fork against an identical pool with no hook attached, so these are the hook's marginal cost and not the cost of a v4 swap. A buy pays least by construction: buying moves the tick down, a rung only completes when the tick rises, so the retire scan is skipped entirely and no pool state is read.
| Path | Added gas | How often |
|---|---|---|
| Buy | +8,023 | Every buy. No skim, no scan, no pool-state read. |
| Sell | +22,601 | Every sell. One skim, one word written. |
| Sell that places a rung | +230,419 | Roughly one sell per MIN_RUNG_WEI of accumulated skim. |
| Sell that retires + burns | +164,132 | Once per rung, ever. |
The two large figures are lumpy, not common. A
rung is placed once per MIN_RUNG_WEI of accumulated skim, so at a 0.25 ETH
threshold and a 1.5% skim that is one sell in roughly every 16.7 ETH of sell volume. Anyone
who would rather not leave it to chance can call poke() and place the rung
themselves, at their own expense.
The full lifecycle was run against a fork of Ethereum mainnet using the real PoolManager. Tick math was checked separately against Uniswap's documented boundary constants, and the pool-state read against canonical StateView on three live pools.
| Check | Result | Note |
|---|---|---|
| exact-input sell | skimmed 0.029 ETH from a 1.93 ETH sell | exactly 1.50% |
| exact-input buy | skimmed nothing | ETH is the specified currency |
| rung placement | tickLower 95000 vs spot 92833 | 2,167 ticks = 19.5% below |
| rung composition | single-sided, one tickSpacing wide | 200 ticks, pure ETH |
| fill and retire | burned 684.77 SHELF | totalSupply fell by exactly that |
| hook ETH balance | equal to reserve, to the wei | nothing stranded |
| hook SHELF balance | zero | everything caught was burned |
ShelfToken — ERC-20 with a burn the hook may call. No tax, no rebase, no blacklist, no max wallet. The launcher role erases itself when trading opens.ShelfHook — the mechanism. Binds to exactly one pool and reverts on any other.Rungs — the ERC-721 objects. Claimed, never minted on the swap path.ShelfRenderer — on-chain SVG, read live from the hook.Addresses appear here at deployment. The only two places SHELF publishes anything are shelfv4.xyz and @shelfv4 — an address from anywhere else is not SHELF, however convincing the source looks.
The parts most likely to be misread, stated before anyone has to find out.
You cannot burn SHELF and withdraw ETH. There is no redemption function and there is no treasury behind the token. A rung is a bid; once it has been eaten the price can and will go below it. Anyone describing this as a floor, a peg, or backing per token is describing something the contracts do not do.
A v4 return-delta can only move the unspecified currency of a swap. ETH is unspecified on exact-input sells and exact-output buys, and is unreachable on an exact-input buy. So the ordinary buy pays nothing into the reserve. This is a property of v4 accounting, and it is the reason buying costs nothing.
If nobody sells, nothing accumulates and no rung is ever placed. The mechanism is driven entirely by trading, with no keeper, no schedule and no treasury to fall back on. A quiet pool is a still one.
A resting rung is real liquidity and collects the pool fee on anything that trades through it. Those fees return to the reserve when it retires. This is a second-order effect and should not be modelled as yield.
Each rung is placed a fixed percentage below spot at the moment it is funded. If the price falls steadily, later rungs are placed lower than earlier ones. The burn total only rises; the bid price does not.
There is no owner on the hook and no setter for any parameter. ETH leaves the contract only by entering a rung, and a rung leaves only by burning what it caught. That is the whole surface, and it is fixed at construction.