mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-20 11:45:50 +00:00
Tx Sender: Implement all types except partial payment
This commit is contained in:
@@ -1137,6 +1137,7 @@ a.current {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.page-test-net .throbber,
|
.page-test-net .throbber,
|
||||||
|
.page-tx-sender .throbber,
|
||||||
.interactive-block .throbber {
|
.interactive-block .throbber {
|
||||||
-webkit-animation: rotating 1s linear infinite;
|
-webkit-animation: rotating 1s linear infinite;
|
||||||
-moz-animation: rotating 1s linear infinite;
|
-moz-animation: rotating 1s linear infinite;
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
const set_up_tx_sender = async function() {
|
const set_up_tx_sender = async function() {
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Connection / Setup
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
FAUCET_URL = "https://faucet.altnet.rippletest.net/accounts"
|
FAUCET_URL = "https://faucet.altnet.rippletest.net/accounts"
|
||||||
TESTNET_URL = "wss://s.altnet.rippletest.net:51233"
|
TESTNET_URL = "wss://s.altnet.rippletest.net:51233"
|
||||||
|
|
||||||
@@ -46,10 +50,228 @@ const set_up_tx_sender = async function() {
|
|||||||
console.log("Connecting to Test Net WebSocket...")
|
console.log("Connecting to Test Net WebSocket...")
|
||||||
api.connect()
|
api.connect()
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Generic Transaction Submission
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const INTERVAL = 1000 // milliseconds to wait for new ledger versions
|
||||||
|
async function verify_transaction(hash, options) {
|
||||||
|
try {
|
||||||
|
data = await api.getTransaction(hash, options)
|
||||||
|
return data
|
||||||
|
} catch(error) {
|
||||||
|
/* If transaction not in latest validated ledger,
|
||||||
|
try again until max ledger hit */
|
||||||
|
if (error instanceof api.errors.PendingLedgerVersionError) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
setTimeout(() => verify_transaction(hash, options)
|
||||||
|
.then(resolve, reject), INTERVAL)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submit_and_verify(tx_object) {
|
||||||
|
try {
|
||||||
|
// Auto-fill fields like Fee and Sequence
|
||||||
|
prepared = await api.prepareTransaction(tx_object)
|
||||||
|
} catch(error) {
|
||||||
|
console.log(error)
|
||||||
|
alert("Error preparing tx: "+error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine first and last ledger the tx could be validated in *BEFORE*
|
||||||
|
// signing it.
|
||||||
|
const options = {
|
||||||
|
minLedgerVersion: (await api.getLedger()).ledgerVersion,
|
||||||
|
maxLedgerVersion: prepared.instructions.maxLedgerVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Sign, submit
|
||||||
|
sign_response = api.sign(prepared.txJSON, sending_secret)
|
||||||
|
await api.submit(sign_response.signedTransaction)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
alert("Error signing & submitting tx: "+error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for tx to be in a validated ledger or to expire
|
||||||
|
try {
|
||||||
|
const data = await verify_transaction(sign_response.id, options)
|
||||||
|
const final_result = data.outcome.result
|
||||||
|
// 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+")")
|
||||||
|
} else {
|
||||||
|
alert("Tx failed w/ code "+final_result+" (hash: "+sign_response.id+")")
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
} catch(error) {
|
||||||
|
alert("Error submitting tx: "+error)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Button Handlers
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// 1. Send XRP Payment Handler -------------------------------------------
|
||||||
|
async function on_click_send_xrp_payment(event) {
|
||||||
|
const destination_address = $("#destination_address").val()
|
||||||
|
const xrp_drops_input = $("#send_xrp_payment_amount").val()
|
||||||
|
$("#send_xrp_payment .loader").show()
|
||||||
|
$("#send_xrp_payment button").attr("disabled","disabled")
|
||||||
|
await submit_and_verify({
|
||||||
|
TransactionType: "Payment",
|
||||||
|
Account: sending_address,
|
||||||
|
Destination: destination_address,
|
||||||
|
Amount: xrp_drops_input
|
||||||
|
})
|
||||||
|
$("#send_xrp_payment .loader").hide()
|
||||||
|
$("#send_xrp_payment button").attr("disabled",false)
|
||||||
|
|
||||||
|
}
|
||||||
|
$("#send_xrp_payment button").click(on_click_send_xrp_payment)
|
||||||
|
|
||||||
|
// 2. Send Partial Payment Handler ---------------------------------------
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// 3. Create Escrow Handler ----------------------------------------------
|
||||||
|
async function on_click_create_escrow(event) {
|
||||||
|
const destination_address = $("#destination_address").val()
|
||||||
|
const duration_seconds_txt = $("#create_escrow_duration_seconds").val()
|
||||||
|
const release_auto = $("#create_escrow_release_automatically").prop("checked")
|
||||||
|
|
||||||
|
const duration_seconds = parseInt(duration_seconds_txt, 10)
|
||||||
|
if (duration_seconds === NaN || duration_seconds < 1) {
|
||||||
|
alert("Error: Escrow duration must be a positive number of seconds")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const finish_after = api.iso8601ToRippleTime(Date()) + duration_seconds
|
||||||
|
|
||||||
|
$("#create_escrow .loader").show()
|
||||||
|
$("#create_escrow button").attr("disabled","disabled")
|
||||||
|
const escrowcreate_tx_data = await submit_and_verify({
|
||||||
|
TransactionType: "EscrowCreate",
|
||||||
|
Account: sending_address,
|
||||||
|
Destination: destination_address,
|
||||||
|
Amount: "1000000",
|
||||||
|
FinishAfter: finish_after
|
||||||
|
})
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now submit the EscrowFinish
|
||||||
|
// Future feature: submit from a different sender, just to prove that
|
||||||
|
// escrows can be finished by a third party
|
||||||
|
await submit_and_verify({
|
||||||
|
Account: sending_address,
|
||||||
|
TransactionType: "EscrowFinish",
|
||||||
|
Owner: sending_address,
|
||||||
|
OfferSequence: escrowcreate_tx_data.sequence
|
||||||
|
})
|
||||||
|
}
|
||||||
|
$("#create_escrow .loader").hide()
|
||||||
|
$("#create_escrow button").attr("disabled",false)
|
||||||
|
}
|
||||||
|
$("#create_escrow button").click(on_click_create_escrow)
|
||||||
|
|
||||||
|
// 4. Create Payment Channel Handler -------------------------------------
|
||||||
|
async function on_click_create_payment_channel(event) {
|
||||||
|
const destination_address = $("#destination_address").val()
|
||||||
|
const xrp_drops_input = $("#create_payment_channel_amount").val()
|
||||||
|
const pubkey = api.deriveKeypair(sending_secret).publicKey
|
||||||
|
$("#create_payment_channel .loader").show()
|
||||||
|
$("#create_payment_channel button").attr("disabled","disabled")
|
||||||
|
await submit_and_verify({
|
||||||
|
TransactionType: "PaymentChannelCreate",
|
||||||
|
Account: sending_address,
|
||||||
|
Destination: destination_address,
|
||||||
|
Amount: xrp_drops_input,
|
||||||
|
SettleDelay: 30,
|
||||||
|
PublicKey: pubkey
|
||||||
|
})
|
||||||
|
$("#create_payment_channel .loader").hide()
|
||||||
|
$("#create_payment_channel button").attr("disabled",false)
|
||||||
|
|
||||||
|
// Future feature: figure out channel ID and enable a button that creates
|
||||||
|
// valid claims for the given payment channel to help test redeeming
|
||||||
|
}
|
||||||
|
$("#create_payment_channel button").click(on_click_create_payment_channel)
|
||||||
|
|
||||||
|
|
||||||
|
// 5. Send Issued Currency Handler ---------------------------------------
|
||||||
|
async function on_click_send_issued_currency(event) {
|
||||||
|
const destination_address = $("#destination_address").val()
|
||||||
|
const issue_amount = $("#send_issued_currency_amount").val()
|
||||||
|
const issue_code = $("#send_issued_currency_code").text()
|
||||||
|
$("#send_issued_currency .loader").show()
|
||||||
|
$("#send_issued_currency button").attr("disabled","disabled")
|
||||||
|
// Future feature: cross-currency sending with paths?
|
||||||
|
await submit_and_verify({
|
||||||
|
TransactionType: "Payment",
|
||||||
|
Account: sending_address,
|
||||||
|
Destination: destination_address,
|
||||||
|
Amount: {
|
||||||
|
"currency": issue_code,
|
||||||
|
"value": issue_amount,
|
||||||
|
"issuer": sending_address
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$("#send_issued_currency .loader").hide()
|
||||||
|
$("#send_issued_currency button").attr("disabled",false)
|
||||||
|
}
|
||||||
|
$("#send_issued_currency button").click(on_click_send_issued_currency)
|
||||||
|
|
||||||
|
// 6. Trust For Handler
|
||||||
|
async function on_trust_for(event) {
|
||||||
|
const destination_address = $("#destination_address").val()
|
||||||
|
const trust_limit = $("#trust_for_amount").val()
|
||||||
|
const trust_currency_code = $("#trust_for_currency_code").text()
|
||||||
|
$("#trust_for .loader").show()
|
||||||
|
$("#trust_for button").attr("disabled","disabled")
|
||||||
|
await submit_and_verify({
|
||||||
|
TransactionType: "TrustSet",
|
||||||
|
Account: sending_address,
|
||||||
|
LimitAmount: {
|
||||||
|
currency: trust_currency_code,
|
||||||
|
value: trust_limit,
|
||||||
|
issuer: destination_address
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$("#trust_for .loader").hide()
|
||||||
|
$("#trust_for button").attr("disabled",false)
|
||||||
|
}
|
||||||
|
$("#trust_for button").click(on_trust_for)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$(document).ready( function() {
|
$(document).ready( function() {
|
||||||
console.log("doc ready!")
|
|
||||||
set_up_tx_sender()
|
set_up_tx_sender()
|
||||||
} )
|
} )
|
||||||
|
|||||||
@@ -29,28 +29,34 @@
|
|||||||
|
|
||||||
<form>
|
<form>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="destAddress">Destination Address</label>
|
<label for="destination_address">Destination Address</label>
|
||||||
<input type="text" class="form-control" id="destAddress" aria-describedby="destAddressHelp" value="rUCzEr6jrEyMpjhs4wSdQdz4g8Y382NxfM" />
|
<input type="text" class="form-control" id="destination_address" aria-describedby="destination_address_help" value="rUCzEr6jrEyMpjhs4wSdQdz4g8Y382NxfM" />
|
||||||
<small id="destAddressHelp" class="form-text text-muted">Send transactions to this XRP Test Net address</small>
|
<small id="destination_address_help" class="form-text text-muted">Send transactions to this XRP Test Net address</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3>Send Transaction</h3>
|
<h3>Send Transaction</h3>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" id="send_xrp_payment">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<button class="btn btn-primary form-control" type="button" id="sendPaymentButton">Send XRP Payment</button>
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text loader" style="display: none"><img class="throbber" src="assets/img/rippleThrobber.png" /></span>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary form-control" type="button" id="send_xrp_payment_btn">Send XRP Payment</button>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<input class="form-control" type="number" aria-describedby="payment-drops-of-xrp" value="100000" min="1" max="10000000000" />
|
<input id="send_xrp_payment_amount" class="form-control" type="number" aria-describedby="send_xrp_payment_amount_help" value="100000" min="1" max="10000000000" />
|
||||||
<span class="input-group-text" id="payment-drops-of-xrp">drops of XRP</span>
|
<span class="input-group-text" id="send_xrp_payment_amount_help">drops of XRP</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<small class="form-text text-muted">Send a <a href="send-xrp.html">simple XRP-to-XRP payment</a>.</small>
|
<small class="form-text text-muted">Send a <a href="send-xrp.html">simple XRP-to-XRP payment</a>.</small>
|
||||||
</div><!-- /.form group for payment -->
|
</div><!-- /#send_xrp_payment -->
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text loader" style="display: none"><img class="throbber" src="assets/img/rippleThrobber.png" /></span>
|
||||||
|
</div>
|
||||||
<button class="btn btn-primary form-control" type="button" id="sendPartialPaymentButton">Send Partial Payment</button>
|
<button class="btn btn-primary form-control" type="button" id="sendPartialPaymentButton">Send Partial Payment</button>
|
||||||
</div>
|
</div>
|
||||||
<small class="form-text text-muted">Delivers a small amount of XRP with a large <code>Amount</code> value, to test your handling of <a href="partial-payments.html">partial payments</a>.</small>
|
<small class="form-text text-muted">Delivers a small amount of XRP with a large <code>Amount</code> value, to test your handling of <a href="partial-payments.html">partial payments</a>.</small>
|
||||||
@@ -58,17 +64,20 @@
|
|||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" id="create_escrow">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<button class="btn btn-primary form-control" type="button" id="createEscrowButton">Create Escrow</button>
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text loader" style="display: none"><img class="throbber" src="assets/img/rippleThrobber.png" /></span>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary form-control" type="button" id="create_escrow_btn">Create Escrow</button>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<span class="input-group-text">for</span>
|
<span class="input-group-text">for</span>
|
||||||
<input class="form-control" type="number" value="60" min="5" max="10000" id="escrowSecondsFromNow" />
|
<input class="form-control" type="number" value="60" min="5" max="10000" id="create_escrow_duration_seconds" />
|
||||||
<span class="input-group-text">seconds</span>
|
<span class="input-group-text">seconds</span>
|
||||||
<span class="input-group-text">
|
<span class="input-group-text">
|
||||||
(
|
(
|
||||||
<input type="checkbox" id="escrowAutomatic" checked="checked" />
|
<input type="checkbox" id="create_escrow_release_automatically" value="1" />
|
||||||
<label class="form-check-label" for="escrowAutomatic">Release automatically</label>)
|
<label class="form-check-label" for="create_escrow_release_automatically">Release automatically</label>)
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,13 +86,16 @@
|
|||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" id="create_payment_channel">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<button class="btn btn-primary form-control" type="button" id="createPayChanButton">Create Payment Channel</button>
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text loader" style="display: none"><img class="throbber" src="assets/img/rippleThrobber.png" /></span>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary form-control" type="button" id="create_payment_channel_btn">Create Payment Channel</button>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<span class="input-group-text">funded with</span>
|
<span class="input-group-text">funded with</span>
|
||||||
<input class="form-control" type="number" aria-describedby="paychan-drops-of-xrp" value="100000" min="1" max="10000000000" />
|
<input id="create_payment_channel_amount" class="form-control" type="number" aria-describedby="create_payment_channel_amount_help" value="100000" min="1" max="10000000000" />
|
||||||
<span class="input-group-text" id="paychan-drops-of-xrp">drops of XRP</span>
|
<span class="input-group-text" id="create_payment_channel_amount_help">drops of XRP</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<small class="form-text text-muted">Create a payment channel.</small>
|
<small class="form-text text-muted">Create a payment channel.</small>
|
||||||
@@ -91,25 +103,31 @@
|
|||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" id="send_issued_currency">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<button class="btn btn-primary form-control" type="button" id="sendIssuedCurrencyButton">Send Issued Currency</button>
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text loader" style="display: none"><img class="throbber" src="assets/img/rippleThrobber.png" /></span>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary form-control" type="button" id="send_issued_currency_btn">Send Issued Currency</button>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<input id="issued-currency-amount" class="form-control" type="number" value="100" />
|
<input id="send_issued_currency_amount" class="form-control" type="text" value="100" /><!-- Note: HTML limits "number" inputs to IEEE 764 double precision, which isn't enough for the full range of issued currency amounts -->
|
||||||
<span class="input-group-text" id="issued-currency-code">FOO</span>
|
<span class="input-group-text" id="send_issued_currency_code">FOO</span><!-- TODO: custom currency codes -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<small class="form-text text-muted">Your destination address should trust <span class="sending-address-item">(sending address)</span> for the currency in question.</small>
|
<small class="form-text text-muted">Your destination address should trust <span class="sending-address-item">(the test sender)</span> for the currency in question.</small>
|
||||||
</div><!-- /.form group for issued currency payment -->
|
</div><!-- /.form group for issued currency payment -->
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group" id="trust_for">
|
||||||
<div class="input-group mb-3">
|
<div class="input-group mb-3">
|
||||||
<button class="btn btn-primary form-control" type="button" id="trustLineButton">Trust for</button>
|
<div class="input-group-prepend">
|
||||||
|
<span class="input-group-text loader" style="display: none"><img class="throbber" src="assets/img/rippleThrobber.png" /></span>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary form-control" type="button" id="trust_for_btn">Trust for</button>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<input id="trust-amount" class="form-control disabled" type="number" value="100000" />
|
<input id="trust_for_amount" class="form-control disabled" type="number" value="100000" />
|
||||||
<span class="input-group-text" id="trust-currency-code">FOO</span>
|
<span class="input-group-text" id="trust_for_currency_code">FOO</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<small class="form-text text-muted">The test sender creates a trust line to your account for the given currency.</small>
|
<small class="form-text text-muted">The test sender creates a trust line to your account for the given currency.</small>
|
||||||
|
|||||||
Reference in New Issue
Block a user