<aside> 📜 TABLE OF CONTENTS

1. 📘 Introduction to XRP Ledger

2. 🔑 XRP Ledger Basics

3. 💻 Interacting with XRP Ledger

4. 🏗️ Building dApps on XRPL with React

5. 🔗 EVM Sidechain Integration for XRPL

6. 🛠️ Non-EVM Development

7. 📚 Resources and References

</aside>

http://quiz.xrplsea.org

🚀 Non-EVM Development on XRP Ledger

📊 Transactions on the XRP Ledger

Understanding Transactions

Transactions are the core of the XRP Ledger. They represent actions that can change the ledger state.

🔑 Key Concepts:

💸 Sending XRP

Here's an example of how to send XRP using the xrpl library:

const xrpl = require("xrpl")

async function sendXRP() {
  const client = new xrpl.Client("wss://s.altnet.rippletest.net:51233")
  await client.connect()

  // Create test account
  const fund_result = await client.fundWallet()
  const test_wallet = fund_result.wallet

  const prepared = await client.autofill({
    "TransactionType": "Payment",
    "Account": test_wallet.address,
    "Amount": xrpl.xrpToDrops("22"),
    "Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"
  })

  const signed = test_wallet.sign(prepared)
  console.log("Sending XRP...")
  const tx = await client.submitAndWait(signed.tx_blob)

  console.log("Transaction result:", tx.result.meta.TransactionResult)
  console.log("Balance changes:", JSON.stringify(xrpl.getBalanceChanges(tx.result.meta), null, 2))

  await client.disconnect()
}

sendXRP().catch(console.error)

💡 This script connects to the testnet, funds a wallet, prepares a payment transaction, signs it, and submits it to the network.

🔍 Transaction Metadata

Understanding transaction metadata is crucial for interpreting the results of your transactions.

📝 Key aspects:

🪙 Tokens on the XRP Ledger

Issued Currencies (Tokens)

The XRP Ledger allows the issuance of custom tokens, known as "issued currencies".

🔑 Key Concepts:

🏦 Issuing Tokens

Here's an example of how to issue a token on the XRP Ledger:

const xrpl = require("xrpl")

async function issueToken() {
  const client = new xrpl.Client("wss://s.altnet.rippletest.net:51233")
  await client.connect()

  // Create issuer and customer accounts
  const issuer_wallet = (await client.fundWallet()).wallet
  const customer_wallet = (await client.fundWallet()).wallet

  // Create trust line from customer to issuer
  const trust_set_tx = {
    "TransactionType": "TrustSet",
    "Account": customer_wallet.address,
    "LimitAmount": {
      "currency": "FOO",
      "issuer": issuer_wallet.address,
      "value": "10000000000" // Large limit, arbitrarily chosen
    }
  }

  const ts_prepared = await client.autofill(trust_set_tx)
  const ts_signed = customer_wallet.sign(ts_prepared)
  const ts_result = await client.submitAndWait(ts_signed.tx_blob)
  console.log("TrustSet transaction result:", ts_result.result.meta.TransactionResult)

  // Issue token from issuer to customer
  const issue_tx = {
    "TransactionType": "Payment",
    "Account": issuer_wallet.address,
    "Amount": {
      "currency": "FOO",
      "value": "1000",
      "issuer": issuer_wallet.address
    },
    "Destination": customer_wallet.address
  }

  const issue_prepared = await client.autofill(issue_tx)
  const issue_signed = issuer_wallet.sign(issue_prepared)
  const issue_result = await client.submitAndWait(issue_signed.tx_blob)
  console.log("Issue transaction result:", issue_result.result.meta.TransactionResult)

  await client.disconnect()
}

issueToken().catch(console.error)

🚀 This script creates two accounts, establishes a trust line, and then issues tokens from the issuer to the customer.

💹 Decentralized Exchange (DEX)

The XRP Ledger includes a built-in decentralized exchange for trading tokens.

🔑 Key Concepts:

📈 Creating and Managing Offers

Creating an Offer

Here's how to create an offer on the DEX:

const xrpl = require('xrpl')

async function createOffer() {
  const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
  await client.connect()

  // Create and fund a test wallet
  const wallet = xrpl.Wallet.generate()
  const fund_result = await client.fundWallet(wallet)

  // Prepare the offer
  const offer = {
    "TransactionType": "OfferCreate",
    "Account": wallet.address,
    "TakerGets": {
      "currency": "XRP",
      "value": "100000000" // 100 XRP in drops
    },
    "TakerPays": {
      "currency": "USD",
      "issuer": "rN6KtYz9S76VatuyVLpzJJEkKAdyVaXMUw",
      "value": "50"
    }
  }

  // Submit the transaction
  const prepared = await client.autofill(offer)
  const signed = wallet.sign(prepared)
  const result = await client.submitAndWait(signed.tx_blob)

  console.log("Offer Create result:", result.result.meta.TransactionResult)
  console.log("Offer ID:", result.result.OfferID)

  await client.disconnect()
}

createOffer().catch(console.error)

💡 This example creates an offer to sell 100 XRP for 50 USD.

Canceling an Offer

To cancel an existing offer:

async function cancelOffer() {
  const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
  await client.connect()

  const wallet = xrpl.Wallet.generate() // Use the wallet that created the offer

  const cancelTx = {
    "TransactionType": "OfferCancel",
    "Account": wallet.address,
    "OfferSequence": 12345 // The sequence number of the OfferCreate transaction
  }

  const prepared = await client.autofill(cancelTx)
  const signed = wallet.sign(prepared)
  const result = await client.submitAndWait(signed.tx_blob)

  console.log("Offer Cancel result:", result.result.meta.TransactionResult)

  await client.disconnect()
}

cancelOffer().catch(console.error)

🔄 Trading on DEX

Checking Order Books

Before trading, you might want to check the current state of the order book:

async function checkOrderBook() {
  const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
  await client.connect()

  const request = {
    command: "book_offers",
    taker_gets: {
      currency: "XRP"
    },
    taker_pays: {
      currency: "USD",
      issuer: "rN6KtYz9S76VatuyVLpzJJEkKAdyVaXMUw"
    },
    limit: 10
  }

  const response = await client.request(request)
  console.log("Order Book:", JSON.stringify(response.result.offers, null, 2))

  await client.disconnect()
}

checkOrderBook().catch(console.error)

🔍 This will show you the top 10 offers for the XRP/USD pair.

Executing a Trade

To execute a trade, you create an OfferCreate transaction that crosses an existing offer:

async function executeTrade() {
  const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
  await client.connect()

  const wallet = xrpl.Wallet.generate()
  await client.fundWallet(wallet)

  const trade = {
    "TransactionType": "OfferCreate",
    "Account": wallet.address,
    "TakerGets": {
      "currency": "USD",
      "issuer": "rN6KtYz9S76VatuyVLpzJJEkKAdyVaXMUw",
      "value": "50"
    },
    "TakerPays": "100000000" // 100 XRP in drops
  }

  const prepared = await client.autofill(trade)
  const signed = wallet.sign(prepared)
  const result = await client.submitAndWait(signed.tx_blob)

  console.log("Trade execution result:", result.result.meta.TransactionResult)
  console.log("Balances changed:", JSON.stringify(xrpl.getBalanceChanges(result.result.meta), null, 2))

  await client.disconnect()
}

executeTrade().catch(console.error)

🚀 This creates an offer that should immediately be matched with an existing offer, effectively executing a trade.

🔬 Advanced DEX Features

Partial Payments

Partial payments allow the amount delivered to be less than the amount specified:

async function partialPayment() {
  const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
  await client.connect()

  const wallet = xrpl.Wallet.generate()
  await client.fundWallet(wallet)

  const payment = {
    "TransactionType": "Payment",
    "Account": wallet.address,
    "Amount": {
      "currency": "USD",
      "issuer": "rN6KtYz9S76VatuyVLpzJJEkKAdyVaXMUw",
      "value": "100"
    },
    "Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
    "SendMax": "200000000", // 200 XRP in drops
    "Flags": xrpl.PaymentFlags.tfPartialPayment
  }

  const prepared = await client.autofill(payment)
  const signed = wallet.sign(prepared)
  const result = await client.submitAndWait(signed.tx_blob)

  console.log("Partial payment result:", result.result.meta.TransactionResult)
  console.log("Delivered amount:", result.result.meta.delivered_amount)

  await client.disconnect()
}

partialPayment().catch(console.error)

💡 This attempts to deliver 100 USD, but will deliver less if the full amount can't be exchanged for 200 XRP or less.

Using Autobridging

Autobridging happens automatically when it provides a better rate. Here's how you might set up a trade that could use autobridging:

