# Withdraw from a 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 withdraw assets from a [single asset vault](/docs/xls-65d-single-asset-vault/concepts/single-asset-vault). You can withdraw by specifying either how many assets you want to receive or how many shares you want to redeem. The vault burns the necessary shares and transfers the corresponding assets to your account. *(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: - Withdraw assets from a private/public vault. - Check the vault's state after a successful withdrawal. - Check the depositor account's state after the withdrawal. ## Prerequisites To complete this tutorial, you should: - Have a basic understanding of the XRP Ledger. - Have previously deposited into a vault. See [Deposit into a Vault](/docs/xls-65d-single-asset-vault/tutorials/deposit-into-a-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](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 mport 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 `depositor` account and specify the vault details: JavaScript // Get depositor account const depositor = xrpl.Wallet.fromSeed("sEdVSq9Zsv8vQwfivTk37bWxrvpnruf") // The ID of the vault to withraw from 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 withdrawAmount = "1" ### 3. Check initial vault state Before withdrawing, check the vault's current state to see its total assets 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(`Initial vault state:`) console.log(` Assets Total: ${initialVaultInfo.result.vault.AssetsTotal}`) console.log(` Assets Available: ${initialVaultInfo.result.vault.AssetsAvailable}`) ### 4. Check share balance Verify that the depositor account has vault shares to redeem. If not, the transaction will fail with a `tecINSUFFICIENT_FUNDS` error. JavaScript // Check depositor's share balance ---------------------- console.log("\n=== Checking depositor's share balance... ===") try { const shareBalanceResult = await client.request({ command: "ledger_entry", mptoken: { mpt_issuance_id: shareMPTIssuanceId, account: depositor.address }, ledger_index: "validated" }) const shareBalance = shareBalanceResult.result.node.MPTAmount console.log(`Shares held: ${shareBalance}`) } catch (error) { if (error.data?.error === 'entryNotFound') { console.error(`Error: The depositor doesn't hold any vault shares with ID: ${shareMPTIssuanceId}.`) } await client.disconnect() process.exit(1) } ### 5. Prepare VaultWithdraw transaction Create a [VaultWithdraw transaction](/docs/xls-65d-single-asset-vault/reference/transactions/vaultwithdraw) to withdraw assets from the vault. JavaScript // Prepare VaultWithdraw transaction ---------------------- console.log(`\n=== Preparing VaultWithdraw transaction ===`) const vaultWithdrawTx = { TransactionType: "VaultWithdraw", Account: depositor.address, VaultID: vaultID, Amount: { mpt_issuance_id: assetMPTIssuanceId, value: withdrawAmount }, // Optional: Add Destination field to send assets to a different account // Destination: "rGg4tHPRGJfewwJkd8immCFx9uSo2GgcoY" } // Validate the transaction structure before submitting xrpl.validate(vaultWithdrawTx) console.log(JSON.stringify(vaultWithdrawTx, null, 2)) The transaction defines the account requesting the withdrawal, the vault's unique identifier (`VaultID`), and the amount to withdraw or redeem. You can specify the `Amount` field in two ways: - **Asset amount**: When you specify an asset amount, the vault burns the necessary shares to provide that amount. - **Share amount**: When you specify a share amount, the vault converts those shares into the corresponding asset amount. While not required, you can provide a `Destination` account to receive the assets; if omitted, assets go to the account specified in the `Account` field. Note You can withdraw from a vault regardless of whether it's private or public. If you hold vault shares, you can always redeem them, even if your credentials in a private vault's permissioned domain have expired or been revoked. This prevents you from being locked out of your funds. ### 6. Submit VaultWithdraw transaction Submit the `VaultWithdraw` transaction to the XRP Ledger. JavaScript // Submit VaultWithdraw transaction ---------------------- console.log("\n=== Submitting VaultWithdraw transaction... ===") const withdrawResult = await client.submitAndWait(vaultWithdrawTx, { wallet: depositor, autofill: true, }) if (withdrawResult.result.meta.TransactionResult !== "tesSUCCESS") { const result_code = withdrawResult.result.meta.TransactionResult console.error("Error: Unable to withdraw from vault:", result_code) await client.disconnect() process.exit(1) } console.log("Withdrawal successful!") When the transaction succeeds: - The vault calculates how many shares need to be burned to provide the requested asset amount. - The vault transfers the assets from its pseudo-account to the depositor account (or the `Destination` account if specified). Note Transfer fees are not charged on `VaultWithdraw` transactions. ### 6. Verify withdrawal After withdrawing, check the vault's state. You can extract this information directly from the transaction metadata. JavaScript // Extract vault state from transaction metadata ---------------------- console.log("\n=== Vault state after withdrawal ===") const affectedNodes = withdrawResult.result.meta.AffectedNodes const vaultNode = affectedNodes.find( (node) => { const modifiedNode = node.ModifiedNode || node.DeletedNode return ( modifiedNode && modifiedNode.LedgerEntryType === "Vault" && modifiedNode.LedgerIndex === vaultID ) } ) if (vaultNode) { if (vaultNode.DeletedNode) { console.log(` Vault empty (all assets withdrawn)`) } else { const vaultFields = vaultNode.ModifiedNode.FinalFields console.log(` Assets Total: ${vaultFields.AssetsTotal}`) console.log(` Assets Available: ${vaultFields.AssetsAvailable}`) } } Then, check the depositor's share balance: JavaScript // Get the depositor's share balance ---------------------- console.log("\n=== Depositor's share balance ==") const depositorShareNode = affectedNodes.find((node) => { const modifiedNode = node.ModifiedNode || node.DeletedNode return ( modifiedNode && modifiedNode.LedgerEntryType === "MPToken" && modifiedNode.FinalFields?.Account === depositor.address && modifiedNode.FinalFields?.MPTokenIssuanceID === shareMPTIssuanceId ) }) if (depositorShareNode) { if (depositorShareNode.DeletedNode) { console.log(`No more shares held (redeemed all shares)`) } else { const shareFields = depositorShareNode.ModifiedNode.FinalFields console.log(`Shares held: ${shareFields.MPTAmount}`) } } Finally, verify the correct asset amount has been received by the depositor account: JavaScript // Get the depositor's asset balance ---------------------- console.log("\n=== Depositor's asset balance ==") const depositorAssetNode = affectedNodes.find((node) => { const assetNode = node.ModifiedNode || node.CreatedNode const fields = assetNode?.FinalFields || assetNode?.NewFields return ( assetNode && assetNode.LedgerEntryType === "MPToken" && fields?.Account === depositor.address && fields?.MPTokenIssuanceID === assetMPTIssuanceId ) }) if (depositorAssetNode) { const assetNode = depositorAssetNode.ModifiedNode || depositorAssetNode.CreatedNode const assetFields = assetNode.FinalFields || assetNode.NewFields console.log(`Balance: ${assetFields.MPTAmount}`) } ## 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**: - [Create a Single Asset Vault](/docs/xls-65d-single-asset-vault/tutorials/create-a-single-asset-vault) - [Deposit into a Vault](/docs/xls-65d-single-asset-vault/tutorials/deposit-into-a-vault) **References**: - [VaultWithdraw transaction](/docs/xls-65d-single-asset-vault/reference/transactions/vaultwithdraw) - [vault_info method](/docs/xls-65d-single-asset-vault/reference/api/vault-info)