mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2026-02-15 11:22:28 +00:00
Compare commits
2 Commits
dependabot
...
sav-python
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
681e55dd18 | ||
|
|
0d960fa0d3 |
187
_code-samples/vaults/py/README.md
Normal file
187
_code-samples/vaults/py/README.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# Single Asset Vault Examples (Python)
|
||||
|
||||
This directory contains Python examples demonstrating how to create, deposit into, and withdraw from single asset vaults on the XRP Ledger.
|
||||
|
||||
## Setup
|
||||
|
||||
Install dependencies before running any examples:
|
||||
|
||||
```sh
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install xrpl-py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Create a Vault
|
||||
|
||||
```sh
|
||||
python create_vault.py
|
||||
```
|
||||
|
||||
The script should output the VaultCreate transaction, vault ID, and complete vault information:
|
||||
|
||||
```sh
|
||||
Vault owner address: rfsTcqjyg7j2xfJFNbd9u8mt65yrGZvLnu
|
||||
MPT issuance ID: 00385B21AF216177F319AC73F25F0FCBCDA09330D1D50D03
|
||||
Permissioned domain ID: 76397457A19E093654F74848E5255E6111FDC0A2BF9FB2143F7C2C33424E1B3E
|
||||
|
||||
|
||||
=== VaultCreate transaction ===
|
||||
{
|
||||
"Account": "rfsTcqjyg7j2xfJFNbd9u8mt65yrGZvLnu",
|
||||
"TransactionType": "VaultCreate",
|
||||
"Flags": 65536,
|
||||
"SigningPubKey": "",
|
||||
"Asset": {
|
||||
"mpt_issuance_id": "00385B21AF216177F319AC73F25F0FCBCDA09330D1D50D03"
|
||||
},
|
||||
"Data": "7b226e223a20224c4154414d2046756e64204949222c202277223a20226578616d706c6566756e642e636f6d227d",
|
||||
"AssetsMaximum": "0",
|
||||
"MPTokenMetadata": "7B226163223A2264656669222C226169223A7B226578616D706C655F696E666F223A2274657374227D2C2264223A2250726F706F7274696F6E616C206F776E65727368697020736861726573206F6620746865207661756C742E222C2269223A226578616D706C652E636F6D2F61737365742D69636F6E2E706E67222C22696E223A22417373657420497373756572204E616D65222C226E223A225661756C7420736861726573222C2274223A22534841524531222C227573223A5B7B2263223A2277656273697465222C2274223A2241737365742057656273697465222C2275223A226578616D706C652E636F6D2F6173736574227D2C7B2263223A22646F6373222C2274223A22446F6373222C2275223A226578616D706C652E636F6D2F646F6373227D5D7D",
|
||||
"DomainID": "76397457A19E093654F74848E5255E6111FDC0A2BF9FB2143F7C2C33424E1B3E",
|
||||
"WithdrawalPolicy": 1
|
||||
}
|
||||
|
||||
=== Submitting VaultCreate transaction... ===
|
||||
Vault created successfully!
|
||||
|
||||
Vault ID: 3E5BB3E4789603CC20D7A874ECBA36B74188F1B991EC9199DFA129FDB44D846D
|
||||
Vault pseudo-account address: rPgYFS3qFrUYQ3qWpF9RLKc9ECkGhgADtm
|
||||
Share MPT issuance ID: 00000001F8CD8CC81FFDDC9887627F42390E85DB32D44D0E
|
||||
|
||||
=== Getting vault_info... ===
|
||||
{
|
||||
"ledger_hash": "5851C21E353DEDEC5C6CC285E1E9835C378DCBBE5BA69CF33124DAC7EE5A08AD",
|
||||
"ledger_index": 3693379,
|
||||
"validated": true,
|
||||
"vault": {
|
||||
"Account": "rPgYFS3qFrUYQ3qWpF9RLKc9ECkGhgADtm",
|
||||
"Asset": {
|
||||
"mpt_issuance_id": "00385B21AF216177F319AC73F25F0FCBCDA09330D1D50D03"
|
||||
},
|
||||
"Data": "7B226E223A20224C4154414D2046756E64204949222C202277223A20226578616D706C6566756E642E636F6D227D",
|
||||
"Flags": 65536,
|
||||
"LedgerEntryType": "Vault",
|
||||
"Owner": "rfsTcqjyg7j2xfJFNbd9u8mt65yrGZvLnu",
|
||||
"OwnerNode": "0",
|
||||
"PreviousTxnID": "4B29E4DBA09CBDCAF591792ACFFB5F8717AD230185207C10F10B2A405FB2D576",
|
||||
"PreviousTxnLgrSeq": 3693379,
|
||||
"Sequence": 3693375,
|
||||
"ShareMPTID": "00000001F8CD8CC81FFDDC9887627F42390E85DB32D44D0E",
|
||||
"WithdrawalPolicy": 1,
|
||||
"index": "3E5BB3E4789603CC20D7A874ECBA36B74188F1B991EC9199DFA129FDB44D846D",
|
||||
"shares": {
|
||||
"DomainID": "76397457A19E093654F74848E5255E6111FDC0A2BF9FB2143F7C2C33424E1B3E",
|
||||
"Flags": 60,
|
||||
"Issuer": "rPgYFS3qFrUYQ3qWpF9RLKc9ECkGhgADtm",
|
||||
"LedgerEntryType": "MPTokenIssuance",
|
||||
"MPTokenMetadata": "7B226163223A2264656669222C226169223A7B226578616D706C655F696E666F223A2274657374227D2C2264223A2250726F706F7274696F6E616C206F776E65727368697020736861726573206F6620746865207661756C742E222C2269223A226578616D706C652E636F6D2F61737365742D69636F6E2E706E67222C22696E223A22417373657420497373756572204E616D65222C226E223A225661756C7420736861726573222C2274223A22534841524531222C227573223A5B7B2263223A2277656273697465222C2274223A2241737365742057656273697465222C2275223A226578616D706C652E636F6D2F6173736574227D2C7B2263223A22646F6373222C2274223A22446F6373222C2275223A226578616D706C652E636F6D2F646F6373227D5D7D",
|
||||
"OutstandingAmount": "0",
|
||||
"OwnerNode": "0",
|
||||
"PreviousTxnID": "4B29E4DBA09CBDCAF591792ACFFB5F8717AD230185207C10F10B2A405FB2D576",
|
||||
"PreviousTxnLgrSeq": 3693379,
|
||||
"Sequence": 1,
|
||||
"index": "EAD6924CB5DDA61CC5B85A6776A32E460FBFB0C34F5076A6A52005459B38043D",
|
||||
"mpt_issuance_id": "00000001F8CD8CC81FFDDC9887627F42390E85DB32D44D0E"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Deposit into a Vault
|
||||
|
||||
```sh
|
||||
python deposit.py
|
||||
```
|
||||
|
||||
The script should output the vault state before and after the deposit, along with the depositor's share balance:
|
||||
|
||||
```sh
|
||||
Depositor address: r4pfiPR5y4GTbajHXzUS29KBDHUdxR8kCK
|
||||
Vault ID: 9966AF609568AFFCB3AEDEAC340B6AABB23C0483F013E186E83AF27EDA82C925
|
||||
Asset MPT issuance ID: 00385B21AF216177F319AC73F25F0FCBCDA09330D1D50D03
|
||||
Vault share MPT issuance ID: 00000001890BF384C217368D89BBB82B814B94B2597702B1
|
||||
|
||||
=== Getting initial vault state... ===
|
||||
- Total vault value: 1000
|
||||
- Available assets: 1000
|
||||
|
||||
=== Checking depositor's balance... ===
|
||||
Balance: 9000
|
||||
|
||||
=== VaultDeposit transaction ===
|
||||
{
|
||||
"Account": "r4pfiPR5y4GTbajHXzUS29KBDHUdxR8kCK",
|
||||
"TransactionType": "VaultDeposit",
|
||||
"SigningPubKey": "",
|
||||
"VaultID": "9966AF609568AFFCB3AEDEAC340B6AABB23C0483F013E186E83AF27EDA82C925",
|
||||
"Amount": {
|
||||
"mpt_issuance_id": "00385B21AF216177F319AC73F25F0FCBCDA09330D1D50D03",
|
||||
"value": "1"
|
||||
}
|
||||
}
|
||||
|
||||
=== Submitting VaultDeposit transaction... ===
|
||||
Deposit successful!
|
||||
|
||||
=== Vault state after deposit ===
|
||||
- Total vault value: 1001
|
||||
- Available assets: 1001
|
||||
|
||||
=== Depositor's share balance ===
|
||||
Shares held: 1001
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Withdraw from a Vault
|
||||
|
||||
```sh
|
||||
python withdraw.py
|
||||
```
|
||||
|
||||
The script should output the vault state before and after the withdrawal, along with updated share and asset balances:
|
||||
|
||||
```sh
|
||||
Depositor address: r4pfiPR5y4GTbajHXzUS29KBDHUdxR8kCK
|
||||
Vault ID: 9966AF609568AFFCB3AEDEAC340B6AABB23C0483F013E186E83AF27EDA82C925
|
||||
Asset MPT issuance ID: 00385B21AF216177F319AC73F25F0FCBCDA09330D1D50D03
|
||||
Vault share MPT issuance ID: 00000001890BF384C217368D89BBB82B814B94B2597702B1
|
||||
|
||||
=== Getting initial vault state... ===
|
||||
Initial vault state:
|
||||
Assets Total: 1001
|
||||
Assets Available: 1001
|
||||
|
||||
=== Checking depositor's share balance... ===
|
||||
Shares held: 1001
|
||||
|
||||
=== Preparing VaultWithdraw transaction ===
|
||||
{
|
||||
"Account": "r4pfiPR5y4GTbajHXzUS29KBDHUdxR8kCK",
|
||||
"TransactionType": "VaultWithdraw",
|
||||
"SigningPubKey": "",
|
||||
"VaultID": "9966AF609568AFFCB3AEDEAC340B6AABB23C0483F013E186E83AF27EDA82C925",
|
||||
"Amount": {
|
||||
"mpt_issuance_id": "00385B21AF216177F319AC73F25F0FCBCDA09330D1D50D03",
|
||||
"value": "1"
|
||||
}
|
||||
}
|
||||
|
||||
=== Submitting VaultWithdraw transaction... ===
|
||||
Withdrawal successful!
|
||||
|
||||
=== Vault state after withdrawal ===
|
||||
Assets Total: 1000
|
||||
Assets Available: 1000
|
||||
|
||||
=== Depositor's share balance ==
|
||||
Shares held: 1000
|
||||
|
||||
=== Depositor's asset balance ==
|
||||
Balance: 9000
|
||||
```
|
||||
115
_code-samples/vaults/py/create_vault.py
Normal file
115
_code-samples/vaults/py/create_vault.py
Normal file
@@ -0,0 +1,115 @@
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from xrpl.clients import JsonRpcClient
|
||||
from xrpl.models import VaultCreate
|
||||
from xrpl.models.requests import VaultInfo
|
||||
from xrpl.models.transactions.vault_create import VaultCreateFlag, WithdrawalPolicy
|
||||
from xrpl.transaction import submit_and_wait
|
||||
from xrpl.utils import str_to_hex, encode_mptoken_metadata
|
||||
from xrpl.wallet import generate_faucet_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(["python", "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")
|
||||
|
||||
# Create and fund vault owner account
|
||||
vault_owner = generate_faucet_wallet(client)
|
||||
|
||||
# You can replace these values with your own
|
||||
mpt_issuance_id = setup_data["mptIssuanceId"]
|
||||
domain_id = setup_data["domainId"]
|
||||
|
||||
print(f"Vault owner address: {vault_owner.address}")
|
||||
print(f"MPT issuance ID: {mpt_issuance_id}")
|
||||
print(f"Permissioned domain ID: {domain_id}\n")
|
||||
|
||||
# Prepare VaultCreate transaction ----------------------
|
||||
print("\n=== VaultCreate transaction ===")
|
||||
vault_create_tx = VaultCreate(
|
||||
account=vault_owner.address,
|
||||
asset={"mpt_issuance_id": mpt_issuance_id},
|
||||
flags=VaultCreateFlag.TF_VAULT_PRIVATE, # Omit TF_VAULT_PRIVATE flag for public vaults
|
||||
# To make vault shares non-transferable add the TF_VAULT_SHARE_NON_TRANSFERABLE flag:
|
||||
# flags=VaultCreateFlag.TF_VAULT_PRIVATE | VaultCreateFlag.TF_VAULT_SHARE_NON_TRANSFERABLE,
|
||||
domain_id=domain_id, # Omit for public vaults
|
||||
# Convert Vault data to a string (without excess whitespace), then string to hex.
|
||||
data=str_to_hex(json.dumps(
|
||||
{"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
|
||||
mptoken_metadata=encode_mptoken_metadata({
|
||||
"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",
|
||||
},
|
||||
}),
|
||||
assets_maximum="0", # No cap
|
||||
withdrawal_policy=WithdrawalPolicy.VAULT_STRATEGY_FIRST_COME_FIRST_SERVE,
|
||||
)
|
||||
|
||||
print(json.dumps(vault_create_tx.to_xrpl(), indent=2))
|
||||
|
||||
# Submit, sign, and wait for validation ----------------------
|
||||
print("\n=== Submitting VaultCreate transaction... ===")
|
||||
submit_response = submit_and_wait(vault_create_tx, client, vault_owner, autofill=True)
|
||||
|
||||
if submit_response.result["meta"]["TransactionResult"] != "tesSUCCESS":
|
||||
result_code = submit_response.result["meta"]["TransactionResult"]
|
||||
print(f"Error: Unable to create vault: {result_code}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print("Vault created successfully!")
|
||||
|
||||
# Extract vault information from the transaction result
|
||||
affected_nodes = submit_response.result["meta"].get("AffectedNodes", [])
|
||||
vault_node = next(
|
||||
(node for node in affected_nodes
|
||||
if "CreatedNode" in node and node["CreatedNode"].get("LedgerEntryType") == "Vault"),
|
||||
None
|
||||
)
|
||||
|
||||
if vault_node:
|
||||
print(f"\nVault ID: {vault_node['CreatedNode']['LedgerIndex']}")
|
||||
print(f"Vault pseudo-account address: {vault_node['CreatedNode']['NewFields']['Account']}")
|
||||
print(f"Share MPT issuance ID: {vault_node['CreatedNode']['NewFields']['ShareMPTID']}")
|
||||
|
||||
# Call vault_info method to retrieve the vault's information
|
||||
print("\n=== Getting vault_info... ===")
|
||||
vault_id = vault_node["CreatedNode"]["LedgerIndex"]
|
||||
vault_info_response = client.request(
|
||||
VaultInfo(
|
||||
vault_id=vault_id,
|
||||
ledger_index="validated"
|
||||
)
|
||||
)
|
||||
print(json.dumps(vault_info_response.result, indent=2))
|
||||
|
||||
147
_code-samples/vaults/py/deposit.py
Normal file
147
_code-samples/vaults/py/deposit.py
Normal file
@@ -0,0 +1,147 @@
|
||||
# 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(["python", "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")
|
||||
|
||||
# You can replace these values with your own
|
||||
depositor = Wallet.from_seed(setup_data["depositor"]["seed"])
|
||||
vault_id = setup_data["vaultID"]
|
||||
asset_mpt_issuance_id = setup_data["mptIssuanceId"]
|
||||
share_mpt_issuance_id = setup_data["vaultShareMPTIssuanceId"]
|
||||
|
||||
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
|
||||
|
||||
# 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']}")
|
||||
|
||||
# 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)
|
||||
sys.exit(1)
|
||||
|
||||
# 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))
|
||||
|
||||
# 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!")
|
||||
|
||||
# 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']}")
|
||||
|
||||
# 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']}")
|
||||
|
||||
2
_code-samples/vaults/py/requirements.txt
Normal file
2
_code-samples/vaults/py/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
xrpl-py==4.5.0
|
||||
|
||||
271
_code-samples/vaults/py/vault_setup.py
Normal file
271
_code-samples/vaults/py/vault_setup.py
Normal file
@@ -0,0 +1,271 @@
|
||||
import asyncio
|
||||
import json
|
||||
import sys
|
||||
|
||||
from xrpl.asyncio.clients import AsyncWebsocketClient
|
||||
from xrpl.asyncio.transaction import submit_and_wait
|
||||
from xrpl.asyncio.wallet import generate_faucet_wallet
|
||||
from xrpl.models import (
|
||||
Batch, BatchFlag, CredentialAccept, CredentialCreate, MPTokenAuthorize,
|
||||
MPTokenIssuanceCreate, MPTokenIssuanceCreateFlag, Payment,
|
||||
PermissionedDomainSet, VaultDeposit
|
||||
)
|
||||
from xrpl.models.transactions.deposit_preauth import Credential
|
||||
from xrpl.models.transactions.vault_create import (
|
||||
VaultCreate, VaultCreateFlag, WithdrawalPolicy
|
||||
)
|
||||
from xrpl.utils import encode_mptoken_metadata, str_to_hex
|
||||
|
||||
|
||||
async def main():
|
||||
# Setup script for vault tutorials
|
||||
print("Setting up tutorial: 0/7", end="\r")
|
||||
|
||||
async with AsyncWebsocketClient("wss://s.devnet.rippletest.net:51233") as client:
|
||||
# Create and fund all wallets concurrently
|
||||
mpt_issuer, domain_owner, depositor, vault_owner = await asyncio.gather(
|
||||
generate_faucet_wallet(client),
|
||||
generate_faucet_wallet(client),
|
||||
generate_faucet_wallet(client),
|
||||
generate_faucet_wallet(client),
|
||||
)
|
||||
|
||||
# Step 1: Create MPT issuance
|
||||
print("Setting up tutorial: 1/7", end="\r")
|
||||
|
||||
mpt_create_result = await submit_and_wait(
|
||||
MPTokenIssuanceCreate(
|
||||
account=mpt_issuer.address,
|
||||
flags=(
|
||||
MPTokenIssuanceCreateFlag.TF_MPT_CAN_TRANSFER |
|
||||
MPTokenIssuanceCreateFlag.TF_MPT_CAN_LOCK
|
||||
),
|
||||
asset_scale=2,
|
||||
transfer_fee=0,
|
||||
maximum_amount="1000000000000",
|
||||
mptoken_metadata=encode_mptoken_metadata({
|
||||
"ticker": "USTST",
|
||||
"name": "USTST Stablecoin",
|
||||
"desc": "A test stablecoin token",
|
||||
"icon": "example.org/ustst-icon.png",
|
||||
"asset_class": "rwa",
|
||||
"asset_subclass": "stablecoin",
|
||||
"issuer_name": "Test Stablecoin Inc",
|
||||
"uris": [
|
||||
{
|
||||
"uri": "example.org/ustst",
|
||||
"category": "website",
|
||||
"title": "USTST Official Website",
|
||||
},
|
||||
{
|
||||
"uri": "example.org/ustst/reserves",
|
||||
"category": "attestation",
|
||||
"title": "Reserve Attestation Reports",
|
||||
},
|
||||
{
|
||||
"uri": "example.org/ustst/docs",
|
||||
"category": "docs",
|
||||
"title": "USTST Documentation",
|
||||
},
|
||||
],
|
||||
"additional_info": {
|
||||
"backing": "USD",
|
||||
"reserve_ratio": "1:1",
|
||||
},
|
||||
}),
|
||||
),
|
||||
client,
|
||||
mpt_issuer,
|
||||
autofill=True
|
||||
)
|
||||
|
||||
mpt_issuance_id = mpt_create_result.result["meta"]["mpt_issuance_id"]
|
||||
|
||||
# Step 2: Create Permissioned Domain
|
||||
print("Setting up tutorial: 2/7", end="\r")
|
||||
|
||||
cred_type = "VaultAccess"
|
||||
domain_result = await submit_and_wait(
|
||||
PermissionedDomainSet(
|
||||
account=domain_owner.address,
|
||||
accepted_credentials=[
|
||||
Credential(
|
||||
issuer=domain_owner.address,
|
||||
credential_type=str_to_hex(cred_type)
|
||||
)
|
||||
],
|
||||
),
|
||||
client,
|
||||
domain_owner,
|
||||
autofill=True
|
||||
)
|
||||
|
||||
domain_node = next(
|
||||
node for node in domain_result.result["meta"]["AffectedNodes"]
|
||||
if "CreatedNode" in node and node["CreatedNode"].get("LedgerEntryType") == "PermissionedDomain"
|
||||
)
|
||||
domain_id = domain_node["CreatedNode"]["LedgerIndex"]
|
||||
|
||||
# Step 3: Create depositor account with credentials and MPT balance
|
||||
print("Setting up tutorial: 3/7", end="\r")
|
||||
|
||||
# Create credential for depositor and depositor accepts credential concurrently
|
||||
await submit_and_wait(
|
||||
CredentialCreate(
|
||||
account=domain_owner.address,
|
||||
subject=depositor.address,
|
||||
credential_type=str_to_hex(cred_type),
|
||||
),
|
||||
client,
|
||||
domain_owner,
|
||||
autofill=True
|
||||
)
|
||||
|
||||
# Depositor accepts credential and authorizes MPT in a batch transaction
|
||||
await submit_and_wait(
|
||||
Batch(
|
||||
account=depositor.address,
|
||||
flags=BatchFlag.TF_ALL_OR_NOTHING,
|
||||
raw_transactions=[
|
||||
CredentialAccept(
|
||||
account=depositor.address,
|
||||
issuer=domain_owner.address,
|
||||
credential_type=str_to_hex(cred_type),
|
||||
),
|
||||
MPTokenAuthorize(
|
||||
account=depositor.address,
|
||||
mptoken_issuance_id=mpt_issuance_id,
|
||||
),
|
||||
],
|
||||
),
|
||||
client,
|
||||
depositor,
|
||||
autofill=True
|
||||
)
|
||||
|
||||
print("Setting up tutorial: 4/7", end="\r")
|
||||
|
||||
payment_result = await submit_and_wait(
|
||||
Payment(
|
||||
account=mpt_issuer.address,
|
||||
destination=depositor.address,
|
||||
amount={
|
||||
"mpt_issuance_id": mpt_issuance_id,
|
||||
"value": "10000",
|
||||
},
|
||||
),
|
||||
client,
|
||||
mpt_issuer,
|
||||
autofill=True
|
||||
)
|
||||
|
||||
if payment_result.result["meta"]["TransactionResult"] != "tesSUCCESS":
|
||||
print(f"\nPayment failed: {payment_result.result['meta']['TransactionResult']}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Step 5: Create a vault for deposit/withdraw examples
|
||||
print("Setting up tutorial: 5/7", end="\r")
|
||||
|
||||
vault_create_result = await submit_and_wait(
|
||||
VaultCreate(
|
||||
account=vault_owner.address,
|
||||
asset={"mpt_issuance_id": mpt_issuance_id},
|
||||
flags=VaultCreateFlag.TF_VAULT_PRIVATE,
|
||||
domain_id=domain_id,
|
||||
data=str_to_hex(json.dumps(
|
||||
{"n": "LATAM Fund II", "w": "examplefund.com"}
|
||||
)),
|
||||
mptoken_metadata=encode_mptoken_metadata({
|
||||
"ticker": "SHARE1",
|
||||
"name": "Vault Shares",
|
||||
"desc": "Proportional ownership shares of the vault",
|
||||
"icon": "example.com/vault-shares-icon.png",
|
||||
"asset_class": "defi",
|
||||
"issuer_name": "Vault Owner",
|
||||
"uris": [
|
||||
{
|
||||
"uri": "example.com/asset",
|
||||
"category": "website",
|
||||
"title": "Asset Website",
|
||||
},
|
||||
{
|
||||
"uri": "example.com/docs",
|
||||
"category": "docs",
|
||||
"title": "Docs",
|
||||
},
|
||||
],
|
||||
"additional_info": {
|
||||
"example_info": "test",
|
||||
},
|
||||
}),
|
||||
assets_maximum="0",
|
||||
withdrawal_policy=WithdrawalPolicy.VAULT_STRATEGY_FIRST_COME_FIRST_SERVE,
|
||||
),
|
||||
client,
|
||||
vault_owner,
|
||||
autofill=True
|
||||
)
|
||||
|
||||
vault_node = next(
|
||||
node for node in vault_create_result.result["meta"]["AffectedNodes"]
|
||||
if "CreatedNode" in node and node["CreatedNode"].get("LedgerEntryType") == "Vault"
|
||||
)
|
||||
vault_id = vault_node["CreatedNode"]["LedgerIndex"]
|
||||
vault_share_mpt_issuance_id = vault_node["CreatedNode"]["NewFields"]["ShareMPTID"]
|
||||
|
||||
# Step 6: Make an initial deposit so withdraw example has shares to work with
|
||||
print("Setting up tutorial: 6/7", end="\r")
|
||||
|
||||
initial_deposit_result = await submit_and_wait(
|
||||
VaultDeposit(
|
||||
account=depositor.address,
|
||||
vault_id=vault_id,
|
||||
amount={
|
||||
"mpt_issuance_id": mpt_issuance_id,
|
||||
"value": "1000",
|
||||
},
|
||||
),
|
||||
client,
|
||||
depositor,
|
||||
autofill=True
|
||||
)
|
||||
|
||||
if initial_deposit_result.result["meta"]["TransactionResult"] != "tesSUCCESS":
|
||||
print(f"\nInitial deposit failed: {initial_deposit_result.result['meta']['TransactionResult']}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Step 7: Save setup data to file
|
||||
print("Setting up tutorial: 7/7", end="\r")
|
||||
|
||||
setup_data = {
|
||||
"mptIssuer": {
|
||||
"address": mpt_issuer.address,
|
||||
"seed": mpt_issuer.seed,
|
||||
},
|
||||
"mptIssuanceId": mpt_issuance_id,
|
||||
"domainOwner": {
|
||||
"address": domain_owner.address,
|
||||
"seed": domain_owner.seed,
|
||||
},
|
||||
"domainId": domain_id,
|
||||
"credentialType": cred_type,
|
||||
"depositor": {
|
||||
"address": depositor.address,
|
||||
"seed": depositor.seed,
|
||||
},
|
||||
"vaultOwner": {
|
||||
"address": vault_owner.address,
|
||||
"seed": vault_owner.seed,
|
||||
},
|
||||
"vaultID": vault_id,
|
||||
"vaultShareMPTIssuanceId": vault_share_mpt_issuance_id,
|
||||
}
|
||||
|
||||
with open("vault_setup.json", "w") as f:
|
||||
json.dump(setup_data, f, indent=2)
|
||||
|
||||
print("Setting up tutorial: Complete!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
162
_code-samples/vaults/py/withdraw.py
Normal file
162
_code-samples/vaults/py/withdraw.py
Normal file
@@ -0,0 +1,162 @@
|
||||
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(["python", "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")
|
||||
|
||||
# You can replace these values with your own
|
||||
depositor = Wallet.from_seed(setup_data["depositor"]["seed"])
|
||||
vault_id = setup_data["vaultID"]
|
||||
asset_mpt_issuance_id = setup_data["mptIssuanceId"]
|
||||
share_mpt_issuance_id = setup_data["vaultShareMPTIssuanceId"]
|
||||
|
||||
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}")
|
||||
|
||||
withdraw_amount = 1
|
||||
|
||||
# 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']}")
|
||||
|
||||
# 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)
|
||||
sys.exit(1)
|
||||
|
||||
# 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))
|
||||
|
||||
# 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!")
|
||||
|
||||
# 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']}")
|
||||
|
||||
# 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']}")
|
||||
|
||||
# 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']}")
|
||||
|
||||
@@ -35,7 +35,8 @@ 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](../../../javascript/build-apps/get-started.md) for setup steps.
|
||||
- **JavaScript** with the [xrpl.js library][]. See [Get Started Using JavaScript][] for setup steps.
|
||||
- **Python** with the [xrpl-py library][]. See [Get Started Using Python][] for setup steps.
|
||||
|
||||
## Source Code
|
||||
|
||||
@@ -47,26 +48,47 @@ You can find the complete source code for this tutorial's examples in the {% rep
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
From the code sample folder, use npm to install dependencies:
|
||||
From the code sample folder, use `npm` to install dependencies:
|
||||
|
||||
```bash
|
||||
npm install xrpl
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% tab label="Python" %}
|
||||
From the code sample folder, use `pip` to install dependencies:
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install xrpl-py
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
### 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:
|
||||
To get started, import the necessary libraries and instantiate a client to connect to the XRPL.
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
This example imports:
|
||||
|
||||
- `xrpl`: Used for XRPL client connection and transaction handling.
|
||||
- `fs` and `child_process`: Used to run tutorial setup scripts.
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/createVault.js" language="js" before="// Create and fund" /%}
|
||||
{% /tab %}
|
||||
{% tab label="Python" %}
|
||||
This example imports:
|
||||
|
||||
- `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.
|
||||
|
||||
{% code-snippet file="/_code-samples/vaults/py/create_vault.py" language="python" before="# Create and fund" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
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.
|
||||
@@ -74,11 +96,17 @@ Next, fund a vault owner account, define the MPT issuance ID for the vault's ass
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/createVault.js" language="js" from="// Create and fund" before="// Prepare VaultCreate" /%}
|
||||
|
||||
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`.
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/create_vault.py" language="python" from="# Create and fund" before="# Prepare VaultCreate" /%}
|
||||
|
||||
The example uses an existing MPT issuance and permissioned domain data from the `vault_setup.py` script, but you can also provide your own values. If you want to create a public vault, you don't need to provide the `domain_id`.
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
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][] object:
|
||||
@@ -86,14 +114,24 @@ Create the [VaultCreate transaction][] object:
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/createVault.js" language="js" from="// Prepare VaultCreate" before="// Submit, sign" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
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](../../../../references/protocol/ledger-data/ledger-entry-types/vault.md#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.
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/create_vault.py" language="python" from="# Prepare VaultCreate" before="# Submit, sign" /%}
|
||||
|
||||
The `tfVaultPrivate` flag and `domain_id` 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](../../../../references/protocol/ledger-data/ledger-entry-types/vault.md#data-field-format) for better discoverability in the XRPL ecosystem.
|
||||
|
||||
The `assets_maximum` is set to `0` to indicate no cap on how much of the asset the vault can hold, but you can adjust as needed.
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
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.
|
||||
|
||||
@@ -105,6 +143,9 @@ Sign and submit the `VaultCreate` transaction to the XRP Ledger.
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/createVault.js" language="js" from="// Submit, sign" before="// Extract vault information" /%}
|
||||
{% /tab %}
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/create_vault.py" language="python" from="# Submit, sign" before="# Extract vault information" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
Verify that the transaction succeeded by checking for a `tesSUCCESS` result code.
|
||||
@@ -117,6 +158,9 @@ Retrieve the vault's information from the transaction result by checking for the
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/createVault.js" language="js" from="// Extract vault information" before="// Call vault_info" /%}
|
||||
{% /tab %}
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/create_vault.py" language="python" from="# Extract vault information" before="# Call vault_info" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
You can also use the [vault_info method][] to retrieve the vault's details:
|
||||
@@ -125,6 +169,9 @@ You can also use the [vault_info method][] to retrieve the vault's details:
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/createVault.js" language="js" from="// Call vault_info" /%}
|
||||
{% /tab %}
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/create_vault.py" language="python" from="# Call vault_info" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
This confirms that you have successfully created an empty single asset vault.
|
||||
|
||||
@@ -34,7 +34,8 @@ 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](./create-a-single-asset-vault.md).
|
||||
- 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](../../../javascript/build-apps/get-started.md) for setup steps.
|
||||
- **JavaScript** with the [xrpl.js library][]. See [Get Started Using JavaScript][] for setup steps.
|
||||
- **Python** with the [xrpl-py library][]. See [Get Started Using Python][] for setup steps.
|
||||
|
||||
## Source Code
|
||||
|
||||
@@ -46,25 +47,46 @@ You can find the complete source code for this tutorial's examples in the {% rep
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
From the code sample folder, use npm to install dependencies:
|
||||
From the code sample folder, use `npm` to install dependencies:
|
||||
|
||||
```bash
|
||||
npm install xrpl
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% tab label="Python" %}
|
||||
From the code sample folder, use `pip` to install dependencies:
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install xrpl-py
|
||||
```
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
### 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:
|
||||
To get started, import the necessary libraries and instantiate a client to connect to the XRPL.
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
This example imports:
|
||||
|
||||
- `xrpl`: Used for XRPL client connection and transaction handling.
|
||||
- `fs` and `child_process`: Used to run tutorial setup scripts.
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/deposit.js" language="js" from="import xrpl" before="// You can replace" /%}
|
||||
{% code-snippet file="/_code-samples/vaults/js/deposit.js" language="js" before="// You can replace" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
This example imports:
|
||||
|
||||
- `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.
|
||||
|
||||
{% code-snippet file="/_code-samples/vaults/py/deposit.py" language="py" before="# You can replace" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
@@ -73,10 +95,18 @@ Provide the depositing account and specify the vault details. The depositor must
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/deposit.js" language="js" from="// You can replace" before="// Get initial vault" /%}
|
||||
|
||||
This example uses an existing vault, depositor account, and MPT from the `vaultSetup.js` script, but you can replace these values with your own.
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/deposit.py" language="py" from="# You can replace" before="# Get initial vault" /%}
|
||||
|
||||
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.
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
This example uses an existing vault, depositor account, and MPT from the `vaultSetup.js` script, but you can replace these values with your own. The preconfigured depositor account has:
|
||||
The preconfigured depositor account has:
|
||||
|
||||
- Valid [Credentials](../../../../concepts/decentralized-storage/credentials.md) in the vault's [Permissioned Domain](../../../../concepts/tokens/decentralized-exchange/permissioned-domains.md).
|
||||
- A positive balance of the MPT in the vault.
|
||||
@@ -89,6 +119,10 @@ Use the [vault_info method][] to retrieve the vault's current state, including i
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/deposit.js" language="js" from="// Get initial vault" before="// Check depositor's asset balance" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/deposit.py" language="py" from="# Get initial vault" before="# Check depositor's asset balance" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
### 4. Check depositor's asset balance
|
||||
@@ -99,6 +133,10 @@ Before depositing, verify that the depositor has sufficient balance of the vault
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/deposit.js" language="js" from="// Check depositor's asset balance" before="// Prepare VaultDeposit" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/deposit.py" language="py" from="# Check depositor's asset balance" before="# Prepare VaultDeposit" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
### 5. Prepare VaultDeposit transaction
|
||||
@@ -108,10 +146,16 @@ Create a [VaultDeposit transaction][] object to deposit assets into the vault.
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/deposit.js" language="js" from="// Prepare VaultDeposit" before="// Submit VaultDeposit" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
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.
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/deposit.py" language="py" from="# Prepare VaultDeposit" before="# Submit VaultDeposit" /%}
|
||||
|
||||
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.
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
### 6. Submit VaultDeposit transaction
|
||||
|
||||
@@ -121,6 +165,10 @@ Submit the `VaultDeposit` transaction to the XRP Ledger.
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/deposit.js" language="js" from="// Submit VaultDeposit" before="// Extract vault state" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/deposit.py" language="py" from="# Submit VaultDeposit" before="# Extract vault state" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
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.
|
||||
@@ -142,6 +190,10 @@ After depositing, verify the vault's updated state. You can extract this informa
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/deposit.js" language="js" from="// Extract vault state" before="// Get the depositor's" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/deposit.py" language="py" from="# Extract vault state" before="# Get the depositor's" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
Finally, check that the depositing account has received the shares.
|
||||
@@ -150,6 +202,10 @@ Finally, check that the depositing account has received the shares.
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/deposit.js" language="js" from="// Get the depositor's" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/deposit.py" language="py" from="# Get the depositor's" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
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`).
|
||||
|
||||
@@ -29,7 +29,8 @@ 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](./deposit-into-a-vault.md).
|
||||
- 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](../../../../tutorials/javascript/build-apps/get-started.md) for setup steps.
|
||||
- **JavaScript** with the [xrpl.js library][]. See [Get Started Using JavaScript][] for setup steps.
|
||||
- **Python** with the [xrpl-py library][]. See [Get Started Using Python][] for setup steps.
|
||||
|
||||
## Source Code
|
||||
|
||||
@@ -41,38 +42,65 @@ You can find the complete source code for this tutorial's examples in the {% rep
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
From the code sample folder, use npm to install dependencies:
|
||||
From the code sample folder, use `npm` to install dependencies:
|
||||
|
||||
```bash
|
||||
npm install xrpl
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% tab label="Python" %}
|
||||
From the code sample folder, use `pip` to install dependencies:
|
||||
|
||||
```bash
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install xrpl-py
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
### 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.
|
||||
To get started, import the necessary libraries and instantiate a client to connect to the XRPL.
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
This example imports:
|
||||
|
||||
- `xrpl`: Used for XRPL client connection and transaction handling.
|
||||
|
||||
{% code-snippet file="/_code-samples/vaults/js/withdraw.js" language="js" before="// You can replace" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
This example imports:
|
||||
|
||||
- `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.
|
||||
|
||||
{% code-snippet file="/_code-samples/vaults/py/withdraw.py" language="py" before="# You can replace" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
Provide the depositor account and specify the vault details.
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/withdraw.js" language="js" from="You can replace" before="// Get initial vault" /%}
|
||||
{% code-snippet file="/_code-samples/vaults/js/withdraw.js" language="js" from="// You can replace" before="console.log" /%}
|
||||
|
||||
This example uses preconfigured accounts and vault data from the `vaultSetup.js` script, but you can replace these values with your own.
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/withdraw.py" language="py" from="# You can replace" before="print" /%}
|
||||
|
||||
This example uses preconfigured accounts and vault data from the `vault_setup.py` script, but you can replace these values with your own.
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
This example uses preconfigured accounts and vault data from the `vaultSetup.js` script, but you can replace `depositor`, `vaultID`, `assetMPTIssuanceId`, and `shareMPTIssuanceId` with your own values.
|
||||
|
||||
### 3. Check initial vault state
|
||||
|
||||
Before withdrawing, check the vault's current state to see its total assets and available liquidity.
|
||||
@@ -81,6 +109,10 @@ Before withdrawing, check the vault's current state to see its total assets and
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/withdraw.js" language="js" from="// Get initial vault" before="// Check depositor's share balance" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/withdraw.py" language="py" from="# Get initial vault" before="# Check depositor's share balance" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
### 4. Check share balance
|
||||
@@ -91,6 +123,10 @@ Verify that the depositor account has vault shares to redeem. If not, the transa
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/withdraw.js" language="js" from="// Check depositor's share balance" before="// Prepare VaultWithdraw" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/withdraw.py" language="py" from="# Check depositor's share balance" before="# Prepare VaultWithdraw" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
### 5. Prepare VaultWithdraw transaction
|
||||
@@ -100,15 +136,21 @@ Create a [VaultWithdraw transaction][] to withdraw assets from the vault.
|
||||
{% tabs %}
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/withdraw.js" language="js" from="// Prepare VaultWithdraw" before="// Submit VaultWithdraw" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
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:
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/withdraw.py" language="py" from="# Prepare VaultWithdraw" before="# Submit VaultWithdraw" /%}
|
||||
|
||||
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:
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
- **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.
|
||||
While not required, you can provide a destination account to receive the assets; if omitted, assets go to the account submitting the transaction.
|
||||
|
||||
{% admonition type="info" name="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.
|
||||
@@ -122,6 +164,10 @@ Submit the `VaultWithdraw` transaction to the XRP Ledger.
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/withdraw.js" language="js" from="// Submit VaultWithdraw " before="// Extract vault state" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/withdraw.py" language="py" from="# Submit VaultWithdraw" before="# Extract vault state" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
When the transaction succeeds:
|
||||
@@ -141,6 +187,10 @@ After withdrawing, check the vault's state. You can extract this information dir
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/withdraw.js" language="js" from="// Extract vault state" before="// Get the depositor's share balance" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/withdraw.py" language="py" from="# Extract vault state" before="# Get the depositor's share balance" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
Then, check the depositor's share balance:
|
||||
@@ -149,6 +199,10 @@ Then, check the depositor's share balance:
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/withdraw.js" language="js" from="// Get the depositor's share balance" before="// Get the depositor's asset balance" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/withdraw.py" language="py" from="# Get the depositor's share balance" before="# Get the depositor's asset balance" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
Finally, verify the correct asset amount has been received by the depositor account:
|
||||
@@ -157,6 +211,10 @@ Finally, verify the correct asset amount has been received by the depositor acco
|
||||
{% tab label="JavaScript" %}
|
||||
{% code-snippet file="/_code-samples/vaults/js/withdraw.js" language="js" from="// Get the depositor's asset balance" /%}
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Python" %}
|
||||
{% code-snippet file="/_code-samples/vaults/py/withdraw.py" language="py" from="# Get the depositor's asset balance" /%}
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
|
||||
## See Also
|
||||
|
||||
Reference in New Issue
Block a user