Skip to content

AMMs & Uniswap's x·y = k

The previous page gave us the two things every DeFi application is built from: a common shape for fungible value (ERC-20) and a common shape for a distinct thing (ERC-721/1155). We now have tokens. This page asks the next obvious question — how do you trade one token for another on a chain where every operation costs gas and every step is public?

The naive answer is an order book: a big list of resting buy and sell orders, matched by price. That is how every stock exchange on Earth works. It is also, as we will see, almost unworkable on Ethereum. The Automated Market Maker (AMM) throws the order book away entirely and replaces the human counterparty with a contract holding a pool of two tokens and one line of arithmetic. Uniswap made that line x·y = k. It is the most important equation in DeFi, and by the end of this page you will be able to derive every price, every fee, and every loss from it.

An order book is a mutable, high-churn data structure. To run one you must:

  • store thousands of resting orders,
  • let anyone add, cancel, or amend an order at any time,
  • match incoming orders against the book, fastest price wins.

On a centralized exchange this happens in a datacenter, in microseconds, for free. On Ethereum, every one of those actions is a state write that costs gas and waits for a block. Placing an order is a transaction. Cancelling it is another transaction. A market maker refreshing quotes as the price moves would send hundreds of transactions a minute and pay gas for each — most of which never fill. Worse, because the mempool is public and blocks are ~12 seconds apart, anyone can see your order before it’s mined and trade ahead of it.

So the throughline of this whole book bites hard here: what does it cost to run this on a shared world computer? An order book costs too much. The AMM’s entire reason to exist is to price a trade with one storage read and one storage write, no matter how many people are trading.

ORDER BOOK AUTOMATED MARKET MAKER
---------- ----------------------
many resting orders one pool: (reserve_x, reserve_y)
match against counterparties trade against a formula
N txs to keep quotes fresh 0 txs — the curve re-quotes itself
needs an active market maker passive LPs deposit and walk away

Strip the AMM to its core. A Uniswap v2 pool is a contract that holds a reserve of two ERC-20 tokens — call them X and Y — and offers exactly one service: give me some X and I’ll give you some Y, or vice versa. There is no counterparty on the other side of your trade. You trade against the pool itself.

The only rule the contract enforces is an invariant — a quantity that must be the same before and after every swap:

$$ x \cdot y = k $$

where x is the reserve of token X, y is the reserve of token Y, and k is a constant. That’s it. That single constraint is a complete, self-pricing market. Let’s see why.

Suppose the pool holds x = 1000 DAI and y = 10 ETH. Then k = 1000 · 10 = 10000. The pool doesn’t store a price anywhere — the price is simply the ratio of reserves:

price of ETH in DAI = x / y = 1000 / 10 = 100 DAI per ETH

Now a trader wants to buy 1 ETH. They can’t just take it — that would leave x·y = 1000·9 = 9000 ≠ k. To keep the product at 10000, they must add enough DAI that the product is restored:

after the trade, reserves must satisfy: x' · y' = k
they take 1 ETH, so y' = 10 - 1 = 9
solve for x': x' = k / y' = 10000 / 9 ≈ 1111.11
so they must pay: x' - x = 1111.11 - 1000 = 111.11 DAI

They wanted 1 ETH quoted at 100 DAI, but they paid 111.11 DAI — because the act of buying moved the reserves, and the ratio x'/y' is now 1111.11 / 9 ≈ 123.5 DAI/ETH. The price rose as they bought. This is not a bug. It is the mechanism that lets a pool with finite reserves quote a price for any trade size without ever running dry: as one reserve shrinks toward zero, its price rises toward infinity, so the pool can never be fully drained. That guaranteed liquidity is the whole magic of the constant-product curve.

