Skip to content
Last updated
Edit

Deposit and Withdraw First-Loss Capital

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

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:

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

From the code sample folder, use npm to install dependencies:

npm install xrpl

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

3. Prepare LoanBrokerCoverDeposit transaction

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.

4. Submit LoanBrokerCoverDeposit transaction

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.

5. Check cover balance after deposit

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.

6. Prepare LoanBrokerCoverWithdraw transaction

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))

7. Submit LoanBrokerCoverWithdraw transaction

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.

8. Check cover balance after withdrawal

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.

See Also

Concepts:

Tutorials:

References: