Tech 11 min read

Uniswap's x*y=k: product vs sum, short-vol IL, and V3 JIT liquidity

IkesanContents

Uniswap is a decentralized exchange (DEX) on Ethereum that lets you swap ERC-20 tokens directly on-chain.
Unlike a centralized exchange, it has no order book; prices come from a formula that runs automatically.
This kind of design is called an “automated market maker” (AMM), and Uniswap has been the most-used example of it since 2018.

At the center of an AMM sits a “liquidity pool.”
Anyone can deposit two tokens as a pair (ETH and USDC, for example) and receive a share of the trading fees in return.
Users who want to swap put one token into the pool and pull the other one out.
The rule that governs how the pool’s two balances are allowed to move is the constant product model x * y = k, used in Uniswap V2.

x * y = k is not a way to pull external market prices into the contract.
It is the rule that decides, from the two token balances currently in the pool, how far the next trade can move the price.
This article walks through how the formula shows up in swap prices and impermanent loss, and how it leads into V3/V4 concentrated liquidity, with numeric examples along the way.

k is the value the curve preserves before and after a trade

In a two-token pool, call one balance x and the other y.
Uniswap’s docs describe a swap as something that keeps this product constant.

x * y = k

In an ETH/USDC pool, say a user puts USDC in and takes ETH out.
USDC in the pool goes up, ETH goes down.
For the product to stay the same, the amount of ETH the pool can give back is determined by:

(x - Δx) * (y + Δy) = x * y

Here Δy is the user’s input, Δx is the output. Solving for Δx:

Δx = x * Δy / (y + Δy)

For small trades, the price is close to the current ratio y / x.
For larger trades, the y + Δy in the denominator grows and you receive proportionally less Δx.
That is what AMM “price impact” actually is.

An order book matches incoming orders against resting orders, starting from the best bid/ask and walking outward.
A constant product AMM has no book; it has a curve.
Near the current price the curve is nearly linear, and the further out you push the pool, the steeper it gets.

Same pool, different trade sizes

Plug in concrete numbers and the curve’s effect becomes obvious.
Say an ETH/USDC pool holds x = 100 ETH and y = 200,000 USDC.
That gives k = 20,000,000 and a spot price of y / x = 2,000 USDC/ETH.

A small trade: swap 1,000 USDC for ETH.

Δx = 100 * 1,000 / (200,000 + 1,000)
   ≈ 0.4975 ETH
P_eff = 1,000 / 0.4975 ≈ 2,010 USDC/ETH

About 0.5% off spot. Within noise for most purposes.
After the trade the pool holds 99.5025 ETH / 201,000 USDC, with a new spot price near 2,020 USDC/ETH.

Now push 100,000 USDC into the same pool in a single trade.

Δx = 100 * 100,000 / (200,000 + 100,000)
   ≈ 33.33 ETH
P_eff = 100,000 / 33.33 ≈ 3,000 USDC/ETH

Spot price was 2,000, effective price is 3,000 — a 50% degradation.
After the trade the pool is 66.67 ETH / 300,000 USDC and spot price jumps to 4,500 USDC/ETH.
Same pool, same formula, same k — only the size of the input relative to the pool depth changes the outcome.

This is exactly what a DEX UI’s “price impact” number is showing.
While a trade is small relative to pool depth the curve looks linear; once you move a meaningful fraction of the pool in one shot, the curve gets steep.

What breaks if you use x+y=k instead

The constant product is just one possible curve.
If the only requirement is preserving some invariant after a swap, a sum or a sum-of-squares would also “work” mathematically.
Uniswap settled on x * y = k because the alternatives break in concrete ways.

Consider a constant sum, x + y = k.
The spot price is always 1 — no matter how much you trade, the price never moves.
That looks convenient for two stablecoins at parity, but if someone keeps buying one side, that token’s balance hits exactly zero.
Once it’s drained the pool can’t give you that token back; the pool is dead.

A constant product never drains.
x * y = k requires y to grow exponentially as x shrinks, so completely emptying one side would take infinite input.
The trade-off is that prices range continuously from 0 to infinity.
Unlike an order book, where the listed price range is finite, an AMM will produce some price for any trade in any market state.

Curve’s StableSwap actually sits between the two extremes.
Near a 1:1 ratio it behaves more like constant sum (low slippage between pegged assets); once the pair diverges, it leans toward constant product to avoid draining.
The right framing isn’t “constant product is correct,” it’s “pick the curve that matches your expected price distribution.”

When fees enter, k does not stay flat — it grows

The explanatory form x * y = k ignores fees, but you can’t ignore them when you implement.
Uniswap’s docs describe k as a value that either stays the same or grows after a trade.

With fees, not all of the user’s input is used in the output calculation.
For a 0.3% fee, 99.7% of the input goes into the math, and the rest stays in the pool.
On the idealized curve the product is preserved, but the actual product computed from real on-chain balances drifts upward over time.

If you skip this you get simulations that say “the trade goes through” while production has it failing.
Anyone building a router or aggregator needs to handle fee deduction, rounding, token decimals, and current pool balance in the same order the contract does — not just solve x * y = k.

Price impact is just trade size divided by pool depth

The spot price of a constant product AMM is approximated by the current balance ratio.
What a user actually pays, though, is an average price along the path on the curve from start to finish.

The effective price formula is straightforward.

P_eff = Δy / Δx

The spot price can briefly look reasonable even on a thin pool.
Send a real-sized order through and you move along the curve quickly, so the executed price degrades.
A DEX UI’s “price impact” number is the user-facing form of that gap.

Don’t rely on the spot price alone — look at the amountOut you’d receive for your specific size, the minimum-receive value, and the pool’s balances before and after.
Low-liquidity tokens “fly” not because of sentiment but because of how this curve looks at low depth.

Impermanent loss comes from external prices being followed

An AMM doesn’t know external market prices.
Prices converge anyway because arbitrageurs trade against the pool.

If ETH rises on a centralized exchange, ETH inside the AMM is now relatively cheap.
Arbitrageurs buy ETH from the AMM and put USDC in.
The pool’s ETH balance falls, USDC rises, and the ratio drifts toward the external price.

From an LP’s perspective, this process shifts the portfolio toward one side.
Compared to simply holding ETH and USDC outside the pool, the LP’s position can end up worth less.
That gap is impermanent loss.

“Impermanent” sounds like it goes away, but if you withdraw while prices haven’t reverted, the loss is real.
Whether trading fees cover the gap determines whether providing liquidity comes out positive.

For a constant product pool, the loss has a closed-form expression in terms of the price ratio r vs the deposit time.

IL(r) = 2 * sqrt(r) / (1 + r) - 1

Plugging in numbers gives a sense of the scale.

Price ratio rIL
1.0x0%
1.25x-0.6%
2x-5.7%
4x-20.0%
5x-25.5%

Larger moves cost more, but even a 2x move already costs about -5.7%.
Whether trading fees can cover that gap decides whether the LP ends up profitable.

Mathematically, the value of an LP position in a constant product pool scales with √r.
As the price of one side rises, the pool automatically sells that side, so total value lags behind simply holding.
That payoff shape is the same as a “short gamma” position in options markets.
Put differently, providing liquidity is, structurally, selling volatility without knowing it.
When realized volatility is higher than expected, fees can’t keep up with IL; when it’s lower, IL is small but fees are also small.

Empirically, the same picture shows up in data.
A 2021 Topaze Blue study found that the majority of Uniswap V3 LP positions underperformed simply holding the same assets.
Concentrated liquidity raises fee efficiency, but the narrower the range, the more often the position sits outside its range — earning no fees and stuck on one side.
”Impermanent” assumes mean reversion; if prices don’t come back, withdrawing locks the loss in.

Concentrated liquidity re-places the curve into a narrow band

What changed with Uniswap V3 wasn’t dropping the constant product idea.
The concentrated liquidity docs describe V3 and V4 as still using the same formula, only applied inside whatever price range the LP chose.

In V2, liquidity sits across all prices from 0 to infinity.
Even for two stablecoins that move within a tight band, most of the capital is parked at prices that will never trade.
The same docs note that on V2’s DAI/USDC pool, only about 0.50% of capital was actually being used in the 0.99–1.01 USD range.

In V3, the LP picks the price range.
Put capital between 0.99 and 1.01, and within that range it acts as a deep market.
If the price exits the range, the position becomes one-sided and stops earning fees.

For pegged or low-vol pairs, narrowing the range improves both execution and fee efficiency.
For volatile pairs, the position needs active management.
The “leave it there” liquidity of V2 and the “pick your range” liquidity of V3 behave differently even though they sit on the same curve.

JIT liquidity slips into the cracks of concentrated liquidity

One side effect of concentrated liquidity is JIT (Just-In-Time) liquidity.
MEV (Maximal Extractable Value) searchers watch the public mempool for large pending swaps and, within the same block, do roughly this:

  1. Right before the target swap, add deep liquidity in an extremely narrow range covering the current price
  2. The target swap executes, and a large share of its fees is allocated to this newly added position
  3. Immediately after, in the same block, the position is removed

The position is on the book for one block, so price exposure is near zero and IL is essentially nothing.
The reward is the share of fees from that large swap proportional to the JIT liquidity’s slice.

From a retail LP’s perspective, the fee income they would have earned by being continuously deposited is captured by the JIT side at exactly the moment of the big trade.
The ongoing risks of providing liquidity (IL, opportunity cost, gas) don’t change; only the most profitable slice of the revenue is skimmed off.

Externally JIT looks like using the AMM as designed, so banning it inside the contract is hard.
Uniswap V4 hooks let pool creators inject custom logic before/after swaps and around liquidity adds/removes, so in principle you can charge an extra fee for same-block add-and-remove patterns.
On the other hand, JIT does temporarily improve slippage for the swapping user.
Whether that’s a net negative depends on whose interests you weigh — LPs lose fees, swappers get a tighter execution.

amountOutMinimum stops the trade outside the curve’s reasonable range

The AMM math is only as good as the pool state at the moment your transaction lands.
If other transactions execute before yours in the same block, the balances shift and the same input now produces less output.

That’s why every swap carries a minimum receive amount.
Uniswap-style implementations and routers pass a value like amountOutMinimum, computed by subtracting a slippage tolerance from the expected output.
If the actual output falls below it, the transaction reverts.

amountOutMinimum = expectedAmountOut * (1 - slippageTolerance)

If you set this too loose, you get filled at a bad price after surrounding transactions move the pool.
The textbook version is a sandwich attack: an MEV searcher sees your swap in the mempool, front-runs you to push the price the way that hurts you, and back-runs to dump after.
A 5% or 10% slippage tolerance hands the attacker the entire spread inside that window.
Set it too tight and ordinary market volatility makes your transaction revert, so the value has to be picked per-trade by the user or the app based on current price, pool depth, and gas priority.

The AMM math only answers “given the current pool, how much do I get out?"
"How much degradation is acceptable before refusing?” is decided on a separate layer entirely.