- 1. Introduction
- 2. Ledger Entries
- 3. Transactions
The XRP Ledger decentralized exchange provides asset exchange liquidity through two mechanisms: limit order books and automated market makers (AMM). AMMs are liquidity pools that use algorithmic pricing to enable asset swaps without relying on discrete offers.
XRPL implements AMMs with the following characteristics:
- Geometric mean market maker (GM3): Pools use a weighted geometric mean conservation function to algorithmically determine exchange rates based on pool balances and fees
- Continuous auction mechanism: AMM instances auction a 24-hour slot with discounted trading fees
- Votable trading fee: LP token holders vote on the trading fee charged by the AMM instance, weighted by their LP token balance
- LOB integration: The Flow payment engine processes AMM liquidity and order book offers together, consuming liquidity from both sources in quality order
An AMM instance is represented on-ledger by:
- An
AMMledger entry storing pool parameters, trading fee, vote slots, and auction slot state - An
AccountRootledger entry (pseudo-account) holding the pool's XRP balance RippleStatetrust lines for IOU balances and LP tokensMPTokenledger entries for MPT balances (when applicable)
The AMM manages a liquidity pool containing two assets (any combination of XRP, IOUs, or MPTs) and issues LP tokens representing proportional ownership of the pool.
AMMs integrate with the BookStep of the Flow engine. During payment execution or offer crossing, BookStep generates synthetic offers from the AMM based on the current pool state and compares their quality against order book offers. The callback in revImp or fwdImp consumes whichever source provides better quality, updating either the AMM pool balances or order book entries accordingly.
The AMM uses a conservation function based on weighted geometric mean:
C = Γ_A^W_A * Γ_B^W_B
Where:
Γ_A= current balance of asset A in the AMM instance poolΓ_B= current balance of asset B in the AMM instance poolW_A= weight of asset AW_B= weight of asset BC= conservation function value
For XRPL AMMs, W_A = W_B = 0.5 (equal weights).
The conservation function C remains constant during swaps (payments). Deposits and withdrawals change C as they add or remove liquidity from the pool.
When a trader swaps assets, they add one asset to the pool and remove the other, maintaining C (before accounting for trading fees).
For example, if a trader wants to buy asset A from the pool:
- They deposit asset B into the pool (increasing
Γ_B) - They receive asset A from the pool (decreasing
Γ_A) - The conservation function C remains constant (approximately, accounting for fees)
- Because
Γ_Adecreases whileΓ_Bincreases, the ratioΓ_B / Γ_Aincreases - This means the next trader will get fewer A assets per B asset (the price of A has increased)
When swapping assets, the actual exchange rate differs from the spot price due to slippage. Note that "slippage" in XRPL terminology differs from the standard financial definition. Standard slippage refers to the difference between expected and execution price due to market movement or insufficient liquidity - an unintended outcome. In XRPL AMMs, slippage is the intentional and deterministic price degradation that results from the conservation function as larger trades shift the pool's asset ratio.
The spot price is the weighted ratio of pool balances representing the exchange rate for an infinitesimally small trade:
SpotPrice(A) = (Γ_B / W_B) / (Γ_A / W_A) * 1/(1-TFee)
TFee is trading fee as a fraction (fee units / 100,000; see Trading Fee).
For equal weights (W_A = W_B = 0.5), this simplifies to:
SpotPrice(A) = Γ_B / Γ_A * 1/(1-TFee)
The actual exchange rate of a trade is the ratio of assets actually exchanged:
ActualExchangeRate(A) = Δ_B / Δ_A
Where Δ_B is the amount of asset B swapped in and Δ_A is the amount of asset A received.
Slippage is the percentage change in the actual exchange rate relative to the pre-swap spot price. Larger swaps move the pool balances more significantly, resulting in progressively worse exchange rates.
See Swap Formulas (helpers.md) for the detailed formulas that calculate swap amounts, and Slippage and Quality Degradation (helpers.md) for more on slippage behavior.
Terminology:
- Liquidity Provider (LP): An account that has deposited assets into an AMM pool and holds LP tokens. Also called "LPs" collectively.
- LP Tokens: IOUs representing proportional ownership of an AMM pool's assets. LP tokens are issued by the AMM pseudo-account and tracked via trust lines. See section 2.1.3 for details on LP token currency codes.
- Issuing: When an account deposits assets into the pool, the AMM increases the balance on the LP token trust line between the account and the AMM pseudo-account (the issuer). This increases the total LP token supply.
- Redeeming: When an LP withdraws assets from the pool, they redeem LP tokens by decreasing the balance on their LP token trust line with the AMM. This reduces the account's holdings and decreases the total LP token supply.
- Outstanding LP Tokens: The total number of LP tokens currently in circulation (held by all LPs), tracked in the AMM ledger entry's
LPTokenBalancefield. - LP Token Holdings: The amount of LP tokens that a specific account holds, which determines their proportional share of the pool.
Liquidity providers deposit assets into the AMM pool and receive LP tokens in return. These LP tokens represent proportional ownership of the pool's assets and can later be redeemed to withdraw assets from the pool.
Initial LP Token Calculation:
When creating an AMM pool with initial deposits A and B:1
LPTokens = SQRT(A * B)
This formula uses the geometric mean of the pool balances to calculate the initial LP token supply.
Subsequent Deposits (Issuing LP Tokens):
When depositing both assets proportionally:
LPTokensIssued = (Δ_A / Γ_A) * TotalLPTokens
where Δ_A and Δ_B must satisfy: Δ_A / Γ_A = Δ_B / Γ_B
The system increases the LP token balance on the depositor's trust line with the AMM. The total outstanding LP tokens increase.
Withdrawals (Redeeming LP Tokens):
When withdrawing both assets proportionally:
Δ_A = (LPTokensRedeemed / TotalLPTokens) * Γ_A
Δ_B = (LPTokensRedeemed / TotalLPTokens) * Γ_B
The system reduces the balance on the LP's trust line with the AMM by the redeemed amount. The total outstanding LP tokens decrease by the amount redeemed.
Example:
If Alice creates an AMM with 100 EUR and 1000 USD:
- Initial LP tokens issued = SQRT(100 * 1000) = 316.227766... LP tokens
- Alice receives ~316.23 LP tokens representing 100% ownership
- Total outstanding LP tokens = 316.23
- If Bob later deposits 10 EUR and 100 USD (same ratio), he receives ~31.62 newly issued LP tokens
- Total outstanding LP tokens = ~347.85
- Alice holds ~316.23 LP tokens (~90.9% of the pool)
- Bob holds ~31.62 LP tokens (~9.1% of the pool)
- If Alice later redeems 100 LP tokens to withdraw assets, she receives both EUR and USD proportional to her redeemed LP tokens, and the balance on her LP token trust line decreases by 100
- Total outstanding LP tokens = ~247.85
- Alice now holds ~216.23 LP tokens (~87.2% of the pool)
Single-Asset Deposits and Withdrawals:
The formulas above apply to proportional deposits and withdrawals, where both pool assets are added or removed in the same ratio as the pool.
AMMs also support single-asset operations, where only one asset is deposited or withdrawn:
-
Single-Asset Deposits: When depositing only one asset (e.g., only asset A into an A/B pool), only that asset's pool balance increases. This creates an imbalance in the pool ratio. The depositor receives fewer LP tokens than they would for a proportional deposit of the same value, because the trading fee is applied to account for the imbalance created.
-
Single-Asset Withdrawals: When withdrawing only one asset, only that asset's pool balance decreases, creating an imbalance. The withdrawer must redeem more LP tokens than they would for a proportional withdrawal, with the trading fee applied to account for the imbalance.
The specific formulas for single-asset operations are more complex and involve the trading fee. See Deposit Formulas and Withdrawal Formulas for the mathematical details.
Example: Proportional Deposit
Alice creates an AMM with 100 USD and 100 EUR:
- Initial LP tokens: SQRT(100 * 100) = 100 LP tokens
- Bob later deposits 100 USD and 100 EUR (maintaining the 1:1 ratio)
- LP tokens received: (100 / 100) * 100 = 100 LP tokens
- Total LP tokens: 200
Example: Single-Asset Deposit
Alice creates an AMM with 100 USD and 100 EUR (with 0.3% trading fee):
- Initial LP tokens: SQRT(100 * 100) = 100 LP tokens
- Bob later deposits 100 USD only (no EUR)
- Using the single-asset deposit formula, Bob receives ~41.4 LP tokens
- Total LP tokens: ~141.4
For single-asset operations, users can specify an effective price to protect against unfavorable exchange rates:
-
Deposit Effective Price = Asset Deposited / LP Tokens Issued
- Example: Depositing 100 USD to receive 40 LP tokens = 2.5 USD per LP token
- Users set a maximum effective price (won't pay more than X asset per LP token)
- Used in singleDepositEPrice mode
-
Withdrawal Effective Price = LP Tokens Redeemed / Asset Withdrawn
- Example: Redeeming 40 LP tokens to withdraw 100 USD = 0.4 LP tokens per USD
- Users set a minimum effective price (won't pay less than X LP tokens per unit of asset withdrawn)
- Used in singleWithdrawEPrice mode
AMMs charge a trading fee on swaps, which is added to the pool and distributed proportionally to all LP token holders when they withdraw liquidity. The fee is expressed in fee units.
Fee Range:
- Minimum: 0 units (0%)
- Maximum: 1000 units (1% or 100 basis points)
- Fee units: 1 unit = 0.001% (or 1/10 of a basis point)
- Example: A fee of 30 units = 0.03% = 3 basis points
The trading fee can be set initially when creating the AMM and subsequently adjusted through the voting mechanism.
The auction slot mechanism allows any LP token holder to bid for a 24-hour period of discounted trading fees. During this period, the slot holder pays only one-tenth of the regular trading fee when trading through the AMM. The slot holder can also authorize up to four additional accounts to share this discount.
The auction operates as a continuous bidding system where anyone can take over the slot at any time by outbidding the current holder. The minimum bid price decreases as the current holder uses more of their 24-hour slot time. When someone successfully outbids the current holder, the previous holder receives a refund proportional to their remaining unused time. The difference between the new bid and the refund is burned from the LP token supply, which increases the ownership percentage of all remaining LP token holders.
See AMMBid Implementation Details for comprehensive documentation on the auction mechanics, including price calculations, time-based refunds, and the LP token burning process.
Liquidity providers can vote on the trading fee rate. Each vote is recorded in a vote slot - a data structure stored in the AMM ledger entry that tracks who voted, what fee they proposed, and their voting power. Voting power is determined by the number of LP tokens held: an account holding 30% of all LP tokens has 30% of the voting power. The AMM maintains up to 8 vote slots2, and the actual trading fee is calculated as the weighted average of all votes3.
Voting Mechanism:
- LP token holders submit
AMMVotetransactions with their preferred fee (0-1000) - The system calculates vote weights:
VoteWeight = (LPTokens / TotalLPTokens) * 100,000 - The weighted average determines the actual trading fee:
TradingFee = SUM(Fee_i * LPTokens_i) / SUM(LPTokens_i)
Vote Slot Management:
- Maximum 8 vote slots (defined by
kVoteMaxSlots)2 - If a slot is available, the new vote is added directly
- If all slots are full, replacement is a two-step process4:
- Find the eviction candidate: select the existing slot with the smallest LP token balance, breaking ties by lowest fee, then by lexicographically smallest account ID
- Decide whether to replace: the new vote replaces the candidate only if the new voter holds more LP tokens, or holds an equal amount and sets a higher fee. If both are equal, the new vote is not added
- Vote weights are automatically recalculated when LP token balances change
Example:
AMM has 3 voters:
- Alice: 100 LP tokens, votes 500 (0.5% fee)
- Bob: 50 LP tokens, votes 300 (0.3% fee)
- Carol: 50 LP tokens, votes 700 (0.7% fee)
Actual fee = (100500 + 50300 + 50*700) / (100 + 50 + 50) = 100,000 / 200 = 500 (0.5%)
The AMM system uses several ledger entry types to track state:
classDiagram
class AMM {
+AccountID Account
+UInt16 TradingFee
+Array VoteSlots
+Object AuctionSlot
+Amount LPTokenBalance
+Issue Asset
+Issue Asset2
+UInt64 OwnerNode
}
class AccountRoot {
+AccountID Account
+Amount Balance
+UInt256 AMMID
+UInt32 Flags
}
class RippleState {
+Amount Balance
+Amount LowLimit
+Amount HighLimit
+UInt32 Flags
}
class MPToken {
+AccountID Account
+uint192 MPTokenIssuanceID
+Amount MPTAmount
+UInt32 Flags
}
class DirectoryNode {
}
AMM --> AccountRoot : references via sfAccount
AccountRoot -- RippleState : connects to (via LowLimit/HighLimit)
MPToken --> AccountRoot : owned by (via sfAccount)
AMM --> DirectoryNode : linked via sfOwnerNode
Figure: Key ledger entries that represent an AMM instance
The AMM ledger entry (type ltAMM = 0x0079)5 tracks the state of an AMM instance. Each AMM is uniquely identified by its asset pair6.
The key of the AMM object is the result of SHA512-Half of the AMM space key (0x0041, uppercase A)7 concatenated with the two assets' identifiers.
The two assets are first ordered canonically (lexicographically) to ensure a unique, deterministic key regardless of the order in which assets are specified.
Each asset contributes its identifier to the hash:
- XRP: Issuer AccountID (all zeros) + Currency code (all zeros)
- IOUs: Issuer AccountID + Currency code
- MPTs: MPTID
| Field | Type | Required | Description |
|---|---|---|---|
Account |
AccountID | Yes | The Account ID of the AMM's pseudo-account |
TradingFee |
UInt16 | Defaults to 0 if not set | The current trading fee in units of 1/100,000 (0 if not set) |
VoteSlots |
Array | Optional | Array of up to 8 VoteEntry objects containing fee votes |
AuctionSlot |
Object | Optional | Object containing auction slot information |
LPTokenBalance |
Amount | Yes | Total outstanding LP tokens for this AMM |
Asset |
Issue | Yes | One of the pool's two assets (the lesser by Issue comparison) |
Asset2 |
Issue | Yes | The other pool asset (the greater by Issue comparison) |
OwnerNode |
UInt64 | Yes | Index of the owner directory page for this AMM |
PreviousTxnID |
Hash256 | Optional | Transaction hash that most recently modified this entry |
PreviousTxnLgrSeq |
UInt32 | Optional | Ledger sequence of the transaction that most recently modified this entry |
The VoteSlots field contains an array of VoteEntry inner objects. Each VoteEntry has:
| Field | Type | Description |
|---|---|---|
Account |
AccountID | The account that cast this vote |
TradingFee |
UInt16 | The fee this account voted for (0-1000) |
VoteWeight |
UInt32 | Weight of this vote = (LPTokens / TotalLPTokens) * 100,000 |
The AuctionSlot field contains an inner object with:
| Field | Type | Description |
|---|---|---|
Account |
AccountID | Current auction slot holder |
AuthAccounts |
Array | Optional array of up to 4 authorized accounts |
Expiration |
UInt32 | Unix timestamp when the slot expires (current time + 86,400 seconds) |
Price |
Amount | Price paid for the auction slot in LP tokens |
DiscountedFee |
UInt16 | Discounted fee for slot holder |
The AMM's Account field references a pseudo-account8 created specifically for this AMM. Each AMM instance creates a special pseudo-account to hold the pool's assets. This account:
- Has a disabled master key, allows default rippling and enables deposit authorization (so nobody can pay into the pseudo-account)9
- Is identified by the
sfAMMIDfield10 in itsAccountRootentry - Has an Account ID deterministically generated11 from the AMM ledger entry key
- Holds XRP balance if one of the pool assets is XRP
- Has trust lines for:
- Each IOU in the pool
- Each liquidity provider who holds LP tokens
- Has MPToken entries for MPT assets in the pool (if pool contains MPTs):
- Is automatically deleted when the AMM is deleted
The AMM pseudo-account ID, like any other pseudo-account ID, is generated using a collision-avoidance algorithm12 that ensures no existing account has the same address. The generation process uses the pseudoAccountAddress() function with the following algorithm:
Generation Process:
- Input: The AMM ledger entry key (derived from object identifier)
- Parent Hash: The hash of the parent ledger (provides uniqueness per ledger)
- Iteration Loop: Try up to 256 attempts (hardcoded as
kMaxAccountAttempts)
Collision avoidance: Account IDs are 160-bit values derived from cryptographic hashes. While the probability of collision with an existing account is small, multiple attempts provide a safety mechanism to handle this theoretical edge case.
For each attempt i (0 to 255):
hash = SHA512-Half(i, parentLedgerHash, ammLedgerEntryKey)
accountID = RIPEMD160(SHA256(hash))
- Collision Check: Verify that no
AccountRootexists with thisaccountID - Success: If no collision, return the
accountID - Failure: If all 256 attempts find collisions, return
beast::kZero(all zeros account ID)
Failure Handling:
If pseudoAccountAddress() returns beast::kZero (indicating all 256 attempts failed):
createPseudoAccount()returnstecDUPLICATE- The AMMCreate transaction fails in
doApply - This scenario is extremely unlikely in practice
Determinism:
For a given asset pair and parent ledger hash, all nodes generate the same sequence of candidate account IDs:
- The iteration counter
iis hashed along with fixed inputs (parent hash, AMM keylet) - Each
iproduces a completely different candidate Account ID - All nodes check the same candidates in the same order against their ledger state
- The first unused candidate found is selected consistently across all nodes
- This ensures reproducibility across nodes in consensus and predictable behavior in transaction replay
Example:
For an AMM with USD/XRP:
- AMM keylet =
SHA512-Half(0x0041, XRP_account, XRP_currency, USD_account, USD_currency)13 - Attempt 0:
hash = SHA512-Half(0, parentHash, ammKeylet)-> Account ID candidate - If Account ID exists, try attempt 1:
hash = SHA512-Half(1, parentHash, ammKeylet)-> New candidate - Continue until unused account ID found or 256 attempts exhausted
The AMM ledger entry itself does not require an owner reserve. However:
- Creating an AMM costs an elevated base fee equal to one owner-reserve increment (
view.fees().increment), set higher than the normal per-transaction base fee - The AMM pseudo-account holds reserves if it has XRP
- LP token holders who have trust lines for LP tokens pay reserves according to normal trust line rules
AMMs create RippleState entries (trust lines) for:
- Each IOU asset in the pool
- The LP token issued by the AMM
All AMM trust lines:
- Have zero credit limits14 (to prevent unsolicited deposits)
- Do not have quality modifiers (QualityIn/QualityOut)15
Pool asset trust lines (between the AMM account and the IOU issuer):
- Are additionally marked with the
lsfAMMNodeflag16
See Trust Lines Documentation for complete details on RippleState ledger entries.
When an AMM pool contains MPT assets, the AMM pseudo-account holds MPToken entries for each MPT in the pool. These MPToken entries:
- Are marked with the
lsfMPTAMMflag17 (distinguishing them from regular holder MPTokens) - Are always marked with the
lsfMPTAuthorizedflag18 (the AMM pseudo-account is implicitly authorized to hold the asset, regardless of the issuance'slsfMPTRequireAuth) - Track the AMM's MPT balance via the
MPTAmountfield - Are created when depositing MPT assets19
- Do not count towards the AMM pseudo-account's
OwnerCount20
See MPTokens Documentation for complete details on MPToken ledger entries.
Several AMM transactions (AMMCreate, AMMDeposit, AMMWithdraw, AMMBid) use the accountSend() function to transfer assets between accounts. This function can return various error codes depending on the transfer type and ledger state. These errors may occur during the doApply phase of transaction execution:
For XRP transfers:
tecFAILED_PROCESSINGortelFAILED_PROCESSING: Sender has insufficient XRP balance to complete the transfer (after paying transaction fees and maintaining reserve requirements)21- With fixAMMv1_1:
tecINTERNALif the transfer amount is negative22
For IOU transfers:
- Calls
directSendNoLimitIOU()23 which then callsdirectSendNoFeeIOU()24 and may callissueIOU()25 orredeemIOU()26 - These functions can trigger trust line creation, which may fail with:
tecDIR_FULL: Owner directory is full when creating a new trust line27tecNO_LINE_INSUF_RESERVE: Insufficient XRP reserve to create the trust line28tefINTERNAL: Trust line doesn't exist after transfer29tefINTERNAL: Receiver account SLE does not exist during trust line creation30tecNO_TARGET: Peer account doesn't exist when creating trust line31
- Errors from
directSendNoFeeIOU()are propagated32. These include:tecDIR_FULL: Owner directory is full when creating trust line (fromtrustCreate())27tefINTERNAL: Receiver account SLE is null (fromtrustCreate())30tecNO_TARGET: Peer account doesn't exist when creating trust line (fromtrustCreate())31tefBAD_LEDGER: Directory removal failed when deleting trust line (fromtrustDelete())33
For MPT transfer:
tecOBJECT_NOT_FOUND: MPT issuance object doesn't exist34tecPATH_DRY: Transfer would exceedMaximumAmountwhen issuer is sending MPTs3536tecINSUFFICIENT_FUNDS: Sender's MPToken balance is less than the transfer amount37tecNO_AUTH:tecINTERNAL: Outstanding amount is less than the amount being redeemed when receiver is issuer40
Note: Most of these error conditions are checked during the preclaim phase (validation against the ledger view), so they are unlikely to occur during doApply. However, ledger state can change between validation and application (e.g., due to other transactions in the same ledger), making these errors theoretically possible.
The AMMCreate transaction creates a new AMM instance for a token pair and provides initial liquidity.
| Field Name | Required? | Modifiable? | JSON Type | Internal Type | Default Value | Description |
|---|---|---|---|---|---|---|
TransactionType |
✔️ | No |
String |
UInt16 |
Must be "AMMCreate" |
|
Account |
✔️ | No |
String |
AccountID |
Account creating the AMM instance | |
Amount |
✔️ | No |
String or Object |
Amount |
Amount of one asset to deposit (XRP as string, tokens as object) | |
Amount2 |
✔️ | No |
String or Object |
Amount |
Amount of the other asset to deposit (XRP as string, tokens as object) | |
TradingFee |
✔️ | No |
Number |
UInt16 |
Initial trading fee (0-1000, 1 = 0.001%) | |
Flags |
No |
Number |
UInt32 |
0 |
Transaction flags (must be 0 for AMMCreate, only universal flags allowed) |
The two amounts can be in any order - the AMM will automatically order them as Asset and Asset2 based on Issue comparison.
Static validation41
temDISABLED:- AMM amendment is not enabled
- either
AmountorAmount2is an MPT but MPTokensV2 amendment is not enabled
temINVALID_FLAG: one of the specified flags is not one of common transaction flagstemBAD_AMM_TOKENS:AmountandAmount2have the same currency and issuertemBAD_CURRENCY:AmountorAmount2uses the disallowed 3-letter "XRP" currency codetemBAD_ISSUER:AmountorAmount2is XRP (currency is all zeros) but has a non-zero issuer accounttemBAD_MPT:AmountorAmount2is an MPT with a zero (empty) issuertemBAD_AMOUNT: eitherAmountorAmount2is zero, negativetemBAD_FEE:TradingFeeexceeds 1000
Validation against the ledger view42
tecDUPLICATE: an AMM already exists for this token pairtecNO_LINE:AmountorAmount2issuer haslsfRequireAuthflag set, but account has no trust line with the issuertecNO_AUTH:- For IOUs:
AmountorAmount2issuer haslsfRequireAuthflag set, and the trust line exists but lacks authorization (missinglsfLowAuthorlsfHighAuthflag) - For MPTs: Signing account or AMM pseudo-account lacks required authorization for MPT with
lsfMPTRequireAuthflag
- For IOUs:
tecFROZEN(IOU/XRP) ortecLOCKED(MPT): either asset is globally or individually frozen/lockedterNO_RIPPLE: either asset's issuer does not have DefaultRipple flag set (non-XRP assets only)tecINSUF_RESERVE_LINE: account has insufficient XRP to cover the LP token trust line reservetecUNFUNDED_AMM: account has insufficient balance of either asset or it does not have the trust linetecAMM_INVALID_TOKENS: eitherAmountorAmount2is an LP token from another AMM. The code does not explicitly check for another AMM, but at this point, LP token from this AMM should not exist- With SingleAssetVault:
terADDRESS_COLLISION: generated AMM account ID already exists
- Without AMMClawback:
tecINTERNAL:AmountorAmount2issuer account does not exist in the ledgertecNO_PERMISSION:AmountorAmount2issuer has clawback enabled (lsfAllowTrustLineClawbackflag is set for IOUs)- either
AmountorAmount2is an MPT withlsfMPTCanClawbackflag set
- MPT-specific validations (for either
AmountorAmount2if MPT): Both assets are validated usingcanMPTTradeAndTransfer. See MPT Validation Functions for validation logic and error conditions.
Validation during doApply43
tecDUPLICATE:- AMM pseudo-account ID generation failed (no valid account ID found after 256 attempts)
- LP Token trust line already exists
tecDIR_FULL: Owner directory is full when linking AMM object- Propagate errors from
accountSend()when transferring LP tokens and assets to/from AMM pseudo-account (see Common Error Codes from accountSend())
3.1.2. State Changes44
-
AccountRootobject is created for AMM pseudo-account:Account: Generated pseudo-account ID (from collision-avoidance algorithm)Balance:STAmount{}(zero XRP initially, then updated toAmountifAmountis XRP)Sequence: 0 (with SingleAssetVault), otherwise current ledger sequenceFlags:lsfDisableMaster | lsfDefaultRipple | lsfDepositAuthsfAMMID: Set toammKeylet.key(the AMM ledger entry key)
-
AMMobject is created:Account: AMM pseudo-account IDLPTokenBalance:SQRT(Amount * Amount2)Asset: Lesser of the two assets by Issue comparisonAsset2: Greater of the two assetsTradingFee: As specified (if non-zero)OwnerNode: Link to owner directoryVoteSlots: Array field with singleVoteEntryinner object created:Account: Creator account IDTradingFee: Initial trading fee (if non-zero)VoteWeight: 100,000 (= 100%, since creator owns all LP tokens initially)
AuctionSlot: Object field with an inner object created:Account: Creator account IDExpiration: Current time + 86,400 seconds (24 hours)Price: 0 LP tokensDiscountedFee:TradingFee / 10(if trading fee is non-zero)
-
RippleStateobjects are created (for token assets):- For each non-XRP token asset: Trust line between AMM account and asset issuer
- Marked with
lsfAMMNodeflag
- Marked with
- For LP tokens: Trust line between AMM account and creator
- All trust lines:
- Have zero credit limits
- Initial balances set to deposited/issued amounts
- For each non-XRP token asset: Trust line between AMM account and asset issuer
-
MPTokenobjects are created (for MPT assets):- For each MPT asset: MPToken entry for the AMM pseudo-account
- Flags:
lsfMPTAMM: Marks this as an AMM-owned MPToken entrylsfMPTAuthorized: Always set (the AMM pseudo-account is implicitly authorized to hold the MPT)
- Initial
MPTAmountset to deposited amount - Linked to the AMM pseudo-account's owner directory
-
DirectoryNodeis created for AMM pseudo-account's owner directory:- Links the AMM ledger entry to the pseudo-account
- The AMM entry's
OwnerNodefield is set to the directory page index - This directory will later also contain links to trust lines owned by the AMM account
-
Order books are registered in OrderBookDB (if not already present):
- Asset->Asset2 trading direction registered
- Asset2->Asset trading direction registered
The AMMDeposit transaction adds liquidity to an existing AMM pool. There are multiple deposit modes controlled by transaction flags.
Fields:
| Field Name | Required? | Modifiable? | JSON Type | Internal Type | Default Value | Description |
|---|---|---|---|---|---|---|
TransactionType |
✔️ | No |
String |
UInt16 |
Must be "AMMDeposit" |
|
Account |
✔️ | No |
String |
AccountID |
Account depositing liquidity | |
Asset |
✔️ | No |
Object |
Issue |
One of the pool's assets | |
Asset2 |
✔️ | No |
Object |
Issue |
The other pool asset | |
Amount |
No |
String or Object |
Amount |
Amount of one asset (interpretation depends on flags) | ||
Amount2 |
No |
String or Object |
Amount |
Amount of the other asset (interpretation depends on flags) | ||
LPTokenOut |
No |
String or Object |
Amount |
Amount of LP tokens to receive (interpretation depends on flags) | ||
EPrice |
No |
String or Object |
Amount |
Maximum effective price in same currency as Amount (tfLimitLPToken only) |
||
TradingFee |
No |
Number |
UInt16 |
Trading fee for empty pool deposits (tfTwoAssetIfEmpty only) | ||
Flags |
No |
Number |
UInt32 |
0 |
Transaction flags specifying deposit mode |
The AMMDeposit transaction supports six different deposit modes. See AMMDeposit Implementation Details for detailed documentation.
All deposit modes require the Asset and Asset2 fields to identify which AMM pool to deposit into. The table below shows the additional fields required for each mode.
| Function | Flag | Flag Value | Use Case | Assets | User Specifies | System Calculates |
|---|---|---|---|---|---|---|
| equalDepositLimit | tfTwoAsset |
0x00100000 |
Depositor specifies maximum amounts of both assets. System deposits both assets maintaining the pool's current ratio, maximizing deposit size within both limits | Both | Amount (max), Amount2 (max), Optional: LPTokenOut (min) |
Actual Amount and Amount2 to deposit (tries maximizing Amount first, then Amount2 if that fails) |
| equalDepositTokens | tfLPToken |
0x00010000 |
Depositor specifies exact LP tokens to receive. System calculates required amounts of both assets maintaining the pool's current ratio | Both | LPTokenOut (exact). Optional: both Amount (min) and Amount2 (min), or neither |
Required Amount and Amount2 |
| equalDepositInEmptyState | tfTwoAssetIfEmpty |
0x00800000 |
Used when pool is empty (zero LP tokens and zero asset balances). Depositor deposits both assets to set new pool ratio and becomes initial LP token holder | Both | Amount, Amount2, Optional: TradingFee |
Initial LPTokenOut = sqrt(Amount * Amount2) |
| singleDeposit | tfSingleAsset |
0x00080000 |
Depositor specifies amount of single asset to deposit. System calculates how many LP tokens depositor receives | One | Amount, Optional: LPTokenOut (min) |
LPTokenOut depositor receives |
| singleDepositTokens | tfOneAssetLPToken |
0x00200000 |
Depositor specifies exact LP tokens to receive in exchange for depositing single asset. System calculates required deposit amount | One | LPTokenOut (exact), Amount (max) |
Required Amount |
| singleDepositEPrice | tfLimitLPToken |
0x00400000 |
Depositor sets maximum amount of single asset willing to pay per LP token received. System calculates optimal deposit amount | One | Amount (can be 0), EPrice (max) |
Optimal Amount at EPrice limit |
The deposit mode is determined by exactly one of these flags (enforced by checking popcount(flags & tfDepositSubTx) == 1). See the table above for flag values and usage details, and AMMDeposit Implementation Details for the implementation of each mode.
Static validation45
temDISABLED:- AMM amendment is not enabled
- either
AssetorAsset2is an MPT but MPTokensV2 amendment is not enabled
temINVALID_FLAG: invalid flags (flags set that are not deposit mode flags)temMALFORMED:- Invalid flag combination (must have exactly one deposit mode flag set)
- Required fields missing for chosen deposit mode
temBAD_AMM_TOKENS:AmountandAmount2are the same token (when both specified)LPTokenOutis zero or negativeAssetandAsset2have the same currency and issuerAmountorAmount2currency does not match either pool asset (AssetorAsset2)EPricecurrency does not matchAmountcurrency (checked only when MPTokensV2 is not enabled)
temBAD_CURRENCY:Asset,Asset2,Amount,Amount2, orEPriceuses the disallowed 3-letter "XRP" currency code (0x5852500000000000)temBAD_ISSUER:Asset,Asset2,Amount,Amount2, orEPriceis XRP (currency is all zeros) but has a non-zero issuer accounttemBAD_MPT:Asset,Asset2,Amount,Amount2, orEPriceis an MPT with a zero (empty) issuertemBAD_AMOUNT:Amount,Amount2, orEPriceis zero, negativetemBAD_FEE:TradingFeeexceeds 1000
Validation against the ledger view46
terNO_AMM: AMM ledger entry does not exist for specified asset pairtecINTERNAL:- (tfTwoAssetIfEmpty only) Pool has zero LP tokens but asset balances are not zero (inconsistent empty state)
- pool balances are invalid (zero or negative)
tecAMM_NOT_EMPTY: tfTwoAssetIfEmpty used but AMM is not emptytecAMM_EMPTY: AMM has zero LP tokens (for non-tfTwoAssetIfEmpty modes)- Authorization/freeze checks (applied unconditionally to the deposited
Amount/Amount2for non-tfLPTokenmodes, and with AMMClawback also to the poolAsset/Asset2):tecNO_LINE: the asset's issuer haslsfRequireAuthset, but the account has no trust line with the issuertecNO_AUTH: the asset's issuer haslsfRequireAuthset, and the trust line exists but lacks authorization (missinglsfLowAuthorlsfHighAuthflag)tecFROZEN(IOU/XRP) ortecLOCKED(MPT): the asset is frozen/locked (AMM account, currency/issuance, or depositor account)
tecUNFUNDED_AMM:- account has insufficient token balance to deposit
- account has insufficient XRP to deposit (and LP token trust line already exists)
tecINSUF_RESERVE_LINE:- account has insufficient XRP to deposit and create LP token trust line (when account is not yet an LP)
- non-LP account has insufficient reserve for LP token trust line
temBAD_AMM_TOKENS:LPTokenOutissue (currency code + issuer) does not match the AMM's LP token issue- MPT-specific validations (for either
AssetorAsset2if MPT): Both assets are validated usingcanMPTTradeAndTransfer. See MPT Validation Functions for validation logic and error conditions.
Validation during doApply47
tecINTERNAL: AMM ledger entry does not exist (should not happen if preclaim succeeded)temBAD_AMOUNT: Deposit amount after adjustment/calculation is zero or negative. Deposit amounts are adjusted based on the deposit mode (e.g., proportional calculations for tfLPToken, pool ratio adjustments for tfTwoAsset, or LP token precision adjustments).tecUNFUNDED_AMM: Insufficient balance to deposit the final calculated amounts. This is re-checked during deposit execution (first check is in preclaim with transaction amounts, but final amounts may differ for certain deposit modes like tfLPToken).tecAMM_FAILED: Deposit constraints not satisfied. The interpretation of transaction fields as minimums or maximums depends on the deposit mode flag (see Deposit Modes):- tfLPToken mode: calculated asset deposits are less than
AmountorAmount2(optional minimums) - tfSingleAsset or tfTwoAsset mode: calculated LP tokens are less than
LPTokenOut(optional minimum) - tfTwoAsset mode: neither calculated deposit strategy satisfies both
AmountandAmount2constraints (maximums) - tfOneAssetLPToken mode: calculated deposit amount exceeds
Amount(maximum willing to deposit) - tfLimitLPToken mode: calculated deposit amount is invalid or effective price constraint cannot be satisfied with
EPrice(maximum effective price)
- tfLPToken mode: calculated asset deposits are less than
tecAMM_INVALID_TOKENS: Calculated LP tokens are zero or invalid. This can occur when:- LP token adjustments for precision result in zero tokens (with fixAMMv1_3)
- Deposit amount is too small relative to pool size, resulting in zero LP tokens after rounding
- Occurs in any deposit mode where LP tokens are calculated (tfLPToken, tfSingleAsset, tfTwoAsset, tfOneAssetLPToken, tfLimitLPToken)
- Propagate errors from
accountSend()when transferring assets to AMM account and LP tokens to depositor (see Common Error Codes from accountSend())
-
AMMobject is modified:LPTokenBalance: Increased by deposited LP tokensVoteSlots: (tfTwoAssetIfEmpty only) Reset with depositor's voteAuctionSlot: (tfTwoAssetIfEmpty only) Depositor becomes slot holder withPriceset to 0 and 24-hour expirationTradingFee: (tfTwoAssetIfEmpty only) Updated if specified
-
AMM pseudo-account balances are modified:
- Asset deposits transferred from depositor to AMM pseudo-account
- Balances updated in AMM pseudo-account's
AccountRoot(for XRP),RippleStatetrust lines (for tokens), orMPTokenentries (for MPTs)
-
LP tokens are issued:
- LP tokens sent from AMM pseudo-account to depositor
- Trust line created if depositor doesn't have one
RippleStatebalance updated
-
Depositor's
AccountRootis modified:OwnerCount: Incremented if new LP token trust line createdBalance: Decreased by XRP deposited (if applicable)
The AMMWithdraw transaction removes liquidity from an AMM pool by redeeming LP tokens.
Fields:
| Field Name | Required? | Modifiable? | JSON Type | Internal Type | Default Value | Description |
|---|---|---|---|---|---|---|
TransactionType |
✔️ | No |
String |
UInt16 |
Must be "AMMWithdraw" |
|
Account |
✔️ | No |
String |
AccountID |
Account withdrawing liquidity | |
Asset |
✔️ | No |
Object |
Issue |
One of the pool's assets | |
Asset2 |
✔️ | No |
Object |
Issue |
The other pool asset | |
Amount |
No |
String or Object |
Amount |
Amount of one asset (interpretation depends on flags) | ||
Amount2 |
No |
String or Object |
Amount |
Amount of the other asset (interpretation depends on flags) | ||
LPTokenIn |
No |
String or Object |
Amount |
Amount of LP tokens to redeem (interpretation depends on flags) | ||
EPrice |
No |
String or Object |
Amount |
Minimum effective price in LP token currency (tfLimitLPToken only) | ||
Flags |
No |
Number |
UInt32 |
0 |
Transaction flags specifying withdrawal mode |
The AMMWithdraw transaction supports seven different withdrawal modes. See AMMWithdraw Implementation Details for detailed documentation.
All withdrawal modes require the Asset and Asset2 fields to identify which AMM pool to withdraw from. The table below shows the additional fields required for each mode.
| Function | Flag | Flag Value | Use Case | Assets | User Specifies | System Calculates |
|---|---|---|---|---|---|---|
| equalWithdrawTokens | tfLPToken |
0x00010000 |
Withdrawer specifies exact LP tokens to redeem. System withdraws both assets maintaining the pool's current ratio | Both | LPTokenIn (exact) |
Required Amount and Amount2 to withdraw |
| equalWithdrawTokens | tfWithdrawAll |
0x00020000 |
Withdrawer redeems all LP tokens held. System withdraws both assets proportionally based on entire LP token balance | Both | None (redeems all LP tokens) | Amount and Amount2 based on all LP tokens held |
| equalWithdrawLimit | tfTwoAsset |
0x00100000 |
Withdrawer specifies maximum amounts of both assets. System withdraws both assets maintaining the pool's current ratio, maximizing withdrawal size within both limits | Both | Amount (max), Amount2 (max) |
Actual Amount and Amount2 to withdraw (tries maximizing Amount first, then Amount2 if that fails), LPTokenIn |
| singleWithdraw | tfSingleAsset |
0x00080000 |
Withdrawer specifies amount of single asset to withdraw. System calculates how many LP tokens withdrawer must redeem | One | Amount |
LPTokenIn withdrawer must redeem |
| singleWithdrawTokens | tfOneAssetWithdrawAll |
0x00040000 |
Withdrawer redeems all LP tokens held in exchange for withdrawing single asset. System calculates withdrawal amount based on entire LP token balance | One | Amount (required to specify which asset; value is min constraint or 0 for no min) |
Amount to withdraw based on all LP tokens held |
| singleWithdrawTokens | tfOneAssetLPToken |
0x00200000 |
Withdrawer specifies exact LP tokens to redeem in exchange for withdrawing single asset. System calculates withdrawal amount | One | LPTokenIn (exact), Amount (min or 0 for no min) |
Required Amount |
| singleWithdrawEPrice | tfLimitLPToken |
0x00400000 |
Withdrawer sets minimum effective price (asset received per LP token redeemed). System calculates optimal withdrawal amount | One | Amount (min or 0 for no min), EPrice (min effective price) |
Optimal Amount and LPTokenIn at EPrice limit |
The withdrawal mode is determined by exactly one of these flags (enforced by checking popcount(flags & tfWithdrawSubTx) == 1). See the table above for flag values and usage details, and AMMWithdraw Implementation Details for the implementation of each mode.
Static validation48
temDISABLED:- AMM amendment not enabled
- either
AssetorAsset2is an MPT but MPTokensV2 amendment is not enabled
temINVALID_FLAG: invalid flags (flags set that are not withdraw mode flags)temMALFORMED:- Invalid flag combination (must have exactly one withdrawal mode flag set)
- Required fields missing for chosen withdrawal mode
temBAD_AMM_TOKENS:AmountandAmount2are the same token (when both specified)LPTokenInis zero or negativeAssetandAsset2have the same currency and issuerAmountorAmount2currency does not match either pool asset (AssetorAsset2)
temBAD_CURRENCY:Asset,Asset2,Amount,Amount2, orEPriceuses the disallowed 3-letter "XRP" currency code (0x5852500000000000)temBAD_ISSUER:Asset,Asset2,Amount,Amount2, orEPriceis XRP (currency is all zeros) but has a non-zero issuer accounttemBAD_MPT:Asset,Asset2,Amount,Amount2, orEPriceis an MPT with a zero (empty) issuertemBAD_AMOUNT:Amount,Amount2, orEPriceis zero, negative
Note: AMMWithdraw static validation differs from AMMDeposit static validation in the following ways:
- Does NOT validate that
EPricecurrency matchesAmountcurrency (in deposit, EPrice = asset deposited / LP tokens received so it must match Amount currency; in withdraw, EPrice = LP tokens redeemed / asset received so it must match LP token issue, which is checked in preclaim against the AMM ledger entry, not in preflight) - Does NOT validate
TradingFeefield (withdraw transactions don't have this field) Amountvalidation considers withdrawal mode flags (tfOneAssetWithdrawAll|tfOneAssetLPToken) in addition toEPricepresence
Validation against the ledger view49
terNO_AMM: AMM ledger entry does not exist for specified asset pairtecINTERNAL:- pool balances are invalid (zero or negative)
tecAMM_EMPTY: AMM has zero LP tokens outstandingtecAMM_BALANCE:- Withdrawal amount (
AmountorAmount2) exceeds pool balance - Account has zero LP tokens
- Withdrawal amount (
tecNO_LINE:AssetorAsset2issuer haslsfRequireAuthflag set, but account has no trust line with the issuertecNO_AUTH:AssetorAsset2issuer haslsfRequireAuthflag set, and the trust line exists but lacks authorization (missinglsfLowAuthorlsfHighAuthflag)tecFROZEN(IOU/XRP) ortecLOCKED(MPT):AssetorAsset2is frozen/locked (AMM account, currency/issuance, or withdrawer account)temBAD_AMM_TOKENS:LPTokenInissue (currency code + issuer) does not match the AMM's LP token issueEPriceissue does not match the AMM's LP token issue
tecAMM_INVALID_TOKENS: LP token redemption amount (LPTokenIn) exceeds account's LP token holdings- MPT-specific validations (for either
AssetorAsset2if MPT): Both assets are validated usingcanMPTTradeAndTransfer. See MPT Validation Functions for validation logic and error conditions.
Validation during doApply50
- With fixAMMv1_1:
tecAMM_INVALID_TOKENS: LP token balance adjustment failed. When the withdrawer is the only remaining LP, if their LP token balance differs from the AMM'sLPTokenBalanceby more than 0.1%, the withdrawal fails. If the difference is within 0.1%, the AMM'sLPTokenBalanceis adjusted to match the account's balance to allow full withdrawal despite rounding errors. tecINTERNAL: AMM ledger entry does not exist (should not happen if preclaim succeeded)tecAMM_BALANCE:- Withdrawing one side of the pool (one asset amount equals pool balance but the other doesn't)
- Withdrawing all LP tokens but not all assets
- Withdrawal amount exceeds current pool balance
tecAMM_FAILED: Withdrawal constraints not satisfied (calculated withdrawal amounts don't meet minimum requirements specified in transaction fields)tecAMM_INVALID_TOKENS: Calculated LP tokens or withdrawal amounts are zero or invalidtecINSUFFICIENT_RESERVE: (With fixAMMv1_2) Insufficient XRP reserve to create trust line for withdrawn token that the account doesn't currently holdtecINCOMPLETE: Withdrawal empties the pool (all LP tokens redeemed) but AMM account deletion is incomplete due to too many trust lines to delete in a single transaction. The withdrawal succeeds, but the AMM account cleanup must be completed with subsequent AMMDelete transactions. Limited to deletingkMaxDeletableAmmTrustLinestrust lines per transaction.- Propagate errors from
accountSend()when transferring assets from AMM account to withdrawer (see Common Error Codes from accountSend())
-
AMMobject is modified:LPTokenBalance: Decreased by redeemed LP tokens- May be deleted if balance becomes zero (see AMMDelete)
-
AMMobject is deleted (if LPTokenBalance becomes zero and all trust lines can be deleted):- AMM pseudo-account deleted
- All trust lines deleted (up to
kMaxDeletableAmmTrustLinesper transaction) - Owner directory entries removed
- Note: If deletion is incomplete due to too many trust lines (
tecINCOMPLETEreturned), the AMM object and pseudo-account remain in the ledger with zero LP tokens. SubsequentAMMDeletetransactions are needed to complete cleanup.
-
AMM account balances are modified:
- Assets transferred from AMM account to withdrawer
- Balances updated in
AccountRoot(XRP),RippleState(tokens), orMPTokenentries (MPTs)
-
LP tokens are redeemed:
- LP tokens burned (trust line balance decreased)
- Trust line may be deleted if balance becomes zero and all parameters are default
-
Withdrawer's
AccountRootis modified:Balance: Increased by XRP withdrawn (if applicable)OwnerCount: Decremented if LP token trust line deletedOwnerCount: Incremented if new trust line created for withdrawn token (with fixAMMv1_2)
The AMMVote transaction allows LP token holders to vote on the AMM's trading fee.
Fields:
| Field Name | Required? | Modifiable? | JSON Type | Internal Type | Default Value | Description |
|---|---|---|---|---|---|---|
TransactionType |
✔️ | No |
String |
UInt16 |
Must be "AMMVote" |
|
Account |
✔️ | No |
String |
AccountID |
Account casting the vote | |
Asset |
✔️ | No |
Object |
Issue |
One of the pool's assets | |
Asset2 |
✔️ | No |
Object |
Issue |
The other pool asset | |
TradingFee |
✔️ | No |
Number |
UInt16 |
Proposed trading fee (0-1000, 1 = 0.001%) | |
Flags |
No |
Number |
UInt32 |
0 |
Transaction flags (must be 0 for AMMVote, only universal flags allowed) |
Static validation51
temDISABLED:- AMM amendment not enabled
- either
AssetorAsset2is an MPT but MPTokensV2 amendment is not enabled
temINVALID_FLAG: Invalid transaction flags (any flags set other than universal flags)temBAD_AMM_TOKENS:AssetandAsset2have the same currency and issuertemBAD_CURRENCY:AssetorAsset2uses the disallowed 3-letter "XRP" currency codetemBAD_ISSUER:AssetorAsset2is XRP (currency is all zeros) but has a non-zero issuer accounttemBAD_FEE:TradingFeeexceeds 1000
Validation against the ledger view52
terNO_AMM: AMM ledger entry does not exist for specified asset pairtecAMM_EMPTY: AMM has zero LP tokens outstandingtecAMM_INVALID_TOKENS: Account holds zero LP tokens (not an LP)
Validation during doApply53
tecINTERNAL: AMM ledger entry does not exist (should not happen if preclaim succeeded)
AMMobject is modified:VoteSlots: Updated with new/modified vote entry- Vote slots for accounts with zero LP tokens are removed
- If account already has a vote: Update fee and recalculate weight
- If account doesn't have a vote:
- If fewer than 8 votes: Add new vote
- If 8 votes exist: Replace vote with smallest LP balance (if new vote has more)
TradingFee: Recalculated as weighted average of all votes:TradingFee = SUM(VoteFee_i * LPTokens_i) / SUM(LPTokens_i)- If the calculated fee is non-zero, the
TradingFeefield is set - If the calculated fee is zero, the
TradingFeefield is removed (made absent)
- If the calculated fee is non-zero, the
AuctionSlot.DiscountedFee: Updated based on the new trading fee (ifAuctionSlotexists)- If
TradingFeeis non-zero andTradingFee / 10is non-zero, set toTradingFee / 10 - Otherwise, the
DiscountedFeefield is removed (made absent)
- If
The AMMBid transaction allows LP token holders to bid for the AMM's 24-hour auction slot.
Fields:
| Field Name | Required? | Modifiable? | JSON Type | Internal Type | Default Value | Description |
|---|---|---|---|---|---|---|
TransactionType |
✔️ | No |
String |
UInt16 |
Must be "AMMBid" |
|
Account |
✔️ | No |
String |
AccountID |
Account bidding for the auction slot | |
Asset |
✔️ | No |
Object |
Issue |
One of the pool's assets | |
Asset2 |
✔️ | No |
Object |
Issue |
The other pool asset | |
BidMin |
No |
String or Object |
Amount |
Minimum slot price willing to pay (in LP tokens) | ||
BidMax |
No |
String or Object |
Amount |
Maximum slot price willing to pay (in LP tokens) | ||
AuthAccounts |
No |
Array |
Array |
Array of up to 4 accounts to authorize for discounted fee | ||
Flags |
No |
Number |
UInt32 |
0 |
Transaction flags (must be 0 for AMMBid, only universal flags allowed) |
See Bidding documentation for more details.
Static validation54
temDISABLED:- AMM amendment not enabled
- either
AssetorAsset2is an MPT but MPTokensV2 amendment is not enabled
temINVALID_FLAG: Invalid transaction flags (any flags set other than universal flags)temBAD_AMM_TOKENS:AssetandAsset2have the same currency and issuertemBAD_CURRENCY:Asset,Asset2,BidMin, orBidMaxuses the disallowed 3-letter "XRP" currency code (0x5852500000000000)temBAD_ISSUER:Asset,Asset2,BidMin, orBidMaxis XRP (currency is all zeros) but has a non-zero issuer accounttemBAD_AMOUNT:BidMinorBidMaxis negative or zerotemMALFORMED:- More than 4 accounts in
AuthAccounts - (With fixAMMv1_3)
AuthAccountscontains the bidder account or duplicate accounts
- More than 4 accounts in
Validation against the ledger view55
terNO_AMM: AMM ledger entry does not exist for specified asset pairtecAMM_EMPTY: AMM has zero LP tokens outstandingterNO_ACCOUNT: Any account inAuthAccountsdoes not existtemBAD_AMM_TOKENS:BidMinorBidMaxissue (currency code + issuer) does not match the AMM's LP token issuetecAMM_INVALID_TOKENS:- Account holds zero LP tokens (not an LP)
BidMinorBidMaxis greater than the account's LP token holdings, or greater than or equal to the AMM's total LP token balanceBidMin>BidMax
Validation during doApply56
tecAMM_FAILED: Computed price exceedsBidMaxtecAMM_INVALID_TOKENS: Pay price exceeds LP token holdings- Propagate errors from
accountSend()when transferring LP tokens between bidder, previous holder, and AMM account (see Common Error Codes from accountSend())
The AMMBid transaction executes through the applyBid() function, which determines the slot price based on whether someone currently owns the auction slot and how much time has elapsed. For an unowned or expired slot, the bidder pays only the minimum price. For an owned slot, the price includes a 5% markup with a decay function over the 24-hour period. The system refunds the previous slot holder proportionally to their remaining time and burns the difference (bid price minus refund). State changes only occur when the bid execution succeeds. If validation fails (e.g., computed price exceeds BidMax, insufficient LP tokens), no ledger modifications are made. See Bidding documentation for the complete bidding logic including price calculation, refund mechanism, and LP token burning.
-
AMMobject is modified:AuctionSlot:Account: Set to bidderExpiration: Set to current time + 86,400 secondsPrice: Set to amount paidDiscountedFee: Set toTradingFee / 10when that quotient is non-zero; otherwise the field is removed (made absent)AuthAccounts: Set to specified accounts (or cleared if not specified)
LPTokenBalance: Decreased by burned amount
-
LP tokens are burned:
- Bid amount (minus refund) burned from bidder's LP token balance
- Reduces total LP token supply
-
Previous slot holder receives refund (if slot not expired):
- Refund =
(1 - fractionUsed) * PricePaid - Sent as LP tokens from bidder to previous holder
- Refund =
The AMMDelete transaction is used to clean up AMM instances that have been emptied (all LP tokens withdrawn). While the AMM can be automatically deleted when the last LP token is withdrawn, this transaction provides an explicit way to delete empty AMMs, especially useful when automatic deletion is incomplete.
Fields:
| Field Name | Required? | Modifiable? | JSON Type | Internal Type | Default Value | Description |
|---|---|---|---|---|---|---|
TransactionType |
✔️ | No |
String |
UInt16 |
Must be "AMMDelete" |
|
Account |
✔️ | No |
String |
AccountID |
Account deleting the AMM instance | |
Asset |
✔️ | No |
Object |
Issue |
One of the pool's assets | |
Asset2 |
✔️ | No |
Object |
Issue |
The other pool asset | |
Flags |
No |
Number |
UInt32 |
0 |
Transaction flags (must be 0 for AMMDelete, only universal flags allowed) |
Static validation57
temDISABLED:- AMM amendment not enabled
- either
AssetorAsset2is an MPT but MPTokensV2 amendment is not enabled
temINVALID_FLAG: Invalid transaction flags (any flags set other than universal flags)
Validation against the ledger view58
terNO_AMM: AMM ledger entry does not exist for specified asset pairtecAMM_NOT_EMPTY: AMM has non-zero LP tokens outstanding (AMM must be empty to delete)
Validation during doApply59
tecINTERNAL:- AMM ledger entry does not exist (should not happen if preclaim succeeded)
- AMM pseudo-account does not exist (should not happen if AMM entry exists)
- Directory node has invalid index during trustline deletion
- Non-trustline/non-MPToken ledger entry found in AMM owner directory (should only contain trust lines, MPTokens, and AMM entry)
- Trustline has non-zero balance during deletion (all trust lines should have zero balance if AMM is empty)
- Failed to remove AMM entry from owner directory
- Cannot delete root directory node
tecINCOMPLETE: Too many trust lines to delete in a single transaction (limited bykMaxDeletableAmmTrustLines). The transaction should be called again to continue deletion. This is not an error - it indicates partial success.- Propagate errors from
deleteAMMTrustLine()when deleting individual trust lines:tecINTERNAL: Trust line SLE is null or has wrong typetefBAD_LEDGER: Failed to remove directory link during trust line deletion
The AMMDelete transaction cleans up an empty AMM instance. The deletion process may complete in a single transaction or require multiple transactions if there are many trust lines.
Complete deletion (tesSUCCESS):
-
RippleStateobjects (trust lines) are deleted:- All trust lines associated with the AMM pseudo-account are removed
- This includes LP token trust lines and IOU asset trust lines
- Each trust line must have zero balance
- The counterparty (non-AMM) side of the trust line has its
OwnerCountdecremented - Directory entries for each trust line are removed from both accounts' owner directories
- Limited to
kMaxDeletableAmmTrustLinestrust lines per transaction
-
MPTokenobjects are deleted (if AMM uses MPT assets):- All MPToken entries associated with the AMM pseudo-account are removed
- Each MPToken must have zero
MPTAmountand zeroLockedAmount - At most two MPToken objects (one per asset)
- Each MPToken is removed from the AMM pseudo-account's owner directory and erased; no
OwnerCountis adjusted - MPTokens are only deleted after all trust lines are deleted
-
AMMobject is deleted:- The AMM ledger entry is removed from the ledger
- The entry is removed from the AMM pseudo-account's owner directory
-
AccountRootobject (AMM pseudo-account) is deleted:- The AMM pseudo-account is removed from the ledger
- Any remaining XRP balance should be zero (or minimal dust)
- The account's owner directory is removed
-
DirectoryNodeobjects are deleted:- The AMM pseudo-account's owner directory is removed
- All directory links are cleaned up
Partial deletion (tecINCOMPLETE):
When there are too many trust lines to delete in a single transaction:
-
RippleStateobjects are partially deleted:- Up to
kMaxDeletableAmmTrustLinestrust lines are deleted - Remaining trust lines stay in the ledger
- Each deleted trust line decrements the counterparty account's
OwnerCount
- Up to
-
MPTokenobjects remain in the ledger:- MPToken entries are not deleted during partial deletion
- MPTokens are only deleted after all trust lines are deleted
- This ensures AMM can be re-created with AMMDeposit if needed
-
AMMobject is deleted unless there are remaining trust lines or MPTokens:- When deletion is incomplete, the AMM object remains in the ledger
- Still has
LPTokenBalanceof zero - Still references the pseudo-account
-
AccountRootobject (AMM pseudo-account) is deleted unless there are remaining trust lines or MPTokens:- When deletion is incomplete, the pseudo-account remains in the ledger
- Owner directory still contains remaining trust lines and MPTokens (if present)
-
Subsequent AMMDelete transactions must be submitted:
- Each transaction deletes up to
kMaxDeletableAmmTrustLinesmore trust lines - Process continues until all trust lines are deleted
- Final transaction completes the full deletion (returns tesSUCCESS)
- Each transaction deletes up to
Note: The kMaxDeletableAmmTrustLines limit exists to prevent transactions from consuming excessive resources. AMMs with many LPs (and therefore many LP token trust lines) will require multiple AMMDelete transactions to fully clean up.
The deletion process:
- Verifies the AMM exists and is empty (zero LP tokens)
- Deletes all trust lines associated with the AMM account
- Removes the AMM from owner directories
- Deletes the AMM pseudo-account
- Deletes the AMM ledger entry
If there are too many trust lines to delete in a single transaction (limited by kMaxDeletableAmmTrustLines), the transaction returns tecINCOMPLETE and must be called again.
The AMMClawback transaction allows asset issuers to claw back their issued assets from AMM liquidity pools by withdrawing them from a specific LP token holder's position. This transaction is only available when the AMMClawback amendment is enabled.
Unlike the regular Clawback transaction which claws back trust line tokens and MPTs from individual holder balances, AMMClawback targets assets held in AMM liquidity pools. The issuer specifies an LP token holder, and the transaction withdraws the issuer's assets from the pool proportionally to that holder's LP token position, burning the corresponding LP tokens.
How it works:
The issuer identifies a holder who possesses LP tokens for the AMM pool. The transaction executes a proportional withdrawal from the pool, with the mechanics varying based on whether an explicit amount is specified.
When no Amount is provided, the transaction burns all of the holder's LP tokens and withdraws both pool assets proportionally using the AMM's equal-withdrawal formula. The issuer's asset (Asset) is immediately clawed back - transferred from the pool to the issuer where it is effectively removed from circulation. The second asset (Asset2), if not issued by the same issuer or if the tfClawTwoAssets flag is not set, is transferred to the holder rather than being clawed back.
When an Amount is specified, the transaction calculates the fraction of the pool that corresponds to the requested amount of the issuer's asset. It determines the number of LP tokens required to withdraw that precise amount, accounting for the current pool ratio. If the calculated LP tokens exceed the holder's balance, the transaction instead burns all available LP tokens and withdraws proportionally. Otherwise, it burns only the calculated LP tokens and withdraws both assets proportionally from the pool.
If the tfClawTwoAssets flag is set - which requires the issuer to issue both pool assets - the second asset is also clawed back. Without this flag, the second asset remains with the holder, leaving them with that asset while the issuer's asset is removed from circulation. The withdrawal from the AMM pool ignores freeze and authorization restrictions (FreezeHandling::IgnoreFreeze and AuthHandling::IgnoreAuth), ensuring clawback operations succeed even when assets are frozen or the holder lacks authorization. The subsequent transfer from holder to issuer uses standard clawback mechanics, which also bypasses authorization and freeze checks.
Fields:
| Field Name | Required? | Modifiable? | JSON Type | Internal Type | Default Value | Description |
|---|---|---|---|---|---|---|
TransactionType |
✔️ | No |
String |
UInt16 |
Must be "AMMClawback" |
|
Account |
✔️ | No |
String |
AccountID |
Issuer account (must be issuer of Asset) |
|
Asset |
✔️ | No |
Object |
Issue |
Asset to claw back (issuer must match Account) |
|
Asset2 |
✔️ | No |
Object |
Issue |
The other asset in the AMM pool | |
Holder |
✔️ | No |
String |
AccountID |
LP token holder whose position is being clawed back | |
Amount |
No |
String - Currency Amount |
Amount |
Specific amount of Asset to claw back (if omitted, claws back holder's entire position) |
||
Flags |
No |
Number |
UInt32 |
0 |
Transaction flags (see below) |
Transaction Flags:
| Flag Name | Hex Value | Description |
|---|---|---|
tfClawTwoAssets |
0x00000001 |
Claw back both assets (only valid when issuer issues both Asset and Asset2) |
Clawback mechanics:
The transaction uses AMM withdrawal logic internally:
- Calculates proportional amounts using the preservation function
- Burns LP tokens from the holder
- Transfers withdrawn assets from AMM pool to issuer
- For trust line tokens: Assets are burned (balance adjusts on shared RippleState)
- For MPTs: Assets are burned (holder's
MPTAmountdecreases, issuance'sOutstandingAmountdecreases)
Static validation60
temDISABLED:- AMMClawback amendment not enabled
- Either
AssetorAsset2is an MPT but MPTokensV2 amendment not enabled
temMALFORMED:AccountequalsHolder(cannot claw back from self)Assetis XRP (XRP cannot be clawed back)Asset.issuerdoes not matchAccount(issuer must match transaction sender)
temBAD_AMOUNT:Amountis specified butAmount.assetdoes not matchAssetAmountis zero or negative
temINVALID_FLAG:tfClawTwoAssetsis set butAsset.issuerdiffers fromAsset2.issuer(can only claw both assets if issuer issues both)- Invalid flags specified
Validation against the ledger view61
terNO_ACCOUNT: Issuer account or holder account does not existterNO_AMM: AMM pool does not exist for the specified asset pairtecNO_PERMISSION:- For trust line tokens (
AssetisIssue):- Issuer does not have
lsfAllowTrustLineClawbackflag set - Issuer has
lsfNoFreezeflag set
- Issuer does not have
- For MPTs (
AssetisMPTIssue):- MPT issuance does not have
lsfMPTCanClawbackflag set Asset.issuerdoes not match the MPT issuance's issuer
- MPT issuance does not have
- With
tfClawTwoAssets:Asset2does not meet the clawback requirements above
- For trust line tokens (
Validation during doApply62
tecINTERNAL:- AMM ledger entry does not exist
- AMM pseudo-account does not exist
- With fixAMMClawbackRounding: LP token balance verification encountered internal error when checking if holder is the only LP
tecAMM_BALANCE: Holder has zero LP tokens (nothing to claw back)tecAMM_INVALID_TOKENS:- With fixAMMClawbackRounding: Holder is the only remaining LP and their LP token balance differs from the AMM's
LPTokenBalanceby more than 0.1% - Calculated LP token amount during withdrawal is zero or invalid
- LP token balance adjustment failed during withdrawal
- With fixAMMClawbackRounding: Holder is the only remaining LP and their LP token balance differs from the AMM's
- Propagate errors from withdrawal logic (uses
AMMWithdraw::equalWithdrawTokensorequalWithdrawMatchingOneAmount):tecAMM_FAILED: Withdrawal constraints not satisfied- Other withdrawal-related errors (see AMMWithdraw Failure Conditions)
The AMMClawback transaction withdraws assets from an AMM pool by burning LP tokens from the holder's balance.
LP Token Changes:
- Holder's LP token balance is decreased:
- LP tokens are burned (destroyed from circulation)
- The amount burned equals either:
- All of holder's LP tokens (if
Amountnot specified) - Proportional LP tokens to withdraw the specified
Amount
- All of holder's LP tokens (if
- If holder's LP token balance reaches zero and the trust line has no other non-default fields, the trust line may be deleted
- Holder's
OwnerCountmay decrement if trust line is deleted
AMM Ledger Entry Changes:
AMMobject is modified:LPTokenBalance: Decreased by the burned LP tokens- If
LPTokenBalancereaches zero, the AMM may be automatically deleted (see AMMDelete)
Pool Asset Changes:
- AMM pseudo-account's asset balances are decreased:
- For trust line tokens (
RippleStatebalance adjusted) - For MPTs (
MPToken.MPTAmountdecreased) - For XRP (
AccountRoot.Balancedecreased) - Amounts withdrawn are proportional based on burned LP tokens and current pool balances
- For trust line tokens (
Asset Distribution:
-
Asset(always clawed back):- For trust line tokens: Transferred from holder to issuer via
directSendNoFee, adjusting the sharedRippleStatebalance - For MPTs: Burned from holder's
MPToken(decreases holder'sMPTAmountand issuance'sOutstandingAmount)
- For trust line tokens: Transferred from holder to issuer via
-
Asset2(conditionally clawed back):- With
tfClawTwoAssets: Same treatment asAsset(transferred to issuer and burned) - Without
tfClawTwoAssets: Remains with the holder (transferred from AMM pool to holder's balance)
- With
Footnotes
-
Initial LP token calculation:
AMMHelpers.cpp↩ -
Weighted average fee calculation:
AMMVote.cpp↩ -
Vote slot replacement logic:
AMMVote.cpp↩ -
AMM ledger entry type definition:
ledger_entries.macro↩ -
AMM keylet computation using asset pair:
Indexes.cpp↩ -
AMM namespace constant:
Indexes.cpp↩ -
Pseudo-account creation for AMM:
AMMCreate.cpp↩ -
Master key disabled with lsfDisableMaster flag:
AccountRootHelpers.cpp↩ -
AMMID field set in pseudo-account:
AMMCreate.cpp↩ -
Pseudo-account address generation:
AccountRootHelpers.cpp↩ -
Collision-avoidance algorithm for pseudo-account address:
AccountRootHelpers.cpp↩ -
AMM keylet hash with namespace
0x0041and fields(account, currency)per asset:Indexes.cpp↩ -
LP token trustline created with zero balance:
AMMCreate.cpp↩ -
Quality modifiers only set if non-zero:
RippleStateHelpers.cpp↩ -
Trust line marked with lsfAMMNode flag:
AMMCreate.cpp↩ -
MPToken created with lsfMPTAMM flag:
AMMCreate.cpp↩ -
MPToken creation for AMM pseudo-account:
AMMCreate.cpp↩ -
AMM owner count not adjusted for MPToken:
AMMCreate.cpp↩ -
Insufficient XRP balance check:
TokenHelpers.cpp↩ -
Negative amount check with fixAMMv1_1:
TokenHelpers.cpp↩ -
directSendNoLimitIOU function:
TokenHelpers.cpp↩ -
directSendNoFeeIOU function:
TokenHelpers.cpp↩ -
issueIOU function:
RippleStateHelpers.cpp↩ -
redeemIOU function:
RippleStateHelpers.cpp↩ -
Owner directory full check:
RippleStateHelpers.cpp↩ ↩2 -
Insufficient reserve to create trust line:
RippleStateHelpers.cpp↩ -
Trust line doesn't exist after attempting redeem:
RippleStateHelpers.cpp↩ -
Receiver account SLE null check:
TokenHelpers.cpp,RippleStateHelpers.cpp↩ ↩2 -
Peer account doesn't exist check:
RippleStateHelpers.cpp↩ ↩2 -
IOU send error propagation:
TokenHelpers.cpp↩ -
Directory removal failure in trustDelete:
RippleStateHelpers.cpp↩ -
MPT issuance not found:
TokenHelpers.cpp↩ -
MPT transfer exceeds MaximumAmount (directSendNoLimitMPT):
TokenHelpers.cpp↩ -
MPT transfer exceeds MaximumAmount (directSendNoFeeMPT):
TokenHelpers.cpp↩ -
Sender MPToken balance insufficient:
TokenHelpers.cpp↩ -
Sender MPToken entry missing:
TokenHelpers.cpp↩ -
Receiver MPToken entry missing:
TokenHelpers.cpp↩ -
Outstanding amount less than redemption:
TokenHelpers.cpp↩ -
Static validation (preflight):
AMMCreate.cpp↩ -
Validation against ledger view (preclaim):
AMMCreate.cpp↩ -
Validation during doApply:
AMMCreate.cpp↩ -
State changes (doApply):
AMMCreate.cpp↩ -
Static validation (preflight):
checkExtraFeatures,getFlagsMask,preflight↩ -
Validation against ledger view (preclaim):
AMMDeposit.cpp↩ -
Validation during doApply:
AMMDeposit.cpp↩ -
Static validation (preflight):
checkExtraFeatures,getFlagsMask,preflight↩ -
Validation against ledger view (preclaim):
AMMWithdraw.cpp↩ -
Validation during doApply:
AMMWithdraw.cpp↩ -
Static validation (preflight):
checkExtraFeatures,preflight↩ -
Validation against ledger view (preclaim):
AMMVote.cpp↩ -
Validation during doApply:
AMMVote.cpp↩ -
Static validation (preflight):
checkExtraFeatures,preflight↩ -
Validation against ledger view (preclaim):
AMMBid.cpp↩ -
Validation during doApply:
AMMBid.cpp↩ -
Static validation (preflight):
checkExtraFeatures,preflight↩ -
Validation against ledger view (preclaim):
AMMDelete.cpp↩ -
Validation during doApply:
AMMHelpers.cpp↩ -
Static validation (preflight):
checkExtraFeatures,getFlagsMask,preflight↩ -
Validation against ledger view (preclaim):
AMMClawback.cpp↩ -
Validation during doApply:
AMMClawback.cpp↩