> For the complete documentation index, see [llms.txt](https://docs.xrpl-commons.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.xrpl-commons.org/core-dev-bootcamp/module09bis/amm-transactions.md).

# AMM Transactions

[Back to AMM: Automated Market Maker](/core-dev-bootcamp/module09bis.md)

***

## 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](/core-dev-bootcamp/module02/transactors-transaction-processing-framework.md).

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](/core-dev-bootcamp/module02/transactors-transaction-processing-framework.md) - Transaction processing phases
* [AMM Architecture](/core-dev-bootcamp/module09bis/amm-architecture.md) - Data structures and pool design
* [AMM Logic](/core-dev-bootcamp/module09bis/amm-logic.md) - Formulas used in deposit/withdraw


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.xrpl-commons.org/core-dev-bootcamp/module09bis/amm-transactions.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
