xrpl.js — JavaScript SDK Guide
xrpl.js is the JavaScript and TypeScript library for the XRP Ledger: it connects to a server, signs with your keys on your machine, and submits. Version 5.3.0, 208,637 downloads from npm last week. Every example below runs as written.
How a transaction goes through
- 1. Buildyou
A plain object: type, account, amount.
- 2. Autofillxrpl.js
The library adds the fee, the next sequence number and a last ledger to land in.
- 3. Signxrpl.js
On your machine. The seed never leaves it.
- 4. Submitserver
Sent to a server, which shares it with the network.
- 5. Validatedledger
In a ledger 3–5 seconds later, with a final result code.
client.submitAndWait(tx, { wallet }), when you do not need them apart.Install and connect
npm install xrplimport { Client } from 'xrpl'
const client = new Client('wss://xrplcluster.com')
await client.connect()
// ... the examples below go here ...
await client.disconnect()Public servers: wss://xrplcluster.com, wss://s1.ripple.com and wss://s2.ripple.com. For testing use Testnet, wss://s.altnet.rippletest.net:51233, where XRP is free: xrpl.to’s Testnet sandbox funds an account and shows every transaction type.
Read balances and an order book
// XRP and every token an account holds (this is the ledger's very first account)
const balances = await client.getBalances('rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh')
console.log(balances) // [{ currency: 'XRP', value }, { currency, issuer, value }, ...]
// The two sides of the RLUSD/XRP market
const XRP = { currency: 'XRP' }
const RLUSD = { currency: '524C555344000000000000000000000000000000', issuer: 'rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De' }
const asks = await client.request({ command: 'book_offers', taker_gets: RLUSD, taker_pays: XRP, limit: 10 })
const bids = await client.request({ command: 'book_offers', taker_gets: XRP, taker_pays: RLUSD, limit: 10 })
console.log(asks.result.offers.length, 'offers selling RLUSD,', bids.result.offers.length, 'buying it')- The XRP balance includes the reserve, 1 XRP for the account plus 0.2 XRP for each trust line, offer or other object it owns, which it cannot spend (xrpl.org: reserves).
getBalancesreads every trust line, so on an issuer with thousands of holders it makes thousands of requests and public servers refuse it. Askaccount_infofor the XRP alone.- The library’s
getOrderbookreturnsbuyandsell, but it sorts offers by a flag their creator chose, not by side of the market (source). For bids and asks, ask book_offers once in each direction, as above. - A currency code longer than three characters is written as 40 hex characters: RLUSD is
524C5553440….
Send XRP
import { Wallet, xrpToDrops } from 'xrpl'
const wallet = Wallet.fromSeed(process.env.XRPL_SEED) // never in the source file
const tx = await client.autofill({
TransactionType: 'Payment',
Account: wallet.address,
Destination: 'rDestination...',
Amount: xrpToDrops('10'), // amounts in drops: 1 XRP = 1,000,000
// DestinationTag: 12345, // when an exchange gives you one
})
const signed = wallet.sign(tx)
const res = await client.submitAndWait(signed.tx_blob)
console.log(res.result.meta.TransactionResult) // 'tesSUCCESS'submitAndWait resolves once the transaction is in a validated ledger, so the result it prints is final. To tell your app’s transactions apart on the ledger, add a SourceTag of your own choosing.
Open a trust line, with NoRipple
import { TrustSetFlags } from 'xrpl'
const res = await client.submitAndWait({
TransactionType: 'TrustSet',
Account: wallet.address,
LimitAmount: {
currency: '524C555344000000000000000000000000000000', // RLUSD
issuer: 'rMxCKbEDwqr76QuheSUMdEGf4B9xJ8m5De',
value: '1000000', // the most you will hold
},
Flags: TrustSetFlags.tfSetNoRipple,
}, { wallet })Always set NoRipple. Without it, someone holding two tokens with the same name from different issuers can route a payment through your account and swap your valuable one for their worthless one, with no signature from you. The line holds 0.2 XRP of your balance until you remove it.
Follow new ledgers
await client.request({ command: 'subscribe', streams: ['ledger'] })
client.on('ledgerClosed', (ledger) => {
console.log(ledger.ledger_index, ledger.txn_count, 'transactions')
})A new ledger closes every 3–5 seconds. Subscribe to accounts instead to get only the transactions that touch your addresses.
Reading the result
A result starting with tec is final and costs the fee; one starting with tef or tem never reached a ledger. The ones you will meet first:
| Code | The server says | What it means |
|---|---|---|
| tesSUCCESS | — | It worked. |
| tecUNFUNDED_PAYMENT | Insufficient XRP balance to send. | More than you can spend: the reserve stays locked. |
| tecNO_DST_INSUF_XRP | Destination does not exist. Too little XRP sent to create it. | A new account needs at least 1 XRP to exist. |
| tecDST_TAG_NEEDED | A destination tag is required. | Exchanges need the tag they gave you. |
| tecPATH_DRY | Path could not send partial amount. | No liquidity between the two currencies. |
| tecPATH_PARTIAL | Path could not send full amount. | Not enough liquidity at your price limit. |
| tecINSUF_RESERVE_LINE | Insufficient reserve to add trust line. | Each trust line holds 0.2 XRP; you are short. |
| tefPAST_SEQ | This sequence number has already passed. | Sent twice, or another program used the same account. |
Prices, history and holders
The ledger holds current state; prices over time, candles, holder lists and 24-hour volumes come from an index. xrpl.to’s API serves them over plain HTTPS, so an app can read them with fetch and keep xrpl.js for signing. Full reference: js.xrpl.org; source: XRPLF/xrpl.js.
Questions
- What is xrpl.js?
- The JavaScript and TypeScript library for the XRP Ledger, published on npm as "xrpl" and maintained in the XRPLF/xrpl.js repository. It connects to XRP Ledger servers, signs transactions with your keys and submits them. The current version is 5.3.0.
- Does xrpl.js work in the browser?
- Yes. It runs in Node.js and in browsers; in Node it needs version 20.19 or newer.
- Do I need to tell xrpl.js which key algorithm my seed uses?
- Not in version 5: Wallet.fromSeed reads it from the seed itself (sEd… is ed25519, other s… seeds are secp256k1). Versions 3 and 4 assumed ed25519 unless told otherwise, so a secp256k1 seed gave the wrong address there; pass { algorithm: 'ecdsa-secp256k1' } if you are on one of them.