async function autobridgedTrade() {
  const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
  await client.connect()

  const wallet = xrpl.Wallet.generate()
  await client.fundWallet(wallet)

  const trade = {
    "TransactionType": "OfferCreate",
    "Account": wallet.address,
    "TakerGets": {
      "currency": "EUR",
      "issuer": "rN6KtYz9S76VatuyVLpzJJEkKAdyVaXMUw",
      "value": "50"
    },
    "TakerPays": {
      "currency": "USD",
      "issuer": "rN6KtYz9S76VatuyVLpzJJEkKAdyVaXMUw",
      "value": "60"
    }
  }

  const prepared = await client.autofill(trade)
  const signed = wallet.sign(prepared)
  const result = await client.submitAndWait(signed.tx_blob)

  console.log("Autobridged trade result:", result.result.meta.TransactionResult)
  console.log("Balances changed:", JSON.stringify(xrpl.getBalanceChanges(result.result.meta), null, 2))

  await client.disconnect()
}

autobridgedTrade().catch(console.error)

🏗️ Advanced XRP Ledger Features

🔒 Escrow

Escrow is a feature that allows you to send conditional XRP payments. The XRP can be held in escrow and then either be released to the recipient or returned to the sender when certain conditions are met.

Creating an Escrow

To create an escrow, you use the EscrowCreate transaction:

const xrpl = require('xrpl')
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')

async function createEscrow() {
  await client.connect()

  // Create a wallet for testing
  const wallet = xrpl.Wallet.generate()

  // Fund the wallet using testnet faucet
  const fund_result = await client.fundWallet(wallet)

  // Prepare the transaction
  const prepared = await client.autofill({
    "TransactionType": "EscrowCreate",
    "Account": wallet.address,
    "Amount": xrpl.xrpToDrops("100"), // Amount of XRP to escrow
    "Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe", // Recipient's address
    "FinishAfter": xrpl.isoTimeToRippleTime('2023-01-01T00:00:00Z'), // When the escrow can be finished
    "CancelAfter": xrpl.isoTimeToRippleTime('2023-06-01T00:00:00Z'), // When the escrow can be cancelled
    "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100" // Optional crypto-condition
  })

  // Sign the transaction
  const signed = wallet.sign(prepared)

  // Submit the transaction
  const result = await client.submitAndWait(signed.tx_blob)

  console.log("Escrow created. Transaction result:", result.result.meta.TransactionResult)
  console.log("Escrow sequence number:", result.result.Sequence)

  await client.disconnect()
}

createEscrow().catch(console.error)

🔒 This script creates an escrow that can be finished after January 1, 2023, and cancelled after June 1, 2023.

Finishing an Escrow

To finish (execute) an escrow, you use the EscrowFinish transaction:

async function finishEscrow() {
  await client.connect()

  const wallet = xrpl.Wallet.generate() // The wallet finishing the escrow

  const prepared = await client.autofill({
    "TransactionType": "EscrowFinish",
    "Account": wallet.address,
    "Owner": "rN7n7otQDd6FczFgLdSqtcsAUxDkw6fzRH", // Address of escrow creator
    "OfferSequence": 7, // Sequence number of EscrowCreate transaction
    "Condition": "A0258020E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855810100",
    "Fulfillment": "A0028000" // Fulfillment that satisfies the condition
  })

  const signed = wallet.sign(prepared)
  const result = await client.submitAndWait(signed.tx_blob)

  console.log("Escrow finished. Transaction result:", result.result.meta.TransactionResult)

  await client.disconnect()
}

finishEscrow().catch(console.error)

🔓 This script finishes an escrow, releasing the escrowed XRP to the intended recipient.

📝 Multi-signing

Multi-signing allows multiple parties to authorize a transaction. This is useful for shared accounts or increased security.

Setting Up Multi-signing

First, you need to set up a signer list for an account:

async function setupMultisigning() {
  await client.connect()

  const wallet = xrpl.Wallet.generate() // The account to set up for multi-signing
  await client.fundWallet(wallet)

  const prepared = await client.autofill({
    "TransactionType": "SignerListSet",
    "Account": wallet.address,
    "SignerQuorum": 3, // Number of signers required
    "SignerEntries": [
      {
        "SignerEntry": {
          "Account": "rUpy3eEg8rqjqfUoLeBnZkscbKbFsKXC3v",
          "SignerWeight": 2
        }
      },
      {
        "SignerEntry": {
          "Account": "rPcNzota6B8YBokhYtcTNqQVCngtbnWfux",
          "SignerWeight": 1
        }
      },
      {
        "SignerEntry": {
          "Account": "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf",
          "SignerWeight": 1
        }
      }
    ]
  })

  const signed = wallet.sign(prepared)
  const result = await client.submitAndWait(signed.tx_blob)

  console.log("Multi-signing setup. Transaction result:", result.result.meta.TransactionResult)

  await client.disconnect()
}

setupMultisigning().catch(console.error)

🔐 This script sets up a signer list requiring 3 weights worth of signatures, with one signer having a weight of 2 and two signers having a weight of 1 each.

Creating a Multi-signed Transaction

Here's how to create and submit a multi-signed transaction:

async function createMultisignedTx() {
  await client.connect()

  const wallet1 = xrpl.Wallet.fromSeed("sn3nxiW7v8KXzPzAqzyHXbSSKNuN9") // Example seed, don't use in production
  const wallet2 = xrpl.Wallet.fromSeed("sswoqc5X8puRLqMv1racial3FMvNP") // Example seed, don't use in production

  const prepared = await client.autofill({
    "TransactionType": "Payment",
    "Account": "rEuLyBCvcw4CFmzv8RepSiAoNgF8tTGJQC",
    "Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh",
    "Amount": "20000000" // 20 XRP
  })

  const signed1 = wallet1.sign(prepared, true) // true for multi-signing
  const signed2 = wallet2.sign(prepared, true)

  const multisigned = {
    ...prepared,
    SigningPubKey: "",
    Signers: [
      {
        Signer: {
          Account: wallet1.address,
          SigningPubKey: wallet1.publicKey,
          TxnSignature: signed1.signature
        }
      },
      {
        Signer: {
          Account: wallet2.address,
          SigningPubKey: wallet2.publicKey,
          TxnSignature: signed2.signature
        }
      }
    ]
  }

  const result = await client.submitAndWait(multisigned)

  console.log("Multi-signed transaction result:", result.result.meta.TransactionResult)

  await client.disconnect()
}

createMultisignedTx().catch(console.error)

🖋️ This script creates a payment transaction and signs it with two different wallets, demonstrating how a multi-signed transaction is constructed and submitted.

💳 Payment Channels

Payment channels allow for fast, off-ledger payments that can be later reconciled on the XRP Ledger.

Opening a Payment Channel

To open a payment channel:

async function openPaymentChannel() {
  await client.connect()

  const wallet = xrpl.Wallet.generate()
  await client.fundWallet(wallet)

  const prepared = await client.autofill({
    "TransactionType": "PaymentChannelCreate",
    "Account": wallet.address,
    "Amount": xrpl.xrpToDrops("100"),
    "Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
    "SettleDelay": 86400,
    "PublicKey": wallet.publicKey
  })

  const signed = wallet.sign(prepared)
  const result = await client.submitAndWait(signed.tx_blob)

  console.log("Payment channel opened. Transaction result:", result.result.meta.TransactionResult)
  console.log("Payment Channel ID:", result.result.ChannelID)

  await client.disconnect()
}

openPaymentChannel().catch(console.error)

🔓 This script opens a payment channel, allowing for off-ledger transactions between two parties.

Claiming from a Payment Channel

To claim XRP from a payment channel:

async function claimFromPaymentChannel() {
  await client.connect()

  const wallet = xrpl.Wallet.generate() // The recipient's wallet

  // The payment channel ID
  const channelId = "5DB01B7FFED6B67E6B0414DED11E051D2EE2B7619CE0EAA6286D67A3A4D5BDB3"

  // The amount to claim, in drops of XRP
  const amount = "1000000"

  // The channel owner's public key
  const channelPublicKey = "aB44YfzW24VDEJQ2UuLPV2PvqcPCSoLnL7y5M1EzhdW4LnK5xMS3"

  // Sign the claim with the channel owner's key
  const claim = {
    channelId: channelId,
    amount: amount
  }
  const signature = xrpl.authorizeChannel(claim, channelPublicKey)

  // Prepare the transaction
  const prepared = await client.autofill({
    "TransactionType": "PaymentChannelClaim",
    "Account": wallet.address,
    "Channel": channelId,
    "Amount": amount,
    "Signature": signature,
    "PublicKey": channelPublicKey
  })

  const signed = wallet.sign(prepared)
  const result = await client.submitAndWait(signed.tx_blob)

  console.log("Claim from payment channel. Transaction result:", result.result.meta.TransactionResult)

  await client.disconnect()
}

claimFromPaymentChannel().catch(console.error)

💰 This script demonstrates how to claim XRP from an open payment channel, finalizing off-ledger transactions on the XRP Ledger.

🎓 Conclusion

This guide has covered a wide range of non-EVM development topics on the XRP Ledger, including:

Remember to always test your code thoroughly on the testnet before deploying to the mainnet. Happy coding on the XRP Ledger! 🚀