XRP Ledger Apex is back in Amsterdam Register Now
arrow-left Course Hub
Lesson 3: Create trust line and send currency
Estimated reading time:

25 minutes

Difficulty:

Intermediate

Concepts covered:
  • Configure Account
  • Create Trust Line
  • Send Currency
  • Trustline

Key takeaway:

Learn how to create trust line and send currency on the XRP Ledger. Use the code sandboxes in the lesson to experiment with the XRPL.js library code.

Lesson 3

Create trust line and send currency

Create trust line between two accounts on the XRPL Testnet and transfer currency.

This lesson 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.

Try editing some code: create-trustline-send-currency.js

The interactive code example below can be used with any XRP Ledger network. Currently, there are TestnetandDevnet,with the experimentalNFT-Devnet server with support for NFTokens. When building on your own, you can update the code to choose different or additional XRP Ledger networks. We recommend you click “Edit on Codepen” in a new window (link in the top right).

Open the Token Test Harness and get accounts:

  1. Use the sandbox above to send currency between accounts. We recommend you click “Edit on Codepen” in a new window (link in the top right).
  2. Get test accounts.
    1. If you have existing account seeds
      1. Paste account seeds in theSeedsfield.
      2. ClickGet Accounts from Seeds.
    2. If you do not have account seeds:
      1. ClickGet New Standby Account.
      2. ClickGet New Operational Account.

Create Trust Line

To create a trust line between accounts:

  1. Enter a currency code in theCurrency field (we used USD).
  2. Enter the maximum transfer limit (like 9999) in the Amountfield.
  3. Enter the destination account value in theDestinationfield.
  4. ClickCreate Trust line.

Send an issued currency token

To transfer an issued currency token, once you have created a trust line:

  1. Enter theAmount.
  2. Enter theDestination.
  3. Enter theCurrencytype.
  4. ClickSend Currency.

JavaScript code walkthrough: create-trustline-send-currency.js

configureAccount()

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.

Connect to the ledger.
Update results field with status.
Get the account wallets.
Prepare the transaction. If the rippleDefault argument is true, set the asfDefaultRipple flag. If it is false, clear the asfDefaultRipple flag.
Autofill the default values for the transaction.
Sign the transaction.
Submit the transaction and wait for the result.
Report the result to the results field.
Disconnect from the ledger.
// *******************************************************
// **************** Configure Account ********************
// *******************************************************
      
      async function configureAccount(type, rippleDefault) {
        let net = getNet()
        const client = new xrpl.Client(net)
        results = 'Connecting to ' + getNet() + '....'
        standbyResultField.value = results
        await client.connect()
        results += 'nConnected, finding wallet.'
        standbyResultField.value = results
 
        if (type=='standby') {
          my_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
        } else {
          my_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value)
        }
        results += 'Ripple Default Setting: ' + rippleDefault
        standbyResultField.value = results
        
        let settings_tx = {}
        if (rippleDefault) {
          settings_tx = {
          "TransactionType": "AccountSet",
          "Account": my_wallet.address,
          "SetFlag": xrpl.AccountSetAsfFlags.asfDefaultRipple
          } 
          results += 'n Set Default Ripple flag.' 
        } else {
          settings_tx = {
          "TransactionType": "AccountSet",
          "Account": my_wallet.address,
          "ClearFlag": xrpl.AccountSetAsfFlags.asfDefaultRipple
          }
          results += 'n Clear Default Ripple flag.' 
        }
          results += 'nSending account setting.'
          standbyResultField.value = results
      
          const cst_prepared = await client.autofill(settings_tx)

          const cst_signed = my_wallet.sign(cst_prepared)
          const cst_result = await client.submitAndWait(cst_signed.tx_blob)
          if (cst_result.result.meta.TransactionResult == "tesSUCCESS") {
          results += 'nAccount setting succeeded.'
          standbyResultField.value = results
          } else {
          throw 'Error sending transaction: ${cst_result}'
          results += 'nAccount setting failed.'
          standbyResultField.value = results
          }
      
        client.disconnect()
      } // End of configureAccount()
      
Get the standby and operational wallets.
Capture the currency code from the standby currency field.
Define the transaction, capturing the currency code and (limit) amount from the form fields.
Prepare the transaction by automatically filling the default parameters.
Sign the transaction.
Submit the transaction and wait for the results.
Report the results.

createTrustline()

A trust line enables two accounts to trade a defined currency up to a set limit. This gives the participants assurance that any exchanges are between known entities at agreed upon maximum amounts.

// *******************************************************
// ***************** Create TrustLine ********************
// *******************************************************
      
      async function createTrustline() {
        let net = getNet()
        const client = new xrpl.Client(net)
        results = 'Connecting to ' + getNet() + '....'
        standbyResultField.value = results
        
        await client.connect()
        
        results += 'nConnected.'
        standbyResultField.value = results
          
        const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
        const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value)
        const currency_code = standbyCurrencyField.value

        const trustSet_tx = {
          "TransactionType": "TrustSet",
          "Account": standbyDestinationField.value,
          "LimitAmount": {
            "currency": standbyCurrencyField.value,
            "issuer": standby_wallet.address,
            "value": standbyAmountField.value
          }
        }
        const ts_prepared = await client.autofill(trustSet_tx)

        const ts_signed = operational_wallet.sign(ts_prepared)
        results += 'nCreating trust line from operational account to standby account...'
        standbyResultField.value = results
        const ts_result = await client.submitAndWait(ts_signed.tx_blob)
        if (ts_result.result.meta.TransactionResult == "tesSUCCESS") {
          results += 'nTrustline established between account n' + standbyDestinationField.value + ' n and accountn' + standby_wallet.address + '.'
          standbyResultField.value = results
        } else {
          results += 'nTrustLine failed. See JavaScript console for details.'
          standbyResultField.value = results     
          throw 'Error sending transaction: ${ts_result.result.meta.TransactionResult}'
        }
      } //End of createTrustline()
      
Connect to the ledger.
Get the account wallets, currency code and amount.
Store the transaction information in a constant.
Prepare the transaction by automatically filling default values.
Sign the transaction.
Submit the transaction and wait for the results.
Report the results.
Disconnect from the ledger.

Send issued currency with sendCurrency()

Once you have created a trust line from an account to your own, you can send issued currency tokens to that account, up to the established limit.

// *******************************************************
// *************** Send Issued Currency ******************
// *******************************************************
      
      async function sendCurrency() {
        let net = getNet()
        const client = new xrpl.Client(net)
        results = 'Connecting to ' + getNet() + '....'
        standbyResultField.value = results
        
        await client.connect()
        
        results += 'nConnected.'
        standbyResultField.value = results
          
        const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
        const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value)
        const currency_code = standbyCurrencyField.value
        const issue_quantity = standbyAmountField.value
        
        const send_token_tx = {
          "TransactionType": "Payment",
          "Account": standby_wallet.address,
          "Amount": {
            "currency": standbyCurrencyField.value,
            "value": standbyAmountField.value,
            "issuer": standby_wallet.address
          },
          "Destination": standbyDestinationField.value
        }
      
        const pay_prepared = await client.autofill(send_token_tx)

        const pay_signed = standby_wallet.sign(pay_prepared)
        results += 'Sending ' + standbyAmountField.value + standbyCurrencyField.value + 'to ' + standbyDestinationField.value + '...'
        standbyResultField.value = results
        const pay_result = await client.submitAndWait(pay_signed.tx_blob)
        if (pay_result.result.meta.TransactionResult == "tesSUCCESS") {
          results += 'Transaction succeeded: https://testnet.xrpl.org/transactions/${pay_signed.hash}'
          standbyResultField.value = results
        } else {
          results += 'Transaction failed: See JavaScript console for details.'
          standbyResultField.value = results
          throw 'Error sending transaction: ${pay_result.result.meta.TransactionResult}'
        }
        standbyBalanceField.value = 
              (await client.getXrpBalance(standby_wallet.address))
        operationalBalanceField.value = 
              (await client.getXrpBalance(operational_wallet.address))
        getBalances()
        client.disconnect()
      
      } // end of sendIOU()
      
Connect to the ledger.
Get the account wallets.
Define and send the request for the standby account, then wait for the results.
Report the results.
Define and send the request for the operational account, then wait for the results.
Report the results.
Update the form with current XRP balances.
Disconnect from the ledger.

getBalances()

// *******************************************************
// ****************** Get Balances ***********************
// *******************************************************

      async function getBalances() {
        let net = getNet()
        const client = new xrpl.Client(net)
        results = 'Connecting to ' + getNet() + '....'
        standbyResultField.value = results
        
        await client.connect()
        
        results += 'nConnected.'
        standbyResultField.value = results
       
        const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
        const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value)
      
        results= "nGetting standby account balances...n"
        const standby_balances = await client.request({
          command: "gateway_balances",
          account: standby_wallet.address,
          ledger_index: "validated",
          hotwallet: [operational_wallet.address]
        })
        results += JSON.stringify(standby_balances.result, null, 2)
        standbyResultField.value = results
      
        results= "nGetting operational account balances...n"
        const operational_balances = await client.request({
          command: "account_lines",
          account: operational_wallet.address,
          ledger_index: "validated"
        })
        results += JSON.stringify(operational_balances.result, null, 2)
        operationalResultField.value = results
      
        operationalBalanceField.value = 
          (await client.getXrpBalance(operational_wallet.address))
        standbyBalanceField.value = 
          (await client.getXrpBalance(standby_wallet.address))
          
        client.disconnect()
       
      } // End of getBalances()

Reciprocal transactions oPcreateTrustline() and oPsendCurrency()

For each of the transactions, there is an accompanying reciprocal transaction, with the prefix oP, for the operational account. See the corresponding function for the standby account for code commentary. The getBalances() request does not have a reciprocal transaction, because it reports balances for both accounts.

// **********************************************************************
// ****** Reciprocal Transactions ***************************************
// **********************************************************************
      
// *******************************************************
// ************ Create Operational TrustLine *************
// *******************************************************
      
      async function oPcreateTrustline() {
        let net = getNet()
        const client = new xrpl.Client(net)
        results = 'Connecting to ' + getNet() + '....'
        operationalResultField.value = results
        
        await client.connect()
        
        results += 'nConnected.'
        operationalResultField.value = results
          
        const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
        const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value)
        const trustSet_tx = {
          "TransactionType": "TrustSet",
          "Account": operationalDestinationField.value,
          "LimitAmount": {
            "currency": operationalCurrencyField.value,
            "issuer": operational_wallet.address,
            "value": operationalAmountField.value
          }
        }
        const ts_prepared = await client.autofill(trustSet_tx)
        const ts_signed = standby_wallet.sign(ts_prepared)
        results += 'nCreating trust line from operational account to ' + operationalDestinationField.value + ' account...'
        operationalResultField.value = results
        const ts_result = await client.submitAndWait(ts_signed.tx_blob)
        if (ts_result.result.meta.TransactionResult == "tesSUCCESS") {
          results += 'nTrustline established between account n' + standby_wallet.address + ' n and accountn' + operationalDestinationField.value + '.'
          operationalResultField.value = results
        } else {
          results += 'nTrustLine failed. See JavaScript console for details.'
          operationalResultField.value = results     
          throw 'Error sending transaction: ${ts_result.result.meta.TransactionResult}'
        }
      } //End of oPcreateTrustline
      
// *******************************************************
// ************* Operational Send Issued Currency ********
// *******************************************************
      
      async function oPsendCurrency() {
        let net = getNet()
        const client = new xrpl.Client(net)
        results = 'Connecting to ' + getNet() + '....'
        operationalResultField.value = results
        
        await client.connect()
        
        results += 'nConnected.'
        operationalResultField.value = results
          
        const standby_wallet = xrpl.Wallet.fromSeed(standbySeedField.value)
        const operational_wallet = xrpl.Wallet.fromSeed(operationalSeedField.value)
        const currency_code = operationalCurrencyField.value
        const issue_quantity = operationalAmountField.value
        
        const send_token_tx = {
          "TransactionType": "Payment",
          "Account": operational_wallet.address,
          "Amount": {
            "currency": currency_code,
            "value": issue_quantity,
            "issuer": operational_wallet.address
          },
          "Destination": operationalDestinationField.value
        }
      
        const pay_prepared = await client.autofill(send_token_tx)
        const pay_signed = operational_wallet.sign(pay_prepared)
        results += 'Sending' + operationalAmountField.value + operationalCurrencyField.value + ' to ' + operationalDestinationField.value + '...'
        operationalResultField.value = results
        const pay_result = await client.submitAndWait(pay_signed.tx_blob)
        if (pay_result.result.meta.TransactionResult == "tesSUCCESS") {
          results += 'Transaction succeeded: https://testnet.xrpl.org/transactions/${pay_signed.hash}'
          operationalResultField.value = results
        } else {
          results += 'Transaction failed: See JavaScript console for details.'
          operationalResultField.value = results
          throw 'Error sending transaction: ${pay_result.result.meta.TransactionResult}'
        }
        standbyBalanceField.value = 
              (await client.getXrpBalance(standby_wallet.address))
        operationalBalanceField.value = 
              (await client.getXrpBalance(operational_wallet.address))
        getBalances()
        client.disconnect()

      } // end of oPsendCurrency()

HTML form preview: 2.create-trustline-send-currency.html

Update the form to support the new functions.

<html>
  <head>
    <title>Token Test Harness</title>
    <link href='https://fonts.googleapis.com/css?family=Work Sans' rel='stylesheet'>
    <style>
       body{font-family: "Work Sans", sans-serif;padding: 20px;background: #fafafa;}
       h1{font-weight: bold;}
       input, button {padding: 6px;margin-bottom: 8px;}
       button{font-weight: bold;font-family: "Work Sans", sans-serif;}
       td{vertical-align: top;padding-right:10px;}
    </style>    
    <script src='https://unpkg.com/xrpl@2.2.3'></script>

    <script>
      if (typeof module !== "undefined") {
        const xrpl = require('xrpl')
      }
    </script>
  </head>
  
<!-- ************************************************************** -->
<!-- ********************** The Form ****************************** -->
<!-- ************************************************************** -->

  <body>
    <h1>Token Test Harness</h1>
    <form id="theForm">
            Choose your ledger instance:  
      <input type="radio" id="tn" name="server" value="wss://s.altnet.rippletest.net:51233" >
      <label for="testnet">Testnet</label>
      
      <input type="radio" id="dn" name="server" value="wss://s.devnet.rippletest.net:51233" checked>
      <label for="devnet">Devnet</label>
      <br/><br/>
      <button type="button" onClick="getAccountsFromSeeds()">Get Accounts From Seeds</button>
      <br/>
      <textarea id="seeds" cols="40" rows= "2"></textarea>
      <br/><br/>
      <table>
        <tr valign="top">
          <td>
            <button type="button" onClick="getAccount('standby')">Get New Standby Account</button>
            <table>
              <tr valign="top">
                <td align="left">
                  Standby Account<br/> <input type="text" id="standbyAccountField" size="30" />
                </td>
                <td></td>
              </tr>
              <tr>
                <td align="left">
                  Public Key<br/>
                  <input type="text" id="standbyPubKeyField" size="30"></input>                                                 </td>
                <td align="left">
                  Private Key<br/>
                  <input type="text" id="standbyPrivKeyField" size="30"></input>
                </td>
              </tr>
              <tr>
                <td align="left">
                  Seed <br/>
                  <input type="text" id="standbySeedField" size="30"></input>
                  <br>
                </td>
                <td align="left">
                  XRP Balance <br/>
                  <input type="text" id="standbyBalanceField" size="30"></input>
                </td>
            </tr>
            <tr>
                <td align="left">
                  Amount<br/>
                  <input type="text" id="standbyAmountField" size="30"></input>
                </td>
                <td align="left">
                  Destination Account <br/>
                <input type="text" id="standbyDestinationField" size="30"></input>
                </td>
              </tr>
              <tr valign="top">
                <td><button type="button" onClick="configureAccount('standby',document.querySelector('#standbyDefault').checked)">Configure Account</button><br/>
                  <input type="checkbox" id="standbyDefault" checked="true"/>
                  <label for="standbyDefault">Allow Rippling</label>
                </td>

                <td>
                  Currency<br/>
                  <input type="text" id="standbyCurrencyField" size="30" value="USD"></input>
                </td>
              </tr>
              <tr>
                <td colspan=2>
                  <p align="right">
                    <button type="button" onClick="sendXRP()">Send XRP ↓</button>
                    <button type="button" onClick="createTrustline()">Create TrustLine</button>
                    <button type="button" onClick="sendCurrency()">Send Currency</button>
                    <button type="button" onClick="getBalances()">Get Balances</button>
                  </p>
                </td>
              </tr>
            </table>
          </td>
          <td>
            <textarea id="standbyResultField" cols="60" rows="20" ></textarea>
          </td>
        </tr>
      </table>
      <br/><br/>
      <table>
        <tr valign="top">
          <td>
            <button type="button" onClick="getAccount('operational')">Get New Operational Account</button>
            <table>
              <tr valign="top">
                <td align="left">
                  Operational Account<br/> <input type="text" id="operationalAccountField" size="30" />
                </td>
                <td></td>
              </tr>
              <tr>
                <td align="left">Public Key<br/>
                  <input type="text" id="operationalPubKeyField" size="30" />                                                 
                </td>
                <td align="left">
                  Private Key<br/>
                  <input type="text" id="operationalPrivKeyField" size="30"></input>
                </td>
              </tr>
              <tr>
                <td align="left">
                  Seed <br/>
                  <input type="text" id="operationalSeedField" size="30"></input>
                  <br>
                </td>
                <td align="left">
                  XRP Balance <br/>
                  <input type="text" id="operationalBalanceField" size="30" />
                </td>
            </tr>
            <tr>
                <td align="left">
                  Amount<br/>
                  <input type="text" id="operationalAmountField" size="30" />
                </td>
                <td align="left">
                  Destination Account <br/>
                <input type="text" id="operationalDestinationField" size="30" />
                </td>
              </tr>
              <tr valign="top">
                <td><button type="button" onClick="configureAccount('operational',document.querySelector('#operationalDefault').checked)">Configure Account</button><br/>
                  <input type="checkbox" id="operatoinalDefault" checked="true"/>
                  <label for="operationalDefault">Allow Rippling</label>
                </td>

                <td>
                  Currency<br/>
                  <input type="text" id="operationalCurrencyField" size="30" value="USD"></input>
                </td>
              </tr>
              <tr>
                <td colspan=2>
                  <p align="right">
                    <button type="button" onClick="oPsendXRP()">Send XRP ↑</button>
                    <button type="button" onClick="oPcreateTrustline()">Create TrustLine</button>
                    <button type="button" onClick="oPsendCurrency()">Send Currency</button>
                    <button type="button" onClick="getBalances()">Get Balances</button>
                  </p>
                </td>
              </tr>
            </table>
          </td>
          <td>
            <textarea id="operationalResultField" cols="60" rows="20" ></textarea>
          </td>
        </tr>
      </table>
    </form>
  </body>
  <script src='ripplex1-send-xrp.js' async></script>
  <script src='ripplex2-send-currency.js' async></script>
</html>
END OF LESSON

Now that you've learned a bit about creating trust line and sending currency on the XRP Ledger, test your knowledge with a quiz.

Why is a trust line necessary for transactions with tokens and currency?
Which of the following statements best summarizes how we use trust lines?
Which piece of code defines a transaction as a trust line?