XRP Ledger
Introduction
Section titled “Introduction”The XRP Ledger (XRPL) is a non-EVM family that’s payment-native: ~3–5s single-step finality, no
reorgs, with a built-in Memo and DestinationTag the protocol uses for proof binding. Name it —
chain: 'xrpl' — and the driver auto-mounts on first use, so a pure-EVM or Solana install
never downloads the XRPL library. The protocol layer is unchanged; only the wallet shape and the
receive prerequisite differ.
import { requirePayment } from '@piprail/sdk'
requirePayment({ chain: 'xrpl', token: 'USDC', amount: '0.10', payTo: 'r…' })Install the peer dependency
Section titled “Install the peer dependency”The XRPL library is an optional peer dep — install it once and the lazy import finds it:
npm install xrplThe wallet
Section titled “The wallet”An XRPL wallet is { seed } (an s… family secret seed) or a ready { wallet } (an xrpl.js
Wallet, if you built it yourself). payTo is a classic r… address.
import { PipRailClient } from '@piprail/sdk'
const client = new PipRailClient({ chain: 'xrpl', wallet: { seed: process.env.XRPL_SEED }, // 's…' secret seed})The shape is checked synchronously at bind time, so passing an EVM, Solana, TON, or Stellar
wallet fails fast with a WrongFamilyError. See Wallets by
family for the full set of shapes, and
PipRailClient for the client surface.
Tokens
Section titled “Tokens”Name the symbol; the SDK fills in the issuer and currency code. Both built-in issuers were
verified live on mainnet before shipping (the on-ledger Domain and currency code read off
gateway_balances).
| Token | Built in | Notes |
|---|---|---|
'USDC' | Yes | Circle USDC. Issuer Domain https://circle.com verified on mainnet. |
'RLUSD' | Yes | Ripple’s RLUSD. Issuer Domain https://ripple.com/ verified on mainnet. |
'native' | Yes | XRP, 6 decimals (drops — 1 drop = 1e-6 XRP). |
| custom IOU | — | Any other issued currency via { issuer, currencyHex, decimals }. |
import { requirePayment } from '@piprail/sdk'
// A custom issued currency is { issuer, currencyHex, decimals }:requirePayment({ chain: 'xrpl', token: { issuer: 'r…', currencyHex: '5553444300000000000000000000000000000000', decimals: 6 }, amount: '0.10', payTo: 'r…',})Receive prerequisite — activation + a trustline
Section titled “Receive prerequisite — activation + a trustline”To receive an issued asset (USDC/RLUSD), the merchant (payTo) must (1) be an activated
account — holding the ~1 XRP base reserve to exist — and (2) hold a trustline (TrustSet) to
that issuer’s currency before its first IOU payment. Native XRP needs neither. The payer must
be activated and trustlined too.
When the recipient isn’t ready, PipRail raises RecipientNotReadyError
— a chain requirement, not an SDK bug — echoing the raw XRPL engine code and the fix:
| Raw XRPL code | What it means | Fix |
|---|---|---|
tecNO_DST_INSUF_XRP / tecNO_DST | payTo isn’t activated (needs ≥1 XRP base reserve to exist) | send the recipient ≥1 XRP to activate it |
tecNO_LINE / tecPATH_DRY | recipient has no trustline for the IOU | add the trustline on the recipient |
tecDST_TAG_NEEDED | recipient requires a DestinationTag (PipRail sets one automatically) | — |
Reserves are locked, not spent (~1 XRP base + an owner reserve per trustline) — recoverable.
Check readiness before you spend with planPayment(): it
surfaces a not-ready recipient as a RECIPIENT_NOT_READY blocker rather than throwing, so an
agent branches on data instead of broadcasting into a stranded payment:
const url = 'https://api.example.com/report'const plan = await client.planPayment(url)// → PaymentPlan | null (null when the URL isn't 402-gated)
if (!plan) { await client.fetch(url) // not payment-gated — just fetch it} else if (plan.payable) { await client.fetch(url) // safe — we checked} else { console.log(plan.fundingHint) // e.g. "recipient has no trustline for USDC"}See planPayment() for the full readiness model and the
RECIPIENT_NOT_READY blocker.
Proof binding — Template A (memo-bound)
Section titled “Proof binding — Template A (memo-bound)”XRPL uses Template A: the challenge nonce rides in a Memo — the
cryptographic binding — plus a derived DestinationTag for deliverability (some accounts, like
the RLUSD issuer, set requireDestinationTag). verify() re-derives every checked field from
the trusted accept, never the client-supplied ref, so a forged echo can’t redirect it.
Server-side only
Section titled “Server-side only”Endpoints
Section titled “Endpoints”The default RPC is a public community cluster (https://xrplcluster.com/) — rate-limited and not
for sustained business use. Pass your own rpcUrl in production:
import { requirePayment, PipRailClient } from '@piprail/sdk'
requirePayment({ chain: 'xrpl', token: 'USDC', amount: '0.10', payTo: 'r…', rpcUrl: 'https://your-xrpl-node/' })
const client = new PipRailClient({ chain: 'xrpl', wallet: { seed: process.env.XRPL_SEED }, rpcUrl: 'https://your-xrpl-node/',})