Vault Guide (XLS-65d)

This guide explains how to use XRPL Single Asset Vaults with the XrplCSharp SDK. Vaults enable pooling assets (XRP, IOU tokens, or MPTs) and issuing shares to depositors proportional to their contribution.

Note: Vaults require the Single Asset Vault amendment (XLS-65d). This feature is in draft and subject to change.

Table of Contents

  • Overview
  • Key Concepts
  • Transaction Types
  • Step-by-Step: Creating and Managing a Vault
  • Vault Data Format
  • Ledger Object: Vault
  • Common Errors
  • Best Practices

Overview

A Vault is a ledger structure that holds a single type of asset and issues shares (as MPT tokens) to depositors. When a user deposits assets into a vault, they receive shares proportional to their contribution. Shares can later be redeemed for the underlying assets.

Depositor A                        Vault (pseudo-account)
┌──────────────────┐              ┌──────────────────────────┐
│  Deposit 100 XRP │ ──────────►  │  Asset: XRP              │
│  Receive shares  │ ◄──────────  │  AssetsTotal: 300 XRP    │
└──────────────────┘              │  Shares (MPT): issued    │
                                  │  Owner: rBroker...       │
Depositor B                       │  ShareMPTID: 000ABC...   │
┌──────────────────┐              └──────────────────────────┘
│  Deposit 200 XRP │ ──────────►
│  Receive shares  │ ◄──────────
└──────────────────┘

Each vault creates:

  • A pseudo-account that holds the pooled assets
  • An MPTokenIssuance for vault shares

Key Concepts

Vault Shares (MPT)

When a depositor adds assets, the vault issues shares as MPT tokens. The number of shares depends on the vault's Scale and the current exchange rate.

Share calculation: Assets are multiplied by 10^Scale to convert fractional amounts into whole-number shares. For example, with Scale = 6, depositing 20.3 units creates 20,300,000 shares.

  • XRP and MPT vaults: Fixed Scale of 0 (1 asset unit = 1 share)
  • Trust line token vaults: Scale 0–18 (default 6)

Withdrawal Policy

Defines how withdrawals are handled:

  • 0x0001 (vaultStrategyFirstComeFirstServe) — depositors can redeem any amount of assets provided they hold sufficient shares

Vault Flags

Set only at creation time via VaultCreate:

  • tfVaultPrivate (0x00010000) — restricts access to credentialed accounts within a specified Permissioned Domain
  • tfVaultShareNonTransferable (0x00020000) — makes vault shares non-transferable between accounts

Unrealized Loss

The LossUnrealized field tracks potential losses in vault assets (e.g., from clawbacks). When unrealized losses exist, each share's redeemable value decreases proportionally.


Transaction Types

Transaction Purpose Who Submits
VaultCreate Create a new vault Vault Owner
VaultDeposit Deposit assets into a vault Any account
VaultWithdraw Redeem shares for assets Any share holder
VaultSet Update vault metadata/settings Vault Owner
VaultDelete Delete an empty vault Vault Owner
VaultClawback Claw back assets from a holder Asset Issuer

Step-by-Step: Creating and Managing a Vault

1. Create a Vault

using Xrpl.Models.Transactions;
using Xrpl.Models.Common;
using Xrpl.Sugar;
using static Xrpl.Models.Common.Common;

// Create an XRP vault
VaultCreate vaultTx = new VaultCreate
{
    Account = wallet.ClassicAddress,
    Asset = new IssuedCurrency { Currency = "XRP" },
};
vaultTx = await client.Autofill(vaultTx);
TransactionSummary result = await client.SubmitAndWait(vaultTx, wallet, true);

// Extract VaultID from metadata
string vaultId = GetCreatedObjectId(result, LedgerEntryType.Vault);

2. Create a Vault with Optional Fields

VaultCreate vaultTx = new VaultCreate
{
    Account = wallet.ClassicAddress,
    Asset = new IssuedCurrency { Currency = "XRP" },
    AssetsMaximum = "1000000000",           // max 1000 XRP (in drops)
    MPTokenMetadata = "48656C6C6F",         // hex-encoded metadata for shares
    Data = "7B226E223A225465737420566175"
         + "6C74222C2277223A226578616D70"
         + "6C652E636F6D227D",              // hex-encoded JSON metadata
    WithdrawalPolicy = 1,                   // FirstComeFirstServe
    Scale = 6,                              // decimal precision (trust line tokens only)
    Flags = (uint)VaultCreateFlags.tfVaultShareNonTransferable,
};
vaultTx = await client.Autofill(vaultTx);
TransactionSummary result = await client.SubmitAndWait(vaultTx, wallet, true);

3. Deposit Assets

Any account can deposit assets into a vault:

VaultDeposit depositTx = new VaultDeposit
{
    Account = depositor.ClassicAddress,
    VaultID = vaultId,
    Amount = new Currency { Value = "50000000", CurrencyCode = "XRP" }, // 50 XRP
};
depositTx = await client.Autofill(depositTx);
await client.SubmitAndWait(depositTx, depositor, true);

4. Withdraw Assets

Any share holder can redeem shares for assets. Two approaches:

Asset-based withdrawal — specify desired asset quantity:

VaultWithdraw withdrawTx = new VaultWithdraw
{
    Account = depositor.ClassicAddress,
    VaultID = vaultId,
    Amount = new Currency { Value = "25000000", CurrencyCode = "XRP" }, // 25 XRP
};
withdrawTx = await client.Autofill(withdrawTx);
await client.SubmitAndWait(withdrawTx, depositor, true);

Withdraw to a different account:

VaultWithdraw withdrawTx = new VaultWithdraw
{
    Account = depositor.ClassicAddress,
    VaultID = vaultId,
    Amount = new Currency { Value = "25000000", CurrencyCode = "XRP" },
    Destination = "rRecipient...",
    DestinationTag = 42,
};

Note: Transfer fees never apply to VaultWithdraw transactions.

5. Update Vault Settings

The vault owner can update mutable fields:

VaultSet setTx = new VaultSet
{
    Account = wallet.ClassicAddress,
    VaultID = vaultId,
    AssetsMaximum = "2000000000",   // increase cap to 2000 XRP
    Data = "7B226E223A2255706461746564227D",  // new metadata
};
setTx = await client.Autofill(setTx);
await client.SubmitAndWait(setTx, wallet, true);

Restriction: VaultSet can only modify Data, AssetsMaximum, and DomainID. The vault's public/private status is permanent.

6. Delete an Empty Vault

The vault must have zero assets and no outstanding shares:

VaultDelete deleteTx = new VaultDelete
{
    Account = wallet.ClassicAddress,
    VaultID = vaultId,
};
deleteTx = await client.Autofill(deleteTx);
await client.SubmitAndWait(deleteTx, wallet, true);

7. Clawback Assets (Issuer Only)

The asset issuer can claw back assets from a vault share holder. Clawbacks cannot be performed on native XRP.

VaultClawback clawbackTx = new VaultClawback
{
    Account = issuer.ClassicAddress,
    VaultID = vaultId,
    Holder = "rShareHolder...",
    Amount = new Currency
    {
        Value = "100",
        CurrencyCode = "USD",
        Issuer = issuer.ClassicAddress,
    },
};
clawbackTx = await client.Autofill(clawbackTx);
await client.SubmitAndWait(clawbackTx, issuer, true);

When Amount is zero, the issuer claws back all funds up to the total shares the Holder owns.


Vault Data Format

The Data field stores arbitrary metadata as a hex-encoded string (max 256 bytes). The SDK provides a helper class VaultDataFormat for a recommended JSON structure:

using Xrpl.Models.Ledger;

// Create structured data
VaultDataFormat data = new VaultDataFormat
{
    Name = "My Investment Vault",
    Website = "https://example.com",
};

// Convert to hex for VaultCreate/VaultSet
string hex = VaultDataFormat.ToHex(data);

// Parse hex from a Vault ledger entry
LOVault vault = ...; // fetched via ledger_entry
VaultDataFormat parsed = vault.DataParsed;  // [JsonIgnore] helper property
string rawUtf8 = vault.DataRaw;            // [JsonIgnore] raw UTF-8 string

The VaultDataFormat JSON structure: {"n":"name","w":"website"} — whitespace-removed and hex-encoded.


Ledger Object: Vault

Fetch a Vault ledger entry via ledger_entry:

using Xrpl.Models.Methods;
using Xrpl.Models.Ledger;

LedgerEntryRequest request = new LedgerEntryRequest { Index = vaultId };
LedgerEntryResponse response = await client.LedgerEntry(request);

LOVault vault = (LOVault)response.Node;

Console.WriteLine($"Owner: {vault.Owner}");
Console.WriteLine($"Asset: {vault.Asset}");
Console.WriteLine($"AssetsTotal: {vault.AssetsTotal}");
Console.WriteLine($"AssetsAvailable: {vault.AssetsAvailable}");
Console.WriteLine($"ShareMPTID: {vault.ShareMPTID}");
Console.WriteLine($"Scale: {vault.Scale}");
Console.WriteLine($"Metadata: {vault.DataParsed?.Name}");

Key LOVault Fields

Field Type Description
Account string Vault's pseudo-account address
Owner string Vault owner's account address
Asset IssuedCurrency The asset held by the vault
AssetsTotal string Total vault value (Number type)
AssetsAvailable string Currently accessible assets (Number type)
AssetsMaximum string Holding capacity, 0 = unlimited (Number type)
LossUnrealized string Unrealized loss amount (Number type)
ShareMPTID string MPTokenIssuance ID for vault shares
WithdrawalPolicy uint? Withdrawal strategy
Scale uint? Decimal precision for share calculations
Data string Hex-encoded metadata (max 256 bytes)
Sequence uint? Creation transaction sequence

Vault Flags (LOVault)

[Flags]
public enum VaultLedgerFlags : uint
{
    lsfVaultPrivate = 0x00010000,
}

Common Errors

Error Code Cause
tecHAS_OBLIGATIONS Cannot delete vault with outstanding shares or assets
tecNO_PERMISSION Non-owner attempting VaultSet/VaultDelete, or non-issuer attempting VaultClawback
tecUNFUNDED Insufficient balance for VaultDeposit
tecINSUFFICIENT_FUNDS Insufficient shares for VaultWithdraw
tecFROZEN Trust line between vault pseudo-account and issuer is frozen
temMALFORMED Data exceeds 256 bytes, or invalid field values
tecOBJECT_NOT_FOUND VaultID does not exist

Best Practices

  1. Always set AssetsMaximum — limits exposure and prevents unbounded deposits
  2. Use VaultDataFormat — provides a standard metadata structure readable by other applications
  3. Check LossUnrealized — before withdrawing, verify the vault has no unrealized losses that would reduce share value
  4. Private vaults for regulated assets — use tfVaultPrivate with a Permissioned Domain for KYC-gated vaults
  5. Non-transferable shares — use tfVaultShareNonTransferable when shares should not be traded on secondary markets
  6. Scale for trust line tokens — choose Scale carefully at creation (it cannot be changed later). Higher Scale = finer granularity but larger share numbers
  7. Clawback considerations — only the asset issuer can clawback, and only for non-XRP assets. Clawbacks create unrealized losses for other depositors
  • Edit this page
In this article
Back to top Generated by DocFX