Skip to content

XRP Ledger (XRPL) Integration

Live

The XRP Ledger is a public blockchain with built-in payments, issued tokens and NFTs. It is one of the two live chains in Smart Engines; Hedera is the other. It runs on the Smart Engines testnet, whose validators HSuite operates, against XRPL testnet.

XRPL is the canonical chain for value. The HSUITE token, the SEMV membership NFTs and the SAS subscription NFTs are issued here and nowhere else. A smart app can also create its own XRPL accounts and tokens as entities, funded by a payer wallet.

flowchart TD
    XRPL["XRPL (canonical)"] --> HST["HSUITE token<br/>(trust line)"]
    XRPL --> NFT["Membership / subscription<br/>NFTs (XLS-20)"]
    XRPL --> COMP["Native compliance"]
    COMP --> GF["GlobalFreeze<br/>(pausable)"]
    COMP --> TF["Trust-line freeze<br/>(restrictable)"]
    COMP --> AUTH["RequireAuth<br/>(compliant)"]
    COMP --> CB["Clawback<br/>(wipeable)"]
    XRPL --> SL["SignerList multisig<br/>(zero single-sig)"]

Trust Line Tokens

Fungible tokens issued over trust lines, one issuer account per token

Native NFTs

XLS-20 NFTs for membership and subscriptions

Issuer compliance

GlobalFreeze, trust-line freeze, RequireAuth and clawback

SignerList multisig

Every entity account is controlled by a SignerList of validator keys, with its master key disabled

Rule anchor

Each account’s rule reference is written on chain in its first SignerListSet

Live reserves

Funding is sized from the ledger’s current reserve, not a fixed amount


XRPL uses a trust line model for issued currencies. A token is identified as CURRENCY.ISSUER, for example HST.rIssuer....

Capabilities map to XRPL issuer flags and transactions:

Capability XRPL Feature Description
pausable GlobalFreeze Freeze all transfers of the issuer’s tokens
restrictable Trust-line freeze Freeze one holder’s trust line
compliant RequireAuth Holders need a trust line the issuer authorized
wipeable AllowTrustLineClawback The issuer can claw tokens back
mintable Inherent The issuer mints by sending a payment
burnable Inherent Tokens sent back to the issuer are destroyed
transferable Always on Token transfers are always enabled

XRPL issuer flags apply to the whole account, not to one currency. So each token gets its own issuer account, and compliant and wipeable can only be set when the token is created (see below).

An XRPL token is a token entity. Its issuer is the entity’s own account, derived from its DKG key and funded by the payer. The create runs in two halves, prepare and execute. createToken drives both:

import { xrplWalletFundWith } from '@hsuite/smart-engines-sdk';
// `baas` is a connected BaasClient (see the SDK guide); `wallet` is an xrpl.js Wallet.
const token = await baas.entities.createToken({
chain: 'xrpl',
name: 'My Token',
symbol: 'MTK',
currency: 'MTK', // the one currency code this issuer issues
ruleRef,
compliant: true, // optional: asfRequireAuth
wipeable: true, // optional: asfAllowTrustLineClawback
fundWith: xrplWalletFundWith(wallet),
});

currency is a 3-character code (not XRP) or 40 upper-case hex characters. It is required for any token whose rule can mint, and the cluster refuses a mint of any other code. If you send a creationIntent (from signEntityCreateIntent), it must sign the same currency and the same flags.

Finalize order. After the funding payment lands, the execute half finalizes the issuer in this order:

  1. It checks that the payer’s funding Payment has landed.
  2. One AccountSet sets exactly the requested flags: SetFlag: 2 (compliant), SetFlag: 16 (wipeable), or SetFlag: 16 plus Flags: tfRequireAuth (both). There is no such step when no flag was requested.
  3. SignerListSet installs the validator multisig.
  4. AccountSet with asfDisableMaster turns off the master key.

The entity’s own DKG key, threshold-signed by the validators, signs each step as the account’s master key. The payer funds everything, and the flags add no owner reserve. The platform never sets NoFreeze: XRPL does not allow NoFreeze and clawback on the same account, and NoFreeze can never be cleared.

Why the flags must be requested at create. XRPL refuses RequireAuth and AllowTrustLineClawback once the account owns any ledger object (tecOWNERS). A SignerList is one, and so is a trust line. So the flags can only be set before the SignerListSet, and a token that did not request them at create cannot get them later.

What validators will co-sign. Validators release signature shares for the flag AccountSet only while the account has no SignerList and its master key is still enabled. It must be on the entity’s own account, and the flags must equal exactly what the create request recorded. Anything else is denied.

A holder needs a trust line to the issuer before it can receive the token. A holder that is an account entity opens it through the SDK:

const prepared = await baas.entities.trustline(accountEntityId, {
chain: 'xrpl',
currency: 'MTK',
issuerAddress: 'rIssuer...',
limit: '1000000', // optional
});
// sign + submit prepared.transactionBytes

Any other holder submits a TrustSet from their own wallet.

The entity-scoped methods return prepared bytes for you to sign and submit. The entity is the source, issuer or authority and the fee payer, resolved server-side, and the validators co-sign under its SignerList only after its rule allows the operation.

Method What it prepares on XRPL
transfer / prepareWithdraw A Payment of XRP or an issued token from the entity
compliance with action: 'pause' AccountSet GlobalFreeze on the issuer
compliance with action: 'restrict' A trust-line freeze on account
compliance with action: 'wipe' A Clawback of amount from account (needs wipeable)
trustline A TrustSet from the entity
const prepared = await baas.entities.compliance(tokenEntityId, {
action: 'restrict',
chain: 'xrpl',
tokenId: 'MTK.rIssuer...',
account: 'rHolder...',
});

A rule denial comes back as HTTP 403 rule_rejected; narrow it with isRuleRejected.


XRPL has native NFTs (the XLS-20 standard), minted with NFTokenMint. No trust line is needed to hold one.

  • Membership (SEMV) and subscription (SAS) NFTs are minted by their own issuer accounts and delivered to the recipient through an NFT offer.
  • An app’s subscription NFTs come from the app’s own subscription issuer, an XRPL token entity whose rule is built with the subscriptionNft() rule template.
  • Subscriptions are not resellable. Subscription NFTs are minted with tfBurnable only, without tfTransferable, so the ledger refuses a holder-to-holder transfer.
Flag Value Name Description
1 tfBurnable The issuer can burn the NFT
2 tfOnlyXRP Offers for the NFT must be in XRP
8 tfTransferable The NFT can be transferred

An XRPL account entity is created payer-funded, like a token:

const account = await baas.entities.createAccount({
chain: 'xrpl',
ruleRef,
fundWith: xrplWalletFundWith(wallet),
});

The account’s address is derived from the entity’s DKG key. After the payer funds it, the cluster installs a SignerListSet and disables the master key.

The SignerList lists one XRPL key per validator, each with weight 1, and its quorum is the threshold. In partial security mode the app owner’s key is added with weight 2, and the quorum becomes 2 plus the threshold, so the owner and a threshold of validators must both sign.

The funding amount is the reserve the account needs, read from the ledger at the time of the create. See Reserve Management.


Every XRPL entity account is bound to its rule on chain. Its first SignerListSet carries a memo with MemoType rule and MemoData rule:<topicId>@<consensusTimestamp>:<entityType> (hex-encoded), pointing at the rule’s message on the Hedera rules topic. That transaction is the first one the account signs, and it never changes. Unlike the account’s Domain field, the signers cannot rewrite it later.

Before releasing a signature, the validators resolve the rule from HCS and check it against the entity’s record. A mismatch is denied.


