🎓 A1Learner
🔍
0 of 45 lessons completed

Solidity AMM Math in Depth

An automated market maker is a pool that prices a swap from its own token reserves instead of from an order book. Its constant product rule keeps the product of the two reserves fixed while a swap moves the ratio.

The constant product formula gives the exact swap output

With reserves of 1000 and 1000, swapping in 100 returns 90. The formula is y times dx divided by x plus dx.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract AmmMath {
    function quoteOut(uint256 dx, uint256 x, uint256 y) public pure returns (uint256) {
        return (y * dx) / (x + dx);
    }

    function quoteOutWithFee(uint256 dx, uint256 x, uint256 y) public pure returns (uint256) {
        uint256 dxAfterFee = (dx * 997) / 1000;
        return (y * dxAfterFee) / (x + dxAfterFee);
    }
}

A fee shrinks the input before the formula runs

A 0.3 percent fee turns an input of 100 into 99 before the formula runs. The fee version returns 180 where the fee-free version returns 181.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract AmmMath {
    function quoteOut(uint256 dx, uint256 x, uint256 y) public pure returns (uint256) {
        return (y * dx) / (x + dx);
    }

    function quoteOutWithFee(uint256 dx, uint256 x, uint256 y) public pure returns (uint256) {
        uint256 dxAfterFee = (dx * 997) / 1000;
        return (y * dxAfterFee) / (x + dxAfterFee);
    }
}

A second swap gets a worse price than the first

The first swap moves the reserves, so the second swap starts from a worse ratio. The same input of 100 returns 90 first and only 75 second.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract AmmPool {
    uint256 public reserveX;
    uint256 public reserveY;

    constructor(uint256 x, uint256 y) {
        reserveX = x;
        reserveY = y;
    }

    function swap(uint256 dx) public returns (uint256 dy) {
        dy = (reserveY * dx) / (reserveX + dx);
        reserveX += dx;
        reserveY -= dy;
    }
}

Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

contract AmmPoolWithFee {
    uint256 public reserveX;
    uint256 public reserveY;

    constructor(uint256 x, uint256 y) {
        reserveX = x;
        reserveY = y;
    }

    function swap(uint256 dx) public returns (uint256 dy) {
        uint256 dxAfterFee = (dx * 997) / 1000;
        dy = (reserveY * dxAfterFee) / (reserveX + dxAfterFee);
        reserveX += dx;
        reserveY -= dy;
    }
}

Key points

  • AMM: An automated market maker is a pool that prices swaps from its own reserves.
  • Constant product: The constant product rule keeps x times y equal to a fixed number k.
  • Swap output: A swap of dx tokens in returns dy equal to y times dx divided by x plus dx.
  • Fee: A fee reduces the input first, so the output is computed from the smaller input.
  • Slippage: Slippage is the worse average price a large swap pays compared with a small swap.
  • Restatement: A pool prices every trade from its reserves, and each trade moves those reserves.

In this example, the pool starts with reserves of 1000 and 2000. A swap of 100 first reduces the input to 99 by the fee, then applies the constant product formula to give 180 out. The reserves update to 1100 and 1820, so a second swap of 100 starts from a worse ratio and returns less.

Note

Integer division truncates, so the contract output is the exact truncated value, not a rounded decimal.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Remix IDE

Exercise

Reserves are 1000 and 1000. What does a swap of 100 tokens in return before any fee?