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...
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.
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.
- Python with the xrpl-py library. See Get Started Using Python 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, transaction submission, and wallet handling.fsandchild_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.
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.
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)}`)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)}`)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.
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()Concepts:
Tutorials:
References: