# AMM Logic

[Back to AMM: Automated Market Maker](https://docs.xrpl-commons.org/core-dev-bootcamp/module09bis)

***

## Introduction

The logic behind XRPL's AMM is critical for understanding how prices are determined, LP tokens are calculated, and trades execute. This chapter provides a deep dive into the constant product formula, swap calculations, LP token minting/burning, and the precision handling that ensures numerical correctness.

**Location:** `src/xrpld/app/misc/AMMHelpers.cpp` and `AMMHelpers.h`

## The Constant Product Formula

### Core Invariant

XRPL's AMM uses the **constant product** formula, popularized by Uniswap:

```
x * y = k
```

Where:

* `x` = Balance of Asset 1
* `y` = Balance of Asset 2
* `k` = Constant product (invariant)

### How It Works

After every trade, the product of the two asset balances must remain constant (or increase slightly due to fees):

```
Before trade: A * B = k
After trade:  A' * B' >= k
```

The inequality (`>=`) accounts for trading fees, which slightly increase `k` over time, benefiting liquidity providers.

### Price Determination

The **spot price** of Asset1 in terms of Asset2 is:

```
Price(Asset1) = B / A
```

As traders buy Asset1:

* A decreases (removed from pool)
* B increases (added to pool)
* Price of Asset1 increases (B/A grows)

This creates automatic price discovery through supply and demand.

## Swap Formulas

### Swap Asset In (swapAssetIn)

Given an input amount, calculate the output amount.

**Formula:**

```
out = B - (A * B) / (A + in_fee)
```

Where:

```
in_fee = in * (1 - tradingFee / 100000)
```

**Location:** `src/xrpld/app/misc/AMMHelpers.h:443-505`

```cpp
template <typename TIn, typename TOut>
TOut swapAssetIn(
    TAmounts<TIn, TOut> const& pool,  // {A, B}
    TIn const& assetIn,               // Input amount
    std::uint16_t tfee)               // Trading fee
{
    // Calculate fee-adjusted input
    auto const in_fee = assetIn * feeMult(tfee);

    // Apply constant product formula
    // out = B - (A * B) / (A + in_fee)
    auto const out = pool.out -
        divide(pool.in * pool.out, pool.in + in_fee, ...);

    return out;
}
```

**Example:**

```
Pool: 1000 XRP / 2000 USD
Trading Fee: 0.3% (30 basis points)
Input: 100 XRP

in_fee = 100 * (1 - 30/100000) = 100 * 0.9997 = 99.97 XRP
out = 2000 - (1000 * 2000) / (1000 + 99.97)
out = 2000 - 2000000 / 1099.97
out = 2000 - 1818.23
out = 181.77 USD
```

### Swap Asset Out (swapAssetOut)

Given a desired output amount, calculate the required input.

**Formula:**

```
in = ((A * B) / (B - out) - A) / (1 - fee)
```

**Location:** `src/xrpld/app/misc/AMMHelpers.h:517-578`

```cpp
template <typename TIn, typename TOut>
TIn swapAssetOut(
    TAmounts<TIn, TOut> const& pool,  // {A, B}
    TOut const& assetOut,             // Desired output
    std::uint16_t tfee)               // Trading fee
{
    // Reverse constant product calculation
    auto const in = divide(
        pool.in * pool.out / (pool.out - assetOut) - pool.in,
        feeMult(tfee),
        ...);

    return in;
}
```

### Rounding Strategy

**Critical Design Decision:** Rounding always favors the AMM pool.

* `swapAssetIn`: Output is rounded **down** (less tokens out)
* `swapAssetOut`: Input is rounded **up** (more tokens required)

This ensures the pool never loses value due to rounding errors.

## LP Token Calculations

### Initial LP Tokens (ammLPTokens)

When creating a pool, initial LP tokens are the geometric mean:

```
LPT = sqrt(Asset1 * Asset2)
```

**Location:** `src/xrpld/app/misc/AMMHelpers.cpp:5-17`

```cpp
STAmount ammLPTokens(
    STAmount const& asset1,
    STAmount const& asset2,
    Issue const& lptIssue)
{
    // Geometric mean ensures equal value contribution
    auto const tokens = root2(asset1 * asset2);
    return toSTAmount(lptIssue, tokens);
}
```

**Why Geometric Mean?**

* Prevents manipulation by depositing unequal values
* Initial depositor cannot "steal" value from pool
* LP tokens represent true fractional ownership

### LP Tokens for Deposit (lpTokensOut)

Calculate LP tokens received for depositing assets.

#### Proportional Deposit (Both Assets)

When depositing proportionally (same ratio as pool):

```
LPT_out = LPT_total * (deposit / balance)
```

No trading fee charged for proportional deposits.

#### Single Asset Deposit

**Equation 3** from AMMHelpers.cpp:

```
t = T * [(b/B - (sqrt(f2^2 - b/(B*f1)) - f2)) / (1 + sqrt(f2^2 - b/(B*f1)) - f2)]
```

Where:

* `t` = LP tokens received
* `T` = Total LP tokens
* `b` = Deposit amount
* `B` = Pool balance of deposited asset
* `f1` = 1 - tradingFee
* `f2` = (1 - tradingFee/2) / f1

**Location:** `src/xrpld/app/misc/AMMHelpers.h:131-187`

```cpp
template <typename T>
STAmount lpTokensOut(
    STAmount const& asset,      // Pool balance B
    STAmount const& lptAMMBalance,  // Total LP tokens T
    T const& depositAmount,     // Deposit amount b
    std::uint16_t tfee)         // Trading fee
{
    // Apply Equation 3
    auto const f1 = feeMult(tfee);
    auto const f2 = feeMultHalf(tfee) / f1;
    auto const ratio = depositAmount / asset;

    auto const sqrt_term = root2(f2 * f2 - ratio / f1) - f2;
    auto const lpTokens = lptAMMBalance * (ratio - sqrt_term) / (1 + sqrt_term);

    return lpTokens;
}
```

### Assets Required for LP Tokens (ammAssetIn)

Calculate how much asset is needed for specific LP tokens.

**Equation 4** (inverse of Equation 3):

```cpp
template <typename T>
T ammAssetIn(
    STAmount const& asset,
    STAmount const& lptAMMBalance,
    STAmount const& lpTokens,
    std::uint16_t tfee)
{
    // Inverse calculation to find required deposit
    // for desired LP tokens output
}
```

### LP Tokens to Burn (lpTokensIn)

Calculate LP tokens needed for a specific withdrawal.

**Equation 7:**

```
t = T * (c - sqrt(c^2 - 4*R)) / 2
```

Where:

* `t` = LP tokens to burn
* `T` = Total LP tokens
* `R` = withdrawal / poolBalance
* `c` = R \* fee + 2 - fee

### Assets for LP Token Burn (ammAssetOut)

Calculate assets received for burning LP tokens.

**Equation 8:**

```cpp
template <typename T>
T ammAssetOut(
    STAmount const& asset,
    STAmount const& lptAMMBalance,
    STAmount const& lpTokens,
    std::uint16_t tfee)
{
    // Calculate withdrawal amount for given LP tokens
}
```

## Fee Calculations

### Fee Multipliers

```cpp
// Full fee multiplier: 1 - fee
Number feeMult(std::uint16_t tfee)
{
    return 1 - Number(tfee) / AUCTION_SLOT_FEE_SCALE_FACTOR;
}

// Half fee multiplier: 1 - fee/2 (for proportional operations)
Number feeMultHalf(std::uint16_t tfee)
{
    return 1 - Number(tfee) / (2 * AUCTION_SLOT_FEE_SCALE_FACTOR);
}

// Get fee as decimal
Number getFee(std::uint16_t tfee)
{
    return Number(tfee) / AUCTION_SLOT_FEE_SCALE_FACTOR;
}
```

**Constants:**

```cpp
constexpr std::uint32_t AUCTION_SLOT_FEE_SCALE_FACTOR = 100000;
```

### Fee Ranges

* **Minimum:** 0 (no fee)
* **Maximum:** 1000 basis points (1%)
* **Typical:** 30-100 basis points (0.03% - 0.1%)

### Auction Slot Discount

Auction slot holders pay reduced fees:

```cpp
std::uint16_t getTradingFee(
    ReadView const& view,
    SLE const& ammSle,
    AccountID const& account)
{
    // Check if account is slot holder or authorized
    if (isAuctionSlotHolder(ammSle, account))
        return ammSle[sfAuctionSlot].getFieldU16(sfDiscountedFee);  // Usually 0

    return ammSle.getFieldU16(sfTradingFee);
}
```

## Quality and Price Calculations

### Spot Price Quality

The "quality" represents the exchange rate for offers:

```
Quality = TakerGets / TakerPays
```

For AMM, the spot price quality is:

```
SpotQuality = PoolOut / PoolIn
```

### Quality Matching with CLOB

The pathfinding engine needs AMM offers that match CLOB quality.

**Location:** `src/xrpld/app/misc/AMMHelpers.h:310-420`

```cpp
template <typename TIn, typename TOut>
std::optional<TAmounts<TIn, TOut>>
changeSpotPriceQuality(
    TAmounts<TIn, TOut> const& pool,
    Quality const& quality,          // Target quality (from CLOB)
    std::uint16_t tfee,
    Rules const& rules,
    beast::Journal j)
{
    // Solve quadratic equation to find offer amounts
    // that result in pool having target spot price quality
    // after the trade

    // This enables AMM to compete with CLOB offers
}
```

## Precision and Overflow Handling

### Number Type

XRPL uses a custom `Number` type for AMM calculations:

```cpp
// Number provides arbitrary precision arithmetic
// Essential for avoiding overflow in large pool calculations

Number result = root2(asset1 * asset2);
```

### Precision Amendments

Several amendments improve precision handling:

| Amendment           | Fix                                       |
| ------------------- | ----------------------------------------- |
| fixUniversalNumber  | Required for AMM (precision improvements) |
| fixAMMv1\_1         | Rounding improvements                     |
| fixAMMv1\_3         | Precision loss compensation               |
| fixAMMOverflowOffer | Overflow in offer calculation             |

### Rounding Strategies

```cpp
// Round based on who should benefit
enum class Round {
    upward,    // Round up (more required from user)
    downward   // Round down (less given to user)
};

// Swaps: Round to favor pool
// Deposits: Round to favor pool
// Withdrawals: Round to favor pool
```

### Invariant Checking

After every operation, verify the pool invariant:

```cpp
bool checkInvariant(
    TAmounts<TIn, TOut> const& oldPool,
    TAmounts<TIn, TOut> const& newPool)
{
    // New product must be >= old product
    return (newPool.in * newPool.out) >= (oldPool.in * oldPool.out);
}
```

## Worked Examples

### Example 1: Simple Swap

```
Pool: 10,000 XRP / 20,000 USD
Fee: 0.3% (30 bps)
Swap: 500 XRP -> USD

Step 1: Apply fee
in_fee = 500 * (1 - 0.003) = 498.5 XRP

Step 2: Calculate output
k = 10000 * 20000 = 200,000,000
new_xrp = 10000 + 498.5 = 10498.5
new_usd = k / new_xrp = 200000000 / 10498.5 = 19050.45

out = 20000 - 19050.45 = 949.55 USD

Result: 500 XRP -> 949.55 USD
Effective rate: 1.899 USD/XRP
```

### Example 2: LP Token Minting

```
Pool: 10,000 XRP / 20,000 USD
Total LP Tokens: 14,142.13 (sqrt(10000 * 20000))
Deposit: 1,000 XRP + 2,000 USD (proportional)

Ratio = 1000/10000 = 0.1 (10%)
LP_out = 14142.13 * 0.1 = 1,414.21 LP tokens

New state:
- Pool: 11,000 XRP / 22,000 USD
- Total LP: 15,556.34
- Depositor owns: 1,414.21 / 15,556.34 = 9.09% of pool
```

### Example 3: Single Asset Deposit

```
Pool: 10,000 XRP / 20,000 USD
Total LP: 14,142.13
Fee: 0.3%
Deposit: 1,000 XRP only

Using Equation 3:
f1 = 1 - 0.003 = 0.997
f2 = (1 - 0.0015) / 0.997 = 1.0005
ratio = 1000 / 10000 = 0.1

sqrt_term = sqrt(1.0005^2 - 0.1/0.997) - 1.0005
         = sqrt(1.001 - 0.1003) - 1.0005
         = sqrt(0.9007) - 1.0005
         = 0.949 - 1.0005
         = -0.0515

LP_out = 14142.13 * (0.1 - (-0.0515)) / (1 + (-0.0515))
       = 14142.13 * 0.1515 / 0.9485
       = 2259.3 LP tokens

Note: Single asset deposit gets fewer LP tokens
due to implicit swap + fee
```

## Summary

The AMM mathematics ensure:

1. **Fair Pricing**: Constant product formula provides automatic price discovery
2. **LP Protection**: Geometric mean prevents initial deposit manipulation
3. **Fee Collection**: Trading fees increase pool value over time
4. **Numerical Safety**: Careful rounding always favors the pool
5. **CLOB Integration**: Quality matching enables fair competition

Understanding these formulas is essential for:

* Implementing AMM features
* Debugging price discrepancies
* Building AMM analytics tools
* Auditing pool behavior

## References to Source Code

* `src/xrpld/app/misc/AMMHelpers.cpp` - Core calculations
* `src/xrpld/app/misc/AMMHelpers.h` - Formula implementations
* `src/xrpld/app/misc/AMMCore.h` - Constants
* `src/libxrpl/basics/Number.cpp` - Precision arithmetic

## Cross-References

* [AMM Architecture](https://docs.xrpl-commons.org/core-dev-bootcamp/module09bis/amm-architecture) - Data structures
* [AMM Transactions](https://docs.xrpl-commons.org/core-dev-bootcamp/module09bis/amm-transactions) - How formulas are used
* [AMM Pathfinding](https://docs.xrpl-commons.org/core-dev-bootcamp/module09bis/amm-pathfinding) - Quality matching in practice
