This tutorial shows you how to deposit assets into a single asset vault. The example demonstrates depositing into a private vault with credential-based access control, however you can easily use the same code to deposit into a public vault.
When you deposit into a vault, you receive shares that represent your proportional ownership of the vault's assets. For example, in an institutional lending context, depositing into a vault allows you to pool your assets with other depositors to participate in larger lending markets.
Anyone can create a public vault, and malicious vault owners can drain your assets. Always verify that the vault owner and vault settings meet your standards before depositing assets.
Requires the SingleAssetVault amendment. Loading...
By the end of this tutorial, you will be able to:
- Deposit assets into a private/public vault.
- Check the depositing account's share balance and the vault's state after a successful deposit.
To complete this tutorial, you should:
- Have a basic understanding of the XRP Ledger.
- Have access to an existing vault. This tutorial uses a preconfigured vault. To create your own vault, see Create a Single Asset Vault.
- Have an XRP Ledger client library set up in your development environment. This page provides examples for the following:
- JavaScript with the xrpl.js library. See Get Started Using JavaScript for setup steps.
You can find the complete source code for this tutorial's examples in the code samples section of this website's repository.
From the code sample folder, use npm to install dependencies:
npm install xrplTo get started, import the necessary libraries and instantiate a client to connect to the XRPL. This example imports:
xrpl: Used for XRPL client connection and transaction handling.fsandchild_process: Used to run tutorial setup scripts.
import xrpl from "xrpl"
import { execSync } from "child_process"
import fs from "fs"
// Auto-run setup if needed
if (!fs.existsSync("vaultSetup.json")) {
console.log(`\n=== Vault setup data doesn't exist. Running setup script... ===\n`)
execSync("node vaultSetup.js", { stdio: "inherit" })
}
// Load setup data
const setupData = JSON.parse(fs.readFileSync("vaultSetup.json", "utf8"))
// Connect to the network
const client = new xrpl.Client("wss://s.devnet.rippletest.net:51233")
await client.connect()Provide the depositing account and specify the vault details. The depositor must have a balance of the vault's asset to deposit.
// You can replace these values with your own
const depositor = xrpl.Wallet.fromSeed(setupData.depositor.seed)
const vaultID = setupData.vaultID
const assetMPTIssuanceId = setupData.mptIssuanceId
const shareMPTIssuanceId = setupData.vaultShareMPTIssuanceId
console.log(`Depositor address: ${depositor.address}`)
console.log(`Vault ID: ${vaultID}`)
console.log(`Asset MPT issuance ID: ${assetMPTIssuanceId}`)
console.log(`Vault share MPT issuance ID: ${shareMPTIssuanceId}`)
const depositAmount = 1This example uses an existing vault, depositor account, and MPT from the vaultSetup.js script, but you can replace these values with your own. The preconfigured depositor account has:
- Valid Credentials in the vault's Permissioned Domain.
- A positive balance of the MPT in the vault.
Use the vault_info method to retrieve the vault's current state, including its total value and available liquidity.
// Get initial vault state ----------------------
console.log("\n=== Getting initial vault state... ===")
const initialVaultInfo = await client.request({
command: "vault_info",
vault_id: vaultID,
ledger_index: "validated"
})
console.log(` - Total vault value: ${initialVaultInfo.result.vault.AssetsTotal}`)
console.log(` - Available assets: ${initialVaultInfo.result.vault.AssetsAvailable}`)Before depositing, verify that the depositor has sufficient balance of the vault's asset. If the depositor doesn't have enough funds, the transaction will fail with a tecINSUFFICIENT_FUNDS error.
// Check depositor's asset balance ----------------------
console.log("\n=== Checking depositor's balance... ===")
try {
// Use ledger_entry to get specific MPT issuance balance
const ledgerEntryResult = await client.request({
command: "ledger_entry",
mptoken: {
mpt_issuance_id: assetMPTIssuanceId,
account: depositor.address
},
ledger_index: "validated"
})
const balance = ledgerEntryResult.result.node?.MPTAmount
console.log(`Balance: ${balance}`)
// Check if balance is sufficient
if (balance < depositAmount) {
console.error(`Error: Insufficient balance! Have ${balance}, need ${depositAmount}`)
await client.disconnect()
process.exit(1)
}
} catch (error) {
if (error.data?.error === 'entryNotFound') {
console.log(`Error: The depositor doesn't hold any assets with ID: ${assetMPTIssuanceId}`)
}
await client.disconnect()
process.exit(1)
}Create a VaultDeposit transaction object to deposit assets into the vault.
// Prepare VaultDeposit transaction ----------------------
console.log(`\n=== VaultDeposit transaction ===`)
const vaultDepositTx = {
TransactionType: "VaultDeposit",
Account: depositor.address,
VaultID: vaultID,
Amount: {
mpt_issuance_id: assetMPTIssuanceId,
value: depositAmount.toString()
}
}
// Validate the transaction structure before submitting
xrpl.validate(vaultDepositTx)
console.log(JSON.stringify(vaultDepositTx, null, 2))The transaction specifies the depositing account, the vault's unique identifier (VaultID), and the amount to deposit. The asset in the Amount field must match the vault's asset type, otherwise the transaction will fail with a tecWRONG_ASSET error.
Submit the VaultDeposit transaction to the XRP Ledger.
// Submit VaultDeposit transaction ----------------------
console.log("\n=== Submitting VaultDeposit transaction... ===")
const depositResult = await client.submitAndWait(vaultDepositTx, {
wallet: depositor,
autofill: true,
})
if (depositResult.result.meta.TransactionResult !== "tesSUCCESS") {
const result_code = depositResult.result.meta.TransactionResult
console.error("Error: Unable to deposit:", result_code)
await client.disconnect()
process.exit(1)
}
console.log("Deposit successful!")When depositing into a private vault, the transaction verifies that the depositor has valid credentials in the vault's permissioned domain. Without valid credentials, the VaultDeposit transaction fails with a tecNO_AUTH error.
If the transaction succeeds, the vault:
- Transfers the assets from the depositing account to the vault's pseudo-account.
- Issues vault shares to the depositor.
Transfer fees are not charged on VaultDeposit transactions.
After depositing, verify the vault's updated state. You can extract this information directly from the transaction metadata without making additional API calls:
// Extract vault state from transaction metadata ----------------------
console.log("\n=== Vault state after deposit ===")
const affectedNodes = depositResult.result.meta.AffectedNodes
const vaultNode = affectedNodes.find(
(node) => {
return (
node.ModifiedNode &&
node.ModifiedNode.LedgerEntryType === "Vault" &&
node.ModifiedNode.LedgerIndex === vaultID
)
}
)
if (vaultNode) {
const vaultFields = vaultNode.ModifiedNode.FinalFields
console.log(` - Total vault value: ${vaultFields.AssetsTotal}`)
console.log(` - Available assets: ${vaultFields.AssetsAvailable}`)
}Finally, check that the depositing account has received the shares.
// Get the depositor's share balance ----------------------
console.log("\n=== Depositor's share balance ==")
const depositorShareNode = affectedNodes.find((node) => {
const shareNode = node.ModifiedNode || node.CreatedNode
const fields = shareNode?.FinalFields || shareNode?.NewFields
return (
shareNode &&
shareNode.LedgerEntryType === "MPToken" &&
fields?.Account === depositor.address &&
fields?.MPTokenIssuanceID === shareMPTIssuanceId
)
})
if (depositorShareNode) {
const shareNode = depositorShareNode.ModifiedNode || depositorShareNode.CreatedNode
const shareFields = shareNode.FinalFields || shareNode.NewFields
console.log(`Shares held: ${shareFields.MPTAmount}`)
}
await client.disconnect()
The code checks for both ModifiedNode and CreatedNode because on the first deposit, a new MPToken entry is created for the depositor's shares (CreatedNode). On subsequent deposits, the depositor's existing share balance is updated (ModifiedNode).
Concepts:
Tutorials:
References: