mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2026-04-29 15:37:48 +00:00
154 lines
5.0 KiB
Go
154 lines
5.0 KiB
Go
// 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
|
|
}
|
|
}
|
|
}
|