# AMM Architecture

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

***

## Introduction

The XRP Ledger's AMM implementation introduces a sophisticated architecture that coexists with the traditional Central Limit Order Book (CLOB). Unlike simple AMM designs, XRPL's implementation includes governance mechanisms (fee voting), auction slots for reduced fees, and deep integration with the pathfinding engine.

This chapter explores the core data structures, the pseudo-account model, LP token design, and how these components interact to provide decentralized liquidity.

## Core Concepts

### What is an AMM?

An **Automated Market Maker** is a decentralized exchange mechanism that uses a mathematical formula to price assets. Instead of matching buyers and sellers through an order book, AMMs use liquidity pools where:

1. **Liquidity Providers (LPs)** deposit pairs of assets into pools
2. **Traders** swap one asset for another against the pool
3. **Prices** are determined algorithmically based on pool balances
4. **Fees** are distributed to LPs proportionally to their ownership

### XRPL AMM vs. Traditional AMMs

| Feature      | Traditional AMM (Uniswap v2) | XRPL AMM               |
| ------------ | ---------------------------- | ---------------------- |
| Formula      | x \* y = k                   | x \* y = k (same)      |
| Fee Model    | Fixed fee                    | Governance-voted fee   |
| Fee Discount | None                         | Auction slot mechanism |
| Order Book   | Separate                     | Integrated with CLOB   |
| LP Tokens    | ERC-20                       | Native XRPL tokens     |
| Pathfinding  | External                     | Native integration     |

## The ltAMM Ledger Entry

Every AMM pool is represented by an `ltAMM` ledger entry (type `0x0079`). This entry stores the pool configuration, current state, and governance data.

**Location:** `include/xrpl/protocol/detail/ledger_entries.macro`

### Structure

```cpp
LEDGER_ENTRY(ltAMM, 0x0079, AMM, ({
    {sfAccount,           REQUIRED},  // Pseudo-account ID
    {sfTradingFee,        DEFAULT},   // Current trading fee (0-1000 basis points)
    {sfVoteSlots,         OPTIONAL},  // Array of up to 8 VoteEntry objects
    {sfAuctionSlot,       OPTIONAL},  // Auction slot holder and settings
    {sfLPTokenBalance,    REQUIRED},  // Total outstanding LP tokens
    {sfAsset,             REQUIRED},  // First asset issue
    {sfAsset2,            REQUIRED},  // Second asset issue
    {sfOwnerNode,         REQUIRED},  // Directory linkage
    {sfPreviousTxnID,     OPTIONAL},  // Last transaction hash
    {sfPreviousTxnLgrSeq, OPTIONAL},  // Last transaction ledger sequence
}))
```

### Field Details

**sfAccount** - The pseudo-account that holds pool assets. This is a special account created during AMMCreate with no signing keys.

**sfTradingFee** - Current trading fee in basis points (1/100th of a percent). Range: 0-1000 (0% to 1%). Determined by weighted LP voting.

**sfLPTokenBalance** - Total LP tokens outstanding. Represents total pool ownership claims.

**sfAsset / sfAsset2** - The two assets in the pool. Always ordered: Asset < Asset2 (lexicographic ordering ensures uniqueness).

### Keylet Access

```cpp
// Get AMM keylet from two issues
Keylet keylet::amm(Issue const& issue1, Issue const& issue2);

// The issues are automatically ordered internally
// keylet::amm(USD/Gateway, XRP) == keylet::amm(XRP, USD/Gateway)
```

**Location:** `include/xrpl/protocol/Keylet.h`

## Pseudo-Account Architecture

Each AMM pool operates through a **pseudo-account** - a special XRPL account with unique properties.

### Characteristics

1. **No Master Key**: The account's master key is disabled (cannot sign transactions)
2. **AMM-Controlled**: Only AMM transactors can modify its state
3. **Holds Assets**: Contains trustlines for both pool assets and LP tokens
4. **Identified by sfAMMID**: AccountRoot contains `sfAMMID` field linking to ltAMM

### Creation Process (in AMMCreate)

```cpp
// From AMMCreate.cpp - doApply()

// 1. Generate pseudo-account ID from asset pair
auto const ammAccountID = calcAccountID(
    keylet::amm(ctx_.tx[sfAmount], ctx_.tx[sfAmount2]).key);

// 2. Create AccountRoot with disabled master key
auto sleAMMRoot = std::make_shared<SLE>(keylet::account(ammAccountID));
sleAMMRoot->setAccountID(sfAccount, ammAccountID);
sleAMMRoot->setFieldU32(sfSequence, 0);
sleAMMRoot->setFieldAmount(sfBalance, STAmount{});
sleAMMRoot->setFieldU32(sfFlags, lsfDisableMaster);  // No signing!
sleAMMRoot->setFieldH256(sfAMMID, keylet::amm(...).key);

// 3. Insert into ledger
ctx_.view().insert(sleAMMRoot);
```

### Security Model

The pseudo-account design ensures:

* **No External Control**: Without signing keys, no one can directly transact from this account
* **Protocol-Only Access**: Only AMM transactors (AMMDeposit, AMMWithdraw, etc.) can modify balances
* **Trustline Isolation**: Pool assets are isolated in dedicated trustlines

## LP Token Design

LP (Liquidity Provider) tokens represent fractional ownership of pool assets. XRPL implements them as native tokens with a special currency code.

### Currency Code Structure

```cpp
// LP token currency = 0x03 + SHA512Half(sorted asset currencies)

// Construction:
Currency lptCurrency;
lptCurrency[0] = 0x03;  // AMM LP token marker

// Remaining 19 bytes = truncated hash of sorted currencies
auto const hash = sha512Half(
    std::min(currency1, currency2),
    std::max(currency1, currency2)
);
std::memcpy(&lptCurrency[1], hash.data(), 19);
```

**Location:** `src/xrpld/app/misc/AMMUtils.cpp`

### LP Token Properties

| Property      | Value                      |
| ------------- | -------------------------- |
| Currency Code | 0x03 prefix + 19-byte hash |
| Issuer        | AMM pseudo-account         |
| Precision     | 16 significant digits      |
| Transferable  | Yes (standard trustlines)  |
| Tradeable     | Yes (can be traded on DEX) |

### Initial LP Token Calculation

When a pool is created, the initial LP token balance is:

```
LPT_initial = sqrt(Amount1 * Amount2)
```

This geometric mean ensures the initial LP tokens represent equal value from both assets.

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

```cpp
STAmount ammLPTokens(
    STAmount const& asset1,
    STAmount const& asset2,
    Issue const& lptIssue)
{
    // sqrt(asset1 * asset2)
    auto const tokens = root2(asset1 * asset2);
    return toSTAmount(lptIssue, tokens);
}
```

## VoteSlots: Fee Governance

LPs can vote on the trading fee through the `sfVoteSlots` array. This implements decentralized governance for pool parameters.

### Purpose and Use Cases

**Why Governance-Voted Fees?**

Unlike traditional AMMs with fixed fees (e.g., Uniswap's 0.3%), XRPL allows LPs to collectively decide the optimal fee for their pool. This enables:

* **Market-Driven Optimization**: Volatile pairs may need higher fees; stable pairs can have lower fees
* **Competitive Positioning**: LPs can adjust fees to attract more trading volume
* **Risk Compensation**: Higher fees for riskier asset pairs

**When to Use AMMVote:**

* You hold LP tokens and want to influence the pool's trading fee
* You believe the current fee is too high (reducing volume) or too low (not compensating risk)
* You want to align the pool's economics with market conditions

### Structure

```cpp
// VoteEntry fields
struct VoteEntry {
    AccountID Account;     // Voter's account
    uint16_t FeeVal;       // Proposed fee (0-1000 basis points)
    uint32_t VoteWeight;   // LP token ownership (basis points of total)
};

// Up to 8 VoteEntry objects per AMM
constexpr std::uint16_t VOTE_MAX_SLOTS = 8;
```

### Voting Mechanics

1. **Weight Calculation**: Vote weight = (LP balance / Total LP tokens) \* 100000
2. **Fee Calculation**: TradingFee = sum(weight\_i \* fee\_i) / sum(weight\_i)
3. **Slot Management**: If 8 slots full, new voter must have higher weight than lowest existing voter
4. **Instant Effect**: Fee changes immediately when votes are cast or LP balances change

### Example Scenario

```
Pool: XRP/USD
Total LP Tokens: 10,000

VoteSlots State:
┌─────────────────────────────────────────────────────────────┐
│ Slot │ Account  │ LP Balance │ Weight │ Proposed Fee       │
├──────┼──────────┼────────────┼────────┼────────────────────┤
│  1   │ Alice    │   5,000    │  50%   │ 0.3% (30 bps)      │
│  2   │ Bob      │   3,000    │  30%   │ 0.5% (50 bps)      │
│  3   │ Carol    │   1,500    │  15%   │ 0.2% (20 bps)      │
│  4   │ Dave     │     500    │   5%   │ 0.4% (40 bps)      │
└──────┴──────────┴────────────┴────────┴────────────────────┘

Resulting Trading Fee = (50% × 30) + (30% × 50) + (15% × 20) + (5% × 40)
                      = 15 + 15 + 3 + 2
                      = 35 basis points (0.35%)
```

**What happens when Eve (with 2,000 LP tokens) wants to vote?**

* Eve has more LP than Dave (500), so she can replace Dave's slot
* New fee recalculates with Eve's vote instead of Dave's

## AuctionSlot: Discounted Trading

The auction slot mechanism allows LPs to bid for reduced trading fees during a 24-hour period.

### Purpose and Use Cases

**Why Auction Slots?**

The auction slot creates a market mechanism where those who benefit most from low fees pay for that privilege. This is particularly valuable for:

* **Arbitrage Bots**: Need low fees to capture small price discrepancies
* **High-Frequency Traders**: Volume traders where fees significantly impact profitability
* **Market Makers**: Entities providing liquidity across multiple venues

**When to Use AMMBid:**

* You plan to execute many trades against this pool in the next 24 hours
* The fee savings from 0% trading outweigh the LP token cost
* You want to authorize trading bots to use your discounted fee

**Economic Design:**

* The auction creates revenue for LPs (bid payments are partially burned)
* Competition for the slot ensures fair market pricing
* Time decay prevents slot hoarding at low prices

### Structure

```cpp
// AuctionSlot fields
struct AuctionSlot {
    AccountID Account;           // Current slot holder
    NetClock::time_point Expiration;  // Slot expiration (24h from bid)
    uint16_t DiscountedFee;      // Fee paid by holder (typically 0)
    STAmount Price;              // Bid amount in LP tokens
    std::vector<AccountID> AuthAccounts;  // Up to 4 authorized accounts
};
```

### Auction Mechanics

**Time Division:**

* 24-hour slot divided into 20 intervals (1.2 hours each)
* Three states: Empty, Occupied (>=5% remaining), Tailing (<5% remaining)

**Bidding Formula:**

```
min_bid = current_price * 1.05 + min_slot_price

// Where min_slot_price considers time decay:
// As time passes, minimum bid decreases
```

**Refund Mechanism:**

```
refund_to_previous = (1 - time_fraction_used) * previous_price
burned = new_bid - refund
```

### Example Scenario

```
Pool: XRP/USD
Trading Fee: 0.35%

Initial State: Empty Slot
┌──────────────────────────────────────────────────────────────┐
│ AuctionSlot: EMPTY                                           │
│ Minimum Bid: 100 LP tokens (base price)                      │
└──────────────────────────────────────────────────────────────┘

Alice bids 150 LP tokens:
┌──────────────────────────────────────────────────────────────┐
│ Holder:         Alice                                        │
│ Expiration:     +24 hours                                    │
│ Bid Paid:       150 LP tokens                                │
│ Discounted Fee: 0%                                           │
│ AuthAccounts:   [Alice-Bot-1, Alice-Bot-2]                   │
└──────────────────────────────────────────────────────────────┘

Result: Alice and her bots trade at 0% fee for 24 hours

After 12 hours, Bob wants the slot:
- Time used: 50%
- Alice's refund: 150 × 0.5 = 75 LP tokens
- Bob's minimum bid: 150 × 1.05 + decay_adjusted_min ≈ 160 LP
- If Bob bids 200 LP:
  - Alice receives: 75 LP (refund)
  - Burned: 200 - 75 = 125 LP (benefits all LPs!)
```

### Benefits

* **Slot Holder**: Trades with discounted fee (often 0%)
* **AuthAccounts**: Up to 4 additional accounts can use discounted fee
* **All LPs**: LP tokens paid are partially burned, increasing LP token value

## VoteSlots vs AuctionSlot: Key Differences

| Aspect           | VoteSlots                      | AuctionSlot                     |
| ---------------- | ------------------------------ | ------------------------------- |
| **Purpose**      | Set trading fee for everyone   | Get discounted fee for yourself |
| **Slots**        | Up to 8                        | Exactly 1                       |
| **Cost**         | Free (just hold LP tokens)     | Pay LP tokens to win            |
| **Duration**     | Permanent (until vote changes) | 24 hours                        |
| **Benefit**      | Influence pool economics       | Personal trading advantage      |
| **Who benefits** | All pool users                 | Slot holder + 4 authorized      |
| **Transaction**  | AMMVote                        | AMMBid                          |

## Integration with Pathfinding

AMM pools don't just exist in isolation - they're deeply integrated with XRPL's pathfinding engine (covered in detail in [AMM Pathfinding](https://docs.xrpl-commons.org/core-dev-bootcamp/module09bis/amm-pathfinding)).

### Key Integration Points

1. **AMMLiquidity**: Generates synthetic offers from AMM pools
2. **AMMOffer**: Represents an AMM-backed offer in payment paths
3. **Quality Matching**: AMM offers compete with CLOB offers on price

### Dual Liquidity Model

```
Payment Request
      │
      ▼
┌─────────────────────────────────────┐
│         Pathfinding Engine          │
│                                     │
│   ┌─────────┐    ┌─────────┐       │
│   │  CLOB   │    │   AMM   │       │
│   │ Offers  │    │ Offers  │       │
│   └────┬────┘    └────┬────┘       │
│        │              │             │
│        └──────┬───────┘             │
│               ▼                     │
│      Best Execution Path            │
└─────────────────────────────────────┘
```

## AMM Amendment

The AMM feature is controlled by the `featureAMM` amendment.

**Location:** `include/xrpl/protocol/detail/features.macro`

```cpp
XRPL_FEATURE(AMM, Supported::yes, VoteBehavior::DefaultNo)
```

### Related Amendments

| Amendment             | Purpose                         |
| --------------------- | ------------------------------- |
| `featureAMM`          | Core AMM functionality          |
| `fixUniversalNumber`  | Required precision improvements |
| `fixAMMv1_1`          | Rounding bug fixes              |
| `fixAMMv1_2`          | Additional fixes                |
| `fixAMMv1_3`          | Precision loss compensation     |
| `fixAMMOverflowOffer` | Offer generation overflow fix   |
| `featureAMMClawback`  | Asset issuer clawback support   |

### Checking AMM Availability

```cpp
// From AMMCore.cpp
bool ammEnabled(Rules const& rules) {
    return rules.enabled(featureAMM) &&
           rules.enabled(fixUniversalNumber);
}
```

Both amendments must be active for AMM functionality to work.

## Summary

The XRPL AMM architecture combines:

1. **ltAMM Ledger Entry**: Stores pool state, governance, and auction data
2. **Pseudo-Account**: Holds pool assets securely without signing keys
3. **LP Tokens**: Native tokens representing pool ownership
4. **VoteSlots**: Decentralized fee governance by LPs
5. **AuctionSlot**: Reduced fee mechanism for active participants
6. **Pathfinding Integration**: Seamless liquidity alongside CLOB

This design enables sophisticated DeFi functionality while maintaining XRPL's performance and security characteristics.

## References to Source Code

* `include/xrpl/protocol/detail/ledger_entries.macro` - ltAMM definition
* `src/xrpld/app/misc/AMMHelpers.h` - LP token calculations
* `src/xrpld/app/misc/AMMUtils.cpp` - Utility functions
* `src/xrpld/app/tx/detail/AMMCreate.cpp` - Pool creation
* `include/xrpl/protocol/detail/features.macro` - AMM amendments
* `src/xrpld/app/misc/AMMCore.h` - Constants and core definitions

## Cross-References

* [Module 02: Transactors](https://docs.xrpl-commons.org/core-dev-bootcamp/module02/transactors-transaction-processing-framework) - Transaction processing phases
* [Module 03: Ledger Data Structures](https://github.com/XRPL-Commons/xrpl-trainings/blob/main/core-dev-bootcamp/module03/ledger-data-structures.md) - Ledger entry types
* [Module 09: Amendments](https://docs.xrpl-commons.org/core-dev-bootcamp/module09/amendments) - Feature activation
