DEX Advanced - AMM Basics
Learning Objectives
Explain AMM mechanics and how they differ from order books
Query AMM pool information including reserves and LP token supply
Execute swaps against AMM pools
Provide and withdraw liquidity from AMM pools
Understand impermanent loss and its implications for liquidity providers
XRPL is unique in having both order books and AMMs native to the protocol:
| Feature | Order Book | AMM |
|---|---|---|
| Price discovery | Market makers set prices | Formula-based |
| Liquidity | Requires active orders | Always available |
| Capital efficiency | High (targeted prices) | Lower (spread across curve) |
| Complexity | Higher (manage orders) | Lower (set and forget) |
| Slippage | Depends on depth | Predictable by formula |
The key insight: They're not competitors—they complement each other. Small trades often use AMM; large trades might use order book for better prices; pathfinding can combine both.
XRPL's AMM uses the classic constant product formula:
x * y = k
- x = reserve of token A
- y = reserve of token B
- k = constant (the product)
After any trade:
x' * y' = k (must remain equal)
Example:
Pool: 1000 XRP × 500 USD = 500,000 (k)
- They put in USD, take out XRP
- New XRP: 1000 - 10 = 990
- Required USD: 500,000 / 990 = 505.05
- Cost: 505.05 - 500 = 5.05 USD for 10 XRP
- Price: 0.505 USD/XRP
Notice: Each additional XRP costs more (slippage)
// Calculate price impact for a swap
function calculateSwap(reserveIn, reserveOut, amountIn, tradingFee = 0.003) {
// Apply fee to input
const amountInAfterFee = amountIn * (1 - tradingFee);
// Constant product formula
const k = reserveIn * reserveOut;
const newReserveIn = reserveIn + amountInAfterFee;
const newReserveOut = k / newReserveIn;
const amountOut = reserveOut - newReserveOut;
// Calculate effective price
const spotPrice = reserveOut / reserveIn; // Before trade
const executionPrice = amountOut / amountIn; // What you get
const priceImpact = (spotPrice - executionPrice) / spotPrice;
return {
amountOut: amountOut,
spotPrice: spotPrice,
executionPrice: executionPrice,
priceImpact: priceImpact,
fee: amountIn * tradingFee
};
}
// Example
const result = calculateSwap(1000, 500, 100);
// Swapping 100 XRP for USD in a 1000 XRP / 500 USD pool
console.log(Amount out: ${result.amountOut.toFixed(2)} USD);
console.log(Spot price: ${result.spotPrice.toFixed(4)} USD/XRP);
console.log(Execution price: ${result.executionPrice.toFixed(4)} USD/XRP);
console.log(Price impact: ${(result.priceImpact * 100).toFixed(2)}%);
```
XRPL's AMM has unique features beyond basic Uniswap:
XRPL AMM Features:
- Native protocol integration (no smart contract)
- Trading fee auction (can bid for lower fees)
- Auto-compounding (fees stay in pool)
- LP token as standard XRPL token
- Interoperability with order book// src/amm/query-amm.js
const xrpl = require('xrpl');
async function getAMMInfo(asset1, asset2) {
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233');
await client.connect();
try {
const response = await client.request({
command: 'amm_info',
asset: asset1,
asset2: asset2
});
const amm = response.result.amm;
return {
account: amm.account, // AMM's account address
lpToken: amm.lp_token, // LP token details
tradingFee: amm.trading_fee / 1000, // Convert to percentage
// Pool reserves
amount1: parseAmount(amm.amount),
amount2: parseAmount(amm.amount2),
// Auction slot info (if someone won the fee auction)
auctionSlot: amm.auction_slot,
// Calculated values
spotPrice: calculateSpotPrice(amm.amount, amm.amount2),
tvl: calculateTVL(amm.amount, amm.amount2)
};
} catch (error) {
if (error.data?.error === 'actNotFound') {
return { exists: false, message: 'AMM pool not found' };
}
throw error;
} finally {
await client.disconnect();
}
}
function parseAmount(amount) {
if (typeof amount === 'string') {
return { currency: 'XRP', value: Number(amount) / 1_000_000 };
}
return {
currency: amount.currency,
issuer: amount.issuer,
value: Number(amount.value)
};
}
function calculateSpotPrice(amount1, amount2) {
const val1 = typeof amount1 === 'string'
? Number(amount1) / 1_000_000
: Number(amount1.value);
const val2 = typeof amount2 === 'string'
? Number(amount2) / 1_000_000
: Number(amount2.value);
return val2 / val1; // Price of asset1 in terms of asset2
}
function calculateTVL(amount1, amount2) {
// This is simplified - real TVL needs price oracles
const val1 = typeof amount1 === 'string'
? Number(amount1) / 1_000_000
: Number(amount1.value);
const val2 = typeof amount2 === 'string'
? Number(amount2) / 1_000_000
: Number(amount2.value);
return { asset1: val1, asset2: val2 };
}
// Example usage
async function example() {
const info = await getAMMInfo(
{ currency: 'XRP' },
{ currency: 'USD', issuer: 'rIssuer...' }
);
if (info.exists !== false) {
console.log('AMM Pool Info:');
console.log( Account: ${info.account});
console.log( Trading fee: ${info.tradingFee}%);
console.log( Reserve 1: ${info.amount1.value} ${info.amount1.currency});
console.log( Reserve 2: ${info.amount2.value} ${info.amount2.currency});
console.log( Spot price: ${info.spotPrice.toFixed(6)});
}
}
module.exports = { getAMMInfo };
```
// LP tokens are standard XRPL tokens with special currency code
async function getLPTokenBalance(address, ammAccount) {
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233');
await client.connect();
try {
// First get the LP token currency code
const ammInfo = await client.request({
command: 'amm_info',
amm_account: ammAccount
});
const lpToken = ammInfo.result.amm.lp_token;
// Query holder's balance
const lines = await client.request({
command: 'account_lines',
account: address
});
const lpLine = lines.result.lines.find(
line => line.currency === lpToken.currency &&
line.account === ammAccount
);
return {
currency: lpToken.currency,
issuer: ammAccount,
balance: lpLine ? Number(lpLine.balance) : 0,
totalSupply: Number(lpToken.value)
};
} finally {
await client.disconnect();
}
}
```
// src/amm/swap.js
const xrpl = require('xrpl');
async function swapThroughAMM(
wallet,
assetIn, // What you're selling
amountIn, // How much you're selling
assetOut, // What you're buying
minAmountOut // Minimum you'll accept (slippage protection)
) {
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233');
await client.connect();
try {
// Format amounts
const amountInFormatted = formatAsAmount(assetIn, amountIn);
const amountOutFormatted = formatAsAmount(assetOut, minAmountOut);
// AMMDeposit with tfLimitLPToken or regular Payment with paths
// For swaps, we use Payment with the AMM as path
// Get AMM info for the path
const ammInfo = await client.request({
command: 'amm_info',
asset: assetIn,
asset2: assetOut
});
// Create payment with DeliverMin for slippage protection
const payment = {
TransactionType: 'Payment',
Account: wallet.address,
Destination: wallet.address, // Swap to self
Amount: amountOutFormatted,
SendMax: amountInFormatted,
DeliverMin: amountOutFormatted,
Flags: 131072 // tfPartialPayment
};
// Pathfinding will automatically include AMM if it offers best rate
const prepared = await client.autofill(payment);
const signed = wallet.sign(prepared);
const result = await client.submitAndWait(signed.tx_blob);
if (result.result.meta.TransactionResult === 'tesSUCCESS') {
const delivered = result.result.meta.delivered_amount;
return {
success: true,
amountIn: amountIn,
amountOut: parseAmount(delivered).value,
hash: signed.hash
};
}
return {
success: false,
resultCode: result.result.meta.TransactionResult
};
} finally {
await client.disconnect();
}
}
function formatAsAmount(asset, value) {
if (asset.currency === 'XRP') {
return xrpl.xrpToDrops(value);
}
return {
currency: asset.currency,
issuer: asset.issuer,
value: value.toString()
};
}
function parseAmount(amount) {
if (typeof amount === 'string') {
return { currency: 'XRP', value: Number(amount) / 1_000_000 };
}
return { currency: amount.currency, value: Number(amount.value) };
}
module.exports = { swapThroughAMM };
```
For explicit AMM-only swaps (bypassing order book):
async function directAMMSwap(
wallet,
asset,
asset2,
amountToSwap,
isAsset1 // true if swapping asset1 for asset2
) {
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233');
await client.connect();
try {
// First get current AMM state to calculate expected output
const ammInfo = await client.request({
command: 'amm_info',
asset: asset,
asset2: asset2
});
const amm = ammInfo.result.amm;
// Calculate expected output
const reserve1 = parseAmount(amm.amount).value;
const reserve2 = parseAmount(amm.amount2).value;
const fee = amm.trading_fee / 100000; // Convert from basis points
let expectedOut;
if (isAsset1) {
expectedOut = calculateOutput(reserve1, reserve2, amountToSwap, fee);
} else {
expectedOut = calculateOutput(reserve2, reserve1, amountToSwap, fee);
}
// Set minimum output with 1% slippage tolerance
const minOut = expectedOut * 0.99;
console.log(`Swapping ${amountToSwap} for estimated ${expectedOut.toFixed(4)}`);
console.log(`Minimum acceptable: ${minOut.toFixed(4)}`);
// Use AMMDeposit with tfSingleAsset and withdraw other
// This is more complex - typically use Payment with paths
return await swapThroughAMM(
wallet,
isAsset1 ? asset : asset2,
amountToSwap,
isAsset1 ? asset2 : asset,
minOut
);
} finally {
await client.disconnect();
}
}
function calculateOutput(reserveIn, reserveOut, amountIn, fee) {
const amountInAfterFee = amountIn * (1 - fee);
const k = reserveIn * reserveOut;
const newReserveIn = reserveIn + amountInAfterFee;
const newReserveOut = k / newReserveIn;
return reserveOut - newReserveOut;
}
// src/amm/create-pool.js
const xrpl = require('xrpl');
async function createAMMPool(
wallet,
asset1,
amount1,
asset2,
amount2,
tradingFee = 500 // 0.5% in basis points (500 = 0.5%)
) {
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233');
await client.connect();
try {
// Check if pool already exists
try {
await client.request({
command: 'amm_info',
asset: asset1,
asset2: asset2
});
throw new Error('AMM pool already exists for this pair');
} catch (error) {
if (error.data?.error !== 'actNotFound') {
throw error;
}
// Pool doesn't exist - good, we can create it
}
const ammCreate = {
TransactionType: 'AMMCreate',
Account: wallet.address,
Amount: formatAsAmount(asset1, amount1),
Amount2: formatAsAmount(asset2, amount2),
TradingFee: tradingFee // Basis points (500 = 0.5%)
};
console.log('Creating AMM pool...');
console.log( ${amount1} ${asset1.currency || 'XRP'});
console.log( ${amount2} ${asset2.currency || 'XRP'});
console.log( Trading fee: ${tradingFee / 100}%);
const prepared = await client.autofill(ammCreate);
const signed = wallet.sign(prepared);
const result = await client.submitAndWait(signed.tx_blob);
if (result.result.meta.TransactionResult === 'tesSUCCESS') {
// Find the created AMM account
const ammNode = result.result.meta.AffectedNodes.find(
node => node.CreatedNode?.LedgerEntryType === 'AMM'
);
const ammAccount = ammNode?.CreatedNode?.NewFields?.Account;
console.log('✓ AMM pool created');
console.log( AMM Account: ${ammAccount});
return {
success: true,
ammAccount: ammAccount,
hash: signed.hash
};
}
return {
success: false,
resultCode: result.result.meta.TransactionResult
};
} finally {
await client.disconnect();
}
}
function formatAsAmount(asset, value) {
if (asset.currency === 'XRP' || !asset.currency) {
return xrpl.xrpToDrops(value);
}
return {
currency: asset.currency,
issuer: asset.issuer,
value: value.toString()
};
}
module.exports = { createAMMPool };
```
// src/amm/add-liquidity.js
const xrpl = require('xrpl');
async function addLiquidity(
wallet,
asset,
asset2,
amount, // Amount of asset1 to deposit
amount2 // Amount of asset2 to deposit (proportional)
) {
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233');
await client.connect();
try {
// Get current pool ratio
const ammInfo = await client.request({
command: 'amm_info',
asset: asset,
asset2: asset2
});
const amm = ammInfo.result.amm;
const reserve1 = parseAmount(amm.amount).value;
const reserve2 = parseAmount(amm.amount2).value;
// Calculate proportional amount if not specified
if (!amount2) {
amount2 = amount * (reserve2 / reserve1);
}
console.log(Adding liquidity:);
console.log( ${amount} ${asset.currency || 'XRP'});
console.log( ${amount2} ${asset2.currency || 'XRP'});
const deposit = {
TransactionType: 'AMMDeposit',
Account: wallet.address,
Asset: asset,
Asset2: asset2,
Amount: formatAsAmount(asset, amount),
Amount2: formatAsAmount(asset2, amount2),
Flags: 0x00100000 // tfTwoAsset - deposit both assets proportionally
};
const prepared = await client.autofill(deposit);
const signed = wallet.sign(prepared);
const result = await client.submitAndWait(signed.tx_blob);
if (result.result.meta.TransactionResult === 'tesSUCCESS') {
// Find LP tokens received
const lpTokenChange = findLPTokenChange(result.result.meta);
return {
success: true,
lpTokensReceived: lpTokenChange,
hash: signed.hash
};
}
return {
success: false,
resultCode: result.result.meta.TransactionResult
};
} finally {
await client.disconnect();
}
}
// Single-sided deposit (only one asset)
async function addSingleSidedLiquidity(
wallet,
asset,
asset2,
assetToDeposit, // Which asset you're depositing
amount
) {
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233');
await client.connect();
try {
const isAsset1 = assetToDeposit.currency === asset.currency;
const deposit = {
TransactionType: 'AMMDeposit',
Account: wallet.address,
Asset: asset,
Asset2: asset2,
[isAsset1 ? 'Amount' : 'Amount2']: formatAsAmount(assetToDeposit, amount),
Flags: 0x00080000 // tfSingleAsset
};
const prepared = await client.autofill(deposit);
const signed = wallet.sign(prepared);
const result = await client.submitAndWait(signed.tx_blob);
return {
success: result.result.meta.TransactionResult === 'tesSUCCESS',
hash: signed.hash
};
} finally {
await client.disconnect();
}
}
function findLPTokenChange(meta) {
for (const node of meta.AffectedNodes) {
if (node.ModifiedNode?.LedgerEntryType === 'RippleState') {
const fields = node.ModifiedNode.FinalFields;
const prev = node.ModifiedNode.PreviousFields;
if (fields?.Balance && prev?.Balance) {
// Check if this is LP token by currency format (3 char hex)
const currency = fields.Balance.currency;
if (currency && currency.length === 40) { // LP token format
const newBalance = Number(fields.Balance.value);
const oldBalance = Number(prev.Balance.value);
return Math.abs(newBalance - oldBalance);
}
}
}
}
return null;
}
function parseAmount(amount) {
if (typeof amount === 'string') {
return { currency: 'XRP', value: Number(amount) / 1_000_000 };
}
return { currency: amount.currency, value: Number(amount.value) };
}
function formatAsAmount(asset, value) {
if (asset.currency === 'XRP' || !asset.currency) {
return xrpl.xrpToDrops(value);
}
return {
currency: asset.currency,
issuer: asset.issuer,
value: value.toString()
};
}
module.exports = { addLiquidity, addSingleSidedLiquidity };
```
// src/amm/withdraw-liquidity.js
const xrpl = require('xrpl');
async function withdrawLiquidity(
wallet,
asset,
asset2,
lpTokenAmount // Amount of LP tokens to redeem
) {
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233');
await client.connect();
try {
// Get AMM info for LP token details
const ammInfo = await client.request({
command: 'amm_info',
asset: asset,
asset2: asset2
});
const lpToken = ammInfo.result.amm.lp_token;
const withdraw = {
TransactionType: 'AMMWithdraw',
Account: wallet.address,
Asset: asset,
Asset2: asset2,
LPTokenIn: {
currency: lpToken.currency,
issuer: ammInfo.result.amm.account,
value: lpTokenAmount.toString()
},
Flags: 0x00010000 // tfLPToken - withdraw proportional to LP tokens
};
console.log(Withdrawing ${lpTokenAmount} LP tokens);
const prepared = await client.autofill(withdraw);
const signed = wallet.sign(prepared);
const result = await client.submitAndWait(signed.tx_blob);
if (result.result.meta.TransactionResult === 'tesSUCCESS') {
// Calculate what was withdrawn
const withdrawals = findWithdrawals(result.result.meta, asset, asset2);
return {
success: true,
withdrawn: withdrawals,
hash: signed.hash
};
}
return {
success: false,
resultCode: result.result.meta.TransactionResult
};
} finally {
await client.disconnect();
}
}
// Withdraw all liquidity
async function withdrawAllLiquidity(wallet, asset, asset2) {
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233');
await client.connect();
try {
// Get LP token balance
const ammInfo = await client.request({
command: 'amm_info',
asset: asset,
asset2: asset2
});
const lpToken = ammInfo.result.amm.lp_token;
const ammAccount = ammInfo.result.amm.account;
// Get my balance
const lines = await client.request({
command: 'account_lines',
account: wallet.address
});
const lpLine = lines.result.lines.find(
l => l.currency === lpToken.currency && l.account === ammAccount
);
if (!lpLine || Number(lpLine.balance) <= 0) {
return { success: false, error: 'No LP tokens to withdraw' };
}
return withdrawLiquidity(wallet, asset, asset2, lpLine.balance);
} finally {
await client.disconnect();
}
}
function findWithdrawals(meta, asset, asset2) {
// Parse affected nodes to find withdrawn amounts
// This is simplified - full implementation would track all changes
return { asset1: null, asset2: null };
}
module.exports = { withdrawLiquidity, withdrawAllLiquidity };
```
When you provide liquidity to an AMM, you're exposed to impermanent loss (IL):
Initial deposit: 100 XRP + 50 USD (price: 0.50 USD/XRP)
Value: $100
- If you held: 100 XRP = $100, 50 USD = $50 → Total: $150
- In AMM: ~70.7 XRP + 70.7 USD → Total: $141.4
Impermanent Loss: $150 - $141.4 = $8.60 (5.7%)
// src/amm/impermanent-loss.js
function calculateImpermanentLoss(priceRatio) {
// priceRatio = new price / original price
// IL = 2 * sqrt(r) / (1 + r) - 1
const sqrtR = Math.sqrt(priceRatio);
const il = (2 * sqrtR) / (1 + priceRatio) - 1;
return Math.abs(il); // Return as positive percentage
}
// Impermanent loss at various price changes
console.log('Price Change → Impermanent Loss');
console.log('1.25x (25% up) →', (calculateImpermanentLoss(1.25) * 100).toFixed(2) + '%');
console.log('1.50x (50% up) →', (calculateImpermanentLoss(1.50) * 100).toFixed(2) + '%');
console.log('2.00x (100% up) →', (calculateImpermanentLoss(2.00) * 100).toFixed(2) + '%');
console.log('3.00x (200% up) →', (calculateImpermanentLoss(3.00) * 100).toFixed(2) + '%');
console.log('5.00x (400% up) →', (calculateImpermanentLoss(5.00) * 100).toFixed(2) + '%');
/*
Output:
Price Change → Impermanent Loss
1.25x (25% up) → 0.62%
1.50x (50% up) → 2.02%
2.00x (100% up) → 5.72%
3.00x (200% up) → 13.40%
5.00x (400% up) → 25.46%
*/
// Full profit/loss calculation
function calculateLPReturns(
initialPrice,
finalPrice,
tradingFees, // Accumulated trading fees (as % of position)
initialValue
) {
const priceRatio = finalPrice / initialPrice;
const il = calculateImpermanentLoss(priceRatio);
// Value if just held
const holdValue = initialValue * (1 + priceRatio) / 2;
// Value in AMM (affected by IL)
const ammValue = holdValue * (1 - il);
// Add trading fee income
const totalValue = ammValue * (1 + tradingFees);
return {
holdValue,
ammValueBeforeFees: ammValue,
totalValue,
impermanentLoss: il,
profitFromFees: tradingFees,
netProfitVsHold: (totalValue - holdValue) / holdValue
};
}
module.exports = { calculateImpermanentLoss, calculateLPReturns };
```
// Trading fees can offset impermanent loss
// Break-even analysis
function feesNeededToOffsetIL(priceChange) {
const il = calculateImpermanentLoss(priceChange);
// Fees needed = IL / (1 - IL)
return il / (1 - il);
}
console.log('\nFees needed to break even:');
console.log('50% price move: need', (feesNeededToOffsetIL(1.5) * 100).toFixed(2) + '% fees');
console.log('100% price move: need', (feesNeededToOffsetIL(2.0) * 100).toFixed(2) + '% fees');
console.log('200% price move: need', (feesNeededToOffsetIL(3.0) * 100).toFixed(2) + '% fees');
// Key insight: High-volume, stable pairs are best for LPs
// Low-volume or volatile pairs often don't generate enough fees
```
// When to use AMM:
const useAMM = {
smallTrades: true, // Consistent pricing for small amounts
lowLiquidity: true, // Something always available
passiveLP: true, // Set and forget liquidity
stablePairs: true, // Less IL risk
simplicity: true // No order management
};
// When to use Order Book:
const useOrderBook = {
largeTrades: true, // Better prices if depth exists
specificPrices: true, // Exact limit orders
activeTrading: true, // Manage positions
volatilePairs: true, // Avoid IL
marketMaking: true // Professional strategies
};
// Hybrid approach (XRPL does this automatically):
// - Pathfinding checks both AMM and order book
// - Uses whichever offers better price
// - Can split orders across both
```
AMMs on XRPL provide convenient, always-available liquidity, but they're not free money. Impermanent loss is real and can exceed trading fee income on volatile or low-volume pairs. Best suited for stable pairs where trading volume generates meaningful fees relative to price movement.
Assignment: Build a comprehensive tool for interacting with XRPL AMM pools.
Requirements:
Query AMM pool information
Display reserves, TVL, and current price
Calculate potential swap outputs with slippage
Compare AMM price to order book
Execute swaps through AMM
Calculate and display price impact
Implement slippage protection
Show transaction results
Add liquidity (two-sided and single-sided)
Withdraw liquidity (partial and full)
Display current LP position value
Track LP token balance
Calculate impermanent loss for current position
Estimate fee income (if data available)
Compare LP returns vs holding
Display position history
Pool analysis accurate (25%)
Swaps work correctly with slippage protection (25%)
Liquidity management functional (25%)
Analytics and IL calculation correct (25%)
Time investment: 3-4 hours
Value: Complete AMM interaction capability for any XRPL application
1. AMM Mechanics Question:
An AMM pool has 1000 XRP and 500 USD (k = 500,000). Someone buys 100 XRP. Approximately how much USD do they pay (ignoring fees)?
A) 50 USD
B) 55.56 USD
C) 45 USD
D) 100 USD
Correct Answer: B
Explanation: After removing 100 XRP, the pool has 900 XRP. To maintain k = 500,000: USD = 500,000 / 900 = 555.56. The buyer pays 555.56 - 500 = 55.56 USD. Notice this is more than 50 USD (linear pricing would suggest)—that's slippage from the constant product formula.
2. Impermanent Loss Question:
You provide liquidity when XRP is $0.50. The price moves to $1.00 (2x). What's your approximate impermanent loss?
A) 0%
B) ~5.7%
C) ~25%
D) 50%
Correct Answer: B
Explanation: IL for a 2x price move is approximately 5.7%. The formula: IL = 2×√(r)/(1+r) - 1 where r = 2. This means your LP position is worth 5.7% less than if you had simply held the original assets. This loss is "impermanent" because it reverses if price returns to entry point.
3. LP Strategy Question:
Which pair would typically be best for providing AMM liquidity?
A) XRP/BTC (highly correlated, volatile)
B) USD/EUR (stable, high volume on traditional markets)
C) XRP/USD (volatile XRP, stable USD)
D) MEME/XRP (low volume, high volatility)
Correct Answer: B
Explanation: Stable pairs with high volume are ideal: minimal price divergence means low IL, and high volume generates fees. USD/EUR is most stable with typically high volume. XRP pairs involve XRP volatility (IL risk). MEME tokens have high volatility (severe IL) and likely low volume (few fees). In practice, XRPL may not have USD/EUR pools, but the principle applies.
4. Swap Execution Question:
You want to swap XRP for USD and get the best possible price. What should you do on XRPL?
A) Only use the AMM to avoid order book complexity
B) Only use the order book for better prices
C) Use a regular Payment with pathfinding—it automatically finds the best route
D) Manually split the order between AMM and order book
Correct Answer: C
Explanation: XRPL's pathfinding automatically considers both AMM pools and order book offers, using whichever (or combination) offers the best price. You don't need to manually choose or split—just submit a payment and let the protocol optimize. This is a key advantage of having both systems native to the protocol.
5. Pool Creation Question:
You create an AMM pool with 1000 XRP and 500 USD at a trading fee of 500 basis points (0.5%). Who earns the trading fees?
A) The pool creator exclusively
B) All liquidity providers proportionally to their share
C) XRPL validators
D) Ripple Labs
Correct Answer: B
Explanation: Trading fees are distributed to all liquidity providers proportionally to their share of the LP tokens. If you own 10% of LP tokens, you earn 10% of fees. Fees automatically compound in the pool (increasing pool reserves), so LP tokens gradually become worth more. There's no special privilege for the original creator.
- AMM Overview: https://xrpl.org/automated-market-makers.html
- AMMCreate: https://xrpl.org/ammcreate.html
- AMMDeposit: https://xrpl.org/ammdeposit.html
- AMMWithdraw: https://xrpl.org/ammwithdraw.html
- amm_info: https://xrpl.org/amm_info.html
- Constant Product AMMs: Uniswap documentation
- Impermanent Loss explainers: Various DeFi resources
For Next Lesson:
You now understand both DEX mechanisms. Lesson 10 covers Escrow—time-locked and condition-locked payments for scheduled releases and atomic swaps.
End of Lesson 9
Total words: ~5,300
Estimated completion time: 55 minutes reading + 3-4 hours for deliverable
Key Takeaways
AMM uses constant product formula
: x × y = k. Larger trades cause more slippage as you move along the curve.
XRPL's pathfinding uses both
: You don't always need to choose. XRPL's payment routing automatically finds the best price across AMM and order book.
Impermanent loss is real
: The more prices diverge from your entry, the worse IL gets. Fees must offset IL for profitable LP.
LP tokens are standard XRPL tokens
: Can be transferred, traded, used as collateral (in theory).
Best for stable, high-volume pairs
: IL is manageable, and volume generates fees. Avoid LPing highly volatile pairs unless you're hedged. ---