This tutorial walks you through your first autonomous DEX trading session on the XRP Ledger using Claude. You will install two XRPL skills, read a live order book, and place a limit order on the permissionless XRPL DEX — all driven by natural-language prompts.
What you will build: a live order book read, a limit order placement, and an offer cancellation.
Time to complete: approximately 30 minutes.
Prerequisite: A funded XRPL testnet wallet. If you don't have one yet, follow Getting Started with Agentic Transactions first — it takes about 15 minutes and you will reuse the wallet here.
XRPL agent skills are layered: one shared foundation, one domain skill per use case. This tutorial uses the trading combination.
| Skill | Role | When it applies |
|---|---|---|
| XRPL Agent Wallet | Shared foundation | From the start — owns wallet creation, key loading, and the full signing ceremony (autofill → preview → confirm → sign → submit). |
| XRPL Trading | Domain skill | At transaction time — gives Claude accurate knowledge of XRPL DEX operations: OfferCreate, OfferCancel, order book reads, AMM interaction, fill classification, and agentic best practices. |
The Wallet skill owns the signing ceremony. The Trading skill constructs the right transaction object for any DEX operation. Claude coordinates the handoff — you do not need to manage it manually.
| Requirement | Notes |
|---|---|
| Node.js 18+ or Python 3.9+ | Code samples are provided in both languages. |
| Claude Code | Recommended for development — runs Claude in your terminal alongside your project files. |
| A funded testnet wallet | Follow Getting Started with Agentic Transactions if you don't have one. |
Install the XRP Ledger SDK for your language:
npm install xrplIf you completed the payments getting-started guide, the Wallet skill is already installed. Skip to installing the Trading skill.
# Wallet skill (skip if already installed)
npx skills add https://github.com/XRPLF/xrpl-dev-portal/tree/master/.claude/skills/xrpl-skills/xrpl-agent-wallet --agent claude-code
# Trading skill
npx skills add https://github.com/XRPLF/xrpl-dev-portal/tree/master/.claude/skills/xrpl-skills/xrpl-trading --agent claude-codeVerify both skills are loaded:
/skillsYou should see:
Project skills (.claude/skills)
xrpl-agent-wallet
xrpl-tradingTestnet has almost no real liquidity. The well-known Mainnet issuers (Bitstamp, GateHub, and so on) either do not exist on Testnet or have no usable order book there, so you cannot simply point at USD.Bitstamp and expect a market. Before you can place a meaningful trade you need a pair that actually has an offer on the other side.
You have two options.
Option A — trade against a one-sided book (fastest). Place a limit order that does not cross, let it rest, then cancel it. This exercises the full OfferCreate → verify → OfferCancel → verify loop and needs no counterparty. Skip to Step 3; just expect "one-sided book" when you read prices, and expect your order to rest rather than fill.
Option B — create your own pair (needed to see a real fill). Issue a test token from a second wallet and place a maker offer against it. Four transactions:
import { Client, Wallet, xrpToDrops, AccountSetAsfFlags } from "xrpl";
const client = new Client("wss://s.altnet.rippletest.net:51233");
await client.connect();
// Two throwaway Testnet wallets: an issuer and a market maker.
const { wallet: issuer } = await client.fundWallet();
const { wallet: maker } = await client.fundWallet();
const submit = async (w: Wallet, tx: any) =>
client.submitAndWait((w.sign(await client.autofill(tx))).tx_blob);
// 1. The issuer must allow its token to move between holders.
await submit(issuer, { TransactionType: "AccountSet", Account: issuer.classicAddress,
SetFlag: AccountSetAsfFlags.asfDefaultRipple });
// 2. The maker trusts the issuer for TST.
await submit(maker, { TransactionType: "TrustSet", Account: maker.classicAddress,
LimitAmount: { currency: "TST", issuer: issuer.classicAddress, value: "1000" } });
// 3. The issuer sends the maker 100 TST.
await submit(issuer, { TransactionType: "Payment", Account: issuer.classicAddress,
Destination: maker.classicAddress,
Amount: { currency: "TST", issuer: issuer.classicAddress, value: "100" } });
// 4. The maker posts liquidity: sell 20 TST for 4 XRP (0.2 XRP/TST).
await submit(maker, { TransactionType: "OfferCreate", Account: maker.classicAddress,
TakerPays: xrpToDrops(4),
TakerGets: { currency: "TST", issuer: issuer.classicAddress, value: "20" } });
console.log("Tradeable pair ready. Issuer:", issuer.classicAddress);
await client.disconnect();Note the issuer address that gets printed — the rest of this tutorial uses it wherever TEST_ISSUER appears. Your trading wallet needs no trust line to it: the ledger creates one automatically the first time you receive TST. Budget 0.2 XRP of owner reserve for it.
Order book reads are read-only — no signature or confirmation required. Ask Claude:
Show me the current XRP/USD order book on testnet — best bid, best ask,
mid price, and spread.Claude calls book_offers on both sides of the pair and returns a summary:
Order book: XRP / USD.Bitstamp (testnet)
Best ask : 0.512400 USD/XRP
Best bid : 0.511200 USD/XRP
Mid price: 0.511800 USD/XRP
Spread : 0.235 %
Ask depth: 12 offers, 8,420 XRP available at mid ± 1%The code behind this request:
import { Client, dropsToXrp } from "xrpl";
// NOTE: rvYAfWj5... is Bitstamp's MAINNET issuer. It has no meaningful book on
// Testnet. Substitute an issuer that is actually active on Testnet, or issue your
// own test token and trade against it.
const USD_ISSUER = process.env.TEST_ISSUER!; // a Testnet issuer you control
const client = new Client("wss://s.altnet.rippletest.net:51233");
await client.connect();
const [asks, bids] = await Promise.all([
client.request({
command: "book_offers",
taker_pays: { currency: "USD", issuer: USD_ISSUER },
taker_gets: { currency: "XRP" },
limit: 20,
}),
client.request({
command: "book_offers",
taker_pays: { currency: "XRP" },
taker_gets: { currency: "USD", issuer: USD_ISSUER },
limit: 20,
}),
]);
// `quality` is TakerPays/TakerGets in PROTOCOL units — XRP is in drops, so an
// XRP-denominated quality is 1,000,000x off and the two book sides are inverted.
// Always derive the price from the amounts instead.
const amt = (a: any) => typeof a === "string" ? Number(dropsToXrp(a)) : Number(a.value);
const price = (o: any, getsBase: boolean) =>
getsBase ? amt(o.TakerPays) / amt(o.TakerGets) : amt(o.TakerGets) / amt(o.TakerPays);
// `book_offers` also returns offers their owner cannot currently fund — including
// completely unfunded ones, which carry a real-looking price and size. Tradeable
// size is `taker_gets_funded` when present; drop anything that resolves to 0.
const fundedGets = (o: any) => o.taker_gets_funded ?? o.TakerGets;
const liveOffers = (os: any[]) => os.filter(o => amt(fundedGets(o)) > 0);
const askOffers = liveOffers(asks.result.offers); // taker_gets = XRP -> getsBase = true
const bidOffers = liveOffers(bids.result.offers); // taker_pays = XRP -> getsBase = false
const bestAsk = askOffers.length ? price(askOffers[0], true) : null;
const bestBid = bidOffers.length ? price(bidOffers[0], false) : null;
// A one-sided book has no mid price and no spread. Report that honestly.
if (bestAsk === null || bestBid === null) {
console.log("One-sided book — mid price and spread are undefined.");
} else {
const midPrice = (bestAsk + bestBid) / 2;
const spreadPct = ((bestAsk - bestBid) / midPrice) * 100;
console.log(`Mid price: ${midPrice.toFixed(6)} USD/XRP`);
console.log(`Spread : ${spreadPct.toFixed(3)} %`);
}
await client.disconnect();Ask Claude to place a limit order. The Trading skill builds the transaction object and the Wallet skill runs the signing ceremony — you will see a preview before anything is submitted.
Place a limit order to sell 10 XRP and buy 5 USD at 0.5 USD/XRP,
expiring in 1 hour. Show me the order book first.Claude will show you a pre-trade summary before requesting your signature:
Pre-trade summary
──────────────────────────────────────────────────────
Offering: 10 XRP (10,000,000 drops)
To receive: 5 USD (Bitstamp issuer)
Limit price: 0.500000 USD/XRP (minimum you will accept)
Mid price: 0.511800 USD/XRP (from order book)
Est. fill: 100% — your limit is below the best bid, so the whole order crosses
Exec. price: 0.511200 USD/XRP (you receive the better book price, not your limit)
Slippage: 0.00% vs best bid — depth exceeds order size
Expiry: 2026-08-21T14:30:00Z (1 hour from now)
──────────────────────────────────────────────────────
Proceed to sign? (yes / no)After acknowledgement, the Wallet skill displays its transaction preview:
─── XRPL Transaction Preview ────────────────────────────────────────
Network : testnet
Type : OfferCreate
From : rYourFullAddressShownInFullNoTruncation
To : —
Amount : —
Fee : 0.000012 XRP
Sequence : 48291010
LastLedgerSequence: 48291030 (expires in ~20 ledgers, ~80 seconds)
Flags : 0
Memos : —
TakerGets : 10 XRP (10,000,000 drops)
TakerPays : 5 USD issuer rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B
Other fields : Expiration 2026-08-21T14:30:00Z
─────────────────────────────────────────────────────────────────────
Sign and submit? (yes / no)The code Claude generates for the transaction object:
import { OfferCreate, xrpToDrops } from "xrpl";
const XRPL_EPOCH_OFFSET = 946_684_800;
const expiryXRPL = Math.floor(Date.now() / 1000) + 3600 - XRPL_EPOCH_OFFSET;
const offer: Omit<OfferCreate, "Fee" | "Sequence" | "LastLedgerSequence"> = {
TransactionType: "OfferCreate",
Account: "rYourAddress",
TakerPays: { currency: "USD", issuer: "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", value: "5" },
TakerGets: xrpToDrops(10), // "10000000" drops
Expiration: expiryXRPL,
// Fee, Sequence, LastLedgerSequence: set by Wallet skill autofill
// SourceTag: applied by Wallet skill (20260530) automatically
};
// → hand to XRPL Agent Wallet skillAfter signing, Claude classifies the result:
Fill status: partial (remainder resting)
Amount filled: 6 of 10 XRP sold (60%), 3.07 USD received
Remaining: 4 XRP / 2 USD resting on the book
Offer sequence: 48291010 ← save this to cancel later
Fee paid: 12 drops (0.000012 XRP)
Tx hash: A3F9B2...Save the offer sequence. You will need it to cancel the resting portion.
If the offer doesn't fill completely, cancel it with:
Cancel my resting offer at sequence 48291010.Claude verifies the offer exists, shows a cancellation summary, and presents the Wallet skill's preview before submitting:
Cancelling offer sequence: 48291010
You are giving up: 4 XRP (remaining TakerGets)
You would have received: 2 USD (remaining TakerPays)
Network fee: 12 dropsThe OfferCancel transaction:
import { OfferCancel } from "xrpl";
const cancel: Omit<OfferCancel, "Fee" | "Sequence" | "LastLedgerSequence"> = {
TransactionType: "OfferCancel",
Account: "rYourAddress",
OfferSequence: 48291010, // Sequence of the original OfferCreate
};
// → hand to XRPL Agent Wallet skillA successful cancellation returns tesSUCCESS with the offer in meta.AffectedNodes as a DeletedNode. If the offer was already filled, the cancel still succeeds but does nothing — and the fee is still charged. Always verify offer existence before cancelling.
| Goal | Flag to set | Notes |
|---|---|---|
| Standard limit order (rests if unfilled) | None | Default |
| Market order (fill what you can, cancel rest) | tfImmediateOrCancel | Draws on AMM + order book |
| Must fill completely or cancel | tfFillOrKill | tecKILLED on failure — fee charged |
| Post-only (never cross, always rest) | tfPassive | Use for maker strategies |
Never combine tfImmediateOrCancel and tfFillOrKill. The Trading skill rejects this combination at construction.
Skill reference
- The XRPL Trading Skill — Full reference for offer semantics, flag behaviour, AMM interaction, fill classification, and error codes.
- The XRPL Agent Wallet Skill — Security model, signing ceremony, key handling patterns, and production setup including OWS.
Use case guides
- Agentic Payments with X402 — Pay for HTTP services autonomously using XRPL payments.
- Track and Measure Agent Behavior — Use SourceTag, Memos, and WebSocket monitoring to attribute and audit every agent transaction.
Go deeper on XRPL features
- Decentralized Exchange — XRPL DEX concepts: offer matching, quality, and the order book.
- Automated Market Maker — AMM pools and how they interact with the DEX order book.
SDK references