mirror of
https://github.com/Xahau/xahau.js.git
synced 2025-11-12 08:35:48 +00:00
fix: make docs not output confusing information in xrpl client (#2337)
* fix: make docs not output confusing information in xrpl client --------- Co-authored-by: Jackson Mills <jmills@ripple.com> Co-authored-by: Caleb Kniffen <ckniffen@ripple.com>
This commit is contained in:
committed by
Caleb Kniffen
parent
21c2423bac
commit
3b70a3b919
109
package-lock.json
generated
109
package-lock.json
generated
@@ -3973,6 +3973,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz",
|
||||
"integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"is-nan": "^1.3.2",
|
||||
@@ -6175,12 +6176,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/es6-object-assign": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
|
||||
"integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
@@ -11613,6 +11608,7 @@
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
|
||||
"integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.4",
|
||||
@@ -13228,6 +13224,12 @@
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"node_modules/run-s": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/run-s/-/run-s-0.0.0.tgz",
|
||||
"integrity": "sha512-KPDNauF2Tpnm3nG0+0LJuJxwBFrhAdthpM8bVdDvjWQA7pWP7QoNwEl1+dJ7WVJj81AQP/i6kl6JUmAk7tg3Og==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/rxjs": {
|
||||
"version": "6.6.7",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
|
||||
@@ -14777,6 +14779,51 @@
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.0.tgz",
|
||||
"integrity": "sha512-FvCYWhO1n5jACE0C32qg6b3dSfQ8f2VzExnnRboowHtqUD6ARzM2r8YJeZFYXhcm2hI4C2oCRDgNPk/yaQUN9g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lunr": "^2.3.9",
|
||||
"marked": "^4.3.0",
|
||||
"minimatch": "^9.0.3",
|
||||
"shiki": "^0.14.1"
|
||||
},
|
||||
"bin": {
|
||||
"typedoc": "bin/typedoc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x"
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/typedoc/node_modules/minimatch": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
||||
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/typeforce": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
|
||||
@@ -15713,7 +15760,6 @@
|
||||
"version": "1.11.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"bignumber.js": "^9.0.0",
|
||||
"buffer": "6.0.3",
|
||||
"create-hash": "^1.2.0",
|
||||
"ripple-address-codec": "^4.3.1"
|
||||
@@ -15762,6 +15808,7 @@
|
||||
"karma-webpack": "^5.0.0",
|
||||
"lodash": "^4.17.4",
|
||||
"react": "^18.2.0",
|
||||
"run-s": "^0.0.0",
|
||||
"typedoc": "0.25.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -18954,6 +19001,7 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz",
|
||||
"integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"is-nan": "^1.3.2",
|
||||
@@ -20714,12 +20762,6 @@
|
||||
"is-symbol": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"es6-object-assign": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz",
|
||||
"integrity": "sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==",
|
||||
"dev": true
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
@@ -24921,6 +24963,7 @@
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
|
||||
"integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.1.4",
|
||||
@@ -26132,7 +26175,6 @@
|
||||
"version": "file:packages/ripple-binary-codec",
|
||||
"requires": {
|
||||
"assert": "^2.0.0",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"buffer": "6.0.3",
|
||||
"create-hash": "^1.2.0",
|
||||
"ripple-address-codec": "^4.3.1"
|
||||
@@ -26163,6 +26205,12 @@
|
||||
"queue-microtask": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"run-s": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/run-s/-/run-s-0.0.0.tgz",
|
||||
"integrity": "sha512-KPDNauF2Tpnm3nG0+0LJuJxwBFrhAdthpM8bVdDvjWQA7pWP7QoNwEl1+dJ7WVJj81AQP/i6kl6JUmAk7tg3Og==",
|
||||
"dev": true
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "6.6.7",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz",
|
||||
@@ -27314,6 +27362,38 @@
|
||||
"is-typedarray": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"typedoc": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.0.tgz",
|
||||
"integrity": "sha512-FvCYWhO1n5jACE0C32qg6b3dSfQ8f2VzExnnRboowHtqUD6ARzM2r8YJeZFYXhcm2hI4C2oCRDgNPk/yaQUN9g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lunr": "^2.3.9",
|
||||
"marked": "^4.3.0",
|
||||
"minimatch": "^9.0.3",
|
||||
"shiki": "^0.14.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
||||
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"typeforce": {
|
||||
"version": "1.18.0",
|
||||
"resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
|
||||
@@ -27932,6 +28012,7 @@
|
||||
"ripple-address-codec": "^4.3.1",
|
||||
"ripple-binary-codec": "^1.11.0",
|
||||
"ripple-keypairs": "^1.3.1",
|
||||
"run-s": "^0.0.0",
|
||||
"typedoc": "0.25.0",
|
||||
"ws": "^8.2.2",
|
||||
"xrpl-secret-numbers": "^0.3.3"
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"xrpl-secret-numbers": "^0.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
"@types/node": "^16.18.38",
|
||||
"https-proxy-agent": "^7.0.1",
|
||||
"karma": "^6.4.1",
|
||||
@@ -40,6 +41,7 @@
|
||||
"karma-jasmine": "^5.1.0",
|
||||
"karma-webpack": "^5.0.0",
|
||||
"lodash": "^4.17.4",
|
||||
"run-s": "^0.0.0",
|
||||
"react": "^18.2.0",
|
||||
"typedoc": "0.25.0"
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Client, LedgerResponse, TxResponse } from '../../src'
|
||||
import { Client } from '../../src'
|
||||
|
||||
const client = new Client('wss://s2.ripple.com:51233')
|
||||
|
||||
async function getTransaction(): Promise<void> {
|
||||
await client.connect()
|
||||
const ledger: LedgerResponse = await client.request({
|
||||
const ledger = await client.request({
|
||||
command: 'ledger',
|
||||
transactions: true,
|
||||
ledger_index: 'validated',
|
||||
@@ -12,8 +12,8 @@ async function getTransaction(): Promise<void> {
|
||||
console.log(ledger)
|
||||
|
||||
const transactions = ledger.result.ledger.transactions
|
||||
if (transactions) {
|
||||
const tx: TxResponse = await client.request({
|
||||
if (transactions && transactions.length > 0) {
|
||||
const tx = await client.request({
|
||||
command: 'tx',
|
||||
transaction: transactions[0],
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Client, Payment, RipplePathFindResponse } from '../../src'
|
||||
import { Client, Payment } from '../../src'
|
||||
|
||||
const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
|
||||
@@ -15,7 +15,8 @@ async function createTxWithPaths(): Promise<void> {
|
||||
issuer: 'rVnYNK9yuxBz4uP8zC8LEFokM2nqH3poc',
|
||||
}
|
||||
|
||||
const request = {
|
||||
const resp = await client.request({
|
||||
// TOOD: Replace with path_find - https://github.com/XRPLF/xrpl.js/issues/2385
|
||||
command: 'ripple_path_find',
|
||||
source_account: wallet.classicAddress,
|
||||
source_currencies: [
|
||||
@@ -25,9 +26,7 @@ async function createTxWithPaths(): Promise<void> {
|
||||
],
|
||||
destination_account,
|
||||
destination_amount,
|
||||
}
|
||||
|
||||
const resp: RipplePathFindResponse = await client.request(request)
|
||||
})
|
||||
console.log(resp)
|
||||
|
||||
const paths = resp.result.alternatives[0].paths_computed
|
||||
|
||||
@@ -2,7 +2,7 @@ import fetch from 'cross-fetch'
|
||||
import { isValidClassicAddress } from 'ripple-address-codec'
|
||||
|
||||
import type { Client } from '../client'
|
||||
import { RippledError, XRPLFaucetError } from '../errors'
|
||||
import { XRPLFaucetError } from '../errors'
|
||||
|
||||
import {
|
||||
FaucetWallet,
|
||||
@@ -45,107 +45,97 @@ export interface FundingOptions {
|
||||
usageContext?: string
|
||||
}
|
||||
|
||||
interface FaucetRequestBody {
|
||||
/**
|
||||
* Parameters to pass into a faucet request to fund an XRP account.
|
||||
*/
|
||||
export interface FaucetRequestBody {
|
||||
/**
|
||||
* The address to fund. If no address is provided the faucet will fund a random account.
|
||||
*/
|
||||
destination?: string
|
||||
/**
|
||||
* The total amount of XRP to fund the account with.
|
||||
*/
|
||||
xrpAmount?: string
|
||||
/**
|
||||
* An optional field to indicate the use case context of the faucet transaction
|
||||
* Ex: integration test, code snippets.
|
||||
*/
|
||||
usageContext?: string
|
||||
/**
|
||||
* Information about the context of where the faucet is being called from.
|
||||
* Ex: xrpl.js or xrpl-py
|
||||
*/
|
||||
userAgent: string
|
||||
}
|
||||
|
||||
/**
|
||||
* The fundWallet() method is used to send an amount of XRP (usually 1000) to a new (randomly generated)
|
||||
* or existing XRP Ledger wallet.
|
||||
* Generate a new wallet to fund if no existing wallet is provided or its address is invalid.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* Example 1: Fund a randomly generated wallet
|
||||
* const { Client, Wallet } = require('xrpl')
|
||||
*
|
||||
* const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
* await client.connect()
|
||||
* const { balance, wallet } = await client.fundWallet()
|
||||
*
|
||||
* Under the hood, this will use `Wallet.generate()` to create a new random wallet, then ask a testnet faucet
|
||||
* To send it XRP on ledger to make it a real account. If successful, this will return the new account balance in XRP
|
||||
* Along with the Wallet object to track the keys for that account. If you'd like, you can also re-fill an existing
|
||||
* Account by passing in a Wallet you already have.
|
||||
* ```ts
|
||||
* const api = new xrpl.Client("wss://s.altnet.rippletest.net:51233")
|
||||
* await api.connect()
|
||||
* const { wallet, balance } = await api.fundWallet()
|
||||
* ```
|
||||
*
|
||||
* Example 2: Fund wallet using a custom faucet host and known wallet address
|
||||
*
|
||||
* `fundWallet` will try to infer the url of a faucet API from the network your client is connected to.
|
||||
* There are hardcoded default faucets for popular test networks like testnet and devnet.
|
||||
* However, if you're working with a newer or more obscure network, you may have to specify the faucetHost
|
||||
* And faucetPath so `fundWallet` can ask that faucet to fund your wallet.
|
||||
*
|
||||
* ```ts
|
||||
* const newWallet = Wallet.generate()
|
||||
* const { balance, wallet } = await client.fundWallet(newWallet, {
|
||||
* amount: '10',
|
||||
* faucetHost: 'https://custom-faucet.example.com',
|
||||
* faucetPath: '/accounts'
|
||||
* })
|
||||
* console.log(`Sent 10 XRP to wallet: ${address} from the given faucet. Resulting balance: ${balance} XRP`)
|
||||
* } catch (error) {
|
||||
* console.error(`Failed to fund wallet: ${error}`)
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @param this - Client.
|
||||
* @param wallet - An existing XRPL Wallet to fund. If undefined or null, a new Wallet will be created.
|
||||
* @param options - FundingOptions
|
||||
|
||||
* @returns A Wallet on the Testnet or Devnet that contains some amount of XRP,
|
||||
* and that wallet's balance in XRP.
|
||||
* @throws When either Client isn't connected or unable to fund wallet address.
|
||||
* @param wallet - Optional existing wallet.
|
||||
* @returns The wallet to fund.
|
||||
*/
|
||||
async function fundWallet(
|
||||
this: Client,
|
||||
wallet?: Wallet | null,
|
||||
options: FundingOptions = {},
|
||||
): Promise<{
|
||||
wallet: Wallet
|
||||
balance: number
|
||||
}> {
|
||||
if (!this.isConnected()) {
|
||||
throw new RippledError('Client not connected, cannot call faucet')
|
||||
export function generateWalletToFund(wallet?: Wallet | null): Wallet {
|
||||
if (wallet && isValidClassicAddress(wallet.classicAddress)) {
|
||||
return wallet
|
||||
}
|
||||
const existingWallet = Boolean(wallet)
|
||||
|
||||
// Generate a new Wallet if no existing Wallet is provided or its address is invalid to fund
|
||||
const walletToFund =
|
||||
wallet && isValidClassicAddress(wallet.classicAddress)
|
||||
? wallet
|
||||
: Wallet.generate()
|
||||
|
||||
// Create the POST request body
|
||||
const postBody: FaucetRequestBody = {
|
||||
destination: walletToFund.classicAddress,
|
||||
xrpAmount: options.amount,
|
||||
usageContext: options.usageContext,
|
||||
userAgent: 'xrpl.js',
|
||||
}
|
||||
|
||||
let startingBalance = 0
|
||||
if (existingWallet) {
|
||||
try {
|
||||
startingBalance = Number(
|
||||
await this.getXrpBalance(walletToFund.classicAddress),
|
||||
)
|
||||
} catch {
|
||||
/* startingBalance remains what it was previously */
|
||||
}
|
||||
}
|
||||
|
||||
return requestFunding(options, this, startingBalance, walletToFund, postBody)
|
||||
return Wallet.generate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the starting balance of the wallet.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param classicAddress - The classic address of the wallet.
|
||||
* @returns The starting balance.
|
||||
*/
|
||||
export async function getStartingBalance(
|
||||
client: Client,
|
||||
classicAddress: string,
|
||||
): Promise<number> {
|
||||
let startingBalance = 0
|
||||
try {
|
||||
startingBalance = Number(await client.getXrpBalance(classicAddress))
|
||||
} catch {
|
||||
// startingBalance remains '0'
|
||||
}
|
||||
return startingBalance
|
||||
}
|
||||
|
||||
export interface FundWalletOptions {
|
||||
faucetHost?: string
|
||||
faucetPath?: string
|
||||
amount?: string
|
||||
usageContext?: string
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Helper function to request funding from a faucet. Should not be called directly from outside the xrpl.js library.
|
||||
*
|
||||
* @param options - See below
|
||||
* @param options.faucetHost - A custom host for a faucet server. On devnet,
|
||||
* testnet, AMM devnet, and HooksV3 testnet, `fundWallet` will
|
||||
* attempt to determine the correct server automatically. In other environments,
|
||||
* or if you would like to customize the faucet host in devnet or testnet,
|
||||
* you should provide the host using this option.
|
||||
* @param options.faucetPath - A custom path for a faucet server. On devnet,
|
||||
* testnet, AMM devnet, and HooksV3 testnet, `fundWallet` will
|
||||
* attempt to determine the correct path automatically. In other environments,
|
||||
* or if you would like to customize the faucet path in devnet or testnet,
|
||||
* you should provide the path using this option.
|
||||
* Ex: client.fundWallet(null,{'faucet.altnet.rippletest.net', '/accounts'})
|
||||
* specifies a request to 'faucet.altnet.rippletest.net/accounts' to fund a new wallet.
|
||||
* @param options.amount - A custom amount to fund, if undefined or null, the default amount will be 1000.
|
||||
* @param client - A connection to the XRPL to send requests and transactions.
|
||||
* @param startingBalance - The amount of XRP in the given walletToFund on ledger already.
|
||||
* @param walletToFund - An existing XRPL Wallet to fund.
|
||||
* @param postBody - The content to send the faucet to indicate which address to fund, how much to fund it, and
|
||||
* where the request is coming from.
|
||||
* @returns A promise that resolves to a funded wallet and the balance within it.
|
||||
*/
|
||||
// eslint-disable-next-line max-params -- Helper function created for organizational purposes
|
||||
async function requestFunding(
|
||||
export async function requestFunding(
|
||||
options: FundingOptions,
|
||||
client: Client,
|
||||
startingBalance: number,
|
||||
@@ -291,5 +281,3 @@ async function getUpdatedBalance(
|
||||
}, INTERVAL_SECONDS * 1000)
|
||||
})
|
||||
}
|
||||
|
||||
export default fundWallet
|
||||
|
||||
@@ -4,9 +4,15 @@ import {
|
||||
TimeoutError,
|
||||
XrplError,
|
||||
} from '../errors'
|
||||
import { Response } from '../models/methods'
|
||||
import { Response, RequestResponseMap } from '../models/methods'
|
||||
import { BaseRequest, ErrorResponse } from '../models/methods/baseMethod'
|
||||
|
||||
interface PromiseEntry<T> {
|
||||
resolve: (value: T | PromiseLike<T>) => void
|
||||
reject: (value: Error) => void
|
||||
timer: ReturnType<typeof setTimeout>
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage all the requests made to the websocket, and their async responses
|
||||
* that come in from the WebSocket. Responses come in over the WS connection
|
||||
@@ -17,13 +23,31 @@ export default class RequestManager {
|
||||
private nextId = 0
|
||||
private readonly promisesAwaitingResponse = new Map<
|
||||
string | number,
|
||||
{
|
||||
resolve: (value: Response | PromiseLike<Response>) => void
|
||||
reject: (value: Error) => void
|
||||
timer: ReturnType<typeof setTimeout>
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Necessary and typed wrapper in addPromise method
|
||||
PromiseEntry<any>
|
||||
>()
|
||||
|
||||
/**
|
||||
* Adds a promise to the collection of promises awaiting response. Handles typing with generics.
|
||||
*
|
||||
* @template T The generic type parameter representing the resolved value type.
|
||||
* @param newId - The identifier for the new promise.
|
||||
* @param timer - The timer associated with the promise.
|
||||
* @returns A promise that resolves to the specified generic type.
|
||||
*/
|
||||
public async addPromise<R extends BaseRequest, T = RequestResponseMap<R>>(
|
||||
newId: string | number,
|
||||
timer: ReturnType<typeof setTimeout>,
|
||||
): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
this.promisesAwaitingResponse.set(newId, {
|
||||
resolve,
|
||||
reject,
|
||||
timer,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Successfully resolves a request.
|
||||
*
|
||||
@@ -87,10 +111,10 @@ export default class RequestManager {
|
||||
* @returns Request ID, new request form, and the promise for resolving the request.
|
||||
* @throws XrplError if request with the same ID is already pending.
|
||||
*/
|
||||
public createRequest<T extends BaseRequest>(
|
||||
request: T,
|
||||
public createRequest<R extends BaseRequest, T = RequestResponseMap<R>>(
|
||||
request: R,
|
||||
timeout: number,
|
||||
): [string | number, string, Promise<Response>] {
|
||||
): [string | number, string, Promise<T>] {
|
||||
let newId: string | number
|
||||
if (request.id == null) {
|
||||
newId = this.nextId
|
||||
@@ -129,11 +153,13 @@ export default class RequestManager {
|
||||
request,
|
||||
)
|
||||
}
|
||||
const newPromise = new Promise<Response>(
|
||||
(resolve: (value: Response | PromiseLike<Response>) => void, reject) => {
|
||||
this.promisesAwaitingResponse.set(newId, { resolve, reject, timer })
|
||||
},
|
||||
)
|
||||
const newPromise = new Promise<T>((resolve, reject) => {
|
||||
this.promisesAwaitingResponse.set(newId, {
|
||||
resolve,
|
||||
reject,
|
||||
timer,
|
||||
})
|
||||
})
|
||||
|
||||
return [newId, newRequest, newPromise]
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
ConnectionError,
|
||||
XrplError,
|
||||
} from '../errors'
|
||||
import type { RequestResponseMap } from '../models'
|
||||
import { BaseRequest } from '../models/methods/baseMethod'
|
||||
|
||||
import ConnectionManager from './ConnectionManager'
|
||||
@@ -295,17 +296,17 @@ export class Connection extends EventEmitter {
|
||||
* @returns The response from the rippled server.
|
||||
* @throws NotConnectedError if the Connection isn't connected to a server.
|
||||
*/
|
||||
public async request<T extends BaseRequest>(
|
||||
request: T,
|
||||
public async request<R extends BaseRequest, T = RequestResponseMap<R>>(
|
||||
request: R,
|
||||
timeout?: number,
|
||||
): Promise<unknown> {
|
||||
): Promise<T> {
|
||||
if (!this.shouldBeConnected || this.ws == null) {
|
||||
throw new NotConnectedError(JSON.stringify(request), request)
|
||||
}
|
||||
const [id, message, responsePromise] = this.requestManager.createRequest(
|
||||
request,
|
||||
timeout ?? this.config.timeout,
|
||||
)
|
||||
const [id, message, responsePromise] = this.requestManager.createRequest<
|
||||
R,
|
||||
T
|
||||
>(request, timeout ?? this.config.timeout)
|
||||
this.trace('send', message)
|
||||
websocketSendAsync(this.ws, message).catch((error) => {
|
||||
this.requestManager.reject(id, error)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,13 +3,13 @@ import { decode } from 'ripple-binary-codec'
|
||||
|
||||
import type {
|
||||
AccountTxResponse,
|
||||
Response,
|
||||
ResponseWarning,
|
||||
TransactionEntryResponse,
|
||||
TransactionStream,
|
||||
TxResponse,
|
||||
} from '..'
|
||||
import type { Amount } from '../models/common'
|
||||
import type { RequestResponseMap } from '../models/methods'
|
||||
import { BaseRequest, BaseResponse } from '../models/methods/baseMethod'
|
||||
import {
|
||||
PaymentFlags,
|
||||
PseudoTransaction,
|
||||
@@ -90,7 +90,10 @@ function accountTxHasPartialPayment(response: AccountTxResponse): boolean {
|
||||
return foo
|
||||
}
|
||||
|
||||
function hasPartialPayment(command: string, response: Response): boolean {
|
||||
function hasPartialPayment<R extends BaseRequest, T = RequestResponseMap<R>>(
|
||||
command: string,
|
||||
response: T,
|
||||
): boolean {
|
||||
/* eslint-disable @typescript-eslint/consistent-type-assertions -- Request type is known at runtime from command */
|
||||
switch (command) {
|
||||
case 'tx':
|
||||
@@ -111,12 +114,13 @@ function hasPartialPayment(command: string, response: Response): boolean {
|
||||
* @param command - Command from the request, tells us what response to expect.
|
||||
* @param response - Response to check for a partial payment.
|
||||
*/
|
||||
export function handlePartialPayment(
|
||||
command: string,
|
||||
response: Response,
|
||||
): void {
|
||||
export function handlePartialPayment<
|
||||
R extends BaseRequest,
|
||||
T = RequestResponseMap<R>,
|
||||
>(command: string, response: T): void {
|
||||
if (hasPartialPayment(command, response)) {
|
||||
const warnings: ResponseWarning[] = response.warnings ?? []
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- We are checking dynamically and safely.
|
||||
const warnings = (response as BaseResponse).warnings ?? []
|
||||
|
||||
const warning = {
|
||||
id: WARN_PARTIAL_PAYMENT_CODE,
|
||||
@@ -125,6 +129,8 @@ export function handlePartialPayment(
|
||||
|
||||
warnings.push(warning)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment -- We are checking dynamically and safely.
|
||||
// @ts-expect-error -- We are checking dynamically and safely.
|
||||
response.warnings = warnings
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export { default as ECDSA } from './ECDSA'
|
||||
|
||||
export * from './errors'
|
||||
|
||||
export { default as fundWallet, FundingOptions } from './Wallet/fundWallet'
|
||||
export { FundingOptions } from './Wallet/fundWallet'
|
||||
export { Wallet } from './Wallet'
|
||||
|
||||
export { walletFromSecretNumbers } from './Wallet/walletFromSecretNumbers'
|
||||
|
||||
@@ -15,7 +15,7 @@ import { LedgerEntry } from './LedgerEntry'
|
||||
export default interface Ledger {
|
||||
/** The SHA-512Half of this ledger's state tree information. */
|
||||
account_hash: string
|
||||
/** All the state information in this ledger. */
|
||||
/** All the state information in this ledger. Admin only. */
|
||||
accountState?: LedgerEntry[]
|
||||
/** A bit-map of flags relating to the closing of this ledger. */
|
||||
close_flags: number
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-inline-comments -- Necessary for important note */
|
||||
/* eslint-disable max-lines -- There is a lot to export */
|
||||
import {
|
||||
AccountChannelsRequest,
|
||||
@@ -71,6 +72,11 @@ import {
|
||||
LedgerQueueData,
|
||||
LedgerRequest,
|
||||
LedgerResponse,
|
||||
LedgerRequestExpandedTransactionsOnly,
|
||||
LedgerResponseExpanded,
|
||||
LedgerRequestExpandedAccountsAndTransactions,
|
||||
LedgerRequestExpandedAccountsOnly,
|
||||
LedgerRequestExpandedTransactionsBinary,
|
||||
} from './ledger'
|
||||
import { LedgerClosedRequest, LedgerClosedResponse } from './ledgerClosed'
|
||||
import { LedgerCurrentRequest, LedgerCurrentResponse } from './ledgerCurrent'
|
||||
@@ -259,6 +265,177 @@ type Response =
|
||||
// AMM methods
|
||||
| AMMInfoResponse
|
||||
|
||||
export type RequestResponseMap<T> = T extends AccountChannelsRequest
|
||||
? AccountChannelsResponse
|
||||
: T extends AccountCurrenciesRequest
|
||||
? AccountCurrenciesResponse
|
||||
: T extends AccountInfoRequest
|
||||
? AccountInfoResponse
|
||||
: T extends AccountLinesRequest
|
||||
? AccountLinesResponse
|
||||
: T extends AccountNFTsRequest
|
||||
? AccountNFTsResponse
|
||||
: T extends AccountObjectsRequest
|
||||
? AccountObjectsResponse
|
||||
: T extends AccountOffersRequest
|
||||
? AccountOffersResponse
|
||||
: T extends AccountTxRequest
|
||||
? AccountTxResponse
|
||||
: T extends AMMInfoRequest
|
||||
? AMMInfoResponse
|
||||
: T extends GatewayBalancesRequest
|
||||
? GatewayBalancesResponse
|
||||
: T extends NoRippleCheckRequest
|
||||
? NoRippleCheckResponse
|
||||
: // NOTE: The order of these LedgerRequest types is important
|
||||
// to get the proper type matching overrides based on parameters set
|
||||
// in the request. For example LedgerRequestExpandedTransactionsBinary
|
||||
// should match LedgerRequestExpandedTransactionsOnly, but not
|
||||
// LedgerRequestExpandedAccountsOnly. This is because the
|
||||
// LedgerRequestExpandedTransactionsBinary type is a superset of
|
||||
// LedgerRequestExpandedTransactionsOnly, but not of the other.
|
||||
// This is why LedgerRequestExpandedTransactionsBinary is listed
|
||||
// first in the type list.
|
||||
//
|
||||
// Here is an example using real data:
|
||||
// LedgerRequestExpandedTransactionsBinary = {
|
||||
// command: 'ledger',
|
||||
// ledger_index: 'validated',
|
||||
// expand: true,
|
||||
// transactions: true,
|
||||
// binary: true,
|
||||
// }
|
||||
// LedgerRequestExpandedTransactionsOnly = {
|
||||
// command: 'ledger',
|
||||
// ledger_index: 'validated',
|
||||
// expand: true,
|
||||
// transactions: true,
|
||||
// }
|
||||
// LedgerRequestExpandedAccountsOnly = {
|
||||
// command: 'ledger',
|
||||
// ledger_index: 'validated',
|
||||
// accounts: true,
|
||||
// expand: true,
|
||||
// }
|
||||
// LedgerRequest = {
|
||||
// command: 'ledger',
|
||||
// ledger_index: 'validated',
|
||||
// }
|
||||
//
|
||||
// The type with the most parameters set should be listed first. In this
|
||||
// case LedgerRequestExpandedTransactionsBinary has the most parameters (`expand`, `transactions`, and `binary`)
|
||||
// set, so it is listed first. When TypeScript tries to match the type of
|
||||
// a request to a response, it will try to match the request type to the
|
||||
// response type in the order they are listed. So, if we have a request
|
||||
// with the following parameters:
|
||||
// {
|
||||
// command: 'ledger',
|
||||
// ledger_index: 'validated',
|
||||
// expand: true,
|
||||
// transactions: true,
|
||||
// binary: true,
|
||||
// }
|
||||
// TypeScript will first try to match the request type to
|
||||
// LedgerRequestExpandedTransactionsBinary, which will succeed. It will
|
||||
// then try to match the response type to LedgerResponseExpanded, which
|
||||
// will also succeed. If we had listed LedgerRequestExpandedTransactionsOnly
|
||||
// first, TypeScript would have tried to match the request type to
|
||||
// LedgerRequestExpandedTransactionsOnly, which would have succeeded, but
|
||||
// then we'd get the wrong response type, LedgerResponse, instead of
|
||||
// LedgerResponseExpanded.
|
||||
T extends LedgerRequestExpandedTransactionsBinary
|
||||
? LedgerResponse
|
||||
: T extends LedgerRequestExpandedAccountsAndTransactions
|
||||
? LedgerResponseExpanded
|
||||
: T extends LedgerRequestExpandedTransactionsOnly
|
||||
? LedgerResponseExpanded
|
||||
: T extends LedgerRequestExpandedAccountsOnly
|
||||
? LedgerResponseExpanded
|
||||
: T extends LedgerRequest
|
||||
? LedgerResponse
|
||||
: T extends LedgerClosedRequest
|
||||
? LedgerClosedResponse
|
||||
: T extends LedgerCurrentRequest
|
||||
? LedgerCurrentResponse
|
||||
: T extends LedgerDataRequest
|
||||
? LedgerDataResponse
|
||||
: T extends LedgerEntryRequest
|
||||
? LedgerEntryResponse
|
||||
: T extends SubmitRequest
|
||||
? SubmitResponse
|
||||
: T extends SubmitMultisignedRequest
|
||||
? SubmitMultisignedResponse
|
||||
: T extends TransactionEntryRequest
|
||||
? TransactionEntryResponse
|
||||
: T extends TxRequest
|
||||
? TxResponse
|
||||
: T extends BookOffersRequest
|
||||
? BookOffersResponse
|
||||
: T extends DepositAuthorizedRequest
|
||||
? DepositAuthorizedResponse
|
||||
: T extends PathFindRequest
|
||||
? PathFindResponse
|
||||
: T extends RipplePathFindRequest
|
||||
? RipplePathFindResponse
|
||||
: T extends ChannelVerifyRequest
|
||||
? ChannelVerifyResponse
|
||||
: T extends SubscribeRequest
|
||||
? SubscribeResponse
|
||||
: T extends UnsubscribeRequest
|
||||
? UnsubscribeResponse
|
||||
: T extends FeeRequest
|
||||
? FeeResponse
|
||||
: T extends ManifestRequest
|
||||
? ManifestResponse
|
||||
: T extends ServerInfoRequest
|
||||
? ServerInfoResponse
|
||||
: T extends ServerStateRequest
|
||||
? ServerStateResponse
|
||||
: T extends ServerDefinitionsRequest
|
||||
? ServerDefinitionsResponse
|
||||
: T extends PingRequest
|
||||
? PingResponse
|
||||
: T extends RandomRequest
|
||||
? RandomResponse
|
||||
: T extends NFTBuyOffersRequest
|
||||
? NFTBuyOffersResponse
|
||||
: T extends NFTSellOffersRequest
|
||||
? NFTSellOffersResponse
|
||||
: T extends NFTInfoRequest
|
||||
? NFTInfoResponse
|
||||
: T extends NFTHistoryRequest
|
||||
? NFTHistoryResponse
|
||||
: Response
|
||||
|
||||
export type MarkerRequest = Request & {
|
||||
limit?: number
|
||||
marker?: unknown
|
||||
}
|
||||
|
||||
export type MarkerResponse = Response & {
|
||||
result: {
|
||||
marker?: unknown
|
||||
}
|
||||
}
|
||||
|
||||
export type RequestAllResponseMap<T> = T extends AccountChannelsRequest
|
||||
? AccountChannelsResponse
|
||||
: T extends AccountLinesRequest
|
||||
? AccountLinesResponse
|
||||
: T extends AccountObjectsRequest
|
||||
? AccountObjectsResponse
|
||||
: T extends AccountOffersRequest
|
||||
? AccountOffersResponse
|
||||
: T extends AccountTxRequest
|
||||
? AccountTxResponse
|
||||
: T extends LedgerDataRequest
|
||||
? LedgerDataResponse
|
||||
: T extends AccountTxRequest
|
||||
? AccountTxResponse
|
||||
: T extends BookOffersRequest
|
||||
? BookOffersResponse
|
||||
: MarkerResponse
|
||||
|
||||
export {
|
||||
// Allow users to define their own requests and responses. This is useful for releasing experimental versions
|
||||
BaseRequest,
|
||||
|
||||
@@ -72,6 +72,112 @@ export interface LedgerRequest extends BaseRequest, LookupByLedgerRequest {
|
||||
type?: LedgerEntryFilter
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve information about the public ledger. Expects a response in the form
|
||||
* of a {@link LedgerResponseExpanded}. Will return full JSON-formatted transaction data instead of string hashes.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const ledger: LedgerRequest = {
|
||||
* "id": 14,
|
||||
* "command": "ledger",
|
||||
* "ledger_index": "validated",
|
||||
* "full": false,
|
||||
* "accounts": false,
|
||||
* "transactions": false,
|
||||
* "expand": true,
|
||||
* "owner_funds": false
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @category Requests
|
||||
*/
|
||||
export interface LedgerRequestExpandedTransactionsOnly extends LedgerRequest {
|
||||
expand: true
|
||||
transactions: true
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve information about the public ledger. Expects a response in the form
|
||||
* of a {@link LedgerResponseExpanded}. Will return full JSON-formatted `accountState` data instead of string hashes.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const ledger: LedgerRequest = {
|
||||
* "id": 14,
|
||||
* "command": "ledger",
|
||||
* "ledger_index": "validated",
|
||||
* "full": false,
|
||||
* "accounts": true,
|
||||
* "transactions": false,
|
||||
* "expand": true,
|
||||
* "owner_funds": false
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @category Requests
|
||||
*/
|
||||
export interface LedgerRequestExpandedAccountsOnly extends LedgerRequest {
|
||||
expand: true
|
||||
accounts: true
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve information about the public ledger. Expects a response in the form
|
||||
* of a {@link LedgerResponseExpanded}. Will return full JSON-formatted `accountState` and `transactions`
|
||||
* data instead of string hashes.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const ledger: LedgerRequest = {
|
||||
* "id": 14,
|
||||
* "command": "ledger",
|
||||
* "ledger_index": "validated",
|
||||
* "full": false,
|
||||
* "accounts": true,
|
||||
* "transactions": true,
|
||||
* "expand": true,
|
||||
* "owner_funds": false
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @category Requests
|
||||
*/
|
||||
export interface LedgerRequestExpandedAccountsAndTransactions
|
||||
extends LedgerRequest {
|
||||
expand: true
|
||||
accounts: true
|
||||
transactions: true
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve information about the public ledger. Expects a response in the form
|
||||
* of a {@link LedgerResponse}. Will return binary (hexadecimal string) format
|
||||
* instead of JSON or string hashes for `transactions` data.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const ledger: LedgerRequest = {
|
||||
* "id": 14,
|
||||
* "command": "ledger",
|
||||
* "ledger_index": "validated",
|
||||
* "full": false,
|
||||
* "accounts": true,
|
||||
* "transactions": true,
|
||||
* "expand": true,
|
||||
* "owner_funds": false,
|
||||
* "binary": true
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @category Requests
|
||||
*/
|
||||
export interface LedgerRequestExpandedTransactionsBinary extends LedgerRequest {
|
||||
expand: true
|
||||
transactions: true
|
||||
binary: true
|
||||
}
|
||||
|
||||
/**
|
||||
* Special case transaction definition when the request contains `owner_funds: true`.
|
||||
*/
|
||||
@@ -101,15 +207,7 @@ export interface LedgerBinary
|
||||
transactions?: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Response expected from a {@link LedgerRequest}.
|
||||
*
|
||||
* @category Responses
|
||||
*/
|
||||
export interface LedgerResponse extends BaseResponse {
|
||||
result: {
|
||||
/** The complete header data of this {@link Ledger}. */
|
||||
ledger: Ledger | LedgerBinary
|
||||
interface LedgerResponseBase {
|
||||
/** Unique identifying hash of the entire ledger. */
|
||||
ledger_hash: string
|
||||
/** The Ledger Index of this ledger. */
|
||||
@@ -126,5 +224,36 @@ export interface LedgerResponse extends BaseResponse {
|
||||
* on whether the request specified binary as true.
|
||||
*/
|
||||
validated?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
interface LedgerResponseResult extends LedgerResponseBase {
|
||||
/** The complete header data of this {@link Ledger}. */
|
||||
ledger: LedgerBinary
|
||||
}
|
||||
|
||||
/**
|
||||
* Response expected from a {@link LedgerRequest}.
|
||||
* This is the default request response, triggered when `expand` and `binary` are both false.
|
||||
*
|
||||
* @category Responses
|
||||
*/
|
||||
export interface LedgerResponse extends BaseResponse {
|
||||
result: LedgerResponseResult
|
||||
}
|
||||
|
||||
interface LedgerResponseExpandedResult extends LedgerResponseBase {
|
||||
/** The complete header data of this {@link Ledger}. */
|
||||
ledger: Ledger
|
||||
}
|
||||
|
||||
/**
|
||||
* Response expected from a {@link LedgerRequest} when the request contains `expanded` is true. See {@link LedgerRequestExpanded}.
|
||||
* This response will contain full JSON-formatted data instead of string hashes.
|
||||
* The response will contain either `accounts` or `transactions` or both.
|
||||
* `binary` will be missing altogether.
|
||||
*
|
||||
* @category Responses
|
||||
*/
|
||||
export interface LedgerResponseExpanded extends BaseResponse {
|
||||
result: LedgerResponseExpandedResult
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import { OfferCreate, Transaction } from '../transactions'
|
||||
import { TransactionMetadata } from '../transactions/metadata'
|
||||
|
||||
import type { BaseRequest, BaseResponse } from './baseMethod'
|
||||
import { ManifestRequest } from './manifest'
|
||||
|
||||
export interface SubscribeBook {
|
||||
/**
|
||||
@@ -433,3 +434,29 @@ export type Stream =
|
||||
| PeerStatusStream
|
||||
| OrderBookStream
|
||||
| ConsensusStream
|
||||
|
||||
export type OnEventToListenerMap<T> = T extends 'connected'
|
||||
? () => void
|
||||
: T extends 'disconnected'
|
||||
? (code: number) => void
|
||||
: T extends 'ledgerClosed'
|
||||
? (ledger: LedgerStream) => void
|
||||
: T extends 'validationReceived'
|
||||
? (validation: ValidationStream) => void
|
||||
: T extends 'transaction'
|
||||
? (transaction: TransactionStream) => void
|
||||
: T extends 'peerStatusChange'
|
||||
? (peerStatus: PeerStatusStream) => void
|
||||
: T extends 'consensusPhase'
|
||||
? (consensus: ConsensusStream) => void
|
||||
: T extends 'manifestReceived'
|
||||
? (manifest: ManifestRequest) => void
|
||||
: T extends 'path_find'
|
||||
? (path: PathFindStream) => void
|
||||
: T extends 'error'
|
||||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any -- needs to be any for overload
|
||||
(...err: any[]) => void
|
||||
: T extends string
|
||||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any -- needs to be any for overload
|
||||
(...args: any[]) => void
|
||||
: never
|
||||
|
||||
@@ -5,7 +5,6 @@ import type { Client } from '..'
|
||||
import { ValidationError, XrplError } from '../errors'
|
||||
import { AccountInfoRequest, AccountObjectsRequest } from '../models/methods'
|
||||
import { Transaction } from '../models/transactions'
|
||||
import { setTransactionFlagsToNumber } from '../models/utils/flags'
|
||||
import { xrpToDrops } from '../utils'
|
||||
|
||||
import getFeeXrp from './getFeeXrp'
|
||||
@@ -19,83 +18,6 @@ const LEDGER_OFFSET = 20
|
||||
const RESTRICTED_NETWORKS = 1024
|
||||
const REQUIRED_NETWORKID_VERSION = '1.11.0'
|
||||
const HOOKS_TESTNET_ID = 21338
|
||||
interface ClassicAccountAndTag {
|
||||
classicAccount: string
|
||||
tag: number | false | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Autofills fields in a transaction. This will set `Sequence`, `Fee`,
|
||||
* `lastLedgerSequence` according to the current state of the server this Client
|
||||
* is connected to. It also converts all X-Addresses to classic addresses and
|
||||
* flags interfaces into numbers.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* const { Client } = require('xrpl')
|
||||
*
|
||||
* const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
*
|
||||
* async function createAndAutofillTransaction() {
|
||||
* const transaction = {
|
||||
* TransactionType: 'Payment',
|
||||
* Account: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh',
|
||||
* Destination: 'r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59',
|
||||
* Amount: '10000000' // 10 XRP in drops (1/1,000,000th of an XRP)
|
||||
* }
|
||||
*
|
||||
* try {
|
||||
* const autofilledTransaction = await client.autofill(transaction)
|
||||
* console.log(autofilledTransaction)
|
||||
* } catch (error) {
|
||||
* console.error(`Failed to autofill transaction: ${error}`)
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* createAndAutofillTransaction()
|
||||
* ```
|
||||
*
|
||||
* Autofill helps fill in fields which should be included in a transaction, but can be determined automatically
|
||||
* such as `LastLedgerSequence` and `Fee`. If you override one of the fields `autofill` changes, your explicit
|
||||
* values will be used instead. By default, this is done as part of `submit` and `submitAndWait` when you pass
|
||||
* in an unsigned transaction along with your wallet to be submitted.
|
||||
*
|
||||
* @param this - A client.
|
||||
* @param transaction - A {@link Transaction} in JSON format
|
||||
* @param signersCount - The expected number of signers for this transaction.
|
||||
* Only used for multisigned transactions.
|
||||
* @returns The autofilled transaction.
|
||||
*/
|
||||
async function autofill<T extends Transaction>(
|
||||
this: Client,
|
||||
transaction: T,
|
||||
signersCount?: number,
|
||||
): Promise<T> {
|
||||
const tx = { ...transaction }
|
||||
|
||||
setValidAddresses(tx)
|
||||
|
||||
setTransactionFlagsToNumber(tx)
|
||||
const promises: Array<Promise<void>> = []
|
||||
if (tx.NetworkID == null) {
|
||||
tx.NetworkID = txNeedsNetworkID(this) ? this.networkID : undefined
|
||||
}
|
||||
if (tx.Sequence == null) {
|
||||
promises.push(setNextValidSequenceNumber(this, tx))
|
||||
}
|
||||
if (tx.Fee == null) {
|
||||
promises.push(calculateFeePerTransactionType(this, tx, signersCount))
|
||||
}
|
||||
if (tx.LastLedgerSequence == null) {
|
||||
promises.push(setLatestValidatedLedgerSequence(this, tx))
|
||||
}
|
||||
if (tx.TransactionType === 'AccountDelete') {
|
||||
promises.push(checkAccountDeleteBlockers(this, tx))
|
||||
}
|
||||
|
||||
return Promise.all(promises).then(() => tx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the source rippled version is not later than the target rippled version.
|
||||
@@ -171,7 +93,7 @@ function isNotLaterRippledVersion(source: string, target: string): boolean {
|
||||
* @param client -- The connected client.
|
||||
* @returns True if required networkID, false otherwise.
|
||||
*/
|
||||
function txNeedsNetworkID(client: Client): boolean {
|
||||
export function txNeedsNetworkID(client: Client): boolean {
|
||||
if (
|
||||
client.networkID !== undefined &&
|
||||
client.networkID > RESTRICTED_NETWORKS
|
||||
@@ -190,7 +112,17 @@ function txNeedsNetworkID(client: Client): boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
function setValidAddresses(tx: Transaction): void {
|
||||
interface ClassicAccountAndTag {
|
||||
classicAccount: string
|
||||
tag: number | false | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets valid addresses for the transaction.
|
||||
*
|
||||
* @param tx - The transaction object.
|
||||
*/
|
||||
export function setValidAddresses(tx: Transaction): void {
|
||||
validateAccountAddress(tx, 'Account', 'SourceTag')
|
||||
// eslint-disable-next-line @typescript-eslint/dot-notation -- Destination can exist on Transaction
|
||||
if (tx['Destination'] != null) {
|
||||
@@ -206,6 +138,14 @@ function setValidAddresses(tx: Transaction): void {
|
||||
convertToClassicAddress(tx, 'RegularKey')
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the account address in a transaction object.
|
||||
*
|
||||
* @param tx - The transaction object.
|
||||
* @param accountField - The field name for the account address in the transaction object.
|
||||
* @param tagField - The field name for the tag in the transaction object.
|
||||
* @throws {ValidationError} If the tag field does not match the tag of the account address.
|
||||
*/
|
||||
function validateAccountAddress(
|
||||
tx: Transaction,
|
||||
accountField: string,
|
||||
@@ -227,6 +167,14 @@ function validateAccountAddress(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the classic account and tag from an account address.
|
||||
*
|
||||
* @param Account - The account address.
|
||||
* @param [expectedTag] - The expected tag for the account address.
|
||||
* @returns The classic account and tag.
|
||||
* @throws {ValidationError} If the address includes a tag that does not match the tag specified in the transaction.
|
||||
*/
|
||||
function getClassicAccountAndTag(
|
||||
Account: string,
|
||||
expectedTag?: number,
|
||||
@@ -249,6 +197,12 @@ function getClassicAccountAndTag(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the specified field of a transaction object to a classic address format.
|
||||
*
|
||||
* @param tx - The transaction object.
|
||||
* @param fieldName - The name of the field to convert.export
|
||||
*/
|
||||
function convertToClassicAddress(tx: Transaction, fieldName: string): void {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- assignment is safe
|
||||
const account = tx[fieldName]
|
||||
@@ -259,7 +213,15 @@ function convertToClassicAddress(tx: Transaction, fieldName: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
async function setNextValidSequenceNumber(
|
||||
/**
|
||||
* Sets the next valid sequence number for a transaction.
|
||||
*
|
||||
* @param client - The client object used for making requests.
|
||||
* @param tx - The transaction object for which the sequence number needs to be set.
|
||||
* @returns A Promise that resolves when the sequence number is set.
|
||||
* @throws {Error} If there is an error retrieving the account information.
|
||||
*/
|
||||
export async function setNextValidSequenceNumber(
|
||||
client: Client,
|
||||
tx: Transaction,
|
||||
): Promise<void> {
|
||||
@@ -273,7 +235,14 @@ async function setNextValidSequenceNumber(
|
||||
tx.Sequence = data.result.account_data.Sequence
|
||||
}
|
||||
|
||||
async function fetchOwnerReserveFee(client: Client): Promise<BigNumber> {
|
||||
/**
|
||||
* Fetches the account deletion fee from the server state using the provided client.
|
||||
*
|
||||
* @param client - The client object used to make the request.
|
||||
* @returns A Promise that resolves to the account deletion fee as a BigNumber.
|
||||
* @throws {Error} Throws an error if the account deletion fee cannot be fetched.
|
||||
*/
|
||||
async function fetchAccountDeleteFee(client: Client): Promise<BigNumber> {
|
||||
const response = await client.request({ command: 'server_state' })
|
||||
const fee = response.result.state.validated_ledger?.reserve_inc
|
||||
|
||||
@@ -284,7 +253,15 @@ async function fetchOwnerReserveFee(client: Client): Promise<BigNumber> {
|
||||
return new BigNumber(fee)
|
||||
}
|
||||
|
||||
async function calculateFeePerTransactionType(
|
||||
/**
|
||||
* Calculates the fee per transaction type.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param tx - The transaction object.
|
||||
* @param [signersCount=0] - The number of signers (default is 0). Only used for multisigning.
|
||||
* @returns A promise that resolves with void. Modifies the `tx` parameter to give it the calculated fee.
|
||||
*/
|
||||
export async function calculateFeePerTransactionType(
|
||||
client: Client,
|
||||
tx: Transaction,
|
||||
signersCount = 0,
|
||||
@@ -309,7 +286,7 @@ async function calculateFeePerTransactionType(
|
||||
tx.TransactionType === 'AccountDelete' ||
|
||||
tx.TransactionType === 'AMMCreate'
|
||||
) {
|
||||
baseFee = await fetchOwnerReserveFee(client)
|
||||
baseFee = await fetchAccountDeleteFee(client)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -331,11 +308,25 @@ async function calculateFeePerTransactionType(
|
||||
tx.Fee = totalFee.dp(0, BigNumber.ROUND_CEIL).toString(10)
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales the given value by multiplying it with the provided multiplier.
|
||||
*
|
||||
* @param value - The value to be scaled.
|
||||
* @param multiplier - The multiplier to scale the value.
|
||||
* @returns The scaled value as a string.
|
||||
*/
|
||||
function scaleValue(value, multiplier): string {
|
||||
return new BigNumber(value).times(multiplier).toString()
|
||||
}
|
||||
|
||||
async function setLatestValidatedLedgerSequence(
|
||||
/**
|
||||
* Sets the latest validated ledger sequence for the transaction.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param tx - The transaction object.
|
||||
* @returns A promise that resolves with void. Modifies the `tx` parameter setting `LastLedgerSequence`.
|
||||
*/
|
||||
export async function setLatestValidatedLedgerSequence(
|
||||
client: Client,
|
||||
tx: Transaction,
|
||||
): Promise<void> {
|
||||
@@ -344,7 +335,14 @@ async function setLatestValidatedLedgerSequence(
|
||||
tx.LastLedgerSequence = ledgerSequence + LEDGER_OFFSET
|
||||
}
|
||||
|
||||
async function checkAccountDeleteBlockers(
|
||||
/**
|
||||
* Checks for any blockers that prevent the deletion of an account.
|
||||
*
|
||||
* @param client - The client object.
|
||||
* @param tx - The transaction object.
|
||||
* @returns A promise that resolves with void if there are no blockers, or rejects with an XrplError if there are blockers.
|
||||
*/
|
||||
export async function checkAccountDeleteBlockers(
|
||||
client: Client,
|
||||
tx: Transaction,
|
||||
): Promise<void> {
|
||||
@@ -367,5 +365,3 @@ async function checkAccountDeleteBlockers(
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
|
||||
export default autofill
|
||||
|
||||
@@ -1,121 +1,15 @@
|
||||
import type { Balance, Client } from '..'
|
||||
import {
|
||||
AccountLinesRequest,
|
||||
AccountLinesTrustline,
|
||||
LedgerIndex,
|
||||
AccountInfoRequest,
|
||||
} from '../models'
|
||||
import { dropsToXrp } from '../utils'
|
||||
import { AccountLinesTrustline, Balance } from '../models'
|
||||
|
||||
function formatBalances(trustlines: AccountLinesTrustline[]): Balance[] {
|
||||
/**
|
||||
* Formats an array of trustlines into an array of balances.
|
||||
*
|
||||
* @param trustlines - The array of trustlines to format.
|
||||
* @returns An array of balances, each containing the value, currency, and issuer.
|
||||
*/
|
||||
export function formatBalances(trustlines: AccountLinesTrustline[]): Balance[] {
|
||||
return trustlines.map((trustline) => ({
|
||||
value: trustline.balance,
|
||||
currency: trustline.currency,
|
||||
issuer: trustline.account,
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the XRP balance for an account.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const client = new Client(wss://s.altnet.rippletest.net:51233)
|
||||
* const balance = await client.getXrpBalance('rG1QQv2nh2gr7RCZ1P8YYcBUKCCN633jCn')
|
||||
* console.log(balance)
|
||||
* /// '200'
|
||||
* ```
|
||||
*
|
||||
* @param this - Client.
|
||||
* @param address - Address of the account to retrieve XRP balance.
|
||||
* @param options - Options to include for getting the XRP balance.
|
||||
* @param options.ledger_index - Retrieve the account balances at a given
|
||||
* ledger_index.
|
||||
* @param options.ledger_hash - Retrieve the account balances at the ledger with
|
||||
* a given ledger_hash.
|
||||
* @returns The XRP balance of the account (as a string).
|
||||
*/
|
||||
async function getXrpBalance(
|
||||
this: Client,
|
||||
address: string,
|
||||
options: {
|
||||
ledger_hash?: string
|
||||
ledger_index?: LedgerIndex
|
||||
} = {},
|
||||
): Promise<string> {
|
||||
const xrpRequest: AccountInfoRequest = {
|
||||
command: 'account_info',
|
||||
account: address,
|
||||
ledger_index: options.ledger_index ?? 'validated',
|
||||
ledger_hash: options.ledger_hash,
|
||||
}
|
||||
const response = await this.request(xrpRequest)
|
||||
return dropsToXrp(response.result.account_data.Balance)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get XRP/non-XRP balances for an account.
|
||||
*
|
||||
* @param this - Client.
|
||||
* @param address - Address of the account to retrieve balances for.
|
||||
* @param options - Allows the client to specify a ledger_hash, ledger_index,
|
||||
* filter by peer, and/or limit number of balances.
|
||||
* @param options.ledger_index - Retrieve the account balances at a given
|
||||
* ledger_index.
|
||||
* @param options.ledger_hash - Retrieve the account balances at the ledger with
|
||||
* a given ledger_hash.
|
||||
* @param options.peer - Filter balances by peer.
|
||||
* @param options.limit - Limit number of balances to return.
|
||||
* @returns An array of XRP/non-XRP balances for the given account.
|
||||
*/
|
||||
// eslint-disable-next-line max-lines-per-function -- Longer definition is required for end users to see the definition.
|
||||
async function getBalances(
|
||||
this: Client,
|
||||
address: string,
|
||||
options: {
|
||||
ledger_hash?: string
|
||||
ledger_index?: LedgerIndex
|
||||
peer?: string
|
||||
limit?: number
|
||||
} = {},
|
||||
): Promise<
|
||||
Array<{ value: string; currency: string; issuer?: string | undefined }>
|
||||
> {
|
||||
const balances: Balance[] = []
|
||||
|
||||
// get XRP balance
|
||||
let xrpPromise: Promise<string> = Promise.resolve('')
|
||||
if (!options.peer) {
|
||||
xrpPromise = this.getXrpBalance(address, {
|
||||
ledger_hash: options.ledger_hash,
|
||||
ledger_index: options.ledger_index,
|
||||
})
|
||||
}
|
||||
|
||||
// get non-XRP balances
|
||||
const linesRequest: AccountLinesRequest = {
|
||||
command: 'account_lines',
|
||||
account: address,
|
||||
ledger_index: options.ledger_index ?? 'validated',
|
||||
ledger_hash: options.ledger_hash,
|
||||
peer: options.peer,
|
||||
limit: options.limit,
|
||||
}
|
||||
const linesPromise = this.requestAll(linesRequest)
|
||||
|
||||
// combine results
|
||||
await Promise.all([xrpPromise, linesPromise]).then(
|
||||
([xrpBalance, linesResponses]) => {
|
||||
const accountLinesBalance = linesResponses.flatMap((response) =>
|
||||
formatBalances(response.result.lines),
|
||||
)
|
||||
if (xrpBalance !== '') {
|
||||
balances.push({ currency: 'XRP', value: xrpBalance })
|
||||
}
|
||||
balances.push(...accountLinesBalance)
|
||||
},
|
||||
)
|
||||
return balances.slice(0, options.limit)
|
||||
}
|
||||
|
||||
export { getXrpBalance, getBalances }
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import type { Client } from '..'
|
||||
|
||||
/**
|
||||
* Returns the index of the most recently validated ledger.
|
||||
*
|
||||
* @param this - The Client used to connect to the ledger.
|
||||
* @returns The most recently validated ledger index.
|
||||
*/
|
||||
export default async function getLedgerIndex(this: Client): Promise<number> {
|
||||
const ledgerResponse = await this.request({
|
||||
command: 'ledger',
|
||||
ledger_index: 'validated',
|
||||
})
|
||||
return ledgerResponse.result.ledger_index
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable max-lines-per-function -- Needs to process orderbooks. */
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
import type { Client } from '../client'
|
||||
@@ -9,7 +8,6 @@ import {
|
||||
BookOffer,
|
||||
BookOfferCurrency,
|
||||
BookOffersRequest,
|
||||
BookOffersResponse,
|
||||
} from '../models/methods/bookOffers'
|
||||
|
||||
const DEFAULT_LIMIT = 20
|
||||
@@ -31,43 +29,40 @@ const getOrderbookOptionsSet = new Set([
|
||||
])
|
||||
|
||||
/**
|
||||
* Fetch orderbook (buy/sell orders) between two currency pairs. This checks both sides of the orderbook
|
||||
* by making two `order_book` requests (with the second reversing takerPays and takerGets). Returned offers are
|
||||
* not normalized in this function, so either currency could be takerGets or takerPays.
|
||||
*
|
||||
* @param this - Client.
|
||||
* @param currency1 - Specification of one currency involved. (With a currency code and optionally an issuer)
|
||||
* @param currency2 - Specification of a second currency involved. (With a currency code and optionally an issuer)
|
||||
* @param options - Options allowing the client to specify ledger_index,
|
||||
* ledger_hash, filter by taker, and/or limit number of orders.
|
||||
* @param options.ledger_index - Retrieve the orderbook at a given ledger_index.
|
||||
* @param options.ledger_hash - Retrieve the orderbook at the ledger with a
|
||||
* given ledger_hash.
|
||||
* @param options.taker - Filter orders by taker.
|
||||
* @param options.limit - The limit passed into each book_offers request.
|
||||
* Can return more than this due to two calls being made. Defaults to 20.
|
||||
* @returns An object containing buy and sell objects.
|
||||
* Represents the options for retrieving the order book.
|
||||
*/
|
||||
export interface GetOrderBookOptions {
|
||||
/**
|
||||
* The limit on the number of offers to return.
|
||||
*/
|
||||
// eslint-disable-next-line max-params, complexity -- Once bound to Client, getOrderbook only has 3 parameters.
|
||||
async function getOrderbook(
|
||||
this: Client,
|
||||
currency1: BookOfferCurrency,
|
||||
currency2: BookOfferCurrency,
|
||||
options: {
|
||||
limit?: number
|
||||
/**
|
||||
* The ledger index of the ledger to use.
|
||||
*/
|
||||
ledger_index?: LedgerIndex
|
||||
/**
|
||||
* The ledger hash of the ledger to use.
|
||||
*/
|
||||
ledger_hash?: string | null
|
||||
/**
|
||||
* The account that takes the offers.
|
||||
*/
|
||||
taker?: string | null
|
||||
} = {},
|
||||
): Promise<{
|
||||
buy: BookOffer[]
|
||||
sell: BookOffer[]
|
||||
}> {
|
||||
Object.keys(options).forEach((key) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the options for retrieving the order book.
|
||||
*
|
||||
* @param options - The options to validate.
|
||||
* @throws {ValidationError} If any validation errors occur.
|
||||
*/
|
||||
// eslint-disable-next-line complexity -- Necessary for validation.
|
||||
export function validateOrderbookOptions(options: GetOrderBookOptions): void {
|
||||
for (const key of Object.keys(options)) {
|
||||
if (!getOrderbookOptionsSet.has(key)) {
|
||||
throw new ValidationError(`Unexpected option: ${key}`, options)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (options.limit && typeof options.limit !== 'number') {
|
||||
throw new ValidationError('limit must be a number', options.limit)
|
||||
@@ -101,7 +96,30 @@ async function getOrderbook(
|
||||
if (options.taker !== undefined && typeof options.taker !== 'string') {
|
||||
throw new ValidationError('taker must be a string', options.taker)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request object for retrieving book offers.
|
||||
*
|
||||
* @param currency1 - The first currency in the pair.
|
||||
* @param currency2 - The second currency in the pair.
|
||||
* @param options - Additional options for the request.
|
||||
* @param [options.limit] - The maximum number of offers to retrieve.
|
||||
* @param [options.ledger_index] - The ledger index to use for retrieval.
|
||||
* @param [options.ledger_hash] - The ledger hash to use for retrieval.
|
||||
* @param [options.taker] - The taker address for retrieval.
|
||||
* @returns The created request object.
|
||||
*/
|
||||
export function createBookOffersRequest(
|
||||
currency1: BookOfferCurrency,
|
||||
currency2: BookOfferCurrency,
|
||||
options: {
|
||||
limit?: number
|
||||
ledger_index?: LedgerIndex
|
||||
ledger_hash?: string | null
|
||||
taker?: string | null
|
||||
},
|
||||
): BookOffersRequest {
|
||||
const request: BookOffersRequest = {
|
||||
command: 'book_offers',
|
||||
taker_pays: currency1,
|
||||
@@ -111,26 +129,78 @@ async function getOrderbook(
|
||||
limit: options.limit ?? DEFAULT_LIMIT,
|
||||
taker: options.taker ? options.taker : undefined,
|
||||
}
|
||||
// 2. Make Request
|
||||
const directOfferResults: BookOffersResponse[] = await this.requestAll(
|
||||
request,
|
||||
)
|
||||
request.taker_gets = currency1
|
||||
request.taker_pays = currency2
|
||||
const reverseOfferResults = await this.requestAll(request)
|
||||
// 3. Return Formatted Response
|
||||
|
||||
const directOffers = directOfferResults.flatMap(
|
||||
(directOfferResult: BookOffersResponse) => directOfferResult.result.offers,
|
||||
)
|
||||
const reverseOffers = reverseOfferResults.flatMap(
|
||||
(reverseOfferResult) => reverseOfferResult.result.offers,
|
||||
)
|
||||
return request
|
||||
}
|
||||
|
||||
const orders = [...directOffers, ...reverseOffers]
|
||||
// separate out the buy and sell orders
|
||||
type BookOfferResult = BookOffer[]
|
||||
|
||||
/**
|
||||
* Retrieves all book offer results using the given request.
|
||||
*
|
||||
* @param client - The Ripple client.
|
||||
* @param request - The request object.
|
||||
* @returns The array of book offer results.
|
||||
*/
|
||||
export async function requestAllOffers(
|
||||
client: Client,
|
||||
request: BookOffersRequest,
|
||||
): Promise<BookOfferResult[]> {
|
||||
const results = await client.requestAll(request)
|
||||
return results.map((result) => result.result.offers)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a reverse request object by swapping the taker pays and taker gets amounts.
|
||||
*
|
||||
* @param request - The original request object.
|
||||
* @returns The reverse request object.
|
||||
*/
|
||||
export function reverseRequest(request: BookOffersRequest): BookOffersRequest {
|
||||
return {
|
||||
...request,
|
||||
taker_pays: request.taker_gets,
|
||||
taker_gets: request.taker_pays,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the offers from the book offer results.
|
||||
*
|
||||
* @param offerResults - The array of book offer results.
|
||||
* @returns The extracted offers.
|
||||
*/
|
||||
export function extractOffers(offerResults: BookOfferResult[]): BookOffer[] {
|
||||
return offerResults.flatMap((offerResult) => offerResult)
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines the direct and reverse offers into a single array.
|
||||
*
|
||||
* @param directOffers - The direct offers.
|
||||
* @param reverseOffers - The reverse offers.
|
||||
* @returns The combined array of offers.
|
||||
*/
|
||||
export function combineOrders(
|
||||
directOffers: BookOffer[],
|
||||
reverseOffers: BookOffer[],
|
||||
): BookOffer[] {
|
||||
return [...directOffers, ...reverseOffers]
|
||||
}
|
||||
|
||||
/**
|
||||
* Separates the buy and sell orders from the given array of orders.
|
||||
*
|
||||
* @param orders - The array of orders.
|
||||
* @returns The separated buy and sell orders.
|
||||
*/
|
||||
export function separateBuySellOrders(orders: BookOffer[]): {
|
||||
buy: BookOffer[]
|
||||
sell: BookOffer[]
|
||||
} {
|
||||
const buy: BookOffer[] = []
|
||||
const sell: BookOffer[] = []
|
||||
|
||||
orders.forEach((order) => {
|
||||
// eslint-disable-next-line no-bitwise -- necessary for flags check
|
||||
if ((order.Flags & OfferFlags.lsfSell) === 0) {
|
||||
@@ -139,15 +209,21 @@ async function getOrderbook(
|
||||
sell.push(order)
|
||||
}
|
||||
})
|
||||
/*
|
||||
* Sort the orders
|
||||
* for both buys and sells, lowest quality is closest to mid-market
|
||||
* we sort the orders so that earlier orders are closer to mid-market
|
||||
*/
|
||||
return {
|
||||
buy: sortOffers(buy).slice(0, options.limit),
|
||||
sell: sortOffers(sell).slice(0, options.limit),
|
||||
}
|
||||
|
||||
return { buy, sell }
|
||||
}
|
||||
|
||||
export default getOrderbook
|
||||
/**
|
||||
* Sorts and limits the given array of offers.
|
||||
*
|
||||
* @param offers - The array of offers to sort and limit.
|
||||
* @param [limit] - The maximum number of offers to include.
|
||||
* @returns The sorted and limited array of offers.
|
||||
*/
|
||||
export function sortAndLimitOffers(
|
||||
offers: BookOffer[],
|
||||
limit?: number,
|
||||
): BookOffer[] {
|
||||
const sortedOffers = sortOffers(offers)
|
||||
return sortedOffers.slice(0, limit)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
export { default as autofill } from './autofill'
|
||||
|
||||
export { getBalances, getXrpBalance } from './balances'
|
||||
|
||||
export { default as getLedgerIndex } from './getLedgerIndex'
|
||||
|
||||
export { default as getOrderbook } from './getOrderbook'
|
||||
|
||||
export * from './submit'
|
||||
|
||||
export * from './utils'
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Signer } from '../models/common'
|
||||
import { TxRequest, TxResponse } from '../models/methods'
|
||||
import { Transaction } from '../models/transactions'
|
||||
import { BaseTransaction } from '../models/transactions/common'
|
||||
import { hashes } from '../utils'
|
||||
|
||||
/** Approximate time for a ledger to close, in milliseconds */
|
||||
const LEDGER_CLOSE_TIME = 1000
|
||||
@@ -17,130 +16,31 @@ async function sleep(ms: number): Promise<void> {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits a signed/unsigned transaction.
|
||||
* Steps performed on a transaction:
|
||||
* 1. Autofill.
|
||||
* 2. Sign & Encode.
|
||||
* 3. Submit.
|
||||
*
|
||||
* @param this - A Client.
|
||||
* @param transaction - A transaction to autofill, sign & encode, and submit.
|
||||
* @param opts - (Optional) Options used to sign and submit a transaction.
|
||||
* @param opts.autofill - If true, autofill a transaction.
|
||||
* @param opts.failHard - If true, and the transaction fails locally, do not retry or relay the transaction to other servers.
|
||||
* @param opts.wallet - A wallet to sign a transaction. It must be provided when submitting an unsigned transaction.
|
||||
* @returns A promise that contains SubmitResponse.
|
||||
* @throws RippledError if submit request fails.
|
||||
*/
|
||||
async function submit(
|
||||
this: Client,
|
||||
transaction: Transaction | string,
|
||||
opts?: {
|
||||
// If true, autofill a transaction.
|
||||
autofill?: boolean
|
||||
// If true, and the transaction fails locally, do not retry or relay the transaction to other servers.
|
||||
failHard?: boolean
|
||||
// A wallet to sign a transaction. It must be provided when submitting an unsigned transaction.
|
||||
wallet?: Wallet
|
||||
},
|
||||
): Promise<SubmitResponse> {
|
||||
const signedTx = await getSignedTx(this, transaction, opts)
|
||||
return submitRequest(this, signedTx, opts?.failHard)
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously submits a transaction and verifies that it has been included in a
|
||||
* validated ledger (or has errored/will not be included for some reason).
|
||||
* See [Reliable Transaction Submission](https://xrpl.org/reliable-transaction-submission.html).
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* ```ts
|
||||
* const { Client, Wallet } = require('xrpl')
|
||||
* const client = new Client('wss://s.altnet.rippletest.net:51233')
|
||||
*
|
||||
* async function submitTransaction() {
|
||||
* const senderWallet = client.fundWallet()
|
||||
* const recipientWallet = client.fundWallet()
|
||||
*
|
||||
* const transaction = {
|
||||
* TransactionType: 'Payment',
|
||||
* Account: senderWallet.address,
|
||||
* Destination: recipientWallet.address,
|
||||
* Amount: '10'
|
||||
* }
|
||||
*
|
||||
* try {
|
||||
* await client.submit(signedTransaction, { wallet: senderWallet })
|
||||
* console.log(result)
|
||||
* } catch (error) {
|
||||
* console.error(`Failed to submit transaction: ${error}`)
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* submitTransaction()
|
||||
* ```
|
||||
*
|
||||
* In this example we submit a payment transaction between two newly created testnet accounts.
|
||||
*
|
||||
* Under the hood, `submit` will call `client.autofill` by default, and because we've passed in a `Wallet` it
|
||||
* Will also sign the transaction for us before submitting the signed transaction binary blob to the ledger.
|
||||
*
|
||||
* This is similar to `submitAndWait` which does all of the above, but also waits to see if the transaction has been validated.
|
||||
* @param this - A Client.
|
||||
* @param transaction - A transaction to autofill, sign & encode, and submit.
|
||||
* @param opts - (Optional) Options used to sign and submit a transaction.
|
||||
* @param opts.autofill - If true, autofill a transaction.
|
||||
* @param opts.failHard - If true, and the transaction fails locally, do not retry or relay the transaction to other servers.
|
||||
* @param opts.wallet - A wallet to sign a transaction. It must be provided when submitting an unsigned transaction.
|
||||
* @throws Connection errors: If the `Client` object is unable to establish a connection to the specified WebSocket endpoint,
|
||||
* an error will be thrown.
|
||||
* @throws Transaction errors: If the submitted transaction is invalid or cannot be included in a validated ledger for any
|
||||
* reason, the promise returned by `submitAndWait()` will be rejected with an error. This could include issues with insufficient
|
||||
* balance, invalid transaction fields, or other issues specific to the transaction being submitted.
|
||||
* @throws Ledger errors: If the ledger being used to submit the transaction is undergoing maintenance or otherwise unavailable,
|
||||
* an error will be thrown.
|
||||
* @throws Timeout errors: If the transaction takes longer than the specified timeout period to be included in a validated
|
||||
* ledger, the promise returned by `submitAndWait()` will be rejected with an error.
|
||||
* @returns A promise that contains TxResponse, that will return when the transaction has been validated.
|
||||
*/
|
||||
async function submitAndWait<T extends Transaction = Transaction>(
|
||||
this: Client,
|
||||
transaction: T | string,
|
||||
opts?: {
|
||||
// If true, autofill a transaction.
|
||||
autofill?: boolean
|
||||
// If true, and the transaction fails locally, do not retry or relay the transaction to other servers.
|
||||
failHard?: boolean
|
||||
// A wallet to sign a transaction. It must be provided when submitting an unsigned transaction.
|
||||
wallet?: Wallet
|
||||
},
|
||||
): Promise<TxResponse<T>> {
|
||||
const signedTx = await getSignedTx(this, transaction, opts)
|
||||
|
||||
const lastLedger = getLastLedgerSequence(signedTx)
|
||||
if (lastLedger == null) {
|
||||
throw new ValidationError(
|
||||
'Transaction must contain a LastLedgerSequence value for reliable submission.',
|
||||
)
|
||||
}
|
||||
|
||||
const response = await submitRequest(this, signedTx, opts?.failHard)
|
||||
|
||||
const txHash = hashes.hashSignedTx(signedTx)
|
||||
return waitForFinalTransactionOutcome(
|
||||
this,
|
||||
txHash,
|
||||
lastLedger,
|
||||
response.result.engine_result,
|
||||
)
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
// Encodes and submits a signed transaction.
|
||||
async function submitRequest(
|
||||
/**
|
||||
* Submits a request to the client with a signed transaction.
|
||||
*
|
||||
* @param client - The client to submit the request to.
|
||||
* @param signedTransaction - The signed transaction to submit. It can be either a Transaction object or a
|
||||
* string (encode from ripple-binary-codec) representation of the transaction.
|
||||
* @param [failHard=false] - Optional. Determines whether the submission should fail hard (true) or not (false). Default is false.
|
||||
* @returns A promise that resolves with the response from the client.
|
||||
* @throws {ValidationError} If the signed transaction is not valid (not signed).
|
||||
*
|
||||
* @example
|
||||
* import { Client } from "xrpl"
|
||||
* const client = new Client("wss://s.altnet.rippletest.net:51233");
|
||||
* await client.connect();
|
||||
* const signedTransaction = createSignedTransaction();
|
||||
* // Example 1: Submitting a Transaction object
|
||||
* const response1 = await submitRequest(client, signedTransaction);
|
||||
*
|
||||
* // Example 2: Submitting a string representation of the transaction
|
||||
* const signedTransactionString = encode(signedTransaction);
|
||||
* const response2 = await submitRequest(client, signedTransactionString, true);
|
||||
*/
|
||||
export async function submitRequest(
|
||||
client: Client,
|
||||
signedTransaction: Transaction | string,
|
||||
failHard = false,
|
||||
@@ -161,14 +61,49 @@ async function submitRequest(
|
||||
return client.request(request)
|
||||
}
|
||||
|
||||
/*
|
||||
* The core logic of reliable submission. This polls the ledger until the result of the
|
||||
* transaction can be considered final, meaning it has either been included in a
|
||||
* validated ledger, or the transaction's lastLedgerSequence has been surpassed by the
|
||||
* latest ledger sequence (meaning it will never be included in a validated ledger).
|
||||
/**
|
||||
* Waits for the final outcome of a transaction by polling the ledger until the result can be considered final,
|
||||
* meaning it has either been included in a validated ledger, or the transaction's lastLedgerSequence has been
|
||||
* surpassed by the latest ledger sequence (meaning it will never be included in a validated ledger).
|
||||
*
|
||||
* @template T - The type of the transaction. Defaults to `Transaction`.
|
||||
* @param client - The client to use for requesting transaction information.
|
||||
* @param txHash - The hash of the transaction to wait for.
|
||||
* @param lastLedger - The last ledger sequence of the transaction.
|
||||
* @param submissionResult - The preliminary result of the transaction.
|
||||
* @returns A promise that resolves with the final transaction response.
|
||||
*
|
||||
* @throws {XrplError} If the latest ledger sequence surpasses the transaction's lastLedgerSequence.
|
||||
*
|
||||
* @example
|
||||
* import { hashes, Client } from "xrpl"
|
||||
* const client = new Client("wss://s.altnet.rippletest.net:51233")
|
||||
* await client.connect()
|
||||
*
|
||||
* const transaction = createTransaction() // your transaction function
|
||||
*
|
||||
* const signedTx = await getSignedTx(this, transaction)
|
||||
*
|
||||
* const lastLedger = getLastLedgerSequence(signedTx)
|
||||
*
|
||||
* if (lastLedger == null) {
|
||||
* throw new ValidationError(
|
||||
* 'Transaction must contain a LastLedgerSequence value for reliable submission.',
|
||||
* )
|
||||
* }
|
||||
*
|
||||
* const response = await submitRequest(this, signedTx, opts?.failHard)
|
||||
*
|
||||
* const txHash = hashes.hashSignedTx(signedTx)
|
||||
* return waitForFinalTransactionOutcome(
|
||||
* this,
|
||||
* txHash,
|
||||
* lastLedger,
|
||||
* response.result.engine_result,
|
||||
* )
|
||||
*/
|
||||
// eslint-disable-next-line max-params, max-lines-per-function -- this function needs to display and do with more information.
|
||||
async function waitForFinalTransactionOutcome<
|
||||
export async function waitForFinalTransactionOutcome<
|
||||
T extends BaseTransaction = Transaction,
|
||||
>(
|
||||
client: Client,
|
||||
@@ -248,8 +183,40 @@ function isSigned(transaction: Transaction | string): boolean {
|
||||
return tx.SigningPubKey != null && tx.TxnSignature != null
|
||||
}
|
||||
|
||||
// initializes a transaction for a submit request
|
||||
async function getSignedTx(
|
||||
/**
|
||||
* Updates a transaction with `autofill` then signs it if it is unsigned.
|
||||
*
|
||||
* @param client - The client from which to retrieve the signed transaction.
|
||||
* @param transaction - The transaction to retrieve. It can be either a Transaction object or
|
||||
* a string (encode from ripple-binary-codec) representation of the transaction.
|
||||
* @param [options={}] - Optional. Additional options for retrieving the signed transaction.
|
||||
* @param [options.autofill=true] - Optional. Determines whether the transaction should be autofilled (true)
|
||||
* or not (false). Default is true.
|
||||
* @param [options.wallet] - Optional. A wallet to sign the transaction. It must be provided when submitting
|
||||
* an unsigned transaction. Default is undefined.
|
||||
* @returns A promise that resolves with the signed transaction.
|
||||
*
|
||||
* @throws {ValidationError} If the transaction is not signed and no wallet is provided.
|
||||
*
|
||||
* @example
|
||||
* import { Client } from "xrpl"
|
||||
* import { encode } from "ripple-binary-codec"
|
||||
*
|
||||
* const client = new Client("wss://s.altnet.rippletest.net:51233");
|
||||
* await client.connect():
|
||||
* const transaction = createTransaction(); // createTransaction is your function to create a transaction
|
||||
* const options = {
|
||||
* autofill: true,
|
||||
* wallet: myWallet,
|
||||
* };
|
||||
*
|
||||
* // Example 1: Retrieving a signed Transaction object
|
||||
* const signedTx1 = await getSignedTx(client, transaction, options);
|
||||
*
|
||||
* // Example 2: Retrieving a string representation of the signed transaction
|
||||
* const signedTxString = await getSignedTx(client, encode(transaction), options);
|
||||
*/
|
||||
export async function getSignedTx(
|
||||
client: Client,
|
||||
transaction: Transaction | string,
|
||||
{
|
||||
@@ -258,8 +225,6 @@ async function getSignedTx(
|
||||
}: {
|
||||
// If true, autofill a transaction.
|
||||
autofill?: boolean
|
||||
// If true, and the transaction fails locally, do not retry or relay the transaction to other servers.
|
||||
failHard?: boolean
|
||||
// A wallet to sign a transaction. It must be provided when submitting an unsigned transaction.
|
||||
wallet?: Wallet
|
||||
} = {},
|
||||
@@ -288,7 +253,26 @@ async function getSignedTx(
|
||||
}
|
||||
|
||||
// checks if there is a LastLedgerSequence as a part of the transaction
|
||||
function getLastLedgerSequence(
|
||||
/**
|
||||
* Retrieves the last ledger sequence from a transaction.
|
||||
*
|
||||
* @param transaction - The transaction to retrieve the last ledger sequence from. It can be either a Transaction object or
|
||||
* a string (encode from ripple-binary-codec) representation of the transaction.
|
||||
* @returns The last ledger sequence of the transaction, or null if not available.
|
||||
*
|
||||
* @example
|
||||
* const transaction = createTransaction(); // your function to create a transaction
|
||||
*
|
||||
* // Example 1: Retrieving the last ledger sequence from a Transaction object
|
||||
* const lastLedgerSequence1 = getLastLedgerSequence(transaction);
|
||||
* console.log(lastLedgerSequence1); // Output: 12345
|
||||
*
|
||||
* // Example 2: Retrieving the last ledger sequence from a string representation of the transaction
|
||||
* const transactionString = encode(transaction);
|
||||
* const lastLedgerSequence2 = getLastLedgerSequence(transactionString);
|
||||
* console.log(lastLedgerSequence2); // Output: 67890
|
||||
*/
|
||||
export function getLastLedgerSequence(
|
||||
transaction: Transaction | string,
|
||||
): number | null {
|
||||
const tx = typeof transaction === 'string' ? decode(transaction) : transaction
|
||||
@@ -301,5 +285,3 @@ function isAccountDelete(transaction: Transaction | string): boolean {
|
||||
const tx = typeof transaction === 'string' ? decode(transaction) : transaction
|
||||
return tx.TransactionType === 'AccountDelete'
|
||||
}
|
||||
|
||||
export { submit, submitAndWait }
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
import { expect } from 'chai'
|
||||
import cloneDeep from 'lodash/cloneDeep'
|
||||
|
||||
import type { TransactionStream } from '../../src'
|
||||
import rippled from '../fixtures/rippled'
|
||||
import {
|
||||
setupClient,
|
||||
@@ -23,7 +22,10 @@ describe('client handling of tfPartialPayments', function () {
|
||||
|
||||
it('Tx with no tfPartialPayment', async function () {
|
||||
testContext.mockRippled!.addResponse('tx', rippled.tx.Payment)
|
||||
const resp = await testContext.client.request({ command: 'tx' })
|
||||
const resp = await testContext.client.request({
|
||||
command: 'tx',
|
||||
transaction: rippled.tx.Payment.result.hash,
|
||||
})
|
||||
|
||||
expect(resp.warnings).to.equal(undefined)
|
||||
})
|
||||
@@ -31,7 +33,10 @@ describe('client handling of tfPartialPayments', function () {
|
||||
it('Tx with IOU tfPartialPayment', async function () {
|
||||
const mockResponse = { ...rippled.tx.Payment, result: partialPaymentIOU }
|
||||
testContext.mockRippled!.addResponse('tx', mockResponse)
|
||||
const resp = await testContext.client.request({ command: 'tx' })
|
||||
const resp = await testContext.client.request({
|
||||
command: 'tx',
|
||||
transaction: mockResponse.result.hash,
|
||||
})
|
||||
|
||||
expect(resp.warnings).to.deep.equal([
|
||||
{
|
||||
@@ -44,7 +49,10 @@ describe('client handling of tfPartialPayments', function () {
|
||||
it('Tx with XRP tfPartialPayment', async function () {
|
||||
const mockResponse = { ...rippled.tx.Payment, result: partialPaymentXRP }
|
||||
testContext.mockRippled!.addResponse('tx', mockResponse)
|
||||
const resp = await testContext.client.request({ command: 'tx' })
|
||||
const resp = await testContext.client.request({
|
||||
command: 'tx',
|
||||
transaction: mockResponse.result.hash,
|
||||
})
|
||||
|
||||
expect(resp.warnings).to.deep.equal([
|
||||
{
|
||||
@@ -59,7 +67,10 @@ describe('client handling of tfPartialPayments', function () {
|
||||
'account_tx',
|
||||
rippled.account_tx.normal,
|
||||
)
|
||||
const resp = await testContext.client.request({ command: 'account_tx' })
|
||||
const resp = await testContext.client.request({
|
||||
command: 'account_tx',
|
||||
account: rippled.account_tx.normal.result.account,
|
||||
})
|
||||
|
||||
expect(resp.warnings).to.equal(undefined)
|
||||
})
|
||||
@@ -119,6 +130,7 @@ describe('client handling of tfPartialPayments', function () {
|
||||
)
|
||||
const resp = await testContext.client.request({
|
||||
command: 'transaction_entry',
|
||||
tx_hash: rippled.transaction_entry.result.tx_json.hash,
|
||||
})
|
||||
|
||||
expect(resp.warnings).to.equal(undefined)
|
||||
@@ -130,6 +142,7 @@ describe('client handling of tfPartialPayments', function () {
|
||||
testContext.mockRippled!.addResponse('transaction_entry', mockResponse)
|
||||
const resp = await testContext.client.request({
|
||||
command: 'transaction_entry',
|
||||
tx_hash: mockResponse.result.tx_json.hash,
|
||||
})
|
||||
|
||||
expect(resp.warnings).to.deep.equal([
|
||||
@@ -145,7 +158,7 @@ describe('client handling of tfPartialPayments', function () {
|
||||
'transaction_entry',
|
||||
rippled.transaction_entry,
|
||||
)
|
||||
testContext.client.on('transaction', (tx: TransactionStream) => {
|
||||
testContext.client.on('transaction', (tx) => {
|
||||
expect(tx.warnings).to.equal(undefined)
|
||||
done()
|
||||
})
|
||||
@@ -161,7 +174,7 @@ describe('client handling of tfPartialPayments', function () {
|
||||
'transaction_entry',
|
||||
rippled.transaction_entry,
|
||||
)
|
||||
testContext.client.on('transaction', (tx: TransactionStream) => {
|
||||
testContext.client.on('transaction', (tx) => {
|
||||
expect(tx.warnings).to.deep.equal([
|
||||
{
|
||||
id: 2001,
|
||||
|
||||
@@ -57,4 +57,31 @@ describe('client.requestNextPage', function () {
|
||||
'response does not have a next page',
|
||||
)
|
||||
})
|
||||
|
||||
// TODO: Write this test to verify multiple types of commands can be run - https://github.com/XRPLF/xrpl.js/issues/2384
|
||||
// it('requests different types of commands', async function () {
|
||||
// testContext.mockRippled!.addResponse('account_channels', {
|
||||
// id: 0,
|
||||
// result: {
|
||||
// account: testContext.wallet.classicAddress,
|
||||
// channels: [],
|
||||
// ledger_hash:
|
||||
// 'C8BFA74A740AA22AD9BD724781589319052398B0C6C817B88D55628E07B7B4A1',
|
||||
// ledger_index: 150,
|
||||
// validated: true,
|
||||
// },
|
||||
// type: 'response',
|
||||
// })
|
||||
// const response = await testContext.client.request({
|
||||
// command: 'ledger_data',
|
||||
// })
|
||||
// const responseNextPage = await testContext.client.requestNextPage(
|
||||
// { command: 'ledger_data' },
|
||||
// response,
|
||||
// )
|
||||
// assert.equal(
|
||||
// responseNextPage.result.state[0].index,
|
||||
// '000B714B790C3C79FEE00D17C4DEB436B375466F29679447BA64F265FD63D731',
|
||||
// )
|
||||
// })
|
||||
})
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
ResponseFormatError,
|
||||
XrplError,
|
||||
TimeoutError,
|
||||
SubscribeRequest,
|
||||
} from '../src'
|
||||
import { Connection } from '../src/client/connection'
|
||||
|
||||
@@ -347,6 +348,7 @@ describe('Connection', function () {
|
||||
'DisconnectedError',
|
||||
async () => {
|
||||
await clientContext.client
|
||||
// @ts-expect-error -- Intentionally invalid command
|
||||
.request({ command: 'test_command', data: { closeServer: true } })
|
||||
.then(() => {
|
||||
assert.fail('Should throw DisconnectedError')
|
||||
@@ -438,7 +440,7 @@ describe('Connection', function () {
|
||||
try {
|
||||
await clientContext.client.connect()
|
||||
} catch (error) {
|
||||
// @ts-expect-error -- error.message is expected to be defined
|
||||
// @ts-expect-error -- Error has a message
|
||||
expect(error.message).toEqual(
|
||||
"Error: connect() timed out after 5000 ms. If your internet connection is working, the rippled server may be blocked or inaccessible. You can also try setting the 'connectionTimeout' option in the Client constructor.",
|
||||
)
|
||||
@@ -457,6 +459,7 @@ describe('Connection', function () {
|
||||
async () => {
|
||||
await clientContext.client
|
||||
.request({
|
||||
// @ts-expect-error -- Intentionally invalid command
|
||||
command: 'test_command',
|
||||
data: { unrecognizedResponse: true },
|
||||
})
|
||||
@@ -847,7 +850,10 @@ describe('Connection', function () {
|
||||
it(
|
||||
'propagates RippledError data',
|
||||
async () => {
|
||||
const request = { command: 'subscribe', streams: 'validations' }
|
||||
const request: SubscribeRequest = {
|
||||
command: 'subscribe',
|
||||
streams: ['validations'],
|
||||
}
|
||||
clientContext.mockRippled?.addResponse(
|
||||
request.command,
|
||||
rippled.subscribe.error,
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
"hash": "F4AB442A6D4CBB935D66E1DA7309A5FC71C7143ED4049053EC14E3875B0CF9BF",
|
||||
"inLedger": 348860,
|
||||
"ledger_index": 348860,
|
||||
"validated": true,
|
||||
"meta": {
|
||||
"AffectedNodes": [
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('on handlers', function () {
|
||||
async () => {
|
||||
const client = new Client(serverUrl)
|
||||
return new Promise<void>(function (resolve) {
|
||||
client.on('disconnected', function (code: number) {
|
||||
client.on('disconnected', function (code) {
|
||||
// should be the normal disconnect code
|
||||
assert.equal(code, 1000)
|
||||
client.removeAllListeners()
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
AccountSet,
|
||||
Client,
|
||||
SignerListSet,
|
||||
SubmitMultisignedRequest,
|
||||
Transaction,
|
||||
SubmitMultisignedResponse,
|
||||
hashes,
|
||||
@@ -80,11 +79,10 @@ describe('submit_multisigned', function () {
|
||||
const signed1 = signerWallet1.sign(accountSetTx, true)
|
||||
const signed2 = signerWallet2.sign(accountSetTx, true)
|
||||
const multisigned = multisign([signed1.tx_blob, signed2.tx_blob])
|
||||
const multisignedRequest: SubmitMultisignedRequest = {
|
||||
const submitResponse = await client.request({
|
||||
command: 'submit_multisigned',
|
||||
tx_json: decode(multisigned) as unknown as Transaction,
|
||||
}
|
||||
const submitResponse = await client.request(multisignedRequest)
|
||||
})
|
||||
await ledgerAccept(client)
|
||||
assert.strictEqual(submitResponse.result.engine_result, 'tesSUCCESS')
|
||||
await verifySubmittedTransaction(testContext.client, multisigned)
|
||||
|
||||
@@ -2,6 +2,7 @@ import { assert } from 'chai'
|
||||
|
||||
import { RippledError } from '../src'
|
||||
|
||||
import rippledFixtures from './fixtures/rippled'
|
||||
import {
|
||||
setupClient,
|
||||
teardownClient,
|
||||
@@ -22,7 +23,11 @@ describe('mock rippled tests', function () {
|
||||
}
|
||||
|
||||
await assertRejects(
|
||||
testContext.client.request({ command: 'account_info' }),
|
||||
testContext.client.request({
|
||||
command: 'account_info',
|
||||
account:
|
||||
rippledFixtures.account_info.normal.result.account_data.Account,
|
||||
}),
|
||||
RippledError,
|
||||
)
|
||||
})
|
||||
@@ -44,7 +49,11 @@ describe('mock rippled tests', function () {
|
||||
return { data: request }
|
||||
})
|
||||
await assertRejects(
|
||||
testContext.client.request({ command: 'account_info', account: '' }),
|
||||
testContext.client.request({
|
||||
command: 'account_info',
|
||||
account:
|
||||
rippledFixtures.account_info.normal.result.account_data.Account,
|
||||
}),
|
||||
RippledError,
|
||||
)
|
||||
})
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
"Signing",
|
||||
"Transaction Models",
|
||||
"Transaction Flags",
|
||||
"Ledger Flags",
|
||||
"Utilities",
|
||||
"Requests",
|
||||
"Responses",
|
||||
|
||||
Reference in New Issue
Block a user