Issue a token: working code

- Fix bugs in submit and verify helper function
- Tweak tutorial and placement of helper function
- Finish drafting sample code
- Put sample code into tutorial
This commit is contained in:
mDuo13
2021-08-10 16:33:52 -07:00
parent 5d13f1af17
commit d93467a8af
11 changed files with 355 additions and 217 deletions

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Code Sample - Issue a Token</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous"></script>
<script src="https://unpkg.com/ripple-lib@1.9.8/build/ripple-latest-min.js"></script>
<script type="application/javascript" src="../submit-and-verify/submit-and-verify.js"></script>
<script type="application/javascript" src="issue-a-token.js"></script>
</head>
<body>Open your browser's console (F12) to see the logs.</body>
</html>

View File

@@ -1,12 +1,7 @@
// Example credentials
let hot_address = "rMCcNuTcajgw7YTgBy1sys3b89QqjUrMpH"
let hot_secret = "sn3nxiW7v8KXzPzAqzyHXbSSKNuN9"
let cold_address = ""
let cold_secret = ""
// Connect ---------------------------------------------------------------------
// ripple = require('ripple-lib') // For Node.js. In browsers, use <script>.
api = new ripple.RippleAPI({server: 'wss://s.altnet.rippletest.net:51233'})
console.log("Connecting to Testnet...")
api.connect()
api.on('connected', async () => {
@@ -30,13 +25,14 @@ api.on('connected', async () => {
return data
}
console.log("Requesting addresses from the Testnet faucet...")
const hot_data = await get_faucet_address()
hot_address = hot_data.account.address
hot_secret = hot_data.account.secret
const hot_address = hot_data.account.address
const hot_secret = hot_data.account.secret
const cold_data = await get_faucet_address()
cold_address = cold_data.account.address
cold_secret = cold_data.account.secret
const cold_address = cold_data.account.address
const cold_secret = cold_data.account.secret
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
@@ -45,31 +41,120 @@ api.on('connected', async () => {
// the faucet.
while (true) {
try {
await api.request("account_info", {account: address, ledger_index: "validated"})
await api.request("account_info", {account: cold_address, ledger_index: "validated"})
await api.request("account_info", {account: hot_address, 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_address} and cold address ${cold_address}.`)
// Prepare AccountSet transaction for the issuer (cold address)
// Configure issuer (cold address) settings ----------------------------------
const cold_settings_tx = {
"TransactionType": "AccountSet",
"Account": cold_address,
"TransferFee": 0,
"TransferRate": 0,
"TickSize": 5,
"SetFlag": 8 // enable Default Ripple
//"Flags": (api.txFlags.AccountSet.DisallowXRP |
// api.txFlags.AccountSet.RequireDestTag)
}
const prepared_cst = await api.prepareTransaction(cold_settings_tx, {maxLedgerVersionOffset: 10})
const cst_prepared = await api.prepareTransaction(cold_settings_tx, {maxLedgerVersionOffset: 10})
const cst_signed = api.sign(cst_prepared.txJSON, cold_secret)
// 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...")
const cst_result = await submit_and_verify(api, cst_signed.signedTransaction)
if (cst_result == "tesSUCCESS") {
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${cst_signed.id}`)
} else {
throw `Error sending transaction: ${cst_result}`
}
// Configure hot address settings --------------------------------------------
const hot_settings_tx = {
"TransactionType": "AccountSet",
"Account": hot_address,
"SetFlag": 2 // enable Require Auth so we can't use trust lines that users
// make to the hot address, even by accident.
//"Flags": (api.txFlags.AccountSet.DisallowXRP |
// api.txFlags.AccountSet.RequireDestTag)
}
const hst_prepared = await api.prepareTransaction(hot_settings_tx, {maxLedgerVersionOffset: 10})
const hst_signed = api.sign(hst_prepared.txJSON, hot_secret)
console.log("Sending hot address AccountSet transaction...")
const hst_result = await submit_and_verify(api, hst_signed.signedTransaction)
if (hst_result == "tesSUCCESS") {
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${hst_signed.id}`)
} else {
throw `Error sending transaction: ${hst_result}`
}
// Create trust line from hot to cold address --------------------------------
const currency_code = "FOO"
const trust_set_tx = {
"TransactionType": "TrustSet",
"Account": hot_address,
"LimitAmount": {
"currency": currency_code,
"issuer": cold_address,
"value": "10000000000" // Large limit, arbitrarily chosen
}
}
const ts_prepared = await api.prepareTransaction(trust_set_tx, {maxLedgerVersionOffset: 10})
const ts_signed = api.sign(ts_prepared.txJSON, hot_secret)
console.log("Creating trust line from hot address to issuer...")
const ts_result = await submit_and_verify(api, ts_signed.signedTransaction)
if (ts_result == "tesSUCCESS") {
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${ts_signed.id}`)
} else {
throw `Error sending transaction: ${ts_result}`
}
// Send token ----------------------------------------------------------------
const issue_quantity = "3840"
const send_token_tx = {
"TransactionType": "Payment",
"Account": cold_address,
"Amount": {
"currency": currency_code,
"value": issue_quantity,
"issuer": cold_address
},
"Destination": hot_address
}
const payment_prepared = await api.prepareTransaction(send_token_tx, {maxLedgerVersionOffset: 10})
const payment_signed = api.sign(payment_prepared.txJSON, cold_secret)
// submit_and_verify helper from _code-samples/submit-and-verify
console.log(`Sending ${issue_quantity} ${currency_code} to ${hot_address}...`)
const payment_result = await submit_and_verify(api, payment_signed.signedTransaction)
if (payment_result == "tesSUCCESS") {
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${payment_signed.id}`)
} else {
throw `Error sending transaction: ${payment_result}`
}
// Check balances ------------------------------------------------------------
console.log("Getting hot address balances...")
const hot_balances = await api.request("account_lines", {account: hot_address, ledger_index: "validated"})
console.log(hot_balances)
console.log("Getting cold address balances...")
const cold_balances = await api.request("gateway_balances", {
account: cold_address,
ledger_index: "validated",
hotwallet: [hot_address]
})
console.log(cold_balances)
}) // End of api.on.('connected')

View File

@@ -4,7 +4,7 @@
<meta charset="utf-8" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous"></script>
<script src="https://unpkg.com/ripple-lib@1.9.1/build/ripple-latest-min.js"></script>
<script type="application/javascript" src="../rippleapi_quickstart/semi-reliable-submit.js"></script>
<script type="application/javascript" src="../submit-and-verify/submit-and-verify.js"></script>
<script type="application/javascript" src="require-destination-tags.js"></script>
</head>
<body>Open your browser's console (F12) to see the logs.</body>

View File

@@ -1,85 +0,0 @@
function lookup_tx_final(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]
}
// 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("server_info")
if (si.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.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])
const max_validated = Math.min(lr_max, lr_max)
if (lr_min <= min_ledger && max_validated >= max_ledger) {
return true
}
return false
}
return new Promise((resolve, reject) => {
api.on('ledger', async (ledger) => {
try {
tx_result = await api.request("tx", {
"transaction": tx_id,
"min_ledger": min_ledger,
"max_ledger": max_ledger
})
if (tx_result.validated) {
resolve(tx_result.meta.TransactionResult)
} else if (max_ledger > ledger.ledgerVersion) {
// Transaction found, not validated, but we should have a final result
// by now.
// Work around https://github.com/ripple/rippled/issues/3727
if (server_has_ledger_range(min_ledger, max_ledger)) {
// Transaction should have been validated by now.
reject(`Transaction not found in ledgers ${min_ledger}-${max_ledger}. This result is final if this ledger is correct.`)
} else {
reject("Can't get final result. Check a full history server.")
}
} else {
// Transaction may still be validated later. Keep waiting.
}
} catch(e) {
if (e.data.error == "txnNotFound") {
if (e.data.searched_all) {
reject(`Transaction not found in ledgers ${min_ledger}-${max_ledger}. This result is final if this range is correct.`)
} else {
if (max_ledger > ledger.ledgerVersion) {
// 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 (server_has_ledger_range(min_ledger, max_ledger)) {
reject(`Transaction not found in ledgers ${min_ledger}-${max_ledger}. This result is final if this range is correct.`)
} else {
reject("Can't get final result. Check a full history server.")
}
}
}
} else {
// Unknown error; pass it back up
reject(`Unknown Error: ${e}`)
}
}
}) // end ledger event handler
}) // end promise def
}

View File

@@ -1,90 +0,0 @@
'use strict';
/* import RippleAPI and support libraries */
const RippleAPI = require('ripple-lib').RippleAPI;
/* Credentials of the account placing the order */
const myAddr = 'rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn';
const mySecret = 's████████████████████████████';
/* Define the order to place here */
const myOrder = {
'direction': 'buy',
'quantity': {
'currency': 'FOO',
'counterparty': 'rUpy3eEg8rqjqfUoLeBnZkscbKbFsKXC3v',
'value': '100'
},
'totalPrice': {
'currency': 'XRP',
'value': '1000'
}
};
/* Milliseconds to wait between checks for a new ledger. */
const INTERVAL = 1000;
/* Instantiate RippleAPI. Uses s2 (full history server) */
const api = new RippleAPI({server: 'wss://s2.ripple.com'});
/* Number of ledgers to check for valid transaction before failing */
const ledgerOffset = 5;
const myInstructions = {maxLedgerVersionOffset: ledgerOffset};
/* Verify a transaction is in a validated XRP Ledger version */
function verifyTransaction(hash, options) {
console.log('Verifying Transaction');
return api.getTransaction(hash, options).then(data => {
console.log('Final Result: ', data.outcome.result);
console.log('Validated in Ledger: ', data.outcome.ledgerVersion);
console.log('Sequence: ', data.sequence);
return data.outcome.result === 'tesSUCCESS';
}).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(() => verifyTransaction(hash, options)
.then(resolve, reject), INTERVAL);
});
}
return error;
});
}
/* Function to prepare, sign, and submit a transaction to the XRP Ledger. */
function submitTransaction(lastClosedLedgerVersion, prepared, secret) {
const signedData = api.sign(prepared.txJSON, secret);
return api.submit(signedData.signedTransaction).then(data => {
console.log('Tentative Result: ', data.resultCode);
console.log('Tentative Message: ', data.resultMessage);
/* The tentative result should be ignored. Transactions that succeed here can ultimately fail,
and transactions that fail here can ultimately succeed. */
/* Begin validation workflow */
const options = {
minLedgerVersion: lastClosedLedgerVersion,
maxLedgerVersion: prepared.instructions.maxLedgerVersion
};
return new Promise((resolve, reject) => {
setTimeout(() => verifyTransaction(signedData.id, options)
.then(resolve, reject), INTERVAL);
});
});
}
api.connect().then(() => {
console.log('Connected');
return api.prepareOrder(myAddr, myOrder, myInstructions);
}).then(prepared => {
console.log('Order Prepared');
return api.getLedger().then(ledger => {
console.log('Current Ledger', ledger.ledgerVersion);
return submitTransaction(ledger.ledgerVersion, prepared, mySecret);
});
}).then(() => {
api.disconnect().then(() => {
console.log('api disconnected');
process.exit();
});
}).catch(console.error);

View File

@@ -5,7 +5,7 @@
<title>Code Sample - Send XRP</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous"></script>
<script src="https://unpkg.com/ripple-lib@1.9.1/build/ripple-latest-min.js"></script>
<script type="application/javascript" src="../rippleapi_quickstart/semi-reliable-submit.js"></script>
<script type="application/javascript" src="../submit-and-verify/submit-and-verify.js"></script>
<script type="application/javascript" src="send-xrp.js"></script>
</head>
<body>Open your browser's console (F12) to see the logs.</body>

View File

@@ -0,0 +1,32 @@
# Submit and Verify
Example JavaScript code using ripple-lib to submit a signed transaction blob and wait until it has a final result.
Example usage:
```js
// example testnet creds. Don't use for real
const address = "raXDGCShEGqYz2d94qkv1UmAh2uJd3UTea"
const secret = "ssNBEKCkEY3W6YFfrjcSoNit91Vvj"
api = new ripple.RippleAPI({server: 'wss://s.altnet.rippletest.net:51233'})
api.connect()
api.on('connected', async () => {
const prepared = await api.prepareTransaction({
"TransactionType": "AccountSet",
"Account": address
})
const signed = api.sign(prepared.txJSON, secret)
const result = await submit_and_verify(api, signed.signedTransaction)
if (result == "tesSUCCESS") {
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${signed.id}`)
} else if (result == "unknown") {
console.log(`Transaction status unknown. `)
} else if (result == "tefMAX_LEDGER") {
console.log(`Transaction failed to achieve a consensus.`)
} else {
console.log(`Transaction failed with code ${result}: https://testnet.xrpl.org/transactions/${signed.id}`)
}
}
```

View File

@@ -0,0 +1,167 @@
// Submit-and-verify XRPL transaction using ripple-lib (v1.x)
// 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 RippleAPI instance connected to the network where you
// submitted the transaction.
// @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]
}
// 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("server_info")
// console.log(`Server has ledger range: ${si.info.complete_ledgers}`)
if (si.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.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 {
tx_result = await api.request("tx", {
"transaction": tx_id,
"min_ledger": min_ledger,
"max_ledger": max_ledger
})
if (tx_result.validated) {
resolve(tx_result.meta.TransactionResult)
} else if (ledger.ledgerVersion >= max_ledger) {
api.off("ledger", 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("ledger", 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.ledgerVersion) {
api.off("ledger", 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('ledger', 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) {
const prelim_result = await api.request("submit", {"tx_blob": tx_blob})
console.log("Preliminary result:", prelim_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;
}

View File

@@ -244,7 +244,7 @@ Promiseは、自身の非同期動作を完了すると、渡されたコール
XRP Ledgerまたは任意の分散されたシステムを使用する上で最大の課題の1つとなるのが、最終的かつ不変のトランザクション結果を把握することです。[ベストプラクティスに従っている](reliable-transaction-submission.html)場合も、トランザクションが最終的に受け入れられるか拒否されるまで、[コンセンサスプロセス](consensus.html)を待機しなければならないことに変わりはありません。以下のサンプルコードは、トランザクションの最終的な結果を待機する方法を示しています。
```
{% include '_code-samples/rippleapi_quickstart/submit-and-verify.js' %}
{% include '_code-samples/submit-and-verify/submit-and-verify.js' %}
```
このコードは注文トランザクションを作成して送信するものですが、他のタイプのトランザクションにも同様の原則があてはまります。トランザクションを送信した後、setTimeoutを使用して所定の時間が経過するまで待機し、新しいPromiseでレジャーをもう一度照会して、トランザクションが検証済みとなっているかどうかを確認します。検証済みとなっていない場合は、検証済みレジャーの中にトランザクションが見つかるか、返されたレジャーがLastLedgerSequenceパラメーターの値よりも大きくなるまで、このプロセスを繰り返します。

View File

@@ -242,17 +242,13 @@ The `catch` method ends this Promise chain. The callback provided here runs if a
# Waiting for Validation
One of the biggest challenges in using the XRP Ledger (or any decentralized system) is knowing the final, immutable transaction results. Even if you [follow the best practices](reliable-transaction-submission.html) you still have to wait for the [consensus process](consensus.html) to finally accept or reject your transaction. The following example code demonstrates how to wait for the final outcome of a transaction:
Most transactions are validated and have a final result in one or two ledger versions, about 2-7 seconds after submission. However, when things don't go quite as planned, it can be tricky to know what a transaction's final, immutable results are. Even if you [follow the best practices](reliable-transaction-submission.html) you still have to wait for the [consensus process](consensus.html) to finally accept or reject your transaction.
```
{% include '_code-samples/rippleapi_quickstart/submit-and-verify.js' %}
```
The [submit-and-verify code sample](https://github.com/XRPLF/xrpl-dev-portal/tree/master/content/_code-samples/submit-and-verify/) demonstrates how to submit a transaction and wait for it to have a final result.
This code creates and submits an order transaction, although the same principles apply to other types of transactions as well. After submitting the transaction, the code uses a new Promise, which queries the ledger again after using `setTimeout` to wait a fixed amount of time, to see if the transaction has been verified. If it hasn't been verified, the process repeats until either the transaction is found in a validated ledger or the returned ledger is higher than the `LastLedgerSequence` parameter.
In rare cases (particularly with a large delay, a brief network outage, or a loss of power), the `rippled` server may be missing a ledger version between when you submitted the transaction and when you determined that the network validated the last ledger version that the transaction . In this case, you cannot be definitively sure whether the transaction has failed, or has been included in one of the missing ledger versions.
In rare cases (particularly with a large delay or a loss of power), the `rippled` server may be missing a ledger version between when you submitted the transaction and when you determined that the network has passed the `maxLedgerVersion`. In this case, you cannot be definitively sure whether the transaction has failed, or has been included in one of the missing ledger versions. RippleAPI returns `MissingLedgerHistoryError` in this case.
If you are the administrator of the `rippled` server, you can [manually request the missing ledger(s)](ledger_request.html). Otherwise, you can try checking the ledger history using a different server. (Ripple runs a public full-history server at `s2.ripple.com` for this purpose.)
If you are the administrator of the `rippled` server, you can [manually request the missing ledger(s)](ledger_request.html). Otherwise, you can try checking the ledger history using a different server. Several [public full-history servers](public-servers.html) are available for this purpose.
See [Reliable Transaction Submission](reliable-transaction-submission.html) for a more thorough explanation.

View File

@@ -5,6 +5,7 @@ blurb: Create your own token and issue it on the XRP Ledger Testnet.
embed_ripple_lib: true
filters:
- interactive_steps
- include_code
labels:
- Tokens
---
@@ -113,9 +114,14 @@ Other settings you may want to, optionally, configure for your cold address (iss
The following code sample shows how to send an [AccountSet transaction][] to enable the recommended cold address settings:
```
TODO: CODE SAMPLE
```
<!-- MULTICODE_BLOCK_START -->
_JavaScript_
{{ include_code("_code-samples/issue-a-token/issue-a-token.js", start_with="// Configure issuer", end_before="// Configure hot", language="js") }}
<!-- MULTICODE_BLOCK_END -->
{{ start_step("Configure Issuer") }}
<form>
@@ -176,10 +182,13 @@ The hot address does not strictly require any settings changes from the default,
The following code sample shows how to send an [AccountSet transaction][] to enable the recommended hot address settings:
```
TODO: code
```
<!-- MULTICODE_BLOCK_START -->
_JavaScript_
{{ include_code("_code-samples/issue-a-token/issue-a-token.js", start_with="// Configure hot address", end_before="// Create trust line", language="js") }}
<!-- MULTICODE_BLOCK_END -->
{{ start_step("Configure Hot Address") }}
<form>
@@ -205,9 +214,13 @@ The hot address needs a trust line like this before it can receive tokens from t
The following code sample shows how to send a [TrustSet transaction][] from the hot address, trusting the issuing address for a limit of 1 billion FOO:
```
TODO: code
```
<!-- MULTICODE_BLOCK_START -->
_JavaScript_
{{ include_code("_code-samples/issue-a-token/issue-a-token.js", start_with="// Create trust line", end_before="// Send token", language="js") }}
<!-- MULTICODE_BLOCK_END -->
{{ start_step("Make Trust Line") }}
<form>
@@ -259,9 +272,13 @@ You can use [auto-filled values](transaction-common-fields.html#auto-fillable-fi
The following code sample shows how to send a [Payment transaction][] to issue 88 FOO from the cold address to the hot address:
```
TODO: code
```
<!-- MULTICODE_BLOCK_START -->
_JavaScript_
{{ include_code("_code-samples/issue-a-token/issue-a-token.js", start_with="// Send token", end_before="// Check balances", language="js") }}
<!-- MULTICODE_BLOCK_END -->
{{ start_step("Send Token") }}
<button id="send-token-button" class="btn btn-primary">Send Token</button>
@@ -282,9 +299,13 @@ Use the [gateway_balances method][] to look up balances from the perspective of
The following code sample shows how to use both methods:
```
TODO: code
```
<!-- MULTICODE_BLOCK_START -->
_JavaScript_
{{ include_code("_code-samples/issue-a-token/issue-a-token.js", start_with="// Check balances", end_before="// End of", language="js") }}
<!-- MULTICODE_BLOCK_END -->
{{ start_step("Confirm Balances") }}
<button id="check-balances-button" class="btn btn-primary">Confirm Balances</button>