Temporary: move static files into content

During migration, while we launch Redocly with -d content, the static
files need to be in content. Eventually, when we stop using -d, we need
to move the files again.

Previously:
/assets : static assets used by templates
/img : images used in documentation (mostly)

Now:
/content/static : static assets used by templates
/img : images used in documentation (mostly)

Eventually:
/static : static assets used by templates
/docs/img : images used in documentation
This commit is contained in:
mDuo13
2024-01-31 16:04:58 -08:00
parent 22157c5227
commit e4ceb8b37b
1176 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,273 @@
// 1. Generate
// 2. Connect
// The code for these steps is handled by interactive-tutorial.js
$(document).ready(() => {
const EXPLORER = $("#connect-button").data("explorer")
$("#get-foo").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const wallet = get_wallet(event)
if (!wallet) {return}
const currency_code = "FOO"
const issue_quantity = "1000"
block.find(".loader").show()
show_log(block, "<p>Funding an issuer address with the faucet...</p>")
const issuer = (await api.fundWallet()).wallet
show_log(block, `<p>Got issuer <span id="issuer-address" data-seed="${issuer.seed}">${issuer.address}</span>.</p>`)
$(".foo-issuer").text(issuer.address) // Update display in the "Create AMM" step
// Enable issuer DefaultRipple ----------------------------------------------
const issuer_setup_tx = {
"TransactionType": "AccountSet",
"Account": issuer.address,
"SetFlag": xrpl.AccountSetAsfFlags.asfDefaultRipple
}
add_memo(event, issuer_setup_tx)
const issuer_setup_result = await api.submitAndWait(issuer_setup_tx, {autofill: true, wallet: issuer} )
if (issuer_setup_result.result.meta.TransactionResult == "tesSUCCESS") {
show_log(block, `<p><a href="${EXPLORER}/transactions/${issuer_setup_result.result.hash}">✅ Issuer DefaultRipple enabled</a></p>`)
} else {
show_error(block, `Error sending transaction: <pre><code>${pretty_print(issuer_setup_result)}</code></pre>`)
}
// Create trust line to issuer ----------------------------------------------
const trust_tx = {
"TransactionType": "TrustSet",
"Account": wallet.address,
"LimitAmount": {
"currency": currency_code,
"issuer": issuer.address,
"value": "10000000000" // Large limit, arbitrarily chosen
}
}
add_memo(event, trust_tx)
const trust_result = await api.submitAndWait(trust_tx, {autofill: true, wallet: wallet})
if (trust_result.result.meta.TransactionResult == "tesSUCCESS") {
show_log(block, `<p><a href="${EXPLORER}/transactions/${trust_result.result.hash}">✅ Trust line created</a></p>`)
} else {
show_error(block, `Error sending transaction: <pre><code>${pretty_print(trust_result)}</code></pre>`)
}
// Issue tokens -------------------------------------------------------------
const issue_tx = {
"TransactionType": "Payment",
"Account": issuer.address,
"Amount": {
"currency": currency_code,
"value": issue_quantity,
"issuer": issuer.address
},
"Destination": wallet.address
}
add_memo(event, issue_tx)
const issue_result = await api.submitAndWait(issue_tx, {autofill: true, wallet: issuer})
if (issue_result.result.meta.TransactionResult == "tesSUCCESS") {
show_log(block, `<p><a href="${EXPLORER}/transactions/${issue_result.result.hash}">✅ Tokens issued</a></p>`)
$("#get-foo").data("foo-acquired", true).prop("disabled", true).addClass("disabled").addClass("done")
} else {
show_error(block, `Error sending transaction: <pre><code>${pretty_print(issue_result)}</code></pre>`)
}
block.find(".loader").hide()
if ($("#get-foo").data("foo-acquired") && $("#buy-tst").data("tst-acquired")) {
complete_step("Acquire tokens")
}
})
$("#buy-tst").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const wallet = get_wallet(event)
if (!wallet) {return}
block.find(".loader").show()
const tx_json = {
"TransactionType": "OfferCreate",
"Account": wallet.address,
"TakerPays": {
currency: "TST",
issuer: "rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd",
value: "25"
},
"TakerGets": xrpl.xrpToDrops(25*10*1.16)
}
add_memo(event, tx_json)
const offer_result = await api.submitAndWait(tx_json, {autofill: true, wallet: wallet})
if (offer_result.result.meta.TransactionResult == "tesSUCCESS") {
show_log(block, `<p><a href="${EXPLORER}/transactions/${offer_result.result.hash}">✅ TST offer placed</a></p>`)
const balance_changes = xrpl.getBalanceChanges(offer_result.result.meta)
for (const bc of balance_changes) {
if (bc.account != wallet.address) {continue}
for (const bal of bc.balances) {
if (bal.currency == "TST") {
show_log(block, `<p>Got <strong>${bal.value}</strong> ${bal.currency}.${bal.issuer}.</p>`)
break
}
}
break
}
$("#buy-tst").data("tst-acquired", true).prop("disabled", true).addClass("disabled").addClass("done")
} else {
show_error(block, `<p>Transaction failed:</p><pre><code>${pretty_print(offer_result)}</code></pre>`)
}
block.find(".loader").hide()
if ($("#get-foo").data("foo-acquired") && $("#buy-tst").data("tst-acquired")) {
complete_step("Acquire tokens")
}
})
$("#check-for-amm").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const foo_issuer_address = $("#issuer-address").text()
block.find(".output-area").html("")
block.find(".loader").show()
try {
const amm_info = await api.request({
"command": "amm_info",
"asset": {
"currency": "TST",
"issuer": "rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd"
},
"asset2": {
"currency": "FOO",
"issuer": foo_issuer_address
},
"ledger_index": "validated"
})
show_log(block, `<pre><code>${pretty_print}amm_info</code></pre>`)
} catch(err) {
if (err.data.error === 'actNotFound') {
show_log(block, `<p>✅ No AMM exists yet for the pair
FOO.${foo_issuer_address} /
TST.rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd.`)
complete_step("Check for AMM")
} else {
show_error(block, err)
}
}
block.find(".loader").hide()
})
$("#look-up-ammcreate-cost").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
block.find(".loader").show()
let amm_fee_drops = "5000000"
try {
const ss = await api.request({"command": "server_state"})
amm_fee_drops = ss.result.state.validated_ledger.reserve_inc.toString()
show_log(block, `<p>Current AMMCreate transaction cost: ${xrpl.dropsToXrp(amm_fee_drops)} XRP (<span id="ammcreate-cost-drops">${amm_fee_drops}</span> drops)</p>`)
complete_step("Look up AMMCreate cost")
} catch(err) {
show_error(block, `Error looking up AMMCreate tx cost: ${err}`)
}
block.find(".loader").hide()
})
$("#create-amm").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const wallet = get_wallet(event)
if (!wallet) {return}
amm_fee_drops = $("#ammcreate-cost-drops").text()
if (!amm_fee_drops) {return}
block.find(".output-area").html("")
block.find(".loader").show()
const asset_amount = $("#asset-amount").val()
const asset2_amount = $("#asset2-amount").val()
const asset2_issuer_address = $("#issuer-address").text()
const trading_fee = Math.floor($("#trading-fee").val()*1000) // Convert from %
const ammcreate_tx = {
"TransactionType": "AMMCreate",
"Account": wallet.address,
"Amount": {
currency: "TST",
issuer: "rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd",
value: asset_amount
},
"Amount2": {
"currency": "FOO",
"issuer": asset2_issuer_address,
"value": asset2_amount
},
"TradingFee": 500, // 0.5%
"Fee": amm_fee_drops
}
add_memo(event, ammcreate_tx)
const ammcreate_result = await api.submitAndWait(ammcreate_tx, {autofill: true, wallet: wallet, fail_hard: true})
if (ammcreate_result.result.meta.TransactionResult == "tesSUCCESS") {
show_log(block, `<p><a href="${EXPLORER}/transactions/${ammcreate_result.result.hash}">AMM created</a>:</p>
<pre><code>${pretty_print(ammcreate_result)}</code></pre>`)
complete_step("Create AMM")
} else {
console.error(ammcreate_result)
show_error(block, `Error sending transaction: ${ammcreate_result.result.meta.TransactionResult}`)
}
block.find(".loader").hide()
})
$("#check-amm-info").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const foo_issuer_address = $("#issuer-address").text()
block.find(".output-area").html("")
block.find(".loader").show()
try {
const amm_info = await api.request({
"command": "amm_info",
"asset": {
"currency": "TST",
"issuer": "rP9jPyP5kyvFRb6ZiRghAGw5u8SGAmU4bd"
},
"asset2": {
"currency": "FOO",
"issuer": foo_issuer_address
},
"ledger_index": "validated"
})
show_log(block, `<p><strong>AMM Info:</strong><pre><code>${pretty_print(amm_info)}</code></pre>`)
const lp_token = amm_info.result.amm.lp_token
show_log(block, `<p>The AMM account <strong>${lp_token.issuer}</strong> has <strong>${lp_token.value}</strong> total
LP tokens outstanding, and uses the currency code <code>${lp_token.currency}</code>.</p>`)
const amount = amm_info.result.amm.amount
const amount2 = amm_info.result.amm.amount2
show_log(block, `<p>In its pool, the AMM holds <strong>${amount.value} ${amount.currency}.${amount.issuer}</strong>
and <strong>${amount2.value} ${amount2.currency}.${amount2.issuer}</strong></p>`)
complete_step("Check AMM info")
} catch(err) {
show_error(block, err)
}
block.find(".loader").hide()
})
$("#check-trust-lines").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address()
if (!address) {return}
block.find(".output-area").html("")
block.find(".loader").show()
try {
const account_lines = await api.request({
"command": "account_lines",
"account": address,
"ledger_index": "validated"
})
show_log(block, `<p><strong>Trust lines:</strong><pre><code>${pretty_print(account_lines)}</code></pre>`)
complete_step("Check trust lines")
} catch(err) {
show_error(block, err)
}
block.find(".loader").hide()
})
})

View File

@@ -0,0 +1,59 @@
// 1. Generate
// 2. Connect
// The code for these steps is handled by interactive-tutorial.js
$(document).ready(() => {
// 3. Send AccountSet --------------------------------------------------------
$("#send-accountset").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
try {
await generic_full_send(event, {
"TransactionType": "AccountSet",
"Account": address,
"SetFlag": xrpl.AccountSetAsfFlags.asfNoFreeze
})
complete_step("Send AccountSet")
} catch(err) {
block.find(".loader").hide()
show_error(block, err)
}
})
// 4. Wait for Validation: handled by interactive-tutorial.js and by the
// generic full send in the previous step. -----------------------------------
// 5. Confirm Account Settings -----------------------------------------------
$("#confirm-settings").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
block.find(".output-area").html("")
block.find(".loader").show()
const account_info = await api.request({
"command": "account_info",
"account": address,
"ledger_index": "validated"
})
console.log(account_info)
const flags = xrpl.parseAccountRootFlags(account_info.result.account_data.Flags)
block.find(".loader").hide()
block.find(".output-area").append(
`<p>Got settings for address ${address}:</p>
<pre><code>${pretty_print(flags)}</code></pre>`)
if (flags.lsfNoFreeze) {
block.find(".output-area").append(`<p><i class="fa fa-check-circle"></i>
No Freeze flag is enabled.</p>`)
} else {
block.find(".output-area").append(`<p><i class="fa fa-times-circle"></i>
No Freeze flag is DISABLED.</p>`)
}
complete_step("Confirm Settings")
})
})

View File

@@ -0,0 +1,108 @@
// 1. Generate
// 2. Connect
// The code for these steps is handled by interactive-tutorial.js
$(document).ready(() => {
// 3. Send AccountSet to Start the Freeze ------------------------------------
// also 6. Send AccountSet to End the Freeze.
$(".send-accountset").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
let astx = {
"TransactionType": "AccountSet",
"Account": address
}
let step_name
if ($(event.target).data("action") === "start_freeze") {
astx["SetFlag"] = xrpl.AccountSetAsfFlags.asfGlobalFreeze
step_name = "Send AccountSet (Start Freeze)"
} else if ($(event.target).data("action") === "end_freeze") {
astx["ClearFlag"] = xrpl.AccountSetAsfFlags.asfGlobalFreeze
step_name = "Send AccountSet (End Freeze)"
} else {
show_error(block, "There was an error with this tutorial: the button clicked must have data-action defined.")
}
try {
block.find(".loader").show()
await generic_full_send(event, astx)
complete_step(step_name)
} catch(err) {
block.find(".loader").hide()
show_error(block, err)
}
})
// 4. Wait for Validation: handled by interactive-tutorial.js and by the
// generic full send in the previous step. -----------------------------------
// 5. Confirm Account Settings -----------------------------------------------
$("#confirm-settings").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
block.find(".output-area").html("")
block.find(".loader").show()
const account_info = await api.request({
"command": "account_info",
"account": address,
"ledger_index": "validated"
})
console.log(account_info)
const flags = xrpl.parseAccountRootFlags(account_info.result.account_data.Flags)
block.find(".loader").hide()
block.find(".output-area").append(
`<p>Got settings for address ${address}:</p>
<pre><code>${pretty_print(flags)}</code></pre>`)
if (flags.lsfGlobalFreeze) {
block.find(".output-area").append(`<p><i class="fa fa-check-circle"></i>
Global Freeze flag is enabled.</p>`)
} else {
block.find(".output-area").append(`<p><i class="fa fa-times-circle"></i>
Global Freeze flag is DISABLED.</p>`)
}
complete_step("Confirm Settings")
})
// 6. Send AccountSet to End the Freeze: same handler as step 3.
// 7. Wait for Validation: handled by generic full send as before.
// 8. Confirm Account Settings (Freeze Ended)
$("#confirm-settings-end").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
block.find(".output-area").html("")
block.find(".loader").show()
const account_info = await api.request({
"command": "account_info",
"account": address,
"ledger_index": "validated"
})
console.log(account_info)
const flags = xrpl.parseAccountRootFlags(account_info.result.account_data.Flags)
block.find(".loader").hide()
block.find(".output-area").append(
`<p>Got settings for address ${address}:</p>
<pre><code>${pretty_print(flags)}</code></pre>`)
if (flags.lsfGlobalFreeze) {
block.find(".output-area").append(`<p><i class="fa fa-times-circle"></i>
Global Freeze is ENABLED (still active).</p>`)
} else {
block.find(".output-area").append(`<p><i class="fa fa-check-circle"></i>
Global Freeze Tag is disabled.</p>`)
}
complete_step("Confirm Settings")
})
})

View File

@@ -0,0 +1,177 @@
// 1. Generate
// 2. Connect
// The code for these steps is handled by interactive-tutorial.js
const CURRENCY_TO_FREEZE = "FOO"
// Helper to get a Wallet instance for the peer wallet
function get_peer_wallet(event) {
let peer_seed
try {
peer_seed = $("#peer-seed").val()
} catch(e) {
show_error(block, err)
return
}
if (!peer_seed) {
const block = $(event.target).closest(".interactive-block")
if (!block.length) {return}
show_error(block, "Couldn't get information on the peer account. Check that the previous steps were completed successfully.")
return
}
return xrpl.Wallet.fromSeed(peer_seed)
}
let trust_line_setup_done = false
window.after_connect = window.after_connect || [];
window.after_connect.push(async () => {
// 2.5. One-time setup on connect to create an incoming trust line after
// the "api" instance has been created by the connect handler.
if (trust_line_setup_done) {return} // Don't repeat if we disconnect/reconnect
console.log("Setting up an incoming trust line so our test address has something to freeze...")
$("#trust-line-setup-loader").show()
const address = get_address()
const peer = (await api.fundWallet()).wallet
const tx_json = {
"TransactionType": "TrustSet",
"Account": peer.address,
"LimitAmount": {
"currency": CURRENCY_TO_FREEZE,
"issuer": address,
"value": "123456.789" // arbitrary limit
}
}
try {
const submitted = await api.submitAndWait(tx_json, {wallet: peer})
console.log("Set up incoming trust line result:", submitted)
} catch(e) {
const block = $("#trust-line-setup-loader").closest(".interactive-block")
show_err(block, e)
$("#trust-line-setup-loader").hide()
return
}
$("#trust-line-setup-loader").hide()
$("#look-up-trust-lines").prop("disabled", false).prop("title", "")
$("#peer-seed").val(peer.seed)
trust_line_setup_done = true
})
$(document).ready(() => {
// 3. Choose Trust Line ------------------------------------------------------
$("#look-up-trust-lines").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
block.find(".output-area").html("")
const address = get_address(event)
if (!address) {return}
const peer = get_peer_wallet(event)
if (!peer) {return}
block.find(".loader-looking").show()
let account_lines
try {
account_lines = await api.request({
"command": "account_lines",
"account": address,
"peer": peer.address,
"ledger_index": "validated"
})
} catch(err) {
show_error(block, err)
}
block.find(".loader-looking").hide()
block.find(".output-area").append(
`<p>Found trust line(s) between ${address} and ${peer.address}:</p>
<pre><code>${pretty_print(account_lines.result.lines)}</code></pre>
<p>Choosing ${CURRENCY_TO_FREEZE} trust line.</p>`)
complete_step("Choose Trust Line")
})
// 4. Send TrustSet to Freeze ------------------------------------------------
// also 7. Send TrustSet to End the Freeze
$(".send-trustset").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
const peer = get_peer_wallet(event)
if (!peer) {return}
let tstx = {
"TransactionType": "TrustSet",
"Account": address,
"LimitAmount": {
"currency": CURRENCY_TO_FREEZE,
"issuer": peer.address,
"value": "0"
}
}
let step_name
if ($(event.target).data("action") === "start_freeze") {
tstx["Flags"] = xrpl.TrustSetFlags.tfSetFreeze
step_name = "Send TrustSet to Freeze"
} else if ($(event.target).data("action") === "end_freeze") {
tstx["Flags"] = xrpl.TrustSetFlags.tfClearFreeze
step_name = "Send TrustSet to End Freeze"
} else {
show_error(block, "There was an error with this tutorial: the button clicked must have data-action defined.")
}
try {
await generic_full_send(event, tstx)
complete_step(step_name)
} catch(err) {
block.find(".loader").hide()
show_error(block, err)
}
})
// 5. Wait for Validation: handled by interactive-tutorial.js and by the
// generic full send in the previous step. -----------------------------------
// 6. Check Trust Line Freeze Status
$("#confirm-settings").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
const peer = get_peer_wallet(event)
if (!peer) {return}
block.find(".output-area").html("")
block.find(".loader").show()
const account_lines = await api.request({
"command": "account_lines",
"account": address,
"peer": peer.address,
"ledger_index": "validated"
})
console.log(account_lines)
block.find(".loader").hide()
const trustlines = account_lines.result.lines
for (let i = 0; i < trustlines.length; i++) {
if(trustlines[i].currency === CURRENCY_TO_FREEZE) {
const line = trustlines[i]
block.find(".output-area").append(
`<p>Status of ${CURRENCY_TO_FREEZE} line between ${address} and ${peer.address}:</p>
<pre><code>${pretty_print(line)}</code></pre>`)
if (line.freeze === true) {
block.find(".output-area").append(`<p><i class="fa fa-check-circle"></i>
Line is frozen.`)
} else {
block.find(".output-area").append(`<p><i class="fa fa-times-circle"></i>
Line is NOT FROZEN.</p>`)
}
complete_step("Check Freeze Status")
return
}
}
show_error(block, `Couldn't find a ${CURRENCY_TO_FREEZE} line between ${address} and ${peer.address}`)
})
// 7. Send TrustSet to End the Freeze: same handler as step 4
})

View File

@@ -0,0 +1,337 @@
// Variant setup for generate creds button from interactive-tutorial.js.
// This version generates two sets of creds, one for the issuer and one for
// the hot wallet / receiver
const EXAMPLE_COLD_ADDR = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"
const EXAMPLE_COLD_SECRET = "sIss█████████████████████████"
function setup_2x_generate_step() {
$("#generate-2x-creds-button").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
block.find(".output-area").html("")
block.find(".loader").show()
// Get faucet URL (Testnet/Devnet/etc.)
const faucet_url = $("#generate-2x-creds-button").data("fauceturl")
try {
// destination not defined - API will create account.
const data = await call_faucet(faucet_url,undefined, event)
const data2 = await call_faucet(faucet_url, undefined, event)
block.find(".loader").hide()
block.find(".output-area").html(`<div class="row">
<div class="col-xl-6 p-3">
<div><strong>${tl("Cold Address:")}</strong>
<span id="cold-use-address">${data.account.address}</span></div>
<div><strong>${tl("Cold Secret:")}</strong>
<span id="cold-use-secret">${data.account.secret}</span></div>
<strong>${tl("XRP Balance:")}</strong>
${Number(data.balance).toLocaleString(current_locale)} XRP
</div>
<div class="col-xl-6 p-3">
<div><strong>${tl("Hot Address:")}</strong>
<span id="hot-use-address">${data2.account.address}</span></div>
<div><strong>${tl("Hot Secret:")}</strong>
<span id="hot-use-secret">${data2.account.secret}</span></div>
<strong>${tl("XRP Balance:")}</strong>
${Number(data2.balance).toLocaleString(current_locale)} XRP
</div>
</div>`)
// TODO: Automatically populate all examples in the page with the
// generated credentials...
// $("code span:contains('"+EXAMPLE_ADDR+"')").each( function() {
// let eltext = $(this).text()
// $(this).text( eltext.replace(EXAMPLE_ADDR, data.account.address) )
// })
// $("code span:contains('"+EXAMPLE_SECRET+"')").each( function() {
// let eltext = $(this).text()
// $(this).text( eltext.replace(EXAMPLE_SECRET, data.account.secret) )
// })
//
// block.find(".output-area").append(`<p>${tl("Populated this page's examples with these credentials.")}</p>`)
complete_step("Generate")
} catch(err) {
block.find(".loader").hide()
block.find(".output-area").html(
`<p class="devportal-callout warning"><strong>${tl("Error:")}</strong>
${tl("There was an error connecting to the Faucet. Please try again.")}
</p>`)
return
}
})
}
function get_wallet_2(event, which_one) {
// which_one should be either "cold" or "hot" (case-sensitive)
const secret = $(`#${which_one}-use-secret`).text()
if (!secret) {
const block = $(event.target).closest(".interactive-block")
if (!block.length) {return}
show_error(block, tl("Couldn't get a valid address/secret value. Check that the previous steps were completed successfully."))
}
return xrpl.Wallet.fromSeed(secret)
}
// Get the hexadecimal ASCII representation of a domain name string.
// Note: if the provided string isn't compatible with 7-bit ASCII, this won't
// work. So if you want to use an IDN, you'd need to convert to punycode first.
function domain_to_hex(s) {
result = ""
for (let i=0; i<s.length; i++) {
result += s.charCodeAt(i).toString(16)
}
return result.toUpperCase()
}
$(document).ready(() => {
setup_2x_generate_step()
$("#cold-domain-text").keyup( (event) => {
$("#cold-domain-hex").text(domain_to_hex($("#cold-domain-text").val()))
})
$("#hot-domain-text").keyup( (event) => {
$("#hot-domain-hex").text(domain_to_hex($("#hot-domain-text").val()))
})
function update_currency_code(event) {
let currency_code
if ($("#use-std-code").prop("checked")) {
const std_code = $("#currency-code-std")
currency_code = std_code.val().trim()
// std_code.prop("disabled", false).removeClass("disabled")
// $("#currency-code-hex").prop("disabled", true).addClass("disabled")
} else {
const hex_code = $("#currency-code-hex")
currency_code = hex_code.val().trim()
// hex_code.prop("disabled", false).removeClass("disabled")
// $("#currency-code-std").prop("disabled", true).addClass("disabled")
}
$("#send-currency-code").text(currency_code)
}
$("#currency-code-std").keyup(update_currency_code)
$("#currency-code-hex").keyup(update_currency_code)
$("#use-std-code").change(update_currency_code)
$("#use-hex-code").change(update_currency_code)
// run once on load because some browsers pre-fill values from previous
// pageviews.
update_currency_code()
// Configure Issuer Settings handler -----------------------------------------
$("#config-issuer-button").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
block.find(".output-area").empty()
const cold_wallet = get_wallet_2(event, "cold")
let flags = 0
if ($("#cold-require-dest").prop("checked")) {
flags |= xrpl.AccountSetTfFlags.tfRequireDestTag
}
if ($("#cold-disallow-xrp").prop("checked")) {
flags |= xrpl.AccountSetTfFlags.tfDisallowXRP
}
const tick_size = parseInt($("#cold-tick-size").val(), 10)
if (Number.isNaN(tick_size) || tick_size < 0 || tick_size > 15) {
show_error(block, "TickSize must be an integer from 0 to 15.")
return
}
// Convert transfer fee % to transferrate integer (e.g. 0.5% fee = 1005000000)
const transfer_fee = parseFloat($("#cold-transfer-fee").val())
let transfer_rate = (transfer_fee * 10000000) + 1000000000
if (transfer_rate == 1000000000) {
transfer_rate = 0
}
const domain = $("#cold-domain-hex").text().trim()
block.find(".loader").show()
try {
const cold_settings_tx = {
"TransactionType": "AccountSet",
"Account": cold_wallet.address,
"TransferRate": transfer_rate,
"TickSize": tick_size,
"SetFlag": xrpl.AccountSetAsfFlags.asfDefaultRipple,
"Domain": domain,
"Flags": flags
}
await generic_full_send(event, cold_settings_tx, cold_wallet)
complete_step("Configure Issuer")
} catch(err) {
block.find(".loader").hide()
show_error(block, `An error occurred with the transaction: ${err}`)
}
})
// Configure Hot Address Settings handler ------------------------------------
$("#config-hot-address-button").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
block.find(".output-area").empty()
const hot_wallet = get_wallet_2(event, "hot")
let flags = 0
if ($("#hot-require-dest").prop("checked")) {
flags |= xrpl.AccountSetTfFlags.tfRequireDestTag
}
if ($("#hot-disallow-xrp").prop("checked")) {
flags |= xrpl.AccountSetTfFlags.tfDisallowXRP
}
const domain = $("#hot-domain-hex").text().trim()
block.find(".loader").show()
try {
const hot_settings_tx = {
"TransactionType": "AccountSet",
"Account": hot_wallet.address,
// Require Auth so we can't accidentally issue from the hot address
"SetFlag": xrpl.AccountSetAsfFlags.asfRequireAuth,
"Domain": domain,
"Flags": flags
}
await generic_full_send(event, hot_settings_tx, hot_wallet)
complete_step("Configure Hot Address")
} catch(err) {
block.find(".loader").hide()
show_error(block, `An error occurred with the transaction: ${err}`)
}
})
// Create Trust Line handler -------------------------------------------------
$("#create-trust-line-button").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
block.find(".output-area").empty()
const cold_address = get_wallet_2(event, "cold").address
const hot_wallet = get_wallet_2(event, "hot")
let currency_code
if ($("#use-std-code").prop("checked")) {
currency_code = $("#currency-code-std").val().trim()
if (!currency_code.match(/[A-Za-z0-9?!@#$%*(){}|\x26\x3c\x3e]{3}/)) {
show_error(block, "<a href='currency-formats.html#standard-currency-codes'>Standard currency code</a> must be 3 valid characters.")
}
} else {
currency_code = $("#currency-code-hex").val().trim()
if (!currency_code.match(/^[0-9A-Fa-f]{40}$/)) {
show_error(block, "<a href='currency-formats.html#nonstandard-currency-codes'>Nonstandard currency code</a> must be 40 hexadecimal characters.")
return
}
}
const limit = $("#trust-limit").val() // limit is a string
block.find(".loader").show()
try {
const trust_set_tx = {
"TransactionType": "TrustSet",
"Account": hot_wallet.address,
"LimitAmount": {
"currency": currency_code,
"issuer": cold_address,
"value": limit
}
}
await generic_full_send(event, trust_set_tx, hot_wallet)
complete_step("Make Trust Line")
} catch(err) {
block.find(".loader").hide()
show_error(block, `An error occurred with the transaction: ${err}`)
}
})
// Send Token handler --------------------------------------------------------
$("#send-token-button").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
block.find(".output-area").empty()
const hot_address = get_wallet_2(event, "hot").address
const cold_wallet = get_wallet_2(event, "cold")
const currency_code = $("#send-currency-code").text().trim()
const issue_quantity = $("#send-amount").val().trim()
const use_dest_tag = $("#use-dest-tag").prop("checked")
let dest_tag
if (use_dest_tag) {
dest_tag = parseInt($("#dest-tag").val(), 10)
if (Number.isNaN(dest_tag) || dest_tag < 0 || dest_tag > 4294967295) {
show_error(block, "Destination Tag must be a valid 32-bit integer.")
return
}
}
block.find(".loader").show()
try {
const send_token_tx = {
"TransactionType": "Payment",
"Account": cold_wallet.address,
"Amount": {
"currency": currency_code,
"value": issue_quantity,
"issuer": cold_wallet.address
},
"Destination": hot_address
}
if (use_dest_tag) {
send_token_tx["DestinationTag"] = dest_tag
}
await generic_full_send(event, send_token_tx, cold_wallet)
complete_step("Send Token")
} catch(err) {
block.find(".loader").hide()
show_error(block, `An error occurred with the transaction: ${err}`)
}
})
// Confirm Balances handler --------------------------------------------------
$("#confirm-balances-button").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
block.find(".output-area").empty()
const hot_address = get_wallet_2(event, "hot").address
const cold_address = get_wallet_2(event, "cold").address
block.find(".loader").show()
try {
const hot_balances = await api.request({
"command": "account_lines",
"account": hot_address,
"ledger_index": "validated"
})
block.find(".output-area").append(`
<p>Hot address (<a href="https://testnet.xrpl.org/accounts/${hot_address}">${hot_address}</a>) account_lines result:</p>
<pre><code>${pretty_print(hot_balances.result)}</code></pre>
`)
const cold_balances = await api.request({
"command": "gateway_balances",
"account": cold_address,
"ledger_index": "validated",
"hotwallet": [hot_address]
})
block.find(".output-area").append(`
<p>Issuer (<a href="https://testnet.xrpl.org/accounts/${cold_address}">${cold_address}</a>) gateway_balances result:</p>
<pre><code>${pretty_print(cold_balances.result)}</code></pre>
`)
block.find(".loader").hide()
complete_step("Confirm Balances")
} catch(err) {
block.find(".loader").hide()
show_error(block, `Error looking up balances: ${err}`)
}
})
})

View File

@@ -0,0 +1,128 @@
// 1. Generate
// 2. Connect
// The code for these steps is handled by interactive-tutorial.js
$(document).ready(() => {
// 3. Send AccountSet --------------------------------------------------------
$("#send-accountset").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
try {
await generic_full_send(event, {
"TransactionType": "AccountSet",
"Account": address,
"SetFlag": xrpl.AccountSetAsfFlags.asfRequireDest
})
complete_step("Send AccountSet")
} catch(err) {
block.find(".loader").hide()
show_error(block, err)
}
})
// 4. Wait for Validation: handled by interactive-tutorial.js and by the
// generic full send in the previous step. -----------------------------------
// 5. Confirm Account Settings -----------------------------------------------
$("#confirm-settings").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
block.find(".output-area").html("")
block.find(".loader").show()
let account_info = await api.request({
"command": "account_info",
"account": address,
"ledger_index": "validated"
})
console.log(account_info)
const flags = xrpl.parseAccountRootFlags(account_info.result.account_data.Flags)
block.find(".loader").hide()
block.find(".output-area").append(
`<pre><code>${pretty_print(account_info.result.account_data)}</code></pre>`)
if (flags.lsfRequireDestTag) {
block.find(".output-area").append(`<p><i class="fa fa-check-circle"></i>
Require Destination Tag is enabled.</p>`)
} else {
block.find(".output-area").append(`<p><i class="fa fa-times-circle"></i>
Require Destination Tag is DISABLED.</p>`)
}
complete_step("Confirm Settings")
})
// Send Test Payments --------------------------------------------------------
// Helper to get an address to send the test payments from. Save the values
// from the faucet in data attributes on the block so we don't have to get a
// new sending address every time.
async function get_test_sender(block) {
let test_sender_wallet
let seed = block.data("testSendSecret")
if (!seed) {
console.debug("First-time setup for test sender...")
test_sender_wallet = (await api.fundWallet()).wallet
block.data("testSendSecret", test_sender_wallet.seed)
// First time: Wait for our test sender to be fully funded, so we don't
// get the wrong starting sequence number.
while (true) {
try {
await api.request({
"command": "account_info",
"account": test_sender_wallet.address,
"ledger_index": "validated"
})
break
} catch(e) {
await new Promise(resolve => setTimeout(resolve, 1000))
}
}
} else {
test_sender_wallet = xrpl.Wallet.fromSeed(seed)
}
return test_sender_wallet
}
// Actual handler for the two buttons in the Send Test Payments block.
// Gets the destination tag (or lack thereof) from their data attributes.
$(".test-payment").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
block.find(".loader").show()
try {
const test_sender = await get_test_sender(block)
const tx_json = {
"TransactionType": "Payment",
"Account": test_sender.address,
"Amount": "3152021",
"Destination": address
}
const dt = $(event.target).data("dt")
if (dt) {
tx_json["DestinationTag"] = parseInt(dt)
}
const prepared = await api.autofill(tx_json)
const {tx_blob, hash} = test_sender.sign(prepared)
console.debug("Submitting test payment", prepared)
const prelim = await api.request({"command": "submit", tx_blob})
block.find(".loader").hide()
block.find(".output-area").append(`<p>${tx_json.TransactionType}
${prepared.Sequence} ${(dt?"WITH":"WITHOUT")} Dest. Tag:
<a href="https://testnet.xrpl.org/transactions/${hash}"
target="_blank">${prelim.result.engine_result}</a></p>`)
} catch(err) {
block.find(".loader").hide()
show_error(block, `An error occurred when sending the test payment: ${err}`)
}
})
})

View File

@@ -0,0 +1,102 @@
// Prerequisite: Generate
// 1. Connect
// The code for these steps is handled by interactive-tutorial.js
$(document).ready(() => {
// 2. Prepare Transaction ------------------------------------------------------
$("#prepare-button").click( async function(event) {
const block = $(event.target).closest(".interactive-block")
block.find(".output-area").html("")
const send_amount = $("#xrp-amount").val()
const sender = get_address(event)
if (!sender) {return}
const vli = await api.getLedgerIndex()
const prepared = await api.autofill({
"TransactionType": "Payment",
"Account": sender,
"Amount": xrpl.xrpToDrops(send_amount), // Same as "Amount": "22000000"
"Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
"LastLedgerSequence": vli+75 // gives ~5min, rather than the default ~1min
})
block.find(".output-area").append(
`<div><strong>Prepared transaction instructions:</strong>
<pre><code id='prepared-tx-json'>${pretty_print(prepared)}</code></pre>
</div>
<div><strong>Transaction cost:</strong> ${xrpl.dropsToXrp(prepared.Fee)} XRP</div>
<div><strong>Transaction expires after ledger:</strong>
${prepared.LastLedgerSequence}</div>`)
complete_step("Prepare")
})
// 3. Sign the transaction -----------------------------------------------------
$("#sign-button").click( function(event) {
const block = $(event.target).closest(".interactive-block")
block.find(".output-area").html("")
const preparedTxJSON = JSON.parse($("#prepared-tx-json").text())
const wallet = get_wallet(event)
if (!wallet) {return}
const {tx_blob, hash} = wallet.sign(preparedTxJSON)
block.find(".output-area").html(
`<div><strong>Signed Transaction blob:</strong>
<code id='signed-tx-blob' style='overflow-wrap: anywhere; word-wrap: anywhere'
>${tx_blob}</code></div>
<div><strong>Identifying hash:</strong> <span id='signed-tx-hash'
>${hash}</span></div>`
)
complete_step("Sign")
})
// 4. Submit the signed transaction --------------------------------------------
$("#submit-button").click( submit_handler )
// 5. Wait for Validation: handled by interactive-tutorial.js and by the
// generic submit handler in the previous step. --------------------------------
// 6. Check transaction status -------------------------------------------------
$("#get-tx-button").click( async function(event) {
const block = $(event.target).closest(".interactive-block")
// Wipe previous output
block.find(".output-area").html("")
const txID = $("#signed-tx-hash").text()
const earliestLedgerVersion = parseInt(
$("#interactive-wait .earliest-ledger-version").text(), 10)
const lastLedgerSequence = parseInt(
$("#interactive-wait .lastledgersequence").text(), 10)
try {
const tx = await api.request({
command: "tx",
transaction: txID,
min_ledger: earliestLedgerVersion,
max_ledger: lastLedgerSequence
})
block.find(".output-area").html(
`<div><strong>Transaction result code:</strong>
${tx.result.meta.TransactionResult} (${tx.result.validated ? "validated": "pending"})</div>
<div><strong>Balance changes:</strong>
<pre><code>${pretty_print(xrpl.getBalanceChanges(tx.result.meta))}</code></pre>
</div>`
)
complete_step("Check")
} catch(error) {
show_error(block, "Couldn't get transaction outcome:" + error)
}
})
})

View File

@@ -0,0 +1,288 @@
// Source for interactive examples in the "Trade in the Decentralized Exchange"
// tutorial.
// Get Credentials, Connect steps handled by the snippet
$(document).ready(() => {
// Look Up Offers --------------------------------------------------------------
function update_exchange_rate(event) {
const tpa = $("#taker-pays-amount-1").val()
const tga = $("#taker-gets-amount-1").val()
const exchange_rate = BigNumber(tga) / BigNumber(tpa)
$("#exchange-rate-1").val(`${exchange_rate}`)
}
$("#taker-pays-amount-1").change(update_exchange_rate)
$("#taker-gets-amount-1").change(update_exchange_rate)
$("#look-up-offers").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
block.find(".output-area").html("")
const address = get_address(event)
if (!address) {return}
const we_want = {
"currency": $("#taker-pays-currency-1").val(),
"issuer": $("#taker-pays-issuer-1").val(),
"value": $("#taker-pays-amount-1").val()
}
const we_spend = {
"currency": "XRP",
"value": xrpl.xrpToDrops($("#taker-gets-amount-1").val())
}
const proposed_quality = BigNumber(we_spend.value) / BigNumber(we_want.value)
let orderbook_resp
try {
orderbook_resp = await api.request({
"command": "book_offers",
"taker": address,
"ledger_index": "current",
"taker_gets": we_want,
"taker_pays": we_spend
})
block.find(".output-area").append(
`<pre><code>${pretty_print(orderbook_resp.result)}`)
} catch(err) {
show_error(block, err)
block.find(".loader").hide()
return
}
// Estimate whether a proposed Offer would execute immediately, and...
// If so, how much of it? (Partial execution is possible)
// If not, how much liquidity is above it? (How deep in the order book would
// other Offers have to go before ours would get taken?)
// Note: these estimates can be thrown off by rounding if the token issuer
// uses a TickSize setting other than the default (15). In that case, you
// can increase the TakerGets amount of your final offer just a little bit to
// compensate.
const offers = orderbook_resp.result.offers
const want_amt = BigNumber(we_want.value)
let running_total = BigNumber(0)
if (!offers) {
block.find(".output-area").append(`<p>No Offers in the matching book.
Offer probably won't execute immediately.</p>`)
} else {
for (const o of offers) {
if (o.quality <= proposed_quality) {
block.find(".output-area").append(
`<p>Matching Offer found, funded with
${o.owner_funds} ${we_want.currency}</p>`)
running_total = running_total.plus(BigNumber(o.owner_funds))
if (running_total >= want_amt) {
block.find(".output-area").append("<p>Full Offer will probably fill.</p>")
break
}
} else {
// Offers are in ascending quality order, so no others after this
// will match, either
block.find(".output-area").append(`<p>Remaining orders too expensive.</p>`)
break
}
}
block.find(".output-area").append(`<p>Total matched:
${Math.min(running_total, want_amt)} ${we_want.currency}</p>`)
if (running_total > 0 && running_total < want_amt) {
block.find(".output-area").append(`<p>Remaining
${want_amt - running_total} ${we_want.currency} would probably be
placed on top of the order book.</p>`)
}
}
if (running_total == 0) {
// If the Offer would be partly filled, then the rest would be placed
// at the top of the order book. If no part is filled, then there might be
// other Offers going the same direction as ours already on the books with
// an equal or better rate. This code counts how much liquidity is likely to
// be above ours.
// Unlike above, this looks for Offers going the same direction as the
// proposed Offer, so TakerGets and TakerPays are reversed from the previous
// book_offers request.
let orderbook2_resp
try {
orderbook2_resp = await api.request({
"command": "book_offers",
"taker": address,
"ledger_index": "current",
"taker_gets": we_spend,
"taker_pays": we_want
})
block.find(".output-area").append(
`<pre><code>${pretty_print(orderbook2_resp.result)}`)
} catch(err) {
show_error(block, err)
block.find(".loader").hide()
return
}
// Since TakerGets/TakerPays are reversed, the quality is the inverse.
// You could also calculate this as 1/proposed_quality.
const offered_quality = BigNumber(we_want.value) / BigNumber(we_spend.value)
const offers2 = orderbook2_resp.result.offers
let tally_currency = we_spend.currency
if (tally_currency == "XRP") { tally_currency = "drops of XRP" }
let running_total2 = BigNumber(0)
if (!offers2) {
block.find(".output-area").append(`<p>No similar Offers in the book.
Ours would be the first.</p>`)
} else {
for (const o of offers2) {
if (o.quality <= offered_quality) {
block.find(".output-area").append(`<p>Existing offer found, funded
with ${o.owner_funds} ${tally_currency}</p>`)
running_total2 = running_total2.plus(BigNumber(o.owner_funds))
} else {
block.find(".output-area").append(`<p>Remaining orders are below where
ours would be placed.</p>`)
break
}
}
block.find(".output-area").append(`<p>Our Offer would be placed below at
least ${running_total2} ${tally_currency}</p>`)
if (running_total > 0 && running_total < want_amt) {
block.find(".output-area").append(`<p>Remaining
${want_amt - running_total} ${tally_currency} will probably be
placed on top of the order book.</p>`)
}
}
}
block.find(".loader").hide()
complete_step("Look Up Offers")
})
// Send OfferCreate Transaction ------------------------------------------------
$("#send-offercreate").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
block.find(".output-area").html("")
const we_want = {
"currency": $("#taker-pays-currency-1").val(),
"issuer": $("#taker-pays-issuer-1").val(),
"value": $("#taker-pays-amount-1").val()
}
const we_spend = {
"currency": "XRP",
"value": xrpl.xrpToDrops($("#taker-gets-amount-1").val())
}
const tx = {
"TransactionType": "OfferCreate",
"Account": address,
"TakerPays": {
"currency": $("#taker-pays-currency-1").val(),
"issuer": $("#taker-pays-issuer-1").val(),
"value": $("#taker-pays-amount-1").val()
},
"TakerGets": we_spend.value // since it's XRP
}
try {
await generic_full_send(event, tx)
complete_step("Send OfferCreate")
} catch(err) {
block.find(".loader").hide()
show_error(block, err)
}
})
// Wait for Validation: handled by interactive-tutorial.js and by the
// generic full send in the previous step. -------------------------------------
// Check Metadata --------------------------------------------------------------
$("#check-metadata").click(async (event) => {
const block = $(event.target).closest(".interactive-block")
block.find(".loader").show()
block.find(".output-area").html("")
const txid = $(`#interactive-wait`).find(".waiting-for-tx").text().trim()
let result
try {
result = await api.request({"method": "tx", "transaction": txid})
} catch(err) {
show_err(err)
block.find(".loader").hide()
return
}
const balance_changes = xrpl.getBalanceChanges(result.result.meta)
block.find(".output-area").append(`<p>Total balance changes:</p>
<pre><code>${pretty_print(balance_changes)}</code></pre>`)
// 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")) {
if (affnode.CreatedNode.LedgerEntryType == "RippleState") {
block.find(".output-area").append("<p>Created a trust line.</p>")
} else if (affnode.CreatedNode.LedgerEntryType == "Offer") {
const offer = affnode.CreatedNode.NewFields
block.find(".output-area").append(`<p>Created an Offer owned by ${offer.Account} with
TakerGets=${amt_str(offer.TakerGets)} and
TakerPays=${amt_str(offer.TakerPays)}.</p>`)
}
}
}
block.find(".output-area").append(`<p>Modified or removed ${offers_affected} matching Offer(s).</p>`)
block.find(".loader").hide()
complete_step("Check Metadata")
})
// Check Balances and Offers
$("#check-balances-and-offers").click( async (event) => {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
block.find(".loader").show()
try {
block.find(".output-area").append("<p>Getting address balances as of validated ledger...</p>")
const balances = await api.request({
command: "account_lines",
account: address,
ledger_index: "validated"
// You could also use ledger_index: "current" to get pending data
})
block.find(".output-area").append(`<pre><code>${pretty_print(balances.result)}</code></pre>`)
block.find(".output-area").append(`<p>Getting outstanding Offers from ${address} as of validated ledger...</p>`)
const acct_offers = await api.request({
command: "account_offers",
account: address,
ledger_index: "validated"
})
block.find(".output-area").append(`<pre><code>${pretty_print(acct_offers.result)}</code></pre>`)
block.find(".loader").hide()
} catch(err) {
show_error(block, err)
block.find(".loader").hide()
}
})
}) // end of $(document).ready()

View File

@@ -0,0 +1,216 @@
// 1. Generate
// 2. Connect
// The code for these steps is handled by interactive-tutorial.js
$(document).ready(() => {
const LLS_OFFSET = 75 // Expire unconfirmed transactions after about ~5 min
// 3. Check Sequence Number
$("#check-sequence").click( async function(event) {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
// Wipe previous output
block.find(".output-area").html("")
block.find(".loader").show()
const account_info = await api.request({
"command": "account_info", "account": address
})
block.find(".loader").hide()
block.find(".output-area").append(
`<p>Current sequence:
<code id="current_sequence">${account_info.result.account_data.Sequence}</code>
</p>`)
complete_step("Check Sequence")
})
// 4. Prepare and Sign TicketCreate --------------------------------------------
$("#prepare-and-sign").click( async function(event) {
const block = $(event.target).closest(".interactive-block")
const wallet = get_wallet(event)
if (!wallet) {return}
let current_sequence
try {
current_sequence = parseInt($("#current_sequence").text())
} catch (e) {
current_sequence = null
}
// Wipe previous output
block.find(".output-area").html("")
if (!current_sequence) {
show_error(block,
`Couldn't get a valid sequence value. Check that the
previous steps were completed successfully.`)
return;
}
const vli = await api.getLedgerIndex()
const prepared = await api.autofill({
"TransactionType": "TicketCreate",
"Account": wallet.address,
"TicketCount": 10,
"Sequence": current_sequence,
"LastLedgerSequence": vli+LLS_OFFSET
})
block.find(".output-area").append(
`<p>Prepared transaction:</p>
<pre><code>${pretty_print(prepared)}</code></pre>`)
const {tx_blob, hash} = wallet.sign(prepared)
block.find(".output-area").append(
`<p>Transaction hash: <code id="tx_id">${hash}</code></p>`)
block.find(".output-area").append(
`<p>Signed blob:</p><pre class="tx-blob"><code id="tx_blob">${tx_blob}</code></pre>`)
complete_step("Prepare & Sign")
})
// 5. Submit TicketCreate ------------------------------------------------------
$("#ticketcreate-submit").click( submit_handler )
// 6. Wait for Validation: handled by interactive-tutorial.js and by the
// generic submit handler in the previous step. --------------------------------
// Intermission ----------------------------------------------------------------
async function intermission_submit(event, tx_json) {
const block = $(event.target).closest(".interactive-block")
const wallet = get_wallet(event)
if (!wallet) {return}
const prepared = await api.autofill(tx_json)
const {tx_blob, hash} = wallet.sign(prepared)
const prelim = await api.request({
"command": "submit",
"tx_blob": tx_blob
})
block.find(".output-area").append(`<p>${prepared.TransactionType}
${prepared.Sequence}:
<a href="https://devnet.xrpl.org/transactions/${hash}"
target="_blank">${prelim.result.engine_result}</a></p>`)
}
$("#intermission-payment").click( async function(event) {
const address = get_address(event)
if (!address) {return}
intermission_submit(event, {
"TransactionType": "Payment",
"Account": address,
"Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe", // Testnet Faucet
"Amount": xrpl.xrpToDrops("201")
})
complete_step("Intermission")
})
$("#intermission-escrowcreate").click( async function(event) {
const address = get_address(event)
if (!address) {return}
intermission_submit(event, {
"TransactionType": "EscrowCreate",
"Account": address,
"Destination": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", // Genesis acct
"Amount": xrpl.xrpToDrops("0.13"), // Arbitrary amount
"FinishAfter": xrpl.isoTimeToRippleTime(Date()) + 30 // 30 seconds from now
})
complete_step("Intermission")
})
$("#intermission-accountset").click( async function(event) {
const address = get_address(event)
if (!address) {return}
intermission_submit(event, {
"TransactionType": "AccountSet",
"Account": address
})
complete_step("Intermission")
})
// 7. Check Available Tickets --------------------------------------------------
$("#check-tickets").click( async function(event) {
const block = $(event.target).closest(".interactive-block")
const address = get_address(event)
if (!address) {return}
// Wipe previous output
block.find(".output-area").html("")
block.find(".loader").show()
let response = await api.request({
"command": "account_objects",
"account": address,
"type": "ticket"
})
block.find(".output-area").html(
`<pre><code>${pretty_print(response)}</code></pre>`)
block.find(".loader").hide()
// Reset the next step's form & add these tickets
$("#ticket-selector .form-area").html("")
response.result.account_objects.forEach((ticket, i) => {
$("#ticket-selector .form-area").append(
`<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="ticket${i}"
name="ticket-radio-set" value="${ticket.TicketSequence}">
<label class="form-check-label"
for="ticket${i}">${ticket.TicketSequence}</label></div>`)
})
complete_step("Check Tickets")
})
// 8. Prepare Ticketed Transaction ---------------------------------------------
$("#prepare-ticketed-tx").click(async function(event) {
const wallet = get_wallet(event)
if (!wallet) {return}
const block = $(event.target).closest(".interactive-block")
block.find(".output-area").html("")
const use_ticket = parseInt($('input[name="ticket-radio-set"]:checked').val())
if (!use_ticket) {
show_error(block, "You must choose a ticket first.")
return
}
const vli = await api.getLedgerIndex()
const prepared_t = await api.autofill({
"TransactionType": "AccountSet",
"Account": wallet.address,
"TicketSequence": use_ticket,
"Sequence": 0,
"LastLedgerSequence": vli+LLS_OFFSET
})
block.find(".output-area").append(
`<p>Prepared transaction:</p>
<pre><code>${pretty_print(prepared_t)}</code></pre>`)
const {tx_blob, hash} = wallet.sign(prepared_t)
block.find(".output-area").append(
`<p>Transaction hash: <code id="tx_id_t">${hash}</code></p>`)
block.find(".output-area").append(
`<pre style="visibility: none">
<code id="tx_blob_t">${tx_blob}</code></pre>`)
// Update breadcrumbs & activate next step
complete_step("Prepare Ticketed Tx")
})
// 9. Submit Ticketed Transaction ----------------------------------------------
$("#ticketedtx-submit").click( submit_handler )
// 10. Wait for Validation (Again): handled by interactive-tutorial.js and by
// the generic submit handler in the previous step. --------------------------------
})