UNPKG

xrpl

Version:

A TypeScript/JavaScript API for interacting with the XRP Ledger in Node.js and the browser

209 lines (177 loc) 6.53 kB
import BigNumber from 'bignumber.js' import { decode } from 'ripple-binary-codec' import type { TransactionEntryResponse, TransactionStream, TransactionV1Stream, TxResponse, } from '..' import type { Amount, IssuedCurrency, APIVersion, DEFAULT_API_VERSION, MPTAmount, } from '../models/common' import type { AccountTxTransaction, RequestResponseMap, } from '../models/methods' import { AccountTxVersionResponseMap } from '../models/methods/accountTx' import { BaseRequest, BaseResponse } from '../models/methods/baseMethod' import { PaymentFlags, Transaction, isMPTAmount } from '../models/transactions' import type { TransactionMetadata } from '../models/transactions/metadata' import { isFlagEnabled } from '../models/utils' const WARN_PARTIAL_PAYMENT_CODE = 2001 /* eslint-disable complexity -- check different token types */ /* eslint-disable @typescript-eslint/consistent-type-assertions -- known currency type */ function amountsEqual( amt1: Amount | MPTAmount, amt2: Amount | MPTAmount, ): boolean { // Compare XRP if (typeof amt1 === 'string' && typeof amt2 === 'string') { return amt1 === amt2 } if (typeof amt1 === 'string' || typeof amt2 === 'string') { return false } // Compare MPTs if (isMPTAmount(amt1) && isMPTAmount(amt2)) { const aValue = new BigNumber(amt1.value) const bValue = new BigNumber(amt2.value) return ( amt1.mpt_issuance_id === amt2.mpt_issuance_id && aValue.isEqualTo(bValue) ) } if (isMPTAmount(amt1) || isMPTAmount(amt2)) { return false } // Compare issued currency (IOU) const aValue = new BigNumber(amt1.value) const bValue = new BigNumber(amt2.value) return ( (amt1 as IssuedCurrency).currency === (amt2 as IssuedCurrency).currency && (amt1 as IssuedCurrency).issuer === (amt2 as IssuedCurrency).issuer && aValue.isEqualTo(bValue) ) } /* eslint-enable complexity */ /* eslint-enable @typescript-eslint/consistent-type-assertions */ 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 const amount = tx.DeliverMax ?? tx.Amount 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) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access -- required to check API version model if (tx.tx_json != null) { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- use API v2 model const transaction = tx as AccountTxTransaction return isPartialPayment(transaction.tx_json, transaction.meta) } // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- use API v1 model const transaction = tx as AccountTxTransaction<1> 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)) } }