Skip to content
Last updated
Edit

This tutorial shows you how to create a Loan on the XRP Ledger. A loan requires signatures from both the loan broker and the borrower to be created.

This tutorial demonstrates how a loan broker and a borrower can cosign the terms of a loan and create that loan on the XRPL.

Requires the LendingProtocol amendment. Loading...

Goals

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

  • Create a LoanSet transaction with loan terms.
  • Sign and add the loan broker's signature to the transaction.
  • Sign and add the borrower's signature to the transaction.
  • Submit the cosigned transaction to create a loan.

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 creates a loan using a preconfigured
// loan broker, borrower, and private vault.

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, borrower account, and loan broker 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 borrower = xrpl.Wallet.fromSeed(setupData.borrower.seed)
const loanBrokerID = setupData.loanBrokerID

console.log(`\nLoan broker address: ${loanBroker.address}`)
console.log(`Borrower address: ${borrower.address}`)
console.log(`LoanBrokerID: ${loanBrokerID}`)

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

3. Prepare LoanSet transaction

Create the LoanSet transaction object with the loan terms.

// Prepare LoanSet transaction ----------------------
// Account and Counterparty accounts can be swapped, but determines signing order.
// Account signs first, Counterparty signs second.
console.log(`\n=== Preparing LoanSet transaction ===\n`)

// Suppress unnecessary console warning from autofilling LoanSet.
console.warn = () => {}

const loanSetTx = await client.autofill({
  TransactionType: 'LoanSet',
  Account: loanBroker.address,
  Counterparty: borrower.address,
  LoanBrokerID: loanBrokerID,
  PrincipalRequested: '1000',
  InterestRate: 500,
  PaymentTotal: 12,
  PaymentInterval: 2592000,
  GracePeriod: 604800,
  LoanOriginationFee: '100',
  LoanServiceFee: '10'
})

console.log(JSON.stringify(loanSetTx, null, 2))

The Account field is the loan broker, and the Counterparty field is the borrower. These fields can be swapped, but determine the signing order: the Account signs first, and the Counterparty signs second.

The loan terms include:

  • PrincipalRequested: The amount of an asset requested by the borrower. You don't have to specify the type of asset in this field.
  • InterestRate: The annualized interest rate in 1/10th basis points (500 = 0.5%).
  • PaymentTotal: The number of payments to be made.
  • PaymentInterval: The number of seconds between payments (2592000 = 30 days).
  • GracePeriod: The number of seconds after a missed payment before the loan can be defaulted (604800 = 7 days).
  • LoanOriginationFee: A one-time fee charged when the loan is created, paid in the borrowed asset.
  • LoanServiceFee: A fee charged with every loan payment, paid in the borrowed asset.

4. Add loan broker signature

The loan broker (the Account) signs the transaction first, adding their TxnSignature and SigningPubKey to the LoanSet transaction object.

// Loan broker signs first
console.log(`\n=== Adding loan broker signature ===\n`)
const loanBrokerSigned = loanBroker.sign(loanSetTx)
const loanBrokerSignedTx = xrpl.decode(loanBrokerSigned.tx_blob)

console.log(`TxnSignature: ${loanBrokerSignedTx.TxnSignature}`)
console.log(`SigningPubKey: ${loanBrokerSignedTx.SigningPubKey}\n`)
console.log(`Signed loanSetTx for borrower to sign over:\n${JSON.stringify(loanBrokerSignedTx, null, 2)}`)

5. Add borrower signature

The borrower (the Counterparty) signs the transaction second. Their TxnSignature and SigningPubKey are stored in a CounterpartySignature field, which is added to the LoanSet transaction object.

// Borrower signs second
console.log(`\n=== Adding borrower signature ===\n`)
const fullySigned = xrpl.signLoanSetByCounterparty(borrower, loanBrokerSignedTx)

console.log(`Borrower TxnSignature: ${fullySigned.tx.CounterpartySignature.TxnSignature}`)
console.log(`Borrower SigningPubKey: ${fullySigned.tx.CounterpartySignature.SigningPubKey}`)

// Validate the transaction structure before submitting.
xrpl.validate(fullySigned.tx)
console.log(`\nFully signed LoanSet transaction:\n${JSON.stringify(fullySigned.tx, null, 2)}`)

6. Submit LoanSet transaction

Submit the fully signed LoanSet transaction to the XRP Ledger.

// Submit and wait for validation ----------------------
console.log(`\n=== Submitting signed LoanSet transaction ===\n`)

const submitResponse = await client.submitAndWait(fullySigned.tx)

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

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

7. Get loan information

Retrieve the loan's information from the transaction result by checking for the Loan entry in the transaction metadata.

// Extract loan information from the transaction result.
console.log(`\n=== Loan Information ===\n`)
const loanNode = submitResponse.result.meta.AffectedNodes.find(node =>
  node.CreatedNode?.LedgerEntryType === 'Loan'
)
console.log(JSON.stringify(loanNode.CreatedNode.NewFields, null, 2))

await client.disconnect()

See Also

Concepts:

Tutorials:

References: