Take a blockchain that runs exactly as written when it is called, holding and moving funds according to its code, without an intermediary able to change the outcome.">smart contract holding reserves of two or more tokens, supplied by users, that a decentralized exchange uses to execute trades automatically at a price set by a formula.">pool holding 1,000 ETH and 3,000,000 USDC, and ask it for exactly 10 ETH. These are round example numbers, not a real pool. The price shown by the pool is 3,000 dollars per ether, so 10 ETH should cost 30,000 dollars. The contract bills you 30,394.21 USDC.
It consulted no market to get there, and no one quoted that price. An automated market maker like Uniswap v2 needs only its two balances and one rule: after your trade, the product of those balances must not have fallen. The missing 394.21 dollars splits into 303.03 dollars of curve and 91.18 dollars of fee, and every term a trader meets on a swap screen, from spot price to depth, follows from the same line of algebra.
The principle itself is covered in our guide to decentralized exchanges and AMMs: instead of matching buyers and sellers in an order book, a contract holds a pool of two tokens, deposited by liquidity providers (LPs), and anyone can trade against it at a price set by a formula. This lesson follows how to use a DEX safely, which deals with slippage tolerance, sandwich attacks and approvals. Here we do the math those warnings rest on, then see where the formula stops working.
The automated market maker formula: x·y=k
Call x the reserve of the first token and y the reserve of the second. The rule of a constant product pool, before fees, is x·y = k. In our example x = 1,000, y = 3,000,000 and k = 3,000,000,000.
A trade changes both reserves. Whoever sells Δx of the first token into the pool takes Δy of the second out, and the new reserves must satisfy (x + Δx)·(y − Δy) = k. Nothing else is required. There is no oracle and no matching engine, and the contract has no idea what ether is worth anywhere else. That blindness lets the design run on a slow chain, and later in this lesson it is what costs LPs money.
The Uniswap v2 pair contract does not even compute your output. The router proposes a trade, the pair sends the tokens optimistically, and then it checks the invariant on its final balances. If the product is too low, the transaction reverts with the error “UniswapV2: K”. Everything else, including the fair amount to request, is left to the caller.
From a 2017 blog post to Uniswap v1
The earliest dated primary text we could open is Vitalik Buterin’s post On Path Independence of 22 June 2017. Discussing market makers for existing tokens, he writes that “my favorite (credit to Martin Koppelmann) mechanism is that which maintains the invariant” of a constant product of the two balances. On 2 March 2018 he returned to it on the ethresear.ch forum with a post on front-running resistance. There he traces the idea to a post of his from two years earlier, credits Martin Köppelmann of Gnosis with the simple form he calls the “x*y=k market maker”, and describes a contract holding two tokens whose product stays fixed.
Who deserves credit for what is told differently across secondary accounts, so we stick to those dated texts. Uniswap, built by Hayden Adams, went live on Ethereum on 2 November 2018: the v1 factory contract was created at 05:37 UTC that day, in block 6,627,917. Its first version routed everything through ether. The Uniswap v2 whitepaper of March 2020, by Adams, Noah Zinsmeister and Dan Robinson, added direct pairs between any two ERC-20 tokens on the same formula. Uniswap has since moved on to newer versions, but v2 remains the cleanest place to learn the math, because its swap logic comes down to a few lines of code.
Spot price is the ratio of the reserves
Rewrite the invariant as y = k/x and you have a hyperbola: the set of all reserve pairs the pool can hold. The pool always sits at one point on that curve, and the price of the first token in units of the second is the slope there, without its minus sign.
The slope of k/x is −k/x2. Since k = x·y, that simplifies to −y/x. So the spot price is p = y/x: here 3,000,000 / 1,000 = 3,000 USDC per ETH. The v2 whitepaper states the same result as the marginal price, not including fees, obtained by dividing one reserve by the other.
Spot price is the price of an infinitely small trade. Nobody can actually get it, since any real trade moves along the curve. Note also what the hyperbola forbids: x and y never reach zero. As one reserve shrinks, its price rises without limit, so a pool cannot be fully drained by swaps. It can only become very expensive on one side.
Deriving the swap: what you receive for what you pay
Without a fee
Start from (x + Δx)·(y − Δy) = x·y and solve for the output:
Δy = y·Δx / (x + Δx)
Sell 10 ETH into the example pool: 3,000,000 × 10 / 1,010 = 29,702.97 USDC. Check: the reserves become 1,010 and 2,970,297.03, and their product is 3,000,000,000 again, to within rounding. You got 2,970.30 dollars per ether, not 3,000.
The opposite direction uses the same algebra. Ask for exactly Δy of a token and the required input is Δx = x·Δy / (y − Δy). Buying 10 ETH with USDC, the reserve of what you pay is 3,000,000 and the reserve of what you receive is 1,000, so the cost is 3,000,000 × 10 / 990 = 30,303.03 USDC. That is the 303.03 dollars of curve from the opening.
With the 0.30% fee
Describing v1, the v2 whitepaper says: “Traders pay a 30-basis-point fee on trades, which goes to liquidity providers.” Version 2 kept the same 0.30% for traders, and the code takes it on the input. Only 99.7% of what you send counts when the output is computed, while 100% of it lands in the reserves. Write γ = 0.997 and replace Δx by γ·Δx on the pricing side:
Δy = y·γ·Δx / (x + γ·Δx)
The UniswapV2Library contract does this with integers, since Solidity has no decimals. Multiplying through by 1,000 gives the 997 and 1000 you can read in the code:
amountInWithFee = amountIn * 997
numerator = amountInWithFee * reserveOut
denominator = reserveIn * 1000 + amountInWithFee
amountOut = numerator / denominator
Selling 10 ETH now yields 3,000,000 × 9.97 / 1,009.97 = 29,614.74 USDC, or 88.23 dollars less than the fee-free result. The reverse function, getAmountIn, divides by 997 and adds one smallest unit so that rounding always favours the pool. Buying exactly 10 ETH costs 3,000,000 × 10 × 1,000 / (990 × 997) = 30,394.21 USDC. There is the bill from the opening: 30,303.03 of curve plus 91.18 of fee, which is 0.30% of the amount paid.
The pair enforces this with its own check. It computes each balance times 1,000, minus three times the amount received, and requires that the product of the adjusted balances is at least the old k times 1,000 squared. Because the fee is deducted only inside the test, the real reserves end up slightly above the curve the trade was priced on.

Execution price against spot price
Your execution price is what you actually got: Δy / Δx. Divide the output formula by Δx and, without a fee, it becomes y / (x + Δx). Compare that with the spot price y/x. Call s the size of the trade relative to the reserve, s = Δx / x, and the ratio of execution price to spot price is 1 / (1 + s). Price impact is the shortfall:
price impact, no fee = s / (1 + s)
price impact, with fee = 1 − γ / (1 + γ·s)
Impact depends only on the size of your trade relative to the reserve. The token, the dollar amount and the current price do not appear. For small trades it is close to s itself, and the fee adds about 0.3 of a percentage point on top.
Keep two words apart. Price impact is deterministic: the math above, known before you sign. Slippage, in the sense used by swap interfaces, is the gap between the quote you saw and the price you got, caused by other transactions landing first. A slippage tolerance caps that second gap. The previous lesson covers how to set it.
There is a second price to watch, the one you leave behind. Since the pool’s ETH reserve grew and its USDC reserve fell, the spot after the trade is lower, and without a fee the ratio of new spot to old is 1 / (1 + s)2. The pool’s price therefore moves about twice as far as your average execution price. Your trade starts at the old spot and ends at the new one, and your average sits roughly in the middle.
Price impact at 0.1%, 1%, 5%, 10% and 30% of the pool
Here is the same example pool, 1,000 ETH and 3,000,000 USDC with a spot of 3,000, as ETH is sold into it with the 0.30% fee. All figures are our own calculations from the formulas above and describe no real pool.
| ETH sold | Share of ETH reserve | USDC received | Execution price | Impact, no fee | Impact, 0.30% fee | Pool spot after | Spot move |
|---|---|---|---|---|---|---|---|
| 1 | 0.1% | 2,988.02 | 2,988.02 | 0.100% | 0.399% | 2,994.02 | −0.20% |
| 10 | 1% | 29,614.74 | 2,961.47 | 0.990% | 1.284% | 2,940.98 | −1.97% |
| 50 | 5% | 142,448.92 | 2,848.98 | 4.762% | 5.034% | 2,721.48 | −9.28% |
| 100 | 10% | 271,983.27 | 2,719.83 | 9.091% | 9.339% | 2,480.02 | −17.33% |
| 300 | 30% | 690,708.95 | 2,302.36 | 23.077% | 23.255% | 1,776.38 | −40.79% |
At 0.1% of the reserve, three quarters of your cost is the fee. At 30%, the fee hardly registers and the curve takes almost a quarter of your money. The last column is what arbitrageurs read: a 300 ETH sale leaves the pool quoting ether about 41% below where it started.
The buy side is not a mirror image in percentage terms. Paying 30,000 USDC, which is 1% of the USDC reserve, buys 9.8716 ETH at 3,039.03 dollars, or 1.30% above spot. The formula is the same with the reserves swapped.
Depth: how much a pool can absorb
Depth, for a pool, means the size of trade that costs a given impact. Set the no-fee impact to 1% and solve: s / (1 + s) = 0.01 gives s = 1.01%. In the example pool that is 10.1 ETH, or about 30,300 dollars, in a pool holding 6 million dollars in total. So a trade of roughly 0.5% of the total staking or liquidity, measured at current market prices. It is the main size metric in DeFi.">value locked costs 1% in price impact before the fee. With the 0.30% fee included, a 1% total cost arrives at s = 0.71%.
Because only the ratio s matters, depth scales linearly with reserves. Selling 20 ETH into our pool means s = 2% and 1.96% of impact, before fees. Double the pool to 2,000 ETH and 6,000,000 USDC, and the same trade costs 0.99%. Twice the money, half the impact. A pool ten times as large would carry a 303,000-dollar trade at that same 1%.
The headline figure of total value locked is a poor guide to depth once you leave this simple model. In a constant product pool it maps directly to the curve, as shown here. In a pool with concentrated liquidity, which arrives in a later lesson, two pools of equal value can have very different depth around the current price. The swap interface’s estimate, or a direct simulation, is always better than a guess from the pool’s size.
Order books measure depth differently. There, depth is whatever makers have chosen to post at each price, and it can vanish in a second when they cancel. A constant product pool cannot withdraw its quotes between two trades without an LP removing funds, but it also spreads its capital across every possible price, a cost measured further down.
How fees make k grow
Without a fee, k would be constant and LPs would earn nothing. With the fee, the input stays in the pool in full while the output is priced on 99.7% of it, so the product rises with each trade. That is why the whitepaper describes the invariant as a product of reserves that cannot decrease, rather than one that never changes.
| Trade (ETH sold, example pool) | k before, millions | k after, millions | Growth of k |
|---|---|---|---|
| 1 | 3,000.000 | 3,000.009 | 0.0003% |
| 10 | 3,000.000 | 3,000.089 | 0.0030% |
| 50 | 3,000.000 | 3,000.429 | 0.0143% |
| 100 | 3,000.000 | 3,000.818 | 0.0273% |
| 300 | 3,000.000 | 3,002.078 | 0.0693% |
Growth of k overstates what an LP earns, because k is a product of two reserves. Its square root is the better measure: √k behaves like the size of the pool, and it scales in line with LP shares. It is also what the v2 contract uses to measure fee income, and the protocol fee was designed around it.
A worked case. Run 1,000 round trips through the example pool, each selling 1 ETH and then swapping all the USDC received back. That is about 6 million dollars of volume against a 6-million-dollar pool. By our calculation k ends up 0.599% higher and √k 0.299% higher, which is nearly exactly the 0.30% fee on one turnover of the pool. Fee yield in a constant product pool, then, is roughly the LPs’ fee rate × volume / pool value. In this simplified run the reserves show the whole gain as extra ETH, 5.99 of it, which is also why the spot price drifted down.
On Ethereum mainnet the fee no longer all reaches LPs. The v2 design includes a switchable protocol fee of 5 basis points, one sixth of the 30, and the whitepaper says it starts off. It stayed off until Uniswap governance passed the UNIfication proposal, executed in late December 2025: on mainnet v2 pools, LPs now get 0.25% and the protocol 0.05%, while traders still pay 0.30%. Other chains were left to later votes. Fee tiers other than 0.30% came with v3, whose March 2021 whitepaper starts with 0.05%, 0.30% and 1%; governance added a 0.01% tier in December 2021.
Arbitrage keeps the pool at the market price
The pool does not know the market has moved, so someone has to tell it, and that someone gets paid. The v2 whitepaper says arbitrageurs trade whenever the price is wrong by enough to cover the fee, so the pool price tends to track the market.
Say ether jumps to 3,060 dollars on other venues (an example) while our pool still shows 3,000. Without a fee, an arbitrageur buys until spot reaches 3,060, which means until the ETH reserve equals √(k/p). That reserve is 990.15, so 9.85 ETH leave the pool for 29,851.48 USDC. Sold outside at 3,060, they fetch 30,148.52 USDC, a profit of 297.04 dollars.
With the 0.30% fee, the last unit bought costs spot / 0.997, so buying stops when the price on the curve reaches 3,060 × 0.997 = 3,050.82. Add the fee kept in the USDC reserve and the pool ends up quoting about 3,050.90 rather than 3,060. The best trade is about 25,379 USDC in for 8.36 ETH out, worth about 25,593 dollars, for a profit near 214 dollars before gas. So the fee creates a band. From a spot of 3,000, no trade is profitable while the market stays between 2,991 and 3,009.03 dollars, or within about 0.30% either way, and that gap widens with gas costs.
Arbitrage also needs surprisingly little capital. When both legs happen on-chain, a flash loan can fund the trade inside one transaction. Competition among bots then pushes part of the profit towards whoever orders the block.
The arbitrageur’s 214 dollars came from the LPs. The pool sold 8.36 ETH at an average of about 3,034 dollars while ether was worth 3,060, and that gap is their loss even after keeping the fee on the trade. The pool always sells too low and buys too high, because it updates its price only after someone has already traded against the stale one.
Loss-versus-rebalancing: the bill for quoting a stale price
For years, LP losses were discussed mainly through impermanent loss, the gap against simply holding the tokens, which a later lesson derives in full. In a paper first posted in August 2022 and revised as recently as 2026, Jason Milionis, Ciamac Moallemi, Tim Roughgarden and Anthony Lee Zhang proposed a sharper benchmark: compare the pool with a strategy that holds exactly the same amounts at each moment but rebalances at market prices. The difference, loss-versus-rebalancing or LVR, is what LPs lose to arbitrageurs.
For a constant product pool the result is short. In the paper’s Example 3, LVR per unit of time, as a fraction of pool value, equals σ²/8, one eighth of the instantaneous variance, where σ is the volatility of the price. The authors’ own illustration: a daily volatility of 5% implies a loss of about 3.125 basis points of pool value per day.
| Annual volatility (assumed) | LVR, % of pool value per year | LVR, % of pool value per day |
|---|---|---|
| 40% | 2.0% | 0.0055% |
| 60% | 4.5% | 0.0123% |
| 80% | 8.0% | 0.0219% |
The table is our arithmetic, using σ²/8 and 365 days. Read it against the fee formula from earlier. At 60% volatility, a pool whose LPs keep 0.30% needs volume of about 15 times its value per year, roughly 4% a day, for fees to match LVR. At the 0.25% LPs now keep on mainnet v2, that becomes about 18 times, or 5% a day.
The model is an idealisation, and the authors say so. It runs in continuous time against an infinitely deep outside market, assumes arbitrageurs pay no trading fees, ignores gas and ignores blocks. The paper notes that counting fees would push arbitrage profits below its expressions, which is the fee band above at work. Treat these percentages as model estimates, not measured losses.
The authors also split LP returns into a beta-like part, which is market exposure, and an alpha-like part, fees minus losses to arbitrageurs. On the Uniswap v2 ETH-USDC pool, they write, “over 99.991% of LP return variance is driven by beta exposure to market risk.” That concerns variance on one pool in their sample, not profit. Day to day, a swing in an LP’s position is almost all market move, and what the LP earns or loses as a market maker sits underneath it.

Where the curve runs out
A constant product pool always quotes a price, with no one managing it. Its costs follow from the same hyperbola.
First, the capital sits mostly idle. The v3 whitepaper says of the v1 and v2 formula that “only a fraction of the assets in the pool are available at a given price.” You can measure it. For the spot in our pool to rise 5%, from 3,000 to 3,150 dollars, the ETH reserve falls from 1,000 to 975.9. Just 2.4% of the ether is used across that whole 5% move. The rest waits for prices the pair may never see. Concentrated liquidity, the subject of a later lesson, was built to fix this.
Second, the curve fits some pairs badly. Two stablecoins that should trade near 1:1 gain little from a formula that also prices the pair at 1:100. Specialised stable curves exist for that reason, and they too come in a later lesson.
Third, thin pools are dangerous by construction. A pool of 20,000 dollars gives 1% impact, before the fee, at about 100 dollars. The formula does not warn anyone; it fills the order at whatever price the curve says.
Fourth, the pool is passive between blocks. It cannot adjust its quote when news arrives, so every price move is first captured by an arbitrageur. The LVR formula shows that bill growing with variance, at the same percentage whatever the size of the pool.
Why on-chain order books came back on fast chains
An order book asks more from its chain than a pool does. A maker has to post, cancel and re-post quotes every time the market moves. On Ethereum, blocks arrive every 12 seconds and each of those actions would be a paid transaction, so a quote could sit stale for seconds with no cheap way to pull it. This is our reasoning, not a sourced finding, but it fits the history: an automated market maker needs no updates at all, which is why it won on a slow, costly base layer.
Shorter blocks and lower fees weaken that argument. On a layer 2 like Base, blocks are built every 2 seconds, and its Flashblocks feature brings effective block times down to 200 milliseconds through preconfirmations. Yet in the figures we could check, the order books that matter did not grow on general-purpose layer 2s. They grew on chains built around them. Hyperliquid and dYdX run their own. Lighter, whose spot book appears in the table below, is a rollup on Ethereum, but L2BEAT classes it as application-specific: a layer 2 built for one exchange.
Hyperliquid is the clearest case. Its documentation says that “HyperCore does not rely on the crutch of off-chain order books”, puts median end-to-end latency for a co-located client at 0.2 seconds, with a 99th percentile of 0.9 seconds, and says mainnet handles about 200,000 orders per second. Those are the project’s own numbers.
dYdX went a different way. Its chain, built with Cosmos SDK and CometBFT, keeps part of the book away from consensus: according to its documentation, short-term orders stay in validators’ memory for up to 20 blocks, with only their fill amount and expiry committed to state, while long-term orders go on-chain. “On-chain order book” therefore covers a range of designs, so ask of any venue what exactly is on-chain. At about 10:00 UTC on 30 October 2026 the public dYdX indexer showed the chain still producing blocks, with about 82 million dollars of perpetuals volume over 24 hours across 296 markets, by our own addition.
Solana runs books directly on a general-purpose base layer. Phoenix’s original contract, now labelled Legacy, is described as an on-chain order book that works without a crank, and Phoenix today presents itself as a perpetual futures exchange. Manifest promises a book with no trading fees. The spot volumes below tell a more awkward story.
| Venue | Model | 30-day volume, million dollars |
|---|---|---|
| Uniswap V3 | AMM, concentrated | 32,650 |
| Uniswap V4 | AMM with hooks | 32,492 |
| PancakeSwap AMM V3 | AMM, concentrated | 16,338 |
| Aerodrome Slipstream | AMM, concentrated | 13,046 |
| PumpSwap | AMM | 8,952 |
| Orca | AMM, concentrated | 7,477 |
| Raydium AMM | AMM | 5,236 |
| Hyperliquid spot | Order book, own chain | 3,874 |
| Manifest | Order book, Solana | 3,793 |
| Curve DEX | AMM, stable and crypto pools | 3,221 |
| Lighter spot | Order book | 275 |
| Phoenix spot | Order book, Solana | 1.3 |
As of 30 October 2026, according to DefiLlama, spot trading still lives mostly in pools. The table is a selection, not a ranking, from about 211 billion dollars of 30-day volume filed under DEXs. Uniswap alone reports about 65.8 billion dollars across its versions, against 3.9 billion for Hyperliquid’s spot book. Phoenix’s spot fills come to about 1.3 million dollars, a very small figure that does not count its perpetuals. Manifest’s 3.8 billion is reported, not explained. We could not establish what lies behind it, and a venue with no trading fees makes churning volume cheap, so read it with the usual caution about wash trading. We have no evidence that it applies here.
The comeback is really about perpetual futures. Summing the 235 markets returned by the public info API of Hyperliquid at about 10:00 UTC on 30 October 2026 gives about 16.5 billion dollars of perpetuals volume over the previous 24 hours and about 11 billion dollars of open interest, counted as open contracts times the oracle price. That sum is ours, not a figure Hyperliquid publishes, and it covers the main market list only: ten separately deployed lists on the same API would add about 5 billion of volume and 4 billion of open interest. One day of notional on leveraged contracts cannot be set directly against a month of spot, but the direction is plain. Our reading of why: perps market makers cancel and re-quote constantly, and their customers want tight spreads. Hyperliquid’s base fee schedule charges 0.045% taker and 0.015% maker on perps and 0.070% and 0.040% on spot at the first tier, against a flat 0.30% plus price impact on a Uniswap v2 pool.
That comparison has limits of its own, since v3 pools can charge as little as 0.01%. And the two designs do not do the same job.
| Constant product AMM | On-chain order book | |
|---|---|---|
| Who sets the price | The reserves, through a formula | Makers, through their quotes |
| Who provides liquidity | Any LP, passively | Mostly active market makers |
| New or thin token | Works from the first deposit | Needs a maker willing to quote |
| Cost to quote | None once deposited | One action per quote, needs cheap blockspace |
| Reaction to news | None until arbitrage arrives | Makers can cancel or re-quote |
| Main loss for the provider | LVR | Being picked off on stale quotes |
The AMM kept the long tail of tokens and most of the spot volume in the figures above. Order books took the flow where speed matters most.
What the formula changes when you swap
- Compare your trade with the reserve of the token you are selling. Below about 0.3% of it, the fee is your larger cost; above that, the curve is.
- Read the price impact line on the swap screen as an output of the formula above, for your size and that pool, and treat a large figure on a small trade as a sign of a thin pool.
- Splitting a large order helps only if the pool recovers between the pieces, usually through arbitrage, and each piece pays its own fee and gas. Aggregators, the subject of a later lesson, split across pools instead.
- Check which pool you are hitting. Low fee tiers can have far less liquidity than the 0.30% pool for the same pair, and the total cost is fee plus impact.
None of this replaces the safety routine from the previous lesson. It only tells you what the numbers on the screen mean. On tax, a swap is also a disposal: the IRS lists exchanging one digital asset for another as a disposition that must be reported, whether or not there is a gain. Details are in our guides to crypto taxes in the United States and crypto taxes in the United Kingdom. The next lesson takes the LP’s side of the same curve and proves the impermanent loss formula.



