# AMM Transactions

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

***

## Introduction

The AMM feature introduces seven new transaction types to the XRP Ledger. Each transaction follows the standard transactor pattern (preflight, preclaim, doApply) covered in [Module 02](https://docs.xrpl-commons.org/core-dev-bootcamp/module02/transactors-transaction-processing-framework).

This chapter details each transaction type, its validation phases, flags, and execution logic.

## Transaction Type Overview

| Transaction | Type Code            | Purpose                    |
| ----------- | -------------------- | -------------------------- |
| AMMCreate   | 35 (ttAMM\_CREATE)   | Create new AMM pool        |
| AMMDeposit  | 36 (ttAMM\_DEPOSIT)  | Add liquidity to pool      |
| AMMWithdraw | 37 (ttAMM\_WITHDRAW) | Remove liquidity from pool |
| AMMVote     | 38 (ttAMM\_VOTE)     | Vote on trading fee        |
| AMMBid      | 39 (ttAMM\_BID)      | Bid for auction slot       |
| AMMDelete   | 40 (ttAMM\_DELETE)   | Delete empty pool          |
| AMMClawback | 31 (ttAMM\_CLAWBACK) | Issuer clawback from pool  |

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

## AMMCreate

Creates a new AMM pool for a pair of assets.

**Location:** `src/xrpld/app/tx/detail/AMMCreate.cpp`

### Transaction Fields

| Field      | Required | Description                               |
| ---------- | -------- | ----------------------------------------- |
| Amount     | Yes      | First asset to deposit                    |
| Amount2    | Yes      | Second asset to deposit                   |
| TradingFee | Yes      | Initial trading fee (0-1000 basis points) |

### preflight()

Static validation before ledger access:

```cpp
NotTEC AMMCreate::preflight(PreflightContext const& ctx)
{
    // 1. Check Amount != Amount2 (different currencies)
    if (ctx.tx[sfAmount] == ctx.tx[sfAmount2])
        return temBAD_AMM_TOKENS;

    // 2. Validate both amounts are positive
    if (ctx.tx[sfAmount] <= beast::zero ||
        ctx.tx[sfAmount2] <= beast::zero)
        return temBAD_AMOUNT;

    // 3. Check TradingFee range (0-1000)
    if (ctx.tx[sfTradingFee] > TRADING_FEE_THRESHOLD)
        return temBAD_FEE;

    return tesSUCCESS;
}
```

### preclaim()

Validation with ledger read access:

```cpp
TER AMMCreate::preclaim(PreclaimContext const& ctx)
{
    // 1. Check AMM doesn't already exist
    if (ctx.view.read(keylet::amm(issue1, issue2)))
        return tecDUPLICATE;

    // 2. Validate account authorization for both assets
    // 3. Check neither asset is frozen
    // 4. Verify DefaultRipple is set on token issuers
    // 5. Check sufficient XRP reserve for trustlines
    // 6. Verify neither amount is an LP token
    // 7. Check no clawback enabled (unless featureAMMClawback)

    return tesSUCCESS;
}
```

### doApply()

Execution with ledger modifications:

```cpp
TER AMMCreate::doApply()
{
    // 1. Create pseudo-account for AMM
    auto ammAccountID = calcAccountID(keylet::amm(...).key);

    // 2. Calculate initial LP tokens
    // LPT = sqrt(amount * amount2)
    auto const lpTokens = ammLPTokens(amount, amount2, lptIssue);

    // 3. Create ltAMM ledger entry
    auto sleAMM = std::make_shared<SLE>(keylet::amm(...));
    sleAMM->setFieldAmount(sfLPTokenBalance, lpTokens);
    sleAMM->setFieldU16(sfTradingFee, tradingFee);
    // ... set other fields

    // 4. Transfer assets from creator to AMM account
    // 5. Send LP tokens to creator
    // 6. Initialize vote slots and auction slot

    return tesSUCCESS;
}
```

### Reserve Requirements

Creating an AMM requires reserve for:

* 1 AMM pseudo-account
* 2-3 trustlines (Asset1, Asset2, and potentially LP token)
* 1 ltAMM ledger entry

## AMMDeposit

Adds liquidity to an existing AMM pool in exchange for LP tokens.

**Location:** `src/xrpld/app/tx/detail/AMMDeposit.cpp`

### Deposit Modes

AMMDeposit supports multiple modes controlled by flags:

| Flag              | Required Fields    | Description                                      |
| ----------------- | ------------------ | ------------------------------------------------ |
| tfLPToken         | LPTokenOut         | Deposit proportional amounts for exact LP tokens |
| tfSingleAsset     | Amount             | Deposit single asset (trading fee charged)       |
| tfTwoAsset        | Amount, Amount2    | Deposit both with maximum constraints            |
| tfOneAssetLPToken | Amount, LPTokenOut | Single asset for exact LP tokens                 |
| tfLimitLPToken    | Amount, EPrice     | Single asset with price limit                    |
| tfTwoAssetIfEmpty | Amount, Amount2    | Initialize empty pool (special case)             |

### Transaction Fields

| Field      | Required    | Description                          |
| ---------- | ----------- | ------------------------------------ |
| Asset      | Yes         | First asset issue (identifies pool)  |
| Asset2     | Yes         | Second asset issue (identifies pool) |
| Amount     | Conditional | Asset amount to deposit              |
| Amount2    | Conditional | Second asset amount to deposit       |
| LPTokenOut | Conditional | Minimum LP tokens to receive         |
| EPrice     | Conditional | Effective price limit                |

### preflight()

```cpp
NotTEC AMMDeposit::preflight(PreflightContext const& ctx)
{
    // 1. Validate flag combinations
    if (!validFlagCombination(ctx.tx.getFlags()))
        return temMALFORMED;

    // 2. Check required fields present for chosen mode
    // 3. Validate amount constraints

    return tesSUCCESS;
}
```

### doApply() - Proportional Deposit

```cpp
// For tfLPToken flag (equal value from both assets)
TER AMMDeposit::doApply()
{
    // 1. Get current pool state
    auto const [asset1Balance, asset2Balance, lpBalance] = ammHolds(...);

    // 2. Calculate required deposits for requested LP tokens
    auto const ratio = lpTokensRequested / lpBalance;
    auto const deposit1 = asset1Balance * ratio;
    auto const deposit2 = asset2Balance * ratio;

    // 3. Transfer assets to AMM
    // 4. Mint LP tokens to depositor
    // 5. Update ltAMM.LPTokenBalance

    return tesSUCCESS;
}
```

### doApply() - Single Asset Deposit

```cpp
// For tfSingleAsset flag (trading fee charged)
TER AMMDeposit::doApply()
{
    // 1. Get pool state and trading fee
    // 2. Calculate LP tokens using formula (with fee deduction)
    auto const lpTokens = lpTokensOut(
        assetBalance,
        lpBalance,
        depositAmount,
        tradingFee
    );

    // 3. Transfer single asset to AMM
    // 4. Mint calculated LP tokens
    // 5. Update pool state

    return tesSUCCESS;
}
```

## AMMWithdraw

Removes liquidity from an AMM pool by burning LP tokens.

**Location:** `src/xrpld/app/tx/detail/AMMWithdraw.cpp`

### Withdrawal Modes

| Flag                  | Required Fields   | Description                                 |
| --------------------- | ----------------- | ------------------------------------------- |
| tfLPToken             | LPTokenIn         | Burn LP tokens for proportional assets      |
| tfSingleAsset         | Amount            | Withdraw single asset (trading fee charged) |
| tfTwoAsset            | Amount, Amount2   | Withdraw specific amounts of both           |
| tfOneAssetLPToken     | Amount, LPTokenIn | Withdraw single asset for exact LP tokens   |
| tfOneAssetWithdrawAll | Amount            | Withdraw all LP as single asset             |
| tfWithdrawAll         | (none)            | Withdraw all LP tokens proportionally       |

### Key Validation

```cpp
TER AMMWithdraw::preclaim(PreclaimContext const& ctx)
{
    // 1. Verify AMM exists
    // 2. Check account holds sufficient LP tokens
    // 3. Validate withdrawal won't violate minimum pool size
    // 4. For single asset: check pool can support withdrawal

    return tesSUCCESS;
}
```

### doApply() - Proportional Withdrawal

```cpp
TER AMMWithdraw::doApply()
{
    // 1. Calculate withdrawal amounts
    auto const ratio = lpTokensIn / lpBalance;
    auto const withdraw1 = asset1Balance * ratio;
    auto const withdraw2 = asset2Balance * ratio;

    // 2. Burn LP tokens
    // 3. Transfer assets from AMM to withdrawer
    // 4. Update ltAMM.LPTokenBalance

    // 5. If LP balance becomes 0, mark for deletion
    if (newLPBalance == 0)
        // Pool can be deleted with AMMDelete

    return tesSUCCESS;
}
```

## AMMVote

Allows LP token holders to vote on the trading fee.

**Location:** `src/xrpld/app/tx/detail/AMMVote.cpp`

### Transaction Fields

| Field      | Required | Description                          |
| ---------- | -------- | ------------------------------------ |
| Asset      | Yes      | First asset issue (identifies pool)  |
| Asset2     | Yes      | Second asset issue (identifies pool) |
| TradingFee | Yes      | Proposed fee (0-1000 basis points)   |

### Voting Mechanics

```cpp
TER AMMVote::doApply()
{
    // 1. Get voter's LP token balance
    auto const lpBalance = ammLPHolds(view, ammAccount, voterAccount);

    // 2. Calculate vote weight
    auto const weight = (lpBalance / totalLPTokens) * VOTE_WEIGHT_SCALE_FACTOR;

    // 3. Find or create vote slot
    if (existingSlot)
    {
        // Update existing vote
        slot.FeeVal = proposedFee;
        slot.VoteWeight = weight;
    }
    else if (voteSlots.size() < VOTE_MAX_SLOTS)
    {
        // Add new vote slot
        voteSlots.push_back({account, proposedFee, weight});
    }
    else
    {
        // Replace lowest weight vote if ours is higher
        auto minSlot = findMinWeightSlot(voteSlots);
        if (weight > minSlot.VoteWeight)
            *minSlot = {account, proposedFee, weight};
        else
            return tecAMM_FAILED_VOTE;
    }

    // 4. Recalculate trading fee as weighted average
    uint32_t newFee = calculateWeightedFee(voteSlots);
    sleAMM->setFieldU16(sfTradingFee, newFee);

    return tesSUCCESS;
}
```

### Constants

```cpp
constexpr std::uint16_t VOTE_MAX_SLOTS = 8;
constexpr std::uint32_t VOTE_WEIGHT_SCALE_FACTOR = 100000;
```

## AMMBid

Bid for the auction slot to receive discounted trading fees.

**Location:** `src/xrpld/app/tx/detail/AMMBid.cpp`

### Transaction Fields

| Field        | Required | Description                          |
| ------------ | -------- | ------------------------------------ |
| Asset        | Yes      | First asset issue (identifies pool)  |
| Asset2       | Yes      | Second asset issue (identifies pool) |
| BidMin       | No       | Minimum bid amount (LP tokens)       |
| BidMax       | No       | Maximum bid amount (LP tokens)       |
| AuthAccounts | No       | Up to 4 accounts to authorize        |

### Auction Slot States

1. **Empty**: No current holder, minimum bid applies
2. **Occupied**: Holder with >= 5% time remaining
3. **Tailing**: Holder with < 5% time remaining (easier to outbid)

### Bidding Logic

```cpp
TER AMMBid::doApply()
{
    // 1. Calculate required bid based on slot state
    auto const minBid = calculateMinBid(
        currentSlot,
        timeRemaining,
        minSlotPrice
    );

    // 2. Determine actual bid (between BidMin and BidMax)
    auto const actualBid = std::clamp(minBid, bidMin, bidMax);

    // 3. Calculate refund to previous holder
    auto const refund = (1 - timeFractionUsed) * currentSlot.Price;

    // 4. Execute bid
    // - Transfer LP tokens from bidder
    // - Refund to previous holder
    // - Burn remaining (actualBid - refund)

    // 5. Update auction slot
    auctionSlot.Account = bidder;
    auctionSlot.Expiration = now + 24h;
    auctionSlot.DiscountedFee = 0;  // Usually 0 for slot holder
    auctionSlot.Price = actualBid;
    auctionSlot.AuthAccounts = authAccounts;

    return tesSUCCESS;
}
```

### Slot Constants

```cpp
constexpr std::uint32_t TOTAL_TIME_SLOT_SECS = 86400;      // 24 hours
constexpr std::uint16_t AUCTION_SLOT_TIME_INTERVALS = 20;   // 20 intervals
constexpr std::uint16_t AUCTION_SLOT_MAX_AUTH_ACCOUNTS = 4;
```

## AMMDelete

Deletes an empty AMM pool (LP token balance = 0).

**Location:** `src/xrpld/app/tx/detail/AMMDelete.cpp`

### Transaction Fields

| Field  | Required | Description                          |
| ------ | -------- | ------------------------------------ |
| Asset  | Yes      | First asset issue (identifies pool)  |
| Asset2 | Yes      | Second asset issue (identifies pool) |

### Deletion Process

```cpp
TER AMMDelete::preclaim(PreclaimContext const& ctx)
{
    // 1. Verify AMM exists
    // 2. Check LP token balance is 0
    if (sleAMM->getFieldAmount(sfLPTokenBalance) != beast::zero)
        return tecAMM_NOT_EMPTY;

    return tesSUCCESS;
}

TER AMMDelete::doApply()
{
    // 1. Delete trustlines (limited per transaction)
    auto const maxTrustlines = ctx.view().rules().enabled(fixAMMv1_3)
        ? AMM_MAX_TRUSTLINES_DELETE_V3
        : AMM_MAX_TRUSTLINES_DELETE;

    auto deleted = deleteAMMTrustlines(view, ammAccount, maxTrustlines);

    // 2. If trustlines remain, return incomplete
    if (remainingTrustlines > 0)
        return tecINCOMPLETE;

    // 3. Delete ltAMM ledger entry
    view.erase(sleAMM);

    // 4. Delete pseudo-account
    view.erase(sleAMMAccount);

    return tesSUCCESS;
}
```

### Incremental Deletion

For pools with many trustlines (from LP token holders), deletion happens incrementally:

* Each AMMDelete removes a limited number of trustlines
* Transaction returns `tecINCOMPLETE` if more remain
* Submitter must retry until fully deleted

## AMMClawback

Allows asset issuers to clawback their tokens from AMM pools.

**Location:** `src/xrpld/app/tx/detail/AMMClawback.cpp`

### Prerequisites

* `featureAMMClawback` amendment must be enabled
* Caller must be the issuer of the asset being clawed back
* Issuer must have `lsfAllowClawback` flag set

### Transaction Fields

| Field  | Required | Description                         |
| ------ | -------- | ----------------------------------- |
| Holder | Yes      | LP account to clawback from         |
| Asset  | Yes      | Asset to clawback                   |
| Asset2 | Yes      | Other asset in pool                 |
| Amount | No       | Specific amount (or all if omitted) |

### Clawback Process

```cpp
TER AMMClawback::doApply()
{
    // 1. Calculate LP tokens to burn (proportional to clawback)
    // 2. Burn LP tokens from holder
    // 3. Withdraw clawback amount to issuer
    // 4. Withdraw proportional other asset to holder
    // 5. Update pool state

    return tesSUCCESS;
}
```

## Error Codes

AMM transactions can return specific error codes:

| Code                     | Meaning                        |
| ------------------------ | ------------------------------ |
| tecAMM\_BALANCE          | Insufficient pool balance      |
| tecAMM\_FAILED           | General AMM operation failure  |
| tecAMM\_FAILED\_BID      | Bid too low                    |
| tecAMM\_FAILED\_DEPOSIT  | Deposit constraints not met    |
| tecAMM\_FAILED\_VOTE     | Vote failed (weight too low)   |
| tecAMM\_FAILED\_WITHDRAW | Withdrawal constraints not met |
| tecAMM\_INVALID\_TOKENS  | Invalid LP token operation     |
| tecAMM\_NOT\_EMPTY       | Cannot delete non-empty pool   |
| tecINCOMPLETE            | Deletion in progress           |

## Summary

The seven AMM transaction types provide complete lifecycle management:

1. **AMMCreate**: Initialize pools with two assets
2. **AMMDeposit**: Add liquidity (multiple modes)
3. **AMMWithdraw**: Remove liquidity (multiple modes)
4. **AMMVote**: Govern trading fees
5. **AMMBid**: Compete for reduced fees
6. **AMMDelete**: Clean up empty pools
7. **AMMClawback**: Regulatory compliance for issuers

Each follows the standard transactor pattern (preflight, preclaim, doApply) ensuring consistent validation and execution.

## References to Source Code

* `src/xrpld/app/tx/detail/AMMCreate.cpp`
* `src/xrpld/app/tx/detail/AMMDeposit.cpp`
* `src/xrpld/app/tx/detail/AMMWithdraw.cpp`
* `src/xrpld/app/tx/detail/AMMVote.cpp`
* `src/xrpld/app/tx/detail/AMMBid.cpp`
* `src/xrpld/app/tx/detail/AMMDelete.cpp`
* `src/xrpld/app/tx/detail/AMMClawback.cpp`
* `include/xrpl/protocol/detail/transactions.macro`

## Cross-References

* [Module 02: Transactors](https://docs.xrpl-commons.org/core-dev-bootcamp/module02/transactors-transaction-processing-framework) - Transaction processing phases
* [AMM Architecture](https://docs.xrpl-commons.org/core-dev-bootcamp/module09bis/amm-architecture) - Data structures and pool design
* [AMM Logic](https://docs.xrpl-commons.org/core-dev-bootcamp/module09bis/amm-logic) - Formulas used in deposit/withdraw
