This tutorial shows you how to deposit and withdraw first-loss capital from a 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.
Requires the LendingProtocol amendment. Loading...
By the end of this tutorial, you will be able to:
- Deposit an MPT as first-loss capital into a
LoanBrokerentry. - Check the available cover balance in the loan broker's pseudo-account.
- Withdraw first-loss capital from a
LoanBrokerentry.
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. See Get Started Using JavaScript for setup steps.
You can find the complete source code for this tutorial's examples in the code samples section of this website's repository.
From the code sample folder, use npm to install dependencies:
npm install xrplTo 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.fsandchild_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()Next, load the loan broker account, loan broker ID, and MPT issuance ID.
// 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.
Create the LoanBrokerCoverDeposit transaction object:
// 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. If the transaction succeeds, the amount is deposited and held in the pseudo-account associated with the LoanBroker entry.
Sign and submit the LoanBrokerCoverDeposit transaction to the XRP Ledger.
// 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!')Verify that the transaction succeeded by checking for a tesSUCCESS result code.
Retrieve the cover balance from the transaction result by checking the LoanBroker entry in the transaction metadata.
// 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`)The LoanBroker pseudo-account address is the Account field, and CoverAvailable shows the cover balance.
Create the LoanBrokerCoverWithdraw transaction object:
// 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))Sign and submit the LoanBrokerCoverWithdraw transaction to the XRP Ledger.
// 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!')Verify that the transaction succeeded by checking for a tesSUCCESS result code.
Retrieve the updated cover balance from the transaction result.
// 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()The CoverAvailable field now shows the reduced balance after the withdrawal.
Concepts:
Tutorials:
References: