Finish trade in the DEX tutorial draft

This commit is contained in:
mDuo13
2022-04-19 19:55:41 -07:00
parent 1173425ed9
commit bf166fffa1
8 changed files with 454 additions and 73 deletions

View File

@@ -0,0 +1,21 @@
// In browsers, add the following <script> tags the HTML to load dependencies
// instead of using require():
// <script src="https://unpkg.com/xrpl@2.2.0/build/xrpl-latest-min.js"></script>
// <script src='https://cdn.jsdelivr.net/npm/bignumber.js@9.0.2/bignumber.min.js'></script>
const xrpl = require('xrpl')
const BigNumber = require('bignumber.js')
// Wrap code in an async function so we can use await
async function main() {
// Define the network client
const client = new xrpl.Client("wss://s.altnet.rippletest.net:51233")
await client.connect()
// ... custom code goes here
// Disconnect when done (If you omit this, Node.js won't end the process)
client.disconnect()
}
main()

View File

@@ -1,5 +1,5 @@
// Stand-alone code sample for the "issue a token" tutorial:
// https://xrpl.org/issue-a-fungible-token.html
// Stand-alone code sample for "trade in the decentralized exchange" tutorial:
// https://xrpl.org/trade-in-the-decentralized-exchange.html
// Dependencies for Node.js.
// In browsers, use <script> tags as in the example demo.html.
@@ -9,20 +9,6 @@ if (typeof module !== "undefined") {
var BigNumber = require('bignumber.js')
}
/**
* Convert an XRPL amount, which could be a string (XRP in drops) or an object
* (token amount) to a string for display in one of the following formats:
* "123.456 XRP"
* "123.456 TST.rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd"
*/
def amt_str(amt) {
if (typeof amt == "string") {
return `${xrpl.dropsToXrp(amt)} XRP`
} else {
return `${amt.value} ${amt.currency}.${amt.issuer}`
}
}
// Connect ---------------------------------------------------------------------
async function main() {
const client = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
@@ -33,18 +19,27 @@ async function main() {
console.log("Requesting address from the Testnet faucet...")
const wallet = (await client.fundWallet()).wallet
console.log(`Got address ${wallet.address}.`)
//const wallet = xrpl.Wallet.fromSeed("SEED VALUE HERE") // temp for testing: don't fund every time
// To use existing credentials, you can load them from a seed value, for
// example using an environment variable as follows:
// const wallet = xrpl.Wallet.fromSeed(process.env['MY_SEED'])
// Define the proposed trade.
// Define the proposed trade. ------------------------------------------------
// Technically you don't need to specify the amounts (in the "value" field)
// to look up order books using book_offers, but for this tutorial we reuse
// these variables to construct the actual Offer later.
const we_want = {
currency: "TST",
issuer: "rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd",
value: "25"}
// 250 TST * 10 XRP per TST * 1.15 fx cost
const we_spend = {currency: "XRP", value: xrpl.xrpToDrops(25*10*1.05)}
value: "25"
}
const we_spend = {
currency: "XRP",
value: xrpl.xrpToDrops(25*10*1.15) // 25 TST * 10 XRP per TST * 15% fx cost
}
// "Quality" is defined as TakerPays ÷ TakerGets. The lower the "quality"
// number, the better the proposed exchange rate is for the taker.
// The quality is rounded to a number of significant digits based on the
// issuer's TickSize value (or the lesser of the two for token-token trades.)
const proposed_quality = BigNumber(we_spend.value) / BigNumber(we_want.value)
// Look up Offers. -----------------------------------------------------------
@@ -69,7 +64,7 @@ async function main() {
const offers = orderbook_resp.result.offers
const want_amt = BigNumber(we_want.value)
let running_total = 0
let running_total = BigNumber(0)
if (!offers) {
console.log(`No Offers in the matching book.
Offer probably won't execute immediately.`)
@@ -77,7 +72,7 @@ async function main() {
for (const o of offers) {
if (o.quality <= proposed_quality) {
console.log(`Matching Offer found, funded with ${o.owner_funds}`)
running_total += BigNumber(o.owner_funds)
running_total = running_total.plus(BigNumber(o.owner_funds))
if (running_total >= want_amt) {
console.log("Full Offer will probably fill")
break
@@ -96,7 +91,7 @@ async function main() {
}
}
if (running_total = 0) {
if (running_total == 0) {
// If part of the Offer was expected to cross, then the rest would be placed
// at the top of the order book. If none did, then there might be other
// Offers going the same direction as ours already on the books with an
@@ -127,7 +122,7 @@ async function main() {
for (const o of offers2) {
if (o.quality <= offered_quality) {
console.log(`Existing offer found, funded with ${o.owner_funds}`)
running_total2 += BigNumber(o.owner_funds)
running_total2 = running_total2.plus(BigNumber(o.owner_funds))
} else {
console.log(`Remaining orders are below where ours would be placed.`)
break
@@ -141,14 +136,12 @@ async function main() {
}
}
// Depending on your use case, you may want to present options to the user
// here with estimates of the cost to buy (or proceeds from selling) the
// assets involved. For this tutorial, we already know that TST is pegged to
// XRP at a rate of approximately 10:1 plus spread, so we can use a
// hard-coded TakerGets amount.
// Send OfferCreate transaction ----------------------------------------------
// For this tutorial, we already know that TST is pegged to
// XRP at a rate of approximately 10:1 plus spread, so we use
// hard-coded TakerGets and TakerPays amounts.
const offer_1 = {
"TransactionType": "OfferCreate",
"Account": wallet.address,
@@ -158,7 +151,6 @@ async function main() {
const prepared = await client.autofill(offer_1)
console.log("Prepared transaction:", JSON.stringify(prepared, null, 2))
//throw "BREAK"
const signed = wallet.sign(prepared)
console.log("Sending OfferCreate transaction...")
const result = await client.submitAndWait(signed.tx_blob)
@@ -168,20 +160,33 @@ async function main() {
throw `Error sending transaction: ${result}`
}
// Interpret metadata --------------------------------------------------------
// Check metadata ------------------------------------------------------------
// In JavaScript, you can use getBalanceChanges() to help summarize all the
// balance changes caused by a transaction.
const balance_changes = xrpl.getBalanceChanges(result.result.meta)
console.log("Total balance changes:", JSON.stringify(balance_changes, null, 2))
// Helper to convert an XRPL amount to a string for display
function amt_str(amt) {
if (typeof amt == "string") {
return `${xrpl.dropsToXrp(amt)} XRP`
} else {
return `${amt.value} ${amt.currency}.${amt.issuer}`
}
}
let offers_affected = 0
for (const affnode of result.result.meta.AffectedNodes) {
if (affnode.hasOwnProperty("ModifiedNode")) {
if (affnode.ModifiedNode.LedgerEntryType == "Offer") {
// Usually a ModifiedNode of type Offer indicates a previous Offer that
// was partially consumed by this one.
offers_affected += 1
}
} else if (affnode.hasOwnProperty("DeletedNode")) {
if (affnode.DeletedNode.LedgerEntryType == "Offer") {
// The removed Offer may have been fully consumed, or it may have been
// found to be expired or unfunded.
offers_affected += 1
}
} else if (affnode.hasOwnProperty("CreatedNode")) {
@@ -203,15 +208,16 @@ async function main() {
command: "account_lines",
account: wallet.address,
ledger_index: "validated"
// You could also use ledger_index: "current" to get pending data
})
console.log(JSON.stringify(balances.result, null, 2))
// Check Offers --------------------------------------------------------------
console.log(`Getting outstanding Offers from ${wallet.address} as of current ledger...`)
console.log(`Getting outstanding Offers from ${wallet.address} as of validated ledger...`)
const acct_offers = await client.request({
command: "account_offers",
account: wallet.address,
ledger_index: "current"
ledger_index: "validated"
})
console.log(JSON.stringify(acct_offers.result, null, 2))

View File

@@ -1,6 +1,7 @@
---
parent: use-tokens.html
blurb: Buy or sell fungible tokens for each other or for XRP in the decentralized exchange.
embed_xrpl_js: true
filters:
- interactive_steps
- include_code
@@ -14,41 +15,33 @@ This tutorial demonstrates how you can buy and sell tokens in the [decentralized
## Prerequisites
<!-- Source for this specific tutorial's interactive bits: -->
<script type="application/javascript" src="assets/js/tutorials/dex-trade.js"></script>
<script src='https://cdn.jsdelivr.net/npm/bignumber.js@9.0.2/bignumber.min.js'></script>
<script type="application/javascript" src="assets/js/tutorials/trade-in-the-dex.js"></script>
This page provides JavaScript examples that use the [xrpl.js](https://js.xrpl.org/) library. See [Get Started Using JavaScript](get-started-using-javascript.html) for setup instructions.
Since JavaScript works in the web browser, you can read along and use the interactive steps without any setup.
## Steps
{% set n = cycler(* range(1,99)) %}
This tutorial demonstrates how to conduct two trades: first, buying a token by selling XRP; then, selling that token to buy a different token. The two test tokens used in this tutorial are:
This tutorial demonstrates how to buy a fungible token in the decentralized exchange by selling XRP. (Other types of trades are possible, but selling a token, for example, requires you to have it first.) The example token used in this tutorial is as follows:
| Currency Code | Issuer | Notes |
|---|---|---|
| TST | rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd | A test token with a [`TickSize` setting](ticksize.html) of 5, pegged to XRP at a rate of 10 XRP per 1 TST. |
| TS2 | ***TODO: TS2 issuer setup*** | A test token with a 2% [transfer fee](transfer-fees.html). |
### {{n.next()}}. Get Credentials
To transact on the XRP Ledger, you need an address and secret key, and some XRP. For development purposes, you can get these on the [{{use_network}}](parallel-networks.html) using the following interface:
{% include '_snippets/interactive-tutorials/generate-step.md' %}
When you're [building production-ready software](production-readiness.html), you should use an existing account, and manage your keys using a [secure signing configuration](set-up-secure-signing.html).
| TST | rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd | A test token pegged to XRP at a rate of approximately 10 XRP per 1 TST. The issuer has existing Offers on the XRP Ledger Testnet to buy and sell these tokens for |
### {{n.next()}}. Connect to Network
You must be connected to the network to submit transactions to it. The following code shows how to connect to a public XRP Ledger Testnet server a supported [client library](client-libraries.html):
You must be connected to the network to submit transactions to it. Additionally, some languages (including JavaScript) require a high-precision number library for performing calculations on currency amounts you may encounter in the ledger. The following code shows how to connect to a public XRP Ledger Testnet server a supported [client library](client-libraries.html) with the appropriate dependencies.
<!-- MULTICODE_BLOCK_START -->
_JavaScript_
{{ include_code("_code-samples/get-started/js/base.js", language="js") }}
{{ include_code("_code-samples/trade-in-the-decentralized-exchange/js/base-with-bignumber.js", language="js") }}
<!-- MULTICODE_BLOCK_END -->
@@ -58,28 +51,49 @@ For this tutorial, click the following button to connect:
{% include '_snippets/interactive-tutorials/connect-step.md' %}
### {{n.next()}}. Get Credentials
To transact on the XRP Ledger, you need an address and secret key, and some XRP. For development purposes, you can get these on the [{{use_network}}](parallel-networks.html) using the following interface:
{% include '_snippets/interactive-tutorials/generate-step.md' %}
When you're [building production-ready software](production-readiness.html), you should use an existing account, and manage your keys using a [secure signing configuration](set-up-secure-signing.html). The following code shows how to instantiate a class with the wallet instance:
<!-- MULTICODE_BLOCK_START -->
_JavaScript_
{{ include_code("_code-samples/trade-in-the-decentralized-exchange/js/trade-in-the-dex.js", language="js", start_with="// Get credentials", end_before="// Define the proposed trade") }}
<!-- MULTICODE_BLOCK_END -->
### {{n.next()}}. Look Up Offers
Before you buy or sell a token, you usually want to look up what others are buying and selling for, to get a sense of how others value it. In the XRP Ledger, you can look up existing offers for any currency pair using the [book_offers method][].
***TODO: interactive block, code samples, explanation of results***
**Tip:** Technically, this step is not a requirement for placing an Offer, but it is a good practice to confirm the current situation before trading anything with real value.
### {{n.next()}}. Send OfferCreate Transaction
The following code shows how to look up existing Offers and compare them to a proposed Offer to estimate how it would execute:
To actually make a trade, send an [OfferCreate transaction][]. In this case, you want to buy TST using XRP, so you should set the parameters as follows:
<!-- MULTICODE_BLOCK_START -->
| Field | Type | Description |
|---|---|---|
| `TakerPays` | [Token Amount object](#specifying-currency-amounts) | How much of what currency you want to buy, in total. For this tutorial, buy some amount of **TST** issued by `rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd`. |
| `TakerGets` | [XRP, in drops][] | How much of what currency you are offering to pay in total. For this tutorial, you should offer approximately slightly over 10 XRP per TST. |
_JavaScript_
{{ start_step("Send OfferCreate") }}
{{ include_code("_code-samples/trade-in-the-decentralized-exchange/js/trade-in-the-dex.js", language="js", start_with="// Define the proposed trade", end_before="// Send OfferCreate") }}
<!-- MULTICODE_BLOCK_END -->
**Note:** Other users of the XRP Ledger can also make trades at any time, so this is only an estimate of what would happen if nothing else changes. The outcome of a transaction is not guaranteed until it is [final](finality-of-results.html).
The following block demonstrates these calculations in action:
{{ start_step("Look Up Offers") }}
<form>
<div class="row"><h5>TakerPays</h5></div>
<h5>TakerPays</h5>
<div class="form-group row">
<label for="taker-pays-currency-1" class="col-form-label col-sm-3">currency</label>
<div class="input-group col">
<input type="text" class="form-control" id="taker-pays-currency-1" value="FOO" disabled="disabled" />
<input type="text" class="form-control" id="taker-pays-currency-1" value="TST" disabled="disabled" />
</div>
</div>
<div class="form-group row">
@@ -94,7 +108,7 @@ To actually make a trade, send an [OfferCreate transaction][]. In this case, you
<input type="number" class="form-control" id="taker-pays-amount-1" value="10" step="0.000001" min="0" max="1000" />
</div>
</div>
<div class="row"><h5>TakerGets</h5></div>
<h5>TakerGets</h5>
<div class="form-group row">
<label for="taker-gets-amount-1" class="col-form-label col-sm-3">XRP:</label>
<div class="input-group col">
@@ -103,14 +117,41 @@ To actually make a trade, send an [OfferCreate transaction][]. In this case, you
min=".000001" max="100000000000" step="any" />
</div>
</div>
<div class="row"><h5>Exchange Rate</h5></div>
<h5>Exchange Rate</h5>
<div class="form-group row">
<label for="taker-gets-amount-1" class="col-form-label col-sm-3">XRP cost per 1 FOO:</label>
<label for="exchange-rate-1" class="col-form-label col-sm-3">XRP cost per 1 TST:</label>
<div class="input-group col">
<input type="number" class="form-control" value="" id="exchange-rate-1" />
<input type="number" class="form-control" value="11.5" id="exchange-rate-1" step="any" disabled="disabled" />
</div>
</div>
</form>
<button id="look-up-offers" class="btn btn-primary previous-steps-required">Look Up Offers</button>
<div class="loader collapse"><img class="throbber" src="assets/img/xrp-loader-96.png">Querying...</div>
<div class="output-area"></div>
{{ end_step() }}
### {{n.next()}}. Send OfferCreate Transaction
To actually make a trade, send an [OfferCreate transaction][]. In this case, you want to buy TST using XRP, so you should set the parameters as follows:
| Field | Type | Description |
|---|---|---|
| `TakerPays` | [Token Amount object](#specifying-currency-amounts) | How much of what currency you want to buy, in total. For this tutorial, buy some amount of **TST** issued by `rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd`. |
| `TakerGets` | [XRP, in drops][] | How much of what currency you are offering to pay in total. For this tutorial, you should specify about 11.5 XRP per TST or slightly more. |
The following code shows how to prepare, sign, and submit the transaction:
<!-- MULTICODE_BLOCK_START -->
_JavaScript_
{{ include_code("_code-samples/trade-in-the-decentralized-exchange/js/trade-in-the-dex.js", language="js", start_with="// Send OfferCreate", end_before="// Check metadata") }}
<!-- MULTICODE_BLOCK_END -->
You can use this interface to send the transaction specified by the amounts in the previous step:
{{ start_step("Send OfferCreate") }}
<button id="send-offercreate" class="btn btn-primary previous-steps-required" data-wait-step-name="Wait">Send OfferCreate</button>
<div class="loader collapse"><img class="throbber" src="assets/img/xrp-loader-96.png">Sending...</div>
<div class="output-area"></div>
@@ -126,13 +167,52 @@ Most transactions are accepted into the next ledger version after they're submit
### {{n.next()}}. Check Metadata
The [transaction metadata](transaction-metadata.html) describes the outcome of a transaction. You can use a validated transaction's metadata to determine exactly what it did. (Don't use metadata from tentative transaction results, because it may be different from the [final result](finality-of-results.html), especially when using the decentralized exchange.) In case of an OfferCreate transaction, likely results include:
You can use the validated transaction's [metadata](transaction-metadata.html) to determine exactly what it did. (Don't use metadata from tentative transaction results, because it may be different from the [final result](finality-of-results.html), especially when using the decentralized exchange.) In case of an OfferCreate transaction, likely results include:
- Some or all of the Offer may have been filled by matching with existing Offers in the ledger.
- The unmatched remainder, if any, has been placed into the ledger.
- The unmatched remainder, if any, has been placed into the ledger to await new matching Offers.
- Other bookkeeping may have occurred, such as removing expired or unfunded Offers that would have matched.
The following code demonstrates how to check the metadata of the transaction:
<!-- MULTICODE_BLOCK_START -->
_JavaScript_
{{ include_code("_code-samples/trade-in-the-decentralized-exchange/js/trade-in-the-dex.js", language="js", start_with="// Check metadata", end_before="// Check balances") }}
<!-- MULTICODE_BLOCK_END -->
You can use this interface to test it out:
{{ start_step("Check Metadata") }}
<button id="check-metadata" class="btn btn-primary previous-steps-required">Check Metadata</button>
<div class="loader collapse"><img class="throbber" src="assets/img/xrp-loader-96.png">Checking...</div>
<div class="output-area"></div>
{{ end_step() }}
### {{n.next()}}. Check Balances and Offers
This is also a good time to look up the balances and outstanding Offers owned by your account as of the latest validated ledger. This shows any changes caused by your transaction as well as any others that executed in the same ledger version.
The following code demonstrates how to look up balances using the [account_lines method][] and look up Offers using the [account_offers method][].
<!-- MULTICODE_BLOCK_START -->
_JavaScript_
{{ include_code("_code-samples/trade-in-the-decentralized-exchange/js/trade-in-the-dex.js", language="js", start_with="// Check balances", end_before="client.disconnect()") }}
<!-- MULTICODE_BLOCK_END -->
You can use this interface to test it out:
{{ start_step("Check Balances and Offers") }}
<button id="check-balances-and-offers" class="btn btn-primary previous-steps-required">Check Balances and Offers</button>
<div class="loader collapse"><img class="throbber" src="assets/img/xrp-loader-96.png">Checking...</div>
<div class="output-area"></div>
{{ end_step() }}
<!--{# common link defs #}-->
{% include '_snippets/rippled-api-links.md' %}