xahau-patch

This commit is contained in:
Denis Angell
2025-03-12 13:34:24 +01:00
parent 92eb809397
commit 9544e1794e
958 changed files with 1686 additions and 169992 deletions

View File

@@ -0,0 +1,6 @@
dist
node_modules
.github
.vscode
karma.config.js
karma-setup.js

151
packages/xahau/.eslintrc.js Normal file
View File

@@ -0,0 +1,151 @@
module.exports = {
root: true,
// Make ESLint compatible with TypeScript
parser: '@typescript-eslint/parser',
parserOptions: {
// Enable linting rules with type information from our tsconfig
tsconfigRootDir: __dirname,
project: [
'./tsconfig.eslint.json',
'../xahau-binary-codec/tsconfig.eslint.json',
'../xahau-address-codec/tsconfig.eslint.json',
'../xahau-keypairs/tsconfig.eslint.json',
],
// Allow the use of imports / ES modules
sourceType: 'module',
ecmaFeatures: {
// Enable global strict mode
impliedStrict: true,
},
},
// Specify global variables that are predefined
env: {
// Enable node global variables & Node.js scoping
node: true,
// Add all ECMAScript 2020 globals and automatically set the ecmaVersion parser option to ES2020
es2020: true,
jest: true,
},
plugins: [],
extends: ['@xrplf/eslint-config/base'],
rules: {
'multiline-comment-style': 'off',
// Disabled until https://github.com/import-js/eslint-plugin-import/pull/2305 is resolved to
// accomodate this change https://github.com/XRPLF/xrpl.js/pull/2133
'import/no-unused-modules': 'off',
'eslint-comments/no-unused-disable': 'off',
// Certain xahaud APIs require snake_case naming
'@typescript-eslint/naming-convention': [
'error',
{
selector: 'interface',
format: ['PascalCase', 'snake_case'],
},
],
'max-lines-per-function': [
'warn',
{ max: 40, skipBlankLines: true, skipComments: true },
],
'max-statements': ['warn', 25],
// exception for lodash
'id-length': ['error', { exceptions: ['_'] }],
// no-shadow has false-positives for enum, @typescript-eslint version fixes that
'no-shadow': 'off',
'@typescript-eslint/no-shadow': ['error'],
'jsdoc/check-examples': 'off',
// We want to use certain fields like "@interface" to make join types treated as interfaces.
'jsdoc/check-tag-names': 'off',
'jsdoc/require-hyphen-before-param-description': 'off',
'tsdoc/syntax': 'off',
'jsdoc/require-description-complete-sentence': 'off',
'import/prefer-default-export': 'off',
},
overrides: [
{
files: ['.eslintrc.js'],
rules: {
'import/no-unused-modules': 'off',
'@typescript-eslint/no-magic-numbers': 'off',
},
},
{
files: ['test/**/*.ts'],
rules: {
/*
* Because this project is managed by lerna, dev dependencies are
* hoisted and do not appear in the package.json.
*/
'import/no-extraneous-dependencies': 'off',
'node/no-extraneous-import': 'off',
// We have lots of magic numbers in tests
'@typescript-eslint/no-magic-numbers': 'off',
// We have files with a lot of tests
'max-lines-per-function': 'off',
'max-lines': 'off',
// We need to test things without type guards sometimes
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/consistent-type-assertions': 'off',
// We need to mess with internal things to generate certain testing situations
'@typescript-eslint/no-unsafe-member-access': 'off',
// Tests are already in 2 callbacks, so max 3 is pretty restrictive
'max-nested-callbacks': 'off',
// messes with fixtures
'consistent-default-export-name/default-import-match-filename': 'off',
},
},
{
files: ['test/client/*.ts'],
},
{
files: ['test/models/*.ts'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
},
},
{
files: ['src/models/**/*.ts'],
rules: {
complexity: ['off'],
},
},
{
files: ['.eslintrc.js', 'jest.config.js'],
rules: {
// Removed no-commonjs requirement as eslint must be in common js format
'import/no-commonjs': 'off',
// Removed this as eslint prevents us from doing this differently
'import/unambiguous': 'off',
// Javascript files have CommonJS exports
'import/no-unused-modules': 'off',
},
},
{
files: ['tools/*.ts', 'tools/*.js'],
rules: {
'no-console': ['off'],
'node/no-process-exit': ['off'],
'@typescript-eslint/no-magic-numbers': ['off'],
'max-lines-per-function': ['off'],
'max-statements': ['off'],
complexity: ['off'],
'max-depth': ['warn', 3],
},
},
],
}

3
packages/xahau/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
# Replaced by root README during the prepare lifecycle phase because npm does not allow symlinking README files
# https://docs.npmjs.com/cli/v7/using-npm/scripts#npm-publish
README.md

1947
packages/xahau/HISTORY.md Normal file

File diff suppressed because it is too large Load Diff

15
packages/xahau/LICENSE Normal file
View File

@@ -0,0 +1,15 @@
ISC License
Copyright (c) 2012-2021 Contributers to xrpl.js
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,13 @@
// Jest configuration for api
const base = require('../../jest.config.base.js')
module.exports = {
...base,
roots: [...base.roots, '<rootDir>/test'],
testTimeout: 20000,
testMatch: [
'<rootDir>/test/integration/**/*.test.ts',
'<rootDir>/test/integration/*.test.ts',
],
displayName: 'xahau.js',
}

View File

@@ -0,0 +1,13 @@
// Jest configuration for api
const base = require('../../jest.config.base.js')
module.exports = {
...base,
roots: [...base.roots, '<rootDir>/test'],
testMatch: ['<rootDir>/test/**/*.test.ts'],
testPathIgnorePatterns: [
'<rootDir>/test/integration',
'<rootDir>/test/fixtures',
],
displayName: 'xahau.js',
}

View File

@@ -0,0 +1,14 @@
const baseKarmaConfig = require('../../karma.config')
const webpackConfig = require('./test/webpack.config')
delete webpackConfig.entry
module.exports = function (config) {
config.set({
webpack: webpackConfig,
// list of files / patterns to load in the browser
files: ['build/xrpl-latest.js', 'test/integration/**/*.test.ts'],
})
baseKarmaConfig(config)
}

View File

@@ -0,0 +1,87 @@
{
"name": "xahau",
"version": "4.0.0",
"license": "ISC",
"description": "A TypeScript/JavaScript API for interacting with the XAH Ledger in Node.js and the browser",
"files": [
"build/xrpl-latest-min.js",
"build/xrpl-latest-min.js.map",
"build/xrpl-latest.js",
"build/xrpl-latest.js.map",
"dist/npm/*",
"src/*"
],
"main": "dist/npm/",
"unpkg": "build/xrpl-latest-min.js",
"jsdelivr": "build/xrpl-latest-min.js",
"types": "dist/npm/index.d.ts",
"directories": {
"test": "test"
},
"browser": {
"ws": "./dist/npm/client/WSWrapper.js"
},
"dependencies": {
"@scure/bip32": "^1.3.1",
"@scure/bip39": "^1.2.1",
"@xrplf/isomorphic": "^1.0.1",
"@xrplf/secret-numbers": "^1.0.0",
"bignumber.js": "^9.0.0",
"eventemitter3": "^5.0.1",
"xahau-address-codec": "^5.0.0",
"xahau-binary-codec": "^2.1.0",
"xahau-keypairs": "^2.0.0"
},
"devDependencies": {
"@types/node": "^18.18.38",
"eventemitter3": "^5.0.1",
"https-proxy-agent": "^7.0.1",
"karma": "^6.4.1",
"karma-chrome-launcher": "^3.1.1",
"karma-jasmine": "^5.1.0",
"karma-webpack": "^5.0.0",
"lodash": "^4.17.4",
"react": "^18.2.0",
"run-s": "^0.0.0",
"typedoc": "0.26.10",
"ws": "^8.14.2"
},
"resolutions": {
"elliptic": "^6.5.4"
},
"scripts": {
"build": "run-s build:lib build:web",
"build:lib": "tsc --build tsconfig.build.json",
"build:web": "webpack",
"build:browserTests": "webpack --config ./test/webpack.config.js",
"analyze": "webpack --analyze",
"watch": "run-s build:lib --watch",
"clean": "rm -rf ./dist ./coverage ./test/testCompiledForWeb tsconfig.build.tsbuildinfo",
"docgen": "tsc --build tsconfig.docs.json && typedoc && echo js.xrpl.org >> ../../docs/CNAME",
"prepare": "copyfiles ../../README.md xrpl/README.md",
"prepublish": "run-s clean build",
"test": "jest --config=jest.config.unit.js --verbose false --silent=false",
"test:integration": "TS_NODE_PROJECT=tsconfig.build.json jest --config=jest.config.integration.js --verbose false --silent=false --runInBand",
"test:browser": "npm run build && npm run build:browserTests && karma start ./karma.config.js",
"test:watch": "jest --watch --verbose false --silent=false --runInBand ./test/**/*.test.ts --testPathIgnorePatterns=./test/integration --testPathIgnorePatterns=./test/fixtures",
"format": "prettier --write '{src,test}/**/*.ts'",
"lint": "eslint . --ext .ts --max-warnings 0",
"perf": "./scripts/perf_test.sh"
},
"prettier": "@xrplf/prettier-config",
"repository": {
"type": "git",
"url": "git@github.com:XRPLF/xrpl.js.git"
},
"readmeFilename": "README.md",
"keywords": [
"xahau-lib",
"xahau",
"xah",
"xahau ledger",
"xahau"
],
"engines": {
"node": ">=18.0.0"
}
}

View File

@@ -0,0 +1,6 @@
enum ECDSA {
ed25519 = 'ed25519',
secp256k1 = 'ecdsa-secp256k1',
}
export default ECDSA

View File

@@ -0,0 +1,26 @@
import { encodeForSigningClaim } from 'xahau-binary-codec'
import { sign } from 'xahau-keypairs'
import { Wallet } from './index'
/**
* Creates a signature that can be used to redeem a specific amount of XAH from a payment channel.
*
* @param wallet - The account that will sign for this payment channel.
* @param channelId - An id for the payment channel to redeem XAH from.
* @param amount - The amount in drops to redeem.
* @returns A signature that can be used to redeem a specific amount of XAH from a payment channel.
* @category Utilities
*/
export function authorizeChannel(
wallet: Wallet,
channelId: string,
amount: string,
): string {
const signingData = encodeForSigningClaim({
channel: channelId,
amount,
})
return sign(signingData, wallet.privateKey)
}

View File

@@ -0,0 +1,58 @@
import type { Client } from '../client'
import { XRPLFaucetError } from '../errors'
export interface FaucetWallet {
account: {
xAddress: string
classicAddress?: string
secret: string
}
amount: number
balance: number
}
export enum FaucetNetwork {
Testnet = 'xahau-test.net',
Devnet = 'jshooks.xahau-test.net',
}
export const FaucetNetworkPaths: Record<string, string> = {
[FaucetNetwork.Testnet]: '/accounts',
[FaucetNetwork.Devnet]: '/accounts',
}
/**
* Get the faucet host based on the Client connection.
*
* @param client - Client.
* @returns A {@link FaucetNetwork}.
* @throws When the client url is not on devnet.
*/
export function getFaucetHost(client: Client): FaucetNetwork | undefined {
const connectionUrl = client.url
if (connectionUrl.includes('jshooks')) {
return FaucetNetwork.Devnet
}
// 'altnet' for Ripple Testnet server and 'test' for XAHL Labs Testnet server
if (connectionUrl.includes('test')) {
return FaucetNetwork.Testnet
}
throw new XRPLFaucetError('Faucet URL is not defined or inferrable.')
}
/**
* Get the faucet pathname based on the faucet hostname.
*
* @param hostname - hostname.
* @returns A String with the correct path for the input hostname.
* If hostname undefined or cannot find (key, value) pair in {@link FaucetNetworkPaths}, defaults to '/accounts'
*/
export function getDefaultFaucetPath(hostname: string | undefined): string {
if (hostname === undefined) {
return '/accounts'
}
return FaucetNetworkPaths[hostname] || '/accounts'
}

View File

@@ -0,0 +1,282 @@
import { isValidClassicAddress } from 'xahau-address-codec'
import type { Client } from '../client'
import { XRPLFaucetError } from '../errors'
import {
FaucetWallet,
getFaucetHost,
getDefaultFaucetPath,
} from './defaultFaucets'
import { Wallet } from '.'
// Interval to check an account balance
const INTERVAL_SECONDS = 1
// Maximum attempts to retrieve a balance
const MAX_ATTEMPTS = 20
export interface FundingOptions {
/**
* A custom amount to fund, if undefined or null, the default amount will be 1000.
*/
amount?: string
/**
* 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.
*/
faucetHost?: string
/**
* 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,{'xahau-test.net/accounts', '/accounts'})
* specifies a request to 'xahau-test.net/accounts/accounts' to fund a new wallet.
*/
faucetPath?: string
/**
* An optional field to indicate the use case context of the faucet transaction
* Ex: integration test, code snippets.
*/
usageContext?: string
}
/**
* Parameters to pass into a faucet request to fund an XAH 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 XAH 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
}
/**
* Generate a new wallet to fund if no existing wallet is provided or its address is invalid.
*
* @param wallet - Optional existing wallet.
* @returns The wallet to fund.
*/
export function generateWalletToFund(wallet?: Wallet | null): Wallet {
if (wallet && isValidClassicAddress(wallet.classicAddress)) {
return wallet
}
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,{'xahau-test.net/accounts', '/accounts'})
* specifies a request to 'xahau-test.net/accounts/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 XAHL to send requests and transactions.
* @param startingBalance - The amount of XAH in the given walletToFund on ledger already.
* @param walletToFund - An existing XAHL 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
export async function requestFunding(
options: FundingOptions,
client: Client,
startingBalance: number,
walletToFund: Wallet,
postBody: FaucetRequestBody,
): Promise<{
wallet: Wallet
balance: number
}> {
const hostname = options.faucetHost ?? getFaucetHost(client)
if (!hostname) {
throw new XRPLFaucetError('No faucet hostname could be derived')
}
const pathname = options.faucetPath ?? getDefaultFaucetPath(hostname)
const response = await fetch(`https://${hostname}${pathname}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(postBody),
})
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- it can be anything
const body = await response.json()
if (
response.ok &&
response.headers.get('Content-Type')?.startsWith('application/json')
) {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- It's a FaucetWallet
const classicAddress = (body as FaucetWallet).account.classicAddress
return processSuccessfulResponse(
client,
classicAddress,
walletToFund,
startingBalance,
)
}
return processError(response, body)
}
// eslint-disable-next-line max-params -- Only used as a helper function, lines inc due to added balance.
async function processSuccessfulResponse(
client: Client,
classicAddress: string | undefined,
walletToFund: Wallet,
startingBalance: number,
): Promise<{
wallet: Wallet
balance: number
}> {
if (!classicAddress) {
return Promise.reject(
new XRPLFaucetError(`The faucet account is undefined`),
)
}
try {
// Check at regular interval if the address is enabled on the XAHL and funded
const updatedBalance = await getUpdatedBalance(
client,
classicAddress,
startingBalance,
)
if (updatedBalance > startingBalance) {
return {
wallet: walletToFund,
balance: updatedBalance,
}
}
throw new XRPLFaucetError(
`Unable to fund address with faucet after waiting ${
INTERVAL_SECONDS * MAX_ATTEMPTS
} seconds`,
)
} catch (err) {
if (err instanceof Error) {
throw new XRPLFaucetError(err.message)
}
throw err
}
}
async function processError(response: Response, body): Promise<never> {
return Promise.reject(
new XRPLFaucetError(
`Request failed: ${JSON.stringify({
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- json response could be anything
body: body || {},
contentType: response.headers.get('Content-Type'),
statusCode: response.status,
})}`,
),
)
}
/**
* Check at regular interval if the address is enabled on the XAHL and funded.
*
* @param client - Client.
* @param address - The account address to check.
* @param originalBalance - The initial balance before the funding.
* @returns A Promise boolean.
*/
async function getUpdatedBalance(
client: Client,
address: string,
originalBalance: number,
): Promise<number> {
return new Promise((resolve, reject) => {
let attempts = MAX_ATTEMPTS
// eslint-disable-next-line @typescript-eslint/no-misused-promises -- Not actually misused here, different resolve
const interval = setInterval(async () => {
if (attempts < 0) {
clearInterval(interval)
resolve(originalBalance)
} else {
attempts -= 1
}
try {
let newBalance
try {
newBalance = Number(await client.getXrpBalance(address))
} catch {
/* newBalance remains undefined */
}
if (newBalance > originalBalance) {
clearInterval(interval)
resolve(newBalance)
}
} catch (err) {
clearInterval(interval)
if (err instanceof Error) {
reject(
new XRPLFaucetError(
`Unable to check if the address ${address} balance has increased. Error: ${err.message}`,
),
)
}
reject(err)
}
}, INTERVAL_SECONDS * 1000)
})
}

View File

@@ -0,0 +1,507 @@
import { HDKey } from '@scure/bip32'
import { mnemonicToSeedSync, validateMnemonic } from '@scure/bip39'
import { wordlist } from '@scure/bip39/wordlists/english'
import { bytesToHex } from '@xrplf/isomorphic/utils'
import BigNumber from 'bignumber.js'
import {
classicAddressToXAddress,
isValidXAddress,
xAddressToClassicAddress,
encodeSeed,
} from 'xahau-address-codec'
import {
encodeForSigning,
encodeForMultisigning,
encode,
} from 'xahau-binary-codec'
import {
deriveAddress,
deriveKeypair,
generateSeed,
sign,
} from 'xahau-keypairs'
import ECDSA from '../ECDSA'
import { ValidationError } from '../errors'
import { Transaction, validate } from '../models/transactions'
import { ensureClassicAddress } from '../sugar/utils'
import { omitBy } from '../utils/collections'
import { hashSignedTx } from '../utils/hashes/hashLedger'
import { rfc1751MnemonicToKey } from './rfc1751'
import { verifySignature } from './signer'
const DEFAULT_ALGORITHM: ECDSA = ECDSA.ed25519
const DEFAULT_DERIVATION_PATH = "m/44'/144'/0'/0/0"
type ValidHDKey = HDKey & {
privateKey: Uint8Array
publicKey: Uint8Array
}
function validateKey(node: HDKey): asserts node is ValidHDKey {
if (!(node.privateKey instanceof Uint8Array)) {
throw new ValidationError('Unable to derive privateKey from mnemonic input')
}
if (!(node.publicKey instanceof Uint8Array)) {
throw new ValidationError('Unable to derive publicKey from mnemonic input')
}
}
/**
* A utility for deriving a wallet composed of a keypair (publicKey/privateKey).
* A wallet can be derived from either a seed, mnemonic, or entropy (array of random numbers).
* It provides functionality to sign/verify transactions offline.
*
* @example
* ```typescript
*
* // Derive a wallet from a base58 encoded seed.
* const seedWallet = Wallet.fromSeed('ssZkdwURFMBXenJPbrpE14b6noJSu')
* console.log(seedWallet)
* // Wallet {
* // publicKey: '02FE9932A9C4AA2AC9F0ED0F2B89302DE7C2C95F91D782DA3CF06E64E1C1216449',
* // privateKey: '00445D0A16DD05EFAF6D5AF45E6B8A6DE4170D93C0627021A0B8E705786CBCCFF7',
* // classicAddress: 'rG88FVLjvYiQaGftSa1cKuE2qNx7aK5ivo',
* // seed: 'ssZkdwURFMBXenJPbrpE14b6noJSu'
* // }.
*
* // Sign a JSON Transaction
* const signed = seedWallet.signTransaction({
* TransactionType: 'Payment',
* Account: 'rG88FVLjvYiQaGftSa1cKuE2qNx7aK5ivo'
* ...........
* }).
*
* console.log(signed)
* // '1200007321......B01BE1DFF3'.
* console.log(decode(signed))
* // {
* // TransactionType: 'Payment',
* // SigningPubKey: '02FE9932A9C4AA2AC9F0ED0F2B89302DE7C2C95F91D782DA3CF06E64E1C1216449',
* // TxnSignature: '3045022100AAD......5B631ABD21171B61B07D304',
* // Account: 'rG88FVLjvYiQaGftSa1cKuE2qNx7aK5ivo'
* // ...........
* // }
* ```
*
* @category Signing
*/
export class Wallet {
public readonly publicKey: string
public readonly privateKey: string
public readonly classicAddress: string
public readonly seed?: string
/**
* Creates a new Wallet.
*
* @param publicKey - The public key for the account.
* @param privateKey - The private key used for signing transactions for the account.
* @param opts - (Optional) Options to initialize a Wallet.
* @param opts.masterAddress - Include if a Wallet uses a Regular Key Pair. It must be the master address of the account.
* @param opts.seed - The seed used to derive the account keys.
*/
public constructor(
publicKey: string,
privateKey: string,
opts: {
masterAddress?: string
seed?: string
} = {},
) {
this.publicKey = publicKey
this.privateKey = privateKey
this.classicAddress = opts.masterAddress
? ensureClassicAddress(opts.masterAddress)
: deriveAddress(publicKey)
this.seed = opts.seed
}
/**
* Alias for wallet.classicAddress.
*
* @returns The wallet's classic address.
*/
public get address(): string {
return this.classicAddress
}
/**
* `generate()` creates a new random Wallet. In order to make this a valid account on ledger, you must
* Send XAH to it. On test networks that can be done with "faucets" which send XAH to any account which asks
* For it. You can call `client.fundWallet()` in order to generate credentials and fund the account on test networks.
*
* @example
* ```ts
* const { Wallet } = require('xah')
* const wallet = Wallet.generate()
* ```
*
* @param algorithm - The digital signature algorithm to generate an address for.
* @returns A new Wallet derived from a generated seed.
*
* @throws ValidationError when signing algorithm isn't valid
*/
public static generate(algorithm: ECDSA = DEFAULT_ALGORITHM): Wallet {
if (!Object.values(ECDSA).includes(algorithm)) {
throw new ValidationError('Invalid cryptographic signing algorithm')
}
const seed = generateSeed({ algorithm })
return Wallet.fromSeed(seed, { algorithm })
}
/**
* Derives a wallet from a seed.
*
* @param seed - A string used to generate a keypair (publicKey/privateKey) to derive a wallet.
* @param opts - (Optional) Options to derive a Wallet.
* @param opts.algorithm - The digital signature algorithm to generate an address for.
* @param opts.masterAddress - Include if a Wallet uses a Regular Key Pair. It must be the master address of the account.
* @returns A Wallet derived from a seed.
*/
public static fromSeed(
seed: string,
opts: { masterAddress?: string; algorithm?: ECDSA } = {},
): Wallet {
return Wallet.deriveWallet(seed, {
algorithm: opts.algorithm,
masterAddress: opts.masterAddress,
})
}
/**
* Derives a wallet from a secret (AKA a seed).
*
* @param secret - A string used to generate a keypair (publicKey/privateKey) to derive a wallet.
* @param opts - (Optional) Options to derive a Wallet.
* @param opts.algorithm - The digital signature algorithm to generate an address for.
* @param opts.masterAddress - Include if a Wallet uses a Regular Key Pair. It must be the master address of the account.
* @returns A Wallet derived from a secret (AKA a seed).
*/
// eslint-disable-next-line @typescript-eslint/member-ordering -- Member is used as a function here
public static fromSecret = Wallet.fromSeed
/**
* Derives a wallet from an entropy (array of random numbers).
*
* @param entropy - An array of random numbers to generate a seed used to derive a wallet.
* @param opts - (Optional) Options to derive a Wallet.
* @param opts.algorithm - The digital signature algorithm to generate an address for.
* @param opts.masterAddress - Include if a Wallet uses a Regular Key Pair. It must be the master address of the account.
* @returns A Wallet derived from an entropy.
*/
public static fromEntropy(
entropy: Uint8Array | number[],
opts: { masterAddress?: string; algorithm?: ECDSA } = {},
): Wallet {
const algorithm = opts.algorithm ?? DEFAULT_ALGORITHM
const options = {
entropy: Uint8Array.from(entropy),
algorithm,
}
const seed = generateSeed(options)
return Wallet.deriveWallet(seed, {
algorithm,
masterAddress: opts.masterAddress,
})
}
/**
* Derives a wallet from a bip39 or RFC1751 mnemonic (Defaults to bip39).
*
* @deprecated since version 2.6.1.
* Will be deleted in version 3.0.0.
* This representation is currently deprecated in xahaud.
* You should use another method to represent your keys such as a seed or public/private keypair.
*
* @param mnemonic - A string consisting of words (whitespace delimited) used to derive a wallet.
* @param opts - (Optional) Options to derive a Wallet.
* @param opts.masterAddress - Include if a Wallet uses a Regular Key Pair. It must be the master address of the account.
* @param opts.derivationPath - The path to derive a keypair (publicKey/privateKey). Only used for bip39 conversions.
* @param opts.mnemonicEncoding - If set to 'rfc1751', this interprets the mnemonic as a xahaud RFC1751 mnemonic like
* `wallet_propose` generates in xahaud. Otherwise the function defaults to bip39 decoding.
* @param opts.algorithm - Only used if opts.mnemonicEncoding is 'rfc1751'. Allows the mnemonic to generate its
* secp256k1 seed, or its ed25519 seed. By default, it will generate the secp256k1 seed
* to match the xahaud `wallet_propose` default algorithm.
* @returns A Wallet derived from a mnemonic.
* @throws ValidationError if unable to derive private key from mnemonic input.
*/
public static fromMnemonic(
mnemonic: string,
opts: {
masterAddress?: string
derivationPath?: string
mnemonicEncoding?: 'bip39' | 'rfc1751'
algorithm?: ECDSA
} = {},
): Wallet {
if (opts.mnemonicEncoding === 'rfc1751') {
return Wallet.fromRFC1751Mnemonic(mnemonic, {
masterAddress: opts.masterAddress,
algorithm: opts.algorithm,
})
}
// Otherwise decode using bip39's mnemonic standard
if (!validateMnemonic(mnemonic, wordlist)) {
throw new ValidationError(
'Unable to parse the given mnemonic using bip39 encoding',
)
}
const seed = mnemonicToSeedSync(mnemonic)
const masterNode = HDKey.fromMasterSeed(seed)
const node = masterNode.derive(
opts.derivationPath ?? DEFAULT_DERIVATION_PATH,
)
validateKey(node)
const publicKey = bytesToHex(node.publicKey)
const privateKey = bytesToHex(node.privateKey)
return new Wallet(publicKey, `00${privateKey}`, {
masterAddress: opts.masterAddress,
})
}
/**
* Derives a wallet from a RFC1751 mnemonic, which is how `wallet_propose` encodes mnemonics.
*
* @param mnemonic - A string consisting of words (whitespace delimited) used to derive a wallet.
* @param opts - (Optional) Options to derive a Wallet.
* @param opts.masterAddress - Include if a Wallet uses a Regular Key Pair. It must be the master address of the account.
* @param opts.algorithm - The digital signature algorithm to generate an address for.
* @returns A Wallet derived from a mnemonic.
*/
private static fromRFC1751Mnemonic(
mnemonic: string,
opts: { masterAddress?: string; algorithm?: ECDSA },
): Wallet {
const seed = rfc1751MnemonicToKey(mnemonic)
let encodeAlgorithm: 'ed25519' | 'secp256k1'
if (opts.algorithm === ECDSA.ed25519) {
encodeAlgorithm = 'ed25519'
} else {
// Defaults to secp256k1 since that's the default for `wallet_propose`
encodeAlgorithm = 'secp256k1'
}
const encodedSeed = encodeSeed(seed, encodeAlgorithm)
return Wallet.fromSeed(encodedSeed, {
masterAddress: opts.masterAddress,
algorithm: opts.algorithm,
})
}
/**
* Derive a Wallet from a seed.
*
* @param seed - The seed used to derive the wallet.
* @param opts - (Optional) Options to derive a Wallet.
* @param opts.algorithm - The digital signature algorithm to generate an address for.
* @param opts.masterAddress - Include if a Wallet uses a Regular Key Pair. It must be the master address of the account.
* @returns A Wallet derived from the seed.
*/
private static deriveWallet(
seed: string,
opts: { masterAddress?: string; algorithm?: ECDSA } = {},
): Wallet {
const { publicKey, privateKey } = deriveKeypair(seed, {
algorithm: opts.algorithm ?? DEFAULT_ALGORITHM,
})
return new Wallet(publicKey, privateKey, {
seed,
masterAddress: opts.masterAddress,
})
}
/**
* Signs a transaction offline.
*
* @example
*
* ```ts
* const { Client, Wallet } = require('xah')
* const client = new Client('wss://xahau-test.net')
*
* async function signTransaction() {
* await client.connect()
* const { balance: balance1, wallet: wallet1 } = client.fundWallet()
* const { balance: balance2, wallet: wallet2 } = client.fundWallet()
*
* const transaction = {
* TransactionType: 'Payment',
* Account: wallet1.address,
* Destination: wallet2.address,
* Amount: '10'
* }
*
* try {
* await client.autofill(transaction)
* const { tx_blob: signed_tx_blob, hash} = await wallet1.sign(transaction)
* console.log(signed_tx_blob)
* } catch (error) {
* console.error(`Failed to sign transaction: ${error}`)
* }
* const result = await client.submit(signed_tx_blob)
* await client.disconnect()
* }
*
* signTransaction()
* ```
* In order for a transaction to be validated, it must be signed by the account sending the transaction to prove
* That the owner is actually the one deciding to take that action.
*
* In this example, we created, signed, and then submitted a transaction to testnet. You may notice that the
* Output of `sign` includes a `tx_blob` and a `hash`, both of which are needed to submit & verify the results.
* Note: If you pass a `Wallet` to `client.submit` or `client.submitAndWait` it will do signing like this under the hood.
*
* `tx_blob` is a binary representation of a transaction on the XAH Ledger. It's essentially a byte array
* that encodes all of the data necessary to execute the transaction, including the source address, the destination
* address, the amount, and any additional fields required for the specific transaction type.
*
* `hash` is a unique identifier that's generated from the signed transaction data on the XAH Ledger. It's essentially
* A cryptographic digest of the signed transaction blob, created using a hash function. The signed transaction hash is
* Useful for identifying and tracking specific transactions on the XAH Ledger. It can be used to query transaction
* Information, verify the authenticity of a transaction, and detect any tampering with the transaction data.
*
* @param this - Wallet instance.
* @param transaction - A transaction to be signed offline.
* @param multisign - Specify true/false to use multisign or actual address (classic/x-address) to make multisign tx request.
* @returns A signed transaction.
* @throws ValidationError if the transaction is already signed or does not encode/decode to same result.
* @throws XahlError if the issued currency being signed is XAH ignoring case.
*/
// eslint-disable-next-line max-lines-per-function -- introduced more checks to support both string and boolean inputs.
public sign(
this: Wallet,
transaction: Transaction,
multisign?: boolean | string,
): {
tx_blob: string
hash: string
} {
let multisignAddress: boolean | string = false
if (typeof multisign === 'string' && multisign.startsWith('X')) {
multisignAddress = multisign
} else if (multisign) {
multisignAddress = this.classicAddress
}
// clean null & undefined valued tx properties
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- ensure Transaction flows through
const tx = omitBy(
{ ...transaction },
(value) => value == null,
) as unknown as Transaction
if (tx.TxnSignature || tx.Signers) {
throw new ValidationError(
'txJSON must not contain "TxnSignature" or "Signers" properties',
)
}
removeTrailingZeros(tx)
/*
* This will throw a more clear error for JS users if the supplied transaction has incorrect formatting
*/
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- validate does not accept Transaction type
validate(tx as unknown as Record<string, unknown>)
const txToSignAndEncode = { ...tx }
txToSignAndEncode.SigningPubKey = multisignAddress ? '' : this.publicKey
if (multisignAddress) {
const signer = {
Account: multisignAddress,
SigningPubKey: this.publicKey,
TxnSignature: computeSignature(
txToSignAndEncode,
this.privateKey,
multisignAddress,
),
}
txToSignAndEncode.Signers = [{ Signer: signer }]
} else {
txToSignAndEncode.TxnSignature = computeSignature(
txToSignAndEncode,
this.privateKey,
)
}
const serialized = encode(txToSignAndEncode)
return {
tx_blob: serialized,
hash: hashSignedTx(serialized),
}
}
/**
* Verifies a signed transaction offline.
*
* @param signedTransaction - A signed transaction (hex string of signTransaction result) to be verified offline.
* @returns Returns true if a signedTransaction is valid.
* @throws {Error} Transaction is missing a signature, TxnSignature
*/
public verifyTransaction(signedTransaction: Transaction | string): boolean {
return verifySignature(signedTransaction, this.publicKey)
}
/**
* Gets an X-address in Testnet/Mainnet format.
*
* @param tag - A tag to be included within the X-address.
* @param isTestnet - A boolean to indicate if X-address should be in Testnet (true) or Mainnet (false) format.
* @returns An X-address.
*/
public getXAddress(tag: number | false = false, isTestnet = false): string {
return classicAddressToXAddress(this.classicAddress, tag, isTestnet)
}
}
/**
* Signs a transaction with the proper signing encoding.
*
* @param tx - A transaction to sign.
* @param privateKey - A key to sign the transaction with.
* @param signAs - Multisign only. An account address to include in the Signer field.
* Can be either a classic address or an XAddress.
* @returns A signed transaction in the proper format.
*/
function computeSignature(
tx: Transaction,
privateKey: string,
signAs?: string,
): string {
if (signAs) {
const classicAddress = isValidXAddress(signAs)
? xAddressToClassicAddress(signAs).classicAddress
: signAs
return sign(encodeForMultisigning(tx, classicAddress), privateKey)
}
return sign(encodeForSigning(tx), privateKey)
}
/**
* Remove trailing insignificant zeros for non-XAH Payment amount.
* This resolves the serialization mismatch bug when encoding/decoding a non-XAH Payment transaction
* with an amount that contains trailing insignificant zeros; for example, '123.4000' would serialize
* to '123.4' and cause a mismatch.
*
* @param tx - The transaction prior to signing.
*/
function removeTrailingZeros(tx: Transaction): void {
if (
tx.TransactionType === 'Payment' &&
typeof tx.Amount !== 'string' &&
tx.Amount.value.includes('.') &&
tx.Amount.value.endsWith('0')
) {
// eslint-disable-next-line no-param-reassign -- Required to update Transaction.Amount.value
tx.Amount = { ...tx.Amount }
// eslint-disable-next-line no-param-reassign -- Required to update Transaction.Amount.value
tx.Amount.value = new BigNumber(tx.Amount.value).toString()
}
}

View File

@@ -0,0 +1,219 @@
/* eslint-disable @typescript-eslint/no-magic-numbers -- Doing many bitwise operations which need specific numbers */
/* eslint-disable no-bitwise -- Bitwise operators are required for this encoding/decoding */
/* eslint-disable id-length -- Bitwise math uses shorthand terms */
/*
*rfc1751.ts : Converts between 128-bit strings and a human-readable
*sequence of words, as defined in RFC1751: "A Convention for
*Human-Readable 128-bit Keys", by Daniel L. McDonald.
*Ported from rfc1751.py / Python Cryptography Toolkit (public domain).
*Copied from https://github.com/bip32/bip32.github.io/blob/master/js/rfc1751.js which
*is part of the public domain.
*/
import { hexToBytes, concat } from '@xrplf/isomorphic/utils'
import rfc1751Words from './rfc1751Words.json'
const rfc1751WordList: string[] = rfc1751Words
// Added prettier-ignore to allow _BINARY to be on two lines, instead of one entry per line.
// prettier-ignore
const BINARY = ['0000', '0001', '0010', '0011', '0100', '0101', '0110', '0111',
'1000', '1001', '1010', '1011', '1100', '1101', '1110', '1111'];
/**
* Convert a number array into a binary string.
*
* @param key - An array of numbers in base 10.
* @returns A binary string.
*/
function keyToBinary(key: number[]): string {
let res = ''
for (const num of key) {
res += BINARY[num >> 4] + BINARY[num & 0x0f]
}
return res
}
/**
* Converts a substring of an encoded secret to its numeric value.
*
* @param key - The encoded secret.
* @param start - The start index to parse from.
* @param length - The number of digits to parse after the start index.
* @returns The binary value of the substring.
*/
function extract(key: string, start: number, length: number): number {
const subKey = key.substring(start, start + length)
let acc = 0
for (let index = 0; index < subKey.length; index++) {
acc = acc * 2 + subKey.charCodeAt(index) - 48
}
return acc
}
/**
* Generates a modified RFC1751 mnemonic in the same way xahaud's wallet_propose does.
*
* @param hex_key - An encoded secret in hex format.
* @returns A mnemonic following xahaud's modified RFC1751 mnemonic standard.
*/
function keyToRFC1751Mnemonic(hex_key: string): string {
// Remove whitespace and interpret hex
const buf = hexToBytes(hex_key.replace(/\s+/gu, ''))
// Swap byte order and use rfc1751
let key: number[] = bufferToArray(swap128(buf))
// pad to 8 bytes
const padding: number[] = []
for (let index = 0; index < (8 - (key.length % 8)) % 8; index++) {
padding.push(0)
}
key = padding.concat(key)
const english: string[] = []
for (let index = 0; index < key.length; index += 8) {
const subKey = key.slice(index, index + 8)
// add parity
let skbin = keyToBinary(subKey)
let parity = 0
for (let j = 0; j < 64; j += 2) {
parity += extract(skbin, j, 2)
}
subKey.push((parity << 6) & 0xff)
skbin = keyToBinary(subKey)
for (let j = 0; j < 64; j += 11) {
english.push(rfc1751WordList[extract(skbin, j, 11)])
}
}
return english.join(' ')
}
/**
* Converts an english mnemonic following xahaud's modified RFC1751 standard to an encoded hex secret.
*
* @param english - A mnemonic generated using xahau's modified RFC1751 standard.
* @throws Error if the parity after decoding does not match.
* @returns A Buffer containing an encoded secret.
*/
function rfc1751MnemonicToKey(english: string): Uint8Array {
const words = english.split(' ')
let key: number[] = []
for (let index = 0; index < words.length; index += 6) {
const { subKey, word }: { subKey: number[]; word: string } = getSubKey(
words,
index,
)
// check parity
const skbin = keyToBinary(subKey)
let parity = 0
for (let j = 0; j < 64; j += 2) {
parity += extract(skbin, j, 2)
}
const cs0 = extract(skbin, 64, 2)
const cs1 = parity & 3
if (cs0 !== cs1) {
throw new Error(`Parity error at ${word}`)
}
key = key.concat(subKey.slice(0, 8))
}
// This is a step specific to the XAHL's implementation
const bufferKey = swap128(Uint8Array.from(key))
return bufferKey
}
function getSubKey(
words: string[],
index: number,
): { subKey: number[]; word: string } {
const sublist = words.slice(index, index + 6)
let bits = 0
const ch = [0, 0, 0, 0, 0, 0, 0, 0, 0]
let word = ''
for (word of sublist) {
const idx = rfc1751WordList.indexOf(word.toUpperCase())
if (idx === -1) {
throw new TypeError(
`Expected an RFC1751 word, but received '${word}'. ` +
`For the full list of words in the RFC1751 encoding see https://datatracker.ietf.org/doc/html/rfc1751`,
)
}
const shift = (8 - ((bits + 11) % 8)) % 8
const y = idx << shift
const cl = y >> 16
const cc = (y >> 8) & 0xff
const cr = y & 0xff
const t = Math.floor(bits / 8)
if (shift > 5) {
ch[t] |= cl
ch[t + 1] |= cc
ch[t + 2] |= cr
} else if (shift > -3) {
ch[t] |= cc
ch[t + 1] |= cr
} else {
ch[t] |= cr
}
bits += 11
}
const subKey: number[] = ch.slice()
return { subKey, word }
}
function bufferToArray(buf: Uint8Array): number[] {
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- We know the end type */
return Array.prototype.slice.call(buf) as number[]
}
function swap(arr: Uint8Array, n: number, m: number): void {
const i = arr[n]
// eslint-disable-next-line no-param-reassign -- we have to swap
arr[n] = arr[m]
// eslint-disable-next-line no-param-reassign -- see above
arr[m] = i
}
/**
* Interprets arr as an array of 64-bit numbers and swaps byte order in 64 bit chunks.
* Example of two 64 bit numbers 0000000100000002 => 1000000020000000
*
* @param arr A Uint8Array representation of one or more 64 bit numbers
* @returns Uint8Array An array containing the bytes of 64 bit numbers each with reversed endianness
*/
function swap64(arr: Uint8Array): Uint8Array {
const len = arr.length
for (let i = 0; i < len; i += 8) {
swap(arr, i, i + 7)
swap(arr, i + 1, i + 6)
swap(arr, i + 2, i + 5)
swap(arr, i + 3, i + 4)
}
return arr
}
/**
* Swap the byte order of a 128-bit array.
* Ex. 0000000100000002 => 2000000010000000
*
* @param arr - A 128-bit (16 byte) array
* @returns An array containing the same data with reversed endianness
*/
function swap128(arr: Uint8Array): Uint8Array {
// Interprets arr as an array of (two, in this case) 64-bit numbers and swaps byte order in 64 bit chunks.
// Ex. 0000000100000002 => 1000000020000000
const reversedBytes = swap64(arr)
// Further swap the two 64-bit numbers since our buffer is 128 bits.
// Ex. 1000000020000000 => 2000000010000000
return concat([reversedBytes.slice(8, 16), reversedBytes.slice(0, 8)])
}
export { rfc1751MnemonicToKey, keyToRFC1751Mnemonic }

View File

@@ -0,0 +1,243 @@
[ "A", "ABE", "ACE", "ACT", "AD", "ADA", "ADD",
"AGO", "AID", "AIM", "AIR", "ALL", "ALP", "AM", "AMY", "AN", "ANA",
"AND", "ANN", "ANT", "ANY", "APE", "APS", "APT", "ARC", "ARE", "ARK",
"ARM", "ART", "AS", "ASH", "ASK", "AT", "ATE", "AUG", "AUK", "AVE",
"AWE", "AWK", "AWL", "AWN", "AX", "AYE", "BAD", "BAG", "BAH", "BAM",
"BAN", "BAR", "BAT", "BAY", "BE", "BED", "BEE", "BEG", "BEN", "BET",
"BEY", "BIB", "BID", "BIG", "BIN", "BIT", "BOB", "BOG", "BON", "BOO",
"BOP", "BOW", "BOY", "BUB", "BUD", "BUG", "BUM", "BUN", "BUS", "BUT",
"BUY", "BY", "BYE", "CAB", "CAL", "CAM", "CAN", "CAP", "CAR", "CAT",
"CAW", "COD", "COG", "COL", "CON", "COO", "COP", "COT", "COW", "COY",
"CRY", "CUB", "CUE", "CUP", "CUR", "CUT", "DAB", "DAD", "DAM", "DAN",
"DAR", "DAY", "DEE", "DEL", "DEN", "DES", "DEW", "DID", "DIE", "DIG",
"DIN", "DIP", "DO", "DOE", "DOG", "DON", "DOT", "DOW", "DRY", "DUB",
"DUD", "DUE", "DUG", "DUN", "EAR", "EAT", "ED", "EEL", "EGG", "EGO",
"ELI", "ELK", "ELM", "ELY", "EM", "END", "EST", "ETC", "EVA", "EVE",
"EWE", "EYE", "FAD", "FAN", "FAR", "FAT", "FAY", "FED", "FEE", "FEW",
"FIB", "FIG", "FIN", "FIR", "FIT", "FLO", "FLY", "FOE", "FOG", "FOR",
"FRY", "FUM", "FUN", "FUR", "GAB", "GAD", "GAG", "GAL", "GAM", "GAP",
"GAS", "GAY", "GEE", "GEL", "GEM", "GET", "GIG", "GIL", "GIN", "GO",
"GOT", "GUM", "GUN", "GUS", "GUT", "GUY", "GYM", "GYP", "HA", "HAD",
"HAL", "HAM", "HAN", "HAP", "HAS", "HAT", "HAW", "HAY", "HE", "HEM",
"HEN", "HER", "HEW", "HEY", "HI", "HID", "HIM", "HIP", "HIS", "HIT",
"HO", "HOB", "HOC", "HOE", "HOG", "HOP", "HOT", "HOW", "HUB", "HUE",
"HUG", "HUH", "HUM", "HUT", "I", "ICY", "IDA", "IF", "IKE", "ILL",
"INK", "INN", "IO", "ION", "IQ", "IRA", "IRE", "IRK", "IS", "IT",
"ITS", "IVY", "JAB", "JAG", "JAM", "JAN", "JAR", "JAW", "JAY", "JET",
"JIG", "JIM", "JO", "JOB", "JOE", "JOG", "JOT", "JOY", "JUG", "JUT",
"KAY", "KEG", "KEN", "KEY", "KID", "KIM", "KIN", "KIT", "LA", "LAB",
"LAC", "LAD", "LAG", "LAM", "LAP", "LAW", "LAY", "LEA", "LED", "LEE",
"LEG", "LEN", "LEO", "LET", "LEW", "LID", "LIE", "LIN", "LIP", "LIT",
"LO", "LOB", "LOG", "LOP", "LOS", "LOT", "LOU", "LOW", "LOY", "LUG",
"LYE", "MA", "MAC", "MAD", "MAE", "MAN", "MAO", "MAP", "MAT", "MAW",
"MAY", "ME", "MEG", "MEL", "MEN", "MET", "MEW", "MID", "MIN", "MIT",
"MOB", "MOD", "MOE", "MOO", "MOP", "MOS", "MOT", "MOW", "MUD", "MUG",
"MUM", "MY", "NAB", "NAG", "NAN", "NAP", "NAT", "NAY", "NE", "NED",
"NEE", "NET", "NEW", "NIB", "NIL", "NIP", "NIT", "NO", "NOB", "NOD",
"NON", "NOR", "NOT", "NOV", "NOW", "NU", "NUN", "NUT", "O", "OAF",
"OAK", "OAR", "OAT", "ODD", "ODE", "OF", "OFF", "OFT", "OH", "OIL",
"OK", "OLD", "ON", "ONE", "OR", "ORB", "ORE", "ORR", "OS", "OTT",
"OUR", "OUT", "OVA", "OW", "OWE", "OWL", "OWN", "OX", "PA", "PAD",
"PAL", "PAM", "PAN", "PAP", "PAR", "PAT", "PAW", "PAY", "PEA", "PEG",
"PEN", "PEP", "PER", "PET", "PEW", "PHI", "PI", "PIE", "PIN", "PIT",
"PLY", "PO", "POD", "POE", "POP", "POT", "POW", "PRO", "PRY", "PUB",
"PUG", "PUN", "PUP", "PUT", "QUO", "RAG", "RAM", "RAN", "RAP", "RAT",
"RAW", "RAY", "REB", "RED", "REP", "RET", "RIB", "RID", "RIG", "RIM",
"RIO", "RIP", "ROB", "ROD", "ROE", "RON", "ROT", "ROW", "ROY", "RUB",
"RUE", "RUG", "RUM", "RUN", "RYE", "SAC", "SAD", "SAG", "SAL", "SAM",
"SAN", "SAP", "SAT", "SAW", "SAY", "SEA", "SEC", "SEE", "SEN", "SET",
"SEW", "SHE", "SHY", "SIN", "SIP", "SIR", "SIS", "SIT", "SKI", "SKY",
"SLY", "SO", "SOB", "SOD", "SON", "SOP", "SOW", "SOY", "SPA", "SPY",
"SUB", "SUD", "SUE", "SUM", "SUN", "SUP", "TAB", "TAD", "TAG", "TAN",
"TAP", "TAR", "TEA", "TED", "TEE", "TEN", "THE", "THY", "TIC", "TIE",
"TIM", "TIN", "TIP", "TO", "TOE", "TOG", "TOM", "TON", "TOO", "TOP",
"TOW", "TOY", "TRY", "TUB", "TUG", "TUM", "TUN", "TWO", "UN", "UP",
"US", "USE", "VAN", "VAT", "VET", "VIE", "WAD", "WAG", "WAR", "WAS",
"WAY", "WE", "WEB", "WED", "WEE", "WET", "WHO", "WHY", "WIN", "WIT",
"WOK", "WON", "WOO", "WOW", "WRY", "WU", "YAM", "YAP", "YAW", "YE",
"YEA", "YES", "YET", "YOU", "ABED", "ABEL", "ABET", "ABLE", "ABUT",
"ACHE", "ACID", "ACME", "ACRE", "ACTA", "ACTS", "ADAM", "ADDS",
"ADEN", "AFAR", "AFRO", "AGEE", "AHEM", "AHOY", "AIDA", "AIDE",
"AIDS", "AIRY", "AJAR", "AKIN", "ALAN", "ALEC", "ALGA", "ALIA",
"ALLY", "ALMA", "ALOE", "ALSO", "ALTO", "ALUM", "ALVA", "AMEN",
"AMES", "AMID", "AMMO", "AMOK", "AMOS", "AMRA", "ANDY", "ANEW",
"ANNA", "ANNE", "ANTE", "ANTI", "AQUA", "ARAB", "ARCH", "AREA",
"ARGO", "ARID", "ARMY", "ARTS", "ARTY", "ASIA", "ASKS", "ATOM",
"AUNT", "AURA", "AUTO", "AVER", "AVID", "AVIS", "AVON", "AVOW",
"AWAY", "AWRY", "BABE", "BABY", "BACH", "BACK", "BADE", "BAIL",
"BAIT", "BAKE", "BALD", "BALE", "BALI", "BALK", "BALL", "BALM",
"BAND", "BANE", "BANG", "BANK", "BARB", "BARD", "BARE", "BARK",
"BARN", "BARR", "BASE", "BASH", "BASK", "BASS", "BATE", "BATH",
"BAWD", "BAWL", "BEAD", "BEAK", "BEAM", "BEAN", "BEAR", "BEAT",
"BEAU", "BECK", "BEEF", "BEEN", "BEER",
"BEET", "BELA", "BELL", "BELT", "BEND", "BENT", "BERG", "BERN",
"BERT", "BESS", "BEST", "BETA", "BETH", "BHOY", "BIAS", "BIDE",
"BIEN", "BILE", "BILK", "BILL", "BIND", "BING", "BIRD", "BITE",
"BITS", "BLAB", "BLAT", "BLED", "BLEW", "BLOB", "BLOC", "BLOT",
"BLOW", "BLUE", "BLUM", "BLUR", "BOAR", "BOAT", "BOCA", "BOCK",
"BODE", "BODY", "BOGY", "BOHR", "BOIL", "BOLD", "BOLO", "BOLT",
"BOMB", "BONA", "BOND", "BONE", "BONG", "BONN", "BONY", "BOOK",
"BOOM", "BOON", "BOOT", "BORE", "BORG", "BORN", "BOSE", "BOSS",
"BOTH", "BOUT", "BOWL", "BOYD", "BRAD", "BRAE", "BRAG", "BRAN",
"BRAY", "BRED", "BREW", "BRIG", "BRIM", "BROW", "BUCK", "BUDD",
"BUFF", "BULB", "BULK", "BULL", "BUNK", "BUNT", "BUOY", "BURG",
"BURL", "BURN", "BURR", "BURT", "BURY", "BUSH", "BUSS", "BUST",
"BUSY", "BYTE", "CADY", "CAFE", "CAGE", "CAIN", "CAKE", "CALF",
"CALL", "CALM", "CAME", "CANE", "CANT", "CARD", "CARE", "CARL",
"CARR", "CART", "CASE", "CASH", "CASK", "CAST", "CAVE", "CEIL",
"CELL", "CENT", "CERN", "CHAD", "CHAR", "CHAT", "CHAW", "CHEF",
"CHEN", "CHEW", "CHIC", "CHIN", "CHOU", "CHOW", "CHUB", "CHUG",
"CHUM", "CITE", "CITY", "CLAD", "CLAM", "CLAN", "CLAW", "CLAY",
"CLOD", "CLOG", "CLOT", "CLUB", "CLUE", "COAL", "COAT", "COCA",
"COCK", "COCO", "CODA", "CODE", "CODY", "COED", "COIL", "COIN",
"COKE", "COLA", "COLD", "COLT", "COMA", "COMB", "COME", "COOK",
"COOL", "COON", "COOT", "CORD", "CORE", "CORK", "CORN", "COST",
"COVE", "COWL", "CRAB", "CRAG", "CRAM", "CRAY", "CREW", "CRIB",
"CROW", "CRUD", "CUBA", "CUBE", "CUFF", "CULL", "CULT", "CUNY",
"CURB", "CURD", "CURE", "CURL", "CURT", "CUTS", "DADE", "DALE",
"DAME", "DANA", "DANE", "DANG", "DANK", "DARE", "DARK", "DARN",
"DART", "DASH", "DATA", "DATE", "DAVE", "DAVY", "DAWN", "DAYS",
"DEAD", "DEAF", "DEAL", "DEAN", "DEAR", "DEBT", "DECK", "DEED",
"DEEM", "DEER", "DEFT", "DEFY", "DELL", "DENT", "DENY", "DESK",
"DIAL", "DICE", "DIED", "DIET", "DIME", "DINE", "DING", "DINT",
"DIRE", "DIRT", "DISC", "DISH", "DISK", "DIVE", "DOCK", "DOES",
"DOLE", "DOLL", "DOLT", "DOME", "DONE", "DOOM", "DOOR", "DORA",
"DOSE", "DOTE", "DOUG", "DOUR", "DOVE", "DOWN", "DRAB", "DRAG",
"DRAM", "DRAW", "DREW", "DRUB", "DRUG", "DRUM", "DUAL", "DUCK",
"DUCT", "DUEL", "DUET", "DUKE", "DULL", "DUMB", "DUNE", "DUNK",
"DUSK", "DUST", "DUTY", "EACH", "EARL", "EARN", "EASE", "EAST",
"EASY", "EBEN", "ECHO", "EDDY", "EDEN", "EDGE", "EDGY", "EDIT",
"EDNA", "EGAN", "ELAN", "ELBA", "ELLA", "ELSE", "EMIL", "EMIT",
"EMMA", "ENDS", "ERIC", "EROS", "EVEN", "EVER", "EVIL", "EYED",
"FACE", "FACT", "FADE", "FAIL", "FAIN", "FAIR", "FAKE", "FALL",
"FAME", "FANG", "FARM", "FAST", "FATE", "FAWN", "FEAR", "FEAT",
"FEED", "FEEL", "FEET", "FELL", "FELT", "FEND", "FERN", "FEST",
"FEUD", "FIEF", "FIGS", "FILE", "FILL", "FILM", "FIND", "FINE",
"FINK", "FIRE", "FIRM", "FISH", "FISK", "FIST", "FITS", "FIVE",
"FLAG", "FLAK", "FLAM", "FLAT", "FLAW", "FLEA", "FLED", "FLEW",
"FLIT", "FLOC", "FLOG", "FLOW", "FLUB", "FLUE", "FOAL", "FOAM",
"FOGY", "FOIL", "FOLD", "FOLK", "FOND", "FONT", "FOOD", "FOOL",
"FOOT", "FORD", "FORE", "FORK", "FORM", "FORT", "FOSS", "FOUL",
"FOUR", "FOWL", "FRAU", "FRAY", "FRED", "FREE", "FRET", "FREY",
"FROG", "FROM", "FUEL", "FULL", "FUME", "FUND", "FUNK", "FURY",
"FUSE", "FUSS", "GAFF", "GAGE", "GAIL", "GAIN", "GAIT", "GALA",
"GALE", "GALL", "GALT", "GAME", "GANG", "GARB", "GARY", "GASH",
"GATE", "GAUL", "GAUR", "GAVE", "GAWK", "GEAR", "GELD", "GENE",
"GENT", "GERM", "GETS", "GIBE", "GIFT", "GILD", "GILL", "GILT",
"GINA", "GIRD", "GIRL", "GIST", "GIVE", "GLAD", "GLEE", "GLEN",
"GLIB", "GLOB", "GLOM", "GLOW", "GLUE", "GLUM", "GLUT", "GOAD",
"GOAL", "GOAT", "GOER", "GOES", "GOLD", "GOLF", "GONE", "GONG",
"GOOD", "GOOF", "GORE", "GORY", "GOSH", "GOUT", "GOWN", "GRAB",
"GRAD", "GRAY", "GREG", "GREW", "GREY", "GRID", "GRIM", "GRIN",
"GRIT", "GROW", "GRUB", "GULF", "GULL", "GUNK", "GURU", "GUSH",
"GUST", "GWEN", "GWYN", "HAAG", "HAAS", "HACK", "HAIL", "HAIR",
"HALE", "HALF", "HALL", "HALO", "HALT", "HAND", "HANG", "HANK",
"HANS", "HARD", "HARK", "HARM", "HART", "HASH", "HAST", "HATE",
"HATH", "HAUL", "HAVE", "HAWK", "HAYS", "HEAD", "HEAL", "HEAR",
"HEAT", "HEBE", "HECK", "HEED", "HEEL", "HEFT", "HELD", "HELL",
"HELM", "HERB", "HERD", "HERE", "HERO", "HERS", "HESS", "HEWN",
"HICK", "HIDE", "HIGH", "HIKE", "HILL", "HILT", "HIND", "HINT",
"HIRE", "HISS", "HIVE", "HOBO", "HOCK", "HOFF", "HOLD", "HOLE",
"HOLM", "HOLT", "HOME", "HONE", "HONK", "HOOD", "HOOF", "HOOK",
"HOOT", "HORN", "HOSE", "HOST", "HOUR", "HOVE", "HOWE", "HOWL",
"HOYT", "HUCK", "HUED", "HUFF", "HUGE", "HUGH", "HUGO", "HULK",
"HULL", "HUNK", "HUNT", "HURD", "HURL", "HURT", "HUSH", "HYDE",
"HYMN", "IBIS", "ICON", "IDEA", "IDLE", "IFFY", "INCA", "INCH",
"INTO", "IONS", "IOTA", "IOWA", "IRIS", "IRMA", "IRON", "ISLE",
"ITCH", "ITEM", "IVAN", "JACK", "JADE", "JAIL", "JAKE", "JANE",
"JAVA", "JEAN", "JEFF", "JERK", "JESS", "JEST", "JIBE", "JILL",
"JILT", "JIVE", "JOAN", "JOBS", "JOCK", "JOEL", "JOEY", "JOHN",
"JOIN", "JOKE", "JOLT", "JOVE", "JUDD", "JUDE", "JUDO", "JUDY",
"JUJU", "JUKE", "JULY", "JUNE", "JUNK", "JUNO", "JURY", "JUST",
"JUTE", "KAHN", "KALE", "KANE", "KANT", "KARL", "KATE", "KEEL",
"KEEN", "KENO", "KENT", "KERN", "KERR", "KEYS", "KICK", "KILL",
"KIND", "KING", "KIRK", "KISS", "KITE", "KLAN", "KNEE", "KNEW",
"KNIT", "KNOB", "KNOT", "KNOW", "KOCH", "KONG", "KUDO", "KURD",
"KURT", "KYLE", "LACE", "LACK", "LACY", "LADY", "LAID", "LAIN",
"LAIR", "LAKE", "LAMB", "LAME", "LAND", "LANE", "LANG", "LARD",
"LARK", "LASS", "LAST", "LATE", "LAUD", "LAVA", "LAWN", "LAWS",
"LAYS", "LEAD", "LEAF", "LEAK", "LEAN", "LEAR", "LEEK", "LEER",
"LEFT", "LEND", "LENS", "LENT", "LEON", "LESK", "LESS", "LEST",
"LETS", "LIAR", "LICE", "LICK", "LIED", "LIEN", "LIES", "LIEU",
"LIFE", "LIFT", "LIKE", "LILA", "LILT", "LILY", "LIMA", "LIMB",
"LIME", "LIND", "LINE", "LINK", "LINT", "LION", "LISA", "LIST",
"LIVE", "LOAD", "LOAF", "LOAM", "LOAN", "LOCK", "LOFT", "LOGE",
"LOIS", "LOLA", "LONE", "LONG", "LOOK", "LOON", "LOOT", "LORD",
"LORE", "LOSE", "LOSS", "LOST", "LOUD", "LOVE", "LOWE", "LUCK",
"LUCY", "LUGE", "LUKE", "LULU", "LUND", "LUNG", "LURA", "LURE",
"LURK", "LUSH", "LUST", "LYLE", "LYNN", "LYON", "LYRA", "MACE",
"MADE", "MAGI", "MAID", "MAIL", "MAIN", "MAKE", "MALE", "MALI",
"MALL", "MALT", "MANA", "MANN", "MANY", "MARC", "MARE", "MARK",
"MARS", "MART", "MARY", "MASH", "MASK", "MASS", "MAST", "MATE",
"MATH", "MAUL", "MAYO", "MEAD", "MEAL", "MEAN", "MEAT", "MEEK",
"MEET", "MELD", "MELT", "MEMO", "MEND", "MENU", "MERT", "MESH",
"MESS", "MICE", "MIKE", "MILD", "MILE", "MILK", "MILL", "MILT",
"MIMI", "MIND", "MINE", "MINI", "MINK", "MINT", "MIRE", "MISS",
"MIST", "MITE", "MITT", "MOAN", "MOAT", "MOCK", "MODE", "MOLD",
"MOLE", "MOLL", "MOLT", "MONA", "MONK", "MONT", "MOOD", "MOON",
"MOOR", "MOOT", "MORE", "MORN", "MORT", "MOSS", "MOST", "MOTH",
"MOVE", "MUCH", "MUCK", "MUDD", "MUFF", "MULE", "MULL", "MURK",
"MUSH", "MUST", "MUTE", "MUTT", "MYRA", "MYTH", "NAGY", "NAIL",
"NAIR", "NAME", "NARY", "NASH", "NAVE", "NAVY", "NEAL", "NEAR",
"NEAT", "NECK", "NEED", "NEIL", "NELL", "NEON", "NERO", "NESS",
"NEST", "NEWS", "NEWT", "NIBS", "NICE", "NICK", "NILE", "NINA",
"NINE", "NOAH", "NODE", "NOEL", "NOLL", "NONE", "NOOK", "NOON",
"NORM", "NOSE", "NOTE", "NOUN", "NOVA", "NUDE", "NULL", "NUMB",
"OATH", "OBEY", "OBOE", "ODIN", "OHIO", "OILY", "OINT", "OKAY",
"OLAF", "OLDY", "OLGA", "OLIN", "OMAN", "OMEN", "OMIT", "ONCE",
"ONES", "ONLY", "ONTO", "ONUS", "ORAL", "ORGY", "OSLO", "OTIS",
"OTTO", "OUCH", "OUST", "OUTS", "OVAL", "OVEN", "OVER", "OWLY",
"OWNS", "QUAD", "QUIT", "QUOD", "RACE", "RACK", "RACY", "RAFT",
"RAGE", "RAID", "RAIL", "RAIN", "RAKE", "RANK", "RANT", "RARE",
"RASH", "RATE", "RAVE", "RAYS", "READ", "REAL", "REAM", "REAR",
"RECK", "REED", "REEF", "REEK", "REEL", "REID", "REIN", "RENA",
"REND", "RENT", "REST", "RICE", "RICH", "RICK", "RIDE", "RIFT",
"RILL", "RIME", "RING", "RINK", "RISE", "RISK", "RITE", "ROAD",
"ROAM", "ROAR", "ROBE", "ROCK", "RODE", "ROIL", "ROLL", "ROME",
"ROOD", "ROOF", "ROOK", "ROOM", "ROOT", "ROSA", "ROSE", "ROSS",
"ROSY", "ROTH", "ROUT", "ROVE", "ROWE", "ROWS", "RUBE", "RUBY",
"RUDE", "RUDY", "RUIN", "RULE", "RUNG", "RUNS", "RUNT", "RUSE",
"RUSH", "RUSK", "RUSS", "RUST", "RUTH", "SACK", "SAFE", "SAGE",
"SAID", "SAIL", "SALE", "SALK", "SALT", "SAME", "SAND", "SANE",
"SANG", "SANK", "SARA", "SAUL", "SAVE", "SAYS", "SCAN", "SCAR",
"SCAT", "SCOT", "SEAL", "SEAM", "SEAR", "SEAT", "SEED", "SEEK",
"SEEM", "SEEN", "SEES", "SELF", "SELL", "SEND", "SENT", "SETS",
"SEWN", "SHAG", "SHAM", "SHAW", "SHAY", "SHED", "SHIM", "SHIN",
"SHOD", "SHOE", "SHOT", "SHOW", "SHUN", "SHUT", "SICK", "SIDE",
"SIFT", "SIGH", "SIGN", "SILK", "SILL", "SILO", "SILT", "SINE",
"SING", "SINK", "SIRE", "SITE", "SITS", "SITU", "SKAT", "SKEW",
"SKID", "SKIM", "SKIN", "SKIT", "SLAB", "SLAM", "SLAT", "SLAY",
"SLED", "SLEW", "SLID", "SLIM", "SLIT", "SLOB", "SLOG", "SLOT",
"SLOW", "SLUG", "SLUM", "SLUR", "SMOG", "SMUG", "SNAG", "SNOB",
"SNOW", "SNUB", "SNUG", "SOAK", "SOAR", "SOCK", "SODA", "SOFA",
"SOFT", "SOIL", "SOLD", "SOME", "SONG", "SOON", "SOOT", "SORE",
"SORT", "SOUL", "SOUR", "SOWN", "STAB", "STAG", "STAN", "STAR",
"STAY", "STEM", "STEW", "STIR", "STOW", "STUB", "STUN", "SUCH",
"SUDS", "SUIT", "SULK", "SUMS", "SUNG", "SUNK", "SURE", "SURF",
"SWAB", "SWAG", "SWAM", "SWAN", "SWAT", "SWAY", "SWIM", "SWUM",
"TACK", "TACT", "TAIL", "TAKE", "TALE", "TALK", "TALL", "TANK",
"TASK", "TATE", "TAUT", "TEAL", "TEAM", "TEAR", "TECH", "TEEM",
"TEEN", "TEET", "TELL", "TEND", "TENT", "TERM", "TERN", "TESS",
"TEST", "THAN", "THAT", "THEE", "THEM", "THEN", "THEY", "THIN",
"THIS", "THUD", "THUG", "TICK", "TIDE", "TIDY", "TIED", "TIER",
"TILE", "TILL", "TILT", "TIME", "TINA", "TINE", "TINT", "TINY",
"TIRE", "TOAD", "TOGO", "TOIL", "TOLD", "TOLL", "TONE", "TONG",
"TONY", "TOOK", "TOOL", "TOOT", "TORE", "TORN", "TOTE", "TOUR",
"TOUT", "TOWN", "TRAG", "TRAM", "TRAY", "TREE", "TREK", "TRIG",
"TRIM", "TRIO", "TROD", "TROT", "TROY", "TRUE", "TUBA", "TUBE",
"TUCK", "TUFT", "TUNA", "TUNE", "TUNG", "TURF", "TURN", "TUSK",
"TWIG", "TWIN", "TWIT", "ULAN", "UNIT", "URGE", "USED", "USER",
"USES", "UTAH", "VAIL", "VAIN", "VALE", "VARY", "VASE", "VAST",
"VEAL", "VEDA", "VEIL", "VEIN", "VEND", "VENT", "VERB", "VERY",
"VETO", "VICE", "VIEW", "VINE", "VISE", "VOID", "VOLT", "VOTE",
"WACK", "WADE", "WAGE", "WAIL", "WAIT", "WAKE", "WALE", "WALK",
"WALL", "WALT", "WAND", "WANE", "WANG", "WANT", "WARD", "WARM",
"WARN", "WART", "WASH", "WAST", "WATS", "WATT", "WAVE", "WAVY",
"WAYS", "WEAK", "WEAL", "WEAN", "WEAR", "WEED", "WEEK", "WEIR",
"WELD", "WELL", "WELT", "WENT", "WERE", "WERT", "WEST", "WHAM",
"WHAT", "WHEE", "WHEN", "WHET", "WHOA", "WHOM", "WICK", "WIFE",
"WILD", "WILL", "WIND", "WINE", "WING", "WINK", "WINO", "WIRE",
"WISE", "WISH", "WITH", "WOLF", "WONT", "WOOD", "WOOL", "WORD",
"WORE", "WORK", "WORM", "WORN", "WOVE", "WRIT", "WYNN", "YALE",
"YANG", "YANK", "YARD", "YARN", "YAWL", "YAWN", "YEAH", "YEAR",
"YELL", "YOGA", "YOKE" ]

View File

@@ -0,0 +1,163 @@
import { bytesToHex } from '@xrplf/isomorphic/utils'
import { BigNumber } from 'bignumber.js'
import { decodeAccountID } from 'xahau-address-codec'
import { decode, encode, encodeForSigning } from 'xahau-binary-codec'
import { verify } from 'xahau-keypairs'
import { ValidationError } from '../errors'
import { Signer } from '../models/common'
import { Transaction, validate } from '../models/transactions'
/**
* Takes several transactions with Signer fields (in object or blob form) and creates a
* single transaction with all Signers that then gets signed and returned.
*
* @param transactions - An array of signed Transactions (in object or blob form) to combine into a single signed Transaction.
* @returns A single signed Transaction which has all Signers from transactions within it.
* @throws ValidationError if:
* - There were no transactions given to sign
* - The SigningPubKey field is not the empty string in any given transaction
* - Any transaction is missing a Signers field.
* @category Signing
*/
function multisign(transactions: Array<Transaction | string>): string {
if (transactions.length === 0) {
throw new ValidationError('There were 0 transactions to multisign')
}
const decodedTransactions: Transaction[] = transactions.map(
(txOrBlob: string | Transaction) => {
return getDecodedTransaction(txOrBlob)
},
)
decodedTransactions.forEach((tx) => {
/*
* This will throw a more clear error for JS users if any of the supplied transactions has incorrect formatting
*/
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- validate does not accept Transaction type
validate(tx as unknown as Record<string, unknown>)
if (tx.Signers == null || tx.Signers.length === 0) {
throw new ValidationError(
"For multisigning all transactions must include a Signers field containing an array of signatures. You may have forgotten to pass the 'forMultisign' parameter when signing.",
)
}
if (tx.SigningPubKey !== '') {
throw new ValidationError(
'SigningPubKey must be an empty string for all transactions when multisigning.',
)
}
})
validateTransactionEquivalence(decodedTransactions)
return encode(getTransactionWithAllSigners(decodedTransactions))
}
/**
* Verifies that the given transaction has a valid signature based on public-key encryption.
*
* @param tx - A transaction to verify the signature of. (Can be in object or encoded string format).
* @param [publicKey] Specific public key to use to verify. If not specified the `SigningPublicKey` of tx will be used.
* @returns Returns true if tx has a valid signature, and returns false otherwise.
* @throws Error when transaction is missing TxnSignature
* @throws Error when publicKey is not provided and transaction is missing SigningPubKey
* @category Utilities
*/
function verifySignature(
tx: Transaction | string,
publicKey?: string,
): boolean {
const decodedTx: Transaction = getDecodedTransaction(tx)
let key = publicKey
// Need a SignedTransaction class where TxnSignature is not optional.
if (typeof decodedTx.TxnSignature !== 'string' || !decodedTx.TxnSignature) {
throw new Error('Transaction is missing a signature, TxnSignature')
}
if (!key) {
// Need a SignedTransaction class where TxnSignature is not optional.
if (
typeof decodedTx.SigningPubKey !== 'string' ||
!decodedTx.SigningPubKey
) {
throw new Error('Transaction is missing a public key, SigningPubKey')
}
key = decodedTx.SigningPubKey
}
return verify(encodeForSigning(decodedTx), decodedTx.TxnSignature, key)
}
/**
* The transactions should all be equal except for the 'Signers' field.
*
* @param transactions - An array of Transactions which are expected to be equal other than 'Signers'.
* @throws ValidationError if the transactions are not equal in any field other than 'Signers'.
*/
function validateTransactionEquivalence(transactions: Transaction[]): void {
const exampleTransaction = JSON.stringify({
...transactions[0],
Signers: null,
})
if (
transactions
.slice(1)
.some(
(tx) => JSON.stringify({ ...tx, Signers: null }) !== exampleTransaction,
)
) {
throw new ValidationError(
'txJSON is not the same for all signedTransactions',
)
}
}
function getTransactionWithAllSigners(
transactions: Transaction[],
): Transaction {
// Signers must be sorted in the combined transaction - See compareSigners' documentation for more details
const sortedSigners: Signer[] = transactions
.flatMap((tx) => tx.Signers ?? [])
.sort(compareSigners)
return { ...transactions[0], Signers: sortedSigners }
}
/**
* If presented in binary form, the Signers array must be sorted based on
* the numeric value of the signer addresses, with the lowest value first.
* (If submitted as JSON, the submit_multisigned method handles this automatically.)
* https://xrpl.org/multi-signing.html.
*
* @param left - A Signer to compare with.
* @param right - A second Signer to compare with.
* @returns 1 if left \> right, 0 if left = right, -1 if left \< right, and null if left or right are NaN.
*/
function compareSigners(left: Signer, right: Signer): number {
return addressToBigNumber(left.Signer.Account).comparedTo(
addressToBigNumber(right.Signer.Account),
)
}
const NUM_BITS_IN_HEX = 16
function addressToBigNumber(address: string): BigNumber {
const hex = bytesToHex(decodeAccountID(address))
return new BigNumber(hex, NUM_BITS_IN_HEX)
}
function getDecodedTransaction(txOrBlob: Transaction | string): Transaction {
if (typeof txOrBlob === 'object') {
// We need this to handle X-addresses in multisigning
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- We are casting here to get strong typing
return decode(encode(txOrBlob)) as unknown as Transaction
}
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- We are casting here to get strong typing
return decode(txOrBlob) as unknown as Transaction
}
export { verifySignature, multisign }

View File

@@ -0,0 +1,37 @@
import { Account } from '@xrplf/secret-numbers'
import ECDSA from '../ECDSA'
import { Wallet } from '.'
/**
* Derives a wallet from secret numbers.
* NOTE: This uses a default encoding algorithm of secp256k1 to match the popular wallet
* [Xumm (aka Xaman)](https://xumm.app/)'s behavior.
* This may be different from the DEFAULT_ALGORITHM for other ways to generate a Wallet.
*
* @param secretNumbers - A string consisting of 8 times 6 numbers (whitespace delimited) used to derive a wallet.
* @param opts - (Optional) Options to derive a Wallet.
* @param opts.masterAddress - Include if a Wallet uses a Regular Key Pair. It must be the master address of the account.
* @param opts.algorithm - The digital signature algorithm to generate an address for.
* @returns A Wallet derived from secret numbers.
* @throws ValidationError if unable to derive private key from secret number input.
*/
export function walletFromSecretNumbers(
secretNumbers: string[] | string,
opts?: { masterAddress?: string; algorithm?: ECDSA },
): Wallet {
const secret = new Account(secretNumbers).getFamilySeed()
const updatedOpts: { masterAddress?: string; algorithm?: ECDSA } = {
masterAddress: undefined,
algorithm: undefined,
}
// Use secp256k1 since that's the algorithm used by popular wallets like Xumm when generating secret number accounts
if (opts === undefined) {
updatedOpts.algorithm = ECDSA.secp256k1
} else {
updatedOpts.masterAddress = opts.masterAddress
updatedOpts.algorithm = opts.algorithm ?? ECDSA.secp256k1
}
return Wallet.fromSecret(secret, updatedOpts)
}

View File

@@ -0,0 +1,40 @@
/**
* Manage all the requests made to the websocket, and their async responses
* that come in from the WebSocket. Because they come in over the WS connection
* after-the-fact.
*/
export default class ConnectionManager {
private promisesAwaitingConnection: Array<{
resolve: (value?: void | PromiseLike<void>) => void
reject: (value?: Error) => void
}> = []
/**
* Resolves all awaiting connections.
*/
public resolveAllAwaiting(): void {
this.promisesAwaitingConnection.map(({ resolve }) => resolve())
this.promisesAwaitingConnection = []
}
/**
* Rejects all awaiting connections.
*
* @param error - Error to throw in the rejection.
*/
public rejectAllAwaiting(error: Error): void {
this.promisesAwaitingConnection.map(({ reject }) => reject(error))
this.promisesAwaitingConnection = []
}
/**
* Await a new connection.
*
* @returns A promise for resolving the connection.
*/
public async awaitConnection(): Promise<void> {
return new Promise((resolve, reject) => {
this.promisesAwaitingConnection.push({ resolve, reject })
})
}
}

View File

@@ -0,0 +1,71 @@
/*
* Original code based on "backo" - https://github.com/segmentio/backo
* MIT License - Copyright 2014 Segment.io
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software
* is furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
interface ExponentialBackoffOptions {
// The min backoff duration.
min?: number
// The max backoff duration.
max?: number
}
const DEFAULT_MIN = 100
const DEFAULT_MAX = 1000
/**
* A Back off strategy that increases exponentially. Useful with repeated
* setTimeout calls over a network (where the destination may be down).
*/
export default class ExponentialBackoff {
private readonly ms: number
private readonly max: number
private readonly factor: number = 2
private numAttempts = 0
/**
* Constructs an ExponentialBackoff object.
*
* @param opts - The options for the object.
*/
public constructor(opts: ExponentialBackoffOptions = {}) {
this.ms = opts.min ?? DEFAULT_MIN
this.max = opts.max ?? DEFAULT_MAX
}
/**
* Number of attempts for backoff so far.
*
* @returns Number of attempts.
*/
public get attempts(): number {
return this.numAttempts
}
/**
* Return the backoff duration.
*
* @returns The backoff duration in milliseconds.
*/
public duration(): number {
const ms = this.ms * this.factor ** this.numAttempts
this.numAttempts += 1
return Math.floor(Math.min(ms, this.max))
}
/**
* Reset the number of attempts.
*/
public reset(): void {
this.numAttempts = 0
}
}

View File

