# Deposit and Withdraw First-Loss Capital This tutorial shows you how to deposit and withdraw first-loss capital from a [LoanBroker](/docs/references/protocol/ledger-data/ledger-entry-types/loanbroker) on the XRP Ledger. First-loss capital helps protect vault depositor's assets, acting as a buffer in the event of loan defaults. The tutorial demonstrates how a loan broker can manage risk by depositing XRP as first-loss capital, and how they can withdraw it when needed. LendingProtocol ## Goals By the end of this tutorial, you will be able to: - Deposit an MPT as first-loss capital into a `LoanBroker` entry. - Check the available cover balance in the loan broker's pseudo-account. - Withdraw first-loss capital from a `LoanBroker` entry. ## Prerequisites To complete this tutorial, you should: - Have a basic understanding of the XRP Ledger. - Have an XRP Ledger client library set up in your development environment. This page provides examples for the following: - **JavaScript** with the [xrpl.js library](https://github.com/XRPLF/xrpl.js). See [Get Started Using JavaScript](/docs/tutorials/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 python3 -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, transaction submission, and wallet handling. - `fs` and `child_process`: Used to run tutorial set up scripts. // IMPORTANT: This example deposits and withdraws first-loss capital from a // preconfigured LoanBroker entry. import fs from 'fs' import { execSync } from 'child_process' import xrpl from 'xrpl' // Connect to the network ---------------------- const client = new xrpl.Client('wss://s.devnet.rippletest.net:51233') await client.connect() Python - `xrpl`: Used for XRPL client connection, transaction submission, and wallet handling. - `json`: Used for loading and formatting JSON data. - `os`, `subprocess`, and `sys`: Used to run tutorial set up scripts. # IMPORTANT: This example deposits and withdraws first-loss capital from a # preconfigured LoanBroker entry. import json import os import subprocess import sys from xrpl.clients import JsonRpcClient from xrpl.models import LoanBrokerCoverDeposit, LoanBrokerCoverWithdraw, MPTAmount from xrpl.transaction import submit_and_wait from xrpl.wallet import Wallet # Set up client ---------------------- client = JsonRpcClient("https://s.devnet.rippletest.net:51234") Next, load the loan broker account, loan broker ID, and MPT issuance ID. JavaScript // This step checks for the necessary setup data to run the lending protocol tutorials. // If missing, lendingSetup.js will generate the data. if (!fs.existsSync('lendingSetup.json')) { console.log(`\n=== Lending tutorial data doesn't exist. Running setup script... ===\n`) execSync('node lendingSetup.js', { stdio: 'inherit' }) } // Load preconfigured accounts and LoanBrokerID. const setupData = JSON.parse(fs.readFileSync('lendingSetup.json', 'utf8')) // You can replace these values with your own const loanBroker = xrpl.Wallet.fromSeed(setupData.loanBroker.seed) const loanBrokerID = setupData.loanBrokerID const mptID = setupData.mptID console.log(`\nLoan broker address: ${loanBroker.address}`) console.log(`LoanBrokerID: ${loanBrokerID}`) console.log(`MPT ID: ${mptID}`) This example uses preconfigured accounts and loan broker data from the `lendingSetup.js` script, but you can replace `loanBroker`, `loanBrokerID`, and `mptID` with your own values. Python # This step checks for the necessary setup data to run the lending protocol tutorials. # If missing, lending_setup.py will generate the data. if not os.path.exists("lending_setup.json"): print("\n=== Lending tutorial data doesn't exist. Running setup script... ===\n") subprocess.run([sys.executable, "lending_setup.py"], check=True) # Load preconfigured accounts and loan_broker_id. with open("lending_setup.json") as f: setup_data = json.load(f) # You can replace these values with your own. loan_broker = Wallet.from_seed(setup_data["loan_broker"]["seed"]) loan_broker_id = setup_data["loan_broker_id"] mpt_id = setup_data["mpt_id"] print(f"\nLoan broker address: {loan_broker.address}") print(f"LoanBrokerID: {loan_broker_id}") print(f"MPT ID: {mpt_id}") This example uses preconfigured accounts and loan broker data from the `lending_setup.py` script, but you can replace `loan_broker`, `loan_broker_id`, and `mpt_id` with your own values. ### 3. Prepare LoanBrokerCoverDeposit transaction Create the [LoanBrokerCoverDeposit transaction](/docs/references/protocol/transactions/types/loanbrokercoverdeposit) object. JavaScript // Prepare LoanBrokerCoverDeposit transaction ---------------------- console.log(`\n=== Preparing LoanBrokerCoverDeposit transaction ===\n`) const coverDepositTx = { TransactionType: 'LoanBrokerCoverDeposit', Account: loanBroker.address, LoanBrokerID: loanBrokerID, Amount: { mpt_issuance_id: mptID, value: '2000' } } // Validate the transaction structure before submitting xrpl.validate(coverDepositTx) console.log(JSON.stringify(coverDepositTx, null, 2)) The `Amount` field specifies the MPT and amount to deposit as first-loss capital. Python # Prepare LoanBrokerCoverDeposit transaction ---------------------- print("\n=== Preparing LoanBrokerCoverDeposit transaction ===\n") cover_deposit_tx = LoanBrokerCoverDeposit( account=loan_broker.address, loan_broker_id=loan_broker_id, amount=MPTAmount(mpt_issuance_id=mpt_id, value="2000"), ) print(json.dumps(cover_deposit_tx.to_xrpl(), indent=2)) The `amount` field specifies the MPT and amount to deposit as first-loss capital. If the transaction succeeds, the amount is deposited and held in the pseudo-account associated with the `LoanBroker` entry. ### 4. Submit LoanBrokerCoverDeposit transaction Sign and submit the `LoanBrokerCoverDeposit` transaction to the XRP Ledger. JavaScript // Sign, submit, and wait for deposit validation ---------------------- console.log(`\n=== Submitting LoanBrokerCoverDeposit transaction ===\n`) const depositResponse = await client.submitAndWait(coverDepositTx, { wallet: loanBroker, autofill: true }) if (depositResponse.result.meta.TransactionResult !== 'tesSUCCESS') { const resultCode = depositResponse.result.meta.TransactionResult console.error('Error: Unable to deposit cover:', resultCode) await client.disconnect() process.exit(1) } console.log('Cover deposit successful!') Python # Sign, submit, and wait for deposit validation ---------------------- print("\n=== Submitting LoanBrokerCoverDeposit transaction ===\n") deposit_response = submit_and_wait(cover_deposit_tx, client, loan_broker) if deposit_response.result["meta"]["TransactionResult"] != "tesSUCCESS": result_code = deposit_response.result["meta"]["TransactionResult"] print(f"Error: Unable to deposit cover: {result_code}") sys.exit(1) print("Cover deposit successful!") Verify that the transaction succeeded by checking for a `tesSUCCESS` result code. ### 5. Check cover balance after deposit Retrieve the cover balance from the transaction result by checking the `LoanBroker` entry in the transaction metadata. JavaScript // Extract cover balance from the transaction result console.log(`\n=== Cover Balance ===\n`) let loanBrokerNode = depositResponse.result.meta.AffectedNodes.find(node => node.ModifiedNode?.LedgerEntryType === 'LoanBroker' ) // First-loss capital is stored in the LoanBroker's pseudo-account. console.log(`LoanBroker Pseudo-Account: ${loanBrokerNode.ModifiedNode.FinalFields.Account}`) console.log(`Cover balance after deposit: ${loanBrokerNode.ModifiedNode.FinalFields.CoverAvailable} TSTUSD`) Python # Extract cover balance from the transaction result print("\n=== Cover Balance ===\n") loan_broker_node = next( node for node in deposit_response.result["meta"]["AffectedNodes"] if node.get("ModifiedNode", {}).get("LedgerEntryType") == "LoanBroker" ) # First-loss capital is stored in the LoanBroker's pseudo-account. print(f"LoanBroker Pseudo-Account: {loan_broker_node['ModifiedNode']['FinalFields']['Account']}") print(f"Cover balance after deposit: {loan_broker_node['ModifiedNode']['FinalFields']['CoverAvailable']} TSTUSD") The `LoanBroker` pseudo-account address is the `Account` field, and `CoverAvailable` shows the cover balance. ### 6. Prepare LoanBrokerCoverWithdraw transaction Create the [LoanBrokerCoverWithdraw transaction](/docs/references/protocol/transactions/types/loanbrokercoverwithdraw) object. JavaScript // Prepare LoanBrokerCoverWithdraw transaction ---------------------- console.log(`\n=== Preparing LoanBrokerCoverWithdraw transaction ===\n`) const coverWithdrawTx = { TransactionType: 'LoanBrokerCoverWithdraw', Account: loanBroker.address, LoanBrokerID: loanBrokerID, Amount: { mpt_issuance_id: mptID, value: '1000' } } // Validate the transaction structure before submitting xrpl.validate(coverWithdrawTx) console.log(JSON.stringify(coverWithdrawTx, null, 2)) Python # Prepare LoanBrokerCoverWithdraw transaction ---------------------- print("\n=== Preparing LoanBrokerCoverWithdraw transaction ===\n") cover_withdraw_tx = LoanBrokerCoverWithdraw( account=loan_broker.address, loan_broker_id=loan_broker_id, amount=MPTAmount(mpt_issuance_id=mpt_id, value="1000"), ) print(json.dumps(cover_withdraw_tx.to_xrpl(), indent=2)) ### 7. Submit LoanBrokerCoverWithdraw transaction Sign and submit the `LoanBrokerCoverWithdraw` transaction to the XRP Ledger. JavaScript // Sign, submit, and wait for withdraw validation ---------------------- console.log(`\n=== Submitting LoanBrokerCoverWithdraw transaction ===\n`) const withdrawResponse = await client.submitAndWait(coverWithdrawTx, { wallet: loanBroker, autofill: true }) if (withdrawResponse.result.meta.TransactionResult !== 'tesSUCCESS') { const resultCode = withdrawResponse.result.meta.TransactionResult console.error('Error: Unable to withdraw cover:', resultCode) await client.disconnect() process.exit(1) } console.log('Cover withdraw successful!') Python # Sign, submit, and wait for withdraw validation ---------------------- print("\n=== Submitting LoanBrokerCoverWithdraw transaction ===\n") withdraw_response = submit_and_wait(cover_withdraw_tx, client, loan_broker) if withdraw_response.result["meta"]["TransactionResult"] != "tesSUCCESS": result_code = withdraw_response.result["meta"]["TransactionResult"] print(f"Error: Unable to withdraw cover: {result_code}") sys.exit(1) print("Cover withdraw successful!") Verify that the transaction succeeded by checking for a `tesSUCCESS` result code. ### 8. Check cover balance after withdrawal Retrieve the updated cover balance from the transaction result. JavaScript // Extract updated cover balance from the transaction result console.log(`\n=== Updated Cover Balance ===\n`) loanBrokerNode = withdrawResponse.result.meta.AffectedNodes.find(node => node.ModifiedNode?.LedgerEntryType === 'LoanBroker' ) console.log(`LoanBroker Pseudo-Account: ${loanBrokerNode.ModifiedNode.FinalFields.Account}`) console.log(`Cover balance after withdraw: ${loanBrokerNode.ModifiedNode.FinalFields.CoverAvailable} TSTUSD`) await client.disconnect() Python # Extract updated cover balance from the transaction result print("\n=== Updated Cover Balance ===\n") loan_broker_node = next( node for node in withdraw_response.result["meta"]["AffectedNodes"] if node.get("ModifiedNode", {}).get("LedgerEntryType") == "LoanBroker" ) print(f"LoanBroker Pseudo-Account: {loan_broker_node['ModifiedNode']['FinalFields']['Account']}") print(f"Cover balance after withdraw: {loan_broker_node['ModifiedNode']['FinalFields']['CoverAvailable']} TSTUSD") The `CoverAvailable` field now shows the reduced balance after the withdrawal. ## See Also **Concepts**: - [Lending Protocol](/docs/concepts/tokens/lending-protocol) **Tutorials**: - [Create a Loan Broker](/docs/tutorials/defi/lending/use-the-lending-protocol/create-a-loan-broker) **References**: - [LoanBrokerCoverDeposit transaction](/docs/references/protocol/transactions/types/loanbrokercoverdeposit) - [LoanBrokerCoverWithdraw transaction](/docs/references/protocol/transactions/types/loanbrokercoverwithdraw)