# Deposit into a Vault This tutorial shows you how to deposit assets into a [single asset vault](/ja/docs/concepts/tokens/single-asset-vaults). 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. Warning 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. SingleAssetVault ## Goals 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. ## Prerequisites 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](/ja/docs/tutorials/defi/lending/use-single-asset-vaults/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](https://github.com/XRPLF/xrpl.js). See [Get Started Using JavaScript](/docs/tutorials/get-started/get-started-javascript) for setup steps. - **Python** with the [xrpl-py library](https://github.com/XRPLF/xrpl-py). See [Get Started Using Python](/docs/tutorials/get-started/get-started-python) for setup steps. ## Source Code You can find the complete source code for this tutorial's examples in the code samples section of this website's repository. ## Steps ### 1. Install dependencies JavaScript From the code sample folder, use `npm` to install dependencies: ```bash npm install xrpl ``` Python From the code sample folder, set up a virtual environment and use `pip` to install dependencies: ```bash python -m venv .venv source .venv/bin/activate pip install -r requirements.txt ``` ### 2. Set up client and accounts To get started, import the necessary libraries and instantiate a client to connect to the XRPL. This example imports: JavaScript - `xrpl`: Used for XRPL client connection and transaction handling. - `fs` and `child_process`: Used to run tutorial setup scripts. // IMPORTANT: This example deposits into an existing PRIVATE vault. // The depositor account used has valid credentials in the vault's Permissioned Domain. // Without valid credentials, the VaultDeposit transaction will fail. // If you want to deposit into a public vault, you can replace the vaultID and shareMPTIssuanceId // values with your own. 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() Python - `json`: Used for loading and formatting JSON data. - `os`, `subprocess`, `sys`: Used for file handling and running setup scripts. - `xrpl`: Used for XRPL client connection and transaction handling. # IMPORTANT: This example deposits into an existing PRIVATE vault. # The depositor account used has valid credentials in the vault's Permissioned Domain. # Without valid credentials, the VaultDeposit transaction will fail. # If you want to deposit into a public vault, you can replace the vault_id and share_mpt_issuance_id # values with your own. import json import os import subprocess import sys from xrpl.clients import JsonRpcClient from xrpl.models import VaultDeposit from xrpl.models.requests import VaultInfo, LedgerEntry from xrpl.transaction import submit_and_wait from xrpl.wallet import Wallet # Auto-run setup if needed if not os.path.exists("vault_setup.json"): print("\n=== Vault setup data doesn't exist. Running setup script... ===\n") subprocess.run([sys.executable, "vault_setup.py"], check=True) # Load setup data with open("vault_setup.json", "r") as f: setup_data = json.load(f) # Connect to the network client = JsonRpcClient("https://s.devnet.rippletest.net:51234") Provide the depositing account and specify the vault details. The depositor must have a balance of the vault's asset to deposit. JavaScript // 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 = 1 This example uses an existing vault, depositor account, and MPT from the `vaultSetup.js` script, but you can replace these values with your own. Python # You can replace these values with your own depositor = Wallet.from_seed(setup_data["depositor"]["seed"]) vault_id = setup_data["vault_id"] asset_mpt_issuance_id = setup_data["mpt_issuance_id"] share_mpt_issuance_id = setup_data["vault_share_mpt_issuance_id"] print(f"Depositor address: {depositor.address}") print(f"Vault ID: {vault_id}") print(f"Asset MPT issuance ID: {asset_mpt_issuance_id}") print(f"Vault share MPT issuance ID: {share_mpt_issuance_id}") deposit_amount = 1 This example uses an existing vault, depositor account, and MPT from the `vault_setup.py` script, but you can replace these values with your own. The preconfigured depositor account has: - Valid [Credentials](/ja/docs/concepts/decentralized-storage/credentials) in the vault's [Permissioned Domain](/ja/docs/concepts/tokens/decentralized-exchange/permissioned-domains). - A positive balance of the MPT in the vault. ### 3. Check initial vault state Use the [vault_info method](/docs/references/http-websocket-apis/public-api-methods/vault-methods/vault_info) to retrieve the vault's current state, including its total value and available liquidity. JavaScript // 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}`) Python # Get initial vault state print("\n=== Getting initial vault state... ===") initial_vault_info = client.request( VaultInfo( vault_id=vault_id, ledger_index="validated" ) ) print(f" - Total vault value: {initial_vault_info.result['vault']['AssetsTotal']}") print(f" - Available assets: {initial_vault_info.result['vault']['AssetsAvailable']}") ### 4. Check depositor's asset balance 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. JavaScript // 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.error(`Error: The depositor doesn't hold any assets with ID: ${assetMPTIssuanceId}`) } else { console.error(`Error checking MPT: ${error}`) } await client.disconnect() process.exit(1) } Python # Check depositor's asset balance print("\n=== Checking depositor's balance... ===") try: # Use ledger_entry to get specific MPT issuance balance ledger_entry_result = client.request( LedgerEntry( mptoken={ "mpt_issuance_id": asset_mpt_issuance_id, "account": depositor.address }, ledger_index="validated" ) ) balance = ledger_entry_result.result["node"]["MPTAmount"] print(f"Balance: {balance}") # Check if balance is sufficient if int(balance) < deposit_amount: print(f"Error: Insufficient balance! Have {balance}, need {deposit_amount}", file=sys.stderr) sys.exit(1) except Exception as error: error_data = getattr(error, 'data', {}) if 'error' in error_data and error_data['error'] == 'entryNotFound': print(f"Error: The depositor doesn't hold any assets with ID: {asset_mpt_issuance_id}", file=sys.stderr) else: print(f"Error checking MPT: {error}", file=sys.stderr) sys.exit(1) ### 5. Prepare VaultDeposit transaction Create a [VaultDeposit transaction](/docs/references/protocol/transactions/types/vaultdeposit) object to deposit assets into the vault. JavaScript // 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. Python # Prepare VaultDeposit transaction print("\n=== VaultDeposit transaction ===") vault_deposit_tx = VaultDeposit( account=depositor.address, vault_id=vault_id, amount={ "mpt_issuance_id": asset_mpt_issuance_id, "value": str(deposit_amount) } ) print(json.dumps(vault_deposit_tx.to_xrpl(), indent=2)) The transaction specifies the depositing account, the vault's unique identifier (`vault_id`), 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. ### 6. Submit VaultDeposit transaction Submit the `VaultDeposit` transaction to the XRP Ledger. JavaScript // 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!") Python # Submit VaultDeposit transaction print("\n=== Submitting VaultDeposit transaction... ===") deposit_result = submit_and_wait(vault_deposit_tx, client, depositor, autofill=True) if deposit_result.result["meta"]["TransactionResult"] != "tesSUCCESS": result_code = deposit_result.result["meta"]["TransactionResult"] print(f"Error: Unable to deposit: {result_code}", file=sys.stderr) sys.exit(1) print("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. Note Transfer fees are not charged on `VaultDeposit` transactions. ### 7. Verify deposit and check share balance After depositing, verify the vault's updated state. You can extract this information directly from the transaction metadata without making additional API calls: JavaScript // 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}`) } Python # Extract vault state from transaction metadata print("\n=== Vault state after deposit ===") affected_nodes = deposit_result.result["meta"]["AffectedNodes"] vault_node = None for node in affected_nodes: if "ModifiedNode" in node: modified = node["ModifiedNode"] if modified["LedgerEntryType"] == "Vault" and modified["LedgerIndex"] == vault_id: vault_node = node break if vault_node: vault_fields = vault_node["ModifiedNode"]["FinalFields"] print(f" - Total vault value: {vault_fields['AssetsTotal']}") print(f" - Available assets: {vault_fields['AssetsAvailable']}") Finally, check that the depositing account has received the shares. JavaScript // 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() Python # Get the depositor's share balance print("\n=== Depositor's share balance ===") depositor_share_node = None for node in affected_nodes: if "ModifiedNode" in node: share_node = node["ModifiedNode"] fields = share_node["FinalFields"] elif "CreatedNode" in node: share_node = node["CreatedNode"] fields = share_node["NewFields"] else: continue if (share_node["LedgerEntryType"] == "MPToken" and fields["Account"] == depositor.address and fields["MPTokenIssuanceID"] == share_mpt_issuance_id): depositor_share_node = node break if depositor_share_node: if "ModifiedNode" in depositor_share_node: share_fields = depositor_share_node["ModifiedNode"]["FinalFields"] else: share_fields = depositor_share_node["CreatedNode"]["NewFields"] print(f"Shares held: {share_fields['MPTAmount']}") 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`). ## See Also **Concepts**: - [Single Asset Vaults](/ja/docs/concepts/tokens/single-asset-vaults) - [Credentials](/ja/docs/concepts/decentralized-storage/credentials) - [Permissioned Domains](/ja/docs/concepts/tokens/decentralized-exchange/permissioned-domains) **Tutorials**: - [Create a Single Asset Vault](/ja/docs/tutorials/defi/lending/use-single-asset-vaults/create-a-single-asset-vault) - [Withdraw from a Vault](/ja/docs/tutorials/defi/lending/use-single-asset-vaults/withdraw-from-a-vault) **References**: - [VaultDeposit transaction](/docs/references/protocol/transactions/types/vaultdeposit) - [vault_info method](/docs/references/http-websocket-apis/public-api-methods/vault-methods/vault_info)