mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-24 13:45:49 +00:00
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:
273
content/static/js/tutorials/create-amm.js
Normal file
273
content/static/js/tutorials/create-amm.js
Normal 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()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
59
content/static/js/tutorials/enable-no-freeze.js
Normal file
59
content/static/js/tutorials/enable-no-freeze.js
Normal 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")
|
||||
})
|
||||
|
||||
})
|
||||
108
content/static/js/tutorials/enact-global-freeze.js
Normal file
108
content/static/js/tutorials/enact-global-freeze.js
Normal 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")
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
177
content/static/js/tutorials/freeze-individual-line.js
Normal file
177
content/static/js/tutorials/freeze-individual-line.js
Normal 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
|
||||
|
||||
})
|
||||
337
content/static/js/tutorials/issue-a-token.js
Normal file
337
content/static/js/tutorials/issue-a-token.js
Normal 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}`)
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
128
content/static/js/tutorials/require-destination-tags.js
Normal file
128
content/static/js/tutorials/require-destination-tags.js
Normal 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}`)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
102
content/static/js/tutorials/send-xrp.js
Normal file
102
content/static/js/tutorials/send-xrp.js
Normal 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)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
288
content/static/js/tutorials/trade-in-the-dex.js
Normal file
288
content/static/js/tutorials/trade-in-the-dex.js
Normal 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()
|
||||
216
content/static/js/tutorials/use-tickets.js
Normal file
216
content/static/js/tutorials/use-tickets.js
Normal 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. --------------------------------
|
||||
|
||||
})
|
||||
Reference in New Issue
Block a user