# Create a Single Asset Vault Attention Lending Protocol and Single Asset Vaults are disabled on Devnet. You have two options for testing these features: 1. Run `rippled` in [stand-alone mode](https://xrpl.org/docs/concepts/networks-and-servers/rippled-server-modes#stand-alone-mode) and enable `LendingProtocol` and `SingleAssetVault` in the [rippled.cfg file](https://xrpl.org/docs/infrastructure/testing-and-auditing/test-amendments). 2. Connect to the Lending Protocol-specific Devnet at `https://lend.devnet.rippletest.net:51234/`. This tutorial shows you how to create a [single asset vault](/docs/xls-65d-single-asset-vault/concepts/single-asset-vault) 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](https://xrpl.org/docs/concepts/decentralized-storage/credentials) can deposit, managed through [Permissioned Domains](https://xrpl.org/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. *(Requires the [Single Asset Vault amendment](https://github.com/XRPLF/XRPL-Standards/tree/master/XLS-0065-single-asset-vault) )* ## 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](https://xrpl.org/docs/tutorials/javascript/build-apps/get-started?__step=install-node-tag) 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](https://github.com/ripple/opensource.ripple.com/tree/main/_code-samples/vaults/). ## 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 client library and instantiate a client to connect to the XRPL. JavaScript 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() // Use the Lending Devnet faucet const faucetHost = "lend-faucet.devnet.rippletest.net" const faucetPath = "/accounts" 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(null, { faucetHost, faucetPath }) // A pre-existing Vault asset, created for this tutorial. You can specify your own Vault asset. const mptIssuanceId = "0003E3B486D3DACD8BB468AB33793B9626BD894A92AB3AB4" // A pre-existing Permissioned Domain ID, created for this tutorial. You can specify your own Domain ID. // NOTE: You don't need this if you want to create a public vault. const domainId = "3BB81D0D164456A2D74720F63FD923F16DE08FB3223D3ED103D09F525A8D69D1" 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, 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/xls-65d-single-asset-vault/reference/transactions/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 Data: xrpl.convertStringToHex("Private vault"), // 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 `AssetsMaximum` is set to `0` to indicate no cap on how many assets 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](/docs/xls-65d-single-asset-vault/reference/api/vault-info) method 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 Vault](/docs/xls-65d-single-asset-vault/concepts/single-asset-vault) - [Credentials](https://xrpl.org/docs/concepts/decentralized-storage/credentials) - [Permissioned Domains](https://xrpl.org/docs/concepts/tokens/decentralized-exchange/permissioned-domains) **Tutorials**: - [Issue Credentials](https://xrpl.org/docs/tutorials/javascript/build-apps/credential-issuing-service/) - [Create Permissioned Domain](https://xrpl.org/docs/tutorials/javascript/compliance/create-permissioned-domains/) - [Deposit Assets into a Vault](/docs/xls-65d-single-asset-vault/tutorials/deposit-into-a-vault) **References**: - [VaultCreate transaction](/docs/xls-65d-single-asset-vault/reference/transactions/vaultcreate)