# Create a Loan This tutorial shows you how to create a [Loan](/docs/references/protocol/ledger-data/ledger-entry-types/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. LendingProtocol ## 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: - 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](https://github.com/XRPLF/xrpl.js). See [Get Started Using JavaScript](/docs/tutorials/javascript/build-apps/get-started) for setup steps. ## 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 JavaScript From the code sample folder, use npm to install dependencies: ```bash 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. JavaScript // 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. JavaScript // 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](/docs/references/protocol/transactions/types/loanset) object with the loan terms: JavaScript // 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, using the [sign method](/docs/references/http-websocket-apis/admin-api-methods/signing-methods/sign): JavaScript // Loan broker signs first console.log(`\n=== Adding loan broker signature ===\n`) const loanBrokerSignature = await client.request({ command: 'sign', tx_json: loanSetTx, secret: loanBroker.seed }) const loanBrokerSignatureResult = loanBrokerSignature.result.tx_json console.log(`TxnSignature: ${loanBrokerSignatureResult.TxnSignature}`) console.log(`SigningPubKey: ${loanBrokerSignatureResult.SigningPubKey}\n`) console.log(`Signed loanSetTx for borrower to sign over:\n${JSON.stringify(loanBrokerSignatureResult, null, 2)}`) The loan broker adds their `TxnSignature` and `SigningPubKey` to the `LoanSet` transaction object. ### 5. Add borrower signature The borrower (the `Counterparty`) signs the transaction second, using the [sign method](/docs/references/http-websocket-apis/admin-api-methods/signing-methods/sign): JavaScript // Borrower signs second console.log(`\n=== Adding borrower signature ===\n`) const borrowerSignature = await client.request({ command: 'sign', tx_json: loanBrokerSignatureResult, secret: borrower.seed, signature_target: 'CounterpartySignature' }) const borrowerSignatureResult = borrowerSignature.result.tx_json console.log(`Borrower TxnSignature: ${borrowerSignatureResult.CounterpartySignature.TxnSignature}`) console.log(`Borrower SigningPubKey: ${borrowerSignatureResult.CounterpartySignature.SigningPubKey}`) // Validate the transaction structure before submitting. xrpl.validate(borrowerSignatureResult) console.log(`\nFully signed LoanSet transaction:\n${JSON.stringify(borrowerSignatureResult, null, 2)}`) The borrower must specify `signature_target: 'CounterpartySignature'`. This adds the borrower's signatures to a `CounterpartySignature` object, which includes the borrower's `TxnSignature` and `SigningPubKey`. ### 6. Submit LoanSet transaction Sign and submit the fully signed `LoanSet` transaction to the XRP Ledger. JavaScript // Submit and wait for validation ---------------------- console.log(`\n=== Submitting signed LoanSet transaction ===\n`) // Submit the transaction const submitResult = await client.submit(borrowerSignatureResult) const txHash = submitResult.result.tx_json.hash // Helper function to check tx hash is validated async function validateTx (hash, maxRetries = 20) { for (let i = 0; i < maxRetries; i++) { await new Promise(resolve => setTimeout(resolve, 1000)) try { const tx = await client.request({ command: 'tx', transaction: hash }) if (tx.result.validated) { return tx } } catch (error) { // Transaction not validated yet, check again } } console.error(`Error: Transaction ${hash} not validated after ${maxRetries} attempts.`) await client.disconnect() process.exit(1) } // Validate the transaction const submitResponse = await validateTx(txHash) 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. JavaScript // 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**: - [Lending Protocol](/docs/concepts/tokens/lending-protocol) **Tutorials**: - [Create a Loan Broker](/docs/tutorials/how-tos/set-up-lending/use-the-lending-protocol/create-a-loan-broker) - [Manage a Loan](/docs/tutorials/how-tos/set-up-lending/use-the-lending-protocol/manage-a-loan) **References**: - [LoanSet transaction](/docs/references/protocol/transactions/types/loanset)