DEV Community

Maria Kalala
Maria Kalala

Posted on

Understanding the Constant Product Formula in AMMs (Without Getting Tricked by k)

If you’re learning how Automated Market Makers (AMMs) like Uniswap work, one thing can feel weird at first:

Why do we use the old k when calculating swap outputs?

At a glance, it feels more “correct” to recompute k after adding tokens but doing that completely breaks the swap logic.

Let’s walk through why the invariant must stay constant, step by step, with real numbers and Solidity-style math.

The Core Idea: What Does x * y = k Actually Mean?

In a constant product AMM:

x * y = k

  • x = reserve of token A
  • y = reserve of token B
  • k = a constant

The key invariant is simple:
During a swap, k must remain constant

This doesn’t mean reserves don’t change, they do.
It means the product of the reserves stays the same.
That’s the entire pricing mechanism.

Initial State

Let’s start with a simple pool:

reserveA = 1000
reserveB = 1000
Enter fullscreen mode Exit fullscreen mode

Compute k:
k = reserveA * reserveB = 1_000_000;
This k defines the curve the pool must stay on.

A User Swaps Token A for Token B
The user swaps:

amountAIn = 100
Enter fullscreen mode Exit fullscreen mode

Step 1: Use the current invariant

uint256 k = reserveA * reserveB; // 1,000,000
This is the only valid k for this swap.

Step 2: Add the incoming tokens

uint256 newReserveA = reserveA + amountAIn;
// newReserveA = 1100
Enter fullscreen mode Exit fullscreen mode

At this point:
We know the new A reserve
We don’t know the new B reserve yet

Step 3: Solve for the new B reserve

To preserve the invariant:
newReserveA * newReserveB = k
So:

uint256 newReserveB = k / newReserveA;
// newReserveB ≈ 909.09
Enter fullscreen mode Exit fullscreen mode

Step 4: Calculate what the user receives

amountBOut = reserveB - newReserveB;
// ≈ 1000 - 909.09 = 90.91
Enter fullscreen mode Exit fullscreen mode

*The user receives ~90.91 token B
*The pool stays on the same curve

Verifying the Invariant
1100 * 909.09 ≈ 1,000,000

  • The invariant holds.

The Common Mistake: Recomputing k

A lot of people instinctively try this instead:

uint256 newK = newReserveA * reserveB; // 1100 * 1000 = 1,100,000
uint256 newReserveB = newK / newReserveA; // = 1000
Enter fullscreen mode Exit fullscreen mode

Then:

amountBOut = reserveB - newReserveB;
// = 0
Enter fullscreen mode Exit fullscreen mode

The user gets nothing.

Why? Because this logic moves the pool to a new curve, instead of keeping it on the original one. That violates the fundamental invariant.

The Real Swap Equation;

The actual AMM equation during a swap is:

(reserveA + amountIn) * (reserveB - amountOut)
  = reserveA * reserveB
Enter fullscreen mode Exit fullscreen mode

Solving for amountOut:

amountOut =
reserveB -
(reserveA * reserveB) / (reserveA + amountIn)
Enter fullscreen mode Exit fullscreen mode

Which is exactly what Uniswap-style contracts implement.
No magic. Just algebra.

Why the Old k Must Be Used

Here’s the intuition that finally makes it click:

  • k represents the curve
  • A swap moves the pool along the same curve
  • Adding liquidity creates a new curve
  • Swaps do not

If you recalculate k during a swap:
You’re no longer moving along the curve. You’re jumping to a different curve, and pricing instantly breaks.

Mental Model That Helps;

  • Think of k as a rail track.
  • Liquidity providers lay down new tracks (new k)
  • Traders move along the existing track
  • You never redraw the track mid-swap

Takeaway

  • k is not recomputed during swaps
  • The invariant defines the pricing curve
  • Swaps must start and end on the same curve
  • Using a “new k” destroys the AMM logic

If this ever feels counterintuitive again, just remember:

Swaps move the pool along the curve — they don’t redraw it.

Constant Product Curve for AMMs

Top comments (0)