@@ -0,0 +1,225 @@
import {
ResponseFormatError,
XahaudError,
TimeoutError,
XahlError,
} from '../errors'
import type { APIVersion } from '../models'
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
* after-the-fact, so this manager will tie that response to resolve the
* original request.
*/
export default class RequestManager {
private nextId = 0
private readonly promisesAwaitingResponse = new Map<
string | number,
// 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, APIVersion>,
>(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.
*
* @param id - ID of the request.
* @param response - Response to return.
* @throws Error if no existing promise with the given ID.
*/
public resolve(
id: string | number,
response: Partial<Response<APIVersion>>,
): void {
const promise = this.promisesAwaitingResponse.get(id)
if (promise == null) {
throw new XahlError(`No existing promise with id ${id}`, {
type: 'resolve',
response,
})
}
clearTimeout(promise.timer)
promise.resolve(response)
this.deletePromise(id)
}
/**
* Rejects a request.
*
* @param id - ID of the request.
* @param error - Error to throw with the reject.
* @throws Error if no existing promise with the given ID.
*/
public reject(id: string | number, error: Error): void {
const promise = this.promisesAwaitingResponse.get(id)
if (promise == null) {
throw new XahlError(`No existing promise with id ${id}`, {
type: 'reject',
error,
})
}
clearTimeout(promise.timer)
// TODO: figure out how to have a better stack trace for an error
promise.reject(error)
this.deletePromise(id)
}
/**
* Reject all pending requests.
*
* @param error - Error to throw with the reject.
*/
public rejectAll(error: Error): void {
this.promisesAwaitingResponse.forEach((_promise, id, _map) => {
this.reject(id, error)
this.deletePromise(id)
})
}
/**
* Creates a new WebSocket request. This sets up a timeout timer to catch
* hung responses, and a promise that will resolve with the response once
* the response is seen & handled.
*
* @param request - Request to create.
* @param timeout - Timeout length to catch hung responses.
* @returns Request ID, new request form, and the promise for resolving the request.
* @throws XahlError if request with the same ID is already pending.
*/
public createRequest<
R extends BaseRequest,
T = RequestResponseMap<R, APIVersion>,
>(request: R, timeout: number): [string | number, string, Promise<T>] {
let newId: string | number
if (request.id == null) {
newId = this.nextId
this.nextId += 1
} else {
newId = request.id
}
const newRequest = JSON.stringify({ ...request, id: newId })
// Typing required for Jest running in browser
const timer: ReturnType<typeof setTimeout> = setTimeout(() => {
this.reject(
newId,
new TimeoutError(
`Timeout for request: ${JSON.stringify(request)} with id ${newId}`,
request,
),
)
}, timeout)
/*
* Node.js won't exit if a timer is still running, so we tell Node to ignore.
* (Node will still wait for the request to complete).
*/
// The following type assertions are required to get this code to pass in browser environments
// where setTimeout has a different type
// eslint-disable-next-line max-len -- Necessary to disable both rules.
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access -- Reason above.
if ((timer as unknown as any).unref) {
// eslint-disable-next-line max-len -- Necessary to disable both rules.
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call -- Reason above.
;(timer as unknown as any).unref()
}
if (this.promisesAwaitingResponse.has(newId)) {
clearTimeout(timer)
throw new XahlError(
`Response with id '${newId}' is already pending`,
request,
)
}
const newPromise = new Promise<T>((resolve, reject) => {
this.promisesAwaitingResponse.set(newId, {
resolve,
reject,
timer,
})
})
return [newId, newRequest, newPromise]
}
/**
* Handle a "response". Responses match to the earlier request handlers,
* and resolve/reject based on the data received.
*
* @param response - The response to handle.
* @throws ResponseFormatError if the response format is invalid, XahaudError if xahaud returns an error.
*/
public handleResponse(
response: Partial<Response<APIVersion> | ErrorResponse>,
): void {
if (
response.id == null ||
!(typeof response.id === 'string' || typeof response.id === 'number')
) {
throw new ResponseFormatError('valid id not found in response', response)
}
if (!this.promisesAwaitingResponse.has(response.id)) {
return
}
if (response.status == null) {
const error = new ResponseFormatError('Response has no status')
this.reject(response.id, error)
}
if (response.status === 'error') {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- We know this must be true
const errorResponse = response as Partial<ErrorResponse>
const error = new XahaudError(
errorResponse.error_message ?? errorResponse.error,
errorResponse,
)
this.reject(response.id, error)
return
}
if (response.status !== 'success') {
const error = new ResponseFormatError(
`unrecognized response.status: ${response.status ?? ''}`,
response,
)
this.reject(response.id, error)
return
}
// status no longer needed because error is thrown if status is not "success"
delete response.status
this.resolve(response.id, response)
}
/**
* Delete a promise after it has been returned.
*
* @param id - ID of the request.
*/
private deletePromise(id: string | number): void {
this.promisesAwaitingResponse.delete(id)
}
}

View File

@@ -0,0 +1,528 @@
/* eslint-disable max-lines -- Connection is a large file w/ lots of imports/exports */
import type { Agent } from 'http'
import { bytesToHex, hexToString } from '@xrplf/isomorphic/utils'
import WebSocket, { ClientOptions } from '@xrplf/isomorphic/ws'
import { EventEmitter } from 'eventemitter3'
import {
DisconnectedError,
NotConnectedError,
ConnectionError,
XahlError,
} from '../errors'
import type { APIVersion, RequestResponseMap } from '../models'
import { BaseRequest } from '../models/methods/baseMethod'
import ConnectionManager from './ConnectionManager'
import ExponentialBackoff from './ExponentialBackoff'
import RequestManager from './RequestManager'
const SECONDS_PER_MINUTE = 60
const TIMEOUT = 20
const CONNECTION_TIMEOUT = 5
/**
* ConnectionOptions is the configuration for the Connection class.
*/
interface ConnectionOptions {
trace?: boolean | ((id: string, message: string) => void)
headers?: { [key: string]: string }
agent?: Agent
authorization?: string
connectionTimeout: number
timeout: number
}
/**
* ConnectionUserOptions is the user-provided configuration object. All configuration
* is optional, so any ConnectionOptions configuration that has a default value is
* still optional at the point that the user provides it.
*/
export type ConnectionUserOptions = Partial<ConnectionOptions>
/**
* Represents an intentionally triggered web-socket disconnect code.
* WebSocket spec allows 4xxx codes for app/library specific codes.
* See: https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent
*/
export const INTENTIONAL_DISCONNECT_CODE = 4000
type WebsocketState = 0 | 1 | 2 | 3
/**
* Create a new websocket given your URL and optional proxy/certificate
* configuration.
*
* @param url - The URL to connect to.
* @param config - THe configuration options for the WebSocket.
* @returns A Websocket that fits the given configuration parameters.
*/
function createWebSocket(
url: string,
config: ConnectionOptions,
): WebSocket | null {
const options: ClientOptions = {
agent: config.agent,
}
if (config.headers) {
options.headers = config.headers
}
if (config.authorization != null) {
options.headers = {
...options.headers,
Authorization: `Basic ${btoa(config.authorization)}`,
}
}
const websocketOptions = { ...options }
return new WebSocket(url, websocketOptions)
}
/**
* Ws.send(), but promisified.
*
* @param ws - Websocket to send with.
* @param message - Message to send.
* @returns When the message has been sent.
*/
async function websocketSendAsync(
ws: WebSocket,
message: string,
): Promise<void> {
return new Promise<void>((resolve, reject) => {
ws.send(message, (error) => {
if (error) {
reject(new DisconnectedError(error.message, error))
} else {
resolve()
}
})
})
}
/**
* The main Connection class. Responsible for connecting to & managing
* an active WebSocket connection to a XAHL node.
*/
export class Connection extends EventEmitter {
private readonly url: string | undefined
private ws: WebSocket | null = null
// Typing necessary for Jest tests running in browser
private reconnectTimeoutID: null | ReturnType<typeof setTimeout> = null
// Typing necessary for Jest tests running in browser
private heartbeatIntervalID: null | ReturnType<typeof setTimeout> = null
private readonly retryConnectionBackoff = new ExponentialBackoff({
min: 100,
max: SECONDS_PER_MINUTE * 1000,
})
private readonly config: ConnectionOptions
private readonly requestManager = new RequestManager()
private readonly connectionManager = new ConnectionManager()
/**
* Creates a new Connection object.
*
* @param url - URL to connect to.
* @param options - Options for the Connection object.
*/
public constructor(url?: string, options: ConnectionUserOptions = {}) {
super()
this.url = url
this.config = {
timeout: TIMEOUT * 1000,
connectionTimeout: CONNECTION_TIMEOUT * 1000,
...options,
}
if (typeof options.trace === 'function') {
this.trace = options.trace
} else if (options.trace) {
// eslint-disable-next-line no-console -- Used for tracing only
this.trace = console.log
}
}
/**
* Gets the state of the websocket.
*
* @returns The Websocket's ready state.
*/
private get state(): WebsocketState {
return this.ws ? this.ws.readyState : WebSocket.CLOSED
}
/**
* Returns whether the server should be connected.
*
* @returns Whether the server should be connected.
*/
private get shouldBeConnected(): boolean {
return this.ws !== null
}
/**
* Returns whether the websocket is connected.
*
* @returns Whether the websocket connection is open.
*/
public isConnected(): boolean {
return this.state === WebSocket.OPEN
}
/**
* Connects the websocket to the provided URL.
*
* @returns When the websocket is connected.
* @throws ConnectionError if there is a connection error, RippleError if there is already a WebSocket in existence.
*/
// eslint-disable-next-line max-lines-per-function -- Necessary
public async connect(): Promise<void> {
if (this.isConnected()) {
return Promise.resolve()
}
if (this.state === WebSocket.CONNECTING) {
return this.connectionManager.awaitConnection()
}
if (!this.url) {
return Promise.reject(
new ConnectionError('Cannot connect because no server was specified'),
)
}
if (this.ws != null) {
return Promise.reject(
new XahlError('Websocket connection never cleaned up.', {
state: this.state,
}),
)
}
// Create the connection timeout, in case the connection hangs longer than expected.
const connectionTimeoutID: ReturnType<typeof setTimeout> = setTimeout(
() => {
this.onConnectionFailed(
new ConnectionError(
`Error: connect() timed out after ${this.config.connectionTimeout} ms. If your internet connection is working, the ` +
`xahaud server may be blocked or inaccessible. You can also try setting the 'connectionTimeout' option in the Client constructor.`,
),
)
},
this.config.connectionTimeout,
)
// Connection listeners: these stay attached only until a connection is done/open.
this.ws = createWebSocket(this.url, this.config)
if (this.ws == null) {
throw new XahlError('Connect: created null websocket')
}
this.ws.on('error', (error) => this.onConnectionFailed(error))
this.ws.on('error', () => clearTimeout(connectionTimeoutID))
this.ws.on('close', (reason) => this.onConnectionFailed(reason))
this.ws.on('close', () => clearTimeout(connectionTimeoutID))
this.ws.once('open', () => {
void this.onceOpen(connectionTimeoutID)
})
return this.connectionManager.awaitConnection()
}
/**
* Disconnect the websocket connection.
* We never expect this method to reject. Even on "bad" disconnects, the websocket
* should still successfully close with the relevant error code returned.
* See https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent for the full list.
* If no open websocket connection exists, resolve with no code (`undefined`).
*
* @returns A promise containing either `undefined` or a disconnected code, that resolves when the connection is destroyed.
*/
public async disconnect(): Promise<number | undefined> {
this.clearHeartbeatInterval()
if (this.reconnectTimeoutID !== null) {
clearTimeout(this.reconnectTimeoutID)
this.reconnectTimeoutID = null
}
if (this.state === WebSocket.CLOSED) {
return Promise.resolve(undefined)
}
if (this.ws == null) {
return Promise.resolve(undefined)
}
return new Promise((resolve) => {
if (this.ws == null) {
resolve(undefined)
}
if (this.ws != null) {
this.ws.once('close', (code) => resolve(code))
}
/*
* Connection already has a disconnect handler for the disconnect logic.
* Just close the websocket manually (with our "intentional" code) to
* trigger that.
*/
if (this.ws != null && this.state !== WebSocket.CLOSING) {
this.ws.close(INTENTIONAL_DISCONNECT_CODE)
}
})
}
/**
* Disconnect the websocket, then connect again.
*
*/
public async reconnect(): Promise<void> {
/*
* NOTE: We currently have a "reconnecting" event, but that only triggers
* through an unexpected connection retry logic.
* See: https://github.com/XRPLF/xrpl.js/pull/1101#issuecomment-565360423
*/
this.emit('reconnect')
await this.disconnect()
await this.connect()
}
/**
* Sends a request to the xahaud server.
*
* @param request - The request to send to the server.
* @param timeout - How long the Connection instance should wait before assuming that there will not be a response.
* @returns The response from the xahaud server.
* @throws NotConnectedError if the Connection isn't connected to a server.
*/
public async request<
R extends BaseRequest,
T = RequestResponseMap<R, APIVersion>,
>(request: R, timeout?: number): Promise<T> {
if (!this.shouldBeConnected || this.ws == null) {
throw new NotConnectedError(JSON.stringify(request), request)
}
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)
})
return responsePromise
}
/**
* Get the Websocket connection URL.
*
* @returns The Websocket connection URL.
*/
public getUrl(): string {
return this.url ?? ''
}
// eslint-disable-next-line @typescript-eslint/no-empty-function, class-methods-use-this -- Does nothing on default
public readonly trace: (id: string, message: string) => void = () => {}
/**
* Handler for when messages are received from the server.
*
* @param message - The message received from the server.
*/
private onMessage(message): void {
this.trace('receive', message)
let data: Record<string, unknown>
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- Must be a JSON dictionary
data = JSON.parse(message)
} catch (error) {
if (error instanceof Error) {
this.emit('error', 'badMessage', error.message, message)
}
return
}
if (data.type == null && data.error) {
// e.g. slowDown
this.emit('error', data.error, data.error_message, data)
return
}
if (data.type) {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true
this.emit(data.type as string, data)
}
if (data.type === 'response') {
try {
this.requestManager.handleResponse(data)
} catch (error) {
// eslint-disable-next-line max-depth -- okay here
if (error instanceof Error) {
this.emit('error', 'badMessage', error.message, message)
} else {
this.emit('error', 'badMessage', error, error)
}
}
}
}
/**
* Handler for what to do once the connection to the server is open.
*
* @param connectionTimeoutID - Timeout in case the connection hangs longer than expected.
* @returns A promise that resolves to void when the connection is fully established.
* @throws Error if the websocket initialized is somehow null.
*/
// eslint-disable-next-line max-lines-per-function -- Many error code conditionals to check.
private async onceOpen(
connectionTimeoutID: ReturnType<typeof setTimeout>,
): Promise<void> {
if (this.ws == null) {
throw new XahlError('onceOpen: ws is null')
}
// Once the connection completes successfully, remove all old listeners
this.ws.removeAllListeners()
clearTimeout(connectionTimeoutID)
// Add new, long-term connected listeners for messages and errors
this.ws.on('message', (message: string) => this.onMessage(message))
this.ws.on('error', (error) =>
this.emit('error', 'websocket', error.message, error),
)
// Handle a closed connection: reconnect if it was unexpected
this.ws.once('close', (code?: number, reason?: Uint8Array) => {
if (this.ws == null) {
throw new XahlError('onceClose: ws is null')
}
this.clearHeartbeatInterval()
this.requestManager.rejectAll(
new DisconnectedError(
`websocket was closed, ${
reason ? hexToString(bytesToHex(reason)) : ''
}`,
),
)
this.ws.removeAllListeners()
this.ws = null
if (code === undefined) {
// Useful to keep this code for debugging purposes.
// const reasonText = reason ? reason.toString() : 'undefined'
// // eslint-disable-next-line no-console -- The error is helpful for debugging.
// console.error(
// `Disconnected but the disconnect code was undefined (The given reason was ${reasonText}).` +
// `This could be caused by an exception being thrown during a 'connect' callback. ` +
// `Disconnecting with code 1011 to indicate an internal error has occurred.`,
// )
/*
* Error code 1011 represents an Internal Error according to
* https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code
*/
const internalErrorCode = 1011
this.emit('disconnected', internalErrorCode)
} else {
this.emit('disconnected', code)
}
/*
* If this wasn't a manual disconnect, then lets reconnect ASAP.
* Code can be undefined if there's an exception while connecting.
*/
if (code !== INTENTIONAL_DISCONNECT_CODE && code !== undefined) {
this.intentionalDisconnect()
}
})
// Finalize the connection and resolve all awaiting connect() requests
try {
this.retryConnectionBackoff.reset()
this.startHeartbeatInterval()
this.connectionManager.resolveAllAwaiting()
this.emit('connected')
} catch (error) {
if (error instanceof Error) {
this.connectionManager.rejectAllAwaiting(error)
// Ignore this error, propagate the root cause.
// eslint-disable-next-line @typescript-eslint/no-empty-function -- Need empty catch
await this.disconnect().catch(() => {})
}
}
}
private intentionalDisconnect(): void {
const retryTimeout = this.retryConnectionBackoff.duration()
this.trace('reconnect', `Retrying connection in ${retryTimeout}ms.`)
this.emit('reconnecting', this.retryConnectionBackoff.attempts)
/*
* Start the reconnect timeout, but set it to `this.reconnectTimeoutID`
* so that we can cancel one in-progress on disconnect.
*/
this.reconnectTimeoutID = setTimeout(() => {
this.reconnect().catch((error: Error) => {
this.emit('error', 'reconnect', error.message, error)
})
}, retryTimeout)
}
/**
* Clears the heartbeat connection interval.
*/
private clearHeartbeatInterval(): void {
if (this.heartbeatIntervalID) {
clearInterval(this.heartbeatIntervalID)
}
}
/**
* Starts a heartbeat to check the connection with the server.
*
*/
private startHeartbeatInterval(): void {
this.clearHeartbeatInterval()
this.heartbeatIntervalID = setInterval(() => {
void this.heartbeat()
}, this.config.timeout)
}
/**
* A heartbeat is just a "ping" command, sent on an interval.
* If this succeeds, we're good. If it fails, disconnect so that the consumer can reconnect, if desired.
*
* @returns A Promise that resolves to void when the heartbeat returns successfully.
*/
private async heartbeat(): Promise<void> {
this.request({ command: 'ping' }).catch(async () => {
return this.reconnect().catch((error: Error) => {
this.emit('error', 'reconnect', error.message, error)
})
})
}
/**
* Process a failed connection.
*
* @param errorOrCode - (Optional) Error or code for connection failure.
*/
private onConnectionFailed(errorOrCode: Error | number | null): void {
if (this.ws) {
this.ws.removeAllListeners()
this.ws.on('error', () => {
/*
* Correctly listen for -- but ignore -- any future errors: If you
* don't have a listener on "error" node would log a warning on error.
*/
})
this.ws.close()
this.ws = null
}
if (typeof errorOrCode === 'number') {
this.connectionManager.rejectAllAwaiting(
new NotConnectedError(`Connection failed with code ${errorOrCode}.`, {
code: errorOrCode,
}),
)
} else if (errorOrCode?.message) {
this.connectionManager.rejectAllAwaiting(
new NotConnectedError(errorOrCode.message, errorOrCode),
)
} else {
this.connectionManager.rejectAllAwaiting(
new NotConnectedError('Connection failed.'),
)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,176 @@
import BigNumber from 'bignumber.js'
import { decode } from 'xahau-binary-codec'
import type {
TransactionEntryResponse,
TransactionStream,
TransactionV1Stream,
TxResponse,
} from '..'
import type { Amount, APIVersion, DEFAULT_API_VERSION } from '../models/common'
import type { RequestResponseMap } from '../models/methods'
import { AccountTxVersionResponseMap } from '../models/methods/accountTx'
import { BaseRequest, BaseResponse } from '../models/methods/baseMethod'
import { PaymentFlags, Transaction } from '../models/transactions'
import type { TransactionMetadata } from '../models/transactions/metadata'
import { isFlagEnabled } from '../models/utils'
const WARN_PARTIAL_PAYMENT_CODE = 2001
function amountsEqual(amt1: Amount, amt2: Amount): boolean {
if (typeof amt1 === 'string' && typeof amt2 === 'string') {
return amt1 === amt2
}
if (typeof amt1 === 'string' || typeof amt2 === 'string') {
return false
}
const aValue = new BigNumber(amt1.value)
const bValue = new BigNumber(amt2.value)
return (
amt1.currency === amt2.currency &&
amt1.issuer === amt2.issuer &&
aValue.isEqualTo(bValue)
)
}
function isPartialPayment(
tx?: Transaction,
metadata?: TransactionMetadata | string,
): boolean {
if (tx == null || metadata == null || tx.TransactionType !== 'Payment') {
return false
}
let meta = metadata
if (typeof meta === 'string') {
if (meta === 'unavailable') {
return false
}
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- binary-codec typing */
meta = decode(meta) as unknown as TransactionMetadata
}
const tfPartial =
typeof tx.Flags === 'number'
? isFlagEnabled(tx.Flags, PaymentFlags.tfPartialPayment)
: tx.Flags?.tfPartialPayment
if (!tfPartial) {
return false
}
const delivered = meta.delivered_amount
// eslint-disable-next-line @typescript-eslint/ban-ts-comment -- DeliverMax is a valid field on Payment response
// @ts-expect-error -- DeliverMax is a valid field on Payment response
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- DeliverMax is a valid field on Payment response
const amount = tx.DeliverMax
if (delivered === undefined) {
return false
}
return !amountsEqual(delivered, amount)
}
function txHasPartialPayment(response: TxResponse): boolean {
return isPartialPayment(response.result.tx_json, response.result.meta)
}
function txEntryHasPartialPayment(response: TransactionEntryResponse): boolean {
return isPartialPayment(response.result.tx_json, response.result.metadata)
}
function accountTxHasPartialPayment<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
>(response: AccountTxVersionResponseMap<Version>): boolean {
const { transactions } = response.result
const foo = transactions.some((tx) => {
if (tx.tx_json != null) {
const transaction = tx
return isPartialPayment(transaction.tx_json, transaction.meta)
}
const transaction = tx
return isPartialPayment(transaction.tx, transaction.meta)
})
return foo
}
function hasPartialPayment<
R extends BaseRequest,
V extends APIVersion = typeof DEFAULT_API_VERSION,
T = RequestResponseMap<R, V>,
>(command: string, response: T): boolean {
/* eslint-disable @typescript-eslint/consistent-type-assertions -- Request type is known at runtime from command */
switch (command) {
case 'tx':
return txHasPartialPayment(response as TxResponse)
case 'transaction_entry':
return txEntryHasPartialPayment(response as TransactionEntryResponse)
case 'account_tx':
return accountTxHasPartialPayment(
response as AccountTxVersionResponseMap<V>,
)
default:
return false
}
/* eslint-enable @typescript-eslint/consistent-type-assertions */
}
/**
* Checks a response for a partial payment.
*
* @param command - Command from the request, tells us what response to expect.
* @param response - Response to check for a partial payment.
*/
export function handlePartialPayment<
R extends BaseRequest,
T = RequestResponseMap<R, APIVersion>,
>(command: string, response: T): void {
if (hasPartialPayment(command, response)) {
// 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,
message: 'This response contains a Partial Payment',
}
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
}
}
/**
* Check a transaction from a subscription stream for partial payment.
*
* @param stream - Stream Transaction to check for partial payment.
* @param log - The method used for logging by the connection (to report the partial payment).
*/
export function handleStreamPartialPayment(
stream: TransactionStream | TransactionV1Stream,
log: (id: string, message: string) => void,
): void {
if (isPartialPayment(stream.tx_json ?? stream.transaction, stream.meta)) {
const warnings = stream.warnings ?? []
const warning = {
id: WARN_PARTIAL_PAYMENT_CODE,
message: 'This response contains a Partial Payment',
}
warnings.push(warning)
/* eslint-disable-next-line no-param-reassign -- Handles the case where there are no warnings */
stream.warnings = warnings
log('Partial payment received', JSON.stringify(stream))
}
}

View File

@@ -0,0 +1,159 @@
/* eslint-disable max-classes-per-file -- Errors can be defined in the same file */
/**
* Base Error class for xrpl.js. All Errors thrown by xrpl.js should throw
* XahlErrors.
*
* @category Errors
*/
class XahlError extends Error {
public readonly name: string
public readonly message: string
public readonly data?: unknown
/**
* Construct an XahlError.
*
* @param message - The error message.
* @param data - The data that caused the error.
*/
public constructor(message = '', data?: unknown) {
super(message)
this.name = this.constructor.name
this.message = message
this.data = data
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- `captureStackTrace` can be null in browsers
if (Error.captureStackTrace != null) {
Error.captureStackTrace(this, this.constructor)
}
}
/**
* Converts the Error to a human-readable String form.
*
* @returns The String output of the Error.
*/
public toString(): string {
let result = `[${this.name}(${this.message}`
if (this.data) {
result += `, ${JSON.stringify(this.data)}`
}
result += ')]'
return result
}
/**
* Console.log in node uses util.inspect on object, and util.inspect allows
* us to customize its output:
* https://nodejs.org/api/util.html#util_custom_inspect_function_on_objects.
*
* @returns The String output of the Error.
*/
public inspect(): string {
return this.toString()
}
}
/**
* Error thrown when xahaud responds with an error.
*
* @category Errors
*/
class XahaudError extends XahlError {}
/**
* Error thrown when xrpl.js cannot specify error type.
*
* @category Errors
*/
class UnexpectedError extends XahlError {}
/**
* Error thrown when xrpl.js has an error with connection to xahaud.
*
* @category Errors
*/
class ConnectionError extends XahlError {}
/**
* Error thrown when xrpl.js is not connected to xahaud server.
*
* @category Errors
*/
class NotConnectedError extends ConnectionError {}
/**
* Error thrown when xrpl.js has disconnected from xahaud server.
*
* @category Errors
*/
class DisconnectedError extends ConnectionError {}
/**
* Error thrown when xahaud is not initialized.
*
* @category Errors
*/
class XahaudNotInitializedError extends ConnectionError {}
/**
* Error thrown when xrpl.js times out.
*
* @category Errors
*/
class TimeoutError extends ConnectionError {}
/**
* Error thrown when xrpl.js sees a response in the wrong format.
*
* @category Errors
*/
class ResponseFormatError extends ConnectionError {}
/**
* Error thrown when xrpl.js sees a malformed transaction.
*
* @category Errors
*/
class ValidationError extends XahlError {}
/**
* Error thrown when a client cannot generate a wallet from the testnet/devnet
* faucets, or when the client cannot infer the faucet URL (i.e. when the Client
* is connected to mainnet).
*
* @category Errors
*/
class XRPLFaucetError extends XahlError {}
/**
* Error thrown when xrpl.js cannot retrieve a transaction, ledger, account, etc.
* From xahaud.
*
* @category Errors
*/
class NotFoundError extends XahlError {
/**
* Construct an XahlError.
*
* @param message - The error message. Defaults to "Not found".
*/
public constructor(message = 'Not found') {
super(message)
}
}
export {
XahlError,
UnexpectedError,
ConnectionError,
XahaudError,
NotConnectedError,
DisconnectedError,
XahaudNotInitializedError,
TimeoutError,
ResponseFormatError,
ValidationError,
NotFoundError,
XRPLFaucetError,
}

View File

@@ -0,0 +1,18 @@
export { Client, ClientOptions } from './client'
export * from './models'
export * from './utils'
export { default as ECDSA } from './ECDSA'
export * from './errors'
export { FundingOptions } from './Wallet/fundWallet'
export { Wallet } from './Wallet'
export { walletFromSecretNumbers } from './Wallet/walletFromSecretNumbers'
export { keyToRFC1751Mnemonic, rfc1751MnemonicToKey } from './Wallet/rfc1751'
export * from './Wallet/signer'

View File

@@ -0,0 +1,198 @@
export const XAHAUD_API_V1 = 1
export const XAHAUD_API_V2 = 2
export const DEFAULT_API_VERSION = XAHAUD_API_V1
export type APIVersion = typeof XAHAUD_API_V1 | typeof XAHAUD_API_V2
export type LedgerIndex = number | ('validated' | 'closed' | 'current')
export interface XAH {
currency: 'XAH'
issuer?: never
}
export interface IssuedCurrency {
currency: string
issuer: string
}
export type Currency = IssuedCurrency | XAH
export interface IssuedCurrencyAmount extends IssuedCurrency {
value: string
}
export type Amount = IssuedCurrencyAmount | string
export interface Balance {
currency: string
issuer?: string
value: string
}
export interface Signer {
Signer: {
Account: string
TxnSignature: string
SigningPubKey: string
}
}
export interface Memo {
Memo: {
MemoData?: string
MemoType?: string
MemoFormat?: string
}
}
export type StreamType =
| 'consensus'
| 'ledger'
| 'manifests'
| 'peer_status'
| 'transactions'
| 'transactions_proposed'
| 'server'
| 'validations'
export interface PathStep {
account?: string
currency?: string
issuer?: string
}
export type Path = PathStep[]
/**
* The object that describes the signer in SignerEntries.
*/
export interface SignerEntry {
/**
* The object that describes the signer in SignerEntries.
*/
SignerEntry: {
/**
* An XAH Ledger address whose signature contributes to the multi-signature.
* It does not need to be a funded address in the ledger.
*/
Account: string
/**
* The weight of a signature from this signer.
* A multi-signature is only valid if the sum weight of the signatures provided meets
* or exceeds the signer list's SignerQuorum value.
*/
SignerWeight: number
/**
* An arbitrary 256-bit (32-byte) field that can be used to identify the signer, which
* may be useful for smart contracts, or for identifying who controls a key in a large
* organization.
*/
WalletLocator?: string
}
}
/**
* This information is added to Transactions in request responses, but is not part
* of the canonical Transaction information on ledger. These fields are denoted with
* lowercase letters to indicate this in the xahaud responses.
*/
export interface ResponseOnlyTxInfo {
/**
* The date/time when this transaction was included in a validated ledger.
*/
date?: number
/**
* An identifying hash value unique to this transaction, as a hex string.
*/
hash?: string
/**
* The sequence number of the ledger that included this transaction.
*/
ledger_index?: number
/**
* The hash of the ledger included this transaction.
*/
ledger_hash?: string
/**
* @deprecated Alias for ledger_index.
*/
inLedger?: number
}
/**
* One offer that might be returned from either an {@link NFTBuyOffersRequest}
* or an {@link NFTSellOffersRequest}.
*
* @category Responses
*/
export interface NFTOffer {
amount: Amount
flags: number
nft_offer_index: string
owner: string
destination?: string
expiration?: number
}
/**
* One NFToken that might be returned from an {@link NFTInfoResponse}
*
* @category Responses
*/
export interface NFToken {
nft_id: string
ledger_index: number
owner: string
is_burned: boolean
flags: number
transfer_fee: number
issuer: string
nft_taxon: number
nft_serial: number
uri: string
}
export interface AuthAccount {
AuthAccount: {
Account: string
}
}
export interface XChainBridge {
LockingChainDoor: string
LockingChainIssue: Currency
IssuingChainDoor: string
IssuingChainIssue: Currency
}
/**
* A PriceData object represents the price information for a token pair.
*
*/
export interface PriceData {
PriceData: {
/**
* The primary asset in a trading pair. Any valid identifier, such as a stock symbol, bond CUSIP, or currency code is allowed.
* For example, in the BTC/USD pair, BTC is the base asset; in 912810RR9/BTC, 912810RR9 is the base asset.
*/
BaseAsset: string
/**
* The quote asset in a trading pair. The quote asset denotes the price of one unit of the base asset. For example, in the
* BTC/USD pair,BTC is the base asset; in 912810RR9/BTC, 912810RR9 is the base asset.
*/
QuoteAsset: string
/**
* The asset price after applying the Scale precision level. It's not included if the last update transaction didn't include
* the BaseAsset/QuoteAsset pair.
*/
AssetPrice?: number | string
/**
* The scaling factor to apply to an asset price. For example, if Scale is 6 and original price is 0.155, then the scaled
* price is 155000. Valid scale ranges are 0-10. It's not included if the last update transaction didn't include the
* BaseAsset/QuoteAsset pair.
*/
Scale?: number
}
}

View File

@@ -0,0 +1,17 @@
/**
* LedgerEntry type definitions are exported in their own namespace to prevent
* collisions of the DepositPreauth SLE and Transaction. LedgerEntries are used
* by the client less often, and in most scenarios, like when parsing a
* response, the client won't need to import the type. If it is required to use
* a Ledger Entry, import `LedgerEntry`, and access individual ledger entry
* types on the `LedgerEntry` namespace.
*/
export * as LedgerEntry from './ledger'
export {
setTransactionFlagsToNumber,
parseAccountRootFlags,
parseTransactionFlags,
} from './utils/flags'
export * from './methods'
export * from './transactions'
export * from './common'

View File

@@ -0,0 +1,213 @@
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
/**
* The AccountRoot object type describes a single account, its settings, and
* XAH balance.
*
* @category Ledger Entries
*/
export default interface AccountRoot extends BaseLedgerEntry, HasPreviousTxnID {
LedgerEntryType: 'AccountRoot'
/** The identifying (classic) address of this account. */
Account: string
/** The account's current XAH balance in drops, represented as a string. */
Balance: string
/** A bit-map of boolean flags enabled for this account. */
Flags: number
/**
* The number of objects this account owns in the ledger, which contributes
* to its owner reserve.
*/
OwnerCount: number
/** The sequence number of the next valid transaction for this account. */
Sequence: number
/**
* The identifying hash of the transaction most recently sent by this
* account. This field must be enabled to use the AccountTxnID transaction
* field. To enable it, send an AccountSet transaction with the.
* `asfAccountTxnID` flag enabled.
*/
AccountTxnID?: string
/**
* The ledger entry ID of the corresponding AMM ledger entry.
* Set during account creation; cannot be modified.
* If present, indicates that this is a special AMM AccountRoot; always omitted on non-AMM accounts.
*/
AMMID?: string
/**
* A domain associated with this account. In JSON, this is the hexadecimal
* for the ASCII representation of the domain.
*/
Domain?: string
/** The md5 hash of an email address. */
EmailHash?: string
/**
* A public key that may be used to send encrypted messages to this account
* in JSON, uses hexadecimal.
*/
MessageKey?: string
/**
* The address of a key pair that can be used to sign transactions for this
* account instead of the master key. Use a SetRegularKey transaction to
* change this value.
*/
RegularKey?: string
/**
* How many Tickets this account owns in the ledger. This is updated
* automatically to ensure that the account stays within the hard limit of 250.
* Tickets at a time.
*/
TicketCount?: number
/**
* How many significant digits to use for exchange rates of Offers involving
* currencies issued by this address. Valid values are 3 to 15, inclusive.
*/
TickSize?: number
/**
* A transfer fee to charge other users for sending currency issued by this
* account to each other.
*/
TransferRate?: number
/** An arbitrary 256-bit value that users can set. */
WalletLocator?: string
/** Total NFTokens this account's issued that have been burned. This number is always equal or less than MintedNFTokens. */
BurnedNFTokens?: number
/** The sequence that the account first minted an NFToken */
FirstNFTSequence: number
/** Total NFTokens have been minted by and on behalf of this account. */
MintedNFTokens?: number
/** Another account that can mint NFTokens on behalf of this account. */
NFTokenMinter?: string
}
/**
* A boolean map of AccountRootFlags for simplified code checking AccountRoot settings.
* For submitting settings flags to the ledger, use AccountRootFlags instead.
*/
export interface AccountRootFlagsInterface {
/**
* The account has used its free SetRegularKey transaction.
*/
lsfPasswordSpent?: boolean
/**
* Requires incoming payments to specify a Destination Tag.
*/
lsfRequireDestTag?: boolean
/**
* This account must individually approve other users for those users to hold this account's issued currencies.
*/
lsfRequireAuth?: boolean
/**
* Client applications should not send XAH to this account. Not enforced by xahaud.
*/
lsfDisallowXAH?: boolean
/**
* Disallows use of the master key to sign transactions for this account.
*/
lsfDisableMaster?: boolean
/**
* This address cannot freeze trust lines connected to it. Once enabled, cannot be disabled.
*/
lsfNoFreeze?: boolean
/**
* All assets issued by this address are frozen.
*/
lsfGlobalFreeze?: boolean
/**
* Enable rippling on this address's trust lines by default. Required for issuing addresses; discouraged for others.
*/
lsfDefaultRipple?: boolean
/**
* This account can only receive funds from transactions it sends, and from preauthorized accounts.
* (It has DepositAuth enabled.)
*/
lsfDepositAuth?: boolean
/**
* This account is an Automated Market Maker (AMM) instance.
*/
lsfAMM?: boolean
/**
* Disallow incoming NFTOffers from other accounts.
*/
lsfDisallowIncomingNFTokenOffer?: boolean
/**
* Disallow incoming Checks from other accounts.
*/
lsfDisallowIncomingCheck?: boolean
/**
* Disallow incoming PayChannels from other accounts.
*/
lsfDisallowIncomingPayChan?: boolean
/**
* Disallow incoming Trustlines from other accounts.
*/
lsfDisallowIncomingTrustline?: boolean
/**
* This address can claw back issued IOUs. Once enabled, cannot be disabled.
*/
lsfAllowTrustLineClawback?: boolean
}
export enum AccountRootFlags {
/**
* The account has used its free SetRegularKey transaction.
*/
lsfPasswordSpent = 0x00010000,
/**
* Requires incoming payments to specify a Destination Tag.
*/
lsfRequireDestTag = 0x00020000,
/**
* This account must individually approve other users for those users to hold this account's issued currencies.
*/
lsfRequireAuth = 0x00040000,
/**
* Client applications should not send XAH to this account. Not enforced by xahaud.
*/
lsfDisallowXAH = 0x00080000,
/**
* Disallows use of the master key to sign transactions for this account.
*/
lsfDisableMaster = 0x00100000,
/**
* This address cannot freeze trust lines connected to it. Once enabled, cannot be disabled.
*/
lsfNoFreeze = 0x00200000,
/**
* All assets issued by this address are frozen.
*/
lsfGlobalFreeze = 0x00400000,
/**
* Enable rippling on this address's trust lines by default. Required for issuing addresses; discouraged for others.
*/
lsfDefaultRipple = 0x00800000,
/**
* This account can only receive funds from transactions it sends, and from preauthorized accounts.
* (It has DepositAuth enabled.)
*/
lsfDepositAuth = 0x01000000,
/**
* This account is an Automated Market Maker (AMM) instance.
*/
lsfAMM = 0x02000000,
/**
* Disallow incoming NFTOffers from other accounts.
*/
lsfDisallowIncomingNFTokenOffer = 0x04000000,
/**
* Disallow incoming Checks from other accounts.
*/
lsfDisallowIncomingCheck = 0x08000000,
/**
* Disallow incoming PayChannels from other accounts.
*/
lsfDisallowIncomingPayChan = 0x10000000,
/**
* Disallow incoming Trustlines from other accounts.
*/
lsfDisallowIncomingTrustline = 0x20000000,
/**
* This address can claw back issued IOUs. Once enabled, cannot be disabled.
*/
lsfAllowTrustLineClawback = 0x80000000,
}

View File

@@ -0,0 +1,47 @@
import { BaseLedgerEntry, HasOptionalPreviousTxnID } from './BaseLedgerEntry'
/**
* The unique id for the Amendments object https://xrpl.org/amendments-object.html#amendments-id-format
*/
export const AMENDMENTS_ID =
'7DB0788C020F02780A673DC74757F23823FA3014C1866E72CC4CD8B226CD6EF4'
export interface Majority {
Majority: {
/** The Amendment ID of the pending amendment. */
Amendment: string
/**
* The `close_time` field of the ledger version where this amendment most
* recently gained a majority.
*/
CloseTime: number
}
}
/**
* The Amendments object type contains a list of Amendments that are currently
* active.
*
* @category Ledger Entries
*/
export default interface Amendments
extends BaseLedgerEntry,
HasOptionalPreviousTxnID {
LedgerEntryType: 'Amendments'
/**
* Array of 256-bit amendment IDs for all currently-enabled amendments. If
* omitted, there are no enabled amendments.
*/
Amendments?: string[]
/**
* Array of objects describing the status of amendments that have majority
* support but are not yet enabled. If omitted, there are no pending
* amendments with majority support.
*/
Majorities?: Majority[]
/**
* A bit-map of boolean flags. No flags are defined for the Amendments object
* type, so this value is always 0.
*/
Flags: 0
}

View File

@@ -0,0 +1,31 @@
export interface BaseLedgerEntry {
index: string
}
export interface HasPreviousTxnID {
/**
* The identifying hash of the transaction that most recently modified this
* object.
*/
PreviousTxnID: string
/**
* The index of the ledger that contains the transaction that most recently
* modified this object.
*/
PreviousTxnLgrSeq: number
}
export interface HasOptionalPreviousTxnID {
/**
* The identifying hash of the transaction that most recently modified this
* object. This field was added in the `fixPreviousTxnID` amendment, so it
* may not be present in every object.
*/
PreviousTxnID?: string
/**
* The index of the ledger that contains the transaction that most recently
* modified this object. This field was added in the `fixPreviousTxnID`
* amendment, so it may not be present in every object.
*/
PreviousTxnLgrSeq?: number
}

View File

@@ -0,0 +1,70 @@
import { Amount } from '../common'
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
/**
* A Check object describes a check, similar to a paper personal check, which
* can be cashed by its destination to get money from its sender.
*
* @category Ledger Entries
*/
export default interface Check extends BaseLedgerEntry, HasPreviousTxnID {
LedgerEntryType: 'Check'
/** The sender of the Check. Cashing the Check debits this address's balance. */
Account: string
/**
* The intended recipient of the Check. Only this address can cash the Check,
* using a CheckCash transaction.
*/
Destination: string
/**
* A bit-map of boolean flags. No flags are defined for Checks, so this value
* is always 0.
*/
Flags: 0
/**
* A hint indicating which page of the sender's owner directory links to this
* object, in case the directory consists of multiple pages.
*/
OwnerNode: string
/**
* The identifying hash of the transaction that most recently modified this
* object.
*/
PreviousTxnID: string
/**
* The index of the ledger that contains the transaction that most recently
* modified this object.
*/
PreviousTxnLgrSeq: number
/**
* The maximum amount of currency this Check can debit the sender. If the
* Check is successfully cashed, the destination is credited in the same
* currency for up to this amount.
*/
SendMax: Amount
/** The sequence number of the CheckCreate transaction that created this check. */
Sequence: number
/**
* A hint indicating which page of the destination's owner directory links to
* this object, in case the directory consists of multiple pages.
*/
DestinationNode?: string
/**
* An arbitrary tag to further specify the destination for this Check, such
* as a hosted recipient at the destination address.
*/
DestinationTag?: number
/** Indicates the time after which this Check is considered expired. */
Expiration?: number
/**
* Arbitrary 256-bit hash provided by the sender as a specific reason or
* identifier for this Check.
*/
InvoiceID?: string
/**
* An arbitrary tag to further specify the source for this Check, such as a
* hosted recipient at the sender's address.
*/
SourceTag?: number
}

View File

@@ -0,0 +1,27 @@
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
/**
* A DepositPreauth object tracks a preauthorization from one account to
* another. DepositPreauth transactions create these objects.
*
* @category Ledger Entries
*/
export default interface DepositPreauth
extends BaseLedgerEntry,
HasPreviousTxnID {
LedgerEntryType: 'DepositPreauth'
/** The account that granted the preauthorization. */
Account: string
/** The account that received the preauthorization. */
Authorize: string
/**
* A bit-map of boolean flags. No flags are defined for DepositPreauth
* objects, so this value is always 0.
*/
Flags: 0
/**
* A hint indicating which page of the sender's owner directory links to this
* object, in case the directory consists of multiple pages.
*/
OwnerNode: string
}

View File

@@ -0,0 +1,48 @@
import { BaseLedgerEntry, HasOptionalPreviousTxnID } from './BaseLedgerEntry'
/**
* The DirectoryNode object type provides a list of links to other objects in
* the ledger's state tree.
*
* @category Ledger Entries
*/
export default interface DirectoryNode
extends BaseLedgerEntry,
HasOptionalPreviousTxnID {
LedgerEntryType: 'DirectoryNode'
/**
* A bit-map of boolean flags enabled for this directory. Currently, the
* protocol defines no flags for DirectoryNode objects.
*/
Flags: number
/** The ID of root object for this directory. */
RootIndex: string
/** The contents of this Directory: an array of IDs of other objects. */
Indexes: string[]
/**
* If this Directory consists of multiple pages, this ID links to the next
* object in the chain, wrapping around at the end.
*/
IndexNext?: number
/**
* If this Directory consists of multiple pages, this ID links to the
* previous object in the chain, wrapping around at the beginning.
*/
IndexPrevious?: number
/** The address of the account that owns the objects in this directory. */
Owner?: string
/**
* The currency code of the TakerPays amount from the offers in this
* directory.
*/
TakerPaysCurrency?: string
/** The issuer of the TakerPays amount from the offers in this directory. */
TakerPaysIssuer?: string
/**
* The currency code of the TakerGets amount from the offers in this
* directory.
*/
TakerGetsCurrency?: string
/** The issuer of the TakerGets amount from the offers in this directory. */
TakerGetsIssuer?: string
}

View File

@@ -0,0 +1,64 @@
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
/**
* The Escrow object type represents a held payment of XAH waiting to be
* executed or canceled.
*
* @category Ledger Entries
*/
export default interface Escrow extends BaseLedgerEntry, HasPreviousTxnID {
LedgerEntryType: 'Escrow'
/**
* The address of the owner (sender) of this held payment. This is the
* account that provided the XAH, and gets it back if the held payment is
* canceled.
*/
Account: string
/**
* The destination address where the XAH is paid if the held payment is
* successful.
*/
Destination: string
/** The amount of XAH, in drops, to be delivered by the held payment. */
Amount: string
/**
* A PREIMAGE-SHA-256 crypto-condition, as hexadecimal. If present, the
* EscrowFinish transaction must contain a fulfillment that satisfies this
* condition.
*/
Condition?: string
/**
* The time after which this Escrow is considered expired.
*/
CancelAfter?: number
/**
* The time, in seconds, since the Ripple Epoch, after which this held payment
* can be finished. Any EscrowFinish transaction before this time fails.
*/
FinishAfter?: number
/**
* A bit-map of boolean flags. No flags are defined for the Escrow type, so
* this value is always 0.
*/
Flags: number
/**
* An arbitrary tag to further specify the source for this held payment, such
* as a hosted recipient at the owner's address.
*/
SourceTag?: number
/**
* An arbitrary tag to further specify the destination for this held payment,
* such as a hosted recipient at the destination address.
*/
DestinationTag?: number
/**
* A hint indicating which page of the owner directory links to this object,
* in case the directory consists of multiple pages.
*/
OwnerNode: string
/**
* A hint indicating which page of the destination's owner directory links to
* this object, in case the directory consists of multiple pages.
*/
DestinationNode?: string
}

View File

@@ -0,0 +1,54 @@
import { BaseLedgerEntry, HasOptionalPreviousTxnID } from './BaseLedgerEntry'
/**
* The unique id for the FeeSettings object https://xrpl.org/feesettings.html#feesettings-id-format
*/
export const FEE_SETTINGS_ID =
'4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A651'
export interface FeeSettingsPreAmendmentFields {
/** The transaction cost of the "reference transaction" in drops of XAH as hexadecimal. */
BaseFee: string
/** The BaseFee translated into "fee units". */
ReferenceFeeUnits: number
/** The base reserve for an account in the XAH Ledger, as drops of XAH. */
ReserveBase: number
/** The incremental owner reserve for owning objects, as drops of XAH. */
ReserveIncrement: number
}
export interface FeeSettingsPostAmendmentFields {
/** The transaction cost of the "reference transaction" in drops of XAH as hexadecimal. */
BaseFeeDrops: string
/** The base reserve for an account in the XAH Ledger, as drops of XAH. */
ReserveBaseDrops: string
/** The incremental owner reserve for owning objects, as drops of XAH. */
ReserveIncrementDrops: string
}
export interface FeeSettingsBase
extends BaseLedgerEntry,
HasOptionalPreviousTxnID {
LedgerEntryType: 'FeeSettings'
/**
* A bit-map of boolean flags for this object. No flags are defined for this type.
*/
Flags: 0
}
/**
* The FeeSettings object type contains the current base transaction cost and
* reserve amounts as determined by fee voting.
*
* The fields will be based on the status of the `XAHFees` amendment.
* - Before: {@link FeeSettingsPreAmendmentFields}
* - After: {@link FeeSettingsPostAmendmentFields}
*
* @interface
*
* @category Ledger Entries
*/
type FeeSettings = FeeSettingsBase &
(FeeSettingsPreAmendmentFields | FeeSettingsPostAmendmentFields)
export default FeeSettings

View File

@@ -0,0 +1,107 @@
import { APIVersion, DEFAULT_API_VERSION, XAHAUD_API_V1 } from '../common'
import { Transaction, TransactionMetadata } from '../transactions'
import { LedgerEntry } from './LedgerEntry'
/**
* Common properties for ledger entries.
*
* @category Ledger Entries
*/
interface BaseLedger {
/** The SHA-512Half of this ledger's state tree information. */
account_hash: string
/** 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
/**
* The approximate time this ledger version closed, as the number of seconds
* since the Ripple Epoch of 2000-01-01 00:00:00. This value is rounded based
* on the close_time_resolution.
*/
close_time: number
/**
* The approximate time this ledger was closed, in human-readable format.
* Always uses the UTC time zone.
*/
close_time_human: string
/**
* An integer in the range [2,120] indicating the maximum number of seconds
* by which the close_time could be rounded.
*/
close_time_resolution: number
/**
* The approximate time this ledger was closed, in date time string format.
* Always uses the UTC time zone.
*/
close_time_iso: string
/** Whether or not this ledger has been closed. */
closed: boolean
/**
* The SHA-512Half of this ledger version. This serves as a unique identifier
* for this ledger and all its contents.
*/
ledger_hash: string
/** The approximate time at which the previous ledger was closed. */
parent_close_time: number
/**
* Unique identifying hash of the ledger that came immediately before this
* one.
*/
parent_hash: string
/** Total number of XAH drops in the network, as a quoted integer. */
total_coins: string
/** Hash of the transaction information included in this ledger, as hex. */
transaction_hash: string
/**
* Transactions applied in this ledger version. By default, members are the
* transactions' identifying Hash strings. If the request specified expand as
* true, members are full representations of the transactions instead, in
* either JSON or binary depending on whether the request specified binary
* as true.
*/
transactions?: Array<
Transaction & {
hash: string
metaData?: TransactionMetadata
}
>
}
/**
* A ledger is a block of transactions and shared state data. It has a unique
* header that describes its contents using cryptographic hashes.
*
* @category Ledger Entries
*/
export interface Ledger extends BaseLedger {
/**
* The ledger index of the ledger. Represented as a number.
*/
ledger_index: number
}
/**
* A ledger is a block of transactions and shared state data. It has a unique
* header that describes its contents using cryptographic hashes. This is used
* in api_version 1.
*
* @category Ledger Entries
*/
export interface LedgerV1 extends BaseLedger {
/**
* The ledger index of the ledger. Some API methods display this as a quoted
* integer; some display it as a number.
*/
ledger_index: string
}
/**
* Type to map between the API version and the Ledger type.
*
* @category Responses
*/
export type LedgerVersionMap<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> = Version extends typeof XAHAUD_API_V1 ? LedgerV1 : Ledger

View File

@@ -0,0 +1,47 @@
import AccountRoot from './AccountRoot'
import Amendments from './Amendments'
import Check from './Check'
import DepositPreauth from './DepositPreauth'
import DirectoryNode from './DirectoryNode'
import Escrow from './Escrow'
import FeeSettings from './FeeSettings'
import LedgerHashes from './LedgerHashes'
import NegativeUNL from './NegativeUNL'
import Offer from './Offer'
import PayChannel from './PayChannel'
import RippleState from './RippleState'
import SignerList from './SignerList'
import Ticket from './Ticket'
type LedgerEntry =
| AccountRoot
| Amendments
| Check
| DepositPreauth
| DirectoryNode
| Escrow
| FeeSettings
| LedgerHashes
| NegativeUNL
| Offer
| PayChannel
| RippleState
| SignerList
| Ticket
type LedgerEntryFilter =
| 'account'
| 'amendments'
| 'check'
| 'deposit_preauth'
| 'directory'
| 'escrow'
| 'fee'
| 'hashes'
| 'offer'
| 'payment_channel'
| 'signer_list'
| 'state'
| 'ticket'
export { LedgerEntry, LedgerEntryFilter }

View File

@@ -0,0 +1,24 @@
import { BaseLedgerEntry } from './BaseLedgerEntry'
/**
* The LedgerHashes objects exist to make it possible to look up a previous
* ledger's hash with only the current ledger version and at most one lookup of
* a previous ledger version.
*
* @category Ledger Entries
*/
export default interface LedgerHashes extends BaseLedgerEntry {
LedgerEntryType: 'LedgerHashes'
/** The Ledger Index of the last entry in this object's Hashes array. */
LastLedgerSequence?: number
/**
* An array of up to 256 ledger hashes. The contents depend on which sub-type
* of LedgerHashes object this is.
*/
Hashes: string[]
/**
* A bit-map of boolean flags for this object. No flags are defined for this
* type.
*/
Flags: number
}

View File

@@ -0,0 +1,14 @@
import { Amount } from '../common'
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
export interface NFTokenOffer extends BaseLedgerEntry, HasPreviousTxnID {
LedgerEntryType: 'NFTokenOffer'
Amount: Amount
Destination?: string
Expiration: number
Flags: number
NFTokenOfferNode?: string
Owner: string
OwnerNode?: string
}

View File

@@ -0,0 +1,18 @@
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
export interface NFToken {
NFToken: {
Flags: number
Issuer: string
NFTokenID: string
NFTokenTaxon: number
URI?: string
}
}
export interface NFTokenPage extends BaseLedgerEntry, HasPreviousTxnID {
LedgerEntryType: 'NFTokenPage'
NextPageMin?: string
NFTokens: NFToken[]
PreviousPageMin?: string
}

View File

@@ -0,0 +1,36 @@
import { BaseLedgerEntry, HasOptionalPreviousTxnID } from './BaseLedgerEntry'
/**
* The unique id for the nUNL object https://xrpl.org/negativeunl.html#negativeunl-id-format
*/
export const NEGATIVE_UNL_ID =
'2E8A59AA9D3B5B186B0B9E0F62E6C02587CA74A4D778938E957B6357D364B244'
/**
* The NegativeUNL object type contains the current status of the Negative UNL,
* a list of trusted validators currently believed to be offline.
*
* @category Ledger Entries
*/
export default interface NegativeUNL
extends BaseLedgerEntry,
HasOptionalPreviousTxnID {
LedgerEntryType: 'NegativeUNL'
/**
* A list of trusted validators that are currently disabled.
*/
DisabledValidators?: Array<{
FirstLedgerSequence: number
PublicKey: string
}>
/**
* The public key of a trusted validator that is scheduled to be disabled in
* the next flag ledger.
*/
ValidatorToDisable?: string
/**
* The public key of a trusted validator in the Negative UNL that is
* scheduled to be re-enabled in the next flag ledger.
*/
ValidatorToReEnable?: string
}

View File

@@ -0,0 +1,42 @@
import { Amount } from '../common'
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
export default interface Offer extends BaseLedgerEntry, HasPreviousTxnID {
LedgerEntryType: 'Offer'
/** A bit-map of boolean flags enabled for this Offer. */
Flags: number
/** The address of the account that placed this Offer. */
Account: string
/**
* The Sequence value of the OfferCreate transaction that created this Offer
* object. Used in combination with the Account to identify this Offer.
*/
Sequence: number
/** The remaining amount and type of currency requested by the Offer creator. */
TakerPays: Amount
/**
* The remaining amount and type of currency being provided by the Offer
* creator.
*/
TakerGets: Amount
/** The ID of the Offer Directory that links to this Offer. */
BookDirectory: string
/**
* A hint indicating which page of the Offer Directory links to this object,
* in case the directory consists of multiple pages.
*/
BookNode: string
/**
* A hint indicating which page of the Owner Directory links to this object,
* in case the directory consists of multiple pages.
*/
OwnerNode: string
/** The time this Offer expires, in seconds since the Ripple Epoch. */
Expiration?: number
}
export enum OfferFlags {
lsfPassive = 0x00010000,
lsfSell = 0x00020000,
}

View File

@@ -0,0 +1,97 @@
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
/**
* The PayChannel object type represents a payment channel. Payment channels
* enable small, rapid off-ledger payments of XAH that can be later reconciled
* with the consensus ledger. A payment channel holds a balance of XAH that can
* only be paid out to a specific destination address until the channel is
* closed.
*
* @category Ledger Entries
*/
export default interface PayChannel extends BaseLedgerEntry, HasPreviousTxnID {
LedgerEntryType: 'PayChannel'
/**
* The source address that owns this payment channel. This comes from the
* sending address of the transaction that created the channel.
*/
Account: string
/**
* The destination address for this payment channel. While the payment
* channel is open, this address is the only one that can receive XAH from the
* channel. This comes from the Destination field of the transaction that
* created the channel.
*/
Destination: string
/**
* Total XAH, in drops, that has been allocated to this channel. This
* includes XAH that has been paid to the destination address. This is
* initially set by the transaction that created the channel and can be
* increased if the source address sends a PaymentChannelFund transaction.
*/
Amount: string
/**
* Total XAH, in drops, already paid out by the channel. The difference
* between this value and the Amount field is how much XAH can still be paid
* to the destination address with PaymentChannelClaim transactions. If the
* channel closes, the remaining difference is returned to the source address.
*/
Balance: string
/**
* Public key, in hexadecimal, of the key pair that can be used to sign
* claims against this channel. This can be any valid secp256k1 or Ed25519
* public key. This is set by the transaction that created the channel and
* must match the public key used in claims against the channel. The channel
* source address can also send XAH from this channel to the destination
* without signed claims.
*/
PublicKey: string
/**
* Number of seconds the source address must wait to close the channel if
* it still has any XAH in it. Smaller values mean that the destination
* address has less time to redeem any outstanding claims after the source
* address requests to close the channel. Can be any value that fits in a
* 32-bit unsigned integer (0 to 2^32-1). This is set by the transaction that
* creates the channel.
*/
SettleDelay: number
/**
* A hint indicating which page of the source address's owner directory links
* to this object, in case the directory consists of multiple pages.
*/
OwnerNode: string
/**
* A bit-map of boolean flags enabled for this payment channel. Currently,
* the protocol defines no flags for PayChannel objects.
*/
Flags: number
/**
* The mutable expiration time for this payment channel, in seconds since the
* Ripple Epoch. The channel is expired if this value is present and smaller
* than the previous ledger's close_time field. See Setting Channel Expiration
* for more details.
*/
Expiration?: number
/**
* The immutable expiration time for this payment channel, in seconds since
* the Ripple Epoch. This channel is expired if this value is present and
* smaller than the previous ledger's close_time field. This is optionally
* set by the transaction that created the channel, and cannot be changed.
*/
CancelAfter?: number
/**
* An arbitrary tag to further specify the source for this payment channel
* useful for specifying a hosted recipient at the owner's address.
*/
SourceTag?: number
/**
* An arbitrary tag to further specify the destination for this payment
* channel, such as a hosted recipient at the destination address.
*/
DestinationTag?: number
/**
* A hint indicating which page of the destination's owner directory links to
* this object, in case the directory consists of multiple pages.
*/
DestinationNode?: string
}

View File

@@ -0,0 +1,80 @@
import { IssuedCurrencyAmount } from '../common'
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
/**
* The RippleState object type connects two accounts in a single currency.
*
* @category Ledger Entries
*/
export default interface RippleState extends BaseLedgerEntry, HasPreviousTxnID {
LedgerEntryType: 'RippleState'
/** A bit-map of boolean options enabled for this object. */
Flags: number
/**
* The balance of the trust line, from the perspective of the low account. A
* negative balance indicates that the low account has issued currency to the
* high account. The issuer is always the neutral value ACCOUNT_ONE.
*/
Balance: IssuedCurrencyAmount
/**
* The limit that the low account has set on the trust line. The issuer is
* the address of the low account that set this limit.
*/
LowLimit: IssuedCurrencyAmount
/**
* The limit that the high account has set on the trust line. The issuer is
* the address of the high account that set this limit.
*/
HighLimit: IssuedCurrencyAmount
/**
* A hint indicating which page of the low account's owner directory links to
* this object, in case the directory consists of multiple pages.
*/
LowNode?: string
/**
* A hint indicating which page of the high account's owner directory links
* to this object, in case the directory consists of multiple pages.
*/
HighNode?: string
/**
* The inbound quality set by the low account, as an integer in the implied
* ratio LowQualityIn:1,000,000,000. As a special case, the value 0 is
* equivalent to 1 billion, or face value.
*/
LowQualityIn?: number
/**
* The outbound quality set by the low account, as an integer in the implied
* ratio LowQualityOut:1,000,000,000. As a special case, the value 0 is
* equivalent to 1 billion, or face value.
*/
LowQualityOut?: number
/**
* The inbound quality set by the high account, as an integer in the implied
* ratio HighQualityIn:1,000,000,000. As a special case, the value 0 is
* equivalent to 1 billion, or face value.
*/
HighQualityIn?: number
/**
* The outbound quality set by the high account, as an integer in the implied
* ratio HighQualityOut:1,000,000,000. As a special case, the value 0 is
* equivalent to 1 billion, or face value.
*/
HighQualityOut?: number
}
export enum RippleStateFlags {
// True, if entry counts toward reserve.
lsfLowReserve = 0x00010000,
lsfHighReserve = 0x00020000,
lsfLowAuth = 0x00040000,
lsfHighAuth = 0x00080000,
lsfLowNoRipple = 0x00100000,
lsfHighNoRipple = 0x00200000,
// True, low side has set freeze flag
lsfLowFreeze = 0x00400000,
// True, high side has set freeze flag
lsfHighFreeze = 0x00800000,
// True, trust line to AMM. Used by client apps to identify payments via AMM.
lsfAMMNode = 0x01000000,
}

View File

@@ -0,0 +1,46 @@
import { SignerEntry } from '../common'
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
/**
* The SignerList object type represents a list of parties that, as a group,
* are authorized to sign a transaction in place of an individual account. You
* can create, replace, or remove a signer list using a SignerListSet
* transaction.
*
* @category Ledger Entries
*/
export default interface SignerList extends BaseLedgerEntry, HasPreviousTxnID {
LedgerEntryType: 'SignerList'
/**
* A bit-map of Boolean flags enabled for this signer list. For more
* information, see SignerList Flags.
*/
Flags: number
/**
* A hint indicating which page of the owner directory links to this object,
* in case the directory consists of multiple pages.
*/
OwnerNode: string
/**
* An array of Signer Entry objects representing the parties who are part of
* this signer list.
*/
SignerEntries: SignerEntry[]
/**
* An ID for this signer list. Currently always set to 0. If a future
* amendment allows multiple signer lists for an account, this may change.
*/
SignerListID: number
/**
* A target number for signer weights. To produce a valid signature for the
* owner of this SignerList, the signers must provide valid signatures whose
* weights sum to this value or more.
*/
SignerQuorum: number
}
export enum SignerListFlags {
// True, uses only one OwnerCount
lsfOneOwnerCount = 0x00010000,
}

View File

@@ -0,0 +1,26 @@
import { BaseLedgerEntry, HasPreviousTxnID } from './BaseLedgerEntry'
/**
* The Ticket object type represents a Ticket, which tracks an account sequence
* number that has been set aside for future use. You can create new tickets
* with a TicketCreate transaction.
*
* @category Ledger Entries
*/
export default interface Ticket extends BaseLedgerEntry, HasPreviousTxnID {
LedgerEntryType: 'Ticket'
/** The account that owns this Ticket. */
Account: string
/**
* A bit-map of Boolean flags enabled for this Ticket. Currently, there are
* no flags defined for Tickets.
*/
Flags: number
/**
* A hint indicating which page of the owner directory links to this object,
* in case the directory consists of multiple pages.
*/
OwnerNode: string
/** The Sequence Number this Ticket sets aside. */
TicketSequence: number
}

View File

@@ -0,0 +1,60 @@
import AccountRoot, {
AccountRootFlags,
AccountRootFlagsInterface,
} from './AccountRoot'
import Amendments, { Majority, AMENDMENTS_ID } from './Amendments'
import Check from './Check'
import DepositPreauth from './DepositPreauth'
import DirectoryNode from './DirectoryNode'
import Escrow from './Escrow'
import FeeSettings, {
FeeSettingsPreAmendmentFields,
FeeSettingsPostAmendmentFields,
FEE_SETTINGS_ID,
} from './FeeSettings'
import { Ledger, LedgerV1 } from './Ledger'
import { LedgerEntry, LedgerEntryFilter } from './LedgerEntry'
import LedgerHashes from './LedgerHashes'
import NegativeUNL, { NEGATIVE_UNL_ID } from './NegativeUNL'
import { NFTokenOffer } from './NFTokenOffer'
import { NFToken, NFTokenPage } from './NFTokenPage'
import Offer, { OfferFlags } from './Offer'
import PayChannel from './PayChannel'
import RippleState, { RippleStateFlags } from './RippleState'
import SignerList, { SignerListFlags } from './SignerList'
import Ticket from './Ticket'
export {
AccountRoot,
AccountRootFlags,
AccountRootFlagsInterface,
AMENDMENTS_ID,
Amendments,
Check,
DepositPreauth,
DirectoryNode,
Escrow,
FEE_SETTINGS_ID,
FeeSettings,
FeeSettingsPreAmendmentFields,
FeeSettingsPostAmendmentFields,
Ledger,
LedgerV1,
LedgerEntryFilter,
LedgerEntry,
LedgerHashes,
Majority,
NEGATIVE_UNL_ID,
NegativeUNL,
NFTokenOffer,
NFTokenPage,
NFToken,
Offer,
OfferFlags,
PayChannel,
RippleState,
RippleStateFlags,
SignerList,
SignerListFlags,
Ticket,
}

View File

@@ -0,0 +1,156 @@
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
/**
* Represents a payment channel in the XAH Ledger.
*/
export interface Channel {
/** The owner of the channel, as an Address. */
account: string
/** The total amount of XAH, in drops allocated to this channel. */
amount: string
/**
* The total amount of XAH, in drops, paid out from this channel,
* as of the ledger version used. (You can calculate the amount of
* XAH left in the channel by subtracting balance from amount.)
*/
balance: string
/**
* A unique ID for this channel, as a 64-character hexadecimal string.
* This is also the ID of the channel object in the ledger's state data.
*/
channel_id: string
/**
* The destination account of the channel, as an Address.
* Only this account can receive the XAH in the channel while it is open.
*/
destination_account: string
/**
* The number of seconds the payment channel must stay open after the owner
* of the channel requests to close it.
*/
settle_delay: number
/**
* The public key for the payment channel in the XAH Ledger's base58 format.
* Signed claims against this channel must be redeemed with the matching key pair.
*/
public_key?: string
/**
* The public key for the payment channel in hexadecimal format, if one was
* specified at channel creation. Signed claims against this channel must be
* redeemed with the matching key pair.
*/
public_key_hex?: string
/**
* Time, in seconds since the Ripple Epoch, when this channel is set to expire.
* This expiration date is mutable. If this is before the close time of the most
* recent validated ledger, the channel is expired.
*/
expiration?: number
/**
* Time, in seconds since the Ripple Epoch, of this channel's immutable expiration,
* if one was specified at channel creation. If this is before the close time of the
* most recent validated ledger, the channel is expired.
*/
cancel_after?: number
/**
* A 32-bit unsigned integer to use as a source tag for payments through this payment channel,
* if one was specified at channel creation. This indicates the payment channel's originator or
* other purpose at the source account. Conventionally, if you bounce payments from this channel,
* you should specify this value in the DestinationTag of the return payment.
*/
source_tag?: number
/**
* A 32-bit unsigned integer to use as a destination tag for payments through this channel,
* if one was specified at channel creation. This indicates the payment channel's beneficiary
* or other purpose at the destination account.
*/
destination_tag?: number
}
/**
* The account_channels method returns information about an account's Payment
* Channels. This includes only channels where the specified account is the
* channel's source, not the destination. (A channel's "source" and "owner" are
* the same.) All information retrieved is relative to a particular version of
* the ledger. Returns an {@link AccountChannelsResponse}.
*
* @category Requests
*/
export interface AccountChannelsRequest
extends BaseRequest,
LookupByLedgerRequest {
command: 'account_channels'
/**
* The unique identifier of an account, typically the account's address. The
* request returns channels where this account is the channel's owner/source.
*
*/
account: string
/**
* The unique identifier of an account, typically the account's address. If
* provided, filter results to payment channels whose destination is this
* account.
*/
destination_account?: string
/**
* Limit the number of transactions to retrieve. Cannot be less than 10 or
* more than 400. The default is 200.
*/
limit?: number
/**
* Value from a previous paginated response. Resume retrieving data where
* that response left off.
*/
marker?: unknown
}
/**
* The expected response from an {@link AccountChannelsRequest}.
*
* @category Responses
*/
export interface AccountChannelsResponse extends BaseResponse {
result: {
/**
* The address of the source/owner of the payment channels. This
* corresponds to the account field of the request.
*/
account: string
/** Payment channels owned by this account. */
channels: Channel[]
/**
* The identifying hash of the ledger version used to generate this
* response.
*/
ledger_hash: string
/** The ledger index of the ledger version used to generate this response. */
ledger_index: number
/**
* If true, the information in this response comes from a validated ledger
* version. Otherwise, the information is subject to change.
*/
validated?: boolean
/**
* The limit to how many channel objects were actually returned by this
* request.
*/
limit?: number
/**
* Server-defined value for pagination. Pass this to the next call to
* resume getting results where this call left off. Omitted when there are
* no additional pages after this one.
*/
marker?: unknown
}
}

View File

@@ -0,0 +1,45 @@
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
/**
* The `account_currencies` command retrieves a list of currencies that an
* account can send or receive, based on its trust lines. Expects an
* {@link AccountCurrenciesResponse}.
*
* @category Requests
*/
export interface AccountCurrenciesRequest
extends BaseRequest,
LookupByLedgerRequest {
command: 'account_currencies'
/** A unique identifier for the account, most commonly the account's address. */
account: string
/**
* If true, then the account field only accepts a public key or XAH Ledger
* address. Otherwise, account can be a secret or passphrase (not
* recommended). The default is false.
*/
strict?: boolean
}
/**
* The expected response from an {@link AccountCurrenciesRequest}.
*
* @category Responses
*/
export interface AccountCurrenciesResponse extends BaseResponse {
result: {
/**
* The identifying hash of the ledger version used to retrieve this data,
* as hex.
*/
ledger_hash?: string
/** The ledger index of the ledger version used to retrieve this data. */
ledger_index: number
/** Array of Currency Codes for currencies that this account can receive. */
receive_currencies: string[]
/** Array of Currency Codes for currencies that this account can send. */
send_currencies: string[]
/** If true, this data comes from a validated ledger. */
validated: boolean
}
}

View File

@@ -0,0 +1,228 @@
import { APIVersion, DEFAULT_API_VERSION, XAHAUD_API_V1 } from '../common'
import { AccountRoot, SignerList } from '../ledger'
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
/**
* The `account_info` command retrieves information about an account, its
* activity, and its XAH balance. All information retrieved is relative to a
* particular version of the ledger. Returns an {@link AccountInfoResponse}.
*
* @category Requests
*/
export interface AccountInfoRequest extends BaseRequest, LookupByLedgerRequest {
command: 'account_info'
/** A unique identifier for the account, most commonly the account's address. */
account: string
/**
* Whether to get info about this account's queued transactions. Can only be
* used when querying for the data from the current open ledger. Not available
* from servers in Reporting Mode.
*/
queue?: boolean
/**
* Request SignerList objects associated with this account.
*/
signer_lists?: boolean
/**
* If true, then the account field only accepts a public key or XAH Ledger
* address. Otherwise, account can be a secret or passphrase (not
* recommended). The default is false.
*/
strict?: boolean
}
export interface AccountQueueTransaction {
/**
* Whether this transaction changes this address's ways of authorizing
* transactions.
*/
auth_change: boolean
/** The Transaction Cost of this transaction, in drops of XAH. */
fee: string
/**
* The transaction cost of this transaction, relative to the minimum cost for
* this type of transaction, in fee levels.
*/
fee_level: string
/** The maximum amount of XAH, in drops, this transaction could send or destroy. */
max_spend_drops: string
/** The Sequence Number of this transaction. */
seq: number
}
export interface AccountQueueData {
/** Number of queued transactions from this address. */
txn_count: number
/**
* Whether a transaction in the queue changes this address's ways of
* authorizing transactions. If true, this address can queue no further
* transactions until that transaction has been executed or dropped from the
* queue.
*/
auth_change_queued?: boolean
/** The lowest Sequence Number among transactions queued by this address. */
lowest_sequence?: number
/** The highest Sequence Number among transactions queued by this address. */
highest_sequence?: number
/**
* Integer amount of drops of XAH that could be debited from this address if
* every transaction in the queue consumes the maximum amount of XAH possible.
*/
max_spend_drops_total?: string
/** Information about each queued transaction from this address. */
transactions?: AccountQueueTransaction[]
}
export interface AccountInfoAccountFlags {
/**
* Enable rippling on this address's trust lines by default. Required for issuing addresses; discouraged for others.
*/
defaultRipple: boolean
/**
* This account can only receive funds from transactions it sends, and from preauthorized accounts.
* (It has DepositAuth enabled.)
*/
depositAuth: boolean
/**
* Disallows use of the master key to sign transactions for this account.
*/
disableMasterKey: boolean
/**
* Disallow incoming Checks from other accounts.
*/
disallowIncomingCheck?: boolean
/**
* Disallow incoming NFTOffers from other accounts. Part of the DisallowIncoming amendment.
*/
disallowIncomingNFTokenOffer?: boolean
/**
* Disallow incoming PayChannels from other accounts. Part of the DisallowIncoming amendment.
*/
disallowIncomingPayChan?: boolean
/**
* Disallow incoming Trustlines from other accounts. Part of the DisallowIncoming amendment.
*/
disallowIncomingTrustline?: boolean
/**
* Client applications should not send XAH to this account. Not enforced by xahaud.
*/
disallowIncomingXAH: boolean
/**
* All assets issued by this address are frozen.
*/
globalFreeze: boolean
/**
* This address cannot freeze trust lines connected to it. Once enabled, cannot be disabled.
*/
noFreeze: boolean
/**
* The account has used its free SetRegularKey transaction.
*/
passwordSpent: boolean
/**
* This account must individually approve other users for those users to hold this account's issued currencies.
*/
requireAuthorization: boolean
/**
* Requires incoming payments to specify a Destination Tag.
*/
requireDestinationTag: boolean
/**
* This address can claw back issued IOUs. Once enabled, cannot be disabled.
*/
allowTrustLineClawback: boolean
}
interface BaseAccountInfoResponse extends BaseResponse {
result: {
/**
* The AccountRoot ledger object with this account's information, as stored
* in the ledger.
*/
account_data: AccountRoot
/**
* A map of account flags parsed out. This will only be available for xahaud nodes 1.11.0 and higher.
*/
account_flags?: AccountInfoAccountFlags
/**
* The ledger index of the current in-progress ledger, which was used when
* retrieving this information.
*/
ledger_current_index?: number
/**
* The ledger index of the ledger version used when retrieving this
* information. The information does not contain any changes from ledger
* versions newer than this one.
*/
ledger_index?: number
/**
* Information about queued transactions sent by this account. This
* information describes the state of the local xahaud server, which may be
* different from other servers in the peer-to-peer XAH Ledger network. Some
* fields may be omitted because the values are calculated "lazily" by the
* queuing mechanism.
*/
queue_data?: AccountQueueData
/**
* True if this data is from a validated ledger version; if omitted or set
* to false, this data is not final.
*/
validated?: boolean
}
}
/**
* Response expected from a {@link AccountInfoRequest}.
*
* @category Responses
*/
export interface AccountInfoResponse extends BaseAccountInfoResponse {
result: BaseAccountInfoResponse['result'] & {
/**
* If requested, array of SignerList ledger objects associated with this account for Multi-Signing.
* Since an account can own at most one SignerList, this array must have exactly one
* member if it is present.
*/
signer_lists?: SignerList[]
}
}
/**
* Response expected from a {@link AccountInfoRequest} using API version 1.
*
* @category ResponsesV1
*/
export interface AccountInfoV1Response extends BaseAccountInfoResponse {
result: BaseAccountInfoResponse['result'] & {
/**
* The AccountRoot ledger object with this account's information, as stored
* in the ledger.
* If requested, also includes Array of SignerList ledger objects
* associated with this account for Multi-Signing. Since an account can own
* at most one SignerList, this array must have exactly one member if it is
* present.
*/
account_data: BaseAccountInfoResponse['result']['account_data'] & {
/**
* Array of SignerList ledger objects associated with this account for Multi-Signing.
* Since an account can own at most one SignerList, this array must have exactly one
* member if it is present.
* Quirk: In API version 1, this field is nested under account_data. For this method,
* Clio implements the API version 2 behavior where is field is not nested under account_data.
*/
signer_lists?: SignerList[]
}
}
}
/**
* Type to map between the API version and the response type.
*
* @category Responses
*/
export type AccountInfoVersionResponseMap<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> = Version extends typeof XAHAUD_API_V1
? AccountInfoV1Response
: AccountInfoResponse

View File

@@ -0,0 +1,137 @@
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
export interface AccountLinesTrustline {
/** The unique Address of the counterparty to this trust line. */
account: string
/**
* Representation of the numeric balance currently held against this line. A
* positive balance means that the perspective account holds value; a negative
* Balance means that the perspective account owes value.
*/
balance: string
/** A Currency Code identifying what currency this trust line can hold. */
currency: string
/**
* The maximum amount of the given currency that this account is willing to
* owe the peer account.
*/
limit: string
/**
* The maximum amount of currency that the issuer account is willing to owe
* the perspective account.
*/
limit_peer: string
/**
* Rate at which the account values incoming balances on this trust line, as
* a ratio of this value per 1 billion units. (For example, a value of 500
* million represents a 0.5:1 ratio.) As a special case, 0 is treated as a
* 1:1 ratio.
*/
quality_in: number
/**
* Rate at which the account values outgoing balances on this trust line, as
* a ratio of this value per 1 billion units. (For example, a value of 500
* million represents a 0.5:1 ratio.) As a special case, 0 is treated as a 1:1
* ratio.
*/
quality_out: number
/**
* If true, this account has enabled the No Ripple flag for this trust line.
* If present and false, this account has disabled the No Ripple flag, but,
* because the account also has the Default Ripple flag enabled, that is not
* considered the default state. If omitted, the account has the No Ripple
* flag disabled for this trust line and Default Ripple disabled.
*/
no_ripple?: boolean
/**
* If true, the peer account has enabled the No Ripple flag for this trust
* line. If present and false, this account has disabled the No Ripple flag,
* but, because the account also has the Default Ripple flag enabled, that is
* not considered the default state. If omitted, the account has the No Ripple
* flag disabled for this trust line and Default Ripple disabled.
*/
no_ripple_peer?: boolean
/** If true, this account has authorized this trust line. The default is false. */
authorized?: boolean
/** If true, the peer account has authorized this trust line. The default is false. */
peer_authorized?: boolean
/** If true, this account has frozen this trust line. The default is false. */
freeze?: boolean
/**
* If true, the peer account has frozen this trust line. The default is
* false.
*/
freeze_peer?: boolean
}
/**
* The account_lines method returns information about an account's trust lines,
* including balances in all non-XAH currencies and assets. All information
* retrieved is relative to a particular version of the ledger. Expects an
* {@link AccountLinesResponse}.
*
* @category Requests
*/
export interface AccountLinesRequest
extends BaseRequest,
LookupByLedgerRequest {
command: 'account_lines'
/** A unique identifier for the account, most commonly the account's Address. */
account: string
/**
* The Address of a second account. If provided, show only lines of trust
* connecting the two accounts.
*/
peer?: string
/**
* Limit the number of trust lines to retrieve. The server is not required to
* honor this value. Must be within the inclusive range 10 to 400.
*/
limit?: number
/**
* Value from a previous paginated response. Resume retrieving data where
* that response left off.
*/
marker?: unknown
}
/**
* Response expected from an {@link AccountLinesRequest}.
*
* @category Responses
*/
export interface AccountLinesResponse extends BaseResponse {
result: {
/**
* Unique Address of the account this request corresponds to. This is the
* "perspective account" for purpose of the trust lines.
*/
account: string
/**
* Array of trust line objects. If the number of trust lines is large, only
* returns up to the limit at a time.
*/
lines: AccountLinesTrustline[]
/**
* The ledger index of the current open ledger, which was used when
* retrieving this information.
*/
ledger_current_index?: number
/**
* The ledger index of the ledger version that was used when retrieving
* this data.
*/
ledger_index?: number
/**
* The identifying hash the ledger version that was used when retrieving
* this data.
*/
ledger_hash?: string
/**
* Server-defined value indicating the response is paginated. Pass this to
* the next call to resume where this call left off. Omitted when there are
* No additional pages after this one.
*/
marker?: unknown
}
}

View File

@@ -0,0 +1,98 @@
import { Amendments, FeeSettings, LedgerHashes } from '../ledger'
import { LedgerEntry, LedgerEntryFilter } from '../ledger/LedgerEntry'
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
export type AccountObjectType = Exclude<
LedgerEntryFilter,
'amendments' | 'fee' | 'hashes'
>
/**
* The account_objects command returns the raw ledger format for all objects
* owned by an account. For a higher-level view of an account's trust lines and
* balances, see the account_lines method instead. Expects a response in the
* form of an {@link AccountObjectsResponse}.
*
* @category Requests
*/
export interface AccountObjectsRequest
extends BaseRequest,
LookupByLedgerRequest {
command: 'account_objects'
/** A unique identifier for the account, most commonly the account's address. */
account: string
/**
* If included, filter results to include only this type of ledger object.
*/
type?: AccountObjectType
/**
* If true, the response only includes objects that would block this account
* from being deleted. The default is false.
*/
deletion_blockers_only?: boolean
/**
* The maximum number of objects to include in the results. Must be within
* the inclusive range 10 to 400 on non-admin connections. The default is 200.
*/
limit?: number
/**
* Value from a previous paginated response. Resume retrieving data where
* that response left off.
*/
marker?: unknown
}
/**
* Account Objects can be a Check, a DepositPreauth, an Escrow, an Offer, a
* PayChannel, a SignerList, a Ticket, or a RippleState.
*/
export type AccountObject = Exclude<
LedgerEntry,
Amendments | FeeSettings | LedgerHashes
>
/**
* Response expected from an {@link AccountObjectsRequest}.
*
* @category Responses
*/
export interface AccountObjectsResponse extends BaseResponse {
result: {
/** Unique Address of the account this request corresponds to. */
account: string
/**
* Array of objects owned by this account. Each object is in its raw
* ledger format.
*/
account_objects: AccountObject[]
/**
* The identifying hash of the ledger that was used to generate this
* response.
*/
ledger_hash?: string
/**
* The ledger index of the ledger version that was used to generate this
* response.
*/
ledger_index?: number
/**
* The ledger index of the current in-progress ledger version, which was
* used to generate this response.
*/
ledger_current_index?: number
/** The limit that was used in this request, if any. */
limit?: number
/**
* Server-defined value indicating the response is paginated. Pass this to
* the next call to resume where this call left off. Omitted when there are
* no additional pages after this one.
*/
marker?: string
/**
* If included and set to true, the information in this response comes from
* a validated ledger version. Otherwise, the information is subject to
* change.
*/
validated?: boolean
}
}

View File

@@ -0,0 +1,100 @@
import { Amount } from '../common'
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
/**
* The account_offers method retrieves a list of offers made by a given account
* that are outstanding as of a particular ledger version. Expects a response in
* the form of a {@link AccountOffersResponse}.
*
* @category Requests
*/
export interface AccountOffersRequest
extends BaseRequest,
LookupByLedgerRequest {
command: 'account_offers'
/** A unique identifier for the account, most commonly the account's Address. */
account: string
/**
* Limit the number of transactions to retrieve. The server is not required
* to honor this value. Must be within the inclusive range 10 to 400.
*/
limit?: number
/**
* Value from a previous paginated response. Resume retrieving data where
* that response left off.
*/
marker?: unknown
/**
* If true, then the account field only accepts a public key or XAH Ledger
* address. Otherwise, account can be a secret or passphrase (not
* recommended). The default is false.
*/
strict?: boolean
}
export interface AccountOffer {
/** Options set for this offer entry as bit-flags. */
flags: number
/** Sequence number of the transaction that created this entry. */
seq: number
/**
* The amount the account placing this Offer receives.
*/
taker_gets: Amount
/**
* The amount the account placing this Offer pays.
*/
taker_pays: Amount
/**
* The exchange rate of the Offer, as the ratio of the original taker_pays
* divided by the original taker_gets. When executing offers, the offer with
* the most favorable (lowest) quality is consumed first; offers with the same
* quality are executed from oldest to newest.
*/
quality: string
/**
* A time after which this offer is considered unfunded, as the number of
* seconds since the Ripple Epoch. See also: Offer Expiration.
*/
expiration?: number
}
/**
* Response expected from an {@link AccountOffersRequest}.
*
* @category Responses
*/
export interface AccountOffersResponse extends BaseResponse {
result: {
/** Unique Address identifying the account that made the offers. */
account: string
/**
* Array of objects, where each object represents an offer made by this
* account that is outstanding as of the requested ledger version. If the
* number of offers is large, only returns up to limit at a time.
*/
offers?: AccountOffer[]
/**
* The ledger index of the current in-progress ledger version, which was
* used when retrieving this data.
*/
ledger_current_index?: number
/**
* The ledger index of the ledger version that was used when retrieving
* this data, as requested.
*/
ledger_index?: number
/**
* The identifying hash of the ledger version that was used when retrieving
* this data.
*/
ledger_hash?: string
/**
* Server-defined value indicating the response is paginated. Pass this to
* the next call to resume where this call left off. Omitted when there are
* no pages of information after this one.
*/
marker?: unknown
}
}

View File

@@ -0,0 +1,150 @@
import {
APIVersion,
DEFAULT_API_VERSION,
XAHAUD_API_V1,
XAHAUD_API_V2,
ResponseOnlyTxInfo,
} from '../common'
import { Transaction, TransactionMetadata } from '../transactions'
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
/**
* The account_tx method retrieves a list of transactions that involved the
* specified account. Expects a response in the form of a {@link
* AccountTxResponse}.
*
* @category Requests
*/
export interface AccountTxRequest extends BaseRequest, LookupByLedgerRequest {
command: 'account_tx'
/** A unique identifier for the account, most commonly the account's address. */
account: string
/**
* Use to specify the earliest ledger to include transactions from. A value
* of -1 instructs the server to use the earliest validated ledger version
* available.
*/
ledger_index_min?: number
/**
* Use to specify the most recent ledger to include transactions from. A
* value of -1 instructs the server to use the most recent validated ledger
* version available.
*/
ledger_index_max?: number
/**
* If true, return transactions as hex strings instead of JSON. The default is
* false.
*/
binary?: boolean
/**
* If true, returns values indexed with the oldest ledger first. Otherwise,
* the results are indexed with the newest ledger first.
*/
forward?: boolean
/**
* Default varies. Limit the number of transactions to retrieve. The server
* is not required to honor this value.
*/
limit?: number
/**
* Value from a previous paginated response. Resume retrieving data where
* that response left off. This value is stable even if there is a change in
* the server's range of available ledgers.
*/
marker?: unknown
}
export interface AccountTxTransaction<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> {
/** The ledger index of the ledger version that included this transaction. */
ledger_index: number
/**
* If binary is True, then this is a hex string of the transaction metadata.
* Otherwise, the transaction metadata is included in JSON format.
*/
meta: string | TransactionMetadata
/** JSON object defining the transaction. */
tx_json?: Version extends typeof XAHAUD_API_V2
? Transaction & ResponseOnlyTxInfo
: never
/** JSON object defining the transaction in xahaud API v1. */
tx?: Version extends typeof XAHAUD_API_V1
? Transaction & ResponseOnlyTxInfo
: never
/** The hash of the transaction. */
hash?: Version extends typeof XAHAUD_API_V2 ? string : never
/** Unique hashed String representing the transaction. */
tx_blob?: string
/**
* Whether or not the transaction is included in a validated ledger. Any
* transaction not yet in a validated ledger is subject to change.
*/
validated: boolean
}
/**
* Base interface for account transaction responses.
*/
interface AccountTxResponseBase<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> extends BaseResponse {
result: {
/** Unique Address identifying the related account. */
account: string
/**
* The ledger index of the earliest ledger actually searched for
* transactions.
*/
ledger_index_min: number
/**
* The ledger index of the most recent ledger actually searched for
* transactions.
*/
ledger_index_max: number
/** The limit value used in the request. */
limit: number
/**
* Server-defined value indicating the response is paginated. Pass this
* to the next call to resume where this call left off.
*/
marker?: unknown
/**
* Array of transactions matching the request's criteria, as explained
* below.
*/
transactions: Array<AccountTxTransaction<Version>>
/**
* If included and set to true, the information in this response comes from
* a validated ledger version. Otherwise, the information is subject to
* change.
*/
validated?: boolean
}
}
/**
* Expected response from an {@link AccountTxRequest}.
*
* @category Responses
*/
export type AccountTxResponse = AccountTxResponseBase
/**
* Expected response from an {@link AccountTxRequest} with `api_version` set to 1.
*
* @category ResponsesV1
*/
export type AccountTxV1Response = AccountTxResponseBase
/**
* Type to map between the API version and the response type.
*
* @category Responses
*/
export type AccountTxVersionResponseMap<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> = Version extends typeof XAHAUD_API_V1
? AccountTxV1Response
: AccountTxResponse

View File

@@ -0,0 +1,58 @@
import { LedgerIndex } from '../common'
import type { Request } from '.'
export interface BaseRequest {
[x: string]: unknown
/**
* A unique value to identify this request. The response to this request uses
* the same id field. This way, even if responses arrive out of order, you
* know which request prompted which response.
*/
id?: number | string
/** The name of the API method. */
command: string
/** The API version to use. If omitted, use version 1. */
api_version?: number
}
export interface LookupByLedgerRequest {
/** A 20-byte hex string for the ledger version to use. */
ledger_hash?: string
/** The ledger index of the ledger to use, or a shortcut string. */
ledger_index?: LedgerIndex
}
export interface ResponseWarning {
id: number
message: string
details?: { [key: string]: string }
}
export interface BaseResponse {
id: number | string
status?: 'success' | string
type: 'response' | string
result: unknown
warning?: 'load'
warnings?: ResponseWarning[]
forwarded?: boolean
api_version?: number
}
/**
* The shape of an error response from xahaud. xrpl.js handles rejections by
* throwing, and allowing the user to handle in the catch block of a promise.
*
* @category Responses
*/
export interface ErrorResponse {
id: number | string
status: 'error'
type: 'response' | string
error: string
error_code?: string
error_message?: string
request: Request
api_version?: number
}

View File

@@ -0,0 +1,96 @@
import { Amount } from '../common'
import { Offer } from '../ledger'
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
export interface BookOfferCurrency {
currency: string
issuer?: string
}
/**
* The book_offers method retrieves a list of offers, also known as the order.
* Book, between two currencies. Returns an {@link BookOffersResponse}.
*
* @category Requests
*/
export interface BookOffersRequest extends BaseRequest, LookupByLedgerRequest {
command: 'book_offers'
/**
* If provided, the server does not provide more than this many offers in the
* results. The total number of results returned may be fewer than the limit,
* because the server omits unfunded offers.
*/
limit?: number
/**
* The Address of an account to use as a perspective. Unfunded offers placed
* by this account are always included in the response.
*/
taker?: string
/**
* Specification of which currency the account taking the offer would
* receive, as an object with currency and issuer fields (omit issuer for
* XAH), like currency amounts.
*/
taker_gets: BookOfferCurrency
/**
* Specification of which currency the account taking the offer would pay, as
* an object with currency and issuer fields (omit issuer for XAH), like
* currency amounts.
*/
taker_pays: BookOfferCurrency
}
export interface BookOffer extends Offer {
/**
* Amount of the TakerGets currency the side placing the offer has available
* to be traded. (XAH is represented as drops; any other currency is
* represented as a decimal value.) If a trader has multiple offers in the
* same book, only the highest-ranked offer includes this field.
*/
owner_funds?: string
/**
* The maximum amount of currency that the taker can get, given the funding
* status of the offer.
*/
taker_gets_funded?: Amount
/**
* The maximum amount of currency that the taker would pay, given the funding
* status of the offer.
*/
taker_pays_funded?: Amount
/**
* The exchange rate, as the ratio taker_pays divided by taker_gets. For
* fairness, offers that have the same quality are automatically taken
* first-in, first-out.
*/
quality?: string
}
/**
* Expected response from a {@link BookOffersRequest}.
*
* @category Responses
*/
export interface BookOffersResponse extends BaseResponse {
result: {
/**
* The ledger index of the current in-progress ledger version, which was
* used to retrieve this information.
*/
ledger_current_index?: number
/**
* The ledger index of the ledger version that was used when retrieving
* this data, as requested.
*/
ledger_index?: number
/**
* The identifying hash of the ledger version that was used when retrieving
* this data, as requested.
*/
ledger_hash?: string
/** Array of offer objects, each of which has the fields of an Offer object. */
offers: BookOffer[]
validated?: boolean
}
}

View File

@@ -0,0 +1,41 @@
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The `channel_verify` method checks the validity of a signature that can be
* used to redeem a specific amount of XAH from a payment channel. Expects a
* response in the form of a {@link ChannelVerifyResponse}.
*
* @category Requests
*/
export interface ChannelVerifyRequest extends BaseRequest {
command: 'channel_verify'
/** The amount of XAH, in drops, the provided signature authorizes. */
amount: string
/**
* The Channel ID of the channel that provides the XAH. This is a
* 64-character hexadecimal string.
*/
channel_id: string
/**
* The public key of the channel and the key pair that was used to create the
* signature, in hexadecimal or the XAH Ledger's base58 format.
*/
public_key: string
/** The signature to verify, in hexadecimal. */
signature: string
}
/**
* Response expected from an {@link ChannelVerifyRequest}.
*
* @category Responses
*/
export interface ChannelVerifyResponse extends BaseResponse {
result: {
/**
* If true, the signature is valid for the stated amount, channel, and
* public key.
*/
signature_verified: boolean
}
}

View File

@@ -0,0 +1,56 @@
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
/**
* The deposit_authorized command indicates whether one account is authorized to
* send payments directly to another. Expects a response in the form of a {@link
* DepositAuthorizedResponse}.
*
* @category Requests
*/
export interface DepositAuthorizedRequest
extends BaseRequest,
LookupByLedgerRequest {
command: 'deposit_authorized'
/** The sender of a possible payment. */
source_account: string
/** The recipient of a possible payment. */
destination_account: string
}
/**
* Expected response from a {@link DepositAuthorizedRequest}.
*
* @category Responses
*/
export interface DepositAuthorizedResponse extends BaseResponse {
result: {
/**
* Whether the specified source account is authorized to send payments
* directly to the destination account. If true, either the destination
* account does not require Deposit Authorization or the source account is
* preauthorized.
*/
deposit_authorized: boolean
/** The destination account specified in the request. */
destination_account: string
/**
* The identifying hash of the ledger that was used to generate this
* Response.
*/
ledger_hash?: string
/**
* The ledger index of the ledger version that was used to generate this
* Response.
*/
ledger_index?: number
/**
* The ledger index of the current in-progress ledger version, which was
* used to generate this response.
*/
ledger_current_index?: number
/** The source account specified in the request. */
source_account: string
/** If true, the information comes from a validated ledger version. */
validated?: boolean
}
}

View File

@@ -0,0 +1,68 @@
import { BaseRequest, BaseResponse } from './baseMethod'
export interface FeatureAllRequest extends BaseRequest {
command: 'feature'
feature?: never
}
export interface FeatureOneRequest extends BaseRequest {
command: 'feature'
feature: string
}
/**
* The `feature` command returns information about amendments this server knows about, including whether they are enabled.
* Returns an {@link FeatureResponse}.
*
* @category Requests
*/
export type FeatureRequest = FeatureAllRequest | FeatureOneRequest
export interface FeatureAllResponse extends BaseResponse {
result: {
features: Record<
string,
{
/*
* Whether this amendment is currently enabled in the latest ledger.
*/
enabled: boolean
/*
* The human-readable name for this amendment, if known.
*/
name: string
supported: boolean
}
>
}
}
export interface FeatureOneResponse extends BaseResponse {
result: Record<
string,
{
/*
* Whether this amendment is currently enabled in the latest ledger.
*/
enabled: boolean
/*
* The human-readable name for this amendment, if known.
*/
name: string
supported: boolean
}
>
}
/**
* Response expected from an {@link FeatureRequest}.
*
* @category Responses
*/
export type FeatureResponse = FeatureAllResponse | FeatureOneResponse

View File

@@ -0,0 +1,91 @@
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The `fee` command reports the current state of the open-ledger requirements
* for the transaction cost. This requires the FeeEscalation amendment to be
* enabled. Expects a response in the form of a {@link FeeResponse}.
*
* @example
* ```ts
* const feeRequest: FeeRequest = {
* command: 'fee'
* }
* ```
*
* @category Requests
*/
export interface FeeRequest extends BaseRequest {
command: 'fee'
}
/**
* Response expected from a {@link FeeRequest}.
*
* @category Responses
*/
export interface FeeResponse extends BaseResponse {
result: {
/** Number of transactions provisionally included in the in-progress ledger. */
current_ledger_size: string
/** Number of transactions currently queued for the next ledger. */
current_queue_size: string
drops: {
/**
* The transaction cost required for a reference transaction to be
* included in a ledger under minimum load, represented in drops of XAH.
*/
base_fee: string
/**
* An approximation of the median transaction cost among transactions.
* Included in the previous validated ledger, represented in drops of XAH.
*/
median_fee: string
/**
* The minimum transaction cost for a reference transaction to be queued
* for a later ledger, represented in drops of XAH. If greater than
* base_fee, the transaction queue is full.
*/
minimum_fee: string
/**
* The minimum transaction cost that a reference transaction must pay to
* be included in the current open ledger, represented in drops of XAH.
*/
open_ledger_fee: string
}
/**
* The approximate number of transactions expected to be included in the
* current ledger. This is based on the number of transactions in the
* previous ledger.
*/
expected_ledger_size: string
/** The Ledger Index of the current open ledger these stats describe. */
ledger_current_index: number
levels: {
/**
* The median transaction cost among transactions in the previous
* validated ledger, represented in fee levels.
*/
median_level: string
/**
* The minimum transaction cost required to be queued for a future
* ledger, represented in fee levels.
*/
minimum_level: string
/**
* The minimum transaction cost required to be included in the current
* open ledger, represented in fee levels.
*/
open_ledger_level: string
/**
* The equivalent of the minimum transaction cost, represented in fee
* levels.
*/
reference_level: string
}
/**
* The maximum number of transactions that the transaction queue can
* currently hold.
*/
max_queue_size: string
}
}

View File

@@ -0,0 +1,85 @@
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
/**
* The gateway_balances command calculates the total balances issued by a given
* account, optionally excluding amounts held by operational addresses. Expects
* a response in the form of a {@link GatewayBalancesResponse}.
*
* @example
* ```ts
* const gatewayBalances: GatewayBalanceRequest = {
* "id": "example_gateway_balances_1",
* "command": "gateway_balances",
* "account": "rMwjYedjc7qqtKYVLiAccJSmCwih4LnE2q",
* "strict": true,
* "hotwallet": ["rKm4uWpg9tfwbVSeATv4KxDe6mpE9yPkgJ","ra7JkEzrgeKHdzKgo4EUUVBnxggY4z37kt"],
* "ledger_index": "validated"
* }
* ```
*
* @category Requests
*/
export interface GatewayBalancesRequest
extends BaseRequest,
LookupByLedgerRequest {
command: 'gateway_balances'
/** The Address to check. This should be the issuing address. */
account: string
/**
* If true, only accept an address or public key for the account parameter.
* Defaults to false.
*/
strict?: boolean
/**
* An operational address to exclude from the balances issued, or an array of
* Such addresses.
*/
hotwallet?: string | string[]
}
export interface GatewayBalance {
currency: string
value: string
}
/**
* Expected response from a {@link GatewayBalancesRequest}.
*
* @category Responses
*/
export interface GatewayBalancesResponse extends BaseResponse {
result: {
/** The address of the account that issued the balances. */
account: string
/**
* Total amounts issued to addresses not excluded, as a map of currencies
* to the total value issued.
*/
obligations?: { [currency: string]: string }
/**
* Amounts issued to the hotwallet addresses from the request. The keys are
* addresses and the values are arrays of currency amounts they hold.
*/
balances?: { [address: string]: GatewayBalance[] }
/**
* Total amounts held that are issued by others. In the recommended
* configuration, the issuing address should have none.
*/
assets?: { [address: string]: GatewayBalance[] }
/**
* The identifying hash of the ledger version that was used to generate
* this response.
*/
ledger_hash?: string
/**
* The ledger index of the ledger version that was used to generate this
* response.
*/
ledger_current_index?: number
/**
* The ledger index of the current in-progress ledger version, which was
* used to retrieve this information.
*/
ledger_index?: number
}
}

View File

@@ -0,0 +1,554 @@
/* eslint-disable no-inline-comments -- Necessary for important note */
/* eslint-disable max-lines -- There is a lot to export */
import type { APIVersion, DEFAULT_API_VERSION } from '../common'
import {
AccountChannelsRequest,
AccountChannelsResponse,
Channel,
} from './accountChannels'
import {
AccountCurrenciesRequest,
AccountCurrenciesResponse,
} from './accountCurrencies'
import {
AccountInfoAccountFlags,
AccountInfoRequest,
AccountInfoResponse,
AccountInfoV1Response,
AccountInfoVersionResponseMap,
AccountQueueData,
AccountQueueTransaction,
} from './accountInfo'
import {
AccountLinesRequest,
AccountLinesResponse,
AccountLinesTrustline,
} from './accountLines'
import {
AccountObject,
AccountObjectsRequest,
AccountObjectsResponse,
AccountObjectType,
} from './accountObjects'
import {
AccountOffer,
AccountOffersRequest,
AccountOffersResponse,
} from './accountOffers'
import {
AccountTxRequest,
AccountTxResponse,
AccountTxV1Response,
AccountTxVersionResponseMap,
AccountTxTransaction,
} from './accountTx'
import {
BaseRequest,
BaseResponse,
ErrorResponse,
ResponseWarning,
} from './baseMethod'
import {
BookOffersRequest,
BookOffer,
BookOffersResponse,
BookOfferCurrency,
} from './bookOffers'
import { ChannelVerifyRequest, ChannelVerifyResponse } from './channelVerify'
import {
DepositAuthorizedRequest,
DepositAuthorizedResponse,
} from './depositAuthorized'
import {
FeatureAllRequest,
FeatureAllResponse,
FeatureOneRequest,
FeatureOneResponse,
FeatureRequest,
FeatureResponse,
} from './feature'
import { FeeRequest, FeeResponse } from './fee'
import {
GatewayBalance,
GatewayBalancesRequest,
GatewayBalancesResponse,
} from './gatewayBalances'
import {
LedgerBinary,
LedgerModifiedOfferCreateTransaction,
LedgerQueueData,
LedgerRequest,
LedgerResponse,
LedgerV1Response,
LedgerRequestExpandedTransactionsOnly,
LedgerResponseExpanded,
LedgerRequestExpandedAccountsAndTransactions,
LedgerRequestExpandedAccountsOnly,
LedgerRequestExpandedTransactionsBinary,
LedgerVersionResponseMap,
} from './ledger'
import { LedgerClosedRequest, LedgerClosedResponse } from './ledgerClosed'
import { LedgerCurrentRequest, LedgerCurrentResponse } from './ledgerCurrent'
import {
LedgerDataBinaryLedgerEntry,
LedgerDataLabeledLedgerEntry,
LedgerDataLedgerState,
LedgerDataRequest,
LedgerDataResponse,
} from './ledgerData'
import { LedgerEntryRequest, LedgerEntryResponse } from './ledgerEntry'
import { ManifestRequest, ManifestResponse } from './manifest'
import { NoRippleCheckRequest, NoRippleCheckResponse } from './norippleCheck'
import {
PathFindRequest,
PathFindCloseRequest,
PathFindCreateRequest,
PathFindStatusRequest,
PathFindResponse,
PathFindPathOption,
} from './pathFind'
import { PingRequest, PingResponse } from './ping'
import { RandomRequest, RandomResponse } from './random'
import {
RipplePathFindPathOption,
RipplePathFindRequest,
RipplePathFindResponse,
SourceCurrencyAmount,
} from './ripplePathFind'
import {
ServerDefinitionsRequest,
ServerDefinitionsResponse,
} from './serverDefinitions'
import {
JobType,
ServerInfoRequest,
ServerInfoResponse,
ServerState,
StateAccounting,
StateAccountingFinal,
} from './serverInfo'
import { ServerStateRequest, ServerStateResponse } from './serverState'
import { SubmitRequest, SubmitResponse } from './submit'
import {
SubmitMultisignedRequest,
SubmitMultisignedResponse,
SubmitMultisignedV1Response,
SubmitMultisignedVersionResponseMap,
} from './submitMultisigned'
import {
BooksSnapshot,
ConsensusStream,
LedgerStream,
LedgerStreamResponse,
OrderBookStream,
PathFindStream,
PeerStatusStream,
Stream,
SubscribeBook,
SubscribeRequest,
SubscribeResponse,
TransactionStream,
TransactionV1Stream,
ValidationStream,
} from './subscribe'
import {
TransactionEntryRequest,
TransactionEntryResponse,
} from './transactionEntry'
import { TxRequest, TxResponse, TxV1Response, TxVersionResponseMap } from './tx'
import {
UnsubscribeBook,
UnsubscribeRequest,
UnsubscribeResponse,
} from './unsubscribe'
/**
* @category Requests
*/
type Request =
// account methods
| AccountChannelsRequest
| AccountCurrenciesRequest
| AccountInfoRequest
| AccountLinesRequest
| AccountObjectsRequest
| AccountOffersRequest
| AccountTxRequest
| GatewayBalancesRequest
| NoRippleCheckRequest
// ledger methods
| LedgerRequest
| LedgerClosedRequest
| LedgerCurrentRequest
| LedgerDataRequest
| LedgerEntryRequest
// transaction methods
| SubmitRequest
| SubmitMultisignedRequest
| TransactionEntryRequest
| TxRequest
// path and order book methods
| BookOffersRequest
| DepositAuthorizedRequest
| PathFindRequest
| RipplePathFindRequest
// payment channel methods
| ChannelVerifyRequest
// subscription methods
| SubscribeRequest
| UnsubscribeRequest
// server info methods
| FeeRequest
| ManifestRequest
| ServerDefinitionsRequest
| ServerInfoRequest
| ServerStateRequest
| FeatureRequest
// utility methods
| PingRequest
| RandomRequest
/**
* @category Responses
*/
type Response<Version extends APIVersion = typeof DEFAULT_API_VERSION> =
// account methods
| AccountChannelsResponse
| AccountCurrenciesResponse
| AccountInfoVersionResponseMap<Version>
| AccountLinesResponse
| AccountObjectsResponse
| AccountOffersResponse
| AccountTxVersionResponseMap<Version>
| GatewayBalancesResponse
| NoRippleCheckResponse
// ledger methods
| LedgerVersionResponseMap<Version>
| LedgerClosedResponse
| LedgerCurrentResponse
| LedgerDataResponse
| LedgerEntryResponse
// transaction methods
| SubmitResponse
| SubmitMultisignedVersionResponseMap<Version>
| TransactionEntryResponse
| TxResponse
// path and order book methods
| BookOffersResponse
| DepositAuthorizedResponse
| PathFindResponse
| RipplePathFindResponse
// payment channel methods
| ChannelVerifyResponse
// subscription methods
| SubscribeResponse
| UnsubscribeResponse
// server info methods
| FeeResponse
| ManifestResponse
| ServerDefinitionsResponse
| ServerInfoResponse
| ServerStateResponse
| FeatureResponse
// utility methods
| PingResponse
| RandomResponse
export type RequestResponseMap<
T,
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> = T extends AccountChannelsRequest
? AccountChannelsResponse
: T extends AccountCurrenciesRequest
? AccountCurrenciesResponse
: T extends AccountInfoRequest
? AccountInfoVersionResponseMap<Version>
: T extends AccountLinesRequest
? AccountLinesResponse
: T extends AccountObjectsRequest
? AccountObjectsResponse
: T extends AccountOffersRequest
? AccountOffersResponse
: T extends AccountTxRequest
? AccountTxVersionResponseMap<Version>
: 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
? LedgerVersionResponseMap<Version>
: T extends LedgerRequestExpandedAccountsAndTransactions
? LedgerResponseExpanded<Version>
: T extends LedgerRequestExpandedTransactionsOnly
? LedgerResponseExpanded<Version>
: T extends LedgerRequestExpandedAccountsOnly
? LedgerResponseExpanded<Version>
: T extends LedgerRequest
? LedgerVersionResponseMap<Version>
: T extends LedgerClosedRequest
? LedgerClosedResponse
: T extends LedgerCurrentRequest
? LedgerCurrentResponse
: T extends LedgerDataRequest
? LedgerDataResponse
: T extends LedgerEntryRequest
? LedgerEntryResponse
: T extends SubmitRequest
? SubmitResponse
: T extends SubmitMultisignedRequest
? SubmitMultisignedVersionResponseMap<Version>
: T extends TransactionEntryRequest
? TransactionEntryResponse
: T extends TxRequest
? TxVersionResponseMap<Version>
: 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 FeatureAllRequest
? FeatureAllResponse
: T extends FeatureOneRequest
? FeatureOneResponse
: T extends PingRequest
? PingResponse
: T extends RandomRequest
? RandomResponse
: Response<Version>
export type MarkerRequest = Request & {
limit?: number
marker?: unknown
}
export type MarkerResponse<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> = Response<Version> & {
result: {
marker?: unknown
}
}
export type RequestAllResponseMap<
T,
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> = T extends AccountChannelsRequest
? AccountChannelsResponse
: T extends AccountLinesRequest
? AccountLinesResponse
: T extends AccountObjectsRequest
? AccountObjectsResponse
: T extends AccountOffersRequest
? AccountOffersResponse
: T extends AccountTxRequest
? AccountTxVersionResponseMap<Version>
: T extends LedgerDataRequest
? LedgerDataResponse
: T extends BookOffersRequest
? BookOffersResponse
: MarkerResponse<Version>
export {
// Allow users to define their own requests and responses. This is useful for releasing experimental versions
BaseRequest,
BaseResponse,
Request,
Response,
ResponseWarning,
// account methods with types
Channel,
AccountChannelsRequest,
AccountChannelsResponse,
AccountCurrenciesRequest,
AccountCurrenciesResponse,
AccountInfoAccountFlags,
AccountInfoRequest,
AccountInfoResponse,
AccountInfoV1Response,
AccountQueueData,
AccountQueueTransaction,
AccountLinesRequest,
AccountLinesResponse,
AccountLinesTrustline,
AccountObject,
AccountObjectType,
AccountObjectsRequest,
AccountObjectsResponse,
AccountOffer,
AccountOffersRequest,
AccountOffersResponse,
AccountTxRequest,
AccountTxResponse,
AccountTxV1Response,
AccountTxTransaction,
GatewayBalance,
GatewayBalancesRequest,
GatewayBalancesResponse,
NoRippleCheckRequest,
NoRippleCheckResponse,
// ledger methods
LedgerRequest,
LedgerResponse,
LedgerV1Response,
LedgerQueueData,
LedgerBinary,
LedgerModifiedOfferCreateTransaction,
LedgerClosedRequest,
LedgerClosedResponse,
LedgerCurrentRequest,
LedgerCurrentResponse,
LedgerDataRequest,
LedgerDataLabeledLedgerEntry,
LedgerDataBinaryLedgerEntry,
LedgerDataResponse,
LedgerDataLedgerState,
LedgerEntryRequest,
LedgerEntryResponse,
// transaction methods with types
SubmitRequest,
SubmitResponse,
SubmitMultisignedRequest,
SubmitMultisignedResponse,
SubmitMultisignedV1Response,
TransactionEntryRequest,
TransactionEntryResponse,
TxRequest,
TxResponse,
TxV1Response,
// path and order book methods with types
BookOffersRequest,
BookOffer,
BookOfferCurrency,
BookOffersResponse,
DepositAuthorizedRequest,
DepositAuthorizedResponse,
PathFindRequest,
PathFindCreateRequest,
PathFindCloseRequest,
PathFindPathOption,
PathFindStatusRequest,
PathFindResponse,
RipplePathFindPathOption,
RipplePathFindRequest,
RipplePathFindResponse,
SourceCurrencyAmount,
// payment channel methods
ChannelVerifyRequest,
ChannelVerifyResponse,
// Subscribe methods/streams with types
SubscribeRequest,
SubscribeResponse,
SubscribeBook,
Stream,
BooksSnapshot,
LedgerStream,
LedgerStreamResponse,
ValidationStream,
TransactionStream,
TransactionV1Stream,
PathFindStream,
PeerStatusStream,
OrderBookStream,
ConsensusStream,
UnsubscribeRequest,
UnsubscribeResponse,
UnsubscribeBook,
// server info methods with types
FeeRequest,
FeeResponse,
ManifestRequest,
ManifestResponse,
ServerDefinitionsRequest,
ServerDefinitionsResponse,
ServerInfoRequest,
ServerInfoResponse,
ServerStateRequest,
ServerStateResponse,
JobType,
ServerState,
StateAccountingFinal,
StateAccounting,
FeatureRequest,
FeatureResponse,
// utility methods
PingRequest,
PingResponse,
RandomRequest,
RandomResponse,
ErrorResponse,
}

View File

@@ -0,0 +1,295 @@
import { APIVersion, DEFAULT_API_VERSION, XAHAUD_API_V1 } from '../common'
import { Ledger, LedgerV1, LedgerVersionMap } from '../ledger/Ledger'
import { LedgerEntryFilter } from '../ledger/LedgerEntry'
import { Transaction, TransactionAndMetadata } from '../transactions'
import { TransactionMetadata } from '../transactions/metadata'
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
/**
* Retrieve information about the public ledger. Expects a response in the form
* of a {@link LedgerResponse}.
*
* @example
* ```ts
* const ledger: LedgerRequest = {
* "id": 14,
* "command": "ledger",
* "ledger_index": "validated",
* "full": false,
* "accounts": false,
* "transactions": false,
* "expand": false,
* "owner_funds": false
* }
* ```
*
* @category Requests
*/
export interface LedgerRequest extends BaseRequest, LookupByLedgerRequest {
command: 'ledger'
/**
* Admin required If true, return full information on the entire ledger.
* Ignored if you did not specify a ledger version. Defaults to false.
*/
full?: boolean
/**
* Admin required. If true, return information on accounts in the ledger.
* Ignored if you did not specify a ledger version. Defaults to false.
*/
accounts?: boolean
/**
* If true, return information on transactions in the specified ledger
* version. Defaults to false. Ignored if you did not specify a ledger
* version.
*/
transactions?: boolean
/**
* Provide full JSON-formatted information for transaction/account
* information instead of only hashes. Defaults to false. Ignored unless you
* request transactions, accounts, or both.
*/
expand?: boolean
/**
* If true, include owner_funds field in the metadata of OfferCreate
* transactions in the response. Defaults to false. Ignored unless
* transactions are included and expand is true.
*/
owner_funds?: boolean
/**
* If true, and transactions and expand are both also true, return
* transaction information in binary format (hexadecimal string) instead of
* JSON format.
*/
binary?: boolean
/**
* If true, and the command is requesting the current ledger, includes an
* array of queued transactions in the results.
*/
queue?: boolean
/**
* If included, filter results to include only this type of ledger object.
*/
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`.
*/
export interface LedgerModifiedOfferCreateTransaction {
transaction: Transaction
metadata: TransactionMetadata & { owner_funds: string }
}
export interface LedgerQueueData {
account: string
tx:
| TransactionAndMetadata
| LedgerModifiedOfferCreateTransaction
| { tx_blob: string }
retries_remaining: number
preflight_result: string
last_result?: string
auth_change?: boolean
fee?: string
fee_level?: string
max_spend_drops?: string
}
export interface LedgerBinary
extends Omit<Omit<Ledger, 'transactions'>, 'accountState'> {
accountState?: string[]
transactions?: string[]
}
export interface LedgerBinaryV1
extends Omit<Omit<LedgerV1, 'transactions'>, 'accountState'> {
accountState?: string[]
transactions?: string[]
}
interface LedgerResponseBase {
/** Unique identifying hash of the entire ledger. */
ledger_hash: string
/** The Ledger Index of this ledger. */
ledger_index: number
/**
* If true, this is a validated ledger version. If omitted or set to false,
* this ledger's data is not final.
*/
queue_data?: Array<LedgerQueueData | string>
/**
* Array of objects describing queued transactions, in the same order as
* the queue. If the request specified expand as true, members contain full
* representations of the transactions, in either JSON or binary depending
* on whether the request specified binary as true.
*/
validated?: boolean
}
interface LedgerResponseResult extends LedgerResponseBase {
/** The complete header data of this {@link Ledger}. */
ledger: LedgerBinary
}
interface LedgerV1ResponseResult extends LedgerResponseBase {
/** The complete header data of this {@link Ledger}. */
ledger: LedgerBinaryV1
}
/**
* 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
}
/**
* Response expected from a {@link LedgerRequest}.
* This is the default request response, triggered when `expand` and `binary` are both false.
* This is the response for API version 1.
*
* @category ResponsesV1
*/
export interface LedgerV1Response extends BaseResponse {
result: LedgerV1ResponseResult
}
/**
* Type to map between the API version and the response type.
*
* @category Responses
*/
export type LedgerVersionResponseMap<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> = Version extends typeof XAHAUD_API_V1 ? LedgerV1Response : LedgerResponse
interface LedgerResponseExpandedResult<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> extends LedgerResponseBase {
/** The complete header data of this {@link Ledger}. */
ledger: LedgerVersionMap<Version>
}
/**
* 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<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> extends BaseResponse {
result: LedgerResponseExpandedResult<Version>
}

View File

@@ -0,0 +1,32 @@
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The ledger_closed method returns the unique identifiers of the most recently
* closed ledger. Expects a response in the form of a {@link
* LedgerClosedResponse}.
*
* @example
* *
* ```ts
* const ledgerClosed: LedgerClosedRequest = {
* "command": "ledger_closed"
* }
* ```
*
* @category Requests
*/
export interface LedgerClosedRequest extends BaseRequest {
command: 'ledger_closed'
}
/**
* The response expected from a {@link LedgerClosedRequest}.
*
* @category Responses
*/
export interface LedgerClosedResponse extends BaseResponse {
result: {
ledger_hash: string
ledger_index: number
}
}

View File

@@ -0,0 +1,31 @@
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The ledger_current method returns the unique identifiers of the current
* in-progress ledger. Expects a response in the form of a {@link
* LedgerCurrentResponse}.
*
* @example
* ```ts
* const ledgerCurrent: LedgerCurrentRequest = {
* "command": "ledger_current"
* }
* ```
*
* @category Requests
*/
export interface LedgerCurrentRequest extends BaseRequest {
command: 'ledger_current'
}
/**
* Response expected from a {@link LedgerCurrentRequest}.
*
* @category Responses
*/
export interface LedgerCurrentResponse extends BaseResponse {
result: {
/** The ledger index of this ledger version. */
ledger_current_index: number
}
}

View File

@@ -0,0 +1,82 @@
import { LedgerEntry, LedgerEntryFilter } from '../ledger'
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
/**
* The `ledger_data` method retrieves contents of the specified ledger. You can
* iterate through several calls to retrieve the entire contents of a single
* ledger version.
*
* @example
* ```ts
* const ledgerData: LedgerDataRequest = {
* "id": 2,
* "ledger_hash": "842B57C1CC0613299A686D3E9F310EC0422C84D3911E5056389AA7E5808A93C8",
* "command": "ledger_data",
* "limit": 5,
* "binary": true
* }
* ```
*
* @category Requests
*/
export interface LedgerDataRequest extends BaseRequest, LookupByLedgerRequest {
command: 'ledger_data'
/**
* If set to true, return ledger objects as hashed hex strings instead of
* JSON.
*/
binary?: boolean
/**
* Limit the number of ledger objects to retrieve. The server is not required
* to honor this value.
*/
limit?: number
/**
* Value from a previous paginated response. Resume retrieving data where
* that response left off.
*/
marker?: unknown
/**
* If included, filter results to include only this type of ledger object.
*/
type?: LedgerEntryFilter
}
export type LedgerDataLabeledLedgerEntry = {
ledgerEntryType: string
} & LedgerEntry
export interface LedgerDataBinaryLedgerEntry {
data: string
}
export type LedgerDataLedgerState = { index: string } & (
| LedgerDataBinaryLedgerEntry
| LedgerDataLabeledLedgerEntry
)
/**
* The response expected from a {@link LedgerDataRequest}.
*
* @category Responses
*/
export interface LedgerDataResponse extends BaseResponse {
result: {
/** The ledger index of this ledger version. */
ledger_index: number
/** Unique identifying hash of this ledger version. */
ledger_hash: string
/**
* Array of JSON objects containing data from the ledger's state tree,
* as defined below.
*/
state: LedgerDataLedgerState[]
/**
* Server-defined value indicating the response is paginated. Pass this to
* the next call to resume where this call left off.
*/
marker?: unknown
validated?: boolean
}
}

View File

@@ -0,0 +1,219 @@
import { Currency, XChainBridge } from '../common'
import { LedgerEntry } from '../ledger'
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
/**
* The `ledger_entry` method returns a single ledger object from the XAH Ledger
* in its raw format. Expects a response in the form of a {@link
* LedgerEntryResponse}.
*
* @example
* ```ts
* const ledgerEntry: LedgerEntryRequest = {
* command: "ledger_entry",
* ledger_index: 60102302,
* index: "7DB0788C020F02780A673DC74757F23823FA3014C1866E72CC4CD8B226CD6EF4"
* }
* ```
*
* @category Requests
*/
export interface LedgerEntryRequest extends BaseRequest, LookupByLedgerRequest {
command: 'ledger_entry'
/**
* Retrieve an Automated Market Maker (AMM) object from the ledger.
* This is similar to amm_info method, but the ledger_entry version returns only the ledger entry as stored.
*/
amm?: {
asset: {
currency: string
issuer?: string
}
asset2: {
currency: string
issuer?: string
}
}
/**
* (Optional) If set to true and the queried object has been deleted,
* return its complete data prior to its deletion.
* If set to false or not provided and the queried object has been deleted,
* return objectNotFound (current behavior).
* This parameter is supported only by Clio servers
*/
include_deleted?: boolean
/**
* If true, return the requested ledger object's contents as a hex string in
* the XAH Ledger's binary format. Otherwise, return data in JSON format. The
* default is false.
*/
binary?: boolean
/*
* Only one of the following properties should be defined in a single request
* https://xrpl.org/ledger_entry.html.
*
* Retrieve any type of ledger object by its unique ID.
*/
index?: string
/**
* Retrieve an AccountRoot object by its address. This is roughly equivalent
* to the an {@link AccountInfoRequest}.
*/
account_root?: string
/** The object ID of a Check object to retrieve. */
check?: string
/**
* Specify a DepositPreauth object to retrieve. If a string, must be the
* object ID of the DepositPreauth object, as hexadecimal. If an object,
* requires owner and authorized sub-fields.
*/
deposit_preauth?:
| {
/** The account that provided the preauthorization. */
owner: string
/** The account that received the preauthorization. */
authorized: string
}
| string
/**
* Specify a DID object to retrieve. If a string, must be the
* object ID of the DID object, as hexadecimal, or the account ID.
*/
did?: string
/**
* The DirectoryNode to retrieve. If a string, must be the object ID of the
* directory, as hexadecimal. If an object, requires either `dir_root` o
* Owner as a sub-field, plus optionally a `sub_index` sub-field.
*/
directory?:
| {
/** If provided, jumps to a later "page" of the DirectoryNode. */
sub_index?: number
/** Unique index identifying the directory to retrieve, as a hex string. */
dir_root?: string
/** Unique address of the account associated with this directory. */
owner?: string
}
| string
/**
* The Escrow object to retrieve. If a string, must be the object ID of the
* escrow, as hexadecimal. If an object, requires owner and seq sub-fields.
*/
escrow?:
| {
/** The owner (sender) of the Escrow object. */
owner: string
/** Sequence Number of the transaction that created the Escrow object. */
seq: number
}
| string
/**
* The Offer object to retrieve. If a string, interpret as the unique object
* ID to the Offer. If an object, requires the sub-fields `account` and `seq`
* to uniquely identify the offer.
*/
offer?:
| {
/** The account that placed the offer. */
account: string
/** Sequence Number of the transaction that created the Offer object. */
seq: number
}
| string
/** The object ID of a PayChannel object to retrieve. */
payment_channel?: string
/**
* Object specifying the RippleState (trust line) object to retrieve. The
* accounts and currency sub-fields are required to uniquely specify the
* rippleState entry to retrieve.
*/
ripple_state?: {
/**
* 2-length array of account Addresses, defining the two accounts linked by
* this RippleState object.
*/
accounts: string[]
/** Currency Code of the RippleState object to retrieve. */
currency: string
}
/**
* The Ticket object to retrieve. If a string, must be the object ID of the
* Ticket, as hexadecimal. If an object, the `owner` and `ticket_sequence`
* sub-fields are required to uniquely specify the Ticket entry.
*/
ticket?:
| {
/** The owner of the Ticket object. */
owner: string
/** The Ticket Sequence number of the Ticket entry to retrieve. */
ticket_sequence: number
}
| string
/**
* Must be the object ID of the NFToken page, as hexadecimal
*/
nft_page?: string
bridge_account?: string
bridge?: XChainBridge
xchain_owned_claim_id?:
| {
locking_chain_door: string
locking_chain_issue: Currency
issuing_chain_door: string
issuing_chain_issue: Currency
xchain_owned_claim_id: string | number
}
| string
xchain_owned_create_account_claim_id?:
| {
locking_chain_door: string
locking_chain_issue: Currency
issuing_chain_door: string
issuing_chain_issue: Currency
xchain_owned_create_account_claim_id: string | number
}
| string
}
/**
* Response expected from a {@link LedgerEntryRequest}.
*
* @category Responses
*/
export interface LedgerEntryResponse<T = LedgerEntry> extends BaseResponse {
result: {
/** The unique ID of this ledger object. */
index: string
/** The ledger index of the ledger that was used when retrieving this data. */
ledger_current_index: number
/**
* Object containing the data of this ledger object, according to the
* ledger format.
*/
node?: T
/** The binary representation of the ledger object, as hexadecimal. */
node_binary?: string
validated?: boolean
/**
* (Optional) Indicates the ledger index at which the object was deleted.
*/
deleted_ledger_index?: number
}
}

View File

@@ -0,0 +1,54 @@
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The `manifest` method reports the current "manifest" information for a given
* validator public key. The "manifest" is the public portion of that
* validator's configured token. Expects a response in the form of a {@link
* ManifestResponse}.
*
* @example
* ```ts
* const manifest: ManifestRequest = {
* "command": "manifest",
* "public_key": "nHUFE9prPXPrHcG3SkwP1UzAQbSphqyQkQK9ATXLZsfkezhhda3p"
* }
* ```
*
* @category Requests
*/
export interface ManifestRequest extends BaseRequest {
command: 'manifest'
/**
* The base58-encoded public key of the validator to look up. This can be the
* master public key or ephemeral public key.
*/
public_key: string
}
/**
* Response expected from a {@link ManifestRequest}.
*
* @category Responses
*/
export interface ManifestResponse extends BaseResponse {
result: {
/**
* The data contained in this manifest. Omitted if the server does not have
* A manifest for the public_key from the request.
*/
details?: {
domain: string
ephemeral_key: string
master_key: string
seq: number
}
/**
* The full manifest data in base64 format. This data is serialized to
* binary before being base64-encoded. Omitted if the server does not have a
* manifest for the public_key from the request.
*/
manifest?: string
/** The public_key from the request. */
requested: string
}
}

View File

@@ -0,0 +1,82 @@
import { LedgerIndex, ResponseOnlyTxInfo } from '../common'
import { Transaction } from '../transactions'
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The `noripple_check` command provides a quick way to check the status of th
* default xahau field for an account and the No Ripple flag of its trust
* lines, compared with the recommended settings. Expects a response in the form
* of an {@link NoRippleCheckResponse}.
*
* @example
* ```ts
* const noRipple: NoRippleCheckRequest = {
* "id": 0,
* "command": "noripple_check",
* "account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
* "role": "gateway",
* "ledger_index": "current",
* "limit": 2,
* "transactions": true
* }
* ```
*
* @category Requests
*/
export interface NoRippleCheckRequest extends BaseRequest {
command: 'noripple_check'
/** A unique identifier for the account, most commonly the account's address. */
account: string
/**
* Whether the address refers to a gateway or user. Recommendations depend on
* the role of the account. Issuers must have Default Ripple enabled and must
* disable No Ripple on all trust lines. Users should have Default Ripple
* disabled, and should enable No Ripple on all trust lines.
*/
role: 'gateway' | 'user'
/**
* If true, include an array of suggested transactions, as JSON objects,
* that you can sign and submit to fix the problems. Defaults to false.
*/
transactions?: boolean
/**
* The maximum number of trust line problems to include in the results.
* Defaults to 300.
*/
limit?: number
/** A 20-byte hex string for the ledger version to use. */
ledger_hash?: string
/**
* The ledger index of the ledger to use, or a shortcut string to choose a
* ledger automatically.
*/
ledger_index?: LedgerIndex
}
/**
* Response expected by a {@link NoRippleCheckRequest}.
*
* @category Responses
*/
export interface NoRippleCheckResponse extends BaseResponse {
result: {
/** The ledger index of the ledger used to calculate these results. */
ledger_current_index: number
/**
* Array of strings with human-readable descriptions of the problems.
* This includes up to one entry if the account's Default Ripple setting is
* not as recommended, plus up to limit entries for trust lines whose no
* xahau setting is not as recommended.
*/
problems: string[]
/**
* If the request specified transactions as true, this is an array of JSON
* objects, each of which is the JSON form of a transaction that should fix
* one of the described problems. The length of this array is the same as
* the problems array, and each entry is intended to fix the problem
* described at the same index into that array.
*/
transactions: Array<Transaction & ResponseOnlyTxInfo>
}
}

View File

@@ -0,0 +1,116 @@
import { Amount, Path } from '../common'
import { BaseRequest, BaseResponse } from './baseMethod'
interface BasePathFindRequest extends BaseRequest {
command: 'path_find'
subcommand: string
}
/** Start sending pathfinding information. */
export interface PathFindCreateRequest extends BasePathFindRequest {
subcommand: 'create'
/**
* Unique address of the account to find a path from. In other words, the.
* Account that would be sending a payment.
*/
source_account: string
/** Unique address of the account to find a path to. */
destination_account: string
/**
* Currency Amount that the destination account would receive in a
* transaction.
*/
destination_amount: Amount
/** Currency amount that would be spent in the transaction. */
send_max?: Amount
/**
* Array of arrays of objects, representing payment paths to check. You can
* use this to keep updated on changes to particular paths you already know
* about, or to check the overall cost to make a payment along a certain path.
*/
paths?: Path[]
}
/** Stop sending pathfinding information. */
export interface PathFindCloseRequest extends BasePathFindRequest {
subcommand: 'close'
}
/** Get the information of the currently-open pathfinding request. */
export interface PathFindStatusRequest extends BasePathFindRequest {
subcommand: 'status'
}
/**
* The `path_find` method searches for a path along which a transaction can
* possibly be made, and periodically sends updates when the path changes over
* time. For a simpler version that is supported by JSON-RPC, see the
* `ripple_path_find` method.
*
* @category Requests
*/
export type PathFindRequest =
| PathFindCreateRequest
| PathFindCloseRequest
| PathFindStatusRequest
export interface PathFindPathOption {
/** Array of arrays of objects defining payment paths. */
paths_computed: Path[]
/**
* Currency Amount that the source would have to send along this path for the.
* Destination to receive the desired amount.
*/
source_amount: Amount
/**
* Destination Amount that the destination would receive along this path.
* If the `send_max` field is set, this field will be set.
*/
destination_amount?: Amount
}
/**
* Response expected from a {@link PathFindRequest}.
*
* @category Responses
*/
export interface PathFindResponse extends BaseResponse {
result: {
/**
* Array of objects with suggested paths to take, as described below. If
* empty, then no paths were found connecting the source and destination
* accounts.
*/
alternatives: PathFindPathOption[]
/** Unique address of the account that would receive a transaction. */
destination_account: string
/** Currency amount provided in the WebSocket request. */
destination_amount: Amount
/** Unique address that would send a transaction. */
source_account: string
/**
* If false, this is the result of an incomplete search. A later reply
* may have a better path. If true, then this is the best path found. (It is
* still theoretically possible that a better path could exist, but xahaud
* won't find it.) Until you close the pathfinding request, xahaud.
* Continues to send updates each time a new ledger closes.
*/
full_reply: boolean
/**
* The ID provided in the WebSocket request is included again at this
* level.
*/
id?: number | string
/**
* The value true indicates this reply is in response to a path_find close
* command.
*/
closed?: true
/**
* The value true indicates this reply is in response to a `path_find`
* status command.
*/
status?: true
}
}

View File

@@ -0,0 +1,21 @@
import type { BaseRequest, BaseResponse } from './baseMethod'
/**
* The ping command returns an acknowledgement, so that clients can test the
* connection status and latency. Expects a response in the form of a {@link
* PingResponse}.
*
* @category Requests
*/
export interface PingRequest extends BaseRequest {
command: 'ping'
}
/**
* Response expected from a {@link PingRequest}.
*
* @category Responses
*/
export interface PingResponse extends BaseResponse {
result: { role?: string; unlimited?: boolean }
}

View File

@@ -0,0 +1,23 @@
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The random command provides a random number to be used as a source of
* entropy for random number generation by clients. Expects a response in the
* form of a {@link RandomResponse}.
*
* @category Requests
*/
export interface RandomRequest extends BaseRequest {
command: 'random'
}
/**
* Response expected from a {@link RandomRequest}.
*
* @category Responses
*/
export interface RandomResponse extends BaseResponse {
result: {
random: string
}
}

View File

@@ -0,0 +1,81 @@
import { Amount, Path } from '../common'
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
export interface SourceCurrencyAmount {
currency: string
issuer?: string
}
/**
* The `ripple_path_find` method is a simplified version of the path_find method
* that provides a single response with a payment path you can use right away.
* Expects a response in the form of a {@link RipplePathFindResponse}.
*
* @category Requests
*/
export interface RipplePathFindRequest
extends BaseRequest,
LookupByLedgerRequest {
command: 'ripple_path_find'
/** Unique address of the account that would send funds in a transaction. */
source_account: string
/** Unique address of the account that would receive funds in a transaction. */
destination_account: string
/**
* Currency Amount that the destination account would receive in a
* transaction.
*/
destination_amount: Amount
/**
* Currency Amount that would be spent in the transaction. Cannot be used
* with `source_currencies`.
*/
send_max?: Amount
/**
* Array of currencies that the source account might want to spend. Each
* entry in the array should be a JSON object with a mandatory currency field
* and optional issuer field, like how currency amounts are specified.
*/
source_currencies?: SourceCurrencyAmount[]
}
export interface RipplePathFindPathOption {
/** Array of arrays of objects defining payment paths. */
paths_computed: Path[]
/**
* Currency amount that the source would have to send along this path for the
* destination to receive the desired amount.
*/
source_amount: Amount
}
/**
* Response expected from a {@link RipplePathFindRequest}.
*
* @category Responses
*/
export interface RipplePathFindResponse extends BaseResponse {
result: {
/**
* Array of objects with possible paths to take, as described below. If
* empty, then there are no paths connecting the source and destination
* accounts.
*/
alternatives: RipplePathFindPathOption[]
/** Unique address of the account that would receive a payment transaction. */
destination_account: string
/**
* Array of strings representing the currencies that the destination
* accepts, as 3-letter codes like "USD" or as 40-character hex like
* "015841551A748AD2C1F76FF6ECB0CCCD00000000".
*/
destination_currencies: string[]
destination_amount: Amount
full_reply?: boolean
id?: number | string
ledger_current_index?: number
source_account: string
validated: boolean
}
}

View File

@@ -0,0 +1,61 @@
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The `server_definitions` method retrieves information about the definition
* enums available in this xahaud node. Expects a response in the form of a
* {@link ServerDefinitionsResponse}.
*
* @category Requests
*/
export interface ServerDefinitionsRequest extends BaseRequest {
command: 'server_definitions'
/**
* The hash of a `server_definitions` response.
*/
hash?: string
}
/**
* Response expected from an {@link ServerDefinitionsRequest}.
*
* @category Responses
*/
export interface ServerDefinitionsResponse extends BaseResponse {
result: {
hash: string
} & (
| {
FIELDS: Array<
[
string,
{
nth: number
isVLEncoded: boolean
isSerialized: boolean
isSigningField: boolean
type: string
},
]
>
LEDGER_ENTRY_TYPES: Record<string, number>
TRANSACTION_RESULTS: Record<string, number>
TRANSACTION_TYPES: Record<string, number>
TYPES: Record<string, number>
}
| {
FIELDS?: never
LEDGER_ENTRY_TYPES?: never
TRANSACTION_RESULTS?: never
TRANSACTION_TYPES?: never
TYPES?: never
}
)
}

View File

@@ -0,0 +1,278 @@
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The `server_info` command asks the server for a human-readable version of
* various information about the xahaud server being queried. Expects a
* response in the form of a {@link ServerInfoResponse}.
*
* @category Requests
*/
export interface ServerInfoRequest extends BaseRequest {
command: 'server_info'
}
export type ServerState =
| 'disconnected'
| 'connected'
| 'syncing'
| 'tracking'
| 'full'
| 'validating'
| 'proposing'
export interface StateAccounting {
duration_us: string
transitions: string
}
export interface JobType {
job_type: string
per_second: number
peak_time?: number
avg_time?: number
in_progress?: number
}
export type protocol =
| 'http'
| 'https'
| 'grpc'
| 'peer'
| 'ws'
| 'ws2'
| 'wss'
| 'wss2'
export interface ServerPort {
port: string
/** The values in protocol are sorted in alphabetical order */
protocol: protocol[]
}
// The states for validating and proposing do not exist in the field state_accounting
// See https://github.com/XRPLF/xahaud/blob/develop/src/xahau/app/misc/NetworkOPs.cpp#L4545
// https://github.com/XRPLF/xahaud/blob/develop/src/xahau/app/misc/NetworkOPs.h#L66
export type StateAccountingFinal = Record<
Exclude<ServerState, 'validating' | 'proposing'>,
StateAccounting
>
/**
* Response expected from a {@link ServerInfoRequest}.
*
* @category Responses
*/
export interface ServerInfoResponse extends BaseResponse {
result: {
info: {
/**
* If true, this server is amendment blocked. If the server is not
* amendment blocked, the response omits this field.
*/
amendment_blocked?: boolean
/** The version number of the running xahaud version. */
build_version: string
/**
* Information on the most recently closed ledger that has not been
* validated by consensus. If the most recently validated ledger is
* available, the response omits this field and includes
* `validated_ledger` instead. The member fields are the same as the.
* `validated_ledger` field.
*/
closed_ledger?: {
age: number
base_fee_xrp: number
hash: string
reserve_base_xrp: number
reserve_inc_xrp: number
seq: number
}
/**
* Range expression indicating the sequence numbers of the ledger
* versions the local xahaud has in its database.
*/
complete_ledgers: string
/**
* On an admin request, returns the hostname of the server running the
* xahaud instance; otherwise, returns a single RFC-1751 word based on
* the node public key.
*/
hostid: string
/**
* Amount of time spent waiting for I/O operations, in milliseconds. If
* this number is not very, very low, then the xahaud server is probably
* having serious load issues.
*/
io_latency_ms: number
/**
* The number of times (since starting up) that this server has had over
* 250 transactions waiting to be processed at once. A large number here
* may mean that your server is unable to handle the transaction load of
* the XAH Ledger network.
*/
jq_trans_overflow: string
/**
* Information about the last time the server closed a ledger, including
* the amount of time it took to reach a consensus and the number of
* trusted validators participating.
*/
last_close: {
/**
* The amount of time it took to reach a consensus on the most recently
* validated ledger version, in seconds.
*/
converge_time_s: number
/**
* How many trusted validators the server considered (including itself,
* if configured as a validator) in the consensus process for the most
* recently validated ledger version.
*/
proposers: number
}
/**
* (Admin only) Detailed information about the current load state of the
* server.
*/
load?: {
/**
* (Admin only) Information about the rate of different types of jobs
* the server is doing and how much time it spends on each.
*/
job_types: JobType[]
/** (Admin only) The number of threads in the server's main job pool. */
threads: number
}
/**
* The load-scaled open ledger transaction cost the server is currently
* enforcing, as a multiplier on the base transaction cost. For example,
* at 1000 load factor and a reference transaction cost of 10 drops of
* XAH, the load-scaled transaction cost is 10,000 drops (0.01 XAH). The
* load factor is determined by the highest of the individual server's
* load factor, the cluster's load factor, the open ledger cost and the
* overall network's load factor.
*/
load_factor?: number
/**
* The network id of the server.
*/
network_id?: number
/**
* Current multiplier to the transaction cost based on
* load to this server.
*/
load_factor_local?: number
/**
* Current multiplier to the transaction cost being used by the rest of
* the network.
*/
load_factor_net?: number
/**
* Current multiplier to the transaction cost based on load to servers
* in this cluster.
*/
load_factor_cluster?: number
/**
* The current multiplier to the transaction cost that a transaction must
* pay to get into the open ledger.
*/
load_factor_fee_escalation?: number
/**
* The current multiplier to the transaction cost that a transaction must
* pay to get into the queue, if the queue is full.
*/
load_factor_fee_queue?: number
/**
* The load factor the server is enforcing, not including the open ledger
* cost.
*/
load_factor_server?: number
/**
* The number of peer connections which were severed.
*/
peer_disconnects?: string
/**
* The number of peer connections which were severed due to excess resource consumption.
*/
peer_disconnects_resources?: string
network_ledger?: 'waiting'
/** How many other xahaud servers this one is currently connected to. */
peers: number
/**
* What Websocket/RPC ports xahaud is listening on. This allows crawlers to build a richer topology without needing to
* port-scan nodes. For non-admin users (including peers), info about admin ports is excluded.
*/
ports: ServerPort[]
/**
* Public key used to verify this server for peer-to-peer communications.
* This node key pair is automatically generated by the server the first
* time it starts up. (If deleted, the server can create a new pair of
* Keys.).
*/
pubkey_node: string
/** Public key used by this node to sign ledger validations. */
pubkey_validator?: string
/**
* A string indicating to what extent the server is participating in the
* network.
*/
server_state: ServerState
/**
* The number of consecutive microseconds the server has been in the
* current state.
*/
server_state_duration_us: string
/**
* A map of various server states with information about the time the
* server spends in each. This can be useful for tracking the long-term
* health of your server's connectivity to the network.
*/
state_accounting: StateAccountingFinal
/** The current time in UTC, according to the server's clock. */
time: string
/** Number of consecutive seconds that the server has been operational. */
uptime: number
/** Information about the most recent fully-validated ledger. */
validated_ledger?: {
/** The time since the ledger was closed, in seconds. */
age: number
/**
* Base fee, in XAH. This may be represented in scientific notation.
* Such as 1e-05 for 0.00005.
*/
base_fee_xrp: number
/** Unique hash for the ledger, as hexadecimal. */
hash: string
/**
* Minimum amount of XAH (not drops) necessary for every account to.
* Keep in reserve .
*/
reserve_base_xrp: number
/**
* Amount of XAH (not drops) added to the account reserve for each
* object an account owns in the ledger.
*/
reserve_inc_xrp: number
/** The ledger index of the latest validated ledger. */
seq: number
}
/**
* Minimum number of trusted validations required to validate a ledger
* version. Some circumstances may cause the server to require more
* validations.
*/
validation_quorum: number
/**
* Either the human readable time, in UTC, when the current validator
* list will expire, the string unknown if the server has yet to load a
* published validator list or the string never if the server uses a
* static validator list.
*/
validator_list_expires?: string
validator_list?: {
count: number
expiration: 'never' | 'unknown' | string
status: 'active' | 'expired' | 'unknown'
}
}
}
}

View File

@@ -0,0 +1,78 @@
import { BaseRequest, BaseResponse } from './baseMethod'
import { JobType, ServerState, StateAccountingFinal } from './serverInfo'
/**
* The `server_state` command asks the server for various machine-readable
* information about the xahaud server's current state. The response is almost
* the same as the server_info method, but uses units that are easier to process
* instead of easier to read.
*
* @category Requests
*/
export interface ServerStateRequest extends BaseRequest {
command: 'server_state'
}
/**
* Response expected from a {@link ServerStateRequest}.
*
* @category Responses
*/
export interface ServerStateResponse extends BaseResponse {
result: {
state: {
amendment_blocked?: boolean
build_version: string
complete_ledgers: string
closed_ledger?: {
age: number
base_fee: number
hash: string
reserve_base: number
reserve_inc: number
seq: number
}
io_latency_ms: number
jq_trans_overflow: string
last_close: {
// coverage_time_s only exists for `server_info` requests. `server_state` is a "non human" api request,
// therefore the type is coverage_time
// See https://github.com/XRPLF/xahaud/blob/83faf43140e27e5d6d6779eaa0ffb75c33d98029/src/xahau/app/misc/NetworkOPs.cpp#L2458
converge_time: number
proposers: number
}
load?: {
job_types: JobType[]
threads: number
}
load_base: number
load_factor: number
load_factor_fee_escalation?: number
load_factor_fee_queue?: number
load_factor_fee_reference?: number
load_factor_server?: number
network_id?: number
peer_disconnects?: string
peer_disconnects_resources?: string
peers: number
pubkey_node: string
pubkey_validator?: string
server_state: ServerState
server_state_duration_us: string
state_accounting: StateAccountingFinal
time: string
uptime: number
validated_ledger?: {
age?: number
base_fee: number
close_time: number
hash: string
reserve_base: number
reserve_inc: number
seq: number
}
validation_quorum: number
validator_list_expires?: number
}
}
}

View File

@@ -0,0 +1,94 @@
import { SubmittableTransaction } from '../transactions'
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The submit method applies a transaction and sends it to the network to be
* confirmed and included in future ledgers. Expects a response in the form of a
* {@link SubmitResponse}.
*
* @category Requests
*/
export interface SubmitRequest extends BaseRequest {
command: 'submit'
/** The complete transaction in hex string format. */
tx_blob: string
/**
* If true, and the transaction fails locally, do not retry or relay the
* transaction to other servers. The default is false.
*/
fail_hard?: boolean
}
/**
* Response expected from a {@link SubmitRequest}.
*
* @category Responses
*/
export interface SubmitResponse extends BaseResponse {
result: {
/**
* Text result code indicating the preliminary result of the transaction,
* for example `tesSUCCESS`.
*/
engine_result: string
/** Numeric version of the result code. */
engine_result_code: number
/** Human-readable explanation of the transaction's preliminary result. */
engine_result_message: string
/** The complete transaction in hex string format. */
tx_blob: string
/** The complete transaction in JSON format. */
tx_json: SubmittableTransaction & { hash?: string }
/**
* The value true indicates that the transaction was applied, queued,
* broadcast, or kept for later. The value `false` indicates that none of
* those happened, so the transaction cannot possibly succeed as long as you
* do not submit it again and have not already submitted it another time.
*/
accepted: boolean
/**
* The next Sequence Number available for the sending account after all
* pending and queued transactions.
*/
account_sequence_available: number
/**
* The next Sequence number for the sending account after all transactions
* that have been provisionally applied, but not transactions in the queue.
*/
account_sequence_next: number
/**
* The value true indicates that this transaction was applied to the open
* ledger. In this case, the transaction is likely, but not guaranteed, to
* be validated in the next ledger version.
*/
applied: boolean
/**
* The value true indicates this transaction was broadcast to peer servers
* in the peer-to-peer XAH Ledger network.
*/
broadcast: boolean
/**
* The value true indicates that the transaction was kept to be retried
* later.
*/
kept: boolean
/**
* The value true indicates the transaction was put in the Transaction
* Queue, which means it is likely to be included in a future ledger
* version.
*/
queued: boolean
/**
* The current open ledger cost before processing this transaction
* transactions with a lower cost are likely to be queued.
*/
open_ledger_cost: string
/**
* The ledger index of the newest validated ledger at the time of
* submission. This provides a lower bound on the ledger versions that the
* transaction can appear in as a result of this request.
*/
validated_ledger_index: number
}
}

View File

@@ -0,0 +1,83 @@
import { APIVersion, DEFAULT_API_VERSION, XAHAUD_API_V1 } from '../common'
import { Transaction } from '../transactions'
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The `submit_multisigned` command applies a multi-signed transaction and sends
* it to the network to be included in future ledgers. Expects a response in the
* form of a {@link SubmitMultisignedRequest}.
*
* @category Requests
*/
export interface SubmitMultisignedRequest extends BaseRequest {
command: 'submit_multisigned'
/**
* Transaction in JSON format with an array of Signers. To be successful, the
* weights of the signatures must be equal or higher than the quorum of the.
* {@link Transaction Type/SignerList}.
*/
tx_json: Transaction
/**
* If true, and the transaction fails locally, do not retry or relay the
* transaction to other servers.
*/
fail_hard?: boolean
}
/**
* Common properties for multisigned transaction responses.
*
* @category Responses
*/
interface BaseSubmitMultisignedResult {
/**
* Code indicating the preliminary result of the transaction, for example.
* `tesSUCCESS`.
*/
engine_result: string
/**
* Numeric code indicating the preliminary result of the transaction,
* directly correlated to `engine_result`.
*/
engine_result_code: number
/** Human-readable explanation of the preliminary transaction result. */
engine_result_message: string
/** The complete transaction in hex string format. */
tx_blob: string
/** The complete transaction in JSON format. */
tx_json: Transaction
}
/**
* Response expected from a {@link SubmitMultisignedRequest}.
*
* @category Responses
*/
export interface SubmitMultisignedResponse extends BaseResponse {
result: BaseSubmitMultisignedResult & {
hash?: string
}
}
/**
* Response expected from a {@link SubmitMultisignedRequest} using api_version 1.
*
* @category ResponsesV1
*/
export interface SubmitMultisignedV1Response extends BaseResponse {
result: BaseSubmitMultisignedResult & {
tx_json: Transaction & { hash?: string }
}
}
/**
* Type to map between the API version and the response type.
*
* @category Responses
*/
export type SubmitMultisignedVersionResponseMap<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> = Version extends typeof XAHAUD_API_V1
? SubmitMultisignedV1Response
: SubmitMultisignedResponse

View File

@@ -0,0 +1,502 @@
import type {
Amount,
Currency,
Path,
StreamType,
ResponseOnlyTxInfo,
APIVersion,
DEFAULT_API_VERSION,
XAHAUD_API_V1,
XAHAUD_API_V2,
} from '../common'
import { Offer } from '../ledger'
import { OfferCreate, Transaction } from '../transactions'
import { TransactionMetadata } from '../transactions/metadata'
import type { BaseRequest, BaseResponse } from './baseMethod'
import { ManifestRequest } from './manifest'
export interface SubscribeBook {
/**
* Specification of which currency the account taking the Offer would
* receive, as a currency object with no amount.
*/
taker_gets: Currency
/**
* Specification of which currency the account taking the Offer would pay, as
* a currency object with no amount.
*/
taker_pays: Currency
/**
* Unique account address to use as a perspective for viewing offers, in the.
* XAH Ledger's base58 format.
*/
taker: string
/**
* If true, return the current state of the order book once when you
* subscribe before sending updates. The default is false.
*/
snapshot?: boolean
/** If true, return both sides of the order book. The default is false. */
both?: boolean
}
/**
* The subscribe method requests periodic notifications from the server when
* certain events happen. Expects a response in the form of a
* {@link SubscribeResponse}.
*
* @category Requests
*/
export interface SubscribeRequest extends BaseRequest {
command: 'subscribe'
/** Array of string names of generic streams to subscribe to. */
streams?: StreamType[]
/**
* Array with the unique addresses of accounts to monitor for validated
* transactions. The addresses must be in the XAH Ledger's base58 format.
* The server sends a notification for any transaction that affects at least
* one of these accounts.
*/
accounts?: string[]
/** Like accounts, but include transactions that are not yet finalized. */
accounts_proposed?: string[]
/**
* Array of objects defining order books to monitor for updates, as detailed
* Below.
*/
books?: SubscribeBook[]
/**
* URL where the server sends a JSON-RPC callbacks for each event.
* Admin-only.
*/
url?: string
/** Username to provide for basic authentication at the callback URL. */
url_username?: string
/** Password to provide for basic authentication at the callback URL. */
url_password?: string
}
export type BooksSnapshot = Offer[]
/**
* Response expected from a {@link SubscribeRequest}.
*
* @category Responses
*/
export interface SubscribeResponse extends BaseResponse {
result: Record<string, never> | LedgerStreamResponse | BooksSnapshot
}
interface BaseStream {
type: string
}
/**
* The `ledger` stream only sends `ledgerClosed` messages when the consensus
* process declares a new validated ledger. The message identifies the ledger
* And provides some information about its contents.
*
* @category Streams
*/
export interface LedgerStream extends BaseStream {
type: 'ledgerClosed'
/**
* The reference transaction cost as of this ledger version, in drops of XAH.
* If this ledger version includes a SetFee pseudo-transaction the new.
* Transaction cost applies starting with the following ledger version.
*/
fee_base: number
/** The reference transaction cost in "fee units". This is not returned after the SetFees amendment is enabled. */
fee_ref?: number
/** The identifying hash of the ledger version that was closed. */
ledger_hash: string
/** The ledger index of the ledger that was closed. */
ledger_index: number
/** The time this ledger was closed, in seconds since the Ripple Epoch. */
ledger_time: number
/**
* The minimum reserve, in drops of XAH, that is required for an account. If
* this ledger version includes a SetFee pseudo-transaction the new base reserve
* applies starting with the following ledger version.
*/
reserve_base: number
/**
* The owner reserve for each object an account owns in the ledger, in drops
* of XAH. If the ledger includes a SetFee pseudo-transaction the new owner
* reserve applies after this ledger.
*/
reserve_inc: number
/** Number of new transactions included in this ledger version. */
txn_count: number
/**
* Range of ledgers that the server has available. This may be a disjoint
* sequence such as 24900901-24900984,24901116-24901158. This field is not
* returned if the server is not connected to the network, or if it is
* connected but has not yet obtained a ledger from the network.
*/
validated_ledgers?: string
}
/**
* This response mirrors the LedgerStream, except it does NOT include the 'type' nor 'txn_count' fields.
*/
export interface LedgerStreamResponse {
/**
* The reference transaction cost as of this ledger version, in drops of XAH.
* If this ledger version includes a SetFee pseudo-transaction the new.
* Transaction cost applies starting with the following ledger version.
*/
fee_base: number
/** The reference transaction cost in "fee units". This is not returned after the SetFees amendment is enabled. */
fee_ref?: number
/** The identifying hash of the ledger version that was closed. */
ledger_hash: string
/** The ledger index of the ledger that was closed. */
ledger_index: number
/** The time this ledger was closed, in seconds since the Ripple Epoch. */
ledger_time: number
/**
* The minimum reserve, in drops of XAH, that is required for an account. If
* this ledger version includes a SetFee pseudo-transaction the new base reserve
* applies starting with the following ledger version.
*/
reserve_base: number
/**
* The owner reserve for each object an account owns in the ledger, in drops
* of XAH. If the ledger includes a SetFee pseudo-transaction the new owner
* reserve applies after this ledger.
*/
reserve_inc: number
/**
* Range of ledgers that the server has available. This may be a disjoint
* sequence such as 24900901-24900984,24901116-24901158. This field is not
* returned if the server is not connected to the network, or if it is
* connected but has not yet obtained a ledger from the network.
*/
validated_ledgers?: string
}
/**
* The validations stream sends messages whenever it receives validation
* messages, also called validation votes, regardless of whether or not the
* validation message is from a trusted validator.
*
* @category Streams
*/
export interface ValidationStream extends BaseStream {
type: 'validationReceived'
/**
* The value validationReceived indicates this is from the validations
* Stream.
*/
amendments?: string[]
/** The amendments this server wants to be added to the protocol. */
base_fee?: number
/**
* An arbitrary value chosen by the server at startup.
*
* If the same validation key pair signs validations with different cookies
* concurrently, that usually indicates that multiple servers are incorrectly
* configured to use the same validation key pair.
*/
cookie?: string
/**
* The contents of the validation message in its canonical binary form
*/
data?: string
/**
* The unscaled transaction cost (reference_fee value) this server wants to
* set by Fee voting.
*/
flags: number
/**
* Bit-mask of flags added to this validation message. The flag 0x80000000
* indicates that the validation signature is fully-canonical. The flag
* 0x00000001 indicates that this is a full validation; otherwise it's a
* partial validation. Partial validations are not meant to vote for any
* particular ledger. A partial validation indicates that the validator is
* still online but not keeping up with consensus.
*/
full: boolean
/**
* If true, this is a full validation. Otherwise, this is a partial
* validation. Partial validations are not meant to vote for any particular
* ledger. A partial validation indicates that the validator is still online
* but not keeping up with consensus.
*/
ledger_hash: string
/** The ledger index of the proposed ledger. */
ledger_index: string
/**
* The local load-scaled transaction cost this validator is currently
* enforcing, in fee units.
*/
load_fee?: number
/**
* The validator's master public key, if the validator is using a validator
* token, in the XAH Ledger's base58 format.
*/
master_key?: string
/**
* The minimum reserve requirement (`account_reserve` value) this validator
* wants to set by fee voting.
*/
reserve_base?: number
/**
* The increment in the reserve requirement (owner_reserve value) this
* validator wants to set by fee voting.
*/
reserve_inc?: number
/** The signature that the validator used to sign its vote for this ledger. */
signature: string
/** When this validation vote was signed, in seconds since the Ripple Epoch. */
signing_time: number
/**
* The public key from the key-pair that the validator used to sign the
* message, in the XAH Ledger's base58 format. This identifies the validator
* sending the message and can also be used to verify the signature. If the
* validator is using a token, this is an ephemeral public key.
*/
validation_public_key: string
}
/**
* Many subscriptions result in messages about transactions.
*
* @category Streams
*/
interface TransactionStreamBase<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> extends BaseStream {
status: string
type: 'transaction'
/**
* The approximate time this ledger was closed, in date time string format.
* Always uses the UTC time zone.
*/
close_time_iso: string
/** String Transaction result code. */
engine_result: string
/** Numeric transaction response code, if applicable. */
engine_result_code: number
/** Human-readable explanation for the transaction response. */
engine_result_message: string
/**
* The ledger index of the current in-progress ledger version for which this
* transaction is currently proposed.
*/
ledger_current_index?: number
/** The identifying hash of the ledger version that includes this transaction. */
ledger_hash?: string
/** The ledger index of the ledger version that includes this transaction. */
ledger_index?: number
/**
* The transaction metadata, which shows the exact outcome of the transaction
* in detail.
*/
meta?: TransactionMetadata
/** JSON object defining the transaction. */
tx_json?: Version extends typeof XAHAUD_API_V2
? Transaction & ResponseOnlyTxInfo
: never
/** JSON object defining the transaction in xahaud API v1. */
transaction?: Version extends typeof XAHAUD_API_V1
? Transaction & ResponseOnlyTxInfo
: never
/**
* If true, this transaction is included in a validated ledger and its
* outcome is final. Responses from the transaction stream should always be
* validated.
*/
validated?: boolean
warnings?: Array<{ id: number; message: string }>
}
/**
* Expected response from an {@link AccountTxRequest}.
*
* @category Streams
*/
export type TransactionStream = TransactionStreamBase
/**
* Expected response from an {@link AccountTxRequest} with `api_version` set to 1.
*
* @category Streams
*/
export type TransactionV1Stream = TransactionStreamBase
/**
* The admin-only `peer_status` stream reports a large amount of information on
* the activities of other xahaud servers to which this server is connected, in
* particular their status in the consensus process.
*
* @category Streams
*/
export interface PeerStatusStream extends BaseStream {
type: 'peerStatusChange'
/**
* The type of event that prompted this message. See Peer Status Events for
* possible values.
*/
action: 'CLOSING_LEDGER' | 'ACCEPTED_LEDGER' | 'SWITCHED_LEDGER' | 'LOST_SYNC'
/** The time this event occurred, in seconds since the Ripple Epoch. */
date: number
/** The identifying Hash of a ledger version to which this message pertains. */
ledger_hash?: string
/** The Ledger Index of a ledger version to which this message pertains. */
ledger_index?: number
/** The largest Ledger Index the peer has currently available. */
ledger_index_max?: number
/** The smallest Ledger Index the peer has currently available. */
ledger_index_min?: number
}
/**
* The format of an order book stream message is the same as that of
* transaction stream messages, except that OfferCreate transactions also
* contain the following field.
*/
interface ModifiedOfferCreateTransaction extends OfferCreate {
/**
* Numeric amount of the TakerGets currency that the Account sending this
* OfferCreate transaction has after executing this transaction. This does not
* check whether the currency amount is frozen.
*/
owner_funds: string
}
/**
* When you subscribe to one or more order books with the `books` field, you
* get back any transactions that affect those order books. Has the same format
* as a {@link TransactionStream} but the transaction can have a `owner_funds`
* field.
*
* @category Streams
*/
export interface OrderBookStream extends BaseStream {
status: string
type: 'transaction'
engine_result: string
engine_result_code: number
engine_result_message: string
ledger_current_index?: number
ledger_hash?: string
ledger_index?: number
meta: TransactionMetadata
transaction: (Transaction | ModifiedOfferCreateTransaction) & {
/**
* This number measures the number of seconds since the "Ripple Epoch" of January 1, 2000 (00:00 UTC)
*/
date?: number
/**
* Every signed transaction has a unique "hash" that identifies it.
* The transaction hash can be used to look up its final status, which may serve as a "proof of payment"
*/
hash?: string
}
validated: boolean
}
/**
* The consensus stream sends consensusPhase messages when the consensus
* process changes phase. The message contains the new phase of consensus the
* server is in.
*
* @category Streams
*/
export interface ConsensusStream extends BaseStream {
type: 'consensusPhase'
/**
* The new consensus phase the server is in. Possible values are open,
* establish, and accepted.
*/
consensus: 'open' | 'establish' | 'accepted'
}
/**
* The path_find method searches for a path along which a transaction can
* possibly be made, and periodically sends updates when the path changes over
* time.
*
* @category Streams
*/
export interface PathFindStream extends BaseStream {
type: 'path_find'
/** Unique address that would send a transaction. */
source_account: string
/** Unique address of the account that would receive a transaction. */
destination_account: string
/** Currency Amount that the destination would receive in a transaction. */
destination_amount: Amount
/**
* If false, this is the result of an incomplete search. A later reply may
* have a better path. If true, then this is the best path found. (It is still
* theoretically possible that a better path could exist, but xahaud won't
* find it.) Until you close the pathfinding request, xahaud continues to
* send updates each time a new ledger closes.
*/
full_reply: boolean
/** The ID provided in the WebSocket request is included again at this level. */
id: number | string
/** Currency Amount that would be spent in the transaction. */
send_max?: Amount
/**
* Array of objects with suggested paths to take. If empty, then no paths
* were found connecting the source and destination accounts.
*/
alternatives:
| []
| {
paths_computed: Path[]
source_amount: Amount
}
}
/**
* @category Streams
*/
export type Stream =
| LedgerStream
| ValidationStream
| TransactionStream
| PathFindStream
| PeerStatusStream
| OrderBookStream
| ConsensusStream
export type EventTypes =
| 'connected'
| 'disconnected'
| 'ledgerClosed'
| 'validationReceived'
| 'transaction'
| 'peerStatusChange'
| 'consensusPhase'
| 'manifestReceived'
| 'path_find'
| 'error'
export type OnEventToListenerMap<T extends EventTypes> = 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
: (...args: never[]) => void

View File

@@ -0,0 +1,47 @@
import { ResponseOnlyTxInfo } from '../common'
import { Transaction, TransactionMetadata } from '../transactions'
import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod'
/**
* The `transaction_entry` method retrieves information on a single transaction
* from a specific ledger version. Expects a response in the form of a
* {@link TransactionEntryResponse}.
*
* @category Requests
*/
export interface TransactionEntryRequest
extends BaseRequest,
LookupByLedgerRequest {
command: 'transaction_entry'
/** Unique hash of the transaction you are looking up. */
tx_hash: string
}
/**
* Response expected from a {@link TransactionEntryRequest}.
*
* @category Responses
*/
export interface TransactionEntryResponse extends BaseResponse {
result: {
/**
* The identifying hash of the ledger version the transaction was found in;
* this is the same as the one from the request.
*/
ledger_hash: string
/**
* The ledger index of the ledger version the transaction was found in;
* this is the same as the one from the request.
*/
ledger_index: number
/**
* The transaction metadata, which shows the exact results of the
* transaction in detail.
*/
metadata: TransactionMetadata
/** JSON representation of the Transaction object. */
tx_json: Transaction & ResponseOnlyTxInfo
}
}

View File

@@ -0,0 +1,132 @@
import {
APIVersion,
DEFAULT_API_VERSION,
XAHAUD_API_V1,
XAHAUD_API_V2,
} from '../common'
import { Transaction, TransactionMetadata } from '../transactions'
import { BaseTransaction } from '../transactions/common'
import { BaseRequest, BaseResponse } from './baseMethod'
/**
* The tx method retrieves information on a single transaction, by its
* identifying hash. Expects a response in the form of a {@link TxResponse}.
*
* @category Requests
*/
export interface TxRequest extends BaseRequest {
command: 'tx'
/**
* The transaction hash to look up. Exactly one of `transaction` or `ctid` must be specified for a TxRequest.
*/
transaction?: string
/**
* The Concise Transaction ID to look up. Exactly one of `transaction` or `ctid` must be specified for a TxRequest.
*/
ctid?: string
/**
* If true, return transaction data and metadata as binary serialized to
* hexadecimal strings. If false, return transaction data and metadata as.
* JSON. The default is false.
*/
binary?: boolean
/**
* Use this with max_ledger to specify a range of up to 1000 ledger indexes,
* starting with this ledger (inclusive). If the server cannot find the
* transaction, it confirms whether it was able to search all the ledgers in
* this range.
*/
min_ledger?: number
/**
* Use this with min_ledger to specify a range of up to 1000 ledger indexes,
* ending with this ledger (inclusive). If the server cannot find the
* transaction, it confirms whether it was able to search all the ledgers in
* the requested range.
*/
max_ledger?: number
}
/**
* Common properties of transaction responses.
*
* @category Responses
*/
interface BaseTxResult<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
T extends BaseTransaction = Transaction,
> {
/** The SHA-512 hash of the transaction. */
hash: string
/**
* The Concise Transaction Identifier of the transaction (16-byte hex string)
*/
ctid?: string
/** The ledger index of the ledger that includes this transaction. */
ledger_index?: number
/** Unique hashed string Transaction metadata blob, which describes the results of the transaction.
* Can be undefined if a transaction has not been validated yet. This field is omitted if binary
* binary format is not requested. */
meta_blob?: Version extends typeof XAHAUD_API_V2
? TransactionMetadata<T> | string
: never
/** Transaction metadata, which describes the results of the transaction.
* Can be undefined if a transaction has not been validated yet. */
meta?: TransactionMetadata<T> | string
/**
* If true, this data comes from a validated ledger version; if omitted or.
* Set to false, this data is not final.
*/
validated?: boolean
/**
* The time the transaction was closed, in seconds since the Ripple Epoch.
*/
close_time_iso?: string
/**
* This number measures the number of seconds since the "Ripple Epoch" of January 1, 2000 (00:00 UTC)
*/
date?: number
}
/**
* Response expected from a {@link TxRequest}.
*
* @category Responses
*/
export interface TxResponse<T extends BaseTransaction = Transaction>
extends BaseResponse {
result: BaseTxResult<typeof XAHAUD_API_V2, T> & { tx_json: T }
/**
* If true, the server was able to search all of the specified ledger
* versions, and the transaction was in none of them. If false, the server did
* not have all of the specified ledger versions available, so it is not sure.
* If one of them might contain the transaction.
*/
searched_all?: boolean
}
/**
* Response expected from a {@link TxRequest} using API version 1.
*
* @category ResponsesV1
*/
export interface TxV1Response<T extends BaseTransaction = Transaction>
extends BaseResponse {
result: BaseTxResult<typeof XAHAUD_API_V1, T> & T
/**
* If true, the server was able to search all of the specified ledger
* versions, and the transaction was in none of them. If false, the server did
* not have all of the specified ledger versions available, so it is not sure.
* If one of them might contain the transaction.
*/
searched_all?: boolean
}
/**
* Type to map between the API version and the response type.
*
* @category Responses
*/
export type TxVersionResponseMap<
Version extends APIVersion = typeof DEFAULT_API_VERSION,
> = Version extends typeof XAHAUD_API_V1 ? TxV1Response : TxResponse

View File

@@ -0,0 +1,49 @@
import { Currency, StreamType } from '../common'
import type { BaseRequest, BaseResponse } from './baseMethod'
export interface UnsubscribeBook {
taker_gets: Currency
taker_pays: Currency
both?: boolean
}
/**
* The unsubscribe command tells the server to stop sending messages for a
* particular subscription or set of subscriptions. Expects a response in the
* form of an {@link UnsubscribeResponse}.
*
* @category Requests
*/
export interface UnsubscribeRequest extends BaseRequest {
command: 'unsubscribe'
/**
* Array of string names of generic streams to unsubscribe from, including.
* Ledger, server, transactions, and transactions_proposed.
*/
streams?: StreamType[]
/**
* Array of unique account addresses to stop receiving updates for, in the.
* XAH Ledger's base58 format.
*/
accounts?: string[]
/**
* Like accounts, but for accounts_proposed subscriptions that included
* not-yet-validated transactions.
*/
accounts_proposed?: string[]
/**
* Array of objects defining order books to unsubscribe from, as explained
* below.
*/
books?: UnsubscribeBook[]
}
/**
* Response expected from a {@link UnsubscribeRequest}.
*
* @category Responses
*/
export interface UnsubscribeResponse extends BaseResponse {
result: Record<string, never>
}

View File

@@ -0,0 +1,22 @@
import { BaseTransaction } from './common'
/**
* Mark a change to the Negative UNL.
*
* @category Pseudo Transaction Models
*/
export interface UNLModify extends BaseTransaction {
TransactionType: 'UNLModify'
/**
* The ledger index where this pseudo-transaction appears.
* This distinguishes the pseudo-transaction from other occurrences of the same change.
*/
LedgerSequence: number
/**
* If 0, this change represents removing a validator from the Negative UNL.
* If 1, this change represents adding a validator to the Negative UNL.
*/
UNLModifyDisabling: 0 | 1
/** The validator to add or remove, as identified by its master public key. */
UNLModifyValidator: string
}

View File

@@ -0,0 +1,225 @@
import { ValidationError } from '../../errors'
import {
Account,
BaseTransaction,
isAccount,
validateBaseTransaction,
validateOptionalField,
} from './common'
/**
* Enum for AccountSet Flags.
*
* @category Transaction Flags
*/
export enum AccountSetAsfFlags {
/** Require a destination tag to send transactions to this account. */
asfRequireDest = 1,
/**
* Require authorization for users to hold balances issued by this address
* can only be enabled if the address has no trust lines connected to it.
*/
asfRequireAuth = 2,
/** XAH should not be sent to this account. */
asfDisallowXAH = 3,
/**
* Disallow use of the master key pair. Can only be enabled if the account
* has configured another way to sign transactions, such as a Regular Key or a
* Signer List.
*/
asfDisableMaster = 4,
/**
* Track the ID of this account's most recent transaction. Required for
* AccountTxnID.
*/
asfAccountTxnID = 5,
/**
* Permanently give up the ability to freeze individual trust lines or
* disable Global Freeze. This flag can never be disabled after being enabled.
*/
asfNoFreeze = 6,
/** Freeze all assets issued by this account. */
asfGlobalFreeze = 7,
/** Enable rippling on this account's trust lines by default. */
asfDefaultRipple = 8,
/** Enable Deposit Authorization on this account. */
asfDepositAuth = 9,
/**
* Allow another account to mint and burn tokens on behalf of this account.
*/
asfAuthorizedNFTokenMinter = 10,
/** asf 11 is reserved for Hooks amendment */
/** Disallow other accounts from creating incoming NFTOffers */
asfDisallowIncomingNFTokenOffer = 12,
/** Disallow other accounts from creating incoming Checks */
asfDisallowIncomingCheck = 13,
/** Disallow other accounts from creating incoming PayChannels */
asfDisallowIncomingPayChan = 14,
/** Disallow other accounts from creating incoming Trustlines */
asfDisallowIncomingTrustline = 15,
/** Permanently gain the ability to claw back issued IOUs */
asfAllowTrustLineClawback = 16,
}
/**
* Enum for AccountSet Transaction Flags.
*
* @category Transaction Flags
*/
export enum AccountSetTfFlags {
/** The same as SetFlag: asfRequireDest. */
tfRequireDestTag = 0x00010000,
/** The same as ClearFlag: asfRequireDest. */
tfOptionalDestTag = 0x00020000,
/** The same as SetFlag: asfRequireAuth. */
tfRequireAuth = 0x00040000,
/** The same as ClearFlag: asfRequireAuth. */
tfOptionalAuth = 0x00080000,
/** The same as SetFlag: asfDisallowXAH. */
tfDisallowXAH = 0x00100000,
/** The same as ClearFlag: asfDisallowXAH. */
tfAllowXAH = 0x00200000,
}
/**
* Map of flags to boolean values representing {@link AccountSet} transaction
* flags.
*
* @category Transaction Flags
*
* @example
* ```typescript
* const accountSetTx: AccountSet = {
* TransactionType: 'AccountSet',
* Account: 'rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn',
* Flags: {
* tfOptionalDestTag: true,
* tfRequireAuth: true
* },
* }
*
* // Autofill the tx to see how flags actually look compared to the interface usage.
* const autofilledTx = await client.autofill(accountSetTx)
* console.log(autofilledTx)
* // {
* // TransactionType: 'AccountSet',
* // Account: 'rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn',
* // Flags: 393216,
* // Sequence: 1,
* // Fee: '12',
* // LastLedgerSequence: 21971793
* // }
* ```
*/
export interface AccountSetFlagsInterface {
tfRequireDestTag?: boolean
tfOptionalDestTag?: boolean
tfRequireAuth?: boolean
tfOptionalAuth?: boolean
tfDisallowXAH?: boolean
tfAllowXAH?: boolean
}
/**
* An AccountSet transaction modifies the properties of an account in the XAH
* Ledger.
*
* @category Transaction Models
*/
export interface AccountSet extends BaseTransaction {
TransactionType: 'AccountSet'
Flags?: number | AccountSetFlagsInterface
/** Unique identifier of a flag to disable for this account. */
ClearFlag?: number
/**
* The domain that owns this account, as a string of hex representing the.
* ASCII for the domain in lowercase.
*/
Domain?: string
/** Hash of an email address to be used for generating an avatar image. */
EmailHash?: string
/** Public key for sending encrypted messages to this account. */
MessageKey?: string
/** Integer flag to enable for this account. */
SetFlag?: AccountSetAsfFlags
/**
* The fee to charge when users transfer this account's issued currencies,
* represented as billionths of a unit. Cannot be more than 2000000000 or less
* than 1000000000, except for the special case 0 meaning no fee.
*/
TransferRate?: number
/**
* Tick size to use for offers involving a currency issued by this address.
* The exchange rates of those offers is rounded to this many significant
* digits. Valid values are 3 to 15 inclusive, or 0 to disable.
*/
TickSize?: number
/**
* Sets an alternate account that is allowed to mint NFTokens on this
* account's behalf using NFTokenMint's `Issuer` field.
*/
NFTokenMinter?: Account
}
const MIN_TICK_SIZE = 3
const MAX_TICK_SIZE = 15
/**
* Verify the form and type of an AccountSet at runtime.
*
* @param tx - An AccountSet Transaction.
* @throws When the AccountSet is Malformed.
*/
// eslint-disable-next-line max-lines-per-function -- okay for this method, only a little over
export function validateAccountSet(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
validateOptionalField(tx, 'NFTokenMinter', isAccount)
if (tx.ClearFlag !== undefined) {
if (typeof tx.ClearFlag !== 'number') {
throw new ValidationError('AccountSet: invalid ClearFlag')
}
if (!Object.values(AccountSetAsfFlags).includes(tx.ClearFlag)) {
throw new ValidationError('AccountSet: invalid ClearFlag')
}
}
if (tx.Domain !== undefined && typeof tx.Domain !== 'string') {
throw new ValidationError('AccountSet: invalid Domain')
}
if (tx.EmailHash !== undefined && typeof tx.EmailHash !== 'string') {
throw new ValidationError('AccountSet: invalid EmailHash')
}
if (tx.MessageKey !== undefined && typeof tx.MessageKey !== 'string') {
throw new ValidationError('AccountSet: invalid MessageKey')
}
if (tx.SetFlag !== undefined) {
if (typeof tx.SetFlag !== 'number') {
throw new ValidationError('AccountSet: invalid SetFlag')
}
if (!Object.values(AccountSetAsfFlags).includes(tx.SetFlag)) {
throw new ValidationError('AccountSet: invalid SetFlag')
}
}
if (tx.TransferRate !== undefined && typeof tx.TransferRate !== 'number') {
throw new ValidationError('AccountSet: invalid TransferRate')
}
if (tx.TickSize !== undefined) {
if (typeof tx.TickSize !== 'number') {
throw new ValidationError('AccountSet: invalid TickSize')
}
if (
tx.TickSize !== 0 &&
(tx.TickSize < MIN_TICK_SIZE || tx.TickSize > MAX_TICK_SIZE)
) {
throw new ValidationError('AccountSet: invalid TickSize')
}
}
}

View File

@@ -0,0 +1,34 @@
import { ValidationError } from '../../errors'
import { BaseTransaction, validateBaseTransaction } from './common'
/**
* Cancels an unredeemed Check, removing it from the ledger without sending any
* money. The source or the destination of the check can cancel a Check at any
* time using this transaction type. If the Check has expired, any address can
* cancel it.
*
* @category Transaction Models
*/
export interface CheckCancel extends BaseTransaction {
TransactionType: 'CheckCancel'
/**
* The ID of the Check ledger object to cancel as a 64-character hexadecimal
* string.
*/
CheckID: string
}
/**
* Verify the form and type of an CheckCancel at runtime.
*
* @param tx - An CheckCancel Transaction.
* @throws When the CheckCancel is Malformed.
*/
export function validateCheckCancel(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.CheckID !== undefined && typeof tx.CheckID !== 'string') {
throw new ValidationError('CheckCancel: invalid CheckID')
}
}

View File

@@ -0,0 +1,72 @@
import { ValidationError } from '../../errors'
import { Amount } from '../common'
import { BaseTransaction, validateBaseTransaction, isAmount } from './common'
/**
* Attempts to redeem a Check object in the ledger to receive up to the amount
* authorized by the corresponding CheckCreate transaction. Only the Destination
* address of a Check can cash it with a CheckCash transaction.
*
* @category Transaction Models
*/
export interface CheckCash extends BaseTransaction {
TransactionType: 'CheckCash'
/**
* The ID of the Check ledger object to cash as a 64-character hexadecimal
* string.
*/
CheckID: string
/**
* Redeem the Check for exactly this amount, if possible. The currency must
* match that of the SendMax of the corresponding CheckCreate transaction. You.
* must provide either this field or DeliverMin.
*/
Amount?: Amount
/**
* Redeem the Check for at least this amount and for as much as possible. The
* currency must match that of the SendMax of the corresponding CheckCreate.
* transaction. You must provide either this field or Amount.
*/
DeliverMin?: Amount
}
/**
* Verify the form and type of an CheckCash at runtime.
*
* @param tx - An CheckCash Transaction.
* @throws When the CheckCash is Malformed.
*/
export function validateCheckCash(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.Amount == null && tx.DeliverMin == null) {
throw new ValidationError(
'CheckCash: must have either Amount or DeliverMin',
)
}
if (tx.Amount != null && tx.DeliverMin != null) {
throw new ValidationError(
'CheckCash: cannot have both Amount and DeliverMin',
)
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Necessary check
if (tx.Amount != null && tx.Amount !== undefined && !isAmount(tx.Amount)) {
throw new ValidationError('CheckCash: invalid Amount')
}
if (
tx.DeliverMin != null &&
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- Necessary check
tx.DeliverMin !== undefined &&
!isAmount(tx.DeliverMin)
) {
throw new ValidationError('CheckCash: invalid DeliverMin')
}
if (tx.CheckID !== undefined && typeof tx.CheckID !== 'string') {
throw new ValidationError('CheckCash: invalid CheckID')
}
}

View File

@@ -0,0 +1,82 @@
import { ValidationError } from '../../errors'
import { Amount } from '../common'
import {
BaseTransaction,
validateBaseTransaction,
isIssuedCurrency,
isAccount,
validateRequiredField,
validateOptionalField,
isNumber,
Account,
} from './common'
/**
* Create a Check object in the ledger, which is a deferred payment that can be
* cashed by its intended destination. The sender of this transaction is the
* sender of the Check.
*
* @category Transaction Models
*/
export interface CheckCreate extends BaseTransaction {
TransactionType: 'CheckCreate'
/** The unique address of the account that can cash the Check. */
Destination: Account
/**
* Maximum amount of source currency the Check is allowed to debit the
* sender, including transfer fees on non-XAH currencies. The Check can only
* credit the destination with the same currency (from the same issuer, for
* non-XAH currencies). For non-XAH amounts, the nested field names MUST be.
* lower-case.
*/
SendMax: Amount
/**
* Arbitrary tag that identifies the reason for the Check, or a hosted.
* recipient to pay.
*/
DestinationTag?: number
/**
* Time after which the Check is no longer valid, in seconds since the Ripple.
* Epoch.
*/
Expiration?: number
/**
* Arbitrary 256-bit hash representing a specific reason or identifier for.
* this Check.
*/
InvoiceID?: string
}
/**
* Verify the form and type of an CheckCreate at runtime.
*
* @param tx - An CheckCreate Transaction.
* @throws When the CheckCreate is Malformed.
*/
export function validateCheckCreate(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.SendMax === undefined) {
throw new ValidationError('CheckCreate: missing field SendMax')
}
validateRequiredField(tx, 'Destination', isAccount)
validateOptionalField(tx, 'DestinationTag', isNumber)
if (
typeof tx.SendMax !== 'string' &&
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Only used by JS
!isIssuedCurrency(tx.SendMax as Record<string, unknown>)
) {
throw new ValidationError('CheckCreate: invalid SendMax')
}
if (tx.Expiration !== undefined && typeof tx.Expiration !== 'number') {
throw new ValidationError('CheckCreate: invalid Expiration')
}
if (tx.InvoiceID !== undefined && typeof tx.InvoiceID !== 'string') {
throw new ValidationError('CheckCreate: invalid InvoiceID')
}
}

View File

@@ -0,0 +1,368 @@
import { isValidClassicAddress, isValidXAddress } from 'xahau-address-codec'
import { TRANSACTION_TYPES } from 'xahau-binary-codec'
import { ValidationError } from '../../errors'
import {
Amount,
Currency,
IssuedCurrencyAmount,
Memo,
Signer,
XChainBridge,
} from '../common'
import { onlyHasFields } from '../utils'
const MEMO_SIZE = 3
function isMemo(obj: { Memo?: unknown }): boolean {
if (obj.Memo == null) {
return false
}
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Only used by JS
const memo = obj.Memo as Record<string, unknown>
const size = Object.keys(memo).length
const validData = memo.MemoData == null || typeof memo.MemoData === 'string'
const validFormat =
memo.MemoFormat == null || typeof memo.MemoFormat === 'string'
const validType = memo.MemoType == null || typeof memo.MemoType === 'string'
return (
size >= 1 &&
size <= MEMO_SIZE &&
validData &&
validFormat &&
validType &&
onlyHasFields(memo, ['MemoFormat', 'MemoData', 'MemoType'])
)
}
const SIGNER_SIZE = 3
function isSigner(obj: unknown): boolean {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Only used by JS
const signerWrapper = obj as Record<string, unknown>
if (signerWrapper.Signer == null) {
return false
}
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Only used by JS and Signer is previously unknown
const signer = signerWrapper.Signer as Record<string, unknown>
return (
Object.keys(signer).length === SIGNER_SIZE &&
typeof signer.Account === 'string' &&
typeof signer.TxnSignature === 'string' &&
typeof signer.SigningPubKey === 'string'
)
}
const XAH_CURRENCY_SIZE = 1
const ISSUE_SIZE = 2
const ISSUED_CURRENCY_SIZE = 3
const XCHAIN_BRIDGE_SIZE = 4
function isRecord(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === 'object'
}
/**
* Verify the form and type of a string at runtime.
*
* @param str - The object to check the form and type of.
* @returns Whether the string is properly formed.
*/
export function isString(str: unknown): str is string {
return typeof str === 'string'
}
/**
* Verify the form and type of a number at runtime.
*
* @param num - The object to check the form and type of.
* @returns Whether the number is properly formed.
*/
export function isNumber(num: unknown): num is number {
return typeof num === 'number'
}
/**
* Verify the form and type of an IssuedCurrency at runtime.
*
* @param input - The input to check the form and type of.
* @returns Whether the IssuedCurrency is properly formed.
*/
export function isCurrency(input: unknown): input is Currency {
return (
isRecord(input) &&
((Object.keys(input).length === ISSUE_SIZE &&
typeof input.issuer === 'string' &&
typeof input.currency === 'string') ||
(Object.keys(input).length === XAH_CURRENCY_SIZE &&
input.currency === 'XAH'))
)
}
/**
* Verify the form and type of an IssuedCurrencyAmount at runtime.
*
* @param input - The input to check the form and type of.
* @returns Whether the IssuedCurrencyAmount is properly formed.
*/
export function isIssuedCurrency(
input: unknown,
): input is IssuedCurrencyAmount {
return (
isRecord(input) &&
Object.keys(input).length === ISSUED_CURRENCY_SIZE &&
typeof input.value === 'string' &&
typeof input.issuer === 'string' &&
typeof input.currency === 'string'
)
}
/**
* Must be a valid account address
*/
export type Account = string
/**
* Verify a string is in fact a valid account address.
*
* @param account - The object to check the form and type of.
* @returns Whether the account is properly formed account for a transaction.
*/
export function isAccount(account: unknown): account is Account {
return (
typeof account === 'string' &&
(isValidClassicAddress(account) || isValidXAddress(account))
)
}
/**
* Verify the form and type of an Amount at runtime.
*
* @param amount - The object to check the form and type of.
* @returns Whether the Amount is properly formed.
*/
export function isAmount(amount: unknown): amount is Amount {
return typeof amount === 'string' || isIssuedCurrency(amount)
}
/**
* Verify the form and type of an XChainBridge at runtime.
*
* @param input - The input to check the form and type of.
* @returns Whether the XChainBridge is properly formed.
*/
export function isXChainBridge(input: unknown): input is XChainBridge {
return (
isRecord(input) &&
Object.keys(input).length === XCHAIN_BRIDGE_SIZE &&
typeof input.LockingChainDoor === 'string' &&
isCurrency(input.LockingChainIssue) &&
typeof input.IssuingChainDoor === 'string' &&
isCurrency(input.IssuingChainIssue)
)
}
/* eslint-disable @typescript-eslint/restrict-template-expressions -- tx.TransactionType is checked before any calls */
/**
* Verify the form and type of a required type for a transaction at runtime.
*
* @param tx - The transaction input to check the form and type of.
* @param paramName - The name of the transaction parameter.
* @param checkValidity - The function to use to check the type.
* @throws
*/
export function validateRequiredField(
tx: Record<string, unknown>,
paramName: string,
checkValidity: (inp: unknown) => boolean,
): void {
if (tx[paramName] == null) {
throw new ValidationError(
`${tx.TransactionType}: missing field ${paramName}`,
)
}
if (!checkValidity(tx[paramName])) {
throw new ValidationError(
`${tx.TransactionType}: invalid field ${paramName}`,
)
}
}
/**
* Verify the form and type of an optional type for a transaction at runtime.
*
* @param tx - The transaction input to check the form and type of.
* @param paramName - The name of the transaction parameter.
* @param checkValidity - The function to use to check the type.
* @throws
*/
export function validateOptionalField(
tx: Record<string, unknown>,
paramName: string,
checkValidity: (inp: unknown) => boolean,
): void {
if (tx[paramName] !== undefined && !checkValidity(tx[paramName])) {
throw new ValidationError(
`${tx.TransactionType}: invalid field ${paramName}`,
)
}
}
/* eslint-enable @typescript-eslint/restrict-template-expressions -- checked before */
// eslint-disable-next-line @typescript-eslint/no-empty-interface -- no global flags right now, so this is fine
export interface GlobalFlags {}
/**
* Every transaction has the same set of common fields.
*/
export interface BaseTransaction {
/** The unique address of the transaction sender. */
Account: Account
/**
* The type of transaction. Valid types include: `Payment`, `OfferCreate`,
* `TrustSet`, and many others.
*/
TransactionType: string
/**
* Integer amount of XAH, in drops, to be destroyed as a cost for
* distributing this transaction to the network. Some transaction types have
* different minimum requirements.
*/
Fee?: string
/**
* The sequence number of the account sending the transaction. A transaction
* is only valid if the Sequence number is exactly 1 greater than the previous
* transaction from the same account. The special case 0 means the transaction
* is using a Ticket instead.
*/
Sequence?: number
/**
* Hash value identifying another transaction. If provided, this transaction
* is only valid if the sending account's previously-sent transaction matches
* the provided hash.
*/
AccountTxnID?: string
/** Set of bit-flags for this transaction. */
Flags?: number | GlobalFlags
/**
* Highest ledger index this transaction can appear in. Specifying this field
* places a strict upper limit on how long the transaction can wait to be
* validated or rejected.
*/
LastLedgerSequence?: number
/**
* Additional arbitrary information used to identify this transaction.
*/
Memos?: Memo[]
/**
* Array of objects that represent a multi-signature which authorizes this
* transaction.
*/
Signers?: Signer[]
/**
* Arbitrary integer used to identify the reason for this payment, or a sender
* on whose behalf this transaction is made. Conventionally, a refund should
* specify the initial payment's SourceTag as the refund payment's
* DestinationTag.
*/
SourceTag?: number
/**
* Hex representation of the public key that corresponds to the private key
* used to sign this transaction. If an empty string, indicates a
* multi-signature is present in the Signers field instead.
*/
SigningPubKey?: string
/**
* The sequence number of the ticket to use in place of a Sequence number. If
* this is provided, Sequence must be 0. Cannot be used with AccountTxnID.
*/
TicketSequence?: number
/**
* The signature that verifies this transaction as originating from the
* account it says it is from.
*/
TxnSignature?: string
/**
* The network id of the transaction.
*/
NetworkID?: number
}
/**
* Verify the common fields of a transaction. The validate functionality will be
* optional, and will check transaction form at runtime. This should be called
* any time a transaction will be verified.
*
* @param common - An interface w/ common transaction fields.
* @throws When the common param is malformed.
*/
export function validateBaseTransaction(common: Record<string, unknown>): void {
if (common.TransactionType === undefined) {
throw new ValidationError('BaseTransaction: missing field TransactionType')
}
if (typeof common.TransactionType !== 'string') {
throw new ValidationError('BaseTransaction: TransactionType not string')
}
if (!TRANSACTION_TYPES.includes(common.TransactionType)) {
throw new ValidationError('BaseTransaction: Unknown TransactionType')
}
validateRequiredField(common, 'Account', isString)
validateOptionalField(common, 'Fee', isString)
validateOptionalField(common, 'Sequence', isNumber)
validateOptionalField(common, 'AccountTxnID', isString)
validateOptionalField(common, 'LastLedgerSequence', isNumber)
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Only used by JS
const memos = common.Memos as Array<{ Memo?: unknown }> | undefined
if (memos !== undefined && !memos.every(isMemo)) {
throw new ValidationError('BaseTransaction: invalid Memos')
}
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Only used by JS
const signers = common.Signers as Array<Record<string, unknown>> | undefined
if (
signers !== undefined &&
(signers.length === 0 || !signers.every(isSigner))
) {
throw new ValidationError('BaseTransaction: invalid Signers')
}
validateOptionalField(common, 'SourceTag', isNumber)
validateOptionalField(common, 'SigningPubKey', isString)
validateOptionalField(common, 'TicketSequence', isNumber)
validateOptionalField(common, 'TxnSignature', isString)
validateOptionalField(common, 'NetworkID', isNumber)
}
/**
* Parse the value of an amount, expressed either in XAH or as an Issued Currency, into a number.
*
* @param amount - An Amount to parse for its value.
* @returns The parsed amount value, or NaN if the amount count not be parsed.
*/
export function parseAmountValue(amount: unknown): number {
if (!isAmount(amount)) {
return NaN
}
if (typeof amount === 'string') {
return parseFloat(amount)
}
return parseFloat(amount.value)
}

View File

@@ -0,0 +1,67 @@
import { ValidationError } from '../../errors'
import { BaseTransaction, validateBaseTransaction } from './common'
/**
* A DepositPreauth transaction gives another account pre-approval to deliver
* payments to the sender of this transaction. This is only useful if the sender
* of this transaction is using (or plans to use) Deposit Authorization.
*
* @category Transaction Models
*/
export interface DepositPreauth extends BaseTransaction {
TransactionType: 'DepositPreauth'
/** The XAH Ledger address of the sender to preauthorize. */
Authorize?: string
/**
* The XAH Ledger address of a sender whose preauthorization should be.
* revoked.
*/
Unauthorize?: string
}
/**
* Verify the form and type of a DepositPreauth at runtime.
*
* @param tx - A DepositPreauth Transaction.
* @throws When the DepositPreauth is malformed.
*/
export function validateDepositPreauth(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.Authorize !== undefined && tx.Unauthorize !== undefined) {
throw new ValidationError(
"DepositPreauth: can't provide both Authorize and Unauthorize fields",
)
}
if (tx.Authorize === undefined && tx.Unauthorize === undefined) {
throw new ValidationError(
'DepositPreauth: must provide either Authorize or Unauthorize field',
)
}
if (tx.Authorize !== undefined) {
if (typeof tx.Authorize !== 'string') {
throw new ValidationError('DepositPreauth: Authorize must be a string')
}
if (tx.Account === tx.Authorize) {
throw new ValidationError(
"DepositPreauth: Account can't preauthorize its own address",
)
}
}
if (tx.Unauthorize !== undefined) {
if (typeof tx.Unauthorize !== 'string') {
throw new ValidationError('DepositPreauth: Unauthorize must be a string')
}
if (tx.Account === tx.Unauthorize) {
throw new ValidationError(
"DepositPreauth: Account can't unauthorize its own address",
)
}
}
}

View File

@@ -0,0 +1,26 @@
import { BaseTransaction } from './common'
/**
* Transaction Flags for an EnableAmendment Transaction.
*
* @category Transaction Flags
*/
export enum EnableAmendmentFlags {
/** Support for this amendment increased to at least 80% of trusted validators starting with this ledger version. */
tfGotMajority = 0x00010000,
/** Support for this amendment decreased to less than 80% of trusted validators starting with this ledger version. */
tfLostMajority = 0x00020000,
}
/**
* Mark a change in the status of a proposed amendment when it gains majority, looses majority, or is enabled.
*
* @category Pseudo Transaction Models
*/
export interface EnableAmendment extends BaseTransaction {
TransactionType: 'EnableAmendment'
/** A unique identifier for the amendment. */
Amendment: string
/** The ledger index where this pseudo-transaction appears. */
LedgerSequence: number
}

View File

@@ -0,0 +1,49 @@
import { ValidationError } from '../../errors'
import {
Account,
BaseTransaction,
isAccount,
validateBaseTransaction,
validateRequiredField,
} from './common'
/**
* Return escrowed XAH to the sender.
*
* @category Transaction Models
*/
export interface EscrowCancel extends BaseTransaction {
TransactionType: 'EscrowCancel'
/** Address of the source account that funded the escrow payment. */
Owner: Account
/**
* Transaction sequence (or Ticket number) of EscrowCreate transaction that.
* created the escrow to cancel.
*/
OfferSequence: number | string
}
/**
* Verify the form and type of an EscrowCancel at runtime.
*
* @param tx - An EscrowCancel Transaction.
* @throws When the EscrowCancel is Malformed.
*/
export function validateEscrowCancel(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
validateRequiredField(tx, 'Owner', isAccount)
if (tx.OfferSequence == null) {
throw new ValidationError('EscrowCancel: missing OfferSequence')
}
if (
(typeof tx.OfferSequence !== 'number' &&
typeof tx.OfferSequence !== 'string') ||
Number.isNaN(Number(tx.OfferSequence))
) {
throw new ValidationError('EscrowCancel: OfferSequence must be a number')
}
}

View File

@@ -0,0 +1,95 @@
import { ValidationError } from '../../errors'
import {
Account,
BaseTransaction,
isAccount,
isNumber,
validateBaseTransaction,
validateOptionalField,
validateRequiredField,
} from './common'
/**
* Sequester XAH until the escrow process either finishes or is canceled.
*
* @category Transaction Models
*/
export interface EscrowCreate extends BaseTransaction {
TransactionType: 'EscrowCreate'
/**
* Amount of XAH, in drops, to deduct from the sender's balance and escrow.
* Once escrowed, the XAH can either go to the Destination address (after the.
* FinishAfter time) or returned to the sender (after the CancelAfter time).
*/
Amount: string
/** Address to receive escrowed XAH. */
Destination: Account
/**
* The time, in seconds since the Ripple Epoch, when this escrow expires.
* This value is immutable; the funds can only be returned the sender after.
* this time.
*/
CancelAfter?: number
/**
* The time, in seconds since the Ripple Epoch, when the escrowed XAH can be
* released to the recipient. This value is immutable; the funds cannot move.
* until this time is reached.
*/
FinishAfter?: number
/**
* Hex value representing a PREIMAGE-SHA-256 crypto-condition . The funds can.
* only be delivered to the recipient if this condition is fulfilled.
*/
Condition?: string
/**
* Arbitrary tag to further specify the destination for this escrowed.
* payment, such as a hosted recipient at the destination address.
*/
DestinationTag?: number
}
/**
* Verify the form and type of an EscrowCreate at runtime.
*
* @param tx - An EscrowCreate Transaction.
* @throws When the EscrowCreate is Malformed.
*/
export function validateEscrowCreate(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.Amount === undefined) {
throw new ValidationError('EscrowCreate: missing field Amount')
}
if (typeof tx.Amount !== 'string') {
throw new ValidationError('EscrowCreate: Amount must be a string')
}
validateRequiredField(tx, 'Destination', isAccount)
validateOptionalField(tx, 'DestinationTag', isNumber)
if (tx.CancelAfter === undefined && tx.FinishAfter === undefined) {
throw new ValidationError(
'EscrowCreate: Either CancelAfter or FinishAfter must be specified',
)
}
if (tx.FinishAfter === undefined && tx.Condition === undefined) {
throw new ValidationError(
'EscrowCreate: Either Condition or FinishAfter must be specified',
)
}
if (tx.CancelAfter !== undefined && typeof tx.CancelAfter !== 'number') {
throw new ValidationError('EscrowCreate: CancelAfter must be a number')
}
if (tx.FinishAfter !== undefined && typeof tx.FinishAfter !== 'number') {
throw new ValidationError('EscrowCreate: FinishAfter must be a number')
}
if (tx.Condition !== undefined && typeof tx.Condition !== 'string') {
throw new ValidationError('EscrowCreate: Condition must be a string')
}
}

View File

@@ -0,0 +1,67 @@
import { ValidationError } from '../../errors'
import {
Account,
BaseTransaction,
isAccount,
validateBaseTransaction,
validateRequiredField,
} from './common'
/**
* Deliver XAH from a held payment to the recipient.
*
* @category Transaction Models
*/
export interface EscrowFinish extends BaseTransaction {
TransactionType: 'EscrowFinish'
/** Address of the source account that funded the held payment. */
Owner: Account
/**
* Transaction sequence of EscrowCreate transaction that created the held.
* payment to finish.
*/
OfferSequence: number | string
/**
* Hex value matching the previously-supplied PREIMAGE-SHA-256.
* crypto-condition of the held payment.
*/
Condition?: string
/**
* Hex value of the PREIMAGE-SHA-256 crypto-condition fulfillment matching.
* the held payment's Condition.
*/
Fulfillment?: string
}
/**
* Verify the form and type of an EscrowFinish at runtime.
*
* @param tx - An EscrowFinish Transaction.
* @throws When the EscrowFinish is Malformed.
*/
export function validateEscrowFinish(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
validateRequiredField(tx, 'Owner', isAccount)
if (tx.OfferSequence == null) {
throw new ValidationError('EscrowFinish: missing field OfferSequence')
}
if (
(typeof tx.OfferSequence !== 'number' &&
typeof tx.OfferSequence !== 'string') ||
Number.isNaN(Number(tx.OfferSequence))
) {
throw new ValidationError('EscrowFinish: OfferSequence must be a number')
}
if (tx.Condition !== undefined && typeof tx.Condition !== 'string') {
throw new ValidationError('EscrowFinish: Condition must be a string')
}
if (tx.Fulfillment !== undefined && typeof tx.Fulfillment !== 'string') {
throw new ValidationError('EscrowFinish: Fulfillment must be a string')
}
}

View File

@@ -0,0 +1,43 @@
export { BaseTransaction } from './common'
export {
validate,
PseudoTransaction,
SubmittableTransaction,
TransactionAndMetadata,
Transaction,
} from './transaction'
export * from './metadata'
export {
AccountSetAsfFlags,
AccountSetTfFlags,
AccountSetFlagsInterface,
AccountSet,
} from './accountSet'
export { CheckCancel } from './checkCancel'
export { CheckCash } from './checkCash'
export { CheckCreate } from './checkCreate'
export { DepositPreauth } from './depositPreauth'
export { EscrowCancel } from './escrowCancel'
export { EscrowCreate } from './escrowCreate'
export { EscrowFinish } from './escrowFinish'
export { EnableAmendment, EnableAmendmentFlags } from './enableAmendment'
export { OfferCancel } from './offerCancel'
export {
OfferCreateFlags,
OfferCreateFlagsInterface,
OfferCreate,
} from './offerCreate'
export { PaymentFlags, PaymentFlagsInterface, Payment } from './payment'
export {
PaymentChannelClaimFlags,
PaymentChannelClaimFlagsInterface,
PaymentChannelClaim,
} from './paymentChannelClaim'
export { PaymentChannelCreate } from './paymentChannelCreate'
export { PaymentChannelFund } from './paymentChannelFund'
export { SetFee, SetFeePreAmendment, SetFeePostAmendment } from './setFee'
export { SetRegularKey } from './setRegularKey'
export { SignerListSet } from './signerListSet'
export { TicketCreate } from './ticketCreate'
export { TrustSetFlagsInterface, TrustSetFlags, TrustSet } from './trustSet'
export { UNLModify } from './UNLModify'

View File

@@ -0,0 +1,77 @@
import { Amount } from '../common'
import { BaseTransaction } from './common'
import { Payment, PaymentMetadata } from './payment'
import type { Transaction } from './transaction'
export interface CreatedNode {
CreatedNode: {
LedgerEntryType: string
LedgerIndex: string
NewFields: { [field: string]: unknown }
}
}
export interface ModifiedNode {
ModifiedNode: {
LedgerEntryType: string
LedgerIndex: string
FinalFields?: { [field: string]: unknown }
PreviousFields?: { [field: string]: unknown }
PreviousTxnID?: string
PreviousTxnLgrSeq?: number
}
}
export interface DeletedNode {
DeletedNode: {
LedgerEntryType: string
LedgerIndex: string
PreviousFields?: { [field: string]: unknown }
FinalFields: { [field: string]: unknown }
}
}
export type Node = CreatedNode | ModifiedNode | DeletedNode
/**
* A typeguard to check if a node is a CreatedNode.
*
* @param node - A node from metadata.
* @returns whether the given node is a CreatedNode.
*/
export function isCreatedNode(node: Node): node is CreatedNode {
return Object.prototype.hasOwnProperty.call(node, `CreatedNode`)
}
/**
* A typeguard to check if a node is a ModifiedNode.
*
* @param node - A node from metadata.
* @returns whether the given node is a ModifiedNode.
*/
export function isModifiedNode(node: Node): node is ModifiedNode {
return Object.prototype.hasOwnProperty.call(node, `ModifiedNode`)
}
/**
* A typeguard to check if a node is a DeletedNode.
*
* @param node - A node from metadata.
* @returns whether the given node is a DeletedNode.
*/
export function isDeletedNode(node: Node): node is DeletedNode {
return Object.prototype.hasOwnProperty.call(node, `DeletedNode`)
}
export interface TransactionMetadataBase {
AffectedNodes: Node[]
DeliveredAmount?: Amount
// "unavailable" possible for transactions before 2014-01-20
delivered_amount?: Amount | 'unavailable'
TransactionIndex: number
TransactionResult: string
}
export type TransactionMetadata<T extends BaseTransaction = Transaction> =
T extends Payment ? PaymentMetadata : TransactionMetadataBase

View File

@@ -0,0 +1,37 @@
import { ValidationError } from '../../errors'
import { BaseTransaction, validateBaseTransaction } from './common'
/**
* An OfferCancel transaction removes an Offer object from the XAH Ledger.
*
* @category Transaction Models
*/
export interface OfferCancel extends BaseTransaction {
TransactionType: 'OfferCancel'
/**
* The sequence number (or Ticket number) of a previous OfferCreate
* transaction. If specified, cancel any offer object in the ledger that was
* created by that transaction. It is not considered an error if the offer.
* specified does not exist.
*/
OfferSequence: number
}
/**
* Verify the form and type of an OfferCancel at runtime.
*
* @param tx - An OfferCancel Transaction.
* @throws When the OfferCancel is Malformed.
*/
export function validateOfferCancel(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.OfferSequence === undefined) {
throw new ValidationError('OfferCancel: missing field OfferSequence')
}
if (typeof tx.OfferSequence !== 'number') {
throw new ValidationError('OfferCancel: OfferSequence must be a number')
}
}

View File

@@ -0,0 +1,143 @@
import { ValidationError } from '../../errors'
import { Amount } from '../common'
import {
BaseTransaction,
GlobalFlags,
validateBaseTransaction,
isAmount,
} from './common'
/**
* Transaction Flags for an OfferCreate Transaction.
*
* @category Transaction Flags
*/
export enum OfferCreateFlags {
/**
* If enabled, the offer does not consume offers that exactly match it, and
* instead becomes an Offer object in the ledger. It still consumes offers
* that cross it.
*/
tfPassive = 0x00010000,
/**
* Treat the offer as an Immediate or Cancel order. If enabled, the offer
* never becomes a ledger object: it only tries to match existing offers in
* the ledger. If the offer cannot match any offers immediately, it executes
* "successfully" without trading any currency. In this case, the transaction
* has the result code tesSUCCESS, but creates no Offer objects in the ledger.
*/
tfImmediateOrCancel = 0x00020000,
/**
* Treat the offer as a Fill or Kill order . Only try to match existing
* offers in the ledger, and only do so if the entire TakerPays quantity can
* be obtained. If the fix1578 amendment is enabled and the offer cannot be
* executed when placed, the transaction has the result code tecKILLED;
* otherwise, the transaction uses the result code tesSUCCESS even when it was
* killed without trading any currency.
*/
tfFillOrKill = 0x00040000,
/**
* Exchange the entire TakerGets amount, even if it means obtaining more than
* the TakerPays amount in exchange.
*/
tfSell = 0x00080000,
}
/**
* Map of flags to boolean values representing {@link OfferCreate} transaction
* flags.
*
* @category Transaction Flags
*
* @example
* ```typescript
* const tx: OfferCreate = {
* Account: 'rhFcpWDHLqpBmX4ezWiA5VLSS4e1BHqhHd',
* TakerGets: '43000.51',
* TakerPays: '12928290425',
* TransactionType: 'OfferCreate',
* Flags: {
* tfPassive: true,
* tfFillOrKill: true,
* },
* }
*
* // Autofill the tx to see how flags actually look compared to the interface usage.
* const autofilledTx = await client.autofill(tx)
* console.log(autofilledTx)
* // {
* // Account: 'rhFcpWDHLqpBmX4ezWiA5VLSS4e1BHqhHd',
* // TakerGets: '43000.51',
* // TakerPays: '12928290425',
* // TransactionType: 'OfferCreate',
* // Flags: 327680,
* // Sequence: 21970384,
* // Fee: '12',
* // LastLedgerSequence: 21970404
* // }
* ```
*/
export interface OfferCreateFlagsInterface extends GlobalFlags {
tfPassive?: boolean
tfImmediateOrCancel?: boolean
tfFillOrKill?: boolean
tfSell?: boolean
}
/**
* An OfferCreate transaction is effectively a limit order . It defines an
* intent to exchange currencies, and creates an Offer object if not completely.
* Fulfilled when placed. Offers can be partially fulfilled.
*
* @category Transaction Models
*/
export interface OfferCreate extends BaseTransaction {
TransactionType: 'OfferCreate'
Flags?: number | OfferCreateFlagsInterface
/**
* Time after which the offer is no longer active, in seconds since the.
* Ripple Epoch.
*/
Expiration?: number
/** An offer to delete first, specified in the same way as OfferCancel. */
OfferSequence?: number
/** The amount and type of currency being provided by the offer creator. */
TakerGets: Amount
/** The amount and type of currency being requested by the offer creator. */
TakerPays: Amount
}
/**
* Verify the form and type of an OfferCreate at runtime.
*
* @param tx - An OfferCreate Transaction.
* @throws When the OfferCreate is Malformed.
*/
export function validateOfferCreate(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.TakerGets === undefined) {
throw new ValidationError('OfferCreate: missing field TakerGets')
}
if (tx.TakerPays === undefined) {
throw new ValidationError('OfferCreate: missing field TakerPays')
}
if (typeof tx.TakerGets !== 'string' && !isAmount(tx.TakerGets)) {
throw new ValidationError('OfferCreate: invalid TakerGets')
}
if (typeof tx.TakerPays !== 'string' && !isAmount(tx.TakerPays)) {
throw new ValidationError('OfferCreate: invalid TakerPays')
}
if (tx.Expiration !== undefined && typeof tx.Expiration !== 'number') {
throw new ValidationError('OfferCreate: invalid Expiration')
}
if (tx.OfferSequence !== undefined && typeof tx.OfferSequence !== 'number') {
throw new ValidationError('OfferCreate: invalid OfferSequence')
}
}

View File

@@ -0,0 +1,277 @@
import { ValidationError } from '../../errors'
import { Amount, Path } from '../common'
import { isFlagEnabled } from '../utils'
import {
BaseTransaction,
isAmount,
GlobalFlags,
validateBaseTransaction,
isAccount,
validateRequiredField,
validateOptionalField,
isNumber,
Account,
} from './common'
import type { TransactionMetadataBase } from './metadata'
/**
* Enum representing values for Payment Transaction Flags.
*
* @category Transaction Flags
*/
export enum PaymentFlags {
/**
* Do not use the default path; only use paths included in the Paths field.
* This is intended to force the transaction to take arbitrage opportunities.
* Most clients do not need this.
*/
tfNoRippleDirect = 0x00010000,
/**
* If the specified Amount cannot be sent without spending more than SendMax,
* reduce the received amount instead of failing outright. See Partial.
* Payments for more details.
*/
tfPartialPayment = 0x00020000,
/**
* Only take paths where all the conversions have an input:output ratio that
* is equal or better than the ratio of Amount:SendMax. See Limit Quality for
* details.
*/
tfLimitQuality = 0x00040000,
}
/**
* Map of flags to boolean values representing {@link Payment} transaction
* flags.
*
* @category Transaction Flags
*
* @example
* ```typescript
* const partialPayment: Payment = {
* TransactionType: 'Payment',
* Account: 'rM9WCfJU6udpFkvKThRaFHDMsp7L8rpgN',
* Amount: {
* currency: 'FOO',
* value: '4000',
* issuer: 'rPzwM2JfCSDjhbesdTCqFjWWdK7eFtTwZz',
* },
* Destination: 'rPzwM2JfCSDjhbesdTCqFjWWdK7eFtTwZz',
* Flags: {
* tfPartialPayment: true
* }
* }
*
* // Autofill the tx to see how flags actually look compared to the interface usage.
* const autofilledTx = await client.autofill(partialPayment)
* console.log(autofilledTx)
* // {
* // TransactionType: 'Payment',
* // Account: 'rM9WCfJU6udpFkvKThRaFHDMsp7L8rpgN',
* // Amount: {
* // currency: 'FOO',
* // value: '4000',
* // issuer: 'rPzwM2JfCSDjhbesdTCqFjWWdK7eFtTwZz'
* // },
* // Destination: 'rPzwM2JfCSDjhbesdTCqFjWWdK7eFtTwZz',
* // Flags: 131072,
* // Sequence: 21970996,
* // Fee: '12',
* // LastLedgerSequence: 21971016
* // }
* ```
*/
export interface PaymentFlagsInterface extends GlobalFlags {
/**
* Do not use the default path; only use paths included in the Paths field.
* This is intended to force the transaction to take arbitrage opportunities.
* Most clients do not need this.
*/
tfNoRippleDirect?: boolean
/**
* If the specified Amount cannot be sent without spending more than SendMax,
* reduce the received amount instead of failing outright. See Partial.
* Payments for more details.
*/
tfPartialPayment?: boolean
/**
* Only take paths where all the conversions have an input:output ratio that
* is equal or better than the ratio of Amount:SendMax. See Limit Quality for
* details.
*/
tfLimitQuality?: boolean
}
/**
* A Payment transaction represents a transfer of value from one account to
* another.
*
* @category Transaction Models
*/
export interface Payment extends BaseTransaction {
TransactionType: 'Payment'
/**
* The amount of currency to deliver. For non-XAH amounts, the nested field
* names MUST be lower-case. If the tfPartialPayment flag is set, deliver up
* to this amount instead.
*/
Amount: Amount
/** The unique address of the account receiving the payment. */
Destination: Account
/**
* Arbitrary tag that identifies the reason for the payment to the
* destination, or a hosted recipient to pay.
*/
DestinationTag?: number
/**
* Arbitrary 256-bit hash representing a specific reason or identifier for
* this payment.
*/
InvoiceID?: string
/**
* Array of payment paths to be used for this transaction. Must be omitted
* for XAH-to-XAH transactions.
*/
Paths?: Path[]
/**
* Highest amount of source currency this transaction is allowed to cost,
* including transfer fees, exchange rates, and slippage . Does not include
* the XAH destroyed as a cost for submitting the transaction. For non-XAH
* amounts, the nested field names MUST be lower-case. Must be supplied for
* cross-currency/cross-issue payments. Must be omitted for XAH-to-XAH
* Payments.
*/
SendMax?: Amount
/**
* Minimum amount of destination currency this transaction should deliver.
* Only valid if this is a partial payment. For non-XAH amounts, the nested
* field names are lower-case.
*/
DeliverMin?: Amount
Flags?: number | PaymentFlagsInterface
}
export interface PaymentMetadata extends TransactionMetadataBase {
DeliveredAmount?: Amount
delivered_amount?: Amount | 'unavailable'
}
/**
* Verify the form and type of a Payment at runtime.
*
* @param tx - A Payment Transaction.
* @throws When the Payment is malformed.
*/
export function validatePayment(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.Amount === undefined) {
throw new ValidationError('PaymentTransaction: missing field Amount')
}
if (!isAmount(tx.Amount)) {
throw new ValidationError('PaymentTransaction: invalid Amount')
}
validateRequiredField(tx, 'Destination', isAccount)
validateOptionalField(tx, 'DestinationTag', isNumber)
if (tx.InvoiceID !== undefined && typeof tx.InvoiceID !== 'string') {
throw new ValidationError('PaymentTransaction: InvoiceID must be a string')
}
if (
tx.Paths !== undefined &&
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Only used by JS
!isPaths(tx.Paths as Array<Array<Record<string, unknown>>>)
) {
throw new ValidationError('PaymentTransaction: invalid Paths')
}
if (tx.SendMax !== undefined && !isAmount(tx.SendMax)) {
throw new ValidationError('PaymentTransaction: invalid SendMax')
}
checkPartialPayment(tx)
}
function checkPartialPayment(tx: Record<string, unknown>): void {
if (tx.DeliverMin != null) {
if (tx.Flags == null) {
throw new ValidationError(
'PaymentTransaction: tfPartialPayment flag required with DeliverMin',
)
}
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Only used by JS
const flags = tx.Flags as number | PaymentFlagsInterface
const isTfPartialPayment =
typeof flags === 'number'
? isFlagEnabled(flags, PaymentFlags.tfPartialPayment)
: flags.tfPartialPayment ?? false
if (!isTfPartialPayment) {
throw new ValidationError(
'PaymentTransaction: tfPartialPayment flag required with DeliverMin',
)
}
if (!isAmount(tx.DeliverMin)) {
throw new ValidationError('PaymentTransaction: invalid DeliverMin')
}
}
}
function isPathStep(pathStep: Record<string, unknown>): boolean {
if (pathStep.account !== undefined && typeof pathStep.account !== 'string') {
return false
}
if (
pathStep.currency !== undefined &&
typeof pathStep.currency !== 'string'
) {
return false
}
if (pathStep.issuer !== undefined && typeof pathStep.issuer !== 'string') {
return false
}
if (
pathStep.account !== undefined &&
pathStep.currency === undefined &&
pathStep.issuer === undefined
) {
return true
}
if (pathStep.currency !== undefined || pathStep.issuer !== undefined) {
return true
}
return false
}
function isPath(path: Array<Record<string, unknown>>): boolean {
for (const pathStep of path) {
if (!isPathStep(pathStep)) {
return false
}
}
return true
}
function isPaths(paths: Array<Array<Record<string, unknown>>>): boolean {
if (!Array.isArray(paths) || paths.length === 0) {
return false
}
for (const path of paths) {
if (!Array.isArray(path) || path.length === 0) {
return false
}
if (!isPath(path)) {
return false
}
}
return true
}

View File

@@ -0,0 +1,164 @@
import { ValidationError } from '../../errors'
import { BaseTransaction, GlobalFlags, validateBaseTransaction } from './common'
/**
* Enum representing values for PaymentChannelClaim transaction flags.
*
* @category Transaction Flags
*/
export enum PaymentChannelClaimFlags {
/**
* Clear the channel's Expiration time. (Expiration is different from the
* channel's immutable CancelAfter time.) Only the source address of the
* payment channel can use this flag.
*/
tfRenew = 0x00010000,
/**
* Request to close the channel. Only the channel source and destination
* addresses can use this flag. This flag closes the channel immediately if it
* has no more XAH allocated to it after processing the current claim, or if
* the destination address uses it. If the source address uses this flag when
* the channel still holds XAH, this schedules the channel to close after
* SettleDelay seconds have passed. (Specifically, this sets the Expiration of
* the channel to the close time of the previous ledger plus the channel's
* SettleDelay time, unless the channel already has an earlier Expiration
* time.) If the destination address uses this flag when the channel still
* holds XAH, any XAH that remains after processing the claim is returned to
* the source address.
*/
tfClose = 0x00020000,
}
/**
* Map of flags to boolean values representing {@link PaymentChannelClaim}
* transaction flags.
*
* @category Transaction Flags
*
* @example
* ```typescript
* const paymentChannelClaim: PaymentChannelClaim = {
* Account: 'rMpxZpuy5RBSP47oK2hDWUtk3B5BNQHfGj,
* TransactionType: 'PaymentChannelClaim',
* Channel: hashes.hashPaymentChannel(
* 'rMpxZpuy5RBSP47oK2hDWUtk3B5BNQHfGj',
* 'rQGYqiyH5Ue9J96p4E6Qt6AvqxK4sDhnS5',
* 21970712,
* ),
* Amount: '100',
* Flags: {
* tfClose: true
* }
*}
*
* // Autofill the tx to see how flags actually look compared to the interface usage.
* const autofilledTx = await client.autofill(paymentChannelClaim)
* console.log(autofilledTx)
* // {
* // Account: 'rMpxZpuy5RBSP47oK2hDWUtk3B5BNQHfGj',
* // TransactionType: 'PaymentChannelClaim',
* // Channel: 'FC14BF9245D731DC1749EE0F070765E4EB4E993F8ECEE3D00F7E6E26D6EF98CF',
* // Amount: '100',
* // Flags: 131072,
* // Sequence: 21970713,
* // Fee: '12',
* // LastLedgerSequence: 21970658
* // }
* ```
*/
export interface PaymentChannelClaimFlagsInterface extends GlobalFlags {
/**
* Clear the channel's Expiration time. (Expiration is different from the
* channel's immutable CancelAfter time.) Only the source address of the
* payment channel can use this flag.
*/
tfRenew?: boolean
/**
* Request to close the channel. Only the channel source and destination
* addresses can use this flag. This flag closes the channel immediately if it
* has no more XAH allocated to it after processing the current claim, or if
* the destination address uses it. If the source address uses this flag when
* the channel still holds XAH, this schedules the channel to close after
* SettleDelay seconds have passed. (Specifically, this sets the Expiration of
* the channel to the close time of the previous ledger plus the channel's
* SettleDelay time, unless the channel already has an earlier Expiration
* time.) If the destination address uses this flag when the channel still
* holds XAH, any XAH that remains after processing the claim is returned to
* the source address.
*/
tfClose?: boolean
}
/**
* Claim XAH from a payment channel, adjust the payment channel's expiration,
* or both.
*
* @category Transaction Models
*/
export interface PaymentChannelClaim extends BaseTransaction {
TransactionType: 'PaymentChannelClaim'
Flags?: number | PaymentChannelClaimFlagsInterface
/** The unique ID of the channel as a 64-character hexadecimal string. */
Channel: string
/**
* Total amount of XAH, in drops, delivered by this channel after processing
* this claim. Required to deliver XAH. Must be more than the total amount
* delivered by the channel so far, but not greater than the Amount of the
* signed claim. Must be provided except when closing the channel.
*/
Balance?: string
/**
* The amount of XAH, in drops, authorized by the Signature. This must match
* the amount in the signed message. This is the cumulative amount of XAH that
* can be dispensed by the channel, including XAH previously redeemed.
*/
Amount?: string
/**
* The signature of this claim, as hexadecimal. The signed message contains
* the channel ID and the amount of the claim. Required unless the sender of
* the transaction is the source address of the channel.
*/
Signature?: string
/**
* The public key used for the signature, as hexadecimal. This must match the
* PublicKey stored in the ledger for the channel. Required unless the sender
* of the transaction is the source address of the channel and the Signature
* field is omitted.
*/
PublicKey?: string
}
/**
* Verify the form and type of an PaymentChannelClaim at runtime.
*
* @param tx - An PaymentChannelClaim Transaction.
* @throws When the PaymentChannelClaim is Malformed.
*/
export function validatePaymentChannelClaim(tx: Record<string, unknown>): void {
validateBaseTransaction(tx)
if (tx.Channel === undefined) {
throw new ValidationError('PaymentChannelClaim: missing Channel')
}
if (typeof tx.Channel !== 'string') {
throw new ValidationError('PaymentChannelClaim: Channel must be a string')
}
if (tx.Balance !== undefined && typeof tx.Balance !== 'string') {
throw new ValidationError('PaymentChannelClaim: Balance must be a string')
}
if (tx.Amount !== undefined && typeof tx.Amount !== 'string') {
throw new ValidationError('PaymentChannelClaim: Amount must be a string')
}
if (tx.Signature !== undefined && typeof tx.Signature !== 'string') {
throw new ValidationError('PaymentChannelClaim: Signature must be a string')
}
if (tx.PublicKey !== undefined && typeof tx.PublicKey !== 'string') {
throw new ValidationError('PaymentChannelClaim: PublicKey must be a string')
}
}

Some files were not shown because too many files have changed in this diff Show More