Lending Protocol and Single Asset Vaults are disabled on Devnet. You have two options for testing these features:
- Run
rippledin stand-alone mode and enableLendingProtocolandSingleAssetVaultin the rippled.cfg file. - Connect to the Lending Protocol-specific Devnet at
https://lend.devnet.rippletest.net:51234/.
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 Single Asset Vault amendment )
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. 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 client library and instantiate a client to connect to the XRPL.
import xrpl from "xrpl"
// Connect to the network ----------------------
// This is a lending protocol-specific devnet. This network may be taken
// offline once the lending protocol is live on mainnet.
const client = new xrpl.Client("wss://lend.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.
const depositor = xrpl.Wallet.fromSeed("sEdVSq9Zsv8vQwfivTk37bWxrvpnruf")
// The ID of the vault to deposit into
const vaultID = "6AC4EC2D775C6275D314996D6ECDD16DCB9382A29FDB769951C42192FCED76EF"
// The ID of the vault's asset (MPT Issuance)
const assetMPTIssuanceId = "0003E3B486D3DACD8BB468AB33793B9626BD894A92AB3AB4"
// The ID of the vault's share (MPT Issuance)
const shareMPTIssuanceId = "0000000152E7CD364F869E832EDB806C4A7AD8B3D0C151C5"
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 tutorial deposits to an existing private vault. A preconfigured depositor account is used, which has:
- Valid Credentials in the vault's Permissioned Domain.
- A positive balance of the MPT in the vault.
If you wish to deposit in a different vault, you can replace the relevant values with your own.
Use the vault_info API 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: