diff --git a/_code-samples/modular-tutorials/send-currency.html b/_code-samples/modular-tutorials/send-currency.html deleted file mode 100644 index 6ff900a599a..00000000000 --- a/_code-samples/modular-tutorials/send-currency.html +++ /dev/null @@ -1,229 +0,0 @@ - - - Send Currency - - - - - - - - - - - - - - -

Send Currency

-
- - Choose your ledger instance: - -    - - -    - - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - - -
- - - - - - - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - -
-
- - - - - -
- - Currency Code - - - -
-
- -
- - Issuer - - - -
-
- -
- - - - - -
-
- -
- - Destination - - - -
-
-

- -

-
-
- -
-
- - - \ No newline at end of file diff --git a/_code-samples/modular-tutorials/send-currency.js b/_code-samples/modular-tutorials/send-currency.js deleted file mode 100644 index aba9fbab80f..00000000000 --- a/_code-samples/modular-tutorials/send-currency.js +++ /dev/null @@ -1,96 +0,0 @@ -// ******************************************************* -// ***************** Create TrustLine ******************** -// ******************************************************* - -async function createTrustLine() { - const net = getNet() - const client = new xrpl.Client(net) - await client.connect() - let results = "\nConnected. Creating trust line.\n" - resultField.value = results - try { - const wallet = xrpl.Wallet.fromSeed(accountSeedField.value) - const trustSet_tx = { - "TransactionType": "TrustSet", - "Account": accountAddressField.value, - "LimitAmount": { - "currency": currencyField.value, - "issuer": issuerField.value, - "value": amountField.value - } - } - const ts_prepared = await client.autofill(trustSet_tx) - const ts_signed = wallet.sign(ts_prepared) - resultField.value = results - const ts_result = await client.submitAndWait(ts_signed.tx_blob) - if (ts_result.result.meta.TransactionResult == "tesSUCCESS") { - results += '\n===Trustline established between account\n' + - accountAddressField.value + "\nand account\n" + issuerField.value + '.' - resultField.value = results - } else { - results += `\n===Transaction failed: ${ts_result.result.meta.TransactionResult}` - resultField.value = results - } - } - catch (error) { - console.error('Error creating trust line:', error); - results += `\n===Error: ${error.message}\n` - resultField.value = results - throw error; // Re-throw the error to be handled by the caller - } - finally { - // Disconnect from the client - await client.disconnect(); - } -} //End of createTrustline() - -// ******************************************************* -// *************** Send Issued Currency ****************** -// ******************************************************* - -async function sendCurrency() { - let net = getNet() - const client = new xrpl.Client(net) - results = 'Connecting to ' + getNet() + '....' - resultField.value = results - await client.connect() - results += '\nConnected.' - resultField.value = results - try { - const wallet = xrpl.Wallet.fromSeed(accountSeedField.value) - const send_currency_tx = { - "TransactionType": "Payment", - "Account": wallet.address, - "Amount": { - "currency": currencyField.value, - "value": amountField.value, - "issuer": issuerField.value - }, - "Destination": destinationField.value - } - const pay_prepared = await client.autofill(send_currency_tx) - const pay_signed = wallet.sign(pay_prepared) - results += `\n\n===Sending ${amountField.value} ${currencyField.value} to ${destinationField.value} ...` - resultField.value = results - const pay_result = await client.submitAndWait(pay_signed.tx_blob) - if (pay_result.result.meta.TransactionResult == "tesSUCCESS") { - results += '\n===Transaction succeeded.' - resultField.value = results - getTokenBalance() - } else { - results += `\n===Transaction failed: ${pay_result.result.meta.TransactionResult}\n` - resultField.value = results - } - xrpBalanceField.value = (await client.getXrpBalance(wallet.address)) -} - catch (error) { - console.error('Error sending transaction:', error); - results += `\nError: ${error.message}\n` - resultField.value = results - throw error; // Re-throw the error to be handled by the caller - } - finally { - // Disconnect from the client - await client.disconnect(); - } -} // end of sendCurrency() diff --git a/_code-samples/send-a-trust-line-token/README.md b/_code-samples/send-a-trust-line-token/README.md new file mode 100644 index 00000000000..956fce7c68f --- /dev/null +++ b/_code-samples/send-a-trust-line-token/README.md @@ -0,0 +1,3 @@ +# Send a Trust Line Token Examples + +Send a trust line token payment between two accounts and compare balances before and after to confirm the transfer. diff --git a/_code-samples/send-a-trust-line-token/go/README.md b/_code-samples/send-a-trust-line-token/go/README.md new file mode 100644 index 00000000000..998f1f81944 --- /dev/null +++ b/_code-samples/send-a-trust-line-token/go/README.md @@ -0,0 +1,72 @@ +# Send a Trust Line Token (Go) + +Example code for sending a trust line token between two accounts on Testnet with [xrpl-go](https://github.com/Peersyst/xrpl-go). + +All commands should be run from this `go/` directory. + +## Setup + +```sh +go mod tidy +``` + +## Send a Trust Line Token + +```sh +go run ./send-trust-line-token +``` + +You should see output like the following: + +```sh +Issuer address: r33kXgFnMTP7eVhr7YuZNyPnkz1wyfwVmg +Sender address: rHAcF3ViMQHqjLvLT9GtM34jJyfK9Lq4En +Receiver address: rHEfDbBxc5RmJdPzvi47p37P3XssTA3jB9 + +=== Creating trust line from receiver to issuer... === + +{ + "Account": "rHEfDbBxc5RmJdPzvi47p37P3XssTA3jB9", + "LimitAmount": { + "currency": "FOO", + "issuer": "r33kXgFnMTP7eVhr7YuZNyPnkz1wyfwVmg", + "value": "1000000000" + }, + "TransactionType": "TrustSet" +} +Trust line created from receiver to issuer! +Explorer link: https://testnet.xrpl.org/transactions/835367A47F7D3B466A0BE94A95394AF4D4C46FD17397F3AC4A20964695D73F71 + +=== Checking initial FOO balances... === + +Holders' perspective: + Sender's balance: 900 + Receiver's balance: 100 +Issuer's perspective: + Owed to sender: -900 + Owed to receiver: -100 + +=== Sending FOO payment... === + +{ + "Account": "rHAcF3ViMQHqjLvLT9GtM34jJyfK9Lq4En", + "Amount": { + "currency": "FOO", + "issuer": "r33kXgFnMTP7eVhr7YuZNyPnkz1wyfwVmg", + "value": "100" + }, + "Destination": "rHEfDbBxc5RmJdPzvi47p37P3XssTA3jB9", + "TransactionType": "Payment" +} +Payment successful! +Explorer link: https://testnet.xrpl.org/transactions/3FD2EDE570FADFBB129C6E4703CFBB80BC2BF5D843808F61C30F5041F46A7998 + +=== Checking final FOO balances... === + +Holders' perspective: + Sender's balance: 800 + Receiver's balance: 200 +Issuer's perspective: + Owed to sender: -800 + Owed to receiver: -200 +``` diff --git a/_code-samples/send-a-trust-line-token/go/go.mod b/_code-samples/send-a-trust-line-token/go/go.mod new file mode 100644 index 00000000000..86cb990ea19 --- /dev/null +++ b/_code-samples/send-a-trust-line-token/go/go.mod @@ -0,0 +1,19 @@ +module github.com/XRPLF/send-a-trust-line-token + +go 1.24.3 + +require github.com/Peersyst/xrpl-go v0.2.0 + +require ( + github.com/bsv-blockchain/go-sdk v1.2.9 // indirect + github.com/decred/dcrd/crypto/ripemd160 v1.0.2 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.1 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/gorilla/websocket v1.5.3 // indirect + github.com/json-iterator/go v1.1.12 // 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 +) diff --git a/_code-samples/send-a-trust-line-token/go/send-trust-line-token-setup/main.go b/_code-samples/send-a-trust-line-token/go/send-trust-line-token-setup/main.go new file mode 100644 index 00000000000..d40538aff6a --- /dev/null +++ b/_code-samples/send-a-trust-line-token/go/send-trust-line-token-setup/main.go @@ -0,0 +1,168 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "time" + + "github.com/Peersyst/xrpl-go/pkg/crypto" + "github.com/Peersyst/xrpl-go/xrpl/faucet" + "github.com/Peersyst/xrpl-go/xrpl/queries/account" + "github.com/Peersyst/xrpl-go/xrpl/queries/common" + "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" +) + +const ( + currencyCode = "FOO" + totalSteps = 4 +) + +func main() { + cfg, err := rpc.NewClientConfig( + "https://s.altnet.rippletest.net:51234", + rpc.WithFaucetProvider(faucet.NewTestnetFaucetProvider()), + ) + if err != nil { + panic(err) + } + client := rpc.NewClient(cfg) + + submitOpts := func(w *wallet.Wallet) *rpctypes.SubmitOptions { + return &rpctypes.SubmitOptions{Autofill: true, Wallet: w} + } + + step := 1 + + // Fund issuer, sender, and receiver concurrently + fmt.Printf("Setting up tutorial: %d/%d\r", step, totalSteps) + step++ + + ch := make(chan wallet.Wallet, 3) + createAndFund := func() { + w, err := wallet.New(crypto.ED25519()) + if err != nil { + panic(err) + } + if err := client.FundWallet(&w); err != nil { + panic(err) + } + // Poll until account is validated on ledger + funded := false + for range 20 { + _, err := client.Request(&account.InfoRequest{ + Account: w.GetAddress(), + LedgerIndex: common.Validated, + }) + if err == nil { + funded = true + break + } + time.Sleep(time.Second) + } + if !funded { + panic("Issue funding account: " + w.GetAddress().String()) + } + ch <- w + } + go createAndFund() + go createAndFund() + go createAndFund() + issuer := <-ch + sender := <-ch + receiver := <-ch + + fmt.Printf("Setting up tutorial: %d/%d\r", step, totalSteps) + step++ + + accountSetTx := transaction.AccountSet{ + BaseTx: transaction.BaseTx{Account: issuer.ClassicAddress}, + } + // Enable Default Ripple on the issuer + accountSetTx.SetAsfDefaultRipple() + + flatAccountSet := accountSetTx.Flatten() + accountSetResp, err := client.SubmitTxAndWait(flatAccountSet, submitOpts(&issuer)) + if err != nil { + panic(err) + } + if accountSetResp.Meta.TransactionResult != "tesSUCCESS" { + panic("AccountSet failed: " + accountSetResp.Meta.TransactionResult) + } + + // Create sender trust line to issuer + fmt.Printf("Setting up tutorial: %d/%d\r", step, totalSteps) + step++ + + trustSetTx := transaction.TrustSet{ + BaseTx: transaction.BaseTx{Account: sender.ClassicAddress}, + LimitAmount: types.IssuedCurrencyAmount{ + Currency: currencyCode, + Issuer: issuer.ClassicAddress, + Value: "1000000000", + }, + } + flatTrustSet := trustSetTx.Flatten() + trustSetResp, err := client.SubmitTxAndWait(flatTrustSet, submitOpts(&sender)) + if err != nil { + panic(err) + } + if trustSetResp.Meta.TransactionResult != "tesSUCCESS" { + panic("TrustSet failed: " + trustSetResp.Meta.TransactionResult) + } + + // Issuer sends sender tokens + fmt.Printf("Setting up tutorial: %d/%d\r", step, totalSteps) + + seedTx := transaction.Payment{ + BaseTx: transaction.BaseTx{Account: issuer.ClassicAddress}, + Destination: sender.ClassicAddress, + Amount: types.IssuedCurrencyAmount{ + Currency: currencyCode, + Issuer: issuer.ClassicAddress, + Value: "1000", + }, + } + flatSeed := seedTx.Flatten() + seedResp, err := client.SubmitTxAndWait(flatSeed, submitOpts(&issuer)) + if err != nil { + panic(err) + } + if seedResp.Meta.TransactionResult != "tesSUCCESS" { + panic("Issuer seed payment failed: " + seedResp.Meta.TransactionResult) + } + + // Write setup data + setupData := struct { + Description string `json:"description"` + Issuer any `json:"issuer"` + Sender any `json:"sender"` + Receiver any `json:"receiver"` + }{ + Description: "This file is auto-generated by go/send-trust-line-token-setup/main.go. It stores XRPL account info for the send-a-trust-line-token tutorial.", + Issuer: map[string]string{ + "address": issuer.ClassicAddress.String(), + "seed": issuer.Seed, + }, + Sender: map[string]string{ + "address": sender.ClassicAddress.String(), + "seed": sender.Seed, + }, + Receiver: map[string]string{ + "address": receiver.ClassicAddress.String(), + "seed": receiver.Seed, + }, + } + + jsonData, err := json.MarshalIndent(setupData, "", " ") + if err != nil { + panic(err) + } + if err := os.WriteFile("send-trust-line-token-setup.json", jsonData, 0644); err != nil { + panic(err) + } +} diff --git a/_code-samples/send-a-trust-line-token/go/send-trust-line-token/main.go b/_code-samples/send-a-trust-line-token/go/send-trust-line-token/main.go new file mode 100644 index 00000000000..4fbfc63c3dc --- /dev/null +++ b/_code-samples/send-a-trust-line-token/go/send-trust-line-token/main.go @@ -0,0 +1,216 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + + "github.com/Peersyst/xrpl-go/xrpl/queries/account" + "github.com/Peersyst/xrpl-go/xrpl/queries/common" + "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" +) + +const currencyCode = "FOO" + +func main() { + // Connect to the network ---------------------- + client := websocket.NewClient( + websocket.NewClientConfig(). + WithHost("wss://s.altnet.rippletest.net:51233"), + ) + defer client.Disconnect() + + if err := client.Connect(); err != nil { + panic(err) + } + + // This step checks for the necessary setup data to run the tutorial. + // If missing, send-trust-line-token-setup will generate it. + if _, err := os.Stat("send-trust-line-token-setup.json"); os.IsNotExist(err) { + fmt.Printf("\n=== Tutorial setup data doesn't exist. Running setup script... ===\n\n") + cmd := exec.Command("go", "run", "./send-trust-line-token-setup") + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + panic(err) + } + } + + // Load preconfigured issuer, sender, and receiver accounts. + data, err := os.ReadFile("send-trust-line-token-setup.json") + if err != nil { + panic(err) + } + var setup map[string]any + if err := json.Unmarshal(data, &setup); err != nil { + panic(err) + } + issuer, err := wallet.FromSecret(setup["issuer"].(map[string]any)["seed"].(string)) + if err != nil { + panic(err) + } + sender, err := wallet.FromSecret(setup["sender"].(map[string]any)["seed"].(string)) + if err != nil { + panic(err) + } + receiver, err := wallet.FromSecret(setup["receiver"].(map[string]any)["seed"].(string)) + if err != nil { + panic(err) + } + + fmt.Printf("Issuer address: %s\n", issuer.ClassicAddress) + fmt.Printf("Sender address: %s\n", sender.ClassicAddress) + fmt.Printf("Receiver address: %s\n", receiver.ClassicAddress) + + // Create trust line ---------------------- + // The receiver opts in to the token by creating a trust line to the issuer. + // The LimitAmount sets the maximum amount of the token the receiver will hold. + fmt.Printf("\n=== Creating trust line from receiver to issuer... ===\n\n") + trustSetTx := transaction.TrustSet{ + BaseTx: transaction.BaseTx{ + Account: receiver.ClassicAddress, + }, + LimitAmount: types.IssuedCurrencyAmount{ + Currency: currencyCode, + Issuer: issuer.ClassicAddress, + Value: "1000000000", + }, + } + + flatTrustSet := trustSetTx.Flatten() + trustSetJSON, _ := json.MarshalIndent(flatTrustSet, "", " ") + fmt.Printf("%s\n", string(trustSetJSON)) + + trustSetResponse, err := client.SubmitTxAndWait(flatTrustSet, &wstypes.SubmitOptions{ + Autofill: true, + Wallet: &receiver, + }) + if err != nil { + panic(err) + } + if trustSetResponse.Meta.TransactionResult != "tesSUCCESS" { + fmt.Printf("Error: Unable to create trust line: %s\n", trustSetResponse.Meta.TransactionResult) + os.Exit(1) + } + fmt.Printf("Trust line created from receiver to issuer!\n") + fmt.Printf("Explorer link: https://testnet.xrpl.org/transactions/%s\n", trustSetResponse.Hash.String()) + + // Check initial balances ---------------------- + // getTrustLineBalance returns the trust line balance between `addr` and + // `peer` for the given currency. + getTrustLineBalance := func(addr, currency, peer types.Address) (string, error) { + resp, err := client.Request(&account.LinesRequest{ + Account: addr, + Peer: peer, + LedgerIndex: common.Validated, + }) + if err != nil { + return "", err + } + lines, ok := resp.Result["lines"].([]any) + if !ok { + return "0", nil + } + for _, l := range lines { + line, ok := l.(map[string]any) + if !ok { + continue + } + if line["currency"] == string(currency) { + if bal, ok := line["balance"].(string); ok { + return bal, nil + } + } + } + return "0", nil + } + + fmt.Printf("\n=== Checking initial %s balances... ===\n\n", currencyCode) + senderView, err := getTrustLineBalance(sender.ClassicAddress, currencyCode, issuer.ClassicAddress) + if err != nil { + panic(err) + } + receiverView, err := getTrustLineBalance(receiver.ClassicAddress, currencyCode, issuer.ClassicAddress) + if err != nil { + panic(err) + } + issuerToSender, err := getTrustLineBalance(issuer.ClassicAddress, currencyCode, sender.ClassicAddress) + if err != nil { + panic(err) + } + issuerToReceiver, err := getTrustLineBalance(issuer.ClassicAddress, currencyCode, receiver.ClassicAddress) + if err != nil { + panic(err) + } + fmt.Println("Holders' perspective:") + fmt.Printf(" Sender's balance: %s\n", senderView) + fmt.Printf(" Receiver's balance: %s\n", receiverView) + fmt.Println("Issuer's perspective:") + fmt.Printf(" Owed to sender: %s\n", issuerToSender) + fmt.Printf(" Owed to receiver: %s\n", issuerToReceiver) + + // Send issued token ---------------------- + // The sender pays the receiver with the issued currency. + const sendQuantity = "100" + fmt.Printf("\n=== Sending %s payment... ===\n\n", currencyCode) + paymentTx := transaction.Payment{ + BaseTx: transaction.BaseTx{ + Account: sender.ClassicAddress, + }, + Destination: receiver.ClassicAddress, + Amount: types.IssuedCurrencyAmount{ + Currency: currencyCode, + Issuer: issuer.ClassicAddress, + Value: sendQuantity, + }, + } + + flatPayment := paymentTx.Flatten() + paymentJSON, _ := json.MarshalIndent(flatPayment, "", " ") + fmt.Printf("%s\n", string(paymentJSON)) + + paymentResponse, err := client.SubmitTxAndWait(flatPayment, &wstypes.SubmitOptions{ + Autofill: true, + Wallet: &sender, + }) + if err != nil { + panic(err) + } + if paymentResponse.Meta.TransactionResult != "tesSUCCESS" { + fmt.Printf("Error: Unable to send token: %s\n", paymentResponse.Meta.TransactionResult) + os.Exit(1) + } + fmt.Printf("Payment successful!\n") + fmt.Printf("Explorer link: https://testnet.xrpl.org/transactions/%s\n", paymentResponse.Hash.String()) + + // Verify balances ---------------------- + // Check account balances after the payment. + fmt.Printf("\n=== Checking final %s balances... ===\n\n", currencyCode) + senderView, err = getTrustLineBalance(sender.ClassicAddress, currencyCode, issuer.ClassicAddress) + if err != nil { + panic(err) + } + receiverView, err = getTrustLineBalance(receiver.ClassicAddress, currencyCode, issuer.ClassicAddress) + if err != nil { + panic(err) + } + issuerToSender, err = getTrustLineBalance(issuer.ClassicAddress, currencyCode, sender.ClassicAddress) + if err != nil { + panic(err) + } + issuerToReceiver, err = getTrustLineBalance(issuer.ClassicAddress, currencyCode, receiver.ClassicAddress) + if err != nil { + panic(err) + } + fmt.Println("Holders' perspective:") + fmt.Printf(" Sender's balance: %s\n", senderView) + fmt.Printf(" Receiver's balance: %s\n", receiverView) + fmt.Println("Issuer's perspective:") + fmt.Printf(" Owed to sender: %s\n", issuerToSender) + fmt.Printf(" Owed to receiver: %s\n", issuerToReceiver) +} diff --git a/_code-samples/send-a-trust-line-token/js/.claude/settings.local.json b/_code-samples/send-a-trust-line-token/js/.claude/settings.local.json new file mode 100644 index 00000000000..a6ca3a601bb --- /dev/null +++ b/_code-samples/send-a-trust-line-token/js/.claude/settings.local.json @@ -0,0 +1,19 @@ +{ + "permissions": { + "allow": [ + "Bash(gh api *)", + "Bash(python scripts/diagnose.py --network testnet account r9hubmAsLytrAiAF9oN6rNHdqPitkRCoQv)", + "Bash(python3 scripts/diagnose.py --network testnet account r9hubmAsLytrAiAF9oN6rNHdqPitkRCoQv)", + "Bash(python3 -c \"import xrpl; print\\('xrpl-py', xrpl.__version__\\)\")", + "Bash(curl -s -X POST https://s.altnet.rippletest.net:51234/ -H 'Content-Type: application/json' -d '{\"method\":\"account_info\",\"params\":[{\"account\":\"r9hubmAsLytrAiAF9oN6rNHdqPitkRCoQv\",\"ledger_index\":\"validated\"}]}')", + "Bash(python3 -m json.tool)", + "Bash(curl -s -X POST https://s.altnet.rippletest.net:51234/ -H 'Content-Type: application/json' -d '{\"method\":\"account_tx\",\"params\":[{\"account\":\"r9hubmAsLytrAiAF9oN6rNHdqPitkRCoQv\",\"ledger_index_min\":-1,\"ledger_index_max\":-1,\"limit\":10,\"binary\":false}]}')", + "Bash(python3 -c ' *)", + "Bash(python3 -c \"import json;d=json.load\\(open\\('setup.json'\\)\\);print\\('issuer ',d['issuer']['address']\\);print\\('sender ',d['sender']['address']\\);print\\('receiver',d['receiver']['address']\\)\")" + ] + }, + "enabledMcpjsonServers": [ + "context7", + "xrpl-dev-portal" + ] +} diff --git a/_code-samples/send-a-trust-line-token/js/README.md b/_code-samples/send-a-trust-line-token/js/README.md new file mode 100644 index 00000000000..356451af091 --- /dev/null +++ b/_code-samples/send-a-trust-line-token/js/README.md @@ -0,0 +1,70 @@ +# Send a Trust Line Token (JavaScript) + +Example code for sending a trust line token between two accounts on Testnet with [xrpl.js](https://github.com/XRPLF/xrpl.js). + +## Setup + +```sh +npm install +``` + +## Send a Trust Line Token + +```sh +node sendTrustLineToken.js +``` + +You should see output like the following: + +```sh +Issuer address: rUrWYSHAX8kVwwPRxgVzhYQg9Ko3kxoqoy +Sender address: rEx369HEEn1S2Cg2WC6Wi66awc45KzNBfV +Receiver address: rE1LWiuCAB7VogxWAAymFegbB7t1WKsD6v + +=== Creating trust line from receiver to issuer... === + +{ + "TransactionType": "TrustSet", + "Account": "rE1LWiuCAB7VogxWAAymFegbB7t1WKsD6v", + "LimitAmount": { + "currency": "FOO", + "issuer": "rUrWYSHAX8kVwwPRxgVzhYQg9Ko3kxoqoy", + "value": "1000000000" + } +} +Trust line created from receiver to issuer! +Explorer link: https://testnet.xrpl.org/transactions/76AE546380FFA6CBDD3967FFECF2F75093D0EEC9925DE7969CF63FCFC71A439E + +=== Checking initial FOO balances... === + +Holders' perspective: + Sender's balance: 600 + Receiver's balance: 400 +Issuer's perspective: + Owed to sender: -600 + Owed to receiver: -400 + +=== Sending FOO payment... === + +{ + "TransactionType": "Payment", + "Account": "rEx369HEEn1S2Cg2WC6Wi66awc45KzNBfV", + "Destination": "rE1LWiuCAB7VogxWAAymFegbB7t1WKsD6v", + "Amount": { + "currency": "FOO", + "issuer": "rUrWYSHAX8kVwwPRxgVzhYQg9Ko3kxoqoy", + "value": "100" + } +} +Payment successful! +Explorer link: https://testnet.xrpl.org/transactions/E6313B9CEA42C45EA1047790BCF1DC5B8D49A35AEAC5735288ABBD252C56D1AD + +=== Checking final FOO balances... === + +Holders' perspective: + Sender's balance: 500 + Receiver's balance: 500 +Issuer's perspective: + Owed to sender: -500 + Owed to receiver: -500 +``` diff --git a/_code-samples/send-a-trust-line-token/js/package.json b/_code-samples/send-a-trust-line-token/js/package.json new file mode 100644 index 00000000000..16ac149e536 --- /dev/null +++ b/_code-samples/send-a-trust-line-token/js/package.json @@ -0,0 +1,8 @@ +{ + "name": "send-a-trust-line-token-examples", + "description": "Example code for sending a trust line token on the XRP Ledger.", + "dependencies": { + "xrpl": "^4.6.0" + }, + "type": "module" +} diff --git a/_code-samples/send-a-trust-line-token/js/sendTrustLineToken.js b/_code-samples/send-a-trust-line-token/js/sendTrustLineToken.js new file mode 100644 index 00000000000..6f0fae323aa --- /dev/null +++ b/_code-samples/send-a-trust-line-token/js/sendTrustLineToken.js @@ -0,0 +1,118 @@ +import xrpl from 'xrpl' +import fs from 'fs' +import { setup } from './sendTrustLineTokenSetup.js' + +// Set up client ---------------------- +const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233') +await client.connect() + +// This step checks for the necessary setup data to run the tutorial. +// If missing, sendTrustLineTokenSetup.js will generate it. +if (!fs.existsSync('sendTrustLineTokenSetup.json')) { + console.log(`\n=== Tutorial setup data doesn't exist. Running setup script... ===\n`) + await setup() +} + +// Load preconfigured issuer, sender, and receiver accounts. +const setupData = JSON.parse(fs.readFileSync('sendTrustLineTokenSetup.json', 'utf8')) +const issuer = xrpl.Wallet.fromSeed(setupData.issuer.seed) +const sender = xrpl.Wallet.fromSeed(setupData.sender.seed) +const receiver = xrpl.Wallet.fromSeed(setupData.receiver.seed) +const currencyCode = 'FOO' + +console.log(`Issuer address: ${issuer.address}`) +console.log(`Sender address: ${sender.address}`) +console.log(`Receiver address: ${receiver.address}`) + +// Create trust line ---------------------- +// The receiver opts in to the token by creating a trust line to the issuer. +// The LimitAmount sets the maximum amount of the token the receiver will hold. +console.log(`\n=== Creating trust line from receiver to issuer... ===\n`) +const trustSetTx = { + TransactionType: 'TrustSet', + Account: receiver.address, + LimitAmount: { + currency: currencyCode, + issuer: issuer.address, + value: '1000000000' + } +} +xrpl.validate(trustSetTx) +console.log(JSON.stringify(trustSetTx, null, 2)) + +const trustSetResponse = await client.submitAndWait(trustSetTx, { + wallet: receiver, + autofill: true +}) +if (trustSetResponse.result.meta?.TransactionResult !== 'tesSUCCESS') { + const resultCode = trustSetResponse.result.meta?.TransactionResult + console.error('Error: Unable to create trust line:', resultCode) + await client.disconnect() + process.exit(1) +} +console.log('Trust line created from receiver to issuer!') +console.log(`Explorer link: https://testnet.xrpl.org/transactions/${trustSetResponse.result.hash}`) + +// Check initial balances ---------------------- +// getTrustLineBalance returns the trust line balance between `account` and +// `peer` for the given currency. +async function getTrustLineBalance(account, currency, peer) { + const response = await client.request({ + command: 'account_lines', + account, + peer, + ledger_index: 'validated' + }) + const line = response.result.lines.find(l => l.currency === currency) + return line?.balance ?? '0' +} + +console.log(`\n=== Checking initial ${currencyCode} balances... ===\n`) +console.log("Holders' perspective:") +console.log(` Sender's balance: ${await getTrustLineBalance(sender.address, currencyCode, issuer.address)}`) +console.log(` Receiver's balance: ${await getTrustLineBalance(receiver.address, currencyCode, issuer.address)}`) +console.log("Issuer's perspective:") +console.log(` Owed to sender: ${await getTrustLineBalance(issuer.address, currencyCode, sender.address)}`) +console.log(` Owed to receiver: ${await getTrustLineBalance(issuer.address, currencyCode, receiver.address)}`) + +// Send issued token ---------------------- +// The sender pays the receiver with the issued currency. +const sendQuantity = '100' +console.log(`\n=== Sending ${currencyCode} payment... ===\n`) +const paymentTx = { + TransactionType: 'Payment', + Account: sender.address, + Destination: receiver.address, + Amount: { + currency: currencyCode, + issuer: issuer.address, + value: sendQuantity + } +} +xrpl.validate(paymentTx) +console.log(JSON.stringify(paymentTx, null, 2)) + +const paymentResponse = await client.submitAndWait(paymentTx, { + wallet: sender, + autofill: true +}) +if (paymentResponse.result.meta?.TransactionResult !== 'tesSUCCESS') { + const resultCode = paymentResponse.result.meta?.TransactionResult + console.error('Error: Unable to send token:', resultCode) + await client.disconnect() + process.exit(1) +} +console.log('Payment successful!') +console.log(`Explorer link: https://testnet.xrpl.org/transactions/${paymentResponse.result.hash}`) + +// Verify balances ---------------------- +// Check account balances after the payment. +console.log(`\n=== Checking final ${currencyCode} balances... ===\n`) +console.log("Holders' perspective:") +console.log(` Sender's balance: ${await getTrustLineBalance(sender.address, currencyCode, issuer.address)}`) +console.log(` Receiver's balance: ${await getTrustLineBalance(receiver.address, currencyCode, issuer.address)}`) +console.log("Issuer's perspective:") +console.log(` Owed to sender: ${await getTrustLineBalance(issuer.address, currencyCode, sender.address)}`) +console.log(` Owed to receiver: ${await getTrustLineBalance(issuer.address, currencyCode, receiver.address)}`) + +await client.disconnect() diff --git a/_code-samples/send-a-trust-line-token/js/sendTrustLineTokenSetup.js b/_code-samples/send-a-trust-line-token/js/sendTrustLineTokenSetup.js new file mode 100644 index 00000000000..55364e6bd48 --- /dev/null +++ b/_code-samples/send-a-trust-line-token/js/sendTrustLineTokenSetup.js @@ -0,0 +1,106 @@ +import xrpl from 'xrpl' +import fs from 'fs' +import { fileURLToPath } from 'url' + +const currencyCode = 'FOO' +const TOTAL_STEPS = 4 + +export async function setup() { + process.stdout.write(`Setting up tutorial: 0/${TOTAL_STEPS}\r`) + + const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233') + await client.connect() + + // Fund issuer, sender, and receiver wallets from the Testnet faucet. + const [ + { wallet: issuer }, + { wallet: sender }, + { wallet: receiver } + ] = await Promise.all([ + client.fundWallet(), + client.fundWallet(), + client.fundWallet() + ]) + + process.stdout.write(`Setting up tutorial: 1/${TOTAL_STEPS}\r`) + + // Enable Default Ripple on the issuer so the token can ripple through the + // issuer's trust lines when holders send it between each other. + const accountSetResponse = await client.submitAndWait({ + TransactionType: 'AccountSet', + Account: issuer.address, + SetFlag: xrpl.AccountSetAsfFlags.asfDefaultRipple + }, { wallet: issuer, autofill: true }) + + if (accountSetResponse.result.meta?.TransactionResult !== 'tesSUCCESS') { + await client.disconnect() + throw new Error(`AccountSet failed: ${accountSetResponse.result.meta?.TransactionResult}`) + } + + process.stdout.write(`Setting up tutorial: 2/${TOTAL_STEPS}\r`) + + // Create a trust line from the sender to the issuer so the sender can hold the token. + const senderTrustResponse = await client.submitAndWait({ + TransactionType: 'TrustSet', + Account: sender.address, + LimitAmount: { + currency: currencyCode, + issuer: issuer.address, + value: '1000000000' + } + }, { wallet: sender, autofill: true }) + + if (senderTrustResponse.result.meta?.TransactionResult !== 'tesSUCCESS') { + await client.disconnect() + throw new Error(`Sender TrustSet failed: ${senderTrustResponse.result.meta?.TransactionResult}`) + } + + process.stdout.write(`Setting up tutorial: 3/${TOTAL_STEPS}\r`) + + // Issue the token from the issuer to the sender so the sender has a balance to send. + const issueResponse = await client.submitAndWait({ + TransactionType: 'Payment', + Account: issuer.address, + Destination: sender.address, + Amount: { + currency: currencyCode, + issuer: issuer.address, + value: '1000' + } + }, { wallet: issuer, autofill: true }) + + if (issueResponse.result.meta?.TransactionResult !== 'tesSUCCESS') { + await client.disconnect() + throw new Error(`Issuer Payment failed: ${issueResponse.result.meta?.TransactionResult}`) + } + + process.stdout.write(`Setting up tutorial: 4/${TOTAL_STEPS}\r`) + + // Write setup data to JSON file. + const setupData = { + description: 'This file is auto-generated by sendTrustLineTokenSetup.js. It stores XRPL account info for use in the Send a Trust Line Token tutorial.', + issuer: { + address: issuer.address, + seed: issuer.seed + }, + sender: { + address: sender.address, + seed: sender.seed + }, + receiver: { + address: receiver.address, + seed: receiver.seed + } + } + + fs.writeFileSync('sendTrustLineTokenSetup.json', JSON.stringify(setupData, null, 2)) + + process.stdout.write('Setting up tutorial: Complete!\n') + + await client.disconnect() +} + +// Allow running this file directly: `node sendTrustLineTokenSetup.js` +if (process.argv[1] === fileURLToPath(import.meta.url)) { + await setup() +} diff --git a/_code-samples/send-a-trust-line-token/py/README.md b/_code-samples/send-a-trust-line-token/py/README.md new file mode 100644 index 00000000000..81c8e8d3092 --- /dev/null +++ b/_code-samples/send-a-trust-line-token/py/README.md @@ -0,0 +1,74 @@ +# Send a Trust Line Token (Python) + +Example code for sending a trust line token between two accounts on Testnet with [xrpl-py](https://github.com/XRPLF/xrpl-py). + +## Setup + +```sh +python -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +``` + +## Send a Trust Line Token + +```sh +python send_trust_line_token.py +``` + +You should see output like the following: + +```sh +Issuer address: rpQrTB7Zko3i8QTZa1jn3MqTjFFtjvnpDD +Sender address: rNXVfWdMs8oScEDBUfZgv5Auds6ahWvgMS +Receiver address: rQpATksgqj3XNatYZ89mnxPrCXgSvuWKkN + +=== Creating trust line from receiver to issuer... === + +{ + "Account": "rQpATksgqj3XNatYZ89mnxPrCXgSvuWKkN", + "TransactionType": "TrustSet", + "SigningPubKey": "", + "LimitAmount": { + "currency": "FOO", + "issuer": "rpQrTB7Zko3i8QTZa1jn3MqTjFFtjvnpDD", + "value": "1000000000" + } +} +Trust line created from receiver to issuer! +Explorer link: https://testnet.xrpl.org/transactions/9DA16FEE0B361C96BAD6F341E15E4FD6A6601BC4DD6227EC7EF655C29A1A412D + +=== Checking initial FOO balances... === + +Holders' perspective: + Sender's balance: 900 + Receiver's balance: 100 +Issuer's perspective: + Owed to sender: -900 + Owed to receiver: -100 + +=== Sending FOO payment... === + +{ + "Account": "rNXVfWdMs8oScEDBUfZgv5Auds6ahWvgMS", + "TransactionType": "Payment", + "SigningPubKey": "", + "Amount": { + "currency": "FOO", + "issuer": "rpQrTB7Zko3i8QTZa1jn3MqTjFFtjvnpDD", + "value": "100" + }, + "Destination": "rQpATksgqj3XNatYZ89mnxPrCXgSvuWKkN" +} +Payment successful! +Explorer link: https://testnet.xrpl.org/transactions/7779331C8AE458A8342A36CCCD53CA5ED03219C61B8F159EDC36A1BBD6FC85EC + +=== Checking final FOO balances... === + +Holders' perspective: + Sender's balance: 800 + Receiver's balance: 200 +Issuer's perspective: + Owed to sender: -800 + Owed to receiver: -200 +``` diff --git a/_code-samples/send-a-trust-line-token/py/requirements.txt b/_code-samples/send-a-trust-line-token/py/requirements.txt new file mode 100644 index 00000000000..6550f9202ad --- /dev/null +++ b/_code-samples/send-a-trust-line-token/py/requirements.txt @@ -0,0 +1 @@ +xrpl-py>=4.5.0 diff --git a/_code-samples/send-a-trust-line-token/py/send_trust_line_token.py b/_code-samples/send-a-trust-line-token/py/send_trust_line_token.py new file mode 100644 index 00000000000..114ad42cc4f --- /dev/null +++ b/_code-samples/send-a-trust-line-token/py/send_trust_line_token.py @@ -0,0 +1,112 @@ +import asyncio +import json +import os +import sys + +from xrpl.clients import JsonRpcClient +from xrpl.models import AccountLines +from xrpl.models.amounts import IssuedCurrencyAmount +from xrpl.models.transactions import Payment, TrustSet +from xrpl.transaction import submit_and_wait +from xrpl.wallet import Wallet + +from send_trust_line_token_setup import main as run_setup + +# Set up client ---------------------- +client = JsonRpcClient("https://s.altnet.rippletest.net:51234") + +# This step checks for the necessary setup data to run the tutorial. +# If missing, send_trust_line_token_setup.py will generate it. +if not os.path.exists("send_trust_line_token_setup.json"): + print("\n=== Tutorial setup data doesn't exist. Running setup script... ===\n") + asyncio.run(run_setup()) + +# Load preconfigured issuer, sender, and receiver accounts. +with open("send_trust_line_token_setup.json") as f: + setup_data = json.load(f) + +issuer = Wallet.from_seed(setup_data["issuer"]["seed"]) +sender = Wallet.from_seed(setup_data["sender"]["seed"]) +receiver = Wallet.from_seed(setup_data["receiver"]["seed"]) +currency_code = "FOO" + +print(f"Issuer address: {issuer.address}") +print(f"Sender address: {sender.address}") +print(f"Receiver address: {receiver.address}") + +# Create trust line ---------------------- +# The receiver opts in to the token by creating a trust line to the issuer. +# The limit_amount sets the maximum amount of the token the receiver will hold. +print("\n=== Creating trust line from receiver to issuer... ===\n") +trust_set_tx = TrustSet( + account=receiver.address, + limit_amount=IssuedCurrencyAmount( + currency=currency_code, + issuer=issuer.address, + value="1000000000", + ), +) +print(json.dumps(trust_set_tx.to_xrpl(), indent=2)) + +trust_set_response = submit_and_wait(trust_set_tx, client, receiver) +result_code = trust_set_response.result["meta"]["TransactionResult"] +if result_code != "tesSUCCESS": + print(f"Error: Unable to create trust line: {result_code}") + sys.exit(1) +print("Trust line created from receiver to issuer!") +print(f"Explorer link: https://testnet.xrpl.org/transactions/{trust_set_response.result['hash']}") + +# Check initial balances ---------------------- +# get_trust_line_balance returns the trust line balance between `account` and +# `peer` for the given currency. +def get_trust_line_balance(account, currency, peer): + response = client.request(AccountLines( + account=account, + peer=peer, + ledger_index="validated", + )) + for line in response.result.get("lines", []): + if line["currency"] == currency: + return line["balance"] + return "0" + +print(f"\n=== Checking initial {currency_code} balances... ===\n") +print("Holders' perspective:") +print(f" Sender's balance: {get_trust_line_balance(sender.address, currency_code, issuer.address)}") +print(f" Receiver's balance: {get_trust_line_balance(receiver.address, currency_code, issuer.address)}") +print("Issuer's perspective:") +print(f" Owed to sender: {get_trust_line_balance(issuer.address, currency_code, sender.address)}") +print(f" Owed to receiver: {get_trust_line_balance(issuer.address, currency_code, receiver.address)}") + +# Send issued token ---------------------- +# The sender pays the receiver with the issued currency. +send_quantity = "100" +print(f"\n=== Sending {currency_code} payment... ===\n") +payment_tx = Payment( + account=sender.address, + destination=receiver.address, + amount=IssuedCurrencyAmount( + currency=currency_code, + issuer=issuer.address, + value=send_quantity, + ), +) +print(json.dumps(payment_tx.to_xrpl(), indent=2)) + +payment_response = submit_and_wait(payment_tx, client, sender) +result_code = payment_response.result["meta"]["TransactionResult"] +if result_code != "tesSUCCESS": + print(f"Error: Unable to send token: {result_code}") + sys.exit(1) +print("Payment successful!") +print(f"Explorer link: https://testnet.xrpl.org/transactions/{payment_response.result['hash']}") + +# Verify balances ---------------------- +# Check account balances after the payment. +print(f"\n=== Checking final {currency_code} balances... ===\n") +print("Holders' perspective:") +print(f" Sender's balance: {get_trust_line_balance(sender.address, currency_code, issuer.address)}") +print(f" Receiver's balance: {get_trust_line_balance(receiver.address, currency_code, issuer.address)}") +print("Issuer's perspective:") +print(f" Owed to sender: {get_trust_line_balance(issuer.address, currency_code, sender.address)}") +print(f" Owed to receiver: {get_trust_line_balance(issuer.address, currency_code, receiver.address)}") diff --git a/_code-samples/send-a-trust-line-token/py/send_trust_line_token_setup.py b/_code-samples/send-a-trust-line-token/py/send_trust_line_token_setup.py new file mode 100644 index 00000000000..3609bbfa1f2 --- /dev/null +++ b/_code-samples/send-a-trust-line-token/py/send_trust_line_token_setup.py @@ -0,0 +1,108 @@ +import asyncio +import json + +from xrpl.asyncio.clients import AsyncWebsocketClient +from xrpl.asyncio.transaction import submit_and_wait +from xrpl.asyncio.wallet import generate_faucet_wallet +from xrpl.models.amounts import IssuedCurrencyAmount +from xrpl.models.transactions import AccountSet, Payment, TrustSet +from xrpl.models.transactions.account_set import AccountSetAsfFlag + +WSS_URL = "wss://s.altnet.rippletest.net:51233" +CURRENCY_CODE = "FOO" +TOTAL_STEPS = 4 + + +async def main(): + async with AsyncWebsocketClient(WSS_URL) as client: + print(f"Setting up tutorial: 0/{TOTAL_STEPS}", end="\r") + + # Fund issuer, sender, and receiver wallets from the Testnet faucet in parallel. + issuer, sender, receiver = await asyncio.gather( + generate_faucet_wallet(client), + generate_faucet_wallet(client), + generate_faucet_wallet(client), + ) + + print(f"Setting up tutorial: 1/{TOTAL_STEPS}", end="\r") + + # Enable Default Ripple on the issuer so holders can send + # the token to each other. + account_set_response = await submit_and_wait( + AccountSet( + account=issuer.address, + set_flag=AccountSetAsfFlag.ASF_DEFAULT_RIPPLE, + ), + client, + issuer, + ) + result_code = account_set_response.result["meta"]["TransactionResult"] + if result_code != "tesSUCCESS": + raise RuntimeError(f"AccountSet failed: {result_code}") + + print(f"Setting up tutorial: 2/{TOTAL_STEPS}", end="\r") + + # Create a trust line from the sender to the issuer so the sender can hold the token. + sender_trust_response = await submit_and_wait( + TrustSet( + account=sender.address, + limit_amount=IssuedCurrencyAmount( + currency=CURRENCY_CODE, + issuer=issuer.address, + value="1000000000", + ), + ), + client, + sender, + ) + result_code = sender_trust_response.result["meta"]["TransactionResult"] + if result_code != "tesSUCCESS": + raise RuntimeError(f"Sender TrustSet failed: {result_code}") + + print(f"Setting up tutorial: 3/{TOTAL_STEPS}", end="\r") + + # Issue the token from the issuer to the sender so the sender has a balance to send. + issue_response = await submit_and_wait( + Payment( + account=issuer.address, + destination=sender.address, + amount=IssuedCurrencyAmount( + currency=CURRENCY_CODE, + issuer=issuer.address, + value="1000", + ), + ), + client, + issuer, + ) + result_code = issue_response.result["meta"]["TransactionResult"] + if result_code != "tesSUCCESS": + raise RuntimeError(f"Issuer Payment failed: {result_code}") + + print(f"Setting up tutorial: 4/{TOTAL_STEPS}", end="\r") + + # Write setup data to JSON file. + setup_data = { + "description": "This file is auto-generated by send_trust_line_token_setup.py. It stores XRPL account info for use in the Send a Trust Line Token tutorial.", + "issuer": { + "address": issuer.address, + "seed": issuer.seed, + }, + "sender": { + "address": sender.address, + "seed": sender.seed, + }, + "receiver": { + "address": receiver.address, + "seed": receiver.seed, + }, + } + + with open("send_trust_line_token_setup.json", "w") as f: + json.dump(setup_data, f, indent=2) + + print("Setting up tutorial: Complete!") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/_code-samples/send-an-mpt/go/README.md b/_code-samples/send-an-mpt/go/README.md index 45ceffa4594..d5f1f5cb874 100644 --- a/_code-samples/send-an-mpt/go/README.md +++ b/_code-samples/send-an-mpt/go/README.md @@ -1,4 +1,4 @@ -# Send an MPT (Go) +# Send an MPT (Go) Example code for sending a Multi-Purpose Token (MPT) between non-issuer holders on testnet with [xrpl-go](https://github.com/Peersyst/xrpl-go). diff --git a/_code-samples/send-an-mpt/py/send_mpt_setup.py b/_code-samples/send-an-mpt/py/send_mpt_setup.py index 2ecc93ef188..f17ad1f2c81 100644 --- a/_code-samples/send-an-mpt/py/send_mpt_setup.py +++ b/_code-samples/send-an-mpt/py/send_mpt_setup.py @@ -46,7 +46,7 @@ async def main(): account=issuer.address, asset_scale=2, maximum_amount="1000000", - transfer_fee=30, + transfer_fee=0, flags=MPTokenIssuanceCreateFlag.TF_MPT_CAN_TRANSFER, mptoken_metadata=encode_mptoken_metadata(metadata), ), diff --git a/docs/img/mt-send-currency-1-empty-form-info.png b/docs/img/mt-send-currency-1-empty-form-info.png deleted file mode 100644 index 745906dc34d..00000000000 Binary files a/docs/img/mt-send-currency-1-empty-form-info.png and /dev/null differ diff --git a/docs/img/mt-send-currency-2-distribute-accounts.png b/docs/img/mt-send-currency-2-distribute-accounts.png deleted file mode 100644 index a5de8f3dd3c..00000000000 Binary files a/docs/img/mt-send-currency-2-distribute-accounts.png and /dev/null differ diff --git a/docs/img/mt-send-currency-3-create-trustline.png b/docs/img/mt-send-currency-3-create-trustline.png deleted file mode 100644 index b82411ad3b9..00000000000 Binary files a/docs/img/mt-send-currency-3-create-trustline.png and /dev/null differ diff --git a/docs/img/mt-send-currency-4-send-currency.png b/docs/img/mt-send-currency-4-send-currency.png deleted file mode 100644 index ff4190f9a53..00000000000 Binary files a/docs/img/mt-send-currency-4-send-currency.png and /dev/null differ diff --git a/docs/img/mt-send-currency-5-issuer-token-balance.png b/docs/img/mt-send-currency-5-issuer-token-balance.png deleted file mode 100644 index efd31358d12..00000000000 Binary files a/docs/img/mt-send-currency-5-issuer-token-balance.png and /dev/null differ diff --git a/docs/img/mt-send-currency-6-holder-token-balance.png b/docs/img/mt-send-currency-6-holder-token-balance.png deleted file mode 100644 index f7bca2cb91b..00000000000 Binary files a/docs/img/mt-send-currency-6-holder-token-balance.png and /dev/null differ diff --git a/docs/tutorials/index.page.tsx b/docs/tutorials/index.page.tsx index 36317306b1b..53719588834 100644 --- a/docs/tutorials/index.page.tsx +++ b/docs/tutorials/index.page.tsx @@ -106,6 +106,7 @@ const sectionConfig: Record - - Send Currency - - - - - - - - - - - - - - -

Send Currency

-
- - Choose your ledger instance: - -    - - -    - - -

- - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - - - - -
- - - - - - - - - - - -
- - - - - - - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - -
- - - - - - - -
- - - - - -
-
- - - - - -
- - Currency Code - - - -
-
- -
- - Issuer - - - -
-
- -
- - - - - -
-
- -
- - Destination - - - -
-
-

- -

-
-
- -
-
- - - -``` diff --git a/docs/tutorials/payments/create-trust-line-send-currency-in-python.md b/docs/tutorials/payments/create-trust-line-send-currency-in-python.md deleted file mode 100644 index cb6adbfe13f..00000000000 --- a/docs/tutorials/payments/create-trust-line-send-currency-in-python.md +++ /dev/null @@ -1,580 +0,0 @@ ---- -html: py-create-trustline-send-currency.html -parent: send-payments-using-python.html -seo: - description: Create trust lines and send currency. -labels: - - Cross-Currency - - Payments - - Quickstart - - Tokens ---- -# Create Trust Line and Send Currency Using Python - -This example shows how to: - -1. Configure accounts to allow transfer of funds to third party accounts. -2. Set a currency type for transactions. -3. Create a trust line between the standby account and the operational account. -4. Send issued currency between accounts. -5. Display account balances for all currencies. - -[![Test harness with currency transfer](/docs/img/quickstart-py5.png)](/docs/img/quickstart-py5.png) - -You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/) archive to try each of the samples in your own browser. - -{% admonition type="info" name="Note" %}Without the Quickstart Samples, you will not be able to try the examples that follow. {% /admonition %} - -## Usage - -Open the Quickstart window and get accounts: - -1. Open and run `lesson2-send-currency.py`. -2. Get test accounts. - 1. If you have existing account seeds - 1. Paste account seeds in the **Seeds** field. - 2. Click **Get Accounts from Seeds**. - 2. If you do not have account seeds: - 1. Click **Get New Standby Account**. - 2. Click **Get New Operational Account**. - -## Create Trust Line - -
- -
- - -To create a trust line between accounts: - -1. Enter a [currency code](https://www.iban.com/currency-codes) in the **Currency** field. -2. Enter the maximum transfer limit in the **Amount** field. -3. Enter the destination account value in the **Destination** field. -4. Click **Create Trust Line**. - -[![Trust line results](/docs/img/quickstart-py6.png)](/docs/img/quickstart-py6.png) - -## Send an Issued Currency Token - -To transfer an issued currency token, once you have created a trust line: - -1. Enter the **Amount**. -2. Enter the **Destination**. -3. Enter the **Currency** type. -4. Click **Send Currency**. - -[![Currency transfer](/docs/img/quickstart-py7.png)](/docs/img/quickstart-py7.png) - -### Configure Account - -When transferring fiat currency, the actual transfer of funds is not simultaneous, as it is with XRP. If currency is transferred to a third party for a different currency, there can be a devaluation of the currency that impacts the originating account. To avoid this situation, this up and down valuation of currency, known as _rippling_, is not allowed by default. Currency transferred from one account can only be transferred back to the same account. To enable currency transfer to third parties, you need to set the `rippleDefault` value to true. The Token Test Harness provides a checkbox to enable or disable rippling. - -To enable rippling: - -1. Select the **Allow Rippling** checkbox. -2. Click **Configure Account**. - -Verify the setting by looking for the _Set Flag_ value in the response, which should show a flag setting of _8_. - -[![Configure Account - Enable Rippling](/docs/img/quickstart-py8.png)](/docs/img/quickstart-py8.png) - -To disable rippling: - -1. Deselect the **Allow Rippling** checkbox. -2. Click **Configure Account**. - -Verify the setting by looking for the _Clear Flag_ value in the response, which shold show a flag setting of _8_. - -[![Configure Account - Disable Rippling](/docs/img/quickstart-py9.png)](/docs/img/quickstart-py9.png) - -# Code Walkthrough - -You can download the [Quickstart Samples](https://github.com/XRPLF/xrpl-dev-portal/tree/master/_code-samples/quickstart/py/) archive to try each of the samples. - -## mod2.py - -Module 2 provides the logic for creating trust lines and sending issued currency tokens between accounts. - -Import dependencies and set the `testnet_url`. - -```python -import xrpl -from xrpl.clients import JsonRpcClient -from xrpl.wallet import Wallet - -testnet_url = "https://s.altnet.rippletest.net:51234" -``` - -### Create Trust Line - -Pass the wallet seed, the issuer account, the currency code, and the maximum amount of currency to send. - -```python -def create_trust_line(seed, issuer, currency, amount): - """create_trust_line""" -``` - -Get the wallet and a new client instance. - -```python - receiving_wallet = Wallet.from_seed(seed) - client = JsonRpcClient(testnet_url) -``` - -Define the `TrustSet` transaction. - -```python - trustline_tx=xrpl.models.transactions.TrustSet( - account=receiving_wallet.address, - limit_amount=xrpl.models.amounts.IssuedCurrencyAmount( - currency=currency, - issuer=issuer, - value=int(amount) - ) - ) -``` - -Submit the transaction to the XRP Ledger. - -```python - response = xrpl.transaction.submit_and_wait(trustline_tx, - client, receiving_wallet) -``` - -Return the results. - -```python - return response.result -``` -## send_currency - -Send currency to another account based on the sender wallet, destination account, the currency type, and the amount of the currency. - -```python -def send_currency(seed, destination, currency, amount): - """send_currency""" -``` - -Get the sending wallet and a client instance on Testnet. - -```python - sending_wallet=Wallet.from_seed(seed) - client=JsonRpcClient(testnet_url) -``` - -Define the payment transaction. The amount requires further description to identify the type of currency and issuer. - -```python - send_currency_tx=xrpl.models.transactions.Payment( - account=sending_wallet.address, - amount=xrpl.models.amounts.IssuedCurrencyAmount( - currency=currency, - value=int(amount), - issuer=sending_wallet.address - ), - destination=destination - ) -``` - -Submit the transaction and get the response. - -```python - response=xrpl.transaction.submit_and_wait(send_currency_tx, client, sending_wallet)``` - -Return the results. - -```python - return response.result -``` - -### get_balance - -Update the **XRP Balance** fields and list the balance information for issued currencies in the **Results** text areas. - -```python -def get_balance(sb_account_id, op_account_id): - """get_balance""" -``` - -Connect to the XRP Ledger and instantiate a client. - -```python - client=JsonRpcClient(testnet_url) -``` - -Create the `GatewayBalances` request. - -```python - balance=xrpl.models.requests.GatewayBalances( - account=sb_account_id, - ledger_index="validated", - hotwallet=[op_account_id] - ) -``` - -Return the result. - -```python - response = client.request(balance) - return response.result -``` - -### configure_account - -This example shows how to set and clear configuration flags using the `AccountSet` method. The `ASF_DEFAULT_RIPPLE` flag is pertinent to experimentation with transfer of issued currencies to third-party accounts, so it is demonstrated here. You can set any of the configuration flags using the same structure, substituting the particular flags you want to set. See [AccountSet Flags](../../references/protocol/transactions/types/accountset.md#accountset-flags). - -Send the account seed and a Boolean value for whether to enable or disable rippling. -```python -def configure_account(seed, default_setting): - """configure_account" -``` - -Get the account wallet and instantiate a client. - -```python - wallet=Wallet.from_seed(seed) - client=JsonRpcClient(testnet_url) -``` - -If `default_setting` is true, create a `set_flag` transaction to enable rippling. If false, create a `clear_flag` transaction to disable rippling. - -```python - if (default_setting): - setting_tx=xrpl.models.transactions.AccountSet( - account=wallet.classic_address, - set_flag=xrpl.models.transactions.AccountSetAsfFlag.ASF_DEFAULT_RIPPLE - ) - else: - setting_tx=xrpl.models.transactions.AccountSet( - account=wallet.classic_address, - set_flag=xrpl.models.transactions.AccountSetAsfFlag.ASF_DEFAULT_RIPPLE - ) -``` - -Submit the transaction and get results. - -```python - response=xrpl.transaction.submit_and_wait(setting_tx,client,wallet) - return response.result -``` - -## lesson2-send-currency.py - -This module builds on `lesson1-send-xrp.py`. Changes are noted below. - -```python -import tkinter as tk -import xrpl -import json -``` - -Import methods from `mod2.py`. - -```python -from mod1 import get_account, get_account_info, send_xrp -from mod2 import ( - create_trust_line, - send_currency, - get_balance, - configure_account, -) -``` - -Module 2 Handlers. - -```python -def standby_create_trust_line(): - results = create_trust_line(ent_standby_seed.get(), - ent_standby_destination.get(), - ent_standby_currency.get(), - ent_standby_amount.get()) - text_standby_results.delete("1.0", tk.END) - text_standby_results.insert("1.0", json.dumps(results, indent=4)) - - -def standby_send_currency(): - results = send_currency(ent_standby_seed.get(), - ent_standby_destination.get(), - ent_standby_currency.get(), - ent_standby_amount.get()) - text_standby_results.delete("1.0", tk.END) - text_standby_results.insert("1.0", json.dumps(results, indent=4)) - - -def standby_configure_account(): - results = configure_account( - ent_standby_seed.get(), - standbyRippling) - text_standby_results.delete("1.0", tk.END) - text_standby_results.insert("1.0", json.dumps(results, indent=4)) - - -def operational_create_trust_line(): - results = create_trust_line(ent_operational_seed.get(), - ent_operational_destination.get(), - ent_operational_currency.get(), - ent_operational_amount.get()) - text_operational_results.delete("1.0", tk.END) - text_operational_results.insert("1.0", json.dumps(results, indent=4)) - - -def operational_send_currency(): - results = send_currency(ent_operational_seed.get(), - ent_operational_destination.get(), - ent_operational_currency.get(), - ent_operational_amount.get()) - text_operational_results.delete("1.0", tk.END) - text_operational_results.insert("1.0", json.dumps(results, indent=4)) - - -def operational_configure_account(): - results = configure_account( - ent_operational_seed.get(), - operationalRippling) - text_operational_results.delete("1.0", tk.END) - text_operational_results.insert("1.0", json.dumps(results, indent=4)) - - -def get_balances(): - results = get_balance(ent_operational_account.get(), ent_standby_account.get()) - text_standby_results.delete("1.0", tk.END) - text_standby_results.insert("1.0", json.dumps(results, indent=4)) - results = get_balance(ent_standby_account.get(), ent_operational_account.get()) - text_operational_results.delete("1.0", tk.END) - text_operational_results.insert("1.0", json.dumps(results, indent=4)) - - -# Module 1 Handlers -def get_standby_account(): - new_wallet = get_account(ent_standby_seed.get()) - ent_standby_account.delete(0, tk.END) - ent_standby_seed.delete(0, tk.END) - ent_standby_account.insert(0, new_wallet.classic_address) - ent_standby_seed.insert(0, new_wallet.seed) - - -def get_standby_account_info(): - accountInfo = get_account_info(ent_standby_account.get()) - ent_standby_balance.delete(0, tk.END) - ent_standby_balance.insert(0,accountInfo['Balance']) - text_standby_results.delete("1.0", tk.END) - text_standby_results.insert("1.0",json.dumps(accountInfo, indent=4)) - - -def standby_send_xrp(): - response = send_xrp(ent_standby_seed.get(),ent_standby_amount.get(), - ent_standby_destination.get()) - text_standby_results.delete("1.0", tk.END) - text_standby_results.insert("1.0",json.dumps(response.result, indent=4)) - get_standby_account_info() - get_operational_account_info() - - -def get_operational_account(): - new_wallet = get_account(ent_operational_seed.get()) - ent_operational_account.delete(0, tk.END) - ent_operational_account.insert(0, new_wallet.classic_address) - ent_operational_seed.delete(0, tk.END) - ent_operational_seed.insert(0, new_wallet.seed) - - -def get_operational_account_info(): - accountInfo = get_account_info(ent_operational_account.get()) - ent_operational_balance.delete(0, tk.END) - ent_operational_balance.insert(0,accountInfo['Balance']) - text_operational_results.delete("1.0", tk.END) - text_operational_results.insert("1.0",json.dumps(accountInfo, indent=4)) - - -def operational_send_xrp(): - response = send_xrp(ent_operational_seed.get(),ent_operational_amount.get(), ent_operational_destination.get()) - text_operational_results.delete("1.0", tk.END) - text_operational_results.insert("1.0",json.dumps(response.result,indent=4)) - get_standby_account_info() - get_operational_account_info() - -# Create a new window with the title "Quickstart Module 2" - -window = tk.Tk() -window.title("Quickstart Module 2") - - -standbyRippling = tk.BooleanVar() -operationalRippling = tk.BooleanVar() - - -# Form frame -frm_form = tk.Frame(relief=tk.SUNKEN, borderwidth=3) -frm_form.pack() - -# Create the Label and Entry widgets for "Standby Account" -lbl_standy_seed = tk.Label(master=frm_form, text="Standby Seed") -ent_standby_seed = tk.Entry(master=frm_form, width=50) -lbl_standby_account = tk.Label(master=frm_form, text="Standby Account") -ent_standby_account = tk.Entry(master=frm_form, width=50) -lbl_standy_amount = tk.Label(master=frm_form, text="Amount") -ent_standby_amount = tk.Entry(master=frm_form, width=50) -lbl_standby_destination = tk.Label(master=frm_form, text="Destination") -ent_standby_destination = tk.Entry(master=frm_form, width=50) -lbl_standby_balance = tk.Label(master=frm_form, text="XRP Balance") -ent_standby_balance = tk.Entry(master=frm_form, width=50) -``` - -Add **Currency** field. - -```python -lbl_standby_currency = tk.Label(master=frm_form, text="Currency") -ent_standby_currency = tk.Entry(master=frm_form, width=50) -``` - -Add checkbox to **Allow Rippling**. - -```python -cb_standby_allow_rippling = tk.Checkbutton(master=frm_form, text="Allow Rippling", variable=standbyRippling, onvalue=True, offvalue=False) -lbl_standby_results = tk.Label(master=frm_form,text='Results') -text_standby_results = tk.Text(master=frm_form, height = 20, width = 65) - -# Place field in a grid. -lbl_standy_seed.grid(row=0, column=0, sticky="w") -ent_standby_seed.grid(row=0, column=1) -lbl_standby_account.grid(row=2, column=0, sticky="e") -ent_standby_account.grid(row=2, column=1) -lbl_standy_amount.grid(row=3, column=0, sticky="e") -ent_standby_amount.grid(row=3, column=1) -lbl_standby_destination.grid(row=4, column=0, sticky="e") -ent_standby_destination.grid(row=4, column=1) -lbl_standby_balance.grid(row=5, column=0, sticky="e") -ent_standby_balance.grid(row=5, column=1) -``` - -Place new UI elements. - -```python -lbl_standby_currency.grid(row=6, column=0, sticky="e") -ent_standby_currency.grid(row=6, column=1) -cb_standby_allow_rippling.grid(row=7,column=1, sticky="w") -lbl_standby_results.grid(row=8, column=0, sticky="ne") -text_standby_results.grid(row=8, column=1, sticky="nw") -cb_standby_allow_rippling.select() - -############################################### -## Operational Account ######################## -############################################### - -# Create the Label and Entry widgets for "Operational Account" -lbl_operational_seed = tk.Label(master=frm_form, text="Operational Seed") -ent_operational_seed = tk.Entry(master=frm_form, width=50) -lbl_operational_account = tk.Label(master=frm_form, text="Operational Account") -ent_operational_account = tk.Entry(master=frm_form, width=50) -lbl_operational_amount = tk.Label(master=frm_form, text="Amount") -ent_operational_amount = tk.Entry(master=frm_form, width=50) -lbl_operational_destination = tk.Label(master=frm_form, text="Destination") -ent_operational_destination = tk.Entry(master=frm_form, width=50) -lbl_operational_balance = tk.Label(master=frm_form, text="XRP Balance") -ent_operational_balance = tk.Entry(master=frm_form, width=50) -``` - -Add field for **Currency** and checkbox to **Allow Rippling**. - -```python -lbl_operational_currency = tk.Label(master=frm_form, text="Currency") -ent_operational_currency = tk.Entry(master=frm_form, width=50) -cb_operational_allow_rippling = tk.Checkbutton(master=frm_form, text="Allow Rippling", variable=operationalRippling, onvalue=True, offvalue=False) -lbl_operational_results = tk.Label(master=frm_form,text='Results') -text_operational_results = tk.Text(master=frm_form, height = 20, width = 65) - -#Place the widgets in a grid -lbl_operational_seed.grid(row=0, column=4, sticky="e") -ent_operational_seed.grid(row=0, column=5, sticky="w") -lbl_operational_account.grid(row=2,column=4, sticky="e") -ent_operational_account.grid(row=2,column=5, sticky="w") -lbl_operational_amount.grid(row=3, column=4, sticky="e") -ent_operational_amount.grid(row=3, column=5, sticky="w") -lbl_operational_destination.grid(row=4, column=4, sticky="e") -ent_operational_destination.grid(row=4, column=5, sticky="w") -lbl_operational_balance.grid(row=5, column=4, sticky="e") -ent_operational_balance.grid(row=5, column=5, sticky="w") -``` - -Add elements to the UI. - -```python -lbl_operational_currency.grid(row=6, column=4, sticky="e") -ent_operational_currency.grid(row=6, column=5) -cb_operational_allow_rippling.grid(row=7,column=5, sticky="w") -lbl_operational_results.grid(row=8, column=4, sticky="ne") -text_operational_results.grid(row=8, column=5, sticky="nw") -cb_operational_allow_rippling.select() -``` - -Create the Standby Account Buttons. - -```python -btn_get_standby_account = tk.Button(master=frm_form, text="Get Standby Account", - command = get_standby_account) -btn_get_standby_account.grid(row=0, column=2, sticky = "nsew") -btn_get_standby_account_info = tk.Button(master=frm_form, - text="Get Standby Account Info", - command = get_standby_account_info) -btn_get_standby_account_info.grid(row=1, column=2, sticky = "nsew") -btn_standby_send_xrp = tk.Button(master=frm_form, text="Send XRP >", - command = standby_send_xrp) -btn_standby_send_xrp.grid(row=2, column = 2, sticky = "nsew") -``` - -Add buttons **Create Trust Line**, **Send Currency**, **Get Balances**, and **Configure Account**. - -```python -btn_standby_create_trust_line = tk.Button(master=frm_form, - text="Create Trust Line", - command = standby_create_trust_line) -btn_standby_create_trust_line.grid(row=3, column=2, sticky = "nsew") -btn_standby_send_currency = tk.Button(master=frm_form, text="Send Currency >", - command = standby_send_currency) -btn_standby_send_currency.grid(row=4, column=2, sticky = "nsew") -btn_standby_send_currency = tk.Button(master=frm_form, text="Get Balances", - command = get_balances) -btn_standby_send_currency.grid(row=5, column=2, sticky = "nsew") -btn_standby_configure_account = tk.Button(master=frm_form, - text="Configure Account", - command = standby_configure_account) -btn_standby_configure_account.grid(row=7,column=0, sticky = "nsew") -``` - -Create the Operational Account buttons. - -```python -btn_get_operational_account = tk.Button(master=frm_form, - text="Get Operational Account", - command = get_operational_account) -btn_get_operational_account.grid(row=0, column=3, sticky = "nsew") -btn_get_op_account_info = tk.Button(master=frm_form, text="Get Op Account Info", - command = get_operational_account_info) -btn_get_op_account_info.grid(row=1, column=3, sticky = "nsew") -btn_op_send_xrp = tk.Button(master=frm_form, text="< Send XRP", - command = operational_send_xrp) -btn_op_send_xrp.grid(row=2, column = 3, sticky = "nsew") -``` - -Add operational buttons **Create Trust Line**, **Send Currency**, **Get Balances**, and **Configure Account**. - -```python -btn_op_create_trust_line = tk.Button(master=frm_form, text="Create Trust Line", - command = operational_create_trust_line) -btn_op_create_trust_line.grid(row=3, column=3, sticky = "nsew") -btn_op_send_currency = tk.Button(master=frm_form, text="< Send Currency", - command = operational_send_currency) -btn_op_send_currency.grid(row=4, column=3, sticky = "nsew") -btn_op_get_balances = tk.Button(master=frm_form, text="Get Balances", - command = get_balances) -btn_op_get_balances.grid(row=5, column=3, sticky = "nsew") -btn_op_configure_account = tk.Button(master=frm_form, text="Configure Account", - command = operational_configure_account) -btn_op_configure_account.grid(row=7,column=4, sticky = "nsew") -``` - -# Start the application - -```python -window.mainloop() -``` diff --git a/docs/tutorials/payments/send-a-trust-line-token.md b/docs/tutorials/payments/send-a-trust-line-token.md new file mode 100644 index 00000000000..8d6da974f12 --- /dev/null +++ b/docs/tutorials/payments/send-a-trust-line-token.md @@ -0,0 +1,221 @@ +--- +seo: + description: Send a direct payment of a trust line token on the XRP Ledger. +metadata: + indexPage: true +labels: + - Tokens + - Payments +--- + +# Send a Trust Line Token + +This tutorial shows you how to send a direct [trust line token](../../concepts/tokens/fungible-tokens/trust-line-tokens.md) payment on the XRP Ledger. + +Each account must create a trust line to the issuer before it can hold the token. Holders can send the token to each other only if the issuer has the **Default Ripple** flag enabled, which lets the token move indirectly between holders through the issuer. This indirect movement of funds is called [rippling](../../concepts/tokens/fungible-tokens/rippling.md). + +## Goals + +By the end of this tutorial, you will be able to: + +- Create a trust line that lets a holder receive a token from an issuer. +- Send a trust line token payment between two accounts. +- Verify the payment was successful. + +## Prerequisites + +To complete this tutorial, you should: + +- Have a basic understanding of the XRP Ledger. +- Have an XRP Ledger client library set up in your development environment. This page provides examples for the following: + - **JavaScript** with the [xrpl.js library][]. 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 + +You can find the complete source code for this tutorial's examples in the {% repo-link path="_code-samples/send-a-trust-line-token/" %}code samples section of this website's repository{% /repo-link %}. + +## Steps + +### 1. Install dependencies + +{% tabs %} +{% tab label="JavaScript" %} +From the code sample folder, use `npm` to install dependencies. + +```bash +npm install +``` +{% /tab %} +{% tab label="Python" %} +From the code sample folder, set up a virtual environment and use `pip` to install dependencies. + +```bash +python3 -m venv .venv +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 + +To get started, import the necessary libraries and instantiate a client to connect to the XRPL. This example imports: + +{% tabs %} +{% tab label="JavaScript" %} +- `xrpl`: Used for XRPL client connection, transaction submission, and wallet handling. +- `fs`: Used to check for and load the tutorial setup data. +- `./sendTrustLineTokenSetup.js`: The tutorial set up script, imported and called directly. + +{% code-snippet file="/_code-samples/send-a-trust-line-token/js/sendTrustLineToken.js" language="js" before="// This step checks" /%} +{% /tab %} +{% tab label="Python" %} +- `xrpl`: Used for XRPL client connection, transaction submission, and wallet handling. +- `json`: Used for loading and formatting JSON data. +- `os` and `sys`: Used to check for setup data and exit on transaction failures. +- `send_trust_line_token_setup`: The tutorial set up script, imported and called directly. + +{% code-snippet file="/_code-samples/send-a-trust-line-token/py/send_trust_line_token.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 the tutorial setup script. + +{% code-snippet file="/_code-samples/send-a-trust-line-token/go/send-trust-line-token/main.go" language="go" before="// This step checks" /%} +{% /tab %} +{% /tabs %} + +Next, provide the issuer, sender, and receiver accounts. +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/js/sendTrustLineToken.js" language="js" from="// This step checks" before="// Create trust line" /%} + +This example uses preconfigured accounts from the `sendTrustLineTokenSetup.js` script, but you can replace `issuer`, `sender`, and `receiver` with your own values. + +{% /tab %} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/py/send_trust_line_token.py" language="py" from="# This step checks" before="# Create trust line" /%} + +This example uses preconfigured accounts from the `send_trust_line_token_setup.py` script, but you can replace `issuer`, `sender`, and `receiver` with your own values. +{% /tab %} +{% tab label="Go" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/go/send-trust-line-token/main.go" language="go" from="// This step checks" before="// Create trust line" /%} + +This example uses preconfigured accounts from the `sendTrustLineTokenSetup` script, but you can replace `issuer`, `sender`, and `receiver` with your own values. +{% /tab %} +{% /tabs %} + +The preconfigured issuer account has the **Default Ripple** flag enabled so the token can be transferred between holders. If you provide a different `issuer` account, make sure this flag is enabled. + +### 3. Create a trust line from the receiver to the issuer + +Any account that wants to hold a trust line token, whether sending or receiving, must opt in first. + +Submit a [TrustSet transaction][], signed by the receiver account, to create the trust line. The setup script already created the sender's trust line, so this step is only needed for the receiver. + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/js/sendTrustLineToken.js" language="js" from="// Create trust line" before="// Check initial balances" /%} +{% /tab %} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/py/send_trust_line_token.py" language="py" from="# Create trust line" before="# Check initial balances" /%} +{% /tab %} +{% tab label="Go" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/go/send-trust-line-token/main.go" language="go" from="// Create trust line" before="// Check initial balances" /%} +{% /tab %} +{% /tabs %} + +The `LimitAmount` field specifies the maximum quantity of the token the holder is willing to hold, the currency code, and the issuer's address. + +A `tesSUCCESS` result confirms the trust line now exists, so the receiver can hold up to the `LimitAmount` of this token from the issuer. + +{% admonition type="info" name="Note" %} +If the issuer uses authorized trust lines (enables the **Require Auth** flag), this step isn't enough on its own. After the receiver creates the trust line, the issuer must also approve it by submitting a [TrustSet transaction][] with the `tfSetfAuth` flag enabled and `LimitAmount.issuer` set to the receiver's address. **The issuer in this tutorial doesn't require authorization, so no issuer approval is needed.** +{% /admonition %} + +### 4. Check initial balances + +Before sending the payment, check each account's balance using the [account_lines method][]. The sender should have an initial balance, while the receiver's balance should be `0`. + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/js/sendTrustLineToken.js" language="js" from="// Check initial balances" before="// Send issued token" /%} +{% /tab %} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/py/send_trust_line_token.py" language="py" from="# Check initial balances" before="# Send issued token" /%} +{% /tab %} +{% tab label="Go" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/go/send-trust-line-token/main.go" language="go" from="// Check initial balances" before="// Send issued token" /%} +{% /tab %} +{% /tabs %} + +A trust line is a bidirectional relationship with a single, shared balance that is positive from one account's perspective and negative from the other's. Querying `account_lines` on a holder with `peer` set to the issuer returns the positive value; querying it on the issuer with `peer` set to a holder returns the same value as a negative number. + +### 5. Send the token payment + +Once the receiver's trust line exists, the sender can send the token to the receiver with a [Payment transaction][]. Provide the [issued currency amount](../../references/protocol/data-types/currency-formats.md#token-amounts) object that specifies the currency code, the issuer's address, and the value to send. + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/js/sendTrustLineToken.js" language="js" from="// Send issued token" before="// Verify balances" /%} +{% /tab %} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/py/send_trust_line_token.py" language="py" from="# Send issued token" before="# Verify balances" /%} +{% /tab %} +{% tab label="Go" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/go/send-trust-line-token/main.go" language="go" from="// Send issued token" before="// Verify balances" /%} +{% /tab %} +{% /tabs %} + +The preconfigured issuer in this tutorial doesn't charge a [transfer fee](../../concepts/tokens/fungible-tokens/transfer-fees.md), so the sender's balance decreases by the same amount the receiver gains. If you replace the issuer with one that has a non-zero `TransferRate`, the sender's balance decreases by more than the receiver gains. In that case, include a `SendMax` field in the `Payment` set to the maximum amount the sender is willing to spend, so the payment can cover the transfer fee. + +{% admonition type="warning" name="Caution" %} +If the payment fails, it could be for one of the following reasons: + +- `tecNO_AUTH`: the issuer uses authorized trust lines and hasn't approved the receiver's trust line. +- `tecPATH_PARTIAL`: the sender doesn't have enough balance to cover the payment. This can also occur if the issuer has configured a non-zero `TransferRate` and the sender doesn't have enough to cover the payment plus the transfer fee. +- `tecPATH_DRY`: no path could deliver any amount at all. This commonly happens when the receiver has no trust line for the token, or the issuer doesn't have **Default Ripple** enabled. +{% /admonition %} + +### 6. Verify updated balances + +Check the account balances again to confirm the transfer. + +{% tabs %} +{% tab label="JavaScript" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/js/sendTrustLineToken.js" language="js" from="// Verify balances" /%} +{% /tab %} +{% tab label="Python" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/py/send_trust_line_token.py" language="py" from="# Verify balances" /%} +{% /tab %} +{% tab label="Go" %} +{% code-snippet file="/_code-samples/send-a-trust-line-token/go/send-trust-line-token/main.go" language="go" from="// Verify balances" /%} +{% /tab %} +{% /tabs %} + +The sender's balance should have decreased by the amount sent and the receiver's should have increased by the same amount. On the issuer's side of each trust line, the balance with the sender is reduced and the balance with the receiver is increased. + +## See Also + +- **Concepts**: + - [Trust Line Tokens](../../concepts/tokens/fungible-tokens/trust-line-tokens.md) + - [Rippling](../../concepts/tokens/fungible-tokens/rippling.md) +- **Tutorials**: + - [Issue a Fungible Token](../tokens/fungible-tokens/issue-a-fungible-token.md) +- **References**: + - [TrustSet transaction][] + - [Payment transaction][] + - [account_lines method][] + - [RippleState entry][] + +{% raw-partial file="/docs/_snippets/common-links.md" /%} diff --git a/package-lock.json b/package-lock.json index f1ab0e38725..674dea33287 100644 --- a/package-lock.json +++ b/package-lock.json @@ -266,7 +266,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -486,7 +485,6 @@ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -738,7 +736,6 @@ "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.0.tgz", "integrity": "sha512-V7ZCLQO3Jus9hzh2jVCCPW3mO4IBMr43O37PqSUYautJSnnJF41YlgLw21x0fLJTYvJ+Vkm6Gp+qKGH9pltgXA==", "license": "MIT", - "peer": true, "dependencies": { "@codemirror/state": "^6.6.0", "crelt": "^1.0.6", @@ -785,7 +782,6 @@ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "license": "MIT", - "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -1606,7 +1602,6 @@ "resolved": "https://registry.npmjs.org/@libsql/client/-/client-0.17.3.tgz", "integrity": "sha512-HXk9wiAoJbKFbyBH4O+aEhN6ir5ERXuXvwE5OD2eR4/5RUa3Pw/8L9zrnVdU+iNJitRvisPWaIwmhkO3bH7giA==", "license": "MIT", - "peer": true, "dependencies": { "@libsql/core": "^0.17.3", "@libsql/hrana-client": "^0.10.0", @@ -1772,7 +1767,6 @@ "resolved": "https://registry.npmjs.org/@markdoc/markdoc/-/markdoc-0.5.2.tgz", "integrity": "sha512-clrqWpJ3+S8PXigRE+zBIs6LVZYXaJ7JTDFq1CcCWc4xpoB2kz+9qRUQQ4vXtLUjQ8ige1BGdMruV6gM/2gloA==", "license": "MIT", - "peer": true, "engines": { "node": ">=14.7.0" }, @@ -1843,7 +1837,6 @@ "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -2465,7 +2458,6 @@ "resolved": "https://registry.npmjs.org/@redocly/hookstate-core/-/hookstate-core-4.2.4.tgz", "integrity": "sha512-N+0DkPva4oJXPocDqW8GSRcbKP6J9243lj3jpC34d/kJQgW6k83eudyJFzeq2MFZ5GplbTMm27CF+rng3ReS5Q==", "license": "MIT", - "peer": true, "peerDependencies": { "react": "^16.8.6 || ^17.0.0 || ^18.0.0 || ^19.0.0" } @@ -2916,7 +2908,6 @@ "resolved": "https://registry.npmjs.org/@redocly/theme/-/theme-0.66.0.tgz", "integrity": "sha512-0UYQYpG/HZLXKZiJPbOlyVavrQGUUac3Ke5Dx+gl2KRpW96tJKFOcX+cAmjw4F7mq+WMyF+GL0trlOXFrcgnKg==", "license": "MIT", - "peer": true, "dependencies": { "@emotion/is-prop-valid": "^1.3.1", "@redocly/config": "0.49.0", @@ -3125,6 +3116,7 @@ "resolved": "https://registry.npmjs.org/@styled-system/background/-/background-5.1.2.tgz", "integrity": "sha512-jtwH2C/U6ssuGSvwTN3ri/IyjdHb8W9X/g8Y0JLcrH02G+BW3OS8kZdHphF1/YyRklnrKrBT2ngwGUK6aqqV3A==", "license": "MIT", + "peer": true, "dependencies": { "@styled-system/core": "^5.1.2" } @@ -3134,6 +3126,7 @@ "resolved": "https://registry.npmjs.org/@styled-system/border/-/border-5.1.5.tgz", "integrity": "sha512-JvddhNrnhGigtzWRCVuAHepniyVi6hBlimxWDVAdcTuk7aRn9BYJUwfHslURtwYFsF5FoEs8Zmr1oZq2M1AP0A==", "license": "MIT", + "peer": true, "dependencies": { "@styled-system/core": "^5.1.2" } @@ -3143,6 +3136,7 @@ "resolved": "https://registry.npmjs.org/@styled-system/color/-/color-5.1.2.tgz", "integrity": "sha512-1kCkeKDZkt4GYkuFNKc7vJQMcOmTl3bJY3YBUs7fCNM6mMYJeT1pViQ2LwBSBJytj3AB0o4IdLBoepgSgGl5MA==", "license": "MIT", + "peer": true, "dependencies": { "@styled-system/core": "^5.1.2" } @@ -3152,6 +3146,7 @@ "resolved": "https://registry.npmjs.org/@styled-system/core/-/core-5.1.2.tgz", "integrity": "sha512-XclBDdNIy7OPOsN4HBsawG2eiWfCcuFt6gxKn1x4QfMIgeO6TOlA2pZZ5GWZtIhCUqEPTgIBta6JXsGyCkLBYw==", "license": "MIT", + "peer": true, "dependencies": { "object-assign": "^4.1.1" } @@ -3160,13 +3155,15 @@ "version": "5.1.5", "resolved": "https://registry.npmjs.org/@styled-system/css/-/css-5.1.5.tgz", "integrity": "sha512-XkORZdS5kypzcBotAMPBoeckDs9aSZVkvrAlq5K3xP8IMAUek+x2O4NtwoSgkYkWWzVBu6DGdFZLR790QWGG+A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@styled-system/flexbox": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/@styled-system/flexbox/-/flexbox-5.1.2.tgz", "integrity": "sha512-6hHV52+eUk654Y1J2v77B8iLeBNtc+SA3R4necsu2VVinSD7+XY5PCCEzBFaWs42dtOEDIa2lMrgL0YBC01mDQ==", "license": "MIT", + "peer": true, "dependencies": { "@styled-system/core": "^5.1.2" } @@ -3176,6 +3173,7 @@ "resolved": "https://registry.npmjs.org/@styled-system/grid/-/grid-5.1.2.tgz", "integrity": "sha512-K3YiV1KyHHzgdNuNlaw8oW2ktMuGga99o1e/NAfTEi5Zsa7JXxzwEnVSDSBdJC+z6R8WYTCYRQC6bkVFcvdTeg==", "license": "MIT", + "peer": true, "dependencies": { "@styled-system/core": "^5.1.2" } @@ -3185,6 +3183,7 @@ "resolved": "https://registry.npmjs.org/@styled-system/layout/-/layout-5.1.2.tgz", "integrity": "sha512-wUhkMBqSeacPFhoE9S6UF3fsMEKFv91gF4AdDWp0Aym1yeMPpqz9l9qS/6vjSsDPF7zOb5cOKC3tcKKOMuDCPw==", "license": "MIT", + "peer": true, "dependencies": { "@styled-system/core": "^5.1.2" } @@ -3194,6 +3193,7 @@ "resolved": "https://registry.npmjs.org/@styled-system/position/-/position-5.1.2.tgz", "integrity": "sha512-60IZfMXEOOZe3l1mCu6sj/2NAyUmES2kR9Kzp7s2D3P4qKsZWxD1Se1+wJvevb+1TP+ZMkGPEYYXRyU8M1aF5A==", "license": "MIT", + "peer": true, "dependencies": { "@styled-system/core": "^5.1.2" } @@ -3203,6 +3203,7 @@ "resolved": "https://registry.npmjs.org/@styled-system/shadow/-/shadow-5.1.2.tgz", "integrity": "sha512-wqniqYb7XuZM7K7C0d1Euxc4eGtqEe/lvM0WjuAFsQVImiq6KGT7s7is+0bNI8O4Dwg27jyu4Lfqo/oIQXNzAg==", "license": "MIT", + "peer": true, "dependencies": { "@styled-system/core": "^5.1.2" } @@ -3212,6 +3213,7 @@ "resolved": "https://registry.npmjs.org/@styled-system/space/-/space-5.1.2.tgz", "integrity": "sha512-+zzYpR8uvfhcAbaPXhH8QgDAV//flxqxSjHiS9cDFQQUSznXMQmxJegbhcdEF7/eNnJgHeIXv1jmny78kipgBA==", "license": "MIT", + "peer": true, "dependencies": { "@styled-system/core": "^5.1.2" } @@ -3221,6 +3223,7 @@ "resolved": "https://registry.npmjs.org/@styled-system/typography/-/typography-5.1.2.tgz", "integrity": "sha512-BxbVUnN8N7hJ4aaPOd7wEsudeT7CxarR+2hns8XCX1zp0DFfbWw4xYa/olA0oQaqx7F1hzDg+eRaGzAJbF+jOg==", "license": "MIT", + "peer": true, "dependencies": { "@styled-system/core": "^5.1.2" } @@ -3230,6 +3233,7 @@ "resolved": "https://registry.npmjs.org/@styled-system/variant/-/variant-5.1.5.tgz", "integrity": "sha512-Yn8hXAFoWIro8+Q5J8YJd/mP85Teiut3fsGVR9CAxwgNfIAiqlYxsk5iHU7VHJks/0KjL4ATSjmbtCDC/4l1qw==", "license": "MIT", + "peer": true, "dependencies": { "@styled-system/core": "^5.1.2", "@styled-system/css": "^5.1.5" @@ -3250,7 +3254,6 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.62.3.tgz", "integrity": "sha512-y2zDNKuhgiuMgsKkqd4AcsLIBiCfEO8U11AdrtAUihmLbRNztPrlcZqx2lH1GacZsx+y1qRRbCcJLYTtF1vKsw==", "license": "MIT", - "peer": true, "dependencies": { "@tanstack/query-core": "5.62.3" }, @@ -3496,7 +3499,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz", "integrity": "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } @@ -3800,7 +3802,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3894,7 +3895,6 @@ "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.18.1.tgz", "integrity": "sha512-Ifm/pP/tul1qmAecpbVxCBluVE32rKfjf8gYXH4xI2gCv9mRWFhJMHzkPDM4TXlxwPQYIFegymlsy8lXz7optA==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -4189,7 +4189,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -4573,8 +4572,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/d3-color": { "version": "3.1.0", @@ -4633,7 +4631,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -5601,7 +5598,6 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==", "license": "MIT", - "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -6125,7 +6121,6 @@ "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.20.0.tgz", "integrity": "sha512-b5GAqgmXmXzB4WPaTH26ppk9Sl7AA9WSQX7yfdM+gJ1rFROiWcVbi97gFuN/yVCojOcbcvop2sfLL+fjxW0JVg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.20.0" }, @@ -7292,7 +7287,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -7471,7 +7465,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -7558,7 +7551,6 @@ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.4.tgz", "integrity": "sha512-q4HvNl+mmDdkS0g+MqiBZNteQJCuimWoOyHMy4T/RQLAn9Z29+E91QXRaxOujeMl2HTzRSS0KFPd7lxX3PjV0Q==", "license": "MIT", - "peer": true, "dependencies": { "@remix-run/router": "1.23.3", "react-router": "6.30.4" @@ -7716,7 +7708,6 @@ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.0.tgz", "integrity": "sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.9.2" } @@ -8289,7 +8280,6 @@ "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.4.2.tgz", "integrity": "sha512-xZBhBJsMtGqb+aKcwKgaT+BtuFums9VynX2JRvXJGTx5UfZzN12rk5r4nVdhXYvRw+hE7yiYxVrOqJZaK2+Txg==", "license": "MIT", - "peer": true, "dependencies": { "@emotion/is-prop-valid": "1.4.0", "css-to-react-native": "3.2.0", @@ -9150,7 +9140,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -9159,8 +9148,7 @@ "version": "0.15.1", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz", "integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/zustand": { "version": "4.5.7", diff --git a/redirects.yaml b/redirects.yaml index a95b8d1c13b..526fc493c98 100644 --- a/redirects.yaml +++ b/redirects.yaml @@ -1,5 +1,7 @@ # Docs redirects for moved pages ----------------------------------------------- # (not associated with a major reorg) +/docs/tutorials/payments/issue-and-send-tokens/: + to: /docs/tutorials/payments/send-a-trust-line-token/ /docs/tutorials/defi/dex/trade-with-auction-slot-in-javascript/: to: /docs/tutorials/defi/dex/use-amm-auction-slot-for-lower-fees/ /resources/contribute-documentation/tutorial-structure/: @@ -60,7 +62,9 @@ /docs/tutorials/python/send-payments/create-time-based-escrows/: to: /docs/tutorials/payments/send-a-timed-escrow/ /docs/tutorials/python/send-payments/create-trust-line-send-currency/: - to: /docs/tutorials/payments/create-trust-line-send-currency-in-python/ + to: /docs/tutorials/payments/send-a-trust-line-token/ +/docs/tutorials/payments/create-trust-line-send-currency-in-python/: + to: /docs/tutorials/payments/send-a-trust-line-token/ /docs/tutorials/python/send-payments/create-accounts-send-xrp/: to: /docs/tutorials/payments/send-xrp/ /docs/tutorials/python/send-payments/create-conditional-escrows/: @@ -198,7 +202,9 @@ /docs/tutorials/javascript/send-payments/create-time-based-escrows/: to: /docs/tutorials/payments/send-a-timed-escrow/ /docs/tutorials/javascript/send-payments/create-trust-line-send-currency/: - to: /docs/tutorials/payments/create-trust-line-send-currency-in-javascript/ + to: /docs/tutorials/payments/send-a-trust-line-token/ +/docs/tutorials/payments/create-trust-line-send-currency-in-javascript/: + to: /docs/tutorials/payments/send-a-trust-line-token/ /docs/tutorials/javascript/send-payments/create-accounts-send-xrp/: to: /docs/tutorials/payments/send-xrp/ /docs/tutorials/javascript/send-payments/create-conditional-escrows/: @@ -624,7 +630,7 @@ send-payments-using-python.html: py-create-accounts-send-xrp.html: to: /docs/tutorials/payments/send-xrp/ py-create-trustline-send-currency.html: - to: /docs/tutorials/payments/create-trust-line-send-currency-in-python/ + to: /docs/tutorials/payments/send-a-trust-line-token/ py-create-time-based-escrows.html: to: /docs/tutorials/payments/send-a-timed-escrow/ py-mint-and-burn-nfts.html: @@ -658,7 +664,7 @@ send-payments-using-javascript.html: create-accounts-send-xrp-using-javascript.html: to: /docs/tutorials/payments/send-xrp/ create-trustline-send-currency-using-javascript.html: - to: /docs/tutorials/payments/create-trust-line-send-currency-in-javascript + to: /docs/tutorials/payments/send-a-trust-line-token/ create-time-based-escrows-using-javascript.html: to: /docs/tutorials/payments/send-a-timed-escrow/ create-conditional-escrows-using-javascript.html: diff --git a/sidebars.yaml b/sidebars.yaml index b79db5d9f7d..d274df8f164 100644 --- a/sidebars.yaml +++ b/sidebars.yaml @@ -249,8 +249,7 @@ items: - page: docs/tutorials/payments/send-xrp.md - page: docs/tutorials/payments/send-an-mpt.md - - page: docs/tutorials/payments/create-trust-line-send-currency-in-javascript.md - - page: docs/tutorials/payments/create-trust-line-send-currency-in-python.md + - page: docs/tutorials/payments/send-a-trust-line-token.md - page: docs/tutorials/payments/send-a-conditional-escrow.md - page: docs/tutorials/payments/send-a-timed-escrow.md - page: docs/tutorials/payments/send-fungible-token-escrows.md