コンテンツへスキップ

Manage a Loan

This tutorial shows you how to manage a Loan on the XRP Ledger. Loan management includes marking loans as impaired when payments are missed, defaulting loans after the grace period expires, and deleting repaid or defaulted loans.

The tutorial demonstrates how a loan broker can impair a loan, then default the loan after the grace period expires.

LendingProtocol amendmentが必要です。 Loading...

LendingProtocolV1_1 amendmentが必要です。 Loading...

Note
  • JavaScript code expects both LendingProtocol and LendingProtocolV1_1.
  • Python and Go code expect only LendingProtocol.

Goals

By the end of this tutorial, you will be able to:

  • Check the status of an existing loan.
  • Manually impair a loan to speed up the default process.
  • Wait through the loan's grace period.
  • Default a loan after the grace period expires.

Prerequisites

To complete this tutorial, you should:

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, transaction submission, and wallet handling.
  • fs and child_process: Used to run tutorial set up scripts.
// IMPORTANT: This example impairs an existing loan, which has a 60 second grace period.
// After the 60 seconds pass, this example defaults the loan.

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 and loan 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 LoanID.
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 loanID = setupData.loanID1

console.log(`\nLoan broker address: ${loanBroker.address}`)
console.log(`LoanID: ${loanID}`)

This example uses preconfigured accounts and loan data from the lendingSetup.js script, but you can replace loanBroker and loanID with your own values.

3. Check loan status

Check the current status of the loan using the ledger_entry method.

// Check loan status before impairment ----------------------
console.log(`\n=== Loan Status ===\n`)
const loanStatus = await client.request({
  command: 'ledger_entry',
  index: loanID,
  ledger_index: 'validated'
})

console.log(`Total Amount Owed: ${loanStatus.result.node.TotalValueOutstanding} TSTUSD.`)
// Convert Ripple Epoch timestamp to local date and time
const nextPaymentDueDate = loanStatus.result.node.NextPaymentDueDate
const paymentDue = new Date(xrpl.rippleTimeToUnixTime(nextPaymentDueDate))
const gracePeriod = loanStatus.result.node.GracePeriod
console.log(`Payment Due Date: ${paymentDue.toLocaleString()}`)
console.log(`Grace Period: ${gracePeriod} seconds`)

This shows the total amount owed and the next payment due date. The Ripple Epoch timestamp is converted to a readable date format.

4. Wait for late payment

Loans can only be impaired after missing a payment.

// Countdown until the loan can be impaired ----------------------
// A loan can only be impaired once its payment is late.
// Lateness is measured against the ledger's close time.
console.log(`\n=== Countdown until loan can be impaired ===\n`)

let latestLedger = await client.request({
  command: 'ledger',
  ledger_index: 'validated'
})

for (let secondsLeft = nextPaymentDueDate - latestLedger.result.ledger.close_time + 1; secondsLeft > 0; secondsLeft--) {
  process.stdout.write(`\x1b[K\r${secondsLeft} seconds...`)
  await new Promise((resolve) => setTimeout(resolve, 1000))
}

// The countdown runs on the local clock, so confirm the ledger has caught up.
while (true) {
  latestLedger = await client.request({ command: 'ledger', ledger_index: 'validated' })
  if (latestLedger.result.ledger.close_time > nextPaymentDueDate) break
  await new Promise((resolve) => setTimeout(resolve, 1000))
}
process.stdout.write('\x1b[K\rPayment is late. Loan can now be impaired.\n')

The countdown displays the expected remaining seconds in real-time, but is validated against the ledger's actual close time.

5. Prepare LoanManage transaction to impair the loan

Create the LoanManage transaction with the tfLoanImpair flag.

// Prepare LoanManage transaction to impair the loan ----------------------
console.log(`\n=== Preparing LoanManage transaction to impair loan ===\n`)
const loanManageImpair = {
  TransactionType: 'LoanManage',
  Account: loanBroker.address,
  LoanID: loanID,
  Flags: xrpl.LoanManageFlags.tfLoanImpair
}

// Validate the impairment transaction before submitting
xrpl.validate(loanManageImpair)
console.log(JSON.stringify(loanManageImpair, null, 2))

6. Submit LoanManage impairment transaction

Sign and submit the LoanManage transaction to impair the loan.

// Sign, submit, and wait for impairment validation ----------------------
console.log(`\n=== Submitting LoanManage impairment transaction ===\n`)
const impairResponse = await client.submitAndWait(loanManageImpair, {
  wallet: loanBroker,
  autofill: true
})

if (impairResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
  const resultCode = impairResponse.result.meta.TransactionResult
  console.error('Error: Unable to impair loan:', resultCode)
  await client.disconnect()
  process.exit(1)
}
console.log('Loan impaired successfully!')

Verify that the transaction succeeded by checking for a tesSUCCESS result code.

7. Get loan impairment information

Retrieve the loan's grace period and updated payment due date from the transaction result by checking for the Loan entry in the transaction metadata.

This step is required if only LendingProtocol is enabled. The JavaScript code is written for both LendingProtocol and LendingProtocolV1_1, which can skip this step.

8. Wait for grace period to expire

Loans can only be defaulted when the grace period expires after the missed payment due date.

// Countdown until the loan can be defaulted ----------------------
// A loan can only be defaulted once the grace period has elapsed
// past the payment due date. Measured against the ledger's close time.
console.log(`\n=== Countdown until loan can be defaulted ===\n`)

latestLedger = await client.request({
  command: 'ledger',
  ledger_index: 'validated'
})

const defaultTime = nextPaymentDueDate + gracePeriod
for (let secondsLeft = defaultTime - latestLedger.result.ledger.close_time + 1; secondsLeft > 0; secondsLeft--) {
  process.stdout.write(`\x1b[K\r${secondsLeft} seconds...`)
  await new Promise((resolve) => setTimeout(resolve, 1000))
}

// The countdown runs on the local clock, so confirm the ledger has caught up.
while (true) {
  latestLedger = await client.request({ command: 'ledger', ledger_index: 'validated' })
  if (latestLedger.result.ledger.close_time > defaultTime) break
  await new Promise((resolve) => setTimeout(resolve, 1000))
}
process.stdout.write('\x1b[K\rGrace period expired. Loan can now be defaulted.\n')

The countdown displays the expected remaining seconds in real-time, but is validated against the ledger's actual close time.

9. Prepare LoanManage transaction to default the loan

After the grace period expires, create a LoanManage transaction with the tfLoanDefault flag.

// Prepare LoanManage transaction to default the loan ----------------------
console.log(`\n=== Preparing LoanManage transaction to default loan ===\n`)
const loanManageDefault = {
  TransactionType: 'LoanManage',
  Account: loanBroker.address,
  LoanID: loanID,
  Flags: xrpl.LoanManageFlags.tfLoanDefault
}

// Validate the default transaction before submitting
xrpl.validate(loanManageDefault)
console.log(JSON.stringify(loanManageDefault, null, 2))

10. Submit LoanManage default transaction

Sign and submit the LoanManage transaction to default the loan.

// Sign, submit, and wait for default validation ----------------------
console.log(`\n=== Submitting LoanManage default transaction ===\n`)
const defaultResponse = await client.submitAndWait(loanManageDefault, {
  wallet: loanBroker,
  autofill: true
})

if (defaultResponse.result.meta.TransactionResult !== 'tesSUCCESS') {
  const resultCode = defaultResponse.result.meta.TransactionResult
  console.error('Error: Unable to default loan:', resultCode)
  await client.disconnect()
  process.exit(1)
}
console.log('Loan defaulted successfully!')

Verify that the transaction succeeded by checking for a tesSUCCESS result code.

11. Verify loan default status

Confirm the loan has been defaulted by checking the loan flags.

// Verify loan default status from transaction results ----------------------
console.log(`\n=== Checking final loan status ===\n`)
const loanNode = defaultResponse.result.meta.AffectedNodes.find(node =>
  node.ModifiedNode?.LedgerEntryType === 'Loan'
)
const loanFlags = loanNode.ModifiedNode.FinalFields.Flags
console.log(`Final loan flags (parsed): ${JSON.stringify(xrpl.parseTransactionFlags({
  TransactionType: 'LoanManage',
  Flags: loanFlags
}))}`)

await client.disconnect()

The loan flags are parsed to confirm the tfLoanDefault flag is now set.

See Also

Concepts:

Tutorials:

References: