# Create a Single Asset Vault This tutorial shows you how to create a [single asset vault](/docs/concepts/tokens/single-asset-vaults) on the XRP Ledger. Vaults can only hold a single type of asset, such as XRP, a trust line token, or a Multi-Purpose Token (MPT). You can create either a: - **Public vault**: Anyone can deposit assets. - **Private vault**: Only users with valid [Credentials](/docs/concepts/decentralized-storage/credentials) can deposit, managed through [Permissioned Domains](/docs/concepts/tokens/decentralized-exchange/permissioned-domains). The tutorial demonstrates how a financial institution could use a **private vault** to pool lender assets for uncollateralized lending while maintaining regulatory compliance through credential-based access control. SingleAssetVault ## Goals By the end of this tutorial, you will be able to: - Create a **private** vault. - Configure vault parameters such as the asset type, maximum deposit amount, and withdrawal policy. - Configure whether depositors can transfer their vault shares to other accounts. ## Prerequisites To complete this tutorial, you should: - Have a basic understanding of the XRP Ledger. - 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/javascript/build-apps/get-started) 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 ``` ### 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: - `xrpl`: Used for XRPL client connection and transaction handling. - `fs` and `child_process`: Used to run tutorial setup scripts. JavaScript 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() Next, fund a vault owner account, define the MPT issuance ID for the vault's asset, and provide a permissioned domain ID to control who can deposit into the vault. JavaScript // Create and fund vault owner account const { wallet: vaultOwner } = await client.fundWallet() // You can replace these values with your own const mptIssuanceId = setupData.mptIssuanceId const domainId = setupData.domainId console.log(`Vault owner address: ${vaultOwner.address}`) console.log(`MPT issuance ID: ${mptIssuanceId}`) console.log(`Permissioned domain ID: ${domainId}\n`) The example uses an existing MPT issuance and permissioned domain data from the `vaultSetup.js` script, but you can also provide your own values. If you want to create a public vault, you don't need to provide the `domainId`. ### 3. Prepare VaultCreate transaction Create the [VaultCreate transaction](/docs/references/protocol/transactions/types/vaultcreate) object: JavaScript // Prepare VaultCreate transaction ---------------------- console.log(`\n=== VaultCreate transaction ===`) const vaultCreateTx = { TransactionType: "VaultCreate", Account: vaultOwner.address, Asset: { mpt_issuance_id: mptIssuanceId }, Flags: xrpl.VaultCreateFlags.tfVaultPrivate, // Omit tfVaultPrivate flag for public vaults // To make vault shares non-transferable add the tfVaultShareNonTransferable flag: // Flags: xrpl.VaultCreateFlags.tfVaultPrivate | xrpl.VaultCreateFlags.tfVaultShareNonTransferable DomainID: domainId, // Omit for public vaults // Convert Vault data to a string (without excess whitespace), then string to hex. Data: xrpl.convertStringToHex(JSON.stringify( { n: "LATAM Fund II", w: "examplefund.com" }) ), // Encode JSON metadata as hex string per XLS-89 MPT Metadata Schema. // See: https://xls.xrpl.org/xls/XLS-0089-multi-purpose-token-metadata-schema.html MPTokenMetadata: xrpl.encodeMPTokenMetadata({ ticker: "SHARE1", name: "Vault shares", desc: "Proportional ownership shares of the vault.", icon: "example.com/asset-icon.png", asset_class: "defi", issuer_name: "Asset Issuer Name", uris: [ { uri: "example.com/asset", category: "website", title: "Asset Website", }, { uri: "example.com/docs", category: "docs", title: "Docs", }, ], additional_info: { example_info: "test", }, }), AssetsMaximum: "0", // No cap WithdrawalPolicy: xrpl.VaultWithdrawalPolicy.vaultStrategyFirstComeFirstServe, }; // Validate the transaction structure before submitting xrpl.validate(vaultCreateTx) console.log(JSON.stringify(vaultCreateTx, null, 2)) The `tfVaultPrivate` flag and `DomainID` field restrict deposits to accounts with valid credentials in the specified permissioned domain. These can be omitted if you want to create a public vault instead. The `Data` field contains hex-encoded metadata about the vault itself, such as its name (`n`) and website (`w`). While any data structure is allowed, it's recommended to follow the [defined data schema](/docs/references/protocol/ledger-data/ledger-entry-types/vault#data-field-format) for better discoverability in the XRPL ecosystem. The `AssetsMaximum` is set to `0` to indicate no cap on how much of the asset the vault can hold, but you can adjust as needed. Vault shares are **transferable** by default, meaning depositors can transfer their shares to other accounts. If you don't want the vault's shares to be transferable, enable the `tfVaultShareNonTransferable` flag. ### 4. Submit VaultCreate transaction Sign and submit the `VaultCreate` transaction to the XRP Ledger. JavaScript // Submit, sign, and wait for validation ---------------------- console.log("\n=== Submitting VaultCreate transaction... ===") const submit_response = await client.submitAndWait(vaultCreateTx, { wallet: vaultOwner, autofill: true, }) if (submit_response.result.meta.TransactionResult !== "tesSUCCESS") { const result_code = submit_response.result.meta.TransactionResult; console.error("Error: Unable to create vault:", result_code) await client.disconnect() process.exit(1) } console.log("Vault created successfully!") Verify that the transaction succeeded by checking for a `tesSUCCESS` result code. ### 5. Get vault information Retrieve the vault's information from the transaction result by checking for the `Vault` object in the transaction metadata. JavaScript // Extract vault information from the transaction result const affectedNodes = submit_response.result.meta.AffectedNodes || [] const vaultNode = affectedNodes.find( (node) => node.CreatedNode?.LedgerEntryType === "Vault" ) if (vaultNode) { console.log(`\nVault ID: ${vaultNode.CreatedNode.LedgerIndex}`) console.log(`Vault pseudo-account address: ${vaultNode.CreatedNode.NewFields.Account}`) console.log(`Share MPT issuance ID: ${vaultNode.CreatedNode.NewFields.ShareMPTID}`) } You can also use the [vault_info method](/docs/references/http-websocket-apis/public-api-methods/vault-methods/vault_info) to retrieve the vault's details: JavaScript // Call vault_info method to retrieve the vault's information console.log("\n=== Getting vault_info... ===") const vaultID = vaultNode.CreatedNode.LedgerIndex const vault_info_response = await client.request({ command: "vault_info", vault_id: vaultID, ledger_index: "validated" }) console.log(JSON.stringify(vault_info_response, null, 2)) await client.disconnect() This confirms that you have successfully created an empty single asset vault. ## See Also **Concepts**: - [Single Asset Vaults](/docs/concepts/tokens/single-asset-vaults) - [Credentials](/docs/concepts/decentralized-storage/credentials) - [Permissioned Domains](/docs/concepts/tokens/decentralized-exchange/permissioned-domains) **Tutorials**: - [Issue Credentials](/docs/tutorials/javascript/build-apps/credential-issuing-service) - [Create Permissioned Domain](/docs/tutorials/javascript/compliance/create-permissioned-domains) - [Deposit Assets into a Vault](/docs/tutorials/how-tos/set-up-lending/use-single-asset-vaults/deposit-into-a-vault) **References**: - [VaultCreate transaction](/docs/references/protocol/transactions/types/vaultcreate)