mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-27 15:45:48 +00:00
Docs: Snippets (#1729)
* refactor: adds snippets for sendEscrow, claimPayChannel, reliableSubmission, getTransaction, partialPayment, getPaths
This commit is contained in:
@@ -62,12 +62,14 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
{
|
||||
// TODO: remove when snippets are written
|
||||
files: ['snippets/src/*.ts'],
|
||||
rules: {
|
||||
'max-len': 'off',
|
||||
'import/unambiguous': 'off',
|
||||
'import/no-unused-modules': 'off',
|
||||
// Each file has a particular flow.
|
||||
'max-lines-per-function': 'off',
|
||||
'max-statements': 'off',
|
||||
// Snippets have logs on console to better understand the working.
|
||||
'no-console': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -97,8 +97,8 @@
|
||||
"lint": "eslint . --ext .ts --max-warnings 0",
|
||||
"perf": "./scripts/perf_test.sh",
|
||||
"compile:snippets": "tsc -p snippets/tsconfig.json",
|
||||
"start:snippet": "npm run compile:snippets && node ./snippets/dist/start.js",
|
||||
"inspect:snippet": "npm run compile:snippets && node inspect ./snippets/dist/start.js"
|
||||
"start:snippet": "npm run compile:snippets && node",
|
||||
"inspect:snippet": "npm run compile:snippets && node inspect"
|
||||
},
|
||||
"prettier": "@xrplf/prettier-config",
|
||||
"repository": {
|
||||
|
||||
74
snippets/src/claimPayChannel.ts
Normal file
74
snippets/src/claimPayChannel.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import {
|
||||
AccountObjectsRequest,
|
||||
Client,
|
||||
PaymentChannelCreate,
|
||||
PaymentChannelClaim,
|
||||
hashes,
|
||||
} from '../../dist/npm'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
void claimPayChannel()
|
||||
|
||||
// The snippet walks us through creating and claiming a Payment Channel.
|
||||
async function claimPayChannel(): Promise<void> {
|
||||
await client.connect()
|
||||
|
||||
// creating wallets as prerequisite
|
||||
const { wallet: wallet1 } = await client.fundWallet()
|
||||
const { wallet: wallet2 } = await client.fundWallet()
|
||||
|
||||
console.log('Balances of wallets before Payment Channel is claimed:')
|
||||
console.log(await client.getXrpBalance(wallet1.classicAddress))
|
||||
console.log(await client.getXrpBalance(wallet2.classicAddress))
|
||||
|
||||
// create a Payment Channel and submit and wait for tx to be validated
|
||||
const paymentChannelCreate: PaymentChannelCreate = {
|
||||
TransactionType: 'PaymentChannelCreate',
|
||||
Account: wallet1.classicAddress,
|
||||
Amount: '100',
|
||||
Destination: wallet2.classicAddress,
|
||||
SettleDelay: 86400,
|
||||
PublicKey: wallet1.publicKey,
|
||||
}
|
||||
|
||||
const paymentChannelResponse = await client.submitAndWait(
|
||||
paymentChannelCreate,
|
||||
{ wallet: wallet1 },
|
||||
)
|
||||
console.log(paymentChannelResponse)
|
||||
|
||||
// check that the object was actually created
|
||||
const accountObjectsRequest: AccountObjectsRequest = {
|
||||
command: 'account_objects',
|
||||
account: wallet1.classicAddress,
|
||||
}
|
||||
|
||||
const accountObjects = (await client.request(accountObjectsRequest)).result
|
||||
.account_objects
|
||||
|
||||
console.log(accountObjects)
|
||||
|
||||
// destination claims the Payment Channel and we see the balances to verify.
|
||||
const paymentChannelClaim: PaymentChannelClaim = {
|
||||
Account: wallet2.classicAddress,
|
||||
TransactionType: 'PaymentChannelClaim',
|
||||
Channel: hashes.hashPaymentChannel(
|
||||
wallet1.classicAddress,
|
||||
wallet2.classicAddress,
|
||||
paymentChannelResponse.result.Sequence ?? 0,
|
||||
),
|
||||
Amount: '100',
|
||||
}
|
||||
|
||||
const channelClaimResponse = await client.submit(paymentChannelClaim, {
|
||||
wallet: wallet1,
|
||||
})
|
||||
console.log(channelClaimResponse)
|
||||
|
||||
console.log('Balances of wallets after Payment Channel is claimed:')
|
||||
console.log(await client.getXrpBalance(wallet1.classicAddress))
|
||||
console.log(await client.getXrpBalance(wallet2.classicAddress))
|
||||
|
||||
await client.disconnect()
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
// import * as codec from 'ripple-binary-codec'
|
||||
|
||||
/*
|
||||
* const original = codec.decode(
|
||||
* '12000022800200002400000001201B00EF81E661EC6386F26FC0FFFF0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461968400000000000000C6940000000000000646AD3504529A0465E2E0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D1664619732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D87446304402200A693FB5CA6B21250EBDFD8CFF526EE0DF7C9E4E31EB0660692E75E6A93BF5F802203CC39463DDA21386898CA31E18AD1A6828647D65741DD637BAD71BC83E29DB9481145E7B112523F68D2F5E879DB4EAC51C6698A693048314CA6EDC7A28252DAEA6F2045B24F4D7C333E146170112300000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461900'
|
||||
* )
|
||||
*/
|
||||
|
||||
/*
|
||||
* const test = codec.decode(
|
||||
* '12000022800200002400000017201B008694F261EC6386F26FC0FFFF0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461968400000000000000C6940000000000000646AD3504529A0465E2E0000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D1664619732102F89EAEC7667B30F33D0687BBA86C3FE2A08CCA40A9186C5BDE2DAA6FA97A37D874473045022100D8B57E8E06EAE27B1343AF8CAD3F501E18260CCF8BCED08066074106F0F191A3022058FEA6CE9E7FA69D1244C3A70F18983CC2DAF0B10CBB86A6677CF2A5D2B8A68081145E7B112523F68D2F5E879DB4EAC51C6698A693048314CA6EDC7A28252DAEA6F2045B24F4D7C333E146170112300000000000000000000000005553440000000000054F6F784A58F9EFB0A9EB90B83464F9D166461900'
|
||||
* )
|
||||
*/
|
||||
|
||||
// console.log('original:', JSON.stringify(original))
|
||||
|
||||
// console.log('test:', JSON.stringify(test))
|
||||
@@ -1,26 +1,39 @@
|
||||
/*
|
||||
* import {Client} from '../../dist/npm'
|
||||
* import {TransactionMetadata} from 'xrpl-local/models/common/transaction'
|
||||
*/
|
||||
import { Client, LedgerResponse, TxResponse } from '../../dist/npm'
|
||||
|
||||
// const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
// getTransaction()
|
||||
async function getTransaction(): Promise<void> {
|
||||
await client.connect()
|
||||
const ledger: LedgerResponse = await client.request({
|
||||
command: 'ledger',
|
||||
transactions: true,
|
||||
ledger_index: 'validated',
|
||||
})
|
||||
console.log(ledger)
|
||||
|
||||
/*
|
||||
* async function getTransaction() {
|
||||
* await client.connect()
|
||||
* const ledger = await client.request({command: 'ledger', transactions: true})
|
||||
* console.log(ledger)
|
||||
* const tx = await client.request({
|
||||
* command: 'tx',
|
||||
* transaction: ledger.result.ledger.transactions[0] as string
|
||||
* })
|
||||
* console.log(tx)
|
||||
* console.log(
|
||||
* 'deliveredAmount:',
|
||||
* (tx.result.meta as TransactionMetadata).DeliveredAmount
|
||||
* )
|
||||
* process.exit(0)
|
||||
* }
|
||||
*/
|
||||
const transactions = ledger.result.ledger.transactions
|
||||
if (transactions) {
|
||||
const tx: TxResponse = await client.request({
|
||||
command: 'tx',
|
||||
transaction: transactions[0],
|
||||
})
|
||||
console.log(tx)
|
||||
|
||||
// The meta field would be a string(hex) when the `binary` parameter is `true` for the `tx` request.
|
||||
if (tx.result.meta == null) {
|
||||
throw new Error('meta not included in the response')
|
||||
}
|
||||
/*
|
||||
* delivered_amount is the amount actually received by the destination account.
|
||||
* Use this field to determine how much was delivered, regardless of whether the transaction is a partial payment.
|
||||
* https://xrpl.org/transaction-metadata.html#delivered_amount
|
||||
*/
|
||||
if (typeof tx.result.meta !== 'string') {
|
||||
console.log('delivered_amount:', tx.result.meta.delivered_amount)
|
||||
}
|
||||
}
|
||||
|
||||
await client.disconnect()
|
||||
}
|
||||
|
||||
void getTransaction()
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* import {Client} from '../../dist/npm'
|
||||
* import {AccountFlags} from '../../dist/npm/common/constants'
|
||||
*/
|
||||
|
||||
// const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
// parseAccountFlags()
|
||||
|
||||
/*
|
||||
* async function parseAccountFlags() {
|
||||
* await client.connect()
|
||||
* const account_info = await client.request({
|
||||
* command: 'account_info',
|
||||
* account: 'rKsdkGhyZH6b2Zzd5hNnEqSv2wpznn4n6N'
|
||||
* })
|
||||
* const flags = account_info.result.account_data.Flags
|
||||
* for (const flagName in AccountFlags) {
|
||||
* if (flags & AccountFlags[flagName]) {
|
||||
* console.log(`${flagName} enabled`)
|
||||
* }
|
||||
* }
|
||||
* process.exit(0)
|
||||
* }
|
||||
*/
|
||||
91
snippets/src/partialPayment.ts
Normal file
91
snippets/src/partialPayment.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { Client, Payment, PaymentFlags, TrustSet } from '../../dist/npm'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
// This snippet walks us through partial payment.
|
||||
async function partialPayment(): Promise<void> {
|
||||
await client.connect()
|
||||
|
||||
// creating wallets as prerequisite
|
||||
const { wallet: wallet1 } = await client.fundWallet()
|
||||
const { wallet: wallet2 } = await client.fundWallet()
|
||||
|
||||
// create a trustline to issue an IOU `FOO` and set limit on it.
|
||||
const trust_set_tx: TrustSet = {
|
||||
TransactionType: 'TrustSet',
|
||||
Account: wallet2.classicAddress,
|
||||
LimitAmount: {
|
||||
currency: 'FOO',
|
||||
issuer: wallet1.classicAddress,
|
||||
// Value for the new IOU - 10000000000 - is arbitarily chosen.
|
||||
value: '10000000000',
|
||||
},
|
||||
}
|
||||
|
||||
await client.submitAndWait(trust_set_tx, {
|
||||
wallet: wallet2,
|
||||
})
|
||||
|
||||
console.log('Balances after trustline is created')
|
||||
console.log(await client.getBalances(wallet1.classicAddress))
|
||||
console.log(await client.getBalances(wallet2.classicAddress))
|
||||
|
||||
// Initially, the issuer(wallet1) sends an amount to the other account(wallet2)
|
||||
const issue_quantity = '3840'
|
||||
const payment: Payment = {
|
||||
TransactionType: 'Payment',
|
||||
Account: wallet1.classicAddress,
|
||||
Amount: {
|
||||
currency: 'FOO',
|
||||
value: issue_quantity,
|
||||
issuer: wallet1.classicAddress,
|
||||
},
|
||||
Destination: wallet2.classicAddress,
|
||||
}
|
||||
|
||||
// submit payment
|
||||
const initialPayment = await client.submitAndWait(payment, {
|
||||
wallet: wallet1,
|
||||
})
|
||||
console.log(initialPayment)
|
||||
|
||||
console.log('Balances after issuer(wallet1) sends IOU("FOO") to wallet2')
|
||||
console.log(await client.getBalances(wallet1.classicAddress))
|
||||
console.log(await client.getBalances(wallet2.classicAddress))
|
||||
|
||||
/*
|
||||
* Send money less than the amount specified on 2 conditions:
|
||||
* 1. Sender has less money than the aamount specified in the payment Tx.
|
||||
* 2. Sender has the tfPartialPayment flag activated.
|
||||
*
|
||||
* Other ways to specify flags are by using Hex code and decimal code.
|
||||
* eg. For partial payment(tfPartialPayment)
|
||||
* decimal ->131072, hex -> 0x00020000
|
||||
*/
|
||||
const partialPaymentTx: Payment = {
|
||||
TransactionType: 'Payment',
|
||||
Account: wallet2.classicAddress,
|
||||
Amount: {
|
||||
currency: 'FOO',
|
||||
value: '4000',
|
||||
issuer: wallet1.classicAddress,
|
||||
},
|
||||
Destination: wallet1.classicAddress,
|
||||
Flags: PaymentFlags.tfPartialPayment,
|
||||
}
|
||||
|
||||
// submit payment
|
||||
const submitResponse = await client.submitAndWait(partialPaymentTx, {
|
||||
wallet: wallet2,
|
||||
})
|
||||
console.log(submitResponse)
|
||||
|
||||
console.log(
|
||||
"Balances after Partial Payment, when wallet2 tried to send 4000 FOO's",
|
||||
)
|
||||
console.log(await client.getBalances(wallet1.classicAddress))
|
||||
console.log(await client.getBalances(wallet2.classicAddress))
|
||||
|
||||
await client.disconnect()
|
||||
}
|
||||
void partialPayment()
|
||||
@@ -1,63 +1,49 @@
|
||||
// import {Client} from '../../dist/npm'
|
||||
import { Client, Payment, RipplePathFindResponse } from '../../dist/npm'
|
||||
|
||||
/*
|
||||
* const client = new Client(
|
||||
* // 'wss://s.altnet.rippletest.net:51233'
|
||||
* // 'ws://35.158.96.209:51233'
|
||||
* 'ws://34.210.87.206:51233'
|
||||
* )
|
||||
*/
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
// sign()
|
||||
async function createTxWithPaths(): Promise<void> {
|
||||
await client.connect()
|
||||
|
||||
/*
|
||||
* async function sign() {
|
||||
* await client.connect()
|
||||
* const pathfind: any = {
|
||||
* source: {
|
||||
* address: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
|
||||
* amount: {
|
||||
* currency: 'drops',
|
||||
* value: '100'
|
||||
* }
|
||||
* },
|
||||
* destination: {
|
||||
* address: 'rKT4JX4cCof6LcDYRz8o3rGRu7qxzZ2Zwj',
|
||||
* amount: {
|
||||
* currency: 'USD',
|
||||
* counterparty: 'rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc'
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
const { wallet } = await client.fundWallet()
|
||||
const destination_account = 'rKT4JX4cCof6LcDYRz8o3rGRu7qxzZ2Zwj'
|
||||
const destination_amount = {
|
||||
value: '0.001',
|
||||
currency: 'USD',
|
||||
issuer: 'rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc',
|
||||
}
|
||||
|
||||
/*
|
||||
* await client
|
||||
* .getPaths(pathfind)
|
||||
* .then(async (data) => {
|
||||
* console.log('paths:', JSON.stringify(data))
|
||||
* const fakeSecret = 'shsWGZcmZz6YsWWmcnpfr6fLTdtFV'
|
||||
*/
|
||||
const request = {
|
||||
command: 'ripple_path_find',
|
||||
source_account: wallet.classicAddress,
|
||||
source_currencies: [
|
||||
{
|
||||
currency: 'XRP',
|
||||
},
|
||||
],
|
||||
destination_account,
|
||||
destination_amount,
|
||||
}
|
||||
|
||||
/*
|
||||
* pathfind.paths = data[0].paths
|
||||
* pathfind.destination = data[0].destination
|
||||
* await client
|
||||
* .preparePayment('r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59', pathfind)
|
||||
* .then((ret) => {
|
||||
* const signed = client.sign(ret.txJSON, fakeSecret)
|
||||
* console.log('signed:', signed)
|
||||
* })
|
||||
* .catch((err) => {
|
||||
* console.log('ERR 1:', JSON.stringify(err))
|
||||
* })
|
||||
* })
|
||||
* .catch((err) => {
|
||||
* console.log('ERR 2:', err)
|
||||
* })
|
||||
*/
|
||||
const resp: RipplePathFindResponse = await client.request(request)
|
||||
console.log(resp)
|
||||
|
||||
/*
|
||||
* client.disconnect()
|
||||
* }
|
||||
*/
|
||||
const paths = resp.result.alternatives[0].paths_computed
|
||||
console.log(paths)
|
||||
|
||||
const tx: Payment = {
|
||||
TransactionType: 'Payment',
|
||||
Account: wallet.classicAddress,
|
||||
Amount: destination_amount,
|
||||
Destination: destination_account,
|
||||
Paths: paths,
|
||||
}
|
||||
|
||||
await client.autofill(tx)
|
||||
const signed = wallet.sign(tx)
|
||||
console.log('signed:', signed)
|
||||
|
||||
await client.disconnect()
|
||||
}
|
||||
|
||||
void createTxWithPaths()
|
||||
|
||||
@@ -1,231 +1,83 @@
|
||||
// import https = require('https')
|
||||
import { Client, Payment } from '../../dist/npm'
|
||||
|
||||
// import {Client, AccountInfoResponse, LedgerClosedEvent} from '../../dist/npm'
|
||||
|
||||
// /**
|
||||
// * When implementing Reliable Transaction Submission, there are many potential solutions, each with different trade-offs. The main decision points are:
|
||||
// * 1) Transaction preparation:
|
||||
// * - How do we decide which account sequence and LastLedgerSequence numbers to use?
|
||||
// * (To prevent unintentional duplicate transactions, an {account, account_sequence} pair can be used as a transaction's idempotency key)
|
||||
// * - How do we decide how much to pay for the transaction fee? (If our transactions have been failing due to low fee, we should consider increasing this value)
|
||||
// * 2) Transaction status retrieval. Options include:
|
||||
// * - Poll for transaction status:
|
||||
// * - On a regular interval (e.g. Every 3-5 seconds), or
|
||||
// * - When a new validated ledger is detected
|
||||
// * + (To accommodate an edge case in transaction retrieval, check the sending account's Sequence number to confirm that it has the expected value;
|
||||
// * alternatively, wait until a few additional ledgers have been validated before deciding that a transaction has definitively not been included in a validated ledger)
|
||||
// * - Listen for transaction status: scan all validated transactions to see if our transactions are among them
|
||||
// * 3) What do we do when a transaction fails? It is possible to implement retry logic, but caution is advised. Note that there are a few ways for a transaction to fail:
|
||||
// * A) `tec`: The transaction was included in a ledger but only claimed the transaction fee
|
||||
// * B) `tesSUCCESS` but unexpected result: The transaction was successful but did not have the expected result. This generally does not occur for XRP-to-XRP payments
|
||||
// * C) The transaction was not, and never will be, included in a validated ledger [3C].
|
||||
// *
|
||||
// * References:
|
||||
// * - https://xrpl.org/reliable-transaction-submission.html
|
||||
// * - https://xrpl.org/send-xrp.html
|
||||
// * - https://xrpl.org/look-up-transaction-results.html
|
||||
// * - https://xrpl.org/get-started-with-rippleapi-for-javascript.html
|
||||
// * - https://xrpl.org/monitor-incoming-payments-with-websocket.html.
|
||||
// *
|
||||
// * For the implementation in this example, we have made the following decisions:
|
||||
// * 1) The script will choose the account sequence and LastLedgerSequence numbers automatically. We allow xrpl.js to choose the fee.
|
||||
// * Payments are defined upfront, and idempotency is not needed. If the script is run a second time, duplicate payments will result.
|
||||
// * 2) We will listen for notification that a new validated ledger has been found, and poll for transaction status at that time.
|
||||
// * Futhermore, as a precaution, we will wait until the server is 3 ledgers past the transaction's LastLedgerSequence
|
||||
// * (with the transaction nowhere to be seen) before deciding that it has definitively failed per [3C]
|
||||
// * 3) Transactions will not be automatically retried. Transactions are limited to XRP-to-XRP payments and cannot "succeed" in an unexpected way.
|
||||
// */
|
||||
// reliableTransactionSubmissionExample()
|
||||
|
||||
// async function reliableTransactionSubmissionExample() {
|
||||
// /**
|
||||
// * Array of payments to execute.
|
||||
// *
|
||||
// * For brevity, these are XRP-to-XRP payments, taking a source, destination, and an amount in drops.
|
||||
// *
|
||||
// * The script will attempt to make all of these payments as quickly as possible, and report the final status of each. Transactions that fail are NOT retried.
|
||||
// */
|
||||
// const payments = []
|
||||
|
||||
/*
|
||||
* const sourceAccount = (await generateTestnetAccount()).account
|
||||
* console.log(
|
||||
* `Generated new Testnet account: ${sourceAccount.classicAddress}/${sourceAccount.secret}`
|
||||
* )
|
||||
* // Send amounts from 1 drop to 10 drops
|
||||
* for (let i = 1; i <= 10; i++) {
|
||||
* payments.push({
|
||||
* source: sourceAccount,
|
||||
* destination: 'rhsoCozhUxwcyQgzFi1FVRoMVQgk7cZd4L', // Random Testnet destination
|
||||
* amount_drops: i.toString()
|
||||
* })
|
||||
* }
|
||||
* const results = await performPayments(payments)
|
||||
* console.log(JSON.stringify(results, null, 2))
|
||||
* process.exit(0)
|
||||
* }
|
||||
/**
|
||||
* When implementing Reliable Transaction Submission, there are many potential solutions, each with different trade-offs.
|
||||
* The main decision points are:
|
||||
* 1) Transaction preparation:
|
||||
* - The autofill function as a part of the submitAndWait should be able to correctly populate
|
||||
* values for the fields Sequence, LastLedgerSequence and Fee.
|
||||
* 2) Transaction status retrieval. Options include:
|
||||
* - Poll for transaction status:
|
||||
* - On a regular interval (e.g. Every 3-5 seconds), or
|
||||
* - When a new validated ledger is detected
|
||||
* + (To accommodate an edge case in transaction retrieval,
|
||||
* check the sending account's Sequence number to confirm that it has the expected value;
|
||||
* alternatively, wait until a few additional ledgers have been validated before deciding that a
|
||||
* transaction has definitively not been included in a validated ledger)
|
||||
* - Listen for transaction status: scan all validated transactions to see if our transactions are among them
|
||||
* 3) What do we do when a transaction fails? It is possible to implement retry logic, but caution is advised.
|
||||
* Note that there are a few ways for a transaction to fail:
|
||||
* A) `tec`: The transaction was included in a ledger but only claimed the transaction fee
|
||||
* B) `tesSUCCESS` but unexpected result: The transaction was successful but did not have the expected result.
|
||||
* This generally does not occur for XRP-to-XRP payments
|
||||
* C) The transaction was not, and never will be, included in a validated ledger [3C].
|
||||
*
|
||||
* References:
|
||||
* - https://xrpl.org/reliable-transaction-submission.html
|
||||
* - https://xrpl.org/send-xrp.html
|
||||
* - https://xrpl.org/look-up-transaction-results.html
|
||||
* - https://xrpl.org/monitor-incoming-payments-with-websocket.html.
|
||||
*
|
||||
* For the implementation in this example, we have made the following decisions:
|
||||
* 1) We allow the autofill function as a part of submitAndWait to fill up the account sequence,
|
||||
* LastLedgerSequence and Fee. Payments are defined upfront, and idempotency is not needed.
|
||||
* If the script is run a second time, duplicate payments will result.
|
||||
* 2) We will rely on the xrpl.js submitAndWait function to get us the transaction submission result after the wait time.
|
||||
* 3) Transactions will not be automatically retried. Transactions are limited to XRP-to-XRP payments
|
||||
* and cannot "succeed" in an unexpected way.
|
||||
*/
|
||||
|
||||
/*
|
||||
* async function performPayments(payments) {
|
||||
* const finalResults = []
|
||||
* const txFinalizedPromises = []
|
||||
* const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
* await client.connect()
|
||||
*/
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
/*
|
||||
* for (let i = 0; i < payments.length; i++) {
|
||||
* const payment = payments[i]
|
||||
* const account_info: AccountInfoResponse = await client.request({
|
||||
* command: 'account_info',
|
||||
* account: payment.source.classicAddress,
|
||||
* ledger_index: 'current'
|
||||
* })
|
||||
* const sequence = account_info.result.account_data.Sequence
|
||||
* const preparedPayment = await client.preparePayment(
|
||||
* payment.source.classicAddress,
|
||||
* {
|
||||
* source: {
|
||||
* address: payment.source.classicAddress,
|
||||
* amount: {
|
||||
* value: payment.amount_drops,
|
||||
* currency: 'drops'
|
||||
* }
|
||||
* },
|
||||
* destination: {
|
||||
* address: payment.destination,
|
||||
* minAmount: {
|
||||
* value: payment.amount_drops,
|
||||
* currency: 'drops'
|
||||
* }
|
||||
* }
|
||||
* },
|
||||
* {
|
||||
* sequence
|
||||
* }
|
||||
* )
|
||||
* const signed = client.sign(preparedPayment.txJSON, payment.source.secret)
|
||||
* finalResults.push({
|
||||
* id: signed.id
|
||||
* })
|
||||
* const response = await client.request({
|
||||
* command: 'submit',
|
||||
* tx_blob: signed.signedTransaction
|
||||
* })
|
||||
*/
|
||||
void sendReliableTx()
|
||||
|
||||
/*
|
||||
* // Most of the time we'll get 'tesSUCCESS' or (after many submissions) 'terQUEUED'
|
||||
* console.log(`tx ${i} - tentative: ${response.result.engine_result}`)
|
||||
*/
|
||||
async function sendReliableTx(): Promise<void> {
|
||||
await client.connect()
|
||||
|
||||
/*
|
||||
* const txFinalizedPromise = new Promise<void>((resolve) => {
|
||||
* const ledgerClosedCallback = async (event: LedgerClosedEvent) => {
|
||||
* let status
|
||||
* try {
|
||||
* status = await client.request({command: 'tx', transaction: signed.id})
|
||||
* } catch (e) {
|
||||
* // Typical error when the tx hasn't been validated yet:
|
||||
* if (e.name !== 'MissingLedgerHistoryError') {
|
||||
* console.log(e)
|
||||
* }
|
||||
*/
|
||||
// creating wallets as prerequisite
|
||||
const { wallet: wallet1 } = await client.fundWallet()
|
||||
const { wallet: wallet2 } = await client.fundWallet()
|
||||
|
||||
/*
|
||||
* if (
|
||||
* event.ledger_index >
|
||||
* preparedPayment.instructions.maxLedgerVersion + 3
|
||||
* ) {
|
||||
* // Assumptions:
|
||||
* // - We are still connected to the same rippled server
|
||||
* // - No ledger gaps occurred
|
||||
* // - All ledgers between the time we submitted the tx and now have been checked for the tx
|
||||
* status = {
|
||||
* finalResult:
|
||||
* 'Transaction was not, and never will be, included in a validated ledger'
|
||||
* }
|
||||
* } else {
|
||||
* // Check again later:
|
||||
* client.connection.once('ledgerClosed', ledgerClosedCallback)
|
||||
* return
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
console.log('Balances of wallets before Payment tx')
|
||||
console.log(
|
||||
await client.getXrpBalance(wallet1.classicAddress),
|
||||
await client.getXrpBalance(wallet2.classicAddress),
|
||||
)
|
||||
|
||||
/*
|
||||
* for (let j = 0; j < finalResults.length; j++) {
|
||||
* if (finalResults[j].id === signed.id) {
|
||||
* finalResults[j].result = status.address
|
||||
* ? {
|
||||
* source: status.address,
|
||||
* destination: status.specification.destination.address,
|
||||
* deliveredAmount: status.outcome.deliveredAmount,
|
||||
* result: status.outcome.result,
|
||||
* timestamp: status.outcome.timestamp,
|
||||
* ledgerVersion: status.outcome.ledgerVersion
|
||||
* }
|
||||
* : status
|
||||
* process.stdout.write('.')
|
||||
* return resolve()
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* client.connection.once('ledgerClosed', ledgerClosedCallback)
|
||||
* })
|
||||
* txFinalizedPromises.push(txFinalizedPromise)
|
||||
* }
|
||||
* await Promise.all(txFinalizedPromises)
|
||||
* return finalResults
|
||||
* }
|
||||
*/
|
||||
// create a Payment tx and submit and wait for tx to be validated
|
||||
const payment: Payment = {
|
||||
TransactionType: 'Payment',
|
||||
Account: wallet1.classicAddress,
|
||||
Amount: '1000',
|
||||
Destination: wallet2.classicAddress,
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Generate a new Testnet account by requesting one from the faucet.
|
||||
// */
|
||||
// async function generateTestnetAccount(): Promise<{
|
||||
// account: {
|
||||
// xAddress: string
|
||||
// classicAddress
|
||||
// string
|
||||
// secret: string
|
||||
// }
|
||||
// balance: number
|
||||
// }> {
|
||||
// const options = {
|
||||
// hostname: 'faucet.altnet.rippletest.net',
|
||||
// port: 443,
|
||||
// path: '/accounts',
|
||||
// method: 'POST'
|
||||
// }
|
||||
// return new Promise((resolve, reject) => {
|
||||
// const request = https.request(options, (response) => {
|
||||
// const chunks = []
|
||||
// response.on('data', (d) => {
|
||||
// chunks.push(d)
|
||||
// })
|
||||
// response.on('end', () => {
|
||||
// const body = Buffer.concat(chunks).toString()
|
||||
const paymentResponse = await client.submitAndWait(payment, {
|
||||
wallet: wallet1,
|
||||
})
|
||||
console.log('\nTransaction was submitted.\n')
|
||||
const txResponse = await client.request({
|
||||
command: 'tx',
|
||||
transaction: paymentResponse.result.hash,
|
||||
})
|
||||
// With the following reponse we are able to see that the tx was indeed validated.
|
||||
console.log('Validated:', txResponse.result.validated)
|
||||
|
||||
/*
|
||||
* // "application/json; charset=utf-8"
|
||||
* if (response.headers['content-type'].startsWith('application/json')) {
|
||||
* resolve(JSON.parse(body))
|
||||
* } else {
|
||||
* reject({
|
||||
* statusCode: response.statusCode,
|
||||
* contentType: response.headers['content-type'],
|
||||
* body
|
||||
* })
|
||||
* }
|
||||
* })
|
||||
* })
|
||||
* request.on('error', (error) => {
|
||||
* console.error(error)
|
||||
* reject(error)
|
||||
* })
|
||||
* request.end()
|
||||
* })
|
||||
* }
|
||||
*/
|
||||
console.log('Balances of wallets after Payment tx:')
|
||||
console.log(
|
||||
await client.getXrpBalance(wallet1.classicAddress),
|
||||
await client.getXrpBalance(wallet2.classicAddress),
|
||||
)
|
||||
|
||||
await client.disconnect()
|
||||
}
|
||||
|
||||
77
snippets/src/sendEscrow.ts
Normal file
77
snippets/src/sendEscrow.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import {
|
||||
AccountObjectsRequest,
|
||||
Client,
|
||||
EscrowCreate,
|
||||
EscrowFinish,
|
||||
isoTimeToRippleTime,
|
||||
} from '../../dist/npm'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
void sendEscrow()
|
||||
|
||||
// The snippet walks us through creating and finishing escrows.
|
||||
async function sendEscrow(): Promise<void> {
|
||||
await client.connect()
|
||||
|
||||
// creating wallets as prerequisite
|
||||
const { wallet: wallet1 } = await client.fundWallet()
|
||||
const { wallet: wallet2 } = await client.fundWallet()
|
||||
|
||||
console.log('Balances of wallets before Escrow tx was created:')
|
||||
console.log(
|
||||
await client.getXrpBalance(wallet1.classicAddress),
|
||||
await client.getXrpBalance(wallet2.classicAddress),
|
||||
)
|
||||
|
||||
// finish 2 seconds after the escrow is actually created and tx is validated.
|
||||
const finishAfter = isoTimeToRippleTime(Date()) + 2
|
||||
|
||||
// creating an Escrow using `EscrowCreate` and submit and wait for tx to be validated.
|
||||
const createTx: EscrowCreate = {
|
||||
TransactionType: 'EscrowCreate',
|
||||
Account: wallet1.address,
|
||||
Destination: wallet2.address,
|
||||
Amount: '1000000',
|
||||
FinishAfter: finishAfter,
|
||||
}
|
||||
|
||||
const createEscrowResponse = await client.submitAndWait(createTx, {
|
||||
wallet: wallet1,
|
||||
})
|
||||
|
||||
console.log(createEscrowResponse)
|
||||
|
||||
// check that the object was actually created
|
||||
const accountObjectsRequest: AccountObjectsRequest = {
|
||||
command: 'account_objects',
|
||||
account: wallet1.classicAddress,
|
||||
}
|
||||
|
||||
const accountObjects = (await client.request(accountObjectsRequest)).result
|
||||
.account_objects
|
||||
|
||||
console.log("Escrow object exists in `wallet1`'s account")
|
||||
console.log(accountObjects)
|
||||
|
||||
// the creator finishes the Escrow using `EscrowFinish` to release the Escrow
|
||||
const finishTx: EscrowFinish = {
|
||||
TransactionType: 'EscrowFinish',
|
||||
Account: wallet1.classicAddress,
|
||||
Owner: wallet1.classicAddress,
|
||||
OfferSequence: Number(createEscrowResponse.result.Sequence),
|
||||
}
|
||||
|
||||
await client.submit(finishTx, {
|
||||
wallet: wallet1,
|
||||
})
|
||||
|
||||
// we see the balances to verify.
|
||||
console.log('Balances of wallets after Escrow was sent')
|
||||
console.log(
|
||||
await client.getXrpBalance(wallet1.classicAddress),
|
||||
await client.getXrpBalance(wallet2.classicAddress),
|
||||
)
|
||||
|
||||
await client.disconnect()
|
||||
}
|
||||
54
snippets/src/setRegularKey.ts
Normal file
54
snippets/src/setRegularKey.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { Client, Payment, SetRegularKey } from '../../dist/npm'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
/*
|
||||
* The snippet walks us through an example usage of RegularKey.
|
||||
* Later,
|
||||
*/
|
||||
async function setRegularKey(): Promise<void> {
|
||||
await client.connect()
|
||||
const { wallet: wallet1 } = await client.fundWallet()
|
||||
const { wallet: wallet2 } = await client.fundWallet()
|
||||
const { wallet: regularKeyWallet } = await client.fundWallet()
|
||||
|
||||
console.log('Balances before payment')
|
||||
console.log(await client.getXrpBalance(wallet1.classicAddress))
|
||||
console.log(await client.getXrpBalance(wallet2.classicAddress))
|
||||
|
||||
// assigns key-pair(regularKeyWallet) to wallet1 using `SetRegularKey`.
|
||||
const tx: SetRegularKey = {
|
||||
TransactionType: 'SetRegularKey',
|
||||
Account: wallet1.classicAddress,
|
||||
RegularKey: regularKeyWallet.classicAddress,
|
||||
}
|
||||
const response = await client.submitAndWait(tx, {
|
||||
wallet: wallet1,
|
||||
})
|
||||
|
||||
console.log('Response for successful SetRegularKey tx')
|
||||
console.log(response)
|
||||
|
||||
/*
|
||||
* when wallet1 sends payment to wallet2 and
|
||||
* signs using the regular key wallet, the transaction goes through.
|
||||
*/
|
||||
const payment: Payment = {
|
||||
TransactionType: 'Payment',
|
||||
Account: wallet1.classicAddress,
|
||||
Destination: wallet2.classicAddress,
|
||||
Amount: '1000',
|
||||
}
|
||||
|
||||
const submitTx = await client.submit(payment, {
|
||||
wallet: wallet1,
|
||||
})
|
||||
console.log('Response for tx signed using Regular Key:')
|
||||
console.log(submitTx)
|
||||
console.log('Balances after payment:')
|
||||
console.log(await client.getXrpBalance(wallet1.classicAddress))
|
||||
console.log(await client.getXrpBalance(wallet2.classicAddress))
|
||||
|
||||
await client.disconnect()
|
||||
}
|
||||
void setRegularKey()
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Transaction } from '../transactions'
|
||||
import { TransactionMetadata } from '../transactions/metadata'
|
||||
import { Transaction, TransactionMetadata } from '../transactions'
|
||||
|
||||
import LedgerEntry from './LedgerEntry'
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { LedgerIndex } from '../common'
|
||||
import { Transaction } from '../transactions'
|
||||
import { TransactionMetadata } from '../transactions/metadata'
|
||||
import { Transaction, TransactionMetadata } from '../transactions'
|
||||
|
||||
import { BaseRequest, BaseResponse } from './baseMethod'
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { LedgerIndex } from '../common'
|
||||
import { Transaction } from '../transactions'
|
||||
import { TransactionMetadata } from '../transactions/metadata'
|
||||
import { Transaction, TransactionMetadata } from '../transactions'
|
||||
|
||||
import { BaseRequest, BaseResponse } from './baseMethod'
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Transaction } from '../transactions'
|
||||
import { TransactionMetadata } from '../transactions/metadata'
|
||||
import { Transaction, TransactionMetadata } from '../transactions'
|
||||
|
||||
import { BaseRequest, BaseResponse } from './baseMethod'
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export { validate, TransactionAndMetadata, Transaction } from './transaction'
|
||||
export { TransactionMetadata } from './metadata'
|
||||
export {
|
||||
AccountSetAsfFlags,
|
||||
AccountSetTfFlags,
|
||||
|
||||
@@ -9,8 +9,7 @@ import { decode, encode } from 'ripple-binary-codec'
|
||||
import { ValidationError, XrplError } from '../../errors'
|
||||
import type { Ledger } from '../../models/ledger'
|
||||
import { LedgerEntry } from '../../models/ledger'
|
||||
import { Transaction } from '../../models/transactions'
|
||||
import { TransactionMetadata } from '../../models/transactions/metadata'
|
||||
import { Transaction, TransactionMetadata } from '../../models/transactions'
|
||||
|
||||
import HashPrefix from './HashPrefix'
|
||||
import sha512Half from './sha512Half'
|
||||
|
||||
Reference in New Issue
Block a user