Compare commits

..

2 Commits

Author SHA1 Message Date
mDuo13
9f4fcc845f Update Redocly plugin syntax for 0.131+ 2026-03-19 12:26:23 -07:00
mDuo13
26c966fa51 Bump Redocly to Realm 0.131.0 2026-03-18 11:12:41 -07:00
22 changed files with 652 additions and 2837 deletions

1
.gitignore vendored
View File

@@ -8,7 +8,6 @@ yarn-error.log
*.iml
.venv/
_code-samples/*/js/package-lock.json
_code-samples/*/go/go.sum
_code-samples/*/*/*[Ss]etup.json
# PHP

View File

@@ -1,6 +1,6 @@
// @ts-check
import { getInnerText } from '@redocly/realm/dist/server/plugins/markdown/markdoc/helpers/get-inner-text.js';
import { getInnerText } from '@redocly/realm/dist/markdoc/helpers/get-inner-text.js';
import { dirname, relative, join as joinPath } from 'path';
import markdoc from '@markdoc/markdoc';

View File

@@ -1,12 +1,13 @@
// @ts-check
import { getInnerText } from '@redocly/realm/dist/server/plugins/markdown/markdoc/helpers/get-inner-text.js';
import { getInnerText } from '@redocly/realm/dist/markdoc/helpers/get-inner-text.js';
import { dirname, relative, join as joinPath } from 'path';
export function codeSamples() {
/** @type {import("@redocly/realm/dist/server/plugins/types").PluginInstance } */
/** @type {import("@redocly/realm/dist/server/types").ExternalPlugin } */
const instance = {
id: 'code-samples',
processContent: async (actions, { fs, cache }) => {
try {
const samples = [];

View File

@@ -3,8 +3,9 @@ import { readSharedData } from '@redocly/realm/dist/server/utils/shared-data.js'
const INDEX_PAGE_INFO_DATA_KEY = 'index-page-items';
export function indexPages() {
/** @type {import("@redocly/realm/dist/server/plugins/types").PluginInstance } */
/** @type {import("@redocly/realm/dist/server/types").ExternalPlugin } */
const instance = {
id: 'index-pages',
// hook that gets executed after all routes were created
async afterRoutesCreated(actions, { cache }) {
// get all the routes that are ind pages

View File

@@ -1,395 +0,0 @@
# Lending Protocol Examples (Go)
This directory contains Go examples demonstrating how to create a loan broker, claw back first-loss capital, deposit and withdraw first-loss capital, create a loan, manage a loan, and repay a loan.
## Setup
All commands should be run from this `go/` directory.
Install dependencies before running any examples:
```sh
go mod tidy
```
---
## Create a Loan Broker
```sh
go run ./create-loan-broker
```
The script should output the LoanBrokerSet transaction, loan broker ID, and loan broker pseudo-account.
```sh
Loan broker/vault owner address: rLsTX2RjNTqwiwNpMn7mny3MyrXtmbhFQV
Vault ID: A300D6F7D43E1B143683F1917EE6456B0C3E84F0F763D9A1366FCD22138A11E9
=== Preparing LoanBrokerSet transaction ===
{
"Account": "rLsTX2RjNTqwiwNpMn7mny3MyrXtmbhFQV",
"ManagementFeeRate": 1000,
"TransactionType": "LoanBrokerSet",
"VaultID": "A300D6F7D43E1B143683F1917EE6456B0C3E84F0F763D9A1366FCD22138A11E9"
}
=== Submitting LoanBrokerSet transaction ===
Loan broker created successfully!
=== Loan Broker Information ===
LoanBroker ID: E4D9C485E101FAE449C8ACEC7FD039920CC02D2443687F2593DB397CC8EA670B
LoanBroker Pseudo-Account Address: rMDsnf9CVRLRJzrL12Ex7nhstbni78y8af
```
---
## Claw Back First-loss Capital
```sh
go run ./cover-clawback
```
The script should output the cover available, the LoanBrokerCoverDeposit transaction, cover available after the deposit, the LoanBrokerCoverClawback transaction, and the final cover available after the clawback.
```sh
Loan broker address: rLsTX2RjNTqwiwNpMn7mny3MyrXtmbhFQV
MPT issuer address: rfYxCEWxA9ACyvpciPZYbKujjLEVX5CCwW
LoanBrokerID: 373BEBB8A1EF735FCD330C2B0DDF2C37FD3B1589B084C94F2CA52A904FBED08D
MPT ID: 003A9D5247DC1C9997DB5500A84C3EC748F3F61D2BC56D51
=== Cover Available ===
0 TSTUSD
=== Preparing LoanBrokerCoverDeposit transaction ===
{
"Account": "rLsTX2RjNTqwiwNpMn7mny3MyrXtmbhFQV",
"Amount": {
"mpt_issuance_id": "003A9D5247DC1C9997DB5500A84C3EC748F3F61D2BC56D51",
"value": "1000"
},
"LoanBrokerID": "373BEBB8A1EF735FCD330C2B0DDF2C37FD3B1589B084C94F2CA52A904FBED08D",
"TransactionType": "LoanBrokerCoverDeposit"
}
=== Submitting LoanBrokerCoverDeposit transaction ===
Cover deposit successful!
=== Cover Available After Deposit ===
1000 TSTUSD
=== Verifying Asset Issuer ===
MPT issuer account verified: rfYxCEWxA9ACyvpciPZYbKujjLEVX5CCwW. Proceeding to clawback.
=== Preparing LoanBrokerCoverClawback transaction ===
{
"Account": "rfYxCEWxA9ACyvpciPZYbKujjLEVX5CCwW",
"Amount": {
"mpt_issuance_id": "003A9D5247DC1C9997DB5500A84C3EC748F3F61D2BC56D51",
"value": "1000"
},
"LoanBrokerID": "373BEBB8A1EF735FCD330C2B0DDF2C37FD3B1589B084C94F2CA52A904FBED08D",
"TransactionType": "LoanBrokerCoverClawback"
}
=== Submitting LoanBrokerCoverClawback transaction ===
Successfully clawed back 1000 TSTUSD!
=== Final Cover Available After Clawback ===
0 TSTUSD
```
---
## Deposit and Withdraw First-loss Capital
```sh
go run ./cover-deposit-and-withdraw
```
The script should output the LoanBrokerCoverDeposit, cover balance after the deposit, the LoanBrokerCoverWithdraw transaction, and the cover balance after the withdrawal.
```sh
Loan broker address: rU9ANkvdSCs7p59Guf2XzGrrCPSMM2tDQV
LoanBrokerID: 633375BCB82DB1440189D3E0721AF92B0888304717DA1B002C8824D631E97DC3
MPT ID: 003B6A9EE92357082A44FA2EAA26385E2F85071634BC3315
=== Preparing LoanBrokerCoverDeposit transaction ===
{
"Account": "rU9ANkvdSCs7p59Guf2XzGrrCPSMM2tDQV",
"Amount": {
"mpt_issuance_id": "003B6A9EE92357082A44FA2EAA26385E2F85071634BC3315",
"value": "2000"
},
"LoanBrokerID": "633375BCB82DB1440189D3E0721AF92B0888304717DA1B002C8824D631E97DC3",
"TransactionType": "LoanBrokerCoverDeposit"
}
=== Submitting LoanBrokerCoverDeposit transaction ===
Cover deposit successful!
=== Cover Balance ===
LoanBroker Pseudo-Account: rJoTTaGKQr8o475xKNZkEPRsmTbUkr6sbi
Cover balance after deposit: 2000 TSTUSD
=== Preparing LoanBrokerCoverWithdraw transaction ===
{
"Account": "rU9ANkvdSCs7p59Guf2XzGrrCPSMM2tDQV",
"Amount": {
"mpt_issuance_id": "003B6A9EE92357082A44FA2EAA26385E2F85071634BC3315",
"value": "1000"
},
"LoanBrokerID": "633375BCB82DB1440189D3E0721AF92B0888304717DA1B002C8824D631E97DC3",
"TransactionType": "LoanBrokerCoverWithdraw"
}
=== Submitting LoanBrokerCoverWithdraw transaction ===
Cover withdraw successful!
=== Updated Cover Balance ===
LoanBroker Pseudo-Account: rJoTTaGKQr8o475xKNZkEPRsmTbUkr6sbi
Cover balance after withdraw: 1000 TSTUSD
```
---
## Create a Loan
```sh
go run ./create-loan
```
The script should output the LoanSet transaction, the updated LoanSet transaction with the loan broker signature, the final LoanSet transaction with the borrower signature added, and then the loan information.
```sh
Loan broker address: rKHVvo9vMQwm2xz44qfhHyDC2VwYKfzgrX
Borrower address: rKU5hZEsT71BUgCnnPekEF3d2zn4AXsyqp
LoanBrokerID: 490DB29AD0CCFBDFE9A176F71AB7512497407CCA37E86F0C88CCDA1DF99A1F09
=== Preparing LoanSet transaction ===
{
"Account": "rKHVvo9vMQwm2xz44qfhHyDC2VwYKfzgrX",
"Counterparty": "rKU5hZEsT71BUgCnnPekEF3d2zn4AXsyqp",
"Fee": "2",
"GracePeriod": 604800,
"InterestRate": 500,
"LastLedgerSequence": 437349,
"LoanBrokerID": "490DB29AD0CCFBDFE9A176F71AB7512497407CCA37E86F0C88CCDA1DF99A1F09",
"LoanOriginationFee": "100",
"LoanServiceFee": "10",
"PaymentInterval": 2592000,
"PaymentTotal": 12,
"PrincipalRequested": "1000",
"Sequence": 6905,
"TransactionType": "LoanSet"
}
=== Adding loan broker signature ===
TxnSignature: 9984D6061F4B03734CDCC5A5367A928671FEE1486EFD335B6C875423FCB9FCEF2464F2A610B4DF31875567869696DC36D16F72AFB7D5F245B43C19415537F50F
SigningPubKey: ED378AB6DCCD0401D6DB3358A9135CE43455A57DAF0CBC459E8D7B6611193690B1
Signed loanSetTx for borrower to sign over:
{
"Account": "rKHVvo9vMQwm2xz44qfhHyDC2VwYKfzgrX",
"Counterparty": "rKU5hZEsT71BUgCnnPekEF3d2zn4AXsyqp",
"Fee": "2",
"GracePeriod": 604800,
"InterestRate": 500,
"LastLedgerSequence": 437349,
"LoanBrokerID": "490DB29AD0CCFBDFE9A176F71AB7512497407CCA37E86F0C88CCDA1DF99A1F09",
"LoanOriginationFee": "100",
"LoanServiceFee": "10",
"PaymentInterval": 2592000,
"PaymentTotal": 12,
"PrincipalRequested": "1000",
"Sequence": 6905,
"SigningPubKey": "ED378AB6DCCD0401D6DB3358A9135CE43455A57DAF0CBC459E8D7B6611193690B1",
"TransactionType": "LoanSet",
"TxnSignature": "9984D6061F4B03734CDCC5A5367A928671FEE1486EFD335B6C875423FCB9FCEF2464F2A610B4DF31875567869696DC36D16F72AFB7D5F245B43C19415537F50F"
}
=== Adding borrower signature ===
Borrower TxnSignature: 5578161BA5480216644D63428D4FAA6FC761BEA10D91FFB733636AB4EA7C6CC4E07E241BF5418D92FBE9F0133E97CC3E6A2CDC56C86C801438C1CBAC4497B005
Borrower SigningPubKey: ED2BF9FFE428F80E3E174476EA334E2109BAF6C7309BB08D56A6A97CE0432AD85E
Fully signed LoanSet transaction:
{
"Account": "rKHVvo9vMQwm2xz44qfhHyDC2VwYKfzgrX",
"Counterparty": "rKU5hZEsT71BUgCnnPekEF3d2zn4AXsyqp",
"CounterpartySignature": {
"SigningPubKey": "ED2BF9FFE428F80E3E174476EA334E2109BAF6C7309BB08D56A6A97CE0432AD85E",
"TxnSignature": "5578161BA5480216644D63428D4FAA6FC761BEA10D91FFB733636AB4EA7C6CC4E07E241BF5418D92FBE9F0133E97CC3E6A2CDC56C86C801438C1CBAC4497B005"
},
"Fee": "2",
"GracePeriod": 604800,
"InterestRate": 500,
"LastLedgerSequence": 437349,
"LoanBrokerID": "490DB29AD0CCFBDFE9A176F71AB7512497407CCA37E86F0C88CCDA1DF99A1F09",
"LoanOriginationFee": "100",
"LoanServiceFee": "10",
"PaymentInterval": 2592000,
"PaymentTotal": 12,
"PrincipalRequested": "1000",
"Sequence": 6905,
"SigningPubKey": "ED378AB6DCCD0401D6DB3358A9135CE43455A57DAF0CBC459E8D7B6611193690B1",
"TransactionType": "LoanSet",
"TxnSignature": "9984D6061F4B03734CDCC5A5367A928671FEE1486EFD335B6C875423FCB9FCEF2464F2A610B4DF31875567869696DC36D16F72AFB7D5F245B43C19415537F50F"
}
=== Submitting signed LoanSet transaction ===
Loan created successfully!
=== Loan Information ===
{
"Borrower": "rKU5hZEsT71BUgCnnPekEF3d2zn4AXsyqp",
"GracePeriod": 604800,
"InterestRate": 500,
"LoanBrokerID": "490DB29AD0CCFBDFE9A176F71AB7512497407CCA37E86F0C88CCDA1DF99A1F09",
"LoanOriginationFee": "100",
"LoanSequence": 4,
"LoanServiceFee": "10",
"NextPaymentDueDate": 829803181,
"PaymentInterval": 2592000,
"PaymentRemaining": 12,
"PeriodicPayment": "83.55610375293148956",
"PrincipalOutstanding": "1000",
"StartDate": 827211181,
"TotalValueOutstanding": "1003"
}
```
---
## Manage a Loan
```sh
go run ./loan-manage
```
The script should output the initial status of the loan, the LoanManage transaction, and the updated loan status and grace period after impairment. The script will countdown the grace period before outputting another LoanManage transaction, and then the final flags on the loan.
```sh
Loan broker address: rN7eCZhKHcq5LEC2W2RrrGcUPBYwZagEPX
LoanID: 2BD3F3F587D1BD4FB247B0935FB098E2DC6E3B571F493472CED914216990EC6C
=== Loan Status ===
Total Amount Owed: 1001 TSTUSD.
Payment Due Date: 2026-03-23 01:23:40
=== Preparing LoanManage transaction to impair loan ===
{
"Account": "rN7eCZhKHcq5LEC2W2RrrGcUPBYwZagEPX",
"Flags": 131072,
"LoanID": "2BD3F3F587D1BD4FB247B0935FB098E2DC6E3B571F493472CED914216990EC6C",
"TransactionType": "LoanManage"
}
=== Submitting LoanManage impairment transaction ===
Loan impaired successfully!
New Payment Due Date: 2026-02-21 00:24:10
Grace Period: 60 seconds
=== Countdown until loan can be defaulted ===
Grace period expired. Loan can now be defaulted.
=== Preparing LoanManage transaction to default loan ===
{
"Account": "rN7eCZhKHcq5LEC2W2RrrGcUPBYwZagEPX",
"Flags": 65536,
"LoanID": "2BD3F3F587D1BD4FB247B0935FB098E2DC6E3B571F493472CED914216990EC6C",
"TransactionType": "LoanManage"
}
=== Submitting LoanManage default transaction ===
Loan defaulted successfully!
=== Checking final loan status ===
Final loan flags: [tfLoanDefault tfLoanImpair]
```
---
## Pay a Loan
```sh
go run ./loan-pay
```
The script should output the amount required to totally pay off a loan, the LoanPay transaction, the amount due after the payment, the LoanDelete transaction, and then the status of the loan ledger entry.
```sh
Borrower address: rFx8s3P5J66MAvWkp5rMj5bBF76gQUCt2
LoanID: D0455CD5F9C2FEC62FC67F31BD97134FBA877D7FE1AE7130EE0006D10661325A
MPT ID: 003B8FC2F51C1BC4E0211E6370EC4FC78BB20D5C4069F07B
=== Loan Status ===
Amount Owed: 1001 TSTUSD
Loan Service Fee: 10 TSTUSD
Total Payment Due (including fees): 1011 TSTUSD
=== Preparing LoanPay transaction ===
{
"Account": "rFx8s3P5J66MAvWkp5rMj5bBF76gQUCt2",
"Amount": {
"mpt_issuance_id": "003B8FC2F51C1BC4E0211E6370EC4FC78BB20D5C4069F07B",
"value": "1011"
},
"LoanID": "D0455CD5F9C2FEC62FC67F31BD97134FBA877D7FE1AE7130EE0006D10661325A",
"TransactionType": "LoanPay"
}
=== Submitting LoanPay transaction ===
Loan paid successfully!
=== Loan Status After Payment ===
Outstanding Loan Balance: Loan fully paid off!
=== Preparing LoanDelete transaction ===
{
"Account": "rFx8s3P5J66MAvWkp5rMj5bBF76gQUCt2",
"LoanID": "D0455CD5F9C2FEC62FC67F31BD97134FBA877D7FE1AE7130EE0006D10661325A",
"TransactionType": "LoanDelete"
}
=== Submitting LoanDelete transaction ===
Loan deleted successfully!
=== Verifying Loan Deletion ===
Loan has been successfully removed from the XRP Ledger!
```

View File

@@ -1,210 +0,0 @@
// IMPORTANT: This example deposits and claws back first-loss capital from a
// preconfigured LoanBroker entry. The first-loss capital is an MPT
// with clawback enabled.
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"github.com/Peersyst/xrpl-go/xrpl/queries/common"
"github.com/Peersyst/xrpl-go/xrpl/queries/ledger"
"github.com/Peersyst/xrpl-go/xrpl/transaction"
"github.com/Peersyst/xrpl-go/xrpl/transaction/types"
"github.com/Peersyst/xrpl-go/xrpl/wallet"
"github.com/Peersyst/xrpl-go/xrpl/websocket"
wstypes "github.com/Peersyst/xrpl-go/xrpl/websocket/types"
)
// mptIssuanceEntryRequest looks up an MPTIssuance ledger entry by its MPT ID.
// The library's GetLedgerEntry() method only supports lookup by ledger entry ID,
// so this custom type is used with the generic Request() method.
type mptIssuanceEntryRequest struct {
common.BaseRequest
MPTIssuance string `json:"mpt_issuance"`
LedgerIndex common.LedgerSpecifier `json:"ledger_index,omitempty"`
}
func (*mptIssuanceEntryRequest) Method() string { return "ledger_entry" }
func (*mptIssuanceEntryRequest) Validate() error { return nil }
func (*mptIssuanceEntryRequest) APIVersion() int { return 2 }
func main() {
// Connect to the network ----------------------
client := websocket.NewClient(
websocket.NewClientConfig().
WithHost("wss://s.devnet.rippletest.net:51233"),
)
defer client.Disconnect()
if err := client.Connect(); err != nil {
panic(err)
}
// Check for setup data; run lending-setup if missing
if _, err := os.Stat("lending-setup.json"); os.IsNotExist(err) {
fmt.Printf("\n=== Lending tutorial data doesn't exist. Running setup script... ===\n\n")
cmd := exec.Command("go", "run", "./lending-setup")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}
}
// Load preconfigured accounts and LoanBrokerID
data, err := os.ReadFile("lending-setup.json")
if err != nil {
panic(err)
}
var setup map[string]any
if err := json.Unmarshal(data, &setup); err != nil {
panic(err)
}
// You can replace these values with your own
loanBrokerWallet, err := wallet.FromSecret(setup["loanBroker"].(map[string]any)["seed"].(string))
if err != nil {
panic(err)
}
mptIssuerWallet, err := wallet.FromSecret(setup["depositor"].(map[string]any)["seed"].(string))
if err != nil {
panic(err)
}
loanBrokerID := setup["loanBrokerID"].(string)
mptID := setup["mptID"].(string)
fmt.Printf("\nLoan broker address: %s\n", loanBrokerWallet.ClassicAddress)
fmt.Printf("MPT issuer address: %s\n", mptIssuerWallet.ClassicAddress)
fmt.Printf("LoanBrokerID: %s\n", loanBrokerID)
fmt.Printf("MPT ID: %s\n", mptID)
// Check cover available ----------------------
fmt.Printf("\n=== Cover Available ===\n\n")
coverInfo, err := client.GetLedgerEntry(&ledger.EntryRequest{
Index: loanBrokerID,
LedgerIndex: common.Validated,
})
if err != nil {
panic(err)
}
currentCoverAvailable := "0"
if ca, ok := coverInfo.Node["CoverAvailable"].(string); ok {
currentCoverAvailable = ca
}
fmt.Printf("%s TSTUSD\n", currentCoverAvailable)
// Prepare LoanBrokerCoverDeposit transaction ----------------------
fmt.Printf("\n=== Preparing LoanBrokerCoverDeposit transaction ===\n\n")
coverDepositTx := transaction.LoanBrokerCoverDeposit{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.ClassicAddress,
},
LoanBrokerID: loanBrokerID,
Amount: types.MPTCurrencyAmount{
MPTIssuanceID: mptID,
Value: "1000",
},
}
// Flatten() converts the struct to a map and adds the TransactionType field
flatCoverDepositTx := coverDepositTx.Flatten()
coverDepositTxJSON, _ := json.MarshalIndent(flatCoverDepositTx, "", " ")
fmt.Printf("%s\n", string(coverDepositTxJSON))
// Sign, submit, and wait for deposit validation ----------------------
fmt.Printf("\n=== Submitting LoanBrokerCoverDeposit transaction ===\n\n")
depositResponse, err := client.SubmitTxAndWait(flatCoverDepositTx, &wstypes.SubmitOptions{
Autofill: true,
Wallet: &loanBrokerWallet,
})
if err != nil {
panic(err)
}
if depositResponse.Meta.TransactionResult != "tesSUCCESS" {
fmt.Printf("Error: Unable to deposit cover: %s\n", depositResponse.Meta.TransactionResult)
os.Exit(1)
}
fmt.Printf("Cover deposit successful!\n")
// Extract updated cover available after deposit ----------------------
fmt.Printf("\n=== Cover Available After Deposit ===\n\n")
for _, node := range depositResponse.Meta.AffectedNodes {
if node.ModifiedNode != nil && node.ModifiedNode.LedgerEntryType == "LoanBroker" {
currentCoverAvailable = node.ModifiedNode.FinalFields["CoverAvailable"].(string)
break
}
}
fmt.Printf("%s TSTUSD\n", currentCoverAvailable)
// Verify issuer of cover asset matches ----------------------
// Only the issuer of the asset can submit clawback transactions.
// The asset must also have clawback enabled.
fmt.Printf("\n=== Verifying Asset Issuer ===\n\n")
assetIssuerInfo, err := client.Request(&mptIssuanceEntryRequest{
MPTIssuance: mptID,
LedgerIndex: common.Validated,
})
if err != nil {
panic(err)
}
issuer := assetIssuerInfo.Result["node"].(map[string]any)["Issuer"].(string)
if issuer != string(mptIssuerWallet.ClassicAddress) {
fmt.Printf("Error: %s does not match account (%s) attempting clawback!\n", issuer, mptIssuerWallet.ClassicAddress)
os.Exit(1)
}
fmt.Printf("MPT issuer account verified: %s. Proceeding to clawback.\n", mptIssuerWallet.ClassicAddress)
// Prepare LoanBrokerCoverClawback transaction ----------------------
fmt.Printf("\n=== Preparing LoanBrokerCoverClawback transaction ===\n\n")
lbID := types.LoanBrokerID(loanBrokerID)
coverClawbackTx := transaction.LoanBrokerCoverClawback{
BaseTx: transaction.BaseTx{
Account: mptIssuerWallet.ClassicAddress,
},
LoanBrokerID: &lbID,
Amount: types.MPTCurrencyAmount{
MPTIssuanceID: mptID,
Value: currentCoverAvailable,
},
}
flatCoverClawbackTx := coverClawbackTx.Flatten()
coverClawbackTxJSON, _ := json.MarshalIndent(flatCoverClawbackTx, "", " ")
fmt.Printf("%s\n", string(coverClawbackTxJSON))
// Sign, submit, and wait for clawback validation ----------------------
fmt.Printf("\n=== Submitting LoanBrokerCoverClawback transaction ===\n\n")
clawbackResponse, err := client.SubmitTxAndWait(flatCoverClawbackTx, &wstypes.SubmitOptions{
Autofill: true,
Wallet: &mptIssuerWallet,
})
if err != nil {
panic(err)
}
if clawbackResponse.Meta.TransactionResult != "tesSUCCESS" {
fmt.Printf("Error: Unable to clawback cover: %s\n", clawbackResponse.Meta.TransactionResult)
os.Exit(1)
}
fmt.Printf("Successfully clawed back %s TSTUSD!\n", currentCoverAvailable)
// Extract final cover available ----------------------
fmt.Printf("\n=== Final Cover Available After Clawback ===\n\n")
for _, node := range clawbackResponse.Meta.AffectedNodes {
if node.ModifiedNode != nil && node.ModifiedNode.LedgerEntryType == "LoanBroker" {
finalCover := "0"
if ca, ok := node.ModifiedNode.FinalFields["CoverAvailable"].(string); ok {
finalCover = ca
}
fmt.Printf("%s TSTUSD\n", finalCover)
break
}
}
}

View File

@@ -1,151 +0,0 @@
// IMPORTANT: This example deposits and withdraws first-loss capital from a
// preconfigured LoanBroker entry.
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"github.com/Peersyst/xrpl-go/xrpl/transaction"
"github.com/Peersyst/xrpl-go/xrpl/transaction/types"
"github.com/Peersyst/xrpl-go/xrpl/wallet"
"github.com/Peersyst/xrpl-go/xrpl/websocket"
wstypes "github.com/Peersyst/xrpl-go/xrpl/websocket/types"
)
func main() {
// Connect to the network ----------------------
client := websocket.NewClient(
websocket.NewClientConfig().
WithHost("wss://s.devnet.rippletest.net:51233"),
)
defer client.Disconnect()
if err := client.Connect(); err != nil {
panic(err)
}
// Check for setup data; run lending-setup if missing
if _, err := os.Stat("lending-setup.json"); os.IsNotExist(err) {
fmt.Printf("\n=== Lending tutorial data doesn't exist. Running setup script... ===\n\n")
cmd := exec.Command("go", "run", "./lending-setup")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}
}
// Load preconfigured accounts and LoanBrokerID
data, err := os.ReadFile("lending-setup.json")
if err != nil {
panic(err)
}
var setup map[string]any
if err := json.Unmarshal(data, &setup); err != nil {
panic(err)
}
// You can replace these values with your own
loanBrokerWallet, err := wallet.FromSecret(setup["loanBroker"].(map[string]any)["seed"].(string))
if err != nil {
panic(err)
}
loanBrokerID := setup["loanBrokerID"].(string)
mptID := setup["mptID"].(string)
fmt.Printf("\nLoan broker address: %s\n", loanBrokerWallet.ClassicAddress)
fmt.Printf("LoanBrokerID: %s\n", loanBrokerID)
fmt.Printf("MPT ID: %s\n", mptID)
// Prepare LoanBrokerCoverDeposit transaction ----------------------
fmt.Printf("\n=== Preparing LoanBrokerCoverDeposit transaction ===\n\n")
coverDepositTx := transaction.LoanBrokerCoverDeposit{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.ClassicAddress,
},
LoanBrokerID: loanBrokerID,
Amount: types.MPTCurrencyAmount{
MPTIssuanceID: mptID,
Value: "2000",
},
}
// Flatten() converts the struct to a map and adds the TransactionType field
flatCoverDepositTx := coverDepositTx.Flatten()
coverDepositTxJSON, _ := json.MarshalIndent(flatCoverDepositTx, "", " ")
fmt.Printf("%s\n", string(coverDepositTxJSON))
// Sign, submit, and wait for deposit validation ----------------------
fmt.Printf("\n=== Submitting LoanBrokerCoverDeposit transaction ===\n\n")
depositResponse, err := client.SubmitTxAndWait(flatCoverDepositTx, &wstypes.SubmitOptions{
Autofill: true,
Wallet: &loanBrokerWallet,
})
if err != nil {
panic(err)
}
if depositResponse.Meta.TransactionResult != "tesSUCCESS" {
fmt.Printf("Error: Unable to deposit cover: %s\n", depositResponse.Meta.TransactionResult)
os.Exit(1)
}
fmt.Printf("Cover deposit successful!\n")
// Extract cover balance from the transaction result
fmt.Printf("\n=== Cover Balance ===\n\n")
for _, node := range depositResponse.Meta.AffectedNodes {
if node.ModifiedNode != nil && node.ModifiedNode.LedgerEntryType == "LoanBroker" {
// First-loss capital is stored in the LoanBroker's pseudo-account.
fmt.Printf("LoanBroker Pseudo-Account: %s\n", node.ModifiedNode.FinalFields["Account"])
fmt.Printf("Cover balance after deposit: %s TSTUSD\n", node.ModifiedNode.FinalFields["CoverAvailable"])
break
}
}
// Prepare LoanBrokerCoverWithdraw transaction ----------------------
fmt.Printf("\n=== Preparing LoanBrokerCoverWithdraw transaction ===\n\n")
coverWithdrawTx := transaction.LoanBrokerCoverWithdraw{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.ClassicAddress,
},
LoanBrokerID: loanBrokerID,
Amount: types.MPTCurrencyAmount{
MPTIssuanceID: mptID,
Value: "1000",
},
}
flatCoverWithdrawTx := coverWithdrawTx.Flatten()
coverWithdrawTxJSON, _ := json.MarshalIndent(flatCoverWithdrawTx, "", " ")
fmt.Printf("%s\n", string(coverWithdrawTxJSON))
// Sign, submit, and wait for withdraw validation ----------------------
fmt.Printf("\n=== Submitting LoanBrokerCoverWithdraw transaction ===\n\n")
withdrawResponse, err := client.SubmitTxAndWait(flatCoverWithdrawTx, &wstypes.SubmitOptions{
Autofill: true,
Wallet: &loanBrokerWallet,
})
if err != nil {
panic(err)
}
if withdrawResponse.Meta.TransactionResult != "tesSUCCESS" {
fmt.Printf("Error: Unable to withdraw cover: %s\n", withdrawResponse.Meta.TransactionResult)
os.Exit(1)
}
fmt.Printf("Cover withdraw successful!\n")
// Extract updated cover balance from the transaction result
fmt.Printf("\n=== Updated Cover Balance ===\n\n")
for _, node := range withdrawResponse.Meta.AffectedNodes {
if node.ModifiedNode != nil && node.ModifiedNode.LedgerEntryType == "LoanBroker" {
fmt.Printf("LoanBroker Pseudo-Account: %s\n", node.ModifiedNode.FinalFields["Account"])
fmt.Printf("Cover balance after withdraw: %s TSTUSD\n", node.ModifiedNode.FinalFields["CoverAvailable"])
break
}
}
}

View File

@@ -1,105 +0,0 @@
// IMPORTANT: This example creates a loan broker using an existing account
// that has already created a PRIVATE vault.
// If you want to create a loan broker for a PUBLIC vault, you can replace the vaultID
// and loanBroker values with your own.
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"github.com/Peersyst/xrpl-go/xrpl/transaction"
"github.com/Peersyst/xrpl-go/xrpl/transaction/types"
"github.com/Peersyst/xrpl-go/xrpl/wallet"
"github.com/Peersyst/xrpl-go/xrpl/websocket"
wstypes "github.com/Peersyst/xrpl-go/xrpl/websocket/types"
)
func main() {
// Connect to the network ----------------------
client := websocket.NewClient(
websocket.NewClientConfig().
WithHost("wss://s.devnet.rippletest.net:51233"),
)
defer client.Disconnect()
if err := client.Connect(); err != nil {
panic(err)
}
// Check for setup data; run lending-setup if missing
if _, err := os.Stat("lending-setup.json"); os.IsNotExist(err) {
fmt.Printf("\n=== Lending tutorial data doesn't exist. Running setup script... ===\n\n")
cmd := exec.Command("go", "run", "./lending-setup")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}
}
// Load preconfigured accounts and VaultID
data, err := os.ReadFile("lending-setup.json")
if err != nil {
panic(err)
}
var setup map[string]any
if err := json.Unmarshal(data, &setup); err != nil {
panic(err)
}
// You can replace these values with your own
loanBrokerWallet, err := wallet.FromSecret(setup["loanBroker"].(map[string]any)["seed"].(string))
if err != nil {
panic(err)
}
vaultID := setup["vaultID"].(string)
fmt.Printf("\nLoan broker/vault owner address: %s\n", loanBrokerWallet.ClassicAddress)
fmt.Printf("Vault ID: %s\n", vaultID)
// Prepare LoanBrokerSet transaction ----------------------
fmt.Printf("\n=== Preparing LoanBrokerSet transaction ===\n\n")
mgmtFeeRate := types.InterestRate(1000)
loanBrokerSetTx := transaction.LoanBrokerSet{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.ClassicAddress,
},
VaultID: vaultID,
ManagementFeeRate: &mgmtFeeRate,
}
// Flatten() converts the struct to a map and adds the TransactionType field
flatLoanBrokerSetTx := loanBrokerSetTx.Flatten()
loanBrokerSetTxJSON, _ := json.MarshalIndent(flatLoanBrokerSetTx, "", " ")
fmt.Printf("%s\n", string(loanBrokerSetTxJSON))
// Submit, sign, and wait for validation ----------------------
fmt.Printf("\n=== Submitting LoanBrokerSet transaction ===\n\n")
loanBrokerSetResponse, err := client.SubmitTxAndWait(flatLoanBrokerSetTx, &wstypes.SubmitOptions{
Autofill: true,
Wallet: &loanBrokerWallet,
})
if err != nil {
panic(err)
}
if loanBrokerSetResponse.Meta.TransactionResult != "tesSUCCESS" {
fmt.Printf("Error: Unable to create loan broker: %s\n", loanBrokerSetResponse.Meta.TransactionResult)
os.Exit(1)
}
fmt.Printf("Loan broker created successfully!\n")
// Extract loan broker information from the transaction result
fmt.Printf("\n=== Loan Broker Information ===\n\n")
for _, node := range loanBrokerSetResponse.Meta.AffectedNodes {
if node.CreatedNode != nil && node.CreatedNode.LedgerEntryType == "LoanBroker" {
fmt.Printf("LoanBroker ID: %s\n", node.CreatedNode.LedgerIndex)
fmt.Printf("LoanBroker Pseudo-Account Address: %s\n", node.CreatedNode.NewFields["Account"])
break
}
}
}

View File

@@ -1,153 +0,0 @@
// IMPORTANT: This example creates a loan using a preconfigured
// loan broker, borrower, and private vault.
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"github.com/Peersyst/xrpl-go/xrpl/transaction"
"github.com/Peersyst/xrpl-go/xrpl/transaction/types"
"github.com/Peersyst/xrpl-go/xrpl/wallet"
"github.com/Peersyst/xrpl-go/xrpl/websocket"
)
// ptr is a helper that returns a pointer to the given value,
// used for setting optional transaction fields in Go.
func ptr[T any](v T) *T { return &v }
func main() {
// Connect to the network ----------------------
client := websocket.NewClient(
websocket.NewClientConfig().
WithHost("wss://s.devnet.rippletest.net:51233"),
)
defer client.Disconnect()
if err := client.Connect(); err != nil {
panic(err)
}
// Check for setup data; run lending-setup if missing
if _, err := os.Stat("lending-setup.json"); os.IsNotExist(err) {
fmt.Printf("\n=== Lending tutorial data doesn't exist. Running setup script... ===\n\n")
cmd := exec.Command("go", "run", "./lending-setup")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}
}
// Load preconfigured accounts and LoanBrokerID
data, err := os.ReadFile("lending-setup.json")
if err != nil {
panic(err)
}
var setup map[string]any
if err := json.Unmarshal(data, &setup); err != nil {
panic(err)
}
// You can replace these values with your own
loanBrokerWallet, err := wallet.FromSecret(setup["loanBroker"].(map[string]any)["seed"].(string))
if err != nil {
panic(err)
}
borrowerWallet, err := wallet.FromSecret(setup["borrower"].(map[string]any)["seed"].(string))
if err != nil {
panic(err)
}
loanBrokerID := setup["loanBrokerID"].(string)
fmt.Printf("\nLoan broker address: %s\n", loanBrokerWallet.ClassicAddress)
fmt.Printf("Borrower address: %s\n", borrowerWallet.ClassicAddress)
fmt.Printf("LoanBrokerID: %s\n", loanBrokerID)
// Prepare LoanSet transaction ----------------------
// Account and Counterparty accounts can be swapped, but determines signing order.
// Account signs first, Counterparty signs second.
fmt.Printf("\n=== Preparing LoanSet transaction ===\n\n")
counterparty := borrowerWallet.ClassicAddress
loanSetTx := transaction.LoanSet{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.ClassicAddress,
},
LoanBrokerID: loanBrokerID,
PrincipalRequested: "1000",
Counterparty: &counterparty,
InterestRate: ptr(types.InterestRate(500)),
PaymentTotal: ptr(types.PaymentTotal(12)),
PaymentInterval: ptr(types.PaymentInterval(2592000)),
GracePeriod: ptr(types.GracePeriod(604800)),
LoanOriginationFee: ptr(types.XRPLNumber("100")),
LoanServiceFee: ptr(types.XRPLNumber("10")),
}
// Flatten() converts the struct to a map and adds the TransactionType field.
// The result is cast to FlatTransaction, which is required by Autofill and signing methods.
flatLoanSetTx := transaction.FlatTransaction(loanSetTx.Flatten())
// Autofill the transaction
if err := client.Autofill(&flatLoanSetTx); err != nil {
panic(err)
}
loanSetTxJSON, _ := json.MarshalIndent(flatLoanSetTx, "", " ")
fmt.Printf("%s\n", string(loanSetTxJSON))
// Loan broker signs first
fmt.Printf("\n=== Adding loan broker signature ===\n\n")
_, _, err = loanBrokerWallet.Sign(flatLoanSetTx)
if err != nil {
panic(err)
}
fmt.Printf("TxnSignature: %s\n", flatLoanSetTx["TxnSignature"])
fmt.Printf("SigningPubKey: %s\n\n", flatLoanSetTx["SigningPubKey"])
loanBrokerSignedJSON, _ := json.MarshalIndent(flatLoanSetTx, "", " ")
fmt.Printf("Signed loanSetTx for borrower to sign over:\n%s\n", string(loanBrokerSignedJSON))
// Borrower signs second
fmt.Printf("\n=== Adding borrower signature ===\n\n")
fullySignedBlob, _, err := wallet.SignLoanSetByCounterparty(borrowerWallet, &flatLoanSetTx, nil)
if err != nil {
panic(err)
}
borrowerSignatures := flatLoanSetTx["CounterpartySignature"].(map[string]any)
fmt.Printf("Borrower TxnSignature: %s\n", borrowerSignatures["TxnSignature"])
fmt.Printf("Borrower SigningPubKey: %s\n", borrowerSignatures["SigningPubKey"])
fullySignedJSON, _ := json.MarshalIndent(flatLoanSetTx, "", " ")
fmt.Printf("\nFully signed LoanSet transaction:\n%s\n", string(fullySignedJSON))
// Submit and wait for validation ----------------------
fmt.Printf("\n=== Submitting signed LoanSet transaction ===\n\n")
loanSetResponse, err := client.SubmitTxBlobAndWait(fullySignedBlob, false)
if err != nil {
panic(err)
}
if loanSetResponse.Meta.TransactionResult != "tesSUCCESS" {
fmt.Printf("Error: Unable to create loan: %s\n", loanSetResponse.Meta.TransactionResult)
os.Exit(1)
}
fmt.Printf("Loan created successfully!\n")
// Extract loan information from the transaction result
fmt.Printf("\n=== Loan Information ===\n\n")
for _, node := range loanSetResponse.Meta.AffectedNodes {
if node.CreatedNode != nil && node.CreatedNode.LedgerEntryType == "Loan" {
loanJSON, _ := json.MarshalIndent(node.CreatedNode.NewFields, "", " ")
fmt.Printf("%s\n", string(loanJSON))
break
}
}
}

View File

@@ -1,20 +0,0 @@
module github.com/XRPLF
go 1.24.3
require github.com/Peersyst/xrpl-go v0.1.16
require (
github.com/bsv-blockchain/go-sdk v1.2.9 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/decred/dcrd/crypto/ripemd160 v1.0.2 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/crypto v0.44.0 // indirect
)

View File

@@ -1,637 +0,0 @@
// Setup script for lending protocol tutorials
package main
import (
"encoding/hex"
"encoding/json"
"fmt"
"os"
"time"
"github.com/Peersyst/xrpl-go/pkg/crypto"
"github.com/Peersyst/xrpl-go/xrpl/faucet"
ledger "github.com/Peersyst/xrpl-go/xrpl/ledger-entry-types"
requests "github.com/Peersyst/xrpl-go/xrpl/queries/transactions"
"github.com/Peersyst/xrpl-go/xrpl/rpc"
rpctypes "github.com/Peersyst/xrpl-go/xrpl/rpc/types"
"github.com/Peersyst/xrpl-go/xrpl/transaction"
"github.com/Peersyst/xrpl-go/xrpl/transaction/types"
"github.com/Peersyst/xrpl-go/xrpl/wallet"
)
// ptr is a helper that returns a pointer to the given value,
// used for setting optional transaction fields in Go.
func ptr[T any](v T) *T { return &v }
func main() {
fmt.Print("Setting up tutorial: 0/7\r")
// Connect to devnet
cfg, err := rpc.NewClientConfig(
"https://s.devnet.rippletest.net:51234",
rpc.WithFaucetProvider(faucet.NewDevnetFaucetProvider()),
rpc.WithTimeout(10*time.Second),
)
if err != nil {
panic(err)
}
client := rpc.NewClient(cfg)
submitOpts := func(w *wallet.Wallet) *rpctypes.SubmitOptions {
return &rpctypes.SubmitOptions{Autofill: true, Wallet: w}
}
// withTicket works around a library issue where Flatten() omits Sequence
// when it's 0 (Go zero value). Autofill then fills in the account sequence,
// conflicting with TicketSequence. This explicitly sets Sequence to 0 so
// Autofill skips it.
withTicket := func(flat transaction.FlatTransaction) transaction.FlatTransaction {
flat["Sequence"] = uint32(0)
return flat
}
// Create and fund wallets concurrently
createAndFund := func(ch chan<- wallet.Wallet) {
w, err := wallet.New(crypto.ED25519())
if err != nil {
panic(err)
}
if err := client.FundWallet(&w); err != nil {
panic(err)
}
ch <- w
}
lbWalletCh := make(chan wallet.Wallet, 1)
brWalletCh := make(chan wallet.Wallet, 1)
depWalletCh := make(chan wallet.Wallet, 1)
credWalletCh := make(chan wallet.Wallet, 1)
go createAndFund(lbWalletCh)
go createAndFund(brWalletCh)
go createAndFund(depWalletCh)
go createAndFund(credWalletCh)
loanBrokerWallet := <-lbWalletCh
borrowerWallet := <-brWalletCh
depositorWallet := <-depWalletCh
credIssuerWallet := <-credWalletCh
fmt.Print("Setting up tutorial: 1/7\r")
// Create tickets for parallel transactions
extractTickets := func(resp *requests.TxResponse) []int {
var tickets []int
for _, node := range resp.Meta.AffectedNodes {
if node.CreatedNode != nil && node.CreatedNode.LedgerEntryType == "Ticket" {
ticketSeq, _ := node.CreatedNode.NewFields["TicketSequence"].(json.Number).Int64()
tickets = append(tickets, int(ticketSeq))
}
}
return tickets
}
ciTicketCh := make(chan []int, 1)
lbTicketCh := make(chan []int, 1)
brTicketCh := make(chan []int, 1)
dpTicketCh := make(chan []int, 1)
go func() {
resp, err := client.SubmitTxAndWait((&transaction.TicketCreate{
BaseTx: transaction.BaseTx{
Account: credIssuerWallet.GetAddress(),
},
TicketCount: 4,
}).Flatten(), submitOpts(&credIssuerWallet))
if err != nil {
panic(err)
}
ciTicketCh <- extractTickets(resp)
}()
go func() {
resp, err := client.SubmitTxAndWait((&transaction.TicketCreate{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.GetAddress(),
},
TicketCount: 4,
}).Flatten(), submitOpts(&loanBrokerWallet))
if err != nil {
panic(err)
}
lbTicketCh <- extractTickets(resp)
}()
go func() {
resp, err := client.SubmitTxAndWait((&transaction.TicketCreate{
BaseTx: transaction.BaseTx{
Account: borrowerWallet.GetAddress(),
},
TicketCount: 2,
}).Flatten(), submitOpts(&borrowerWallet))
if err != nil {
panic(err)
}
brTicketCh <- extractTickets(resp)
}()
go func() {
resp, err := client.SubmitTxAndWait((&transaction.TicketCreate{
BaseTx: transaction.BaseTx{
Account: depositorWallet.GetAddress(),
},
TicketCount: 2,
}).Flatten(), submitOpts(&depositorWallet))
if err != nil {
panic(err)
}
dpTicketCh <- extractTickets(resp)
}()
ciTickets := <-ciTicketCh
lbTickets := <-lbTicketCh
brTickets := <-brTicketCh
dpTickets := <-dpTicketCh
fmt.Print("Setting up tutorial: 2/7\r")
// Issue MPT with depositor
// Set up credentials and domain with credentialIssuer
credentialType := hex.EncodeToString([]byte("KYC-Verified"))
mptData := types.ParsedMPTokenMetadata{
Ticker: "TSTUSD",
Name: "Test USD MPT",
Desc: ptr("A sample non-yield-bearing stablecoin backed by U.S. Treasuries."),
Icon: "https://example.org/tstusd-icon.png",
AssetClass: "rwa",
AssetSubclass: ptr("stablecoin"),
IssuerName: "Example Treasury Reserve Co.",
URIs: []types.ParsedMPTokenMetadataURI{
{
URI: "https://exampletreasury.com/tstusd",
Category: "website",
Title: "Product Page",
},
{
URI: "https://exampletreasury.com/tstusd/reserve",
Category: "docs",
Title: "Reserve Attestation",
},
},
AdditionalInfo: map[string]any{
"reserve_type": "U.S. Treasury Bills",
"custody_provider": "Example Custodian Bank",
"audit_frequency": "Monthly",
"last_audit_date": "2026-01-15",
"pegged_currency": "USD",
},
}
mptMetadataHex, err := types.EncodeMPTokenMetadata(mptData)
if err != nil {
panic(err)
}
mptCh := make(chan *requests.TxResponse, 1)
domainCh := make(chan *requests.TxResponse, 1)
credLbCh := make(chan struct{}, 1)
credBrCh := make(chan struct{}, 1)
credDpCh := make(chan struct{}, 1)
// MPT issuance
go func() {
resp, err := client.SubmitTxAndWait((&transaction.MPTokenIssuanceCreate{
BaseTx: transaction.BaseTx{
Account: depositorWallet.GetAddress(),
Flags: transaction.TfMPTCanTransfer | transaction.TfMPTCanClawback | transaction.TfMPTCanTrade,
},
MaximumAmount: ptr(types.XRPCurrencyAmount(100000000)),
TransferFee: ptr(uint16(0)),
MPTokenMetadata: &mptMetadataHex,
}).Flatten(), submitOpts(&depositorWallet))
if err != nil {
panic(err)
}
mptCh <- resp
}()
// PermissionedDomainSet
go func() {
resp, err := client.SubmitTxAndWait(withTicket((&transaction.PermissionedDomainSet{
BaseTx: transaction.BaseTx{
Account: credIssuerWallet.GetAddress(),
TicketSequence: uint32(ciTickets[0]),
},
AcceptedCredentials: types.AuthorizeCredentialList{
{
Credential: types.Credential{
Issuer: credIssuerWallet.GetAddress(),
CredentialType: types.CredentialType(credentialType),
},
},
},
}).Flatten()), submitOpts(&credIssuerWallet))
if err != nil {
panic(err)
}
domainCh <- resp
}()
// CredentialCreate for loan broker
go func() {
_, err := client.SubmitTxAndWait(withTicket((&transaction.CredentialCreate{
BaseTx: transaction.BaseTx{
Account: credIssuerWallet.GetAddress(),
TicketSequence: uint32(ciTickets[1]),
},
CredentialType: types.CredentialType(credentialType),
Subject: loanBrokerWallet.GetAddress(),
}).Flatten()), submitOpts(&credIssuerWallet))
if err != nil {
panic(err)
}
credLbCh <- struct{}{}
}()
// CredentialCreate for borrower
go func() {
_, err := client.SubmitTxAndWait(withTicket((&transaction.CredentialCreate{
BaseTx: transaction.BaseTx{
Account: credIssuerWallet.GetAddress(),
TicketSequence: uint32(ciTickets[2]),
},
CredentialType: types.CredentialType(credentialType),
Subject: borrowerWallet.GetAddress(),
}).Flatten()), submitOpts(&credIssuerWallet))
if err != nil {
panic(err)
}
credBrCh <- struct{}{}
}()
// CredentialCreate for depositor
go func() {
_, err := client.SubmitTxAndWait(withTicket((&transaction.CredentialCreate{
BaseTx: transaction.BaseTx{
Account: credIssuerWallet.GetAddress(),
TicketSequence: uint32(ciTickets[3]),
},
CredentialType: types.CredentialType(credentialType),
Subject: depositorWallet.GetAddress(),
}).Flatten()), submitOpts(&credIssuerWallet))
if err != nil {
panic(err)
}
credDpCh <- struct{}{}
}()
mptResp := <-mptCh
domainResp := <-domainCh
<-credLbCh
<-credBrCh
<-credDpCh
// Extract MPT issuance ID
mptID := string(*mptResp.Meta.MPTIssuanceID)
// Extract domain ID from transaction meta
var domainID string
for _, node := range domainResp.Meta.AffectedNodes {
if node.CreatedNode != nil && node.CreatedNode.LedgerEntryType == "PermissionedDomain" {
domainID = node.CreatedNode.LedgerIndex
break
}
}
fmt.Print("Setting up tutorial: 3/7\r")
// Accept credentials and authorize MPT for each account
lbCredCh := make(chan struct{}, 1)
lbMptCh := make(chan struct{}, 1)
brCredCh := make(chan struct{}, 1)
brMptCh := make(chan struct{}, 1)
depCredCh := make(chan struct{}, 1)
// Loan broker: accept credential
go func() {
_, err := client.SubmitTxAndWait(withTicket((&transaction.CredentialAccept{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.GetAddress(),
TicketSequence: uint32(lbTickets[0]),
},
CredentialType: types.CredentialType(credentialType),
Issuer: credIssuerWallet.GetAddress(),
}).Flatten()), submitOpts(&loanBrokerWallet))
if err != nil {
panic(err)
}
lbCredCh <- struct{}{}
}()
// Loan broker: authorize MPT
go func() {
_, err := client.SubmitTxAndWait(withTicket((&transaction.MPTokenAuthorize{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.GetAddress(),
TicketSequence: uint32(lbTickets[1]),
},
MPTokenIssuanceID: mptID,
}).Flatten()), submitOpts(&loanBrokerWallet))
if err != nil {
panic(err)
}
lbMptCh <- struct{}{}
}()
// Borrower: accept credential
go func() {
_, err := client.SubmitTxAndWait(withTicket((&transaction.CredentialAccept{
BaseTx: transaction.BaseTx{
Account: borrowerWallet.GetAddress(),
TicketSequence: uint32(brTickets[0]),
},
CredentialType: types.CredentialType(credentialType),
Issuer: credIssuerWallet.GetAddress(),
}).Flatten()), submitOpts(&borrowerWallet))
if err != nil {
panic(err)
}
brCredCh <- struct{}{}
}()
// Borrower: authorize MPT
go func() {
_, err := client.SubmitTxAndWait(withTicket((&transaction.MPTokenAuthorize{
BaseTx: transaction.BaseTx{
Account: borrowerWallet.GetAddress(),
TicketSequence: uint32(brTickets[1]),
},
MPTokenIssuanceID: mptID,
}).Flatten()), submitOpts(&borrowerWallet))
if err != nil {
panic(err)
}
brMptCh <- struct{}{}
}()
// Depositor: accept credential only
go func() {
_, err := client.SubmitTxAndWait((&transaction.CredentialAccept{
BaseTx: transaction.BaseTx{
Account: depositorWallet.GetAddress(),
},
CredentialType: types.CredentialType(credentialType),
Issuer: credIssuerWallet.GetAddress(),
}).Flatten(), submitOpts(&depositorWallet))
if err != nil {
panic(err)
}
depCredCh <- struct{}{}
}()
<-lbCredCh
<-lbMptCh
<-brCredCh
<-brMptCh
<-depCredCh
fmt.Print("Setting up tutorial: 4/7\r")
// Create private vault and distribute MPT to accounts concurrently
vaultCh := make(chan *requests.TxResponse, 1)
distLbCh := make(chan struct{}, 1)
distBrCh := make(chan struct{}, 1)
go func() {
resp, err := client.SubmitTxAndWait((&transaction.VaultCreate{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.GetAddress(),
Flags: transaction.TfVaultPrivate,
},
Asset: ledger.Asset{MPTIssuanceID: mptID},
DomainID: &domainID,
}).Flatten(), submitOpts(&loanBrokerWallet))
if err != nil {
panic(err)
}
vaultCh <- resp
}()
go func() {
_, err := client.SubmitTxAndWait(withTicket((&transaction.Payment{
BaseTx: transaction.BaseTx{
Account: depositorWallet.GetAddress(),
TicketSequence: uint32(dpTickets[0]),
},
Destination: loanBrokerWallet.GetAddress(),
Amount: types.MPTCurrencyAmount{
MPTIssuanceID: mptID,
Value: "5000",
},
}).Flatten()), submitOpts(&depositorWallet))
if err != nil {
panic(err)
}
distLbCh <- struct{}{}
}()
go func() {
_, err := client.SubmitTxAndWait(withTicket((&transaction.Payment{
BaseTx: transaction.BaseTx{
Account: depositorWallet.GetAddress(),
TicketSequence: uint32(dpTickets[1]),
},
Destination: borrowerWallet.GetAddress(),
Amount: types.MPTCurrencyAmount{
MPTIssuanceID: mptID,
Value: "2500",
},
}).Flatten()), submitOpts(&depositorWallet))
if err != nil {
panic(err)
}
distBrCh <- struct{}{}
}()
vaultResp := <-vaultCh
<-distLbCh
<-distBrCh
var vaultID string
for _, node := range vaultResp.Meta.AffectedNodes {
if node.CreatedNode != nil && node.CreatedNode.LedgerEntryType == "Vault" {
vaultID = node.CreatedNode.LedgerIndex
break
}
}
fmt.Print("Setting up tutorial: 5/7\r")
// Create LoanBroker and deposit MPT into vault
lbSetCh := make(chan *requests.TxResponse, 1)
vaultDepCh := make(chan struct{}, 1)
go func() {
resp, err := client.SubmitTxAndWait((&transaction.LoanBrokerSet{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.GetAddress(),
},
VaultID: vaultID,
}).Flatten(), submitOpts(&loanBrokerWallet))
if err != nil {
panic(err)
}
lbSetCh <- resp
}()
go func() {
_, err := client.SubmitTxAndWait((&transaction.VaultDeposit{
BaseTx: transaction.BaseTx{
Account: depositorWallet.GetAddress(),
},
VaultID: types.Hash256(vaultID),
Amount: types.MPTCurrencyAmount{
MPTIssuanceID: mptID,
Value: "50000000",
},
}).Flatten(), submitOpts(&depositorWallet))
if err != nil {
panic(err)
}
vaultDepCh <- struct{}{}
}()
lbSetResp := <-lbSetCh
<-vaultDepCh
var loanBrokerID string
for _, node := range lbSetResp.Meta.AffectedNodes {
if node.CreatedNode != nil && node.CreatedNode.LedgerEntryType == "LoanBroker" {
loanBrokerID = node.CreatedNode.LedgerIndex
break
}
}
fmt.Print("Setting up tutorial: 6/7\r")
// Create 2 identical loans with complete repayment due in 30 days
// Helper function to create, sign, and submit a LoanSet transaction
createLoan := func(ticketSequence int) *requests.TxResponse {
counterparty := borrowerWallet.GetAddress()
loanSetTx := &transaction.LoanSet{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.GetAddress(),
TicketSequence: uint32(ticketSequence),
},
LoanBrokerID: loanBrokerID,
PrincipalRequested: "1000",
Counterparty: &counterparty,
InterestRate: ptr(types.InterestRate(500)),
PaymentTotal: ptr(types.PaymentTotal(1)),
PaymentInterval: ptr(types.PaymentInterval(2592000)),
LoanOriginationFee: ptr(types.XRPLNumber("100")),
LoanServiceFee: ptr(types.XRPLNumber("10")),
}
flatTx := withTicket(loanSetTx.Flatten())
if err := client.Autofill(&flatTx); err != nil {
panic(err)
}
// Loan broker signs first
_, _, err := loanBrokerWallet.Sign(flatTx)
if err != nil {
panic(err)
}
// Borrower signs second
blob, _, err := wallet.SignLoanSetByCounterparty(borrowerWallet, &flatTx, nil)
if err != nil {
panic(err)
}
// Submit and wait for validation
resp, err := client.SubmitTxBlobAndWait(blob, false)
if err != nil {
panic(err)
}
return resp
}
loan1Ch := make(chan *requests.TxResponse, 1)
loan2Ch := make(chan *requests.TxResponse, 1)
go func() { loan1Ch <- createLoan(lbTickets[2]) }()
go func() { loan2Ch <- createLoan(lbTickets[3]) }()
loan1Resp := <-loan1Ch
loan2Resp := <-loan2Ch
var loanID1, loanID2 string
for _, node := range loan1Resp.Meta.AffectedNodes {
if node.CreatedNode != nil && node.CreatedNode.LedgerEntryType == "Loan" {
loanID1 = node.CreatedNode.LedgerIndex
break
}
}
for _, node := range loan2Resp.Meta.AffectedNodes {
if node.CreatedNode != nil && node.CreatedNode.LedgerEntryType == "Loan" {
loanID2 = node.CreatedNode.LedgerIndex
break
}
}
fmt.Print("Setting up tutorial: 7/7\r")
// Write setup data to JSON file.
// Using a struct to preserve field order.
setupData := struct {
Description string `json:"description"`
LoanBroker any `json:"loanBroker"`
Borrower any `json:"borrower"`
Depositor any `json:"depositor"`
CredentialIssuer any `json:"credentialIssuer"`
DomainID string `json:"domainID"`
MptID string `json:"mptID"`
VaultID string `json:"vaultID"`
LoanBrokerID string `json:"loanBrokerID"`
LoanID1 string `json:"loanID1"`
LoanID2 string `json:"loanID2"`
}{
Description: "This file is auto-generated by lending-setup. It stores XRPL account info for use in lending protocol tutorials.",
LoanBroker: map[string]string{
"address": string(loanBrokerWallet.ClassicAddress),
"seed": loanBrokerWallet.Seed,
},
Borrower: map[string]string{
"address": string(borrowerWallet.ClassicAddress),
"seed": borrowerWallet.Seed,
},
Depositor: map[string]string{
"address": string(depositorWallet.ClassicAddress),
"seed": depositorWallet.Seed,
},
CredentialIssuer: map[string]string{
"address": string(credIssuerWallet.ClassicAddress),
"seed": credIssuerWallet.Seed,
},
DomainID: domainID,
MptID: mptID,
VaultID: vaultID,
LoanBrokerID: loanBrokerID,
LoanID1: loanID1,
LoanID2: loanID2,
}
jsonData, err := json.MarshalIndent(setupData, "", " ")
if err != nil {
panic(err)
}
if err := os.WriteFile("lending-setup.json", jsonData, 0644); err != nil {
panic(err)
}
fmt.Println("Setting up tutorial: Complete!")
}

View File

@@ -1,195 +0,0 @@
// IMPORTANT: This example impairs an existing loan, which has a 60 second grace period.
// After the 60 seconds pass, this example defaults the loan.
package main
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"time"
"github.com/Peersyst/xrpl-go/xrpl/flag"
"github.com/Peersyst/xrpl-go/xrpl/queries/common"
"github.com/Peersyst/xrpl-go/xrpl/queries/ledger"
xrpltime "github.com/Peersyst/xrpl-go/xrpl/time"
"github.com/Peersyst/xrpl-go/xrpl/transaction"
"github.com/Peersyst/xrpl-go/xrpl/wallet"
"github.com/Peersyst/xrpl-go/xrpl/websocket"
wstypes "github.com/Peersyst/xrpl-go/xrpl/websocket/types"
)
func main() {
// Connect to the network ----------------------
client := websocket.NewClient(
websocket.NewClientConfig().
WithHost("wss://s.devnet.rippletest.net:51233"),
)
defer client.Disconnect()
if err := client.Connect(); err != nil {
panic(err)
}
// Check for setup data; run lending-setup if missing
if _, err := os.Stat("lending-setup.json"); os.IsNotExist(err) {
fmt.Printf("\n=== Lending tutorial data doesn't exist. Running setup script... ===\n\n")
cmd := exec.Command("go", "run", "./lending-setup")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}
}
// Load preconfigured accounts and LoanID
data, err := os.ReadFile("lending-setup.json")
if err != nil {
panic(err)
}
var setup map[string]any
if err := json.Unmarshal(data, &setup); err != nil {
panic(err)
}
// You can replace these values with your own
loanBrokerWallet, err := wallet.FromSecret(setup["loanBroker"].(map[string]any)["seed"].(string))
if err != nil {
panic(err)
}
loanID := setup["loanID1"].(string)
fmt.Printf("\nLoan broker address: %s\n", loanBrokerWallet.ClassicAddress)
fmt.Printf("LoanID: %s\n", loanID)
// Check loan status before impairment ----------------------
fmt.Printf("\n=== Loan Status ===\n\n")
loanStatus, err := client.GetLedgerEntry(&ledger.EntryRequest{
Index: loanID,
LedgerIndex: common.Validated,
})
if err != nil {
panic(err)
}
fmt.Printf("Total Amount Owed: %s TSTUSD.\n", loanStatus.Node["TotalValueOutstanding"])
// Convert Ripple Epoch timestamp to local date and time
nextPaymentDueDate := int64(loanStatus.Node["NextPaymentDueDate"].(float64))
paymentDue := time.UnixMilli(xrpltime.RippleTimeToUnixTime(nextPaymentDueDate))
fmt.Printf("Payment Due Date: %s\n", paymentDue.Local().Format(time.DateTime))
// Prepare LoanManage transaction to impair the loan ----------------------
fmt.Printf("\n=== Preparing LoanManage transaction to impair loan ===\n\n")
loanManageImpair := transaction.LoanManage{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.ClassicAddress,
},
LoanID: loanID,
}
loanManageImpair.SetLoanImpairFlag()
// Flatten() converts the struct to a map and adds the TransactionType field
flatLoanManageImpair := loanManageImpair.Flatten()
loanManageImpairJSON, _ := json.MarshalIndent(flatLoanManageImpair, "", " ")
fmt.Printf("%s\n", string(loanManageImpairJSON))
// Sign, submit, and wait for impairment validation ----------------------
fmt.Printf("\n=== Submitting LoanManage impairment transaction ===\n\n")
impairResponse, err := client.SubmitTxAndWait(flatLoanManageImpair, &wstypes.SubmitOptions{
Autofill: true,
Wallet: &loanBrokerWallet,
})
if err != nil {
panic(err)
}
if impairResponse.Meta.TransactionResult != "tesSUCCESS" {
fmt.Printf("Error: Unable to impair loan: %s\n", impairResponse.Meta.TransactionResult)
os.Exit(1)
}
fmt.Printf("Loan impaired successfully!\n")
// Extract loan impairment info from transaction results ----------------------
var loanNode map[string]any
for _, node := range impairResponse.Meta.AffectedNodes {
if node.ModifiedNode != nil && node.ModifiedNode.LedgerEntryType == "Loan" {
loanNode = node.ModifiedNode.FinalFields
break
}
}
// Check grace period and next payment due date
gracePeriod := int64(loanNode["GracePeriod"].(float64))
nextPaymentDueDate = int64(loanNode["NextPaymentDueDate"].(float64))
defaultTime := nextPaymentDueDate + gracePeriod
paymentDue = time.UnixMilli(xrpltime.RippleTimeToUnixTime(nextPaymentDueDate))
fmt.Printf("New Payment Due Date: %s\n", paymentDue.Local().Format(time.DateTime))
fmt.Printf("Grace Period: %d seconds\n", gracePeriod)
// Convert current time to Ripple Epoch timestamp
currentTime := xrpltime.UnixTimeToRippleTime(time.Now().Unix())
// Add a small buffer (5 seconds) to account for ledger close time
secondsUntilDefault := defaultTime - currentTime + 5
// Countdown until loan can be defaulted ----------------------
fmt.Printf("\n=== Countdown until loan can be defaulted ===\n\n")
for secondsUntilDefault >= 0 {
fmt.Printf("\r%d seconds...", secondsUntilDefault)
time.Sleep(time.Second)
secondsUntilDefault--
}
fmt.Print("\rGrace period expired. Loan can now be defaulted.\n")
// Prepare LoanManage transaction to default the loan ----------------------
fmt.Printf("\n=== Preparing LoanManage transaction to default loan ===\n\n")
loanManageDefault := transaction.LoanManage{
BaseTx: transaction.BaseTx{
Account: loanBrokerWallet.ClassicAddress,
},
LoanID: loanID,
}
loanManageDefault.SetLoanDefaultFlag()
flatLoanManageDefault := loanManageDefault.Flatten()
loanManageDefaultJSON, _ := json.MarshalIndent(flatLoanManageDefault, "", " ")
fmt.Printf("%s\n", string(loanManageDefaultJSON))
// Sign, submit, and wait for default validation ----------------------
fmt.Printf("\n=== Submitting LoanManage default transaction ===\n\n")
defaultResponse, err := client.SubmitTxAndWait(flatLoanManageDefault, &wstypes.SubmitOptions{
Autofill: true,
Wallet: &loanBrokerWallet,
})
if err != nil {
panic(err)
}
if defaultResponse.Meta.TransactionResult != "tesSUCCESS" {
fmt.Printf("Error: Unable to default loan: %s\n", defaultResponse.Meta.TransactionResult)
os.Exit(1)
}
fmt.Printf("Loan defaulted successfully!\n")
// Verify loan default status from transaction results ----------------------
fmt.Printf("\n=== Checking final loan status ===\n\n")
for _, node := range defaultResponse.Meta.AffectedNodes {
if node.ModifiedNode != nil && node.ModifiedNode.LedgerEntryType == "Loan" {
loanNode = node.ModifiedNode.FinalFields
break
}
}
loanFlags := uint32(loanNode["Flags"].(float64))
// Check which loan flags are set
activeFlags := []string{}
if flag.Contains(loanFlags, transaction.TfLoanDefault) {
activeFlags = append(activeFlags, "tfLoanDefault")
}
if flag.Contains(loanFlags, transaction.TfLoanImpair) {
activeFlags = append(activeFlags, "tfLoanImpair")
}
fmt.Printf("Final loan flags: %v\n", activeFlags)
}

View File

@@ -1,179 +0,0 @@
// IMPORTANT: This example pays off an existing loan and then deletes it.
package main
import (
"encoding/json"
"fmt"
"math/big"
"os"
"os/exec"
"github.com/Peersyst/xrpl-go/xrpl/queries/common"
"github.com/Peersyst/xrpl-go/xrpl/queries/ledger"
"github.com/Peersyst/xrpl-go/xrpl/transaction"
"github.com/Peersyst/xrpl-go/xrpl/transaction/types"
"github.com/Peersyst/xrpl-go/xrpl/wallet"
"github.com/Peersyst/xrpl-go/xrpl/websocket"
wstypes "github.com/Peersyst/xrpl-go/xrpl/websocket/types"
)
func main() {
// Connect to the network ----------------------
client := websocket.NewClient(
websocket.NewClientConfig().
WithHost("wss://s.devnet.rippletest.net:51233"),
)
defer client.Disconnect()
if err := client.Connect(); err != nil {
panic(err)
}
// Check for setup data; run lending-setup if missing
if _, err := os.Stat("lending-setup.json"); os.IsNotExist(err) {
fmt.Printf("\n=== Lending tutorial data doesn't exist. Running setup script... ===\n\n")
cmd := exec.Command("go", "run", "./lending-setup")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
panic(err)
}
}
// Load preconfigured accounts and LoanID
data, err := os.ReadFile("lending-setup.json")
if err != nil {
panic(err)
}
var setup map[string]any
if err := json.Unmarshal(data, &setup); err != nil {
panic(err)
}
// You can replace these values with your own
borrowerWallet, err := wallet.FromSecret(setup["borrower"].(map[string]any)["seed"].(string))
if err != nil {
panic(err)
}
loanID := setup["loanID2"].(string)
mptID := setup["mptID"].(string)
fmt.Printf("\nBorrower address: %s\n", borrowerWallet.ClassicAddress)
fmt.Printf("LoanID: %s\n", loanID)
fmt.Printf("MPT ID: %s\n", mptID)
// Check initial loan status ----------------------
fmt.Printf("\n=== Loan Status ===\n\n")
loanStatus, err := client.GetLedgerEntry(&ledger.EntryRequest{
Index: loanID,
LedgerIndex: common.Validated,
})
if err != nil {
panic(err)
}
totalValueOutstanding := loanStatus.Node["TotalValueOutstanding"].(string)
loanServiceFee := loanStatus.Node["LoanServiceFee"].(string)
outstanding, _ := new(big.Int).SetString(totalValueOutstanding, 10)
serviceFee, _ := new(big.Int).SetString(loanServiceFee, 10)
totalPayment := new(big.Int).Add(outstanding, serviceFee).String()
fmt.Printf("Amount Owed: %s TSTUSD\n", totalValueOutstanding)
fmt.Printf("Loan Service Fee: %s TSTUSD\n", loanServiceFee)
fmt.Printf("Total Payment Due (including fees): %s TSTUSD\n", totalPayment)
// Prepare LoanPay transaction ----------------------
fmt.Printf("\n=== Preparing LoanPay transaction ===\n\n")
loanPayTx := transaction.LoanPay{
BaseTx: transaction.BaseTx{
Account: borrowerWallet.ClassicAddress,
},
LoanID: loanID,
Amount: types.MPTCurrencyAmount{
MPTIssuanceID: mptID,
Value: totalPayment,
},
}
// Flatten() converts the struct to a map and adds the TransactionType field
flatLoanPayTx := loanPayTx.Flatten()
loanPayTxJSON, _ := json.MarshalIndent(flatLoanPayTx, "", " ")
fmt.Printf("%s\n", string(loanPayTxJSON))
// Sign, submit, and wait for payment validation ----------------------
fmt.Printf("\n=== Submitting LoanPay transaction ===\n\n")
payResponse, err := client.SubmitTxAndWait(flatLoanPayTx, &wstypes.SubmitOptions{
Autofill: true,
Wallet: &borrowerWallet,
})
if err != nil {
panic(err)
}
if payResponse.Meta.TransactionResult != "tesSUCCESS" {
fmt.Printf("Error: Unable to pay loan: %s\n", payResponse.Meta.TransactionResult)
os.Exit(1)
}
fmt.Printf("Loan paid successfully!\n")
// Extract updated loan info from transaction results ----------------------
fmt.Printf("\n=== Loan Status After Payment ===\n\n")
for _, node := range payResponse.Meta.AffectedNodes {
if node.ModifiedNode != nil && node.ModifiedNode.LedgerEntryType == "Loan" {
if balance, ok := node.ModifiedNode.FinalFields["TotalValueOutstanding"].(string); ok {
fmt.Printf("Outstanding Loan Balance: %s TSTUSD\n", balance)
} else {
fmt.Printf("Outstanding Loan Balance: Loan fully paid off!\n")
}
break
}
}
// Prepare LoanDelete transaction ----------------------
// Either the loan broker or borrower can submit this transaction.
fmt.Printf("\n=== Preparing LoanDelete transaction ===\n\n")
loanDeleteTx := transaction.LoanDelete{
BaseTx: transaction.BaseTx{
Account: borrowerWallet.ClassicAddress,
},
LoanID: loanID,
}
flatLoanDeleteTx := loanDeleteTx.Flatten()
loanDeleteTxJSON, _ := json.MarshalIndent(flatLoanDeleteTx, "", " ")
fmt.Printf("%s\n", string(loanDeleteTxJSON))
// Sign, submit, and wait for deletion validation ----------------------
fmt.Printf("\n=== Submitting LoanDelete transaction ===\n\n")
deleteResponse, err := client.SubmitTxAndWait(flatLoanDeleteTx, &wstypes.SubmitOptions{
Autofill: true,
Wallet: &borrowerWallet,
})
if err != nil {
panic(err)
}
if deleteResponse.Meta.TransactionResult != "tesSUCCESS" {
fmt.Printf("Error: Unable to delete loan: %s\n", deleteResponse.Meta.TransactionResult)
os.Exit(1)
}
fmt.Printf("Loan deleted successfully!\n")
// Verify loan deletion ----------------------
fmt.Printf("\n=== Verifying Loan Deletion ===\n\n")
_, err = client.GetLedgerEntry(&ledger.EntryRequest{
Index: loanID,
LedgerIndex: common.Validated,
})
if err != nil {
if err.Error() == "entryNotFound" {
fmt.Printf("Loan has been successfully removed from the XRP Ledger!\n")
} else {
panic(err)
}
} else {
fmt.Printf("Warning: Loan still exists in the ledger.\n")
}
}

View File

@@ -370,7 +370,6 @@
[gateway_balances method]: /docs/references/http-websocket-apis/public-api-methods/account-methods/gateway_balances.md
[get_counts command]: /docs/references/http-websocket-apis/admin-api-methods/status-and-debugging-methods/get_counts.md
[get_counts method]: /docs/references/http-websocket-apis/admin-api-methods/status-and-debugging-methods/get_counts.md
[Get Started Using Go]: /docs/tutorials/get-started/get-started-go.md
[Get Started Using JavaScript]: /docs/tutorials/get-started/get-started-javascript.md
[Get Started Using Python]: /docs/tutorials/get-started/get-started-python.md
[hexadecimal]: https://en.wikipedia.org/wiki/Hexadecimal
@@ -488,6 +487,5 @@
[vault_info method]: /docs/references/http-websocket-apis/public-api-methods/vault-methods/vault_info.md
[wallet_propose command]: /docs/references/http-websocket-apis/admin-api-methods/key-generation-methods/wallet_propose.md
[wallet_propose method]: /docs/references/http-websocket-apis/admin-api-methods/key-generation-methods/wallet_propose.md
[xrpl-go library]: https://github.com/XRPLF/xrpl-go
[xrpl.js library]: https://github.com/XRPLF/xrpl.js
[xrpl-py library]: https://github.com/XRPLF/xrpl-py

View File

@@ -31,7 +31,6 @@ To complete this tutorial, you should:
- 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.
- **Go** with the [xrpl-go library][]. See [Get Started Using Go][] for setup steps.
## Source Code
@@ -58,13 +57,6 @@ source .venv/bin/activate
pip install -r requirements.txt
```
{% /tab %}
{% tab label="Go" %}
From the code sample folder, use `go` to install dependencies.
```bash
go mod tidy
```
{% /tab %}
{% /tabs %}
### 2. Set up client and accounts
@@ -85,13 +77,6 @@ To get started, import the necessary libraries and instantiate a client to conne
{% code-snippet file="/_code-samples/lending-protocol/py/cover_clawback.py" language="py" before="# This step checks" /%}
{% /tab %}
{% tab label="Go" %}
- `xrpl-go`: Used for XRPL client connection, transaction submission, and wallet handling.
- `encoding/json` and `fmt`: Used for formatting and printing results to the console.
- `os` and `os/exec`: Used to run tutorial set up scripts.
{% code-snippet file="/_code-samples/lending-protocol/go/cover-clawback/main.go" language="go" before="// Check for setup data" /%}
{% /tab %}
{% /tabs %}
Next, load the loan broker account, MPT issuer account, loan broker ID, and MPT ID.
@@ -107,11 +92,6 @@ This example uses preconfigured accounts, MPTs, and loan broker data from the `l
This example uses preconfigured accounts, MPTs, and loan broker data from the `lending_setup.py` script, but you can replace `loan_broker`, `mpt_issuer`, `loan_broker_id`, and `mpt_id` with your own values.
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-clawback/main.go" language="go" from="// Check for setup data" before="// Check cover available" /%}
This example uses preconfigured accounts, MPTs, and loan broker data from the `lending-setup` script, but you can replace `loanBrokerWallet`, `mptIssuerWallet`, `loanBrokerID`, and `mptID` with your own values.
{% /tab %}
{% /tabs %}
### 3. Check initial cover available
@@ -125,9 +105,6 @@ Check the initial cover (first-loss capital) available using the [ledger_entry m
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/cover_clawback.py" language="py" from="# Check cover available" before="# Prepare LoanBrokerCoverDeposit" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-clawback/main.go" language="go" from="// Check cover available" before="// Prepare LoanBrokerCoverDeposit" /%}
{% /tab %}
{% /tabs %}
If the `CoverAvailable` field is missing, it means no first-loss capital has been deposited.
@@ -147,11 +124,6 @@ The `Amount` field specifies the MPT and amount to deposit as first-loss capital
The `amount` field specifies the MPT and amount to deposit as first-loss capital.
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-clawback/main.go" language="go" from="// Prepare LoanBrokerCoverDeposit" before="// Sign, submit, and wait for deposit validation" /%}
The `Amount` field specifies the MPT and amount to deposit as first-loss capital.
{% /tab %}
{% /tabs %}
If the transaction succeeds, the amount is deposited and held in the pseudo-account associated with the `LoanBroker` entry.
@@ -167,9 +139,6 @@ Sign and submit the `LoanBrokerCoverDeposit` transaction to the XRP Ledger.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/cover_clawback.py" language="py" from="# Sign, submit, and wait for deposit validation" before="# Extract updated cover available" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-clawback/main.go" language="go" from="// Sign, submit, and wait for deposit validation" before="// Extract updated cover available" /%}
{% /tab %}
{% /tabs %}
Verify that the transaction succeeded by checking for a `tesSUCCESS` result code.
@@ -185,9 +154,6 @@ Retrieve the cover available from the transaction result by checking the `LoanBr
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/cover_clawback.py" language="py" from="# Extract updated cover available" before="# Verify issuer of cover asset" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-clawback/main.go" language="go" from="// Extract updated cover available" before="// Verify issuer of cover asset" /%}
{% /tab %}
{% /tabs %}
### 7. Verify the asset issuer
@@ -201,9 +167,6 @@ Before executing a clawback, verify that the account submitting the transaction
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/cover_clawback.py" language="py" from="# Verify issuer of cover asset" before="# Prepare LoanBrokerCoverClawback" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-clawback/main.go" language="go" from="// Verify issuer of cover asset" before="// Prepare LoanBrokerCoverClawback" /%}
{% /tab %}
{% /tabs %}
Clawback functionality is disabled by default. In the case of MPTs, the `tfMPTCanClawback` flag must be enabled when the [MPTokenIssuanceCreate transaction][] is submitted. This tutorial uses an MPT that is already configured for clawback.
@@ -219,9 +182,6 @@ Create the [LoanBrokerCoverClawback transaction][] object.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/cover_clawback.py" language="py" from="# Prepare LoanBrokerCoverClawback" before="# Sign, submit, and wait for clawback validation" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-clawback/main.go" language="go" from="// Prepare LoanBrokerCoverClawback" before="// Sign, submit, and wait for clawback validation" /%}
{% /tab %}
{% /tabs %}
In this example we claw back the entire amount, but you can specify any amount so long as it doesn't exceed the available cover or reduce the cover below the minimum required by the `LoanBroker`.
@@ -237,9 +197,6 @@ Sign and submit the `LoanBrokerCoverClawback` transaction to the XRP Ledger.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/cover_clawback.py" language="py" from="# Sign, submit, and wait for clawback validation" before="# Extract final cover available" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-clawback/main.go" language="go" from="// Sign, submit, and wait for clawback validation" before="// Extract final cover available" /%}
{% /tab %}
{% /tabs %}
Verify that the transaction succeeded by checking for a `tesSUCCESS` result code.
@@ -255,9 +212,6 @@ Retrieve the final cover available from the transaction result.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/cover_clawback.py" language="py" from="# Extract final cover available" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-clawback/main.go" language="go" from="// Extract final cover available" /%}
{% /tab %}
{% /tabs %}
## See Also

View File

@@ -30,7 +30,6 @@ To complete this tutorial, you should:
- 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.
- **Go** with the [xrpl-go library][]. See [Get Started Using Go][] for setup steps.
## Source Code
@@ -57,13 +56,6 @@ source .venv/bin/activate
pip install -r requirements.txt
```
{% /tab %}
{% tab label="Go" %}
From the code sample folder, use `go` to install dependencies.
```bash
go mod tidy
```
{% /tab %}
{% /tabs %}
### 2. Set up client and accounts
@@ -84,13 +76,6 @@ To get started, import the necessary libraries and instantiate a client to conne
{% code-snippet file="/_code-samples/lending-protocol/py/create_loan_broker.py" language="py" before="# This step checks" /%}
{% /tab %}
{% tab label="Go" %}
- `xrpl-go`: Used for XRPL client connection, transaction submission, and wallet handling.
- `encoding/json` and `fmt`: Used for formatting and printing results to the console.
- `os` and `os/exec`: Used to run tutorial set up scripts.
{% code-snippet file="/_code-samples/lending-protocol/go/create-loan-broker/main.go" language="go" before="// Check for setup data" /%}
{% /tab %}
{% /tabs %}
Next, load the vault owner account and vault ID. The vault owner will also be the loan broker.
@@ -106,11 +91,6 @@ This example uses preconfigured accounts and vault data from the `lendingSetup.j
This example uses preconfigured accounts and vault data from the `lending_setup.py` script, but you can replace `loan_broker` and `vault_id` with your own values.
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/create-loan-broker/main.go" language="go" from="// Check for setup data" before="// Prepare LoanBrokerSet" /%}
This example uses preconfigured accounts and vault data from the `lending-setup` script, but you can replace `loanBrokerWallet` and `vaultID` with your own values.
{% /tab %}
{% /tabs %}
### 3. Prepare LoanBrokerSet transaction
@@ -124,9 +104,6 @@ Create the [LoanBrokerSet transaction][] object.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/create_loan_broker.py" language="py" from="# Prepare LoanBrokerSet" before="# Submit, sign" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/create-loan-broker/main.go" language="go" from="// Prepare LoanBrokerSet" before="// Submit, sign" /%}
{% /tab %}
{% /tabs %}
The management fee rate is set in 1/10th basis points. A value of `1000` equals 1% (100 basis points).
@@ -142,9 +119,6 @@ Sign and submit the `LoanBrokerSet` transaction to the XRP Ledger.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/create_loan_broker.py" language="py" from="# Submit, sign" before="# Extract loan broker" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/create-loan-broker/main.go" language="go" from="// Submit, sign" before="// Extract loan broker" /%}
{% /tab %}
{% /tabs %}
Verify that the transaction succeeded by checking for a `tesSUCCESS` result code.
@@ -160,9 +134,6 @@ Retrieve the loan broker's information from the transaction result by checking f
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/create_loan_broker.py" language="py" from="# Extract loan broker" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/create-loan-broker/main.go" language="go" from="// Extract loan broker" /%}
{% /tab %}
{% /tabs %}
The loan broker pseudo-account is a special account that holds first-loss capital.

View File

@@ -33,7 +33,6 @@ To complete this tutorial, you should:
- 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.
- **Go** with the [xrpl-go library][]. See [Get Started Using Go][] for setup steps.
## Source Code
@@ -60,13 +59,6 @@ source .venv/bin/activate
pip install -r requirements.txt
```
{% /tab %}
{% tab label="Go" %}
From the code sample folder, use `go` to install dependencies.
```bash
go mod tidy
```
{% /tab %}
{% /tabs %}
### 2. Set up client and accounts
@@ -87,13 +79,6 @@ To get started, import the necessary libraries and instantiate a client to conne
{% code-snippet file="/_code-samples/lending-protocol/py/create_loan.py" language="py" before="# This step checks" /%}
{% /tab %}
{% tab label="Go" %}
- `xrpl-go`: Used for XRPL client connection, transaction submission, and wallet handling.
- `encoding/json` and `fmt`: Used for formatting and printing results to the console.
- `os` and `os/exec`: Used to run tutorial set up scripts.
{% code-snippet file="/_code-samples/lending-protocol/go/create-loan/main.go" language="go" before="// Check for setup data" /%}
{% /tab %}
{% /tabs %}
Next, load the loan broker account, borrower account, and loan broker ID.
@@ -109,11 +94,6 @@ This example uses preconfigured accounts and loan broker data from the `lendingS
This example uses preconfigured accounts and loan broker data from the `lending_setup.py` script, but you can replace `loan_broker`, `borrower`, and `loan_broker_id` with your own values.
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/create-loan/main.go" language="go" from="// Check for setup data" before="// Prepare LoanSet" /%}
This example uses preconfigured accounts and loan broker data from the `lending-setup` script, but you can replace `loanBrokerWallet`, `borrowerWallet`, and `loanBrokerID` with your own values.
{% /tab %}
{% /tabs %}
### 3. Prepare LoanSet transaction
@@ -149,20 +129,6 @@ The loan terms include:
- `loan_origination_fee`: A one-time fee charged when the loan is created, paid in the borrowed asset.
- `loan_service_fee`: A fee charged with every loan payment, paid in the borrowed asset.
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/create-loan/main.go" language="go" from="// Prepare LoanSet" before="// Loan broker signs first" /%}
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.
{% /tab %}
{% /tabs %}
### 4. Add loan broker signature
@@ -176,9 +142,6 @@ The loan broker (the `Account`) signs the transaction first, adding their `TxnSi
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/create_loan.py" language="py" from="# Loan broker signs first" before="# Borrower signs second" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/create-loan/main.go" language="go" from="// Loan broker signs first" before="// Borrower signs second" /%}
{% /tab %}
{% /tabs %}
### 5. Add borrower signature
@@ -192,9 +155,6 @@ The borrower (the `Counterparty`) signs the transaction second. Their `TxnSignat
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/create_loan.py" language="py" from="# Borrower signs second" before="# Submit and wait" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/create-loan/main.go" language="go" from="// Borrower signs second" before="// Submit and wait" /%}
{% /tab %}
{% /tabs %}
### 6. Submit LoanSet transaction
@@ -208,9 +168,6 @@ Submit the fully signed `LoanSet` transaction to the XRP Ledger.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/create_loan.py" language="py" from="# Submit and wait" before="# Extract loan information" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/create-loan/main.go" language="go" from="// Submit and wait" before="// Extract loan information" /%}
{% /tab %}
{% /tabs %}
Verify that the transaction succeeded by checking for a `tesSUCCESS` result code.
@@ -226,9 +183,6 @@ Retrieve the loan's information from the transaction result by checking for the
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/create_loan.py" language="py" from="# Extract loan information" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/create-loan/main.go" language="go" from="// Extract loan information" /%}
{% /tab %}
{% /tabs %}
## See Also

View File

@@ -32,7 +32,6 @@ To complete this tutorial, you should:
- 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.
- **Go** with the [xrpl-go library][]. See [Get Started Using Go][] for setup steps.
## Source Code
@@ -59,13 +58,6 @@ source .venv/bin/activate
pip install -r requirements.txt
```
{% /tab %}
{% tab label="Go" %}
From the code sample folder, use `go` to install dependencies.
```bash
go mod tidy
```
{% /tab %}
{% /tabs %}
### 2. Set up client and accounts
@@ -86,13 +78,6 @@ To get started, import the necessary libraries and instantiate a client to conne
{% code-snippet file="/_code-samples/lending-protocol/py/cover_deposit_and_withdraw.py" language="py" before="# This step checks" /%}
{% /tab %}
{% tab label="Go" %}
- `xrpl-go`: Used for XRPL client connection, transaction submission, and wallet handling.
- `encoding/json` and `fmt`: Used for formatting and printing results to the console.
- `os` and `os/exec`: Used to run tutorial set up scripts.
{% code-snippet file="/_code-samples/lending-protocol/go/cover-deposit-and-withdraw/main.go" language="go" before="// Check for setup data" /%}
{% /tab %}
{% /tabs %}
Next, load the loan broker account, loan broker ID, and MPT issuance ID.
@@ -108,11 +93,6 @@ This example uses preconfigured accounts and loan broker data from the `lendingS
This example uses preconfigured accounts and loan broker data from the `lending_setup.py` script, but you can replace `loan_broker`, `loan_broker_id`, and `mpt_id` with your own values.
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-deposit-and-withdraw/main.go" language="go" from="// Check for setup data" before="// Prepare LoanBrokerCoverDeposit" /%}
This example uses preconfigured accounts and loan broker data from the `lending-setup` script, but you can replace `loanBrokerWallet`, `loanBrokerID`, and `mptID` with your own values.
{% /tab %}
{% /tabs %}
### 3. Prepare LoanBrokerCoverDeposit transaction
@@ -130,11 +110,6 @@ The `Amount` field specifies the MPT and amount to deposit as first-loss capital
The `amount` field specifies the MPT and amount to deposit as first-loss capital.
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-deposit-and-withdraw/main.go" language="go" from="// Prepare LoanBrokerCoverDeposit" before="// Sign, submit, and wait for deposit" /%}
The `Amount` field specifies the MPT and amount to deposit as first-loss capital.
{% /tab %}
{% /tabs %}
If the transaction succeeds, the amount is deposited and held in the pseudo-account associated with the `LoanBroker` entry.
@@ -150,9 +125,6 @@ Sign and submit the `LoanBrokerCoverDeposit` transaction to the XRP Ledger.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/cover_deposit_and_withdraw.py" language="py" from="# Sign, submit, and wait for deposit" before="# Extract cover balance" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-deposit-and-withdraw/main.go" language="go" from="// Sign, submit, and wait for deposit" before="// Extract cover balance" /%}
{% /tab %}
{% /tabs %}
Verify that the transaction succeeded by checking for a `tesSUCCESS` result code.
@@ -168,9 +140,6 @@ Retrieve the cover balance from the transaction result by checking the `LoanBrok
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/cover_deposit_and_withdraw.py" language="py" from="# Extract cover balance" before="# Prepare LoanBrokerCoverWithdraw" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-deposit-and-withdraw/main.go" language="go" from="// Extract cover balance" before="// Prepare LoanBrokerCoverWithdraw" /%}
{% /tab %}
{% /tabs %}
The `LoanBroker` pseudo-account address is the `Account` field, and `CoverAvailable` shows the cover balance.
@@ -186,9 +155,6 @@ Create the [LoanBrokerCoverWithdraw transaction][] object.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/cover_deposit_and_withdraw.py" language="py" from="# Prepare LoanBrokerCoverWithdraw" before="# Sign, submit, and wait for withdraw" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-deposit-and-withdraw/main.go" language="go" from="// Prepare LoanBrokerCoverWithdraw" before="// Sign, submit, and wait for withdraw" /%}
{% /tab %}
{% /tabs %}
### 7. Submit LoanBrokerCoverWithdraw transaction
@@ -202,9 +168,6 @@ Sign and submit the `LoanBrokerCoverWithdraw` transaction to the XRP Ledger.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/cover_deposit_and_withdraw.py" language="py" from="# Sign, submit, and wait for withdraw" before="# Extract updated cover balance" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-deposit-and-withdraw/main.go" language="go" from="// Sign, submit, and wait for withdraw" before="// Extract updated cover balance" /%}
{% /tab %}
{% /tabs %}
Verify that the transaction succeeded by checking for a `tesSUCCESS` result code.
@@ -220,9 +183,6 @@ Retrieve the updated cover balance from the transaction result.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/cover_deposit_and_withdraw.py" language="py" from="# Extract updated cover balance" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/cover-deposit-and-withdraw/main.go" language="go" from="// Extract updated cover balance" /%}
{% /tab %}
{% /tabs %}
The `CoverAvailable` field now shows the reduced balance after the withdrawal.

View File

@@ -33,7 +33,6 @@ To complete this tutorial, you should:
- 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.
- **Go** with the [xrpl-go library][]. See [Get Started Using Go][] for setup steps.
## Source Code
@@ -60,13 +59,6 @@ source .venv/bin/activate
pip install -r requirements.txt
```
{% /tab %}
{% tab label="Go" %}
From the code sample folder, use `go` to install dependencies.
```bash
go mod tidy
```
{% /tab %}
{% /tabs %}
### 2. Set up client and accounts
@@ -88,14 +80,6 @@ To get started, import the necessary libraries and instantiate a client to conne
{% code-snippet file="/_code-samples/lending-protocol/py/loan_manage.py" language="py" before="# This step checks" /%}
{% /tab %}
{% tab label="Go" %}
- `xrpl-go`: Used for XRPL client connection, transaction submission, and wallet handling.
- `encoding/json` and `fmt`: Used for formatting and printing results to the console.
- `os` and `os/exec`: Used to run tutorial set up scripts.
- `time`: Used for grace period countdown and date formatting.
{% code-snippet file="/_code-samples/lending-protocol/go/loan-manage/main.go" language="go" before="// Check for setup data" /%}
{% /tab %}
{% /tabs %}
Next, load the loan broker account and loan ID.
@@ -111,11 +95,6 @@ This example uses preconfigured accounts and loan data from the `lendingSetup.js
This example uses preconfigured accounts and loan data from the `lending_setup.py` script, but you can replace `loan_broker` and `loan_id` with your own values.
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-manage/main.go" language="go" from="// Check for setup data" before="// Check loan status" /%}
This example uses preconfigured accounts and loan data from the `lending-setup` script, but you can replace `loanBrokerWallet` and `loanID` with your own values.
{% /tab %}
{% /tabs %}
### 3. Check loan status
@@ -129,9 +108,6 @@ Check the current status of the loan using the [ledger_entry method][].
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_manage.py" language="py" from="# Check loan status" before="# Prepare LoanManage transaction to impair" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-manage/main.go" language="go" from="// Check loan status" before="// Prepare LoanManage transaction to impair" /%}
{% /tab %}
{% /tabs %}
This shows the total amount owed and the next payment due date. The [Ripple Epoch][] timestamp is converted to a readable date format.
@@ -147,9 +123,6 @@ Create the [LoanManage transaction][] with the `tfLoanImpair` flag.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_manage.py" language="py" from="# Prepare LoanManage transaction to impair" before="# Sign, submit, and wait for impairment" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-manage/main.go" language="go" from="// Prepare LoanManage transaction to impair" before="// Sign, submit, and wait for impairment" /%}
{% /tab %}
{% /tabs %}
### 5. Submit LoanManage impairment transaction
@@ -163,9 +136,6 @@ Sign and submit the `LoanManage` transaction to impair the loan.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_manage.py" language="py" from="# Sign, submit, and wait for impairment" before="# Extract loan impairment info" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-manage/main.go" language="go" from="// Sign, submit, and wait for impairment" before="// Extract loan impairment info" /%}
{% /tab %}
{% /tabs %}
Verify that the transaction succeeded by checking for a `tesSUCCESS` result code.
@@ -181,9 +151,6 @@ Retrieve the loan's grace period and updated payment due date from the transacti
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_manage.py" language="py" from="# Extract loan impairment info" before="# Countdown until loan can be defaulted" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-manage/main.go" language="go" from="// Extract loan impairment info" before="// Countdown until loan can be defaulted" /%}
{% /tab %}
{% /tabs %}
The loan can only be defaulted after the grace period expires. The example calculates when the grace period ends and displays a countdown.
@@ -199,9 +166,6 @@ This countdown displays the remaining seconds in real-time. Once the grace perio
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_manage.py" language="py" from="# Countdown until loan can be defaulted" before="# Prepare LoanManage transaction to default" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-manage/main.go" language="go" from="// Countdown until loan can be defaulted" before="// Prepare LoanManage transaction to default" /%}
{% /tab %}
{% /tabs %}
### 8. Prepare LoanManage transaction to default the loan
@@ -215,9 +179,6 @@ After the grace period expires, create a `LoanManage` transaction with the `tfLo
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_manage.py" language="py" from="# Prepare LoanManage transaction to default" before="# Sign, submit, and wait for default" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-manage/main.go" language="go" from="// Prepare LoanManage transaction to default" before="// Sign, submit, and wait for default" /%}
{% /tab %}
{% /tabs %}
### 9. Submit LoanManage default transaction
@@ -231,9 +192,6 @@ Sign and submit the `LoanManage` transaction to default the loan.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_manage.py" language="py" from="# Sign, submit, and wait for default" before="# Verify loan default status" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-manage/main.go" language="go" from="// Sign, submit, and wait for default" before="// Verify loan default status" /%}
{% /tab %}
{% /tabs %}
Verify that the transaction succeeded by checking for a `tesSUCCESS` result code.
@@ -249,9 +207,6 @@ Confirm the loan has been defaulted by checking the loan flags.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_manage.py" language="py" from="# Verify loan default status" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-manage/main.go" language="go" from="// Verify loan default status" /%}
{% /tab %}
{% /tabs %}
The loan flags are parsed to confirm the `tfLoanDefault` flag is now set.

View File

@@ -33,7 +33,6 @@ To complete this tutorial, you should:
- 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.
- **Go** with the [xrpl-go library][]. See [Get Started Using Go][] for setup steps.
## Source Code
@@ -60,13 +59,6 @@ source .venv/bin/activate
pip install -r requirements.txt
```
{% /tab %}
{% tab label="Go" %}
From the code sample folder, use `go` to install dependencies.
```bash
go mod tidy
```
{% /tab %}
{% /tabs %}
### 2. Set up client and accounts
@@ -87,14 +79,6 @@ To get started, import the necessary libraries and instantiate a client to conne
{% code-snippet file="/_code-samples/lending-protocol/py/loan_pay.py" language="py" before="# This step checks" /%}
{% /tab %}
{% tab label="Go" %}
- `xrpl-go`: Used for XRPL client connection, transaction submission, and wallet handling.
- `encoding/json` and `fmt`: Used for formatting and printing results to the console.
- `math/big`: Used for calculating the total payment amount.
- `os` and `os/exec`: Used to run tutorial set up scripts.
{% code-snippet file="/_code-samples/lending-protocol/go/loan-pay/main.go" language="go" before="// Check for setup data" /%}
{% /tab %}
{% /tabs %}
Next, load the borrower account, loan ID, and MPT issuance ID.
@@ -110,11 +94,6 @@ This example uses preconfigured accounts and loan data from the `lendingSetup.js
This example uses preconfigured accounts and loan data from the `lending_setup.py` script, but you can replace `borrower`, `loan_id`, and `mpt_id` with your own values.
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-pay/main.go" language="go" from="// Check for setup data" before="// Check initial loan status" /%}
This example uses preconfigured accounts and loan data from the `lending-setup` script, but you can replace `borrowerWallet`, `loanID`, and `mptID` with your own values.
{% /tab %}
{% /tabs %}
### 3. Check loan status
@@ -128,9 +107,6 @@ Check the current status of the loan using the [ledger_entry method][].
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_pay.py" language="py" from="# Check initial loan status" before="# Prepare LoanPay transaction" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-pay/main.go" language="go" from="// Check initial loan status" before="// Prepare LoanPay transaction" /%}
{% /tab %}
{% /tabs %}
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.
@@ -150,9 +126,6 @@ Create the [LoanPay transaction][] with the total payment amount.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_pay.py" language="py" from="# Prepare LoanPay transaction" before="# Sign, submit, and wait for payment validation" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-pay/main.go" language="go" from="// Prepare LoanPay transaction" before="// Sign, submit, and wait for payment validation" /%}
{% /tab %}
{% /tabs %}
### 5. Submit LoanPay transaction
@@ -166,9 +139,6 @@ Sign and submit the `LoanPay` transaction to the XRP Ledger.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_pay.py" language="py" from="# Sign, submit, and wait for payment validation" before="# Extract updated loan info" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-pay/main.go" language="go" from="// Sign, submit, and wait for payment validation" before="// Extract updated loan info" /%}
{% /tab %}
{% /tabs %}
Verify that the transaction succeeded by checking for a `tesSUCCESS` result code.
@@ -184,9 +154,6 @@ Retrieve the loan balance from the transaction result by checking for the `Loan`
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_pay.py" language="py" from="# Extract updated loan info" before="# Prepare LoanDelete transaction" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-pay/main.go" language="go" from="// Extract updated loan info" before="// Prepare LoanDelete transaction" /%}
{% /tab %}
{% /tabs %}
If `TotalValueOutstanding` is absent from the loan metadata, the loan has been fully paid off and is ready for deletion.
@@ -202,9 +169,6 @@ Create a [LoanDelete transaction][] to remove the paid loan from the XRP Ledger.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_pay.py" language="py" from="# Prepare LoanDelete transaction" before="# Sign, submit, and wait for deletion validation" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-pay/main.go" language="go" from="// Prepare LoanDelete transaction" before="// Sign, submit, and wait for deletion validation" /%}
{% /tab %}
{% /tabs %}
Either the loan broker or the borrower can submit a `LoanDelete` transaction. In this example, the borrower deletes their own paid off loan.
@@ -220,9 +184,6 @@ Sign and submit the `LoanDelete` transaction.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_pay.py" language="py" from="# Sign, submit, and wait for deletion validation" before="# Verify loan deletion" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-pay/main.go" language="go" from="// Sign, submit, and wait for deletion validation" before="// Verify loan deletion" /%}
{% /tab %}
{% /tabs %}
Verify that the transaction succeeded by checking for a `tesSUCCESS` result code.
@@ -238,9 +199,6 @@ Confirm that the loan has been removed from the XRP Ledger.
{% tab label="Python" %}
{% code-snippet file="/_code-samples/lending-protocol/py/loan_pay.py" language="py" from="# Verify loan deletion" /%}
{% /tab %}
{% tab label="Go" %}
{% code-snippet file="/_code-samples/lending-protocol/go/loan-pay/main.go" language="go" from="// Verify loan deletion" /%}
{% /tab %}
{% /tabs %}
If the `ledger_entry` method returns an `entryNotFound` error, the loan has been successfully deleted.

1181
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,7 @@
"@codemirror/state": "6.5.2",
"@codemirror/view": "^6.22.2",
"@lezer/highlight": "^1.2.0",
"@redocly/realm": "0.130.4",
"@redocly/realm": "0.131.0",
"@uiw/codemirror-themes": "4.21.21",
"@uiw/react-codemirror": "^4.21.21",
"@xrplf/isomorphic": "^1.0.0-beta.1",