mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-20 11:45:50 +00:00
code updates for xrpl.js 2.0 beta 5
This commit is contained in:
@@ -520,12 +520,12 @@ async function generic_full_send(event, tx_json, wallet) {
|
|||||||
`<p>${tl("Prepared transaction:")}</p>
|
`<p>${tl("Prepared transaction:")}</p>
|
||||||
<pre><code>${pretty_print(prepared)}</code></pre>`)
|
<pre><code>${pretty_print(prepared)}</code></pre>`)
|
||||||
|
|
||||||
const signed = wallet.signTransaction(prepared)
|
const {tx_blob, hash} = wallet.sign(prepared)
|
||||||
block.find(".output-area").append(
|
block.find(".output-area").append(
|
||||||
`<p>${tl("Transaction hash:")} <code id="tx_id">${xrpl.computeSignedTransactionHash(signed)}</code></p>`)
|
`<p>${tl("Transaction hash:")} <code id="tx_id">${hash}</code></p>`)
|
||||||
// TODO: update computeSignedTransactionHash if that changes
|
// TODO: update computeSignedTransactionHash if that changes
|
||||||
|
|
||||||
await do_submit(block, {"tx_blob": signed}, wait_step_name)
|
await do_submit(block, {"tx_blob": tx_blob}, wait_step_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,183 +0,0 @@
|
|||||||
// Submit-and-verify XRPL transaction using xrpl.js (v2.0)
|
|
||||||
// Demonstrates how to submit a transaction and wait for validation.
|
|
||||||
// This is not true "robust" transaction submission because it does not protect
|
|
||||||
// against power outages or other sudden interruptions.
|
|
||||||
|
|
||||||
// Look up a transaction's result.
|
|
||||||
// Arguments:
|
|
||||||
// @param api object Client instance connected to the network where you
|
|
||||||
// submitted the transaction. MUST ALREADY BE SUBSCRIBED TO THE
|
|
||||||
// `ledger` event stream.
|
|
||||||
// @param tx_id string The identifying hash of the transaction.
|
|
||||||
// @param max_ledger int optional The highest ledger index where the
|
|
||||||
// transaction can be validated.
|
|
||||||
// @param min_ledger int optional The lowest ledger index where the
|
|
||||||
// transaction can be validated.
|
|
||||||
// Returns: Promise<object> -> result of the tx command with the transaction's
|
|
||||||
// validated transaction results.
|
|
||||||
// On failure, the reason is an object with two fields:
|
|
||||||
// - failure_final: if true, this transaction did not achieve consensus and
|
|
||||||
// it can never be validated in the future (assuming the
|
|
||||||
// min_ledger and max_ledger values provided were accurate).
|
|
||||||
// - msg: A human-readable message explaining what happened.
|
|
||||||
function lookup_tx_final(api, tx_id, max_ledger, min_ledger) {
|
|
||||||
if (typeof min_ledger == "undefined") {
|
|
||||||
min_ledger = -1
|
|
||||||
}
|
|
||||||
if (typeof max_ledger == "undefined") {
|
|
||||||
max_ledger = -1
|
|
||||||
}
|
|
||||||
if (min_ledger > max_ledger) {
|
|
||||||
// Assume the args were just passed backwards & swap them
|
|
||||||
[min_ledger, max_ledger] = [max_ledger, min_ledger]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we're subscribed to the ledger stream to trigger updates
|
|
||||||
api.request({"command": "subscribe", "streams": ["ledger"]})
|
|
||||||
|
|
||||||
// Helper to determine if we (should) know the transaction's final result yet.
|
|
||||||
// If the server has validated all ledgers the tx could possibly appear in,
|
|
||||||
// then we should know its final result.
|
|
||||||
async function server_has_ledger_range(min_ledger, max_ledger) {
|
|
||||||
const si = await api.request({command: "server_info"})
|
|
||||||
// console.log(`Server has ledger range: ${si.result.info.complete_ledgers}`)
|
|
||||||
if (si.result.info.complete_ledgers == "empty") {
|
|
||||||
console.warn("Connected server is not synced.")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// In case of a discontiguous set, use only the last set, since we need
|
|
||||||
// continuous history from submission to expiration to know that a
|
|
||||||
// transaction failed to achieve consensus.
|
|
||||||
const ledger_ranges = si.result.info.complete_ledgers.split(',')
|
|
||||||
// Note: last_range can be in the form 'x-y' or just 'y'
|
|
||||||
const last_range = ledger_ranges[ledger_ranges.length -1].split('-')
|
|
||||||
const lr_min = parseInt(last_range[0])
|
|
||||||
const lr_max = parseInt(last_range[last_range.length - 1])
|
|
||||||
if (lr_min <= min_ledger && lr_max >= max_ledger) {
|
|
||||||
// Server has ledger range needed.
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
ledger_listener = async (ledger) => {
|
|
||||||
try {
|
|
||||||
const tx_response = await api.request({
|
|
||||||
"command": "tx",
|
|
||||||
"transaction": tx_id,
|
|
||||||
"min_ledger": min_ledger,
|
|
||||||
"max_ledger": max_ledger
|
|
||||||
})
|
|
||||||
|
|
||||||
if (tx_response.result.validated) {
|
|
||||||
resolve(tx_response.result.meta.TransactionResult)
|
|
||||||
} else if (ledger.ledger_index >= max_ledger) {
|
|
||||||
api.off("ledgerClosed", ledger_listener)
|
|
||||||
// Transaction found, not validated, but we should have a final result
|
|
||||||
// by now.
|
|
||||||
// Work around https://github.com/ripple/rippled/issues/3727
|
|
||||||
if (await server_has_ledger_range(min_ledger, max_ledger)) {
|
|
||||||
// Transaction should have been validated by now.
|
|
||||||
reject({
|
|
||||||
failure_final: true,
|
|
||||||
msg: `Transaction not found in ledgers ${min_ledger}-${max_ledger}. This result is final if this range is correct.`
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
reject({
|
|
||||||
failure_final: false,
|
|
||||||
msg: "Can't get final result (1). Check a full history server."
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Transaction may still be validated later. Keep waiting.
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
console.warn(e)
|
|
||||||
if (e.data.error == "txnNotFound") {
|
|
||||||
if (e.data.searched_all) {
|
|
||||||
api.off("ledgerClosed", ledger_listener)
|
|
||||||
reject({
|
|
||||||
failure_final: true,
|
|
||||||
msg: `Transaction not found in ledgers ${min_ledger}-${max_ledger}. This result is final if this range is correct.`
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
if (max_ledger > ledger.ledger_index) {
|
|
||||||
api.off("ledgerClosed", ledger_listener)
|
|
||||||
// Transaction may yet be confirmed. This would not be a bad time
|
|
||||||
// to resubmit the transaction just in case.
|
|
||||||
} else {
|
|
||||||
// Work around https://github.com/ripple/rippled/issues/3750
|
|
||||||
if (await server_has_ledger_range(min_ledger, max_ledger)) {
|
|
||||||
reject({
|
|
||||||
failure_final: true,
|
|
||||||
msg: `Transaction not found in ledgers ${min_ledger}-${max_ledger}. This result is final if this range is correct.`
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
reject({
|
|
||||||
failure_final: false,
|
|
||||||
msg: "Can't get final result. Check a full history server."
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Unknown error; pass it back up
|
|
||||||
reject({
|
|
||||||
failure_final: false,
|
|
||||||
msg: `Unknown Error: ${e}`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // end ledger event handler
|
|
||||||
api.on('ledgerClosed', ledger_listener)
|
|
||||||
}) // end promise def
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Submit a transaction blob and get its final result as a string.
|
|
||||||
// This can be one of these possibilities:
|
|
||||||
// tesSUCCESS. The transaction executed successfully.
|
|
||||||
// tec*. The transaction was validated with a failure code. It destroyed the XRP
|
|
||||||
// transaction cost and may have done some cleanup such as removing
|
|
||||||
// expired objects from the ledger, but nothing else.
|
|
||||||
// See https://xrpl.org/tec-codes.html for the full list.
|
|
||||||
// tefMAX_LEDGER. The transaction expired without ever being included
|
|
||||||
// in a validated ledger.
|
|
||||||
// unknown. Either the server you are querying does not have the
|
|
||||||
// necessary ledger history to find the transaction's final result, or
|
|
||||||
// something else went wrong when trying to look up the results. The
|
|
||||||
// warning written to the console can tell you more about what happened.
|
|
||||||
async function submit_and_verify(api, tx_blob) {
|
|
||||||
// Make sure we subscribe to the ledger stream. This is idempotent so we don't
|
|
||||||
// have to worry about oversubscribing.
|
|
||||||
api.request({"command": "subscribe", "streams": ["ledger"]})
|
|
||||||
const prelim = await api.request({"command": "submit", "tx_blob": tx_blob})
|
|
||||||
console.log("Preliminary result code:", prelim.result.engine_result)
|
|
||||||
const min_ledger = prelim.result.validated_ledger_index
|
|
||||||
if (prelim.result.tx_json.LastLedgerSequence === undefined) {
|
|
||||||
console.warn("Transaction has no LastLedgerSequence field. "+
|
|
||||||
"It may be impossible to determine final failure.")
|
|
||||||
}
|
|
||||||
const max_ledger = prelim.result.tx_json.LastLedgerSequence
|
|
||||||
const tx_id = prelim.result.tx_json.hash
|
|
||||||
|
|
||||||
let final_result
|
|
||||||
try {
|
|
||||||
final_result = await lookup_tx_final(api, tx_id, max_ledger, min_ledger)
|
|
||||||
} catch(reason) {
|
|
||||||
if (reason.failure_final) final_result = "tefMAX_LEDGER"
|
|
||||||
else final_result = "unknown"
|
|
||||||
console.warn(reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
return final_result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exports for node.js; no-op for browsers
|
|
||||||
if (typeof module !== "undefined") {
|
|
||||||
module.exports = {
|
|
||||||
submit_and_verify: submit_and_verify,
|
|
||||||
lookup_tx_final: lookup_tx_final
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -128,10 +128,10 @@ $(document).ready(() => {
|
|||||||
|
|
||||||
let flags = 0
|
let flags = 0
|
||||||
if ($("#cold-require-dest").prop("checked")) {
|
if ($("#cold-require-dest").prop("checked")) {
|
||||||
flags |= xrpl.AccountSetTransactionFlags.tfRequireDestTag
|
flags |= xrpl.AccountSetTfFlags.tfRequireDestTag
|
||||||
}
|
}
|
||||||
if ($("#cold-disallow-xrp").prop("checked")) {
|
if ($("#cold-disallow-xrp").prop("checked")) {
|
||||||
flags |= xrpl.AccountSetTransactionFlags.tfDisallowXRP
|
flags |= xrpl.AccountSetTfFlags.tfDisallowXRP
|
||||||
}
|
}
|
||||||
|
|
||||||
const tick_size = parseInt($("#cold-tick-size").val(), 10)
|
const tick_size = parseInt($("#cold-tick-size").val(), 10)
|
||||||
@@ -153,10 +153,10 @@ $(document).ready(() => {
|
|||||||
try {
|
try {
|
||||||
const cold_settings_tx = {
|
const cold_settings_tx = {
|
||||||
"TransactionType": "AccountSet",
|
"TransactionType": "AccountSet",
|
||||||
"Account": cold_wallet.classicAddress,
|
"Account": cold_wallet.address,
|
||||||
"TransferRate": transfer_rate,
|
"TransferRate": transfer_rate,
|
||||||
"TickSize": tick_size,
|
"TickSize": tick_size,
|
||||||
"SetFlag": xrpl.AccountSetFlags.asfDefaultRipple,
|
"SetFlag": xrpl.AccountSetAsfFlags.asfDefaultRipple,
|
||||||
"Domain": domain,
|
"Domain": domain,
|
||||||
"Flags": flags
|
"Flags": flags
|
||||||
}
|
}
|
||||||
@@ -179,10 +179,10 @@ $(document).ready(() => {
|
|||||||
|
|
||||||
let flags = 0
|
let flags = 0
|
||||||
if ($("#hot-require-dest").prop("checked")) {
|
if ($("#hot-require-dest").prop("checked")) {
|
||||||
flags |= xrpl.AccountSetTransactionFlags.tfRequireDestTag
|
flags |= xrpl.AccountSetTfFlags.tfRequireDestTag
|
||||||
}
|
}
|
||||||
if ($("#hot-disallow-xrp").prop("checked")) {
|
if ($("#hot-disallow-xrp").prop("checked")) {
|
||||||
flags |= xrpl.AccountSetTransactionFlags.tfDisallowXRP
|
flags |= xrpl.AccountSetTfFlags.tfDisallowXRP
|
||||||
}
|
}
|
||||||
|
|
||||||
const domain = $("#hot-domain-hex").text().trim()
|
const domain = $("#hot-domain-hex").text().trim()
|
||||||
@@ -191,9 +191,9 @@ $(document).ready(() => {
|
|||||||
try {
|
try {
|
||||||
const hot_settings_tx = {
|
const hot_settings_tx = {
|
||||||
"TransactionType": "AccountSet",
|
"TransactionType": "AccountSet",
|
||||||
"Account": hot_wallet.classicAddress,
|
"Account": hot_wallet.address,
|
||||||
// Require Auth so we can't accidentally issue from the hot address
|
// Require Auth so we can't accidentally issue from the hot address
|
||||||
"SetFlag": xrpl.AccountSetFlags.asfRequireAuth,
|
"SetFlag": xrpl.AccountSetAsfFlags.asfRequireAuth,
|
||||||
"Domain": domain,
|
"Domain": domain,
|
||||||
"Flags": flags
|
"Flags": flags
|
||||||
}
|
}
|
||||||
@@ -211,7 +211,7 @@ $(document).ready(() => {
|
|||||||
$("#create-trust-line-button").click( async (event) => {
|
$("#create-trust-line-button").click( async (event) => {
|
||||||
const block = $(event.target).closest(".interactive-block")
|
const block = $(event.target).closest(".interactive-block")
|
||||||
block.find(".output-area").empty()
|
block.find(".output-area").empty()
|
||||||
const cold_address = get_wallet_2(event, "cold").classicAddress
|
const cold_address = get_wallet_2(event, "cold").address
|
||||||
const hot_wallet = get_wallet_2(event, "hot")
|
const hot_wallet = get_wallet_2(event, "hot")
|
||||||
|
|
||||||
let currency_code
|
let currency_code
|
||||||
@@ -234,7 +234,7 @@ $(document).ready(() => {
|
|||||||
try {
|
try {
|
||||||
const trust_set_tx = {
|
const trust_set_tx = {
|
||||||
"TransactionType": "TrustSet",
|
"TransactionType": "TrustSet",
|
||||||
"Account": hot_wallet.classicAddress,
|
"Account": hot_wallet.address,
|
||||||
"LimitAmount": {
|
"LimitAmount": {
|
||||||
"currency": currency_code,
|
"currency": currency_code,
|
||||||
"issuer": cold_address,
|
"issuer": cold_address,
|
||||||
@@ -254,7 +254,7 @@ $(document).ready(() => {
|
|||||||
$("#send-token-button").click( async (event) => {
|
$("#send-token-button").click( async (event) => {
|
||||||
const block = $(event.target).closest(".interactive-block")
|
const block = $(event.target).closest(".interactive-block")
|
||||||
block.find(".output-area").empty()
|
block.find(".output-area").empty()
|
||||||
const hot_address = get_wallet_2(event, "hot").classicAddress
|
const hot_address = get_wallet_2(event, "hot").address
|
||||||
const cold_wallet = get_wallet_2(event, "cold")
|
const cold_wallet = get_wallet_2(event, "cold")
|
||||||
|
|
||||||
const currency_code = $("#send-currency-code").text().trim()
|
const currency_code = $("#send-currency-code").text().trim()
|
||||||
@@ -274,11 +274,11 @@ $(document).ready(() => {
|
|||||||
try {
|
try {
|
||||||
const send_token_tx = {
|
const send_token_tx = {
|
||||||
"TransactionType": "Payment",
|
"TransactionType": "Payment",
|
||||||
"Account": cold_wallet.classicAddress,
|
"Account": cold_wallet.address,
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"currency": currency_code,
|
"currency": currency_code,
|
||||||
"value": issue_quantity,
|
"value": issue_quantity,
|
||||||
"issuer": cold_wallet.classicAddress
|
"issuer": cold_wallet.address
|
||||||
},
|
},
|
||||||
"Destination": hot_address
|
"Destination": hot_address
|
||||||
}
|
}
|
||||||
@@ -299,8 +299,8 @@ $(document).ready(() => {
|
|||||||
$("#confirm-balances-button").click( async (event) => {
|
$("#confirm-balances-button").click( async (event) => {
|
||||||
const block = $(event.target).closest(".interactive-block")
|
const block = $(event.target).closest(".interactive-block")
|
||||||
block.find(".output-area").empty()
|
block.find(".output-area").empty()
|
||||||
const hot_address = get_wallet_2(event, "hot").classicAddress
|
const hot_address = get_wallet_2(event, "hot").address
|
||||||
const cold_address = get_wallet_2(event, "cold").classicAddress
|
const cold_address = get_wallet_2(event, "cold").address
|
||||||
|
|
||||||
block.find(".loader").show()
|
block.find(".loader").show()
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ $(document).ready(() => {
|
|||||||
let seed = block.data("testSendSecret")
|
let seed = block.data("testSendSecret")
|
||||||
if (!seed) {
|
if (!seed) {
|
||||||
console.debug("First-time setup for test sender...")
|
console.debug("First-time setup for test sender...")
|
||||||
test_sender_wallet = await api.generateFaucetWallet()
|
test_sender_wallet = (await api.fundWallet()).wallet
|
||||||
block.data("testSendSecret", test_sender_wallet.seed)
|
block.data("testSendSecret", test_sender_wallet.seed)
|
||||||
|
|
||||||
// First time: Wait for our test sender to be fully funded, so we don't
|
// First time: Wait for our test sender to be fully funded, so we don't
|
||||||
@@ -75,7 +75,7 @@ $(document).ready(() => {
|
|||||||
try {
|
try {
|
||||||
await api.request({
|
await api.request({
|
||||||
"command": "account_info",
|
"command": "account_info",
|
||||||
"account": test_sender_wallet.classicAddress,
|
"account": test_sender_wallet.address,
|
||||||
"ledger_index": "validated"
|
"ledger_index": "validated"
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
@@ -101,7 +101,7 @@ $(document).ready(() => {
|
|||||||
const test_sender = await get_test_sender(block)
|
const test_sender = await get_test_sender(block)
|
||||||
const tx_json = {
|
const tx_json = {
|
||||||
"TransactionType": "Payment",
|
"TransactionType": "Payment",
|
||||||
"Account": test_sender.classicAddress,
|
"Account": test_sender.address,
|
||||||
"Amount": "3152021",
|
"Amount": "3152021",
|
||||||
"Destination": address
|
"Destination": address
|
||||||
}
|
}
|
||||||
@@ -111,15 +111,14 @@ $(document).ready(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const prepared = await api.autofill(tx_json)
|
const prepared = await api.autofill(tx_json)
|
||||||
const tx_blob = test_sender.signTransaction(prepared)
|
const {tx_blob, hash} = test_sender.sign(prepared)
|
||||||
console.debug("Submitting test payment", prepared)
|
console.debug("Submitting test payment", prepared)
|
||||||
const prelim = await api.request({"command": "submit", tx_blob})
|
const prelim = await api.request({"command": "submit", tx_blob})
|
||||||
const tx_hash = xrpl.computeSignedTransactionHash(tx_blob)
|
|
||||||
|
|
||||||
block.find(".loader").hide()
|
block.find(".loader").hide()
|
||||||
block.find(".output-area").append(`<p>${tx_json.TransactionType}
|
block.find(".output-area").append(`<p>${tx_json.TransactionType}
|
||||||
${prepared.Sequence} ${(dt?"WITH":"WITHOUT")} Dest. Tag:
|
${prepared.Sequence} ${(dt?"WITH":"WITHOUT")} Dest. Tag:
|
||||||
<a href="https://testnet.xrpl.org/transactions/${tx_hash}"
|
<a href="https://testnet.xrpl.org/transactions/${hash}"
|
||||||
target="_blank">${prelim.result.engine_result}</a></p>`)
|
target="_blank">${prelim.result.engine_result}</a></p>`)
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
block.find(".loader").hide()
|
block.find(".loader").hide()
|
||||||
|
|||||||
@@ -44,13 +44,12 @@ $("#sign-button").click( function(event) {
|
|||||||
const wallet = get_wallet(event)
|
const wallet = get_wallet(event)
|
||||||
if (!wallet) {return}
|
if (!wallet) {return}
|
||||||
|
|
||||||
signed = wallet.signTransaction(preparedTxJSON)
|
{tx_blob, hash} = wallet.sign(preparedTxJSON)
|
||||||
hash = xrpl.computeSignedTransactionHash(signed) // TODO: update if computeSignedTransactionHash changes
|
|
||||||
|
|
||||||
block.find(".output-area").html(
|
block.find(".output-area").html(
|
||||||
`<div><strong>Signed Transaction blob:</strong>
|
`<div><strong>Signed Transaction blob:</strong>
|
||||||
<code id='signed-tx-blob' style='overflow-wrap: anywhere; word-wrap: anywhere'
|
<code id='signed-tx-blob' style='overflow-wrap: anywhere; word-wrap: anywhere'
|
||||||
>${signed}</code></div>
|
>${tx_blob}</code></div>
|
||||||
<div><strong>Identifying hash:</strong> <span id='signed-tx-hash'
|
<div><strong>Identifying hash:</strong> <span id='signed-tx-hash'
|
||||||
>${hash}</span></div>`
|
>${hash}</span></div>`
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -49,9 +49,9 @@ $("#prepare-and-sign").click( async function(event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const vli = await api.getLedgerIndex()
|
const vli = await api.getLedgerIndex()
|
||||||
let prepared = await api.autofill({
|
const prepared = await api.autofill({
|
||||||
"TransactionType": "TicketCreate",
|
"TransactionType": "TicketCreate",
|
||||||
"Account": wallet.classicAddress,
|
"Account": wallet.address,
|
||||||
"TicketCount": 10,
|
"TicketCount": 10,
|
||||||
"Sequence": current_sequence,
|
"Sequence": current_sequence,
|
||||||
"LastLedgerSequence": vli+LLS_OFFSET
|
"LastLedgerSequence": vli+LLS_OFFSET
|
||||||
@@ -61,10 +61,9 @@ $("#prepare-and-sign").click( async function(event) {
|
|||||||
`<p>Prepared transaction:</p>
|
`<p>Prepared transaction:</p>
|
||||||
<pre><code>${pretty_print(prepared)}</code></pre>`)
|
<pre><code>${pretty_print(prepared)}</code></pre>`)
|
||||||
|
|
||||||
let tx_blob = wallet.signTransaction(prepared)
|
const {tx_blob, hash} = wallet.sign(prepared)
|
||||||
let tx_id = xrpl.computeSignedTransactionHash(tx_blob)
|
|
||||||
block.find(".output-area").append(
|
block.find(".output-area").append(
|
||||||
`<p>Transaction hash: <code id="tx_id">${tx_id}</code></p>`)
|
`<p>Transaction hash: <code id="tx_id">${hash}</code></p>`)
|
||||||
block.find(".output-area").append(
|
block.find(".output-area").append(
|
||||||
`<p>Signed blob:</p><pre class="tx-blob"><code id="tx_blob">${tx_blob}</code></pre>`)
|
`<p>Signed blob:</p><pre class="tx-blob"><code id="tx_blob">${tx_blob}</code></pre>`)
|
||||||
|
|
||||||
@@ -84,16 +83,15 @@ async function intermission_submit(event, tx_json) {
|
|||||||
const wallet = get_wallet(event)
|
const wallet = get_wallet(event)
|
||||||
if (!wallet) {return}
|
if (!wallet) {return}
|
||||||
const prepared = await api.autofill(tx_json)
|
const prepared = await api.autofill(tx_json)
|
||||||
const tx_blob = wallet.signTransaction(prepared)
|
const {tx_blob, hash} = wallet.sign(prepared)
|
||||||
const prelim = await api.request({
|
const prelim = await api.request({
|
||||||
"command": "submit",
|
"command": "submit",
|
||||||
"tx_blob": tx_blob
|
"tx_blob": tx_blob
|
||||||
})
|
})
|
||||||
const tx_id = xrpl.computeSignedTransactionHash(tx_blob)
|
|
||||||
|
|
||||||
block.find(".output-area").append(`<p>${prepared.TransactionType}
|
block.find(".output-area").append(`<p>${prepared.TransactionType}
|
||||||
${prepared.Sequence}:
|
${prepared.Sequence}:
|
||||||
<a href="https://devnet.xrpl.org/transactions/${tx_id}"
|
<a href="https://devnet.xrpl.org/transactions/${hash}"
|
||||||
target="_blank">${prelim.result.engine_result}</a></p>`)
|
target="_blank">${prelim.result.engine_result}</a></p>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +185,7 @@ $("#prepare-ticketed-tx").click(async function(event) {
|
|||||||
|
|
||||||
const prepared_t = await api.autofill({
|
const prepared_t = await api.autofill({
|
||||||
"TransactionType": "AccountSet",
|
"TransactionType": "AccountSet",
|
||||||
"Account": wallet.classicAddress,
|
"Account": wallet.address,
|
||||||
"TicketSequence": use_ticket,
|
"TicketSequence": use_ticket,
|
||||||
"Sequence": 0,
|
"Sequence": 0,
|
||||||
"LastLedgerSequence": vli+LLS_OFFSET
|
"LastLedgerSequence": vli+LLS_OFFSET
|
||||||
@@ -197,14 +195,13 @@ $("#prepare-ticketed-tx").click(async function(event) {
|
|||||||
`<p>Prepared transaction:</p>
|
`<p>Prepared transaction:</p>
|
||||||
<pre><code>${pretty_print(prepared_t)}</code></pre>`)
|
<pre><code>${pretty_print(prepared_t)}</code></pre>`)
|
||||||
|
|
||||||
const tx_blob_t = wallet.signTransaction(prepared_t)
|
const {tx_blob, hash} = wallet.sign(prepared_t)
|
||||||
const tx_id_t = xrpl.computeSignedTransactionHash(tx_blob_t)
|
|
||||||
block.find(".output-area").append(
|
block.find(".output-area").append(
|
||||||
`<p>Transaction hash: <code id="tx_id_t">${tx_id_t}</code></p>`)
|
`<p>Transaction hash: <code id="tx_id_t">${hash}</code></p>`)
|
||||||
|
|
||||||
block.find(".output-area").append(
|
block.find(".output-area").append(
|
||||||
`<pre style="visibility: none">
|
`<pre style="visibility: none">
|
||||||
<code id="tx_blob_t">${tx_blob_t}</code></pre>`)
|
<code id="tx_blob_t">${tx_blob}</code></pre>`)
|
||||||
|
|
||||||
// Update breadcrumbs & activate next step
|
// Update breadcrumbs & activate next step
|
||||||
complete_step("Prepare Ticketed Tx")
|
complete_step("Prepare Ticketed Tx")
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ const set_up_tx_sender = async function() {
|
|||||||
api = new xrpl.Client(TESTNET_URL)
|
api = new xrpl.Client(TESTNET_URL)
|
||||||
|
|
||||||
let sending_wallet
|
let sending_wallet
|
||||||
let xrp_balance
|
let xrp_balance = "TBD"
|
||||||
|
|
||||||
function enable_buttons_if_ready() {
|
function enable_buttons_if_ready() {
|
||||||
if ( (typeof sending_wallet) === "undefined") {
|
if ( (typeof sending_wallet) === "undefined") {
|
||||||
@@ -72,17 +72,18 @@ const set_up_tx_sender = async function() {
|
|||||||
|
|
||||||
console.debug("Getting a sending address from the faucet...")
|
console.debug("Getting a sending address from the faucet...")
|
||||||
try {
|
try {
|
||||||
sending_wallet = await api.generateFaucetWallet()
|
const fund_response = await api.fundWallet()
|
||||||
|
sending_wallet = fund_response.wallet
|
||||||
|
xrp_balance = xrpl.dropsToXrp(fund_response.balance)
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
errorNotif("There was an error with the XRP Ledger Testnet Faucet. Reload this page to try again.")
|
errorNotif("There was an error with the XRP Ledger Testnet Faucet. Reload this page to try again.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
xrp_balance = "TBD" // TODO old faucet command gave balance, new doesn't
|
|
||||||
$("#balance-item").text(xrp_balance)
|
$("#balance-item").text(xrp_balance)
|
||||||
|
|
||||||
$(".sending-address-item").text(sending_wallet.classicAddress)
|
$(".sending-address-item").text(sending_wallet.address)
|
||||||
$("#init_button").prop("disabled", "disabled")
|
$("#init_button").prop("disabled", "disabled")
|
||||||
$("#init_button").addClass("disabled")
|
$("#init_button").addClass("disabled")
|
||||||
$("#init_button").attr("title", "Done")
|
$("#init_button").attr("title", "Done")
|
||||||
@@ -112,10 +113,11 @@ const set_up_tx_sender = async function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function update_xrp_balance() {
|
async function update_xrp_balance() {
|
||||||
balances = await api.getBalances(sending_wallet.classicAddress, {currency: "XRP"})
|
balances = await api.getBalances(sending_wallet.address, {currency: "XRP"})
|
||||||
$("#balance-item").text(balances[0].value)
|
$("#balance-item").text(balances[0].value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function submit_and_notify(tx_object, use_wallet, silent) {
|
async function submit_and_notify(tx_object, use_wallet, silent) {
|
||||||
if (use_wallet === undefined) {
|
if (use_wallet === undefined) {
|
||||||
use_wallet = sending_wallet
|
use_wallet = sending_wallet
|
||||||
@@ -132,31 +134,9 @@ const set_up_tx_sender = async function() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine first and last ledger the tx could be validated in *BEFORE*
|
|
||||||
// signing it.
|
|
||||||
min_ledger = await api.getLedgerIndex()
|
|
||||||
max_ledger = prepared.LastLedgerSequence
|
|
||||||
|
|
||||||
|
|
||||||
let signed
|
|
||||||
try {
|
try {
|
||||||
// Sign, submit
|
const {tx_blob, hash} = use_wallet.sign(prepared)
|
||||||
signed = use_wallet.signTransaction(prepared)
|
const final_result_data = await api.submitSignedReliable(tx_blob)
|
||||||
await api.request({"command": "submit", "tx_blob": signed})
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error)
|
|
||||||
if (!silent) {
|
|
||||||
errorNotif(`Error signing & submitting ${tx_object.TransactionType} tx: ${error}`)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for tx to be in a validated ledger or to expire
|
|
||||||
const hash = xrpl.computeSignedTransactionHash(signed)
|
|
||||||
|
|
||||||
try {
|
|
||||||
// use lookup_tx_final() from submit-and-verify2.js
|
|
||||||
let final_result_data = await lookup_tx_final(api, hash, max_ledger, min_ledger)
|
|
||||||
console.log("final_result_data is", final_result_data)
|
console.log("final_result_data is", final_result_data)
|
||||||
let final_result = final_result_data.result.meta.TransactionResult
|
let final_result = final_result_data.result.meta.TransactionResult
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
@@ -169,12 +149,13 @@ const set_up_tx_sender = async function() {
|
|||||||
logTx(tx_object.TransactionType, hash, final_result)
|
logTx(tx_object.TransactionType, hash, final_result)
|
||||||
}
|
}
|
||||||
update_xrp_balance()
|
update_xrp_balance()
|
||||||
return data
|
return final_result_data
|
||||||
} catch(error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
errorNotif("Error submitting "+tx_object.TransactionType+" tx: "+error)
|
errorNotif(`Error signing & submitting ${tx_object.TransactionType} tx: ${error}`)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -196,32 +177,19 @@ const set_up_tx_sender = async function() {
|
|||||||
$("#pp_progress .progress-bar").addClass("progress-bar-animated")
|
$("#pp_progress .progress-bar").addClass("progress-bar-animated")
|
||||||
// 1. Get a funded address to use as issuer
|
// 1. Get a funded address to use as issuer
|
||||||
try {
|
try {
|
||||||
pp_issuer_wallet = await api.generateFaucetWallet()
|
const fund_response = await api.fundWallet()
|
||||||
|
pp_issuer_wallet = fund_response.wallet
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
console.log("Error getting issuer address for partial payments:", error)
|
console.log("Error getting issuer address for partial payments:", error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
$("#pp_progress .progress-bar").width("20%")
|
$("#pp_progress .progress-bar").width("20%")
|
||||||
|
|
||||||
// Wait for the address's funding to be validated so we don't get the wrong
|
|
||||||
// starting sequence number.
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
||||||
await api.request({
|
|
||||||
command: "account_info",
|
|
||||||
account: pp_issuer_wallet.classicAddress,
|
|
||||||
ledger_index: "validated"
|
|
||||||
})
|
|
||||||
break
|
|
||||||
} catch(e) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Set Default Ripple on issuer
|
// 2. Set Default Ripple on issuer
|
||||||
let resp = await submit_and_notify({
|
let resp = await submit_and_notify({
|
||||||
TransactionType: "AccountSet",
|
TransactionType: "AccountSet",
|
||||||
Account: pp_issuer_wallet.classicAddress,
|
Account: pp_issuer_wallet.address,
|
||||||
SetFlag: 8
|
SetFlag: xrpl.AccountSetAsfFlags.asfDefaultRipple
|
||||||
}, pp_issuer_wallet, true)
|
}, pp_issuer_wallet, true)
|
||||||
if (resp === undefined) {
|
if (resp === undefined) {
|
||||||
console.log("Couldn't set Default Ripple for partial payment issuer")
|
console.log("Couldn't set Default Ripple for partial payment issuer")
|
||||||
@@ -232,11 +200,11 @@ const set_up_tx_sender = async function() {
|
|||||||
// 3. Make a trust line from sending address to issuer
|
// 3. Make a trust line from sending address to issuer
|
||||||
resp = await submit_and_notify({
|
resp = await submit_and_notify({
|
||||||
TransactionType: "TrustSet",
|
TransactionType: "TrustSet",
|
||||||
Account: sending_wallet.classicAddress,
|
Account: sending_wallet.address,
|
||||||
LimitAmount: {
|
LimitAmount: {
|
||||||
currency: pp_sending_currency,
|
currency: pp_sending_currency,
|
||||||
value: "1000000000", // arbitrarily, 1 billion fake currency
|
value: "1000000000", // arbitrarily, 1 billion fake currency
|
||||||
issuer: pp_issuer_wallet.classicAddress
|
issuer: pp_issuer_wallet.address
|
||||||
}
|
}
|
||||||
}, sending_wallet, true)
|
}, sending_wallet, true)
|
||||||
if (resp === undefined) {
|
if (resp === undefined) {
|
||||||
@@ -248,12 +216,12 @@ const set_up_tx_sender = async function() {
|
|||||||
// 4. Issue fake currency to main sending address
|
// 4. Issue fake currency to main sending address
|
||||||
resp = await submit_and_notify({
|
resp = await submit_and_notify({
|
||||||
TransactionType: "Payment",
|
TransactionType: "Payment",
|
||||||
Account: pp_issuer_wallet.classicAddress,
|
Account: pp_issuer_wallet.address,
|
||||||
Destination: sending_wallet.classicAddress,
|
Destination: sending_wallet.address,
|
||||||
Amount: {
|
Amount: {
|
||||||
currency: pp_sending_currency,
|
currency: pp_sending_currency,
|
||||||
value: "1000000000",
|
value: "1000000000",
|
||||||
issuer: pp_issuer_wallet.classicAddress
|
issuer: pp_issuer_wallet.address
|
||||||
}
|
}
|
||||||
}, pp_issuer_wallet, true)
|
}, pp_issuer_wallet, true)
|
||||||
if (resp === undefined) {
|
if (resp === undefined) {
|
||||||
@@ -267,12 +235,12 @@ const set_up_tx_sender = async function() {
|
|||||||
// so they end up paying themselves issued currency then delivering XRP.
|
// so they end up paying themselves issued currency then delivering XRP.
|
||||||
resp = await submit_and_notify({
|
resp = await submit_and_notify({
|
||||||
TransactionType: "OfferCreate",
|
TransactionType: "OfferCreate",
|
||||||
Account: sending_wallet.classicAddress,
|
Account: sending_wallet.address,
|
||||||
TakerGets: "1000000000000000", // 1 billion XRP
|
TakerGets: "1000000000000000", // 1 billion XRP
|
||||||
TakerPays: {
|
TakerPays: {
|
||||||
currency: pp_sending_currency,
|
currency: pp_sending_currency,
|
||||||
value: "1000000000",
|
value: "1000000000",
|
||||||
issuer: pp_issuer_wallet.classicAddress
|
issuer: pp_issuer_wallet.address
|
||||||
}
|
}
|
||||||
}, sending_wallet, true)
|
}, sending_wallet, true)
|
||||||
if (resp === undefined) {
|
if (resp === undefined) {
|
||||||
@@ -295,7 +263,7 @@ const set_up_tx_sender = async function() {
|
|||||||
// Destination Address box -----------------------------------------------
|
// Destination Address box -----------------------------------------------
|
||||||
async function on_dest_address_update(event) {
|
async function on_dest_address_update(event) {
|
||||||
const d_a = $("#destination_address").val()
|
const d_a = $("#destination_address").val()
|
||||||
if (api.isValidClassicAddress(d_a) || api.isValidXAddress(d_a)) { // TODO: switch back to isValidAddress
|
if (xrpl.isValidAddress(d_a)) {
|
||||||
$("#destination_address").addClass("is-valid").removeClass("is-invalid")
|
$("#destination_address").addClass("is-valid").removeClass("is-invalid")
|
||||||
if (d_a[0] == "X") {
|
if (d_a[0] == "X") {
|
||||||
$("#x-address-warning").show()
|
$("#x-address-warning").show()
|
||||||
@@ -323,7 +291,7 @@ const set_up_tx_sender = async function() {
|
|||||||
$("#send_xrp_payment button").prop("disabled","disabled")
|
$("#send_xrp_payment button").prop("disabled","disabled")
|
||||||
await submit_and_notify({
|
await submit_and_notify({
|
||||||
TransactionType: "Payment",
|
TransactionType: "Payment",
|
||||||
Account: sending_wallet.classicAddress,
|
Account: sending_wallet.address,
|
||||||
Destination: destination_address,
|
Destination: destination_address,
|
||||||
Amount: xrp_drops_input
|
Amount: xrp_drops_input
|
||||||
})
|
})
|
||||||
@@ -341,17 +309,16 @@ const set_up_tx_sender = async function() {
|
|||||||
|
|
||||||
await submit_and_notify({
|
await submit_and_notify({
|
||||||
TransactionType: "Payment",
|
TransactionType: "Payment",
|
||||||
Account: sending_wallet.classicAddress,
|
Account: sending_wallet.address,
|
||||||
Destination: destination_address,
|
Destination: destination_address,
|
||||||
Amount: "1000000000000000", // 1 billion XRP
|
Amount: "1000000000000000", // 1 billion XRP
|
||||||
SendMax: {
|
SendMax: {
|
||||||
value: (Math.random()*.01).toPrecision(15), // random very small amount
|
value: (Math.random()*.01).toPrecision(15), // random very small amount
|
||||||
currency: pp_sending_currency,
|
currency: pp_sending_currency,
|
||||||
issuer: pp_issuer_wallet.classicAddress
|
issuer: pp_issuer_wallet.address
|
||||||
},
|
},
|
||||||
Flags: api.txFlags.Payment.PartialPayment | api.txFlags.Universal.FullyCanonicalSig
|
Flags: xrpl.PaymentFlags.tfPartialPayment
|
||||||
})
|
})
|
||||||
// TODO: follow txFlags wherever they get moved to
|
|
||||||
$("#send_partial_payment .loader").hide()
|
$("#send_partial_payment .loader").hide()
|
||||||
$("#send_partial_payment button").prop("disabled",false)
|
$("#send_partial_payment button").prop("disabled",false)
|
||||||
}
|
}
|
||||||
@@ -375,13 +342,13 @@ const set_up_tx_sender = async function() {
|
|||||||
$("#create_escrow button").prop("disabled","disabled")
|
$("#create_escrow button").prop("disabled","disabled")
|
||||||
const escrowcreate_tx_data = await submit_and_notify({
|
const escrowcreate_tx_data = await submit_and_notify({
|
||||||
TransactionType: "EscrowCreate",
|
TransactionType: "EscrowCreate",
|
||||||
Account: sending_wallet.classicAddress,
|
Account: sending_wallet.address,
|
||||||
Destination: destination_address,
|
Destination: destination_address,
|
||||||
Amount: "1000000",
|
Amount: "1000000",
|
||||||
FinishAfter: finish_after
|
FinishAfter: finish_after
|
||||||
})
|
})
|
||||||
|
|
||||||
if (release_auto) {
|
if (escrowcreate_tx_data && release_auto) {
|
||||||
// Wait until there's a ledger with a close time > FinishAfter
|
// Wait until there's a ledger with a close time > FinishAfter
|
||||||
// to submit the EscrowFinish
|
// to submit the EscrowFinish
|
||||||
$("#escrow_progress .progress-bar").width("0%").addClass("progress-bar-animated")
|
$("#escrow_progress .progress-bar").width("0%").addClass("progress-bar-animated")
|
||||||
@@ -414,10 +381,10 @@ const set_up_tx_sender = async function() {
|
|||||||
// Future feature: submit from a different sender, just to prove that
|
// Future feature: submit from a different sender, just to prove that
|
||||||
// escrows can be finished by a third party
|
// escrows can be finished by a third party
|
||||||
await submit_and_notify({
|
await submit_and_notify({
|
||||||
Account: sending_wallet.classicAddress,
|
Account: sending_wallet.address,
|
||||||
TransactionType: "EscrowFinish",
|
TransactionType: "EscrowFinish",
|
||||||
Owner: sending_wallet.classicAddress,
|
Owner: sending_wallet.address,
|
||||||
OfferSequence: escrowcreate_tx_data.sequence
|
OfferSequence: escrowcreate_tx_data.result.Sequence
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
$("#create_escrow .loader").hide()
|
$("#create_escrow .loader").hide()
|
||||||
@@ -434,7 +401,7 @@ const set_up_tx_sender = async function() {
|
|||||||
$("#create_payment_channel button").prop("disabled","disabled")
|
$("#create_payment_channel button").prop("disabled","disabled")
|
||||||
await submit_and_notify({
|
await submit_and_notify({
|
||||||
TransactionType: "PaymentChannelCreate",
|
TransactionType: "PaymentChannelCreate",
|
||||||
Account: sending_wallet.classicAddress,
|
Account: sending_wallet.address,
|
||||||
Destination: destination_address,
|
Destination: destination_address,
|
||||||
Amount: xrp_drops_input,
|
Amount: xrp_drops_input,
|
||||||
SettleDelay: 30,
|
SettleDelay: 30,
|
||||||
@@ -459,12 +426,12 @@ const set_up_tx_sender = async function() {
|
|||||||
// Future feature: cross-currency sending with paths?
|
// Future feature: cross-currency sending with paths?
|
||||||
await submit_and_notify({
|
await submit_and_notify({
|
||||||
TransactionType: "Payment",
|
TransactionType: "Payment",
|
||||||
Account: sending_wallet.classicAddress,
|
Account: sending_wallet.address,
|
||||||
Destination: destination_address,
|
Destination: destination_address,
|
||||||
Amount: {
|
Amount: {
|
||||||
"currency": issue_code,
|
"currency": issue_code,
|
||||||
"value": issue_amount,
|
"value": issue_amount,
|
||||||
"issuer": sending_wallet.classicAddress
|
"issuer": sending_wallet.address
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
$("#send_issued_currency .loader").hide()
|
$("#send_issued_currency .loader").hide()
|
||||||
@@ -481,7 +448,7 @@ const set_up_tx_sender = async function() {
|
|||||||
$("#trust_for button").prop("disabled","disabled")
|
$("#trust_for button").prop("disabled","disabled")
|
||||||
await submit_and_notify({
|
await submit_and_notify({
|
||||||
TransactionType: "TrustSet",
|
TransactionType: "TrustSet",
|
||||||
Account: sending_wallet.classicAddress,
|
Account: sending_wallet.address,
|
||||||
LimitAmount: {
|
LimitAmount: {
|
||||||
currency: trust_currency_code,
|
currency: trust_currency_code,
|
||||||
value: trust_limit,
|
value: trust_limit,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
if (typeof module !== "undefined") {
|
if (typeof module !== "undefined") {
|
||||||
// gotta use var here because const/let are block-scoped to the if statement.
|
// gotta use var here because const/let are block-scoped to the if statement.
|
||||||
var xrpl = require('xrpl')
|
var xrpl = require('xrpl')
|
||||||
var submit_and_verify = require('../../submit-and-verify/submit-and-verify2.js').submit_and_verify
|
|
||||||
} else {
|
} else {
|
||||||
// TODO: remove when webpack is fixed
|
// TODO: remove when webpack is fixed
|
||||||
var xrpl = ripple;
|
var xrpl = ripple;
|
||||||
@@ -20,55 +19,28 @@ async function main() {
|
|||||||
|
|
||||||
// Get credentials from the Testnet Faucet -----------------------------------
|
// Get credentials from the Testnet Faucet -----------------------------------
|
||||||
console.log("Requesting addresses from the Testnet faucet...")
|
console.log("Requesting addresses from the Testnet faucet...")
|
||||||
const hot_wallet = await api.generateFaucetWallet()
|
const hot_wallet = (await api.fundWallet()).wallet
|
||||||
const cold_wallet = await api.generateFaucetWallet()
|
const cold_wallet = (await api.fundWallet()).wallet
|
||||||
|
console.log(`Got hot address ${hot_wallet.address} and cold address ${cold_wallet.address}.`)
|
||||||
console.log("Waiting until we have a validated starting sequence number...")
|
|
||||||
// If you go too soon, the funding transaction might slip back a ledger and
|
|
||||||
// then your starting Sequence number will be off. This is mostly relevant
|
|
||||||
// when you want to use a Testnet account right after getting a reply from
|
|
||||||
// the faucet.
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
await api.request({
|
|
||||||
command: "account_info",
|
|
||||||
account: cold_wallet.classicAddress,
|
|
||||||
ledger_index: "validated"
|
|
||||||
})
|
|
||||||
await api.request({
|
|
||||||
command: "account_info",
|
|
||||||
account: hot_wallet.classicAddress,
|
|
||||||
ledger_index: "validated"
|
|
||||||
})
|
|
||||||
break
|
|
||||||
} catch(e) {
|
|
||||||
if (e.data.error != 'actNotFound') throw e
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log(`Got hot address ${hot_wallet.classicAddress} and cold address ${cold_wallet.classicAddress}.`)
|
|
||||||
|
|
||||||
// Configure issuer (cold address) settings ----------------------------------
|
// Configure issuer (cold address) settings ----------------------------------
|
||||||
const cold_settings_tx = {
|
const cold_settings_tx = {
|
||||||
"TransactionType": "AccountSet",
|
"TransactionType": "AccountSet",
|
||||||
"Account": cold_wallet.classicAddress,
|
"Account": cold_wallet.address,
|
||||||
"TransferRate": 0,
|
"TransferRate": 0,
|
||||||
"TickSize": 5,
|
"TickSize": 5,
|
||||||
"Domain": "6578616D706C652E636F6D", // "example.com"
|
"Domain": "6578616D706C652E636F6D", // "example.com"
|
||||||
"SetFlag": 8 // enable Default Ripple
|
"SetFlag": xrpl.AccountSetAsfFlags.asfDefaultRipple
|
||||||
//"Flags": (api.txFlags.AccountSet.DisallowXRP |
|
//"Flags": (xrpl.AccountSetTfFlags.tfDisallowXRP |
|
||||||
// api.txFlags.AccountSet.RequireDestTag)
|
// xrpl.AccountSetTfFlags.tfRequireDestTag)
|
||||||
// TODO: update to txFlags' new location?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const cst_prepared = await api.autofill(cold_settings_tx)
|
const cst_prepared = await api.autofill(cold_settings_tx)
|
||||||
const cst_signed = cold_wallet.signTransaction(cst_prepared)
|
const cst_signed = cold_wallet.sign(cst_prepared)
|
||||||
// submit_and_verify helper function from:
|
|
||||||
// https://github.com/XRPLF/xrpl-dev-portal/tree/master/content/_code-samples/submit-and-verify/
|
|
||||||
console.log("Sending cold address AccountSet transaction...")
|
console.log("Sending cold address AccountSet transaction...")
|
||||||
const cst_result = await submit_and_verify(api, cst_signed)
|
const cst_result = await api.submitSignedReliable(cst_signed.tx_blob)
|
||||||
if (cst_result == "tesSUCCESS") {
|
if (cst_result.result.meta.TransactionResult == "tesSUCCESS") {
|
||||||
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${xrpl.computeSignedTransactionHash(cst_signed)}`)
|
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${cst_signed.hash}`)
|
||||||
} else {
|
} else {
|
||||||
throw `Error sending transaction: ${cst_result}`
|
throw `Error sending transaction: ${cst_result}`
|
||||||
}
|
}
|
||||||
@@ -78,7 +50,7 @@ async function main() {
|
|||||||
|
|
||||||
const hot_settings_tx = {
|
const hot_settings_tx = {
|
||||||
"TransactionType": "AccountSet",
|
"TransactionType": "AccountSet",
|
||||||
"Account": hot_wallet.classicAddress,
|
"Account": hot_wallet.address,
|
||||||
"Domain": "6578616D706C652E636F6D", // "example.com"
|
"Domain": "6578616D706C652E636F6D", // "example.com"
|
||||||
"SetFlag": 2 // enable Require Auth so we can't use trust lines that users
|
"SetFlag": 2 // enable Require Auth so we can't use trust lines that users
|
||||||
// make to the hot address, even by accident.
|
// make to the hot address, even by accident.
|
||||||
@@ -87,13 +59,13 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const hst_prepared = await api.autofill(hot_settings_tx)
|
const hst_prepared = await api.autofill(hot_settings_tx)
|
||||||
const hst_signed = hot_wallet.signTransaction(hst_prepared)
|
const hst_signed = hot_wallet.sign(hst_prepared)
|
||||||
console.log("Sending hot address AccountSet transaction...")
|
console.log("Sending hot address AccountSet transaction...")
|
||||||
const hst_result = await submit_and_verify(api, hst_signed)
|
const hst_result = await api.submitSignedReliable(hst_signed.tx_blob)
|
||||||
if (hst_result == "tesSUCCESS") {
|
if (hst_result.result.meta.TransactionResult == "tesSUCCESS") {
|
||||||
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${xrpl.computeSignedTransactionHash(hst_signed)}`)
|
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${hst_signed.hash}`)
|
||||||
} else {
|
} else {
|
||||||
throw `Error sending transaction: ${hst_result}`
|
throw `Error sending transaction: ${hst_result.result.meta.TransactionResult}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -101,22 +73,22 @@ async function main() {
|
|||||||
const currency_code = "FOO"
|
const currency_code = "FOO"
|
||||||
const trust_set_tx = {
|
const trust_set_tx = {
|
||||||
"TransactionType": "TrustSet",
|
"TransactionType": "TrustSet",
|
||||||
"Account": hot_wallet.classicAddress,
|
"Account": hot_wallet.address,
|
||||||
"LimitAmount": {
|
"LimitAmount": {
|
||||||
"currency": currency_code,
|
"currency": currency_code,
|
||||||
"issuer": cold_wallet.classicAddress,
|
"issuer": cold_wallet.address,
|
||||||
"value": "10000000000" // Large limit, arbitrarily chosen
|
"value": "10000000000" // Large limit, arbitrarily chosen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ts_prepared = await api.autofill(trust_set_tx)
|
const ts_prepared = await api.autofill(trust_set_tx)
|
||||||
const ts_signed = hot_wallet.signTransaction(ts_prepared)
|
const ts_signed = hot_wallet.sign(ts_prepared)
|
||||||
console.log("Creating trust line from hot address to issuer...")
|
console.log("Creating trust line from hot address to issuer...")
|
||||||
const ts_result = await submit_and_verify(api, ts_signed)
|
const ts_result = await api.submitSignedReliable(ts_signed.tx_blob)
|
||||||
if (ts_result == "tesSUCCESS") {
|
if (ts_result.result.meta.TransactionResult == "tesSUCCESS") {
|
||||||
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${xrpl.computeSignedTransactionHash(ts_signed)}`)
|
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${ts_signed.hash}`)
|
||||||
} else {
|
} else {
|
||||||
throw `Error sending transaction: ${ts_result}`
|
throw `Error sending transaction: ${ts_result.result.meta.TransactionResult}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -124,31 +96,30 @@ async function main() {
|
|||||||
const issue_quantity = "3840"
|
const issue_quantity = "3840"
|
||||||
const send_token_tx = {
|
const send_token_tx = {
|
||||||
"TransactionType": "Payment",
|
"TransactionType": "Payment",
|
||||||
"Account": cold_wallet.classicAddress,
|
"Account": cold_wallet.address,
|
||||||
"Amount": {
|
"Amount": {
|
||||||
"currency": currency_code,
|
"currency": currency_code,
|
||||||
"value": issue_quantity,
|
"value": issue_quantity,
|
||||||
"issuer": cold_wallet.classicAddress
|
"issuer": cold_wallet.address
|
||||||
},
|
},
|
||||||
"Destination": hot_wallet.classicAddress
|
"Destination": hot_wallet.address
|
||||||
}
|
}
|
||||||
|
|
||||||
const pay_prepared = await api.autofill(send_token_tx)
|
const pay_prepared = await api.autofill(send_token_tx)
|
||||||
const pay_signed = cold_wallet.signTransaction(pay_prepared)
|
const pay_signed = cold_wallet.sign(pay_prepared)
|
||||||
// submit_and_verify helper from _code-samples/submit-and-verify
|
console.log(`Sending ${issue_quantity} ${currency_code} to ${hot_wallet.address}...`)
|
||||||
console.log(`Sending ${issue_quantity} ${currency_code} to ${hot_wallet.classicAddress}...`)
|
const pay_result = await api.submitSignedReliable(pay_signed.tx_blob)
|
||||||
const pay_result = await submit_and_verify(api, pay_signed)
|
if (pay_result.result.meta.TransactionResult == "tesSUCCESS") {
|
||||||
if (pay_result == "tesSUCCESS") {
|
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${pay_signed.hash}`)
|
||||||
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${xrpl.computeSignedTransactionHash(pay_signed)}`)
|
|
||||||
} else {
|
} else {
|
||||||
throw `Error sending transaction: ${pay_result}`
|
throw `Error sending transaction: ${pay_result.result.meta.TransactionResult}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check balances ------------------------------------------------------------
|
// Check balances ------------------------------------------------------------
|
||||||
console.log("Getting hot address balances...")
|
console.log("Getting hot address balances...")
|
||||||
const hot_balances = await api.request({
|
const hot_balances = await api.request({
|
||||||
command: "account_lines",
|
command: "account_lines",
|
||||||
account: hot_wallet.classicAddress,
|
account: hot_wallet.address,
|
||||||
ledger_index: "validated"
|
ledger_index: "validated"
|
||||||
})
|
})
|
||||||
console.log(hot_balances.result)
|
console.log(hot_balances.result)
|
||||||
@@ -156,9 +127,9 @@ async function main() {
|
|||||||
console.log("Getting cold address balances...")
|
console.log("Getting cold address balances...")
|
||||||
const cold_balances = await api.request({
|
const cold_balances = await api.request({
|
||||||
command: "gateway_balances",
|
command: "gateway_balances",
|
||||||
account: cold_wallet.classicAddress,
|
account: cold_wallet.address,
|
||||||
ledger_index: "validated",
|
ledger_index: "validated",
|
||||||
hotwallet: [hot_wallet.classicAddress]
|
hotwallet: [hot_wallet.address]
|
||||||
})
|
})
|
||||||
console.log(JSON.stringify(cold_balances.result, null, 2))
|
console.log(JSON.stringify(cold_balances.result, null, 2))
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ let my_seq = 21404872
|
|||||||
|
|
||||||
// Provide *all* required fields before signing a transaction
|
// Provide *all* required fields before signing a transaction
|
||||||
const txJSON = {
|
const txJSON = {
|
||||||
"Account": my_wallet.classicAddress,
|
"Account": my_wallet.address,
|
||||||
"TransactionType":"Payment",
|
"TransactionType":"Payment",
|
||||||
"Destination":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Destination":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"Amount":"13000000",
|
"Amount":"13000000",
|
||||||
@@ -19,7 +19,7 @@ const txJSON = {
|
|||||||
"Sequence": my_seq
|
"Sequence": my_seq
|
||||||
}
|
}
|
||||||
|
|
||||||
const tx_blob = my_wallet.signTransaction(txJSON)
|
const signed = my_wallet.sign(txJSON)
|
||||||
|
|
||||||
console.log("tx_blob is:", tx_blob)
|
console.log("tx_blob is:", signed.tx_blob)
|
||||||
console.log("tx hash is:", xrpl.computeBinaryTransactionSigningHash(tx_blob))
|
console.log("tx hash is:", signed.hash)
|
||||||
|
|||||||
@@ -8,30 +8,13 @@ api.connect()
|
|||||||
api.on('connected', async () => {
|
api.on('connected', async () => {
|
||||||
|
|
||||||
// Get credentials from the Testnet Faucet -----------------------------------
|
// Get credentials from the Testnet Faucet -----------------------------------
|
||||||
const wallet = await api.generateFaucetWallet()
|
console.log("Getting a wallet from the Testnet faucet...")
|
||||||
|
const {wallet, balance} = await api.fundWallet()
|
||||||
console.log("Waiting until we have a validated starting sequence number...")
|
|
||||||
// If you go too soon, the funding transaction might slip back a ledger and
|
|
||||||
// then your starting Sequence number will be off. This is mostly relevant
|
|
||||||
// when you want to use a Testnet account right after getting a reply from
|
|
||||||
// the faucet.
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
await api.request({
|
|
||||||
command: "account_info",
|
|
||||||
account: wallet.classicAddress,
|
|
||||||
ledger_index: "validated"
|
|
||||||
})
|
|
||||||
break
|
|
||||||
} catch(e) {
|
|
||||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare transaction -------------------------------------------------------
|
// Prepare transaction -------------------------------------------------------
|
||||||
const prepared = await api.autofill({
|
const prepared = await api.autofill({
|
||||||
"TransactionType": "Payment",
|
"TransactionType": "Payment",
|
||||||
"Account": wallet.classicAddress,
|
"Account": wallet.address,
|
||||||
"Amount": xrpl.xrpToDrops("22"),
|
"Amount": xrpl.xrpToDrops("22"),
|
||||||
"Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"
|
"Destination": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"
|
||||||
})
|
})
|
||||||
@@ -41,10 +24,9 @@ api.on('connected', async () => {
|
|||||||
console.log("Transaction expires after ledger:", max_ledger)
|
console.log("Transaction expires after ledger:", max_ledger)
|
||||||
|
|
||||||
// Sign prepared instructions ------------------------------------------------
|
// Sign prepared instructions ------------------------------------------------
|
||||||
const tx_blob = wallet.signTransaction(prepared)
|
const signed = wallet.sign(prepared)
|
||||||
const txID = xrpl.computeSignedTransactionHash(tx_blob)
|
console.log("Identifying hash:", signed.hash)
|
||||||
console.log("Identifying hash:", txID)
|
console.log("Signed blob:", signed.tx_blob)
|
||||||
console.log("Signed blob:", tx_blob)
|
|
||||||
|
|
||||||
// Submit signed blob --------------------------------------------------------
|
// Submit signed blob --------------------------------------------------------
|
||||||
// The earliest ledger a transaction could appear in is the first ledger
|
// The earliest ledger a transaction could appear in is the first ledger
|
||||||
@@ -52,7 +34,7 @@ api.on('connected', async () => {
|
|||||||
const min_ledger = (await api.getLedgerIndex()) + 1
|
const min_ledger = (await api.getLedgerIndex()) + 1
|
||||||
const result = await api.request({
|
const result = await api.request({
|
||||||
"command": "submit",
|
"command": "submit",
|
||||||
"tx_blob": tx_blob
|
"tx_blob": signed.tx_blob
|
||||||
})
|
})
|
||||||
console.log("Tentative result code:", result.result.engine_result)
|
console.log("Tentative result code:", result.result.engine_result)
|
||||||
console.log("Tentative result message:", result.result.engine_result_message)
|
console.log("Tentative result message:", result.result.engine_result_message)
|
||||||
@@ -61,10 +43,10 @@ api.on('connected', async () => {
|
|||||||
let has_final_status = false
|
let has_final_status = false
|
||||||
api.request({
|
api.request({
|
||||||
"command": "subscribe",
|
"command": "subscribe",
|
||||||
"accounts": [wallet.classicAddress]
|
"accounts": [wallet.address]
|
||||||
})
|
})
|
||||||
api.connection.on("transaction", (event) => {
|
api.connection.on("transaction", (event) => {
|
||||||
if (event.transaction.hash == txID) {
|
if (event.transaction.hash == signed.hash) {
|
||||||
console.log("Transaction has executed!", event)
|
console.log("Transaction has executed!", event)
|
||||||
has_final_status = true
|
has_final_status = true
|
||||||
}
|
}
|
||||||
@@ -87,7 +69,7 @@ api.on('connected', async () => {
|
|||||||
try {
|
try {
|
||||||
const tx = await api.request({
|
const tx = await api.request({
|
||||||
command: "tx",
|
command: "tx",
|
||||||
transaction: txID,
|
transaction: signed.hash,
|
||||||
min_ledger: min_ledger,
|
min_ledger: min_ledger,
|
||||||
max_ledger: max_ledger
|
max_ledger: max_ledger
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user