Diving into the Fundamentals of DeFi Market Making, and Poking at Uniswap v4 Hooks Along the Way
Over the years I led audits at Pashov Audit Group, something like 500 projects passed across my desk with me as the lead. Every shape of DeFi you can imagine. And one thing kept quietly nagging me the whole time: almost none of them used Uniswap v4 hooks. The ones that really built on hooks, with a hook as the core of the thing, you could count on one hand.
v4 hooks are a strong idea. Not many builders have found what to do with them in production yet.
For a long time I told myself it was just our particular mix of clients. Then I stopped believing that. Hooks are a powerful idea - you get to run your own code at the exact moment a swap touches the pool, so liquidity stops being a fixed curve and starts being programmable. But powerful and adopted are two different things. The concept is hard. Builders have other places to point their energy, and clearer trends pulling them there. Writing a serious hook asks a lot, and there isn't yet the critical mass of shipped, battle-tested hooks that inspires the next wave to go build more. So for the complicated hooks, there just wasn't much room yet. That's my read on it.
What pulled me back toward this from a completely different door was market making.
I come from finance, so I wasn't a stranger to it. But market making on traditional markets is a specific craft, and I'd say one of the hardest things people actually do in that world. It isn't "buy low, sell high." It's holding inventory you didn't choose, managing it in real time, and doing it while someone better informed than you is trying to pick you off on every quote. So I got curious what crypto does about all that. And that's where it clicked for me: hooks turn out to be unusually well shaped for market-making problems. The whole reason market making is hard is that you want your pricing to react to state - your inventory, the volatility, who's trading against you - and a plain AMM fee is frozen solid. A hook is the thing that lets that fee breathe.
There's a whole universe of research sitting in this corner - LVR, dynamic fees, toxic flow, inventory control - and I fell into it. So I decided to build a couple of hooks and see what happens.
An honest note on the framing, though. Even this topic had time to feel a little dated by the time I got to it - the sharp minds moved through it a while ago. So none of what follows is me claiming a breakthrough. It's a beginner poking at the fundamentals, measuring basic things for himself, mostly to understand them with my hands instead of from a paper. And the funny part is that building hooks turned out to be barely about writing Solidity at all. The contract is small. What ate all the time was testing, simulation, and measuring things that are not obvious in the slightest. At some point I realized I wasn't building a product, I was doing research. So that is what I'm putting up here: two little repos that are really two research projects, and this is the writeup.
One quick word on the villain here, because it shows up everywhere below. LVR - loss-versus-rebalancing - is the closest thing AMMs have to a Black-Scholes for LP returns. An AMM quotes off a curve, not off the market, so between trades its price is stale. Informed arbitrageurs keep trading against that stale price to realign the pool with the outside world, and every time they do, they walk a little value out of the pool. The nasty part is that it scales with the square of volatility. Double the vol and the tax quadruples. In options language, a passive LP is short gamma and long theta - short volatility - and a fixed fee tier does nothing to price that exposure. That single fact is what both of my hooks are circling, from two different angles.
The two repos
The first one is an LVR-minimizing dynamic-fee hook. The idea is the obvious one you reach for first: if LVR grows with volatility, then let the fee grow with volatility too. The hook estimates the pool's own realized volatility on-chain and sets the fee to clamp(base + slope · variance) - linear in variance, to mirror the shape of LVR itself. The bet is that a wider fee in volatile moments widens the no-arbitrage band exactly when arbitrageurs would otherwise be stripping the pool. The question the repo sets out to answer is narrower than the pitch: does a fee that rises with recent volatility leave the LP better off, or does it just feel like it should?
The second one is an inventory-aware fee skew hook. This one comes straight from how real market makers work. When a market maker's inventory runs long, it skews its quotes to shed the excess: it makes the trade that takes that inventory off its hands cheaper, and the trade that piles on more expensive. That's the century-old core of the craft, formalized by Avellaneda and Stoikov in 2008. An ordinary AMM can't do it, because its fee is symmetric: the same to buy or to sell. But beforeSwap in v4 sees the direction of the trade, so a hook can charge a different fee each way - discount the side that rebalances the pool toward its target, surcharge the side that pushes it further out of balance. The question here: does leaning on inventory - a signal you can read right now, with no lag - actually reduce the LP's inventory risk?
Both are deliberately built as composable overlays. They attach to an ordinary v4 pool and change a fee, nothing more. They don't redesign the curve or ask anyone to migrate liquidity into a new venue the way the heavier LVR-recapture designs (FM-AMM, CoW AMM, Diamond) do. I wanted to know what the simplest version of each idea is worth.
How I worked
I started the first repo the way you'd expect. Build the mechanism, measure the fee revenue, watch it climb with volatility, feel clever. My first pitch to myself was literally "the dynamic fee beats the 0.05% tier by 13x." And then I made myself ask the question I ask on every audit: where is the hole in this?
The first thing wrong was the metric. Fee revenue always goes up when you raise the fee - it's a proxy, not the truth. The number that decides whether an LP should even be in the pool is net PnL against a rebalancing benchmark: fees minus LVR. So I threw out revenue and built that instead. The moment I did, the pretty picture cracked. Under a plain 0.05% fee the LP is deeply net-negative - LVR dwarfs the fees. That's the well-known "most passive LPs lose to arbitrageurs" result, and now my own harness was reproducing it.
The second thing wrong was the baseline. "Beats the 0.05% tier" is a strawman, because 0.05% is a badly chosen fee. The fair question is whether an adaptive fee beats the best fixed fee. So I swept the static fee to find its optimum and compared against that. The 13x collapsed into a loss. At constant volatility there is nothing to adapt to - the best move is simply a high constant fee - and a fee that has to ramp up from a floor as it learns spends its whole life sitting below the optimum.
The third thing wrong was the arbitrageur. Mine was omniscient and fee-blind: it realigned the price every single block no matter the fee, which quietly flatters a wide fee, because the arb pays a huge fee on full volume and hands it all to the LP. I replaced it with a rational arb that only trades to the no-arbitrage band and then stops. Now everything went LP-negative in an honest way, and a hidden cost surfaced that the naive model had been hiding: a wider fee makes the rational arb stop earlier, which leaves the pool more mispriced, so retail inherits staler prices. Less LVR, but more staleness. You don't get the reduction for free.
Then came rigor, because one price path is an anecdote. I ran the comparison as a Monte Carlo across seeds and reported the spread instead of a single number. I swapped the scripted volatility for a stochastic, clustering process. And then I pulled real ETH/USDC moves off mainnet and replayed them through the same harness. That last one is my favorite, because it embarrassed me in a useful way: on a calm real window the dynamic fee was not worse - it correctly kept the fee low. My confident "it always loses" turned out to be wrong, and the accurate statement was conditional: it loses specifically when volatility changes, and it's fine when things are calm.
Underneath all of it sat one mechanism, and I found the cleanest way to show it.
A backward-looking estimator cannot price a move it has not seen yet.
After a calm stretch, a jump pays the pre-jump fee - 0.05% - on the exact block it moves the price, and the deterrent 1% fee only shows up the block after, once the value has already left. A 19x undercharge on the one block that mattered, and no manipulation required, just timing. That's the whole story of the first hook in one picture. A signal built from past prices is always one block late.
That's the moment the project reframed for me. The hook itself was small; the work was in measuring it without fooling myself. So for the second repo I did something I'm a little proud of: I wrote down all twenty lessons from the first one before writing a line of the second, and built the good version first instead of discovering it late. Right metric, best baseline, rational agents, real prices, confidence intervals, from the first commit.
The second repo also let me chase a cleaner hypothesis. The reason the volatility fee lags is that it's an estimate of the past. But inventory isn't an estimate - it's a state you can read right now, straight off the pool's tick. So a fee that skews with inventory has no lag by construction. That felt like the more elegant idea, and it happens to be the oldest idea in market making.
Lean on the fee the way a market maker leans on its quotes when the book runs long.
What I found
Where the first hook landed: a realized-volatility fee reduces the LVR that arbitrageurs extract, but it does not beat a well-tuned static fee. It lost on all five Monte-Carlo seeds. The reason is structural - the signal is one block late - and when I swapped in a fancier backward-looking signal (a directional "toxicity" drift instead of plain variance), it hit the exact same wall and tied. Every on-chain proxy of past prices does. The clean way to see it is the LP's net result against a rebalancing benchmark:
| fee policy (high-volatility run) | LVR extracted | LP net vs rebalancing |
|---|---|---|
| static 0.05% (the naive tier) | 36 bps | −36 bps |
| dynamic, volatility-indexed | 23 bps | −22 bps |
| best static fee (the fair opponent) | – | −14 bps |
The dynamic fee beats the naive tier and still loses to the best fixed one. Read those magnitudes as ratios, not real yields - the volatility in that run is deliberately extreme so the mechanics are visible - but the ranking is robust across seeds, price models, and a real historical replay. The real value of the hook isn't the fee, it's the measurement framework and the fact that it's a composable overlay on any pool. The open problem is a forward-looking signal - implied vol, an oracle - which a purely on-chain, trustless hook deliberately gives up. That trade, trustless-but-lagging versus informed-but-external, is the real design question hiding under all of this.
Where the second hook landed: the inventory skew is at least as good as the best static fee in every regime I tested, and it edges out the directional (Nezlobin) fee on signal quality - because state has no lag and a last-move signal does.
| regime (LP net, higher is better) | best static | directional | inventory skew |
|---|---|---|---|
| persistent trend | 104.60 | 104.58 | 104.67 |
| efficient random walk | 104.69 | 104.18 | 104.78 |
| clustering volatility | 104.74 | 105.06 | 104.85 |
Same units, higher is better; the gaps here are fractions of a percent, and the significance check is in the next paragraph.
But I have to be just as straight about the size of the win as I am about the win. The edge is small, and over ten seeds it sits within noise - a 95% confidence interval that includes zero. And notice the last row: under clustering volatility a vol-aware fee beats a pure inventory signal, which is exactly why a complete design would pair the two. The deeper reason the effect is small is almost philosophical. In a pool that arbitrage keeps pinned to the outside market, the LP's inventory is basically a function of price. The fee can change who trades and nudge the band a little, but it can't move that inventory trajectory much. No fee zeroes impermanent loss - that's a theorem, not a failure of effort.
This detour left me with two things: a fresh respect for how hard market making really is, and a clearer view of why hooks haven't swallowed DeFi yet. The idea - program your liquidity, price it like a market maker would - is a beautiful one. But the fully on-chain version keeps running into the same wall the professionals have always known: you're trying to price a risk you can only see clearly after it has already happened. The mechanism is a weekend. The measurement is the work.
I built these for myself, to feel the fundamentals with my hands rather than read about them, and that is exactly what they gave me. Both are on GitHub if you want to poke at them - the LVR-minimizing dynamic-fee hook and the inventory-aware fee skew hook. If you find the spot where my harness is wrong, that's the most useful thing you could tell me.