Tx sender: feature complete?

- Send partial payments works
- Shows progress bars for partial payment setup, escrow auto-release
- Actually tracks sending address's XRP balance
This commit is contained in:
mDuo13
2019-04-23 17:25:40 -07:00
parent 668fbe2fba
commit 032e2d28b4
2 changed files with 198 additions and 28 deletions

View File

@@ -6,9 +6,11 @@ const set_up_tx_sender = async function() {
FAUCET_URL = "https://faucet.altnet.rippletest.net/accounts"
TESTNET_URL = "wss://s.altnet.rippletest.net:51233"
let sending_address = ""
let sending_secret = ""
let xrp_balance = 0
let connection_ready = false
let sending_address
let sending_secret
let xrp_balance
console.debug("Getting a sending address from the faucet...")
@@ -40,10 +42,12 @@ const set_up_tx_sender = async function() {
api = new ripple.RippleAPI({server: TESTNET_URL})
api.on('connected', () => {
connection_ready = true
$("#connection-status-item").text("Connected")
$("#connection-status-item").removeClass("disabled").addClass("active")
})
api.on('disconnected', (code) => {
connection_ready = false
$("#connection-status-item").text("Not connected")
$("#connection-status-item").removeClass("active").addClass("disabled")
})
@@ -54,6 +58,11 @@ const set_up_tx_sender = async function() {
// Generic Transaction Submission
//////////////////////////////////////////////////////////////////////////////
// Helper function for await-able timeouts
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
const INTERVAL = 1000 // milliseconds to wait for new ledger versions
async function verify_transaction(hash, options) {
try {
@@ -73,13 +82,22 @@ const set_up_tx_sender = async function() {
}
}
async function submit_and_verify(tx_object) {
async function update_xrp_balance() {
balances = await api.getBalances(sending_address, {currency: "XRP"})
$("#balance-item").text(balances[0].value)
}
async function submit_and_verify(tx_object, use_secret, silent) {
if (use_secret === undefined) {
use_secret = sending_secret
}
try {
// Auto-fill fields like Fee and Sequence
prepared = await api.prepareTransaction(tx_object)
console.debug("Prepared:", prepared)
} catch(error) {
console.log(error)
alert("Error preparing tx: "+error)
if (!silent) { alert("Error preparing tx: "+error) }
return
}
@@ -92,11 +110,11 @@ const set_up_tx_sender = async function() {
try {
// Sign, submit
sign_response = api.sign(prepared.txJSON, sending_secret)
sign_response = api.sign(prepared.txJSON, use_secret)
await api.submit(sign_response.signedTransaction)
} catch (error) {
console.log(error)
alert("Error signing & submitting tx: "+error)
if (!silent) { alert("Error signing & submitting tx: "+error) }
return
}
@@ -107,17 +125,121 @@ const set_up_tx_sender = async function() {
// TODO: more "notification-like" system
// TODO: output should link to a tx lookup/explainer
if (final_result === "tesSUCCESS") {
alert("Tx succeeded (hash:"+sign_response.id+")")
if (!silent) { alert("Tx succeeded (hash:"+sign_response.id+")") }
} else {
alert("Tx failed w/ code "+final_result+" (hash: "+sign_response.id+")")
if (!silent) { alert("Tx failed w/ code "+final_result+" (hash: "+sign_response.id+")") }
}
update_xrp_balance()
return data
} catch(error) {
alert("Error submitting tx: "+error)
console.log(error)
if (!silent) { alert("Error submitting tx: "+error) }
}
}
//////////////////////////////////////////////////////////////////////////////
// Issuer Setup for Partial Payments
// (Partial payments must involve at least one issued currency, so we set up
// an issuer for a fake currency to ripple through.)
//////////////////////////////////////////////////////////////////////////////
let pp_issuer_address
let pp_sending_currency = "BAR"
async function set_up_for_partial_payments() {
while (!connection_ready) {
console.debug("... waiting for connection before doing partial payment setup")
await timeout(200)
}
console.debug("Starting partial payment setup...")
$("#pp_progress .progress-bar").addClass("progress-bar-animated")
// 1. Get a funded address to use as issuer
let pp_issuer_secret
try {
const faucet_response = await ($.ajax({
url: FAUCET_URL,
type: 'POST',
dataType: 'json'
}))
pp_issuer_address = faucet_response.account.address
pp_issuer_secret = faucet_response.account.secret
} catch(error) {
console.log("Error getting issuer address for partial payments:", error)
return
}
$("#pp_progress .progress-bar").width("20%")
// 2. Set DefaultRipple on issuer
let resp = await submit_and_verify({
TransactionType: "AccountSet",
Account: pp_issuer_address,
SetFlag: 8
}, pp_issuer_secret, true)
if (resp === undefined) {
console.log("Couldn't set DefaultRipple for partial payment issuer")
return
}
$("#pp_progress .progress-bar").width("40%")
// 3. Make a trust line from sending address to issuer
resp = await submit_and_verify({
TransactionType: "TrustSet",
Account: sending_address,
LimitAmount: {
currency: pp_sending_currency,
value: "1000000000", // arbitrarily, 1 billion fake currency
issuer: pp_issuer_address
}
}, sending_secret, true)
if (resp === undefined) {
console.log("Error making trust line to partial payment issuer")
return
}
$("#pp_progress .progress-bar").width("60%")
// 4. Issue fake currency to main sending address
resp = await submit_and_verify({
TransactionType: "Payment",
Account: pp_issuer_address,
Destination: sending_address,
Amount: {
currency: pp_sending_currency,
value: "1000000000",
issuer: pp_issuer_address
}
}, pp_issuer_secret, true)
if (resp === undefined) {
console.log("Error sending fake currency from partial payment issuer")
return
}
$("#pp_progress .progress-bar").width("80%")
// 5. Place offer to buy issued currency for XRP
// When sending the partial payment, the sender consumes their own offer (!)
// so they end up paying themselves issued currency then delivering XRP.
resp = await submit_and_verify({
TransactionType: "OfferCreate",
Account: sending_address,
TakerGets: "1000000000000000", // 1 billion XRP
TakerPays: {
currency: pp_sending_currency,
value: "1000000000",
issuer: pp_issuer_address
}
}, sending_secret, true)
if (resp === undefined) {
console.log("Error placing order to enable partial payments")
return
}
$("#pp_progress .progress-bar").width("100%").removeClass("progress-bar-animated")
$("#pp_progress").hide()
// Done. Enable "Send Partial Payment" button
console.log("Done getting ready to send partial payments.")
$("#send_partial_payment button").prop("disabled",false)
$("#send_partial_payment button").attr("title", "")
}
set_up_for_partial_payments()
//////////////////////////////////////////////////////////////////////////////
// Button Handlers
@@ -142,7 +264,39 @@ const set_up_tx_sender = async function() {
$("#send_xrp_payment button").click(on_click_send_xrp_payment)
// 2. Send Partial Payment Handler ---------------------------------------
// TODO
async function on_click_send_partial_payment(event) {
const destination_address = $("#destination_address").val()
$("#send_partial_payment .loader").show()
$("#send_partial_payment button").attr("disabled","disabled")
// const path_find_result = await api.request("ripple_path_find", {
// source_account: sending_address,
// destination_account: destination_address,
// destination_amount: "-1", // as much XRP as possible
// source_currencies: [{currency: pp_sending_currency, issuer: pp_issuer_address}]
// })
// console.log("Path find result:", path_find_result)
// use_path = path_find_result.alternatives[0].paths_computed
await submit_and_verify({
TransactionType: "Payment",
Account: sending_address,
Destination: destination_address,
Amount: "1000000000000000", // 1 billion XRP
SendMax: {
value: String(Math.random()*.01), // random very small amount
currency: pp_sending_currency,
issuer: pp_issuer_address
},
Flags: 0x80020000 // tfPartialPayment | tfFullyCanonicalSig
/*,
Paths: use_path*/
})
$("#send_partial_payment .loader").hide()
$("#send_partial_payment button").attr("disabled",false)
}
$("#send_partial_payment button").click(on_click_send_partial_payment)
// 3. Create Escrow Handler ----------------------------------------------
async function on_click_create_escrow(event) {
@@ -169,22 +323,29 @@ const set_up_tx_sender = async function() {
if (release_auto) {
// Wait until there's a ledger with a close time > FinishAfter
// Check at the FinishAfter time, then approx. every INTERVAL thereafter
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
let delay_ms = (finish_after - api.iso8601ToRippleTime(Date())) * 1000
let latestCloseTimeRipple = null
while (delay_ms > 0) {
// console.log("Waiting another "+delay_ms+"ms for escrow to be ready")
await timeout(delay_ms)
latestCloseTimeRipple = api.iso8601ToRippleTime((await api.getLedger()).closeTime)
if (latestCloseTimeRipple > finish_after) {
delay_ms = 0
} else {
delay_ms = 1000
// to submit the EscrowFinish
$("#escrow_progress .progress-bar").width("0%").addClass("progress-bar-animated")
$("#escrow_progress").show()
let seconds_left
let pct_done
let latestCloseTimeRipple
while (true) {
seconds_left = (finish_after - api.iso8601ToRippleTime(Date()))
pct_done = Math.min(99, Math.max(0, (1-(seconds_left / duration_seconds)) * 100))
$("#escrow_progress .progress-bar").width(pct_done+"%")
if (seconds_left <= 0) {
// System time has advanced past FinishAfter. But is there a new
// enough validated ledger?
latestCloseTimeRipple = api.iso8601ToRippleTime((await api.getLedger()).closeTime)
if (latestCloseTimeRipple > finish_after) {
$("#escrow_progress .progress-bar").width("100%").removeClass("progress-bar-animated")
break
}
}
// Update the progress bar & check again in 1 second.
await timeout(1000)
}
$("#escrow_progress").hide()
// Now submit the EscrowFinish
// Future feature: submit from a different sender, just to prove that