y (ETH)
│* the curve x·y = k
│ *
│ *.
│ '*. a swap slides you ALONG the curve:
│ '*.. you give Δx, you receive Δy, product stays k
│ ''*..
│ ''''*....
│ '''''''*........
└───────────────────────────────────── x (DAI)

The gap between the quoted price and the price you actually paid is slippage, and it is a direct consequence of the curve. A small trade barely moves the reserves, so it executes near the quoted ratio. A large trade travels a long way along the curve and pays a much worse average price.

Because slippage grows with size, real AMM front-ends let you set a slippage tolerance — a minimum amount of output you’ll accept. If the price moves against you between signing and mining (someone else traded first), the swap reverts rather than filling at a terrible rate. That revert is the contract protecting you from the very price impact the curve creates.

The fee: what pays the liquidity providers

Section titled “The fee: what pays the liquidity providers”

So far the pool is a perfectly balanced machine that gives nothing to whoever stocked it. In reality every swap charges a fee — classically 0.30% in Uniswap v2 — taken from the input token and left inside the pool. The invariant the contract actually checks is the reserves after adding the input minus the fee:

fee = 0.30% → only 99.70% of your input counts toward the swap
amount_in_with_fee = amount_in · 997 / 1000
require: (x + amount_in_with_fee) · (y - amount_out) ≥ x · y

The fee never leaves the pool, so k grows slightly with every trade. The extra tokens accrue to the people who deposited the reserves — the liquidity providers. More on them next. The elegance: the same one-line invariant both prices the trade and pays the market’s suppliers, with no order book, no matching engine, and no operator.

Under the hood — the swap in a single call

Section titled “Under the hood — the swap in a single call”

A Uniswap v2 swap is one function. The router computes how much you get, then calls the pair, which enforces the invariant:

// Given an input amount and reserves, how much output? (the 0.30% fee is baked in)
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut)
internal pure returns (uint amountOut)
{
uint amountInWithFee = amountIn * 997;
uint numerator = amountInWithFee * reserveOut;
uint denominator = reserveIn * 1000 + amountInWithFee;
amountOut = numerator / denominator; // <= keeps (x')(y') >= k
}

Everything you traded through above — price, slippage, fee — is those five lines. There is no hidden state, no clock, no oracle. The price is always whatever the reserves say, computable by anyone reading the chain. That last property matters enormously two pages from now.

Someone has to put the two tokens into the pool. Those someones are liquidity providers (LPs). An LP deposits both tokens in the current ratio — e.g. 1000 DAI and 10 ETH — and in return the pool mints them an LP token: an ERC-20 (the very standard from the last page) representing their fractional share of the reserves. Hold 10% of the LP tokens, own 10% of the pool. When they want out, they burn the LP token and withdraw their share of whatever the reserves now are, plus their share of every fee earned since.

That is the reward. The risk has a deceptively gentle name: impermanent loss.

The pool is a machine that automatically rebalances toward whatever token is falling in price. When ETH rises, arbitrageurs buy ETH out of the pool (restoring the curve) until the pool’s ratio matches the outside market — which means the pool ends up holding less of the token that went up and more of the token that went down. An LP therefore always ends up with fewer of the winners and more of the losers than if they had simply held their two piles of tokens in a wallet.

The “loss” is that gap, measured against just HODLing the same two tokens:

The shape to remember: impermanent loss is symmetric and grows with how far the price moves. It is zero if the price returns to where you entered, small for a 2x move, and brutal for a 10x. An LP is, in effect, selling volatility — collecting fees in calm markets, paying it back in trends.

Composability: a pool is just tokens in a contract

Section titled “Composability: a pool is just tokens in a contract”

Step back and notice what an AMM is from the last page’s vocabulary. The reserves are two ERC-20 tokens sitting in a contract’s balances. Your ownership of the pool is itself an ERC-20 (the LP token). Nothing here is special-cased into the protocol — it’s ordinary contracts holding ordinary tokens, callable by anyone, including other contracts.

That is the composability payoff. Because an LP token is a normal ERC-20, you can deposit it into a lending protocol as collateral — which is exactly what the next page explores. Because the pool’s price is just x/y, readable by any contract on-chain, other protocols can ask the pool what something is worth — which is the foundation, and the danger, we unpack in Oracles and lean on again in Stablecoins. AMMs don’t just trade assets; they manufacture prices that the rest of DeFi consumes.

  • Why does it exist? Because an on-chain order book is unaffordable: matching, resting, and cancelling orders are each gas-costing state writes on a 12-second, publicly-visible chain. The AMM prices a trade with one read and one write.
  • What problem does it solve? Continuous, permissionless liquidity for any token pair, with no market maker and no counterparty — a passive pool that always quotes a price and can never be fully drained.
  • What are the trade-offs? Traders pay slippage that grows with size relative to pool depth; liquidity providers bear impermanent loss when the price diverges from their entry, offset only by fees. The tidy curve buys simplicity at the cost of capital efficiency.
  • When should I avoid it? For very large trades in thin pools (slippage dominates), for pairs whose price genuinely trends one direction (impermanent loss dominates), or anywhere you need a manipulation-resistant price — a raw spot AMM quote is not that.
  • What breaks if I remove it? Most of DeFi. Lending needs prices to value collateral, stablecoins need deep swap venues to hold their peg, and aggregators need somewhere to route through. Remove AMMs and the on-chain price layer disappears.
  1. Why is a traditional order book impractical to run directly on Ethereum, and what does the AMM replace it with?
  2. A pool holds 5000 USDC and 25 ETH. What is the spot price of ETH, and what is k? If someone buys 5 ETH, roughly how much USDC do they pay (ignore fees), and why is it more than 5 × spot?
  3. In your own words, what is slippage and what single factor most determines how large it is for a given trade?
  4. Where does the swap fee go, and how does it reach liquidity providers? Why does k grow over time?
  5. Define impermanent loss. Why does it appear only when the price moves, and why is it called “impermanent”?
Show answers
  1. Every order-book action (place, cancel, amend, match) is a gas-costing state write on a slow, publicly-visible chain, so keeping quotes fresh would cost a fortune and expose orders to front-running. The AMM replaces the whole book — and the human counterparty — with a contract holding a two-token pool that prices trades from the invariant x·y = k, using one storage read and one write.
  2. Spot price = x/y = 5000/25 = 200 USDC/ETH, and k = 5000·25 = 125000. Buying 5 ETH leaves y' = 20, so x' = k/y' = 125000/20 = 6250; they pay 6250 − 5000 = 1250 USDC (avg 250/ETH). It exceeds 5 × 200 = 1000 because each ETH removed shifts the reserve ratio upward — the price rises as they buy, the essence of moving along the curve.
  3. Slippage is the gap between the quoted (spot) price and the average price you actually pay, caused by your own trade sliding the reserves along the curve. It is determined mainly by your trade size relative to the pool’s depth — big trades in thin pools slip hard; small trades in deep pools barely move.
  4. The fee (e.g. 0.30%) is skimmed from the input token and left inside the pool, so it never leaves — that makes k grow slightly with every swap. LPs own a fractional share of the reserves via their LP token, so when they withdraw they get their share of the now-larger reserves, fees included.
  5. Impermanent loss is the shortfall between holding an LP position and simply HODLing the same two tokens, caused by arbitrage rebalancing the pool to hold more of the falling token and less of the rising one. It appears only when the price diverges from your entry (zero if it returns), and is “impermanent” because it reverses if the price comes back — becoming permanent only when you withdraw at a divergent price.