mirror of
https://github.com/XRPLF/xrpl-dev-portal.git
synced 2025-11-14 00:35:50 +00:00
xrpl.js 2.0: update issue a token sample code, etc.
This commit is contained in:
@@ -36,15 +36,15 @@ function lookup_tx_final(api, tx_id, max_ledger, min_ledger) {
|
||||
// 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.info.complete_ledgers}`)
|
||||
if (si.info.complete_ledgers == "empty") {
|
||||
// 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.info.complete_ledgers.split(',')
|
||||
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])
|
||||
@@ -59,15 +59,15 @@ function lookup_tx_final(api, tx_id, max_ledger, min_ledger) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ledger_listener = async (ledger) => {
|
||||
try {
|
||||
tx_result = await api.request({
|
||||
const tx_response = await api.request({
|
||||
"command": "tx",
|
||||
"transaction": tx_id,
|
||||
"min_ledger": min_ledger,
|
||||
"max_ledger": max_ledger
|
||||
})
|
||||
|
||||
if (tx_result.validated) {
|
||||
resolve(tx_result.meta.TransactionResult)
|
||||
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
|
||||
@@ -145,15 +145,15 @@ function lookup_tx_final(api, tx_id, max_ledger, min_ledger) {
|
||||
// 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({"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) {
|
||||
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
|
||||
const max_ledger = prelim.result.tx_json.LastLedgerSequence
|
||||
const tx_id = prelim.result.tx_json.hash
|
||||
|
||||
let final_result
|
||||
try {
|
||||
|
||||
@@ -153,7 +153,7 @@ const set_up_tx_sender = async function() {
|
||||
}
|
||||
|
||||
// Wait for tx to be in a validated ledger or to expire
|
||||
const hash = xrpl.computeBinaryTransactionSigningHash(signed)
|
||||
const hash = xrpl.computeSignedTransactionHash(signed)
|
||||
|
||||
try {
|
||||
// use lookup_tx_final() from submit-and-verify2.js
|
||||
|
||||
2
assets/js/xrpl-2.0b0.min.js
vendored
2
assets/js/xrpl-2.0b0.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@
|
||||
<meta charset="utf-8" />
|
||||
<title>Code Sample - Issue a Token</title>
|
||||
<script src="https://unpkg.com/ripple-lib@1.10.0/build/ripple-latest-min.js"></script>
|
||||
<script type="application/javascript" src="../../submit-and-verify/submit-and-verify.js"></script>
|
||||
<script type="application/javascript" src="../../submit-and-verify/submit-and-verify2.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>
|
||||
|
||||
@@ -5,25 +5,23 @@
|
||||
// In browsers, use <script> tags as in the example demo.html.
|
||||
if (typeof module !== "undefined") {
|
||||
// gotta use var here because const/let are block-scoped to the if statement.
|
||||
var ripple = require('ripple-lib')
|
||||
var submit_and_verify = require('../../submit-and-verify/submit-and-verify.js').submit_and_verify
|
||||
var xrpl = require('xrpl')
|
||||
var submit_and_verify = require('../../submit-and-verify/submit-and-verify2.js').submit_and_verify
|
||||
} else {
|
||||
// TODO: remove when webpack is fixed
|
||||
var xrpl = ripple;
|
||||
}
|
||||
|
||||
// Connect ---------------------------------------------------------------------
|
||||
async function main() {
|
||||
api = new ripple.RippleAPI({server: 'wss://s.altnet.rippletest.net:51233'})
|
||||
api = new xrpl.Client('wss://s.altnet.rippletest.net:51233')
|
||||
console.log("Connecting to Testnet...")
|
||||
await api.connect()
|
||||
|
||||
// Get credentials from the Testnet Faucet -----------------------------------
|
||||
console.log("Requesting addresses from the Testnet faucet...")
|
||||
const hot_data = await api.generateFaucetWallet()
|
||||
const hot_address = hot_data.account.classicAddress
|
||||
const hot_secret = hot_data.account.secret
|
||||
|
||||
const cold_data = await api.generateFaucetWallet()
|
||||
const cold_address = cold_data.account.classicAddress
|
||||
const cold_secret = cold_data.account.secret
|
||||
const hot_wallet = await api.generateFaucetWallet()
|
||||
const cold_wallet = await api.generateFaucetWallet()
|
||||
|
||||
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
|
||||
@@ -32,12 +30,14 @@ async function main() {
|
||||
// the faucet.
|
||||
while (true) {
|
||||
try {
|
||||
await api.request("account_info", {
|
||||
account: cold_address,
|
||||
await api.request({
|
||||
command: "account_info",
|
||||
account: cold_wallet.classicAddress,
|
||||
ledger_index: "validated"
|
||||
})
|
||||
await api.request("account_info", {
|
||||
account: hot_address,
|
||||
await api.request({
|
||||
command: "account_info",
|
||||
account: hot_wallet.classicAddress,
|
||||
ledger_index: "validated"
|
||||
})
|
||||
break
|
||||
@@ -46,31 +46,29 @@ async function main() {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||
}
|
||||
}
|
||||
console.log(`Got hot address ${hot_address} and cold address ${cold_address}.`)
|
||||
console.log(`Got hot address ${hot_wallet.classicAddress} and cold address ${cold_wallet.classicAddress}.`)
|
||||
|
||||
// Configure issuer (cold address) settings ----------------------------------
|
||||
const cold_settings_tx = {
|
||||
"TransactionType": "AccountSet",
|
||||
"Account": cold_address,
|
||||
"Account": cold_wallet.classicAddress,
|
||||
"TransferRate": 0,
|
||||
"TickSize": 5,
|
||||
"Domain": "6578616D706C652E636F6D", // "example.com"
|
||||
"SetFlag": 8 // enable Default Ripple
|
||||
//"Flags": (api.txFlags.AccountSet.DisallowXRP |
|
||||
// api.txFlags.AccountSet.RequireDestTag)
|
||||
// TODO: update to txFlags' new location?
|
||||
}
|
||||
|
||||
const cst_prepared = await api.prepareTransaction(
|
||||
cold_settings_tx,
|
||||
{maxLedgerVersionOffset: 10}
|
||||
)
|
||||
const cst_signed = api.sign(cst_prepared.txJSON, cold_secret)
|
||||
const cst_prepared = await api.autofill(cold_settings_tx)
|
||||
const cst_signed = cold_wallet.signTransaction(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...")
|
||||
const cst_result = await submit_and_verify(api, cst_signed.signedTransaction)
|
||||
const cst_result = await submit_and_verify(api, cst_signed)
|
||||
if (cst_result == "tesSUCCESS") {
|
||||
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${cst_signed.id}`)
|
||||
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${xrpl.computeSignedTransactionHash(cst_signed)}`)
|
||||
} else {
|
||||
throw `Error sending transaction: ${cst_result}`
|
||||
}
|
||||
@@ -80,7 +78,7 @@ async function main() {
|
||||
|
||||
const hot_settings_tx = {
|
||||
"TransactionType": "AccountSet",
|
||||
"Account": hot_address,
|
||||
"Account": hot_wallet.classicAddress,
|
||||
"Domain": "6578616D706C652E636F6D", // "example.com"
|
||||
"SetFlag": 2 // enable Require Auth so we can't use trust lines that users
|
||||
// make to the hot address, even by accident.
|
||||
@@ -88,15 +86,12 @@ async function main() {
|
||||
// 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)
|
||||
const hst_prepared = await api.autofill(hot_settings_tx)
|
||||
const hst_signed = hot_wallet.signTransaction(hst_prepared)
|
||||
console.log("Sending hot address AccountSet transaction...")
|
||||
const hst_result = await submit_and_verify(api, hst_signed.signedTransaction)
|
||||
const hst_result = await submit_and_verify(api, hst_signed)
|
||||
if (hst_result == "tesSUCCESS") {
|
||||
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${hst_signed.id}`)
|
||||
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${xrpl.computeSignedTransactionHash(hst_signed)}`)
|
||||
} else {
|
||||
throw `Error sending transaction: ${hst_result}`
|
||||
}
|
||||
@@ -106,23 +101,20 @@ async function main() {
|
||||
const currency_code = "FOO"
|
||||
const trust_set_tx = {
|
||||
"TransactionType": "TrustSet",
|
||||
"Account": hot_address,
|
||||
"Account": hot_wallet.classicAddress,
|
||||
"LimitAmount": {
|
||||
"currency": currency_code,
|
||||
"issuer": cold_address,
|
||||
"issuer": cold_wallet.classicAddress,
|
||||
"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)
|
||||
const ts_prepared = await api.autofill(trust_set_tx)
|
||||
const ts_signed = hot_wallet.signTransaction(ts_prepared)
|
||||
console.log("Creating trust line from hot address to issuer...")
|
||||
const ts_result = await submit_and_verify(api, ts_signed.signedTransaction)
|
||||
const ts_result = await submit_and_verify(api, ts_signed)
|
||||
if (ts_result == "tesSUCCESS") {
|
||||
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${ts_signed.id}`)
|
||||
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${xrpl.computeSignedTransactionHash(ts_signed)}`)
|
||||
} else {
|
||||
throw `Error sending transaction: ${ts_result}`
|
||||
}
|
||||
@@ -132,44 +124,43 @@ async function main() {
|
||||
const issue_quantity = "3840"
|
||||
const send_token_tx = {
|
||||
"TransactionType": "Payment",
|
||||
"Account": cold_address,
|
||||
"Account": cold_wallet.classicAddress,
|
||||
"Amount": {
|
||||
"currency": currency_code,
|
||||
"value": issue_quantity,
|
||||
"issuer": cold_address
|
||||
"issuer": cold_wallet.classicAddress
|
||||
},
|
||||
"Destination": hot_address
|
||||
"Destination": hot_wallet.classicAddress
|
||||
}
|
||||
|
||||
const pay_prepared = await api.prepareTransaction(
|
||||
send_token_tx,
|
||||
{maxLedgerVersionOffset: 10}
|
||||
)
|
||||
const pay_signed = api.sign(pay_prepared.txJSON, cold_secret)
|
||||
const pay_prepared = await api.autofill(send_token_tx)
|
||||
const pay_signed = cold_wallet.signTransaction(pay_prepared)
|
||||
// submit_and_verify helper from _code-samples/submit-and-verify
|
||||
console.log(`Sending ${issue_quantity} ${currency_code} to ${hot_address}...`)
|
||||
const pay_result = await submit_and_verify(api, pay_signed.signedTransaction)
|
||||
console.log(`Sending ${issue_quantity} ${currency_code} to ${hot_wallet.classicAddress}...`)
|
||||
const pay_result = await submit_and_verify(api, pay_signed)
|
||||
if (pay_result == "tesSUCCESS") {
|
||||
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${pay_signed.id}`)
|
||||
console.log(`Transaction succeeded: https://testnet.xrpl.org/transactions/${xrpl.computeSignedTransactionHash(pay_signed)}`)
|
||||
} else {
|
||||
throw `Error sending transaction: ${pay_result}`
|
||||
}
|
||||
|
||||
// Check balances ------------------------------------------------------------
|
||||
console.log("Getting hot address balances...")
|
||||
const hot_balances = await api.request("account_lines", {
|
||||
account: hot_address,
|
||||
const hot_balances = await api.request({
|
||||
command: "account_lines",
|
||||
account: hot_wallet.classicAddress,
|
||||
ledger_index: "validated"
|
||||
})
|
||||
console.log(hot_balances)
|
||||
console.log(hot_balances.result)
|
||||
|
||||
console.log("Getting cold address balances...")
|
||||
const cold_balances = await api.request("gateway_balances", {
|
||||
account: cold_address,
|
||||
const cold_balances = await api.request({
|
||||
command: "gateway_balances",
|
||||
account: cold_wallet.classicAddress,
|
||||
ledger_index: "validated",
|
||||
hotwallet: [hot_address]
|
||||
hotwallet: [hot_wallet.classicAddress]
|
||||
})
|
||||
console.log(JSON.stringify(cold_balances, null, 2))
|
||||
console.log(JSON.stringify(cold_balances.result, null, 2))
|
||||
|
||||
api.disconnect()
|
||||
} // End of main()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"ripple-lib": "^1.10.0"
|
||||
"ripple-lib": "^1.10.0",
|
||||
"xrpl": "2.0.0-beta.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,16 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.4.14.tgz#e27705ec2278b2355bd59f1952de23a152b9f208"
|
||||
integrity sha512-GZpnVRNtv7sHDXIFncsERt+qvj4rzAgRQtnvzk3Z7OVNtThD2dHXYCMDNc80D5mv4JE278qo8biZCwcmkbdpqw==
|
||||
|
||||
"@types/node@10.12.18":
|
||||
version "10.12.18"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67"
|
||||
integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==
|
||||
|
||||
"@types/node@11.11.6":
|
||||
version "11.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a"
|
||||
integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==
|
||||
|
||||
"@types/ws@^7.2.0":
|
||||
version "7.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702"
|
||||
@@ -41,7 +51,7 @@ available-typed-arrays@^1.0.4:
|
||||
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz#9e0ae84ecff20caae6a94a1c3bc39b955649b7a9"
|
||||
integrity sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==
|
||||
|
||||
base-x@3.0.8:
|
||||
base-x@3.0.8, base-x@^3.0.2:
|
||||
version "3.0.8"
|
||||
resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.8.tgz#1e1106c2537f0162e8b52474a557ebb09000018d"
|
||||
integrity sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==
|
||||
@@ -63,7 +73,37 @@ bignumber.js@^9.0.0:
|
||||
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5"
|
||||
integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==
|
||||
|
||||
bn.js@^4.11.9:
|
||||
bindings@^1.3.0:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
|
||||
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
|
||||
dependencies:
|
||||
file-uri-to-path "1.0.0"
|
||||
|
||||
bip32@^2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/bip32/-/bip32-2.0.6.tgz#6a81d9f98c4cd57d05150c60d8f9e75121635134"
|
||||
integrity sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==
|
||||
dependencies:
|
||||
"@types/node" "10.12.18"
|
||||
bs58check "^2.1.1"
|
||||
create-hash "^1.2.0"
|
||||
create-hmac "^1.1.7"
|
||||
tiny-secp256k1 "^1.1.3"
|
||||
typeforce "^1.11.5"
|
||||
wif "^2.0.6"
|
||||
|
||||
bip39@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.4.tgz#5b11fed966840b5e1b8539f0f54ab6392969b2a0"
|
||||
integrity sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw==
|
||||
dependencies:
|
||||
"@types/node" "11.11.6"
|
||||
create-hash "^1.1.0"
|
||||
pbkdf2 "^3.0.9"
|
||||
randombytes "^2.0.1"
|
||||
|
||||
bn.js@^4.11.8, bn.js@^4.11.9:
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
|
||||
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
|
||||
@@ -78,6 +118,22 @@ brorand@^1.0.5, brorand@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
||||
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
|
||||
|
||||
bs58@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
|
||||
integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo=
|
||||
dependencies:
|
||||
base-x "^3.0.2"
|
||||
|
||||
bs58check@<3.0.0, bs58check@^2.1.1:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/bs58check/-/bs58check-2.1.2.tgz#53b018291228d82a5aa08e7d796fdafda54aebfc"
|
||||
integrity sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==
|
||||
dependencies:
|
||||
bs58 "^4.0.0"
|
||||
create-hash "^1.1.0"
|
||||
safe-buffer "^5.1.2"
|
||||
|
||||
buffer@5.6.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786"
|
||||
@@ -94,7 +150,7 @@ call-bind@^1.0.0, call-bind@^1.0.2:
|
||||
function-bind "^1.1.1"
|
||||
get-intrinsic "^1.0.2"
|
||||
|
||||
cipher-base@^1.0.1:
|
||||
cipher-base@^1.0.1, cipher-base@^1.0.3:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
|
||||
integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==
|
||||
@@ -102,7 +158,7 @@ cipher-base@^1.0.1:
|
||||
inherits "^2.0.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
create-hash@^1.1.2, create-hash@^1.2.0:
|
||||
create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196"
|
||||
integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==
|
||||
@@ -113,6 +169,18 @@ create-hash@^1.1.2, create-hash@^1.2.0:
|
||||
ripemd160 "^2.0.1"
|
||||
sha.js "^2.4.0"
|
||||
|
||||
create-hmac@^1.1.4, create-hmac@^1.1.7:
|
||||
version "1.1.7"
|
||||
resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff"
|
||||
integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==
|
||||
dependencies:
|
||||
cipher-base "^1.0.3"
|
||||
create-hash "^1.1.0"
|
||||
inherits "^2.0.1"
|
||||
ripemd160 "^2.0.0"
|
||||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
debug@4:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
|
||||
@@ -132,7 +200,7 @@ define-properties@^1.1.3:
|
||||
dependencies:
|
||||
object-keys "^1.0.12"
|
||||
|
||||
elliptic@^6.5.2:
|
||||
elliptic@^6.4.0, elliptic@^6.5.2:
|
||||
version "6.5.4"
|
||||
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
|
||||
integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==
|
||||
@@ -182,6 +250,11 @@ es6-object-assign@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
|
||||
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
|
||||
|
||||
file-uri-to-path@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
|
||||
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
|
||||
|
||||
foreach@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
|
||||
@@ -405,10 +478,10 @@ ms@2.1.2:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
node-fetch@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
nan@^2.13.2:
|
||||
version "2.15.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
|
||||
integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==
|
||||
|
||||
object-inspect@^1.11.0, object-inspect@^1.9.0:
|
||||
version "1.11.0"
|
||||
@@ -438,6 +511,24 @@ object.assign@^4.1.2:
|
||||
has-symbols "^1.0.1"
|
||||
object-keys "^1.1.1"
|
||||
|
||||
pbkdf2@^3.0.9:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075"
|
||||
integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==
|
||||
dependencies:
|
||||
create-hash "^1.1.2"
|
||||
create-hmac "^1.1.4"
|
||||
ripemd160 "^2.0.1"
|
||||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
randombytes@^2.0.1:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
|
||||
dependencies:
|
||||
safe-buffer "^5.1.0"
|
||||
|
||||
readable-stream@^3.6.0:
|
||||
version "3.6.0"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
|
||||
@@ -447,7 +538,7 @@ readable-stream@^3.6.0:
|
||||
string_decoder "^1.1.1"
|
||||
util-deprecate "^1.0.1"
|
||||
|
||||
ripemd160@^2.0.1:
|
||||
ripemd160@^2.0.0, ripemd160@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
|
||||
integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==
|
||||
@@ -511,12 +602,12 @@ ripple-lib@^1.10.0:
|
||||
ripple-lib-transactionparser "0.8.2"
|
||||
ws "^7.2.0"
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
sha.js@^2.4.0:
|
||||
sha.js@^2.4.0, sha.js@^2.4.8:
|
||||
version "2.4.11"
|
||||
resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7"
|
||||
integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==
|
||||
@@ -556,6 +647,22 @@ string_decoder@^1.1.1:
|
||||
dependencies:
|
||||
safe-buffer "~5.2.0"
|
||||
|
||||
tiny-secp256k1@^1.1.3:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz#7e224d2bee8ab8283f284e40e6b4acb74ffe047c"
|
||||
integrity sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==
|
||||
dependencies:
|
||||
bindings "^1.3.0"
|
||||
bn.js "^4.11.8"
|
||||
create-hmac "^1.1.7"
|
||||
elliptic "^6.4.0"
|
||||
nan "^2.13.2"
|
||||
|
||||
typeforce@^1.11.5:
|
||||
version "1.18.0"
|
||||
resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc"
|
||||
integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==
|
||||
|
||||
unbox-primitive@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"
|
||||
@@ -606,7 +713,32 @@ which-typed-array@^1.1.2:
|
||||
has-tostringtag "^1.0.0"
|
||||
is-typed-array "^1.1.6"
|
||||
|
||||
wif@^2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/wif/-/wif-2.0.6.tgz#08d3f52056c66679299726fade0d432ae74b4704"
|
||||
integrity sha1-CNP1IFbGZnkplyb63g1DKudLRwQ=
|
||||
dependencies:
|
||||
bs58check "<3.0.0"
|
||||
|
||||
ws@^7.2.0:
|
||||
version "7.5.3"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.3.tgz#160835b63c7d97bfab418fc1b8a9fced2ac01a74"
|
||||
integrity sha512-kQ/dHIzuLrS6Je9+uv81ueZomEwH0qVYstcAQ4/Z93K8zeko9gtAbttJWzoC5ukqXY1PpoouV3+VSOqEAFt5wg==
|
||||
|
||||
xrpl@2.0.0-beta.0:
|
||||
version "2.0.0-beta.0"
|
||||
resolved "https://registry.yarnpkg.com/xrpl/-/xrpl-2.0.0-beta.0.tgz#4ca0da2eef712cccdd0e5de2e167406d22fd0739"
|
||||
integrity sha512-5aUSpjtzqtzYIu7nue6LoMf7lpfyzDCIOGYe6gjDX0LBCl88++PKI+0c7oY7Ri0nI7yo89kCEHl7LZ2A39OFyw==
|
||||
dependencies:
|
||||
"@types/lodash" "^4.14.136"
|
||||
"@types/ws" "^7.2.0"
|
||||
bignumber.js "^9.0.0"
|
||||
bip32 "^2.0.6"
|
||||
bip39 "^3.0.4"
|
||||
https-proxy-agent "^5.0.0"
|
||||
lodash "^4.17.4"
|
||||
ripple-address-codec "^4.1.1"
|
||||
ripple-binary-codec "^1.1.3"
|
||||
ripple-keypairs "^1.0.3"
|
||||
ripple-lib-transactionparser "0.8.2"
|
||||
ws "^7.2.0"
|
||||
|
||||
180
content/_code-samples/submit-and-verify/submit-and-verify2.js
Normal file
180
content/_code-samples/submit-and-verify/submit-and-verify2.js
Normal file
@@ -0,0 +1,180 @@
|
||||
// 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]
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ In ripple-lib 1.x all methods and properties were on instances of the `RippleAPI
|
||||
| `request(command, options)` | `Client.request(options)` | The `command` field moved into the `options` object for consistency with the WebSocket API. |
|
||||
| `hasNextPage(response)` | ***TODO (check for marker?)*** | See also: `Client.requestNextPage()` and `Client.requestAll()` |
|
||||
| `requestNextPage(command, options, response)` | `Client.requestNextPage(response)` | |
|
||||
| `computeBinaryTransactionHash(tx_blob)` | `computeBinaryTransactionSigningHash(tx_blob)` | |
|
||||
| `computeBinaryTransactionHash(tx_blob)` | `computeSignedTransactionHash(tx_blob)` | |
|
||||
| `classicAddressToXAddress(address)` | ***TBD moving to utils?*** | |
|
||||
| `xAddressToClassicAddress(xAddress)` | ***TBD moving to utils?*** | |
|
||||
| `renameCounterpartyToIssuer(object)` | (None) | xrpl.js always uses `issuer` already. |
|
||||
|
||||
Reference in New Issue
Block a user