code updates for xrpl.js 2.0 beta 5

This commit is contained in:
mDuo13
2021-10-12 19:02:44 -07:00
parent 1c9516aa68
commit 58a9c25c2b
10 changed files with 123 additions and 391 deletions

View File

@@ -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)
} }
/** /**

View File

@@ -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
}
}

View File

@@ -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 {

View File

@@ -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()

View File

@@ -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>`
) )

View File

@@ -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")

View File

@@ -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,

View File

@@ -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))

View File

@@ -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)

View File

@@ -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
}) })