Skip to content
Last updated
Edit

This tutorial shows you how to pay off a Loan and delete it. Loans can only be deleted after they are fully paid off, or if they've been defaulted by the loan broker.

The tutorial demonstrates how to calculate the final payment due, which includes the loan balance and any additional fees, and then pay off the loan. After the loan is fully paid off, the loan is deleted, completely removing it from the XRP Ledger.

Requires the LendingProtocol amendment. Loading...

Goals

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

  • Check the outstanding balance on a loan.
  • Calculate the total payment due, including additional fees.
  • Submit a loan payment.
  • Delete a paid off loan from the XRP Ledger.

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 setup scripts.
// IMPORTANT: This example pays off an existing loan and then deletes it.

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 borrower account, loan 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 LoanID.
const setupData = JSON.parse(fs.readFileSync('lendingSetup.json', 'utf8'))

// You can replace these values with your own
const borrower = xrpl.Wallet.fromSeed(setupData.borrower.seed)
const loanID = setupData.loanID2
const mptID = setupData.mptID

console.log(`\nBorrower address: ${borrower.address}`)
console.log(`LoanID: ${loanID}`)
console.log(`MPT ID: ${mptID}`)

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

3. Check loan status

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

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

const totalValueOutstanding = loanStatus.result.node.TotalValueOutstanding
const loanServiceFee = loanStatus.result.node.LoanServiceFee
const totalPayment = (BigInt(totalValueOutstanding) + BigInt(loanServiceFee)).toString()

console.log(`Amount Owed: ${totalValueOutstanding} TSTUSD`)
console.log(`Loan Service Fee: ${loanServiceFee} TSTUSD`)
console.log(`Total Payment Due (including fees): ${totalPayment} TSTUSD`)

The TotalValueOutstanding field contains the remaining principal plus accrued interest; the LoanServiceFee is an additional fee charged per payment. Add these together to calculate the total payment.

Note

Other fees can be charged on a loan, such as late or early payment fees. These additional fees must be accounted for when calculating payment amounts.

4. Prepare LoanPay transaction

Create the LoanPay transaction with the total payment amount:

// Prepare LoanPay transaction ----------------------
console.log(`\n=== Preparing LoanPay transaction ===\n`)

const loanPayTx = {
  TransactionType: 'LoanPay',
  Account: borrower.address,
  LoanID: loanID,
  Amount: {
    mpt_issuance_id: mptID,
    value: totalPayment
  }
}

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

5. Submit LoanPay transaction

Sign and submit the LoanPay transaction to the XRP Ledger:

// Sign, submit, and wait for payment validation ----------------------
console.log(`\n=== Submitting LoanPay transaction ===\n`)
const payResponse = await client.submitAndWait(loanPayTx, {
  wallet: borrower,
  autofill: true
})

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

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

6. Check loan balance

Retrieve the loan balance from the transaction result by checking for the Loan entry in the transaction metadata:

// Extract updated loan info from transaction results ----------------------
console.log(`\n=== Loan Status After Payment ===\n`)
const loanNode = payResponse.result.meta.AffectedNodes.find(node =>
  node.ModifiedNode?.LedgerEntryType === 'Loan'
)

const finalBalance = loanNode.ModifiedNode.FinalFields.TotalValueOutstanding
  ? `${loanNode.ModifiedNode.FinalFields.TotalValueOutstanding} TSTUSD`
  : 'Loan fully paid off!'
console.log(`Outstanding Loan Balance: ${finalBalance}`)

If TotalValueOutstanding is absent from the loan metadata, the loan has been fully paid off and is ready for deletion.

7. Prepare LoanDelete transaction

Create a LoanDelete transaction to remove the paid loan from the XRP Ledger:

// Prepare LoanDelete transaction ----------------------
// Either the loan broker or borrower can submit this transaction.
console.log(`\n=== Preparing LoanDelete transaction ===\n`)
const loanDeleteTx = {
  TransactionType: 'LoanDelete',
  Account: borrower.address,
  LoanID: loanID
}

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

Either the loan broker or the borrower can submit a LoanDelete transaction. In this example, the borrower deletes their own paid off loan.

8. Submit LoanDelete transaction

Sign and submit the LoanDelete transaction:

// Sign, submit, and wait for deletion validation ----------------------
console.log(`\n=== Submitting LoanDelete transaction ===\n`)
const deleteResponse = await client.submitAndWait(loanDeleteTx, {
  wallet: borrower,
  autofill: true
})

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

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

9. Verify loan deletion

Confirm that the loan has been removed from the XRP Ledger:

// Verify loan deletion ----------------------
console.log(`\n=== Verifying Loan Deletion ===\n`)
try {
  await client.request({
    command: 'ledger_entry',
    index: loanID,
    ledger_index: 'validated'
  })
  console.log('Warning: Loan still exists in the ledger.')
} catch (error) {
  if (error.data.error === 'entryNotFound') {
    console.log('Loan has been successfully removed from the XRP Ledger!')
  } else {
    console.error('Error checking loan status:', error)
  }
}

await client.disconnect()

If the ledger_entry method returns an entryNotFound error, the loan has been successfully deleted.

See Also

Concepts:

Tutorials:

References: