12 KiB
html, funnel, doc_type, category, blurb, filters
| html | funnel | doc_type | category | blurb | filters | |
|---|---|---|---|---|---|---|
| use-tickets.html | Build | Tutorials | Manage Account Settings | Use Tickets to send a transaction outside of normal Sequence order. |
|
Use Tickets
(Requires the [TicketBatch amendment][] :not_enabled:)
Tickets provide a way to send transactions out of the normal order. This tutorial walks through the steps of creating a Ticket, then using it to send another transaction.
Prerequisites
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script> <script type="application/javascript" src="{{target.ripple_lib_url}}"></script> <script type="application/javascript" src="assets/js/interactive-tutorial.js"></script>-
This page provides JavaScript examples that use the ripple-lib (RippleAPI) library. The RippleAPI Beginners Guide describes how to get started using RippleAPI to access XRP Ledger data from JavaScript.
-
To use Tickets, you need a address and secret key, and some XRP. You can get all of these on the Testnet using the following interface:
{% include '_snippets/generate-step.md' %}
Steps
{% set n = cycler(* range(1,99)) %}
{{n.next()}}. Connect to a Testnet Server
You must be connected to the network to submit transactions to it. Currently, Tickets are only available on TODO: Devnet someday? stand-alone mode.
The following code sample instantiates a new RippleAPI instance and connects to one of the public XRP Devnet servers that Ripple runs:
ripple = require('ripple-lib')
async function main() {
api = new ripple.RippleAPI({server: 'wss://s.devnet.rippletest.net:51233'})
await api.connect()
// Code in the following examples continues here...
}
main()
Note: The code samples in this tutorial use JavaScript's async/await pattern. Since await needs to be used from within an async function, the remaining code samples are written to continue inside the main() function started here. You can also use Promise methods .then() and .catch() instead of async/await if you prefer.
For this tutorial, you can connect directly from your browser by pressing the following button:
{{ start_step("Connect") }} Connect to TestNet
{{ end_step() }} <script type="application/javascript"> api = new ripple.RippleAPI({server: 'wss://localhost:'}) //TODO: change to Devnet when possible api.on('connected', async function() { $("#connection-status").text("Connected") $("#connect-button").prop("disabled", true) $("#loader-{{n.current}}").hide() // Update breadcrumbs & active next step complete_step("Connect") $("#interactive-prepare button").prop("disabled", false)//TODO: change ID $("#interactive-prepare button").prop("title", "") // TODO: remove this standalone mode "faucet" code resp = await api.request('wallet_propose') await api.request("submit", {secret: "masterpassphrase", tx_json: { "TransactionType": "Payment", "Account": "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh", "Amount": "100000000000", "Destination": resp.account_id }}) // Populate creds const EXAMPLE_ADDR = "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe" const EXAMPLE_SECRET = "s████████████████████████████" $("#address").hide().html("Address: " + '' + resp.account_id + "").show() $("code span:contains('"+EXAMPLE_ADDR+"')").each( function() { let eltext = $(this).text() $(this).text( eltext.replace(EXAMPLE_ADDR, resp.account_id) ) }) $("#address").hide().html("Secret: " + '' + resp.master_seed + "").show() $("code span:contains('"+EXAMPLE_SECRET+"')").each( function() { let eltext = $(this).text() $(this).text( eltext.replace(EXAMPLE_SECRET, resp.master_seed) ) }) // END temp standalone faucet code }) api.on('disconnected', (code) => { $("#connection-status").text( "Disconnected ("+code+")" ) $("#connect-button").prop("disabled", false) $(".connection-required").prop("disabled", true) $(".connection-required").prop("title", "Connection to Test Net required") }) $("#connect-button").click(() => { $("#connection-status").text( "Connecting..." ) $("#loader-{{n.current}}").show() api.connect() }) </script>{{n.next()}}. Check Sequence Number
If you already have at least one Ticket available in the ledger, you can skip this step. Otherwise, you need to get ready to create some Tickets for your other transactions to use.
Before you create any Tickets, you should check what [Sequence Number][] your account is at. You want the current Sequence number for the next step, and the Ticket Sequence numbers it sets aside start from this number.
async function get_sequence() {
const account_info = await api.request("account_info", {
account: "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe"
}
);
console.log("Current sequence:", account_info.account_data.Sequence)
return account_info.account_data.Sequence;
}
let current_sequence = get_sequence()
{{ start_step("Check Sequence") }} Check Sequence Number
{{ end_step() }} <script type="application/javascript"> $("#check-sequence-button").click( async function() { const address = $("#use-address").text() // Wipe previous output $("#check-sequence-output").html("") const account_info = await api.request("account_info", {account: address}) $("#check-sequence").append("Current sequence: "+account_info.account_data.Sequence) // TODO: populate Sequence number in next example } </script>{{n.next()}}. Prepare and Sign TicketCreate
If you already have at least one Ticket available in the ledger, you can skip this step. Otherwise, you need to prepare the transaction to create some Tickets.
Construct a [TicketCreate transaction][] the sequence number you determined in the previous step. Use the TicketCount field to set how many Tickets to create. For example, this example prepares a transaction that would make 10 Tickets:
let prepared = await api.prepareTransaction({
"TransactionType": "TicketCreate",
"Account": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
"TicketCount": 10,
"Sequence": current_sequence
})
console.log("Prepared transaction:", prepared.txJSON)
let signed = await api.sign(prepared.txJSON, "s████████████████████████████")
console.log("Transaction hash:", signed.id)
let tx_blob = signed.signedTransaction
{{n.next()}}. Submit TicketCreate
If you already have at least one Ticket available in the ledger, you can skip this step. Otherwise, you need to send a transaction to create some Tickets.
Submit the signed transaction blob that you created in the previous step. For example:
let prelim_result = await api.submit(tx_blob)
{{n.next()}}. Wait for Validation
Most transactions are accepted into the next ledger version after they're submitted, which means it may take 4-7 seconds for a transaction's outcome to be final. If the XRP Ledger is busy or poor network connectivity delays a transaction from being relayed throughout the network, a transaction may take longer to be confirmed. (For information on how to set an expiration for transactions, see Reliable Transaction Submission.)
You use the ledger event type in RippleAPI to trigger your code to run whenever there is a new validated ledger version. For example:
api.on('ledger', ledger => {
console.log("Ledger version", ledger.ledgerVersion, "was validated.")
})
{{ start_step("Wait") }}
| Latest Validated Ledger Version: | (Not connected) |
|---|---|
| Ledger Version at Time of Submission: | (Not submitted) |
(Optional) Intermission
The power of Tickets is that you can send other transactions during this time, and generally carry on with your account's normal business during this time. If you are planning on using a given Ticket, then as long as you don't use that Ticket for something else, you're free to continue using your account as normal. When you want to send the transaction using a Ticket, you can do that in parallel with other transactions using regular sequence numbers or different Tickets, and submit any of them in any order when you're ready.
TODO: Maybe insert a mini-tx-sender-like board of buttons here?
{{n.next()}}. Check Available Tickets
When you want to send a Ticketed transaction, you need to know what Ticket Sequence number to use for it. If you've been keeping careful track of your account, you already know which Tickets you have, but if you're not sure, you can use the [account_objects method][] (or getAccountObjects()) to look up your available tickets. For example:
let response = await api.getAccountObjects(
"rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
{type:"ticket"} // TODO: confirm this works, maybe check needed ripple-lib version number
)
console.log("Available Tickets:", response.account_objects)
{{n.next()}}. Prepare Ticketed Transaction
Now that you have a Ticket available, you can prepare a transaction that uses it.
This can be any type of transaction you like. The following example uses an [AccountSet transaction][] to set a MessageKey since that doesn't require any other setup in the ledger. Set the Sequence field to 0 and include a TicketSequence field with the Ticket Sequence number of one of your available Tickets.
let prepared_t = await api.prepareTransaction({
"TransactionType": "AccountSet",
"Account": "rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe",
"MessageKey": "DEADBEEF",
"TicketSequence": use_ticket,
"Sequence": 0
})
console.log("Prepared JSON:", prepared_t.txJSON)
let signed_t = await api.sign(prepared_t.txJSON,
"s████████████████████████████")
console.log("Transaction hash:", signed_t.id)
let tx_blob_t = signed.signedTransaction
console.log("Signed transaction blob:", tx_blob_t)
{{n.next()}}. Submit Ticketed Transaction
Submit the signed transaction blob that you created in the previous step. For example:
TODO
{{n.next()}}. Wait for Validation
TODO
See Also
- Concepts:
- Tutorials:
- References:
- [account_objects method][]
- [sign_for method][]
- [submit_multisigned method][]
- [TicketCreate transaction][]
- Transaction Common Fields
{% include '_snippets/rippled-api-links.md' %} {% include '_snippets/tx-type-links.md' %} {% include '_snippets/rippled_versions.md' %}
