# Withdraw from a Vault

This tutorial shows you how to withdraw assets from a [single asset vault](/docs/concepts/tokens/single-asset-vaults). 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.

SingleAssetVault
## 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. This tutorial uses an account that has already deposited into a vault. To deposit your own asset, see [Deposit into a Vault](/docs/tutorials/defi/lending/use-single-asset-vaults/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](/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.



```js
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.



```py
import json
import os
import subprocess
import sys

from xrpl.clients import JsonRpcClient
from xrpl.models import VaultWithdraw
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 depositor account and specify the vault details.

JavaScript

```js
// 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
```

This example uses preconfigured accounts and vault data from the `vaultSetup.js` script, but you can replace these values with your own.

Python

```py
# 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"]
```

This example uses preconfigured accounts and vault data from the `vault_setup.py` script, but you can replace these values with your own.

### 3. Check initial vault state

Before withdrawing, check the vault's current state to see its total assets and available liquidity.

JavaScript

```js
// 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}`)
```

Python

```py
# Get initial vault state
print("\n=== Getting initial vault state... ===")
initial_vault_info = client.request(
    VaultInfo(
        vault_id=vault_id,
        ledger_index="validated"
    )
)

print("Initial vault state:")
print(f"  Assets Total: {initial_vault_info.result['vault']['AssetsTotal']}")
print(f"  Assets Available: {initial_vault_info.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

```js
// 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}.`)
  } else {
    console.error(`Error checking MPT: ${error}`)
  }
  await client.disconnect()
  process.exit(1)
}
```

Python

```py
# Check depositor's share balance
print("\n=== Checking depositor's share balance... ===")
try:
    share_balance_result = client.request(
        LedgerEntry(
            mptoken={
                "mpt_issuance_id": share_mpt_issuance_id,
                "account": depositor.address
            },
            ledger_index="validated"
        )
    )

    share_balance = share_balance_result.result["node"]["MPTAmount"]
    print(f"Shares held: {share_balance}")
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 vault shares with ID: {share_mpt_issuance_id}.", file=sys.stderr)
    else:
        print(f"Error checking MPT: {error}", file=sys.stderr)
    sys.exit(1)
```

### 5. Prepare VaultWithdraw transaction

Create a [VaultWithdraw transaction](/docs/references/protocol/transactions/types/vaultwithdraw) to withdraw assets from the vault.

JavaScript

```js
// 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:

Python

```py
# Prepare VaultWithdraw transaction
print("\n=== Preparing VaultWithdraw transaction ===")
vault_withdraw_tx = VaultWithdraw(
    account=depositor.address,
    vault_id=vault_id,
    amount={
        "mpt_issuance_id": asset_mpt_issuance_id,
        "value": str(withdraw_amount)
    }
    # Optional: Add destination field to send assets to a different account
    # destination="rGg4tHPRGJfewwJkd8immCFx9uSo2GgcoY"
)

print(json.dumps(vault_withdraw_tx.to_xrpl(), indent=2))
```

The transaction defines the account requesting the withdrawal, the vault's unique identifier (`vault_id`), 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 submitting the transaction.

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

```js
// 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!")
```

Python

```py
# Submit VaultWithdraw transaction
print("\n=== Submitting VaultWithdraw transaction... ===")
withdraw_result = submit_and_wait(vault_withdraw_tx, client, depositor, autofill=True)

if withdraw_result.result["meta"]["TransactionResult"] != "tesSUCCESS":
    result_code = withdraw_result.result["meta"]["TransactionResult"]
    print(f"Error: Unable to withdraw from vault: {result_code}", file=sys.stderr)
    sys.exit(1)

print("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.

### 7. Verify withdrawal

After withdrawing, check the vault's state. You can extract this information directly from the transaction metadata.

JavaScript

```js
// 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}`)
  }
}
```

Python

```py
# Extract vault state from transaction metadata
print("\n=== Vault state after withdrawal ===")
affected_nodes = withdraw_result.result["meta"]["AffectedNodes"]
vault_node = None
for node in affected_nodes:
    if "ModifiedNode" in node or "DeletedNode" in node:
        modified_node = node["ModifiedNode"] if "ModifiedNode" in node else node["DeletedNode"]
        if modified_node["LedgerEntryType"] == "Vault" and modified_node["LedgerIndex"] == vault_id:
            vault_node = node
            break

if vault_node:
    if "DeletedNode" in vault_node:
        print("  Vault empty (all assets withdrawn)")
    else:
        vault_fields = vault_node["ModifiedNode"]["FinalFields"]
        print(f"  Assets Total: {vault_fields['AssetsTotal']}")
        print(f"  Assets Available: {vault_fields['AssetsAvailable']}")
```

Then, check the depositor's share balance:

JavaScript

```js
// 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}`)
  }
}
```

Python

```py
# 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 or "DeletedNode" in node:
        modified_node = node["ModifiedNode"] if "ModifiedNode" in node else node["DeletedNode"]
        if "FinalFields" in modified_node:
            fields = modified_node["FinalFields"]
            if (modified_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 "DeletedNode" in depositor_share_node:
        print("No more shares held (redeemed all shares)")
    else:
        share_fields = depositor_share_node["ModifiedNode"]["FinalFields"]
        print(f"Shares held: {share_fields['MPTAmount']}")
```

Finally, verify the correct asset amount has been received by the depositor account:

JavaScript

```js
// 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}`)
}

await client.disconnect()
```

Python

```py
# Get the depositor's asset balance
print("\n=== Depositor's asset balance ===")
depositor_asset_node = None
for node in affected_nodes:
    if "ModifiedNode" in node:
        asset_node = node["ModifiedNode"]
        fields = asset_node["FinalFields"]
    elif "CreatedNode" in node:
        asset_node = node["CreatedNode"]
        fields = asset_node["NewFields"]
    else:
        continue

    if (asset_node["LedgerEntryType"] == "MPToken" and
        fields["Account"] == depositor.address and
        fields["MPTokenIssuanceID"] == asset_mpt_issuance_id):
        depositor_asset_node = node
        break

if depositor_asset_node:
    if "ModifiedNode" in depositor_asset_node:
        asset_fields = depositor_asset_node["ModifiedNode"]["FinalFields"]
    else:
        asset_fields = depositor_asset_node["CreatedNode"]["NewFields"]
    print(f"Balance: {asset_fields['MPTAmount']}")
```

## 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**:

- [Create a Single Asset Vault](/docs/tutorials/defi/lending/use-single-asset-vaults/create-a-single-asset-vault)
- [Deposit into a Vault](/docs/tutorials/defi/lending/use-single-asset-vaults/deposit-into-a-vault)


**References**:

- [VaultWithdraw transaction](/docs/references/protocol/transactions/types/vaultwithdraw)
- [vault_info method](/docs/references/http-websocket-apis/public-api-methods/vault-methods/vault_info)