Every prepared XRPL transaction carries a LastLedgerSequence. After that ledger closes, the transaction can no longer be included.

  • Transactions you sign in your wallet after a prepare call get the current ledger plus 30 ledgers, about 100 seconds. If you sign later than that, prepare again.
  • Transactions the validators threshold-sign get the current ledger plus 200 ledgers, about 13 minutes, to cover signature collection across the cluster.

A transaction past its window fails with tefMAX_LEDGER. When it is multi-signed, rippled can report the same expiry as tefBAD_SIGNATURE.

When a token’s rule declares a maxSupply, the validators read the live outstanding supply before authorizing a mint. They deny (fail closed) any mint that would breach the cap. This works for both issued-currency tokens (CURRENCY.ISSUER) and Multi-Purpose Tokens (MPT.<issuanceId>).

Some over-mints are invisible before execution, for example a payment routed past the cap through a third currency. For the HSUITE token, the validators also re-check supply after each confirmed transaction. If the issuer is over its cap, they claw back only the excess, from the holder that gained it in that transaction, and alert an operator.


Error Code Cause Solution
tecNO_LINE No trust line exists Set up a trust line first
tecUNFUNDED_PAYMENT Insufficient XRP balance Fund the account (1 XRP base + 0.2 XRP per owned object)
tecNO_AUTH Trust line not authorized Authorize the trust line first
tecFROZEN The account or trust line is frozen Unfreeze before transacting
tecINSUFFICIENT_RESERVE The account would go below its reserve Keep enough XRP for the reserve
tecNO_PERMISSION Clawback is not enabled on the issuer Request wipeable at token create; it cannot be added later
tefMAX_LEDGER The validity window passed Prepare the transaction again

Pre-flight destination validation. A payment or transfer prepare whose destination is not a valid classic address is rejected with HTTP 400 before anything is built or signed. A bad base58 checksum, or a 0, which is not in the base58 alphabet, makes an address invalid.


Request wipeable and compliant at token create

XRPL only accepts these flags before the issuer owns any ledger object (its SignerList counts), so they cannot be added later

Use RequireAuth for regulated tokens

Control who can hold the token

Keep your seed yourself

xrplWalletFundWith signs with your wallet; the SDK never holds the seed

Bind every token to a rule

The rule decides what the validators will co-sign

  1. Account reserve: 1 XRP base reserve.
  2. Owner reserve: 0.2 XRP per owned object (trust lines, offers, escrows). A SignerList counts as one owner object, however many signers it holds (the MultiSignReserve amendment). So a multisig account with only its SignerList reserves 1 + 0.2 = 1.2 XRP, and one that also holds a token trust line reserves 1 + 0.4 = 1.4 XRP.
  3. Read live, not hard-coded: account funding is the reserve read from the ledger (server_info) plus 0.1 XRP for fees, so a fee vote that changes the reserve is picked up without a code change. At today’s values that is 1.3 XRP for a SignerList-only account and 1.5 XRP with one trust line.
  4. Monitor reserves: keep each account above its reserve.

Currency codes

3-character codes (not XRP) or 40 hex characters

Precision

Issued currencies carry 15 significant digits

Declare a supply cap

Set maxSupply in the token’s rule so over-cap mints are denied


Aspect Hedera (HTS) XRPL (Trust Lines)
Token Creation A TokenCreate transaction An issuer account; the token exists once issued
Token ID Numeric id (e.g. 0.0.12345) Currency code + issuer (e.g. MTK.rIssuer...)
Holding The account associates the token The holder opens a trust line
Precision decimals, set at create 15 significant digits
Multisig KeyList with a threshold SignerList with a quorum, master key disabled
NFTs in Smart Engines None XLS-20 (membership and subscriptions)

A fungible token is a token entity with its own issuer account (see Creating Tokens). It is identified as CURRENCY.ISSUER. Holders open a trust line to the issuer, and the issuer, through its SignerList, sends them tokens.

NFTs are minted with NFTokenMint and identified by a 64-character hex id. They need no trust line, and the ledger tracks their owner directly. See NFT Support.