mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-19 19:25:51 +00:00
Issue a token: interactive & node.js compatible
- Make sample code node.js compatible (need to uncomment a few lines) - Wire up interactive tutorial bits to actually run the relevant code - Edit instructions slightly, add notes on Require Destination Tags
This commit is contained in:
@@ -511,6 +511,7 @@ async function do_submit(block, submit_opts, wait_step_name) {
|
||||
if (wait_step_name){
|
||||
activate_wait_step(wait_step_name, prelim_result)
|
||||
}
|
||||
return prelim_result
|
||||
} catch(error) {
|
||||
block.find(".loader").hide()
|
||||
show_error(block, error)
|
||||
|
||||
@@ -63,8 +63,295 @@ function setup_2x_generate_step() {
|
||||
})
|
||||
}
|
||||
|
||||
function get_address_2(event, which_one) {
|
||||
// which_one should be either "cold" or "hot" (case-sensitive)
|
||||
const address = $(`#${which_one}-use-address`).text()
|
||||
if (!address) {
|
||||
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 address
|
||||
}
|
||||
function get_secret_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."))
|
||||
}
|
||||
// TODO: check for *both* example secrets
|
||||
// if (secret == EXAMPLE_SECRET) {
|
||||
// const block = $(event.target).closest(".interactive-block")
|
||||
// if (!block.length) {return}
|
||||
// show_error(block, tl("Can't use the example secret here. Check that the previous steps were completed successfully."))
|
||||
// }
|
||||
return 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")) {
|
||||
currency_code = $("#currency-code-std").val().trim()
|
||||
} else {
|
||||
currency_code = $("#currency-code-hex").val().trim()
|
||||
}
|
||||
$("#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)
|
||||
|
||||
// Configure Issuer Settings handler -----------------------------------------
|
||||
$("#config-issuer-button").click( async (event) => {
|
||||
const block = $(event.target).closest(".interactive-block")
|
||||
block.find(".output-area").empty()
|
||||
const cold_address = get_address_2(event, "cold")
|
||||
const cold_secret = get_secret_2(event, "cold")
|
||||
|
||||
let flags = 0
|
||||
if ($("#cold-require-dest").prop("checked")) {
|
||||
flags |= api.txFlags.AccountSet.RequireDestTag
|
||||
}
|
||||
if ($("#cold-disallow-xrp").prop("checked")) {
|
||||
flags |= api.txFlags.AccountSet.DisallowXRP
|
||||
}
|
||||
|
||||
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_address,
|
||||
"TransferRate": transfer_rate,
|
||||
"TickSize": tick_size,
|
||||
"SetFlag": 8, // enable Default Ripple
|
||||
"Domain": domain,
|
||||
"Flags": flags
|
||||
}
|
||||
|
||||
const cst_prepared = await api.prepareTransaction(cold_settings_tx, {maxLedgerVersionOffset: 20})
|
||||
const cst_signed = api.sign(cst_prepared.txJSON, cold_secret)
|
||||
do_submit(block, {tx_blob: cst_signed.signedTransaction})
|
||||
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_address = get_address_2(event, "hot")
|
||||
const hot_secret = get_secret_2(event, "hot")
|
||||
|
||||
let flags = 0
|
||||
if ($("#hot-require-dest").prop("checked")) {
|
||||
flags |= api.txFlags.AccountSet.RequireDestTag
|
||||
}
|
||||
if ($("#hot-disallow-xrp").prop("checked")) {
|
||||
flags |= api.txFlags.AccountSet.DisallowXRP
|
||||
}
|
||||
|
||||
const domain = $("#hot-domain-hex").text().trim()
|
||||
|
||||
block.find(".loader").show()
|
||||
try {
|
||||
const hot_settings_tx = {
|
||||
"TransactionType": "AccountSet",
|
||||
"Account": hot_address,
|
||||
"SetFlag": 2, // enable Require Auth so we can't accidentally issue from
|
||||
// the hot address
|
||||
"Domain": domain,
|
||||
"Flags": flags
|
||||
}
|
||||
|
||||
const hst_prepared = await api.prepareTransaction(hot_settings_tx, {maxLedgerVersionOffset: 10})
|
||||
const hst_signed = api.sign(hst_prepared.txJSON, hot_secret)
|
||||
do_submit(block, {tx_blob: hst_signed.signedTransaction})
|
||||
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 hot_address = get_address_2(event, "hot")
|
||||
const cold_address = get_address_2(event, "cold")
|
||||
const hot_secret = get_secret_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_address,
|
||||
"LimitAmount": {
|
||||
"currency": currency_code,
|
||||
"issuer": cold_address,
|
||||
"value": limit
|
||||
}
|
||||
}
|
||||
const ts_prepared = await api.prepareTransaction(
|
||||
trust_set_tx,
|
||||
{maxLedgerVersionOffset: 10}
|
||||
)
|
||||
const ts_signed = api.sign(ts_prepared.txJSON, hot_secret)
|
||||
do_submit(block, {tx_blob: ts_signed.signedTransaction})
|
||||
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_address_2(event, "hot")
|
||||
const cold_address = get_address_2(event, "cold")
|
||||
const cold_secret = get_secret_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_address,
|
||||
"Amount": {
|
||||
"currency": currency_code,
|
||||
"value": issue_quantity,
|
||||
"issuer": cold_address
|
||||
},
|
||||
"Destination": hot_address
|
||||
}
|
||||
if (use_dest_tag) {
|
||||
send_token_tx["DestinationTag"] = dest_tag
|
||||
}
|
||||
const pay_prepared = await api.prepareTransaction(
|
||||
send_token_tx,
|
||||
{maxLedgerVersionOffset: 10}
|
||||
)
|
||||
const pay_signed = api.sign(pay_prepared.txJSON, cold_secret)
|
||||
do_submit(block, {tx_blob: pay_signed.signedTransaction})
|
||||
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_address_2(event, "hot")
|
||||
const cold_address = get_address_2(event, "cold")
|
||||
|
||||
block.find(".loader").show()
|
||||
try {
|
||||
const hot_balances = await api.request("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)}</code></pre>
|
||||
`)
|
||||
|
||||
const cold_balances = await api.request("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)}</code></pre>
|
||||
`)
|
||||
|
||||
block.find(".loader").hide()
|
||||
complete_step("Confirm Balances")
|
||||
} catch(err) {
|
||||
block.find(".loader").hide()
|
||||
show_error(block, `Error looking up balances: ${err}`)
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
@@ -112,7 +112,7 @@ $(document).ready(() => {
|
||||
target="_blank">${prelim_result.engine_result}</a></p>`)
|
||||
} catch(err) {
|
||||
block.find(".loader").hide()
|
||||
show_error(`An error occurred when sending the test payment: ${err}`)
|
||||
show_error(block, `An error occurred when sending the test payment: ${err}`)
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user