diff --git a/packages/xrpl/HISTORY.md b/packages/xrpl/HISTORY.md index bb6bd75cf2..2c534d0545 100644 --- a/packages/xrpl/HISTORY.md +++ b/packages/xrpl/HISTORY.md @@ -5,8 +5,12 @@ Subscribe to [the **xrpl-announce** mailing list](https://groups.google.com/g/xr ## Unreleased * Remove references to the Hooks testnet faucet in the xrpl.js code repository. +### BREAKING CHANGES +* Use rippled api_version v2 as default while maintaining support for v1. + ### Added * Add `nfts_by_issuer` clio-only API definition + ## 3.1.0 (2024-06-03) ### BREAKING CHANGES diff --git a/packages/xrpl/snippets/src/claimPayChannel.ts b/packages/xrpl/snippets/src/claimPayChannel.ts index e29340474e..d24bd03580 100644 --- a/packages/xrpl/snippets/src/claimPayChannel.ts +++ b/packages/xrpl/snippets/src/claimPayChannel.ts @@ -56,7 +56,7 @@ async function claimPayChannel(): Promise { Channel: hashes.hashPaymentChannel( wallet1.classicAddress, wallet2.classicAddress, - paymentChannelResponse.result.Sequence ?? 0, + paymentChannelResponse.result.tx_json.Sequence ?? 0, ), Amount: '100', } diff --git a/packages/xrpl/snippets/src/sendEscrow.ts b/packages/xrpl/snippets/src/sendEscrow.ts index 58851f0788..3744b9b0ca 100644 --- a/packages/xrpl/snippets/src/sendEscrow.ts +++ b/packages/xrpl/snippets/src/sendEscrow.ts @@ -63,7 +63,7 @@ async function sendEscrow(): Promise { TransactionType: 'EscrowFinish', Account: wallet1.classicAddress, Owner: wallet1.classicAddress, - OfferSequence: Number(createEscrowResponse.result.Sequence), + OfferSequence: Number(createEscrowResponse.result.tx_json.Sequence), } await client.submit(finishTx, { diff --git a/packages/xrpl/src/client/RequestManager.ts b/packages/xrpl/src/client/RequestManager.ts index f9cff40da4..79712140ac 100644 --- a/packages/xrpl/src/client/RequestManager.ts +++ b/packages/xrpl/src/client/RequestManager.ts @@ -4,6 +4,7 @@ import { TimeoutError, XrplError, } from '../errors' +import type { APIVersion } from '../models' import { Response, RequestResponseMap } from '../models/methods' import { BaseRequest, ErrorResponse } from '../models/methods/baseMethod' @@ -35,10 +36,10 @@ export default class RequestManager { * @param timer - The timer associated with the promise. * @returns A promise that resolves to the specified generic type. */ - public async addPromise>( - newId: string | number, - timer: ReturnType, - ): Promise { + public async addPromise< + R extends BaseRequest, + T = RequestResponseMap, + >(newId: string | number, timer: ReturnType): Promise { return new Promise((resolve, reject) => { this.promisesAwaitingResponse.set(newId, { resolve, @@ -55,7 +56,10 @@ export default class RequestManager { * @param response - Response to return. * @throws Error if no existing promise with the given ID. */ - public resolve(id: string | number, response: Response): void { + public resolve( + id: string | number, + response: Partial>, + ): void { const promise = this.promisesAwaitingResponse.get(id) if (promise == null) { throw new XrplError(`No existing promise with id ${id}`, { @@ -111,10 +115,10 @@ export default class RequestManager { * @returns Request ID, new request form, and the promise for resolving the request. * @throws XrplError if request with the same ID is already pending. */ - public createRequest>( - request: R, - timeout: number, - ): [string | number, string, Promise] { + public createRequest< + R extends BaseRequest, + T = RequestResponseMap, + >(request: R, timeout: number): [string | number, string, Promise] { let newId: string | number if (request.id == null) { newId = this.nextId @@ -171,7 +175,9 @@ export default class RequestManager { * @param response - The response to handle. * @throws ResponseFormatError if the response format is invalid, RippledError if rippled returns an error. */ - public handleResponse(response: Partial): void { + public handleResponse( + response: Partial | ErrorResponse>, + ): void { if ( response.id == null || !(typeof response.id === 'string' || typeof response.id === 'number') @@ -205,8 +211,7 @@ export default class RequestManager { } // status no longer needed because error is thrown if status is not "success" delete response.status - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Must be a valid Response here - this.resolve(response.id, response as unknown as Response) + this.resolve(response.id, response) } /** diff --git a/packages/xrpl/src/client/connection.ts b/packages/xrpl/src/client/connection.ts index a8796e9218..3214b557e2 100644 --- a/packages/xrpl/src/client/connection.ts +++ b/packages/xrpl/src/client/connection.ts @@ -11,7 +11,7 @@ import { ConnectionError, XrplError, } from '../errors' -import type { RequestResponseMap } from '../models' +import type { APIVersion, RequestResponseMap } from '../models' import { BaseRequest } from '../models/methods/baseMethod' import ConnectionManager from './ConnectionManager' @@ -267,6 +267,7 @@ export class Connection extends EventEmitter { /** * Disconnect the websocket, then connect again. + * */ public async reconnect(): Promise { /* @@ -287,10 +288,10 @@ export class Connection extends EventEmitter { * @returns The response from the rippled server. * @throws NotConnectedError if the Connection isn't connected to a server. */ - public async request>( - request: R, - timeout?: number, - ): Promise { + public async request< + R extends BaseRequest, + T = RequestResponseMap, + >(request: R, timeout?: number): Promise { if (!this.shouldBeConnected || this.ws == null) { throw new NotConnectedError(JSON.stringify(request), request) } @@ -468,6 +469,7 @@ export class Connection extends EventEmitter { /** * Starts a heartbeat to check the connection with the server. + * */ private startHeartbeatInterval(): void { this.clearHeartbeatInterval() diff --git a/packages/xrpl/src/client/index.ts b/packages/xrpl/src/client/index.ts index 1e6b89288c..722b169f2a 100644 --- a/packages/xrpl/src/client/index.ts +++ b/packages/xrpl/src/client/index.ts @@ -9,7 +9,12 @@ import { ValidationError, XrplError, } from '../errors' -import type { LedgerIndex, Balance } from '../models/common' +import { + APIVersion, + LedgerIndex, + Balance, + DEFAULT_API_VERSION, +} from '../models/common' import { Request, // account methods @@ -213,6 +218,12 @@ class Client extends EventEmitter { */ public buildVersion: string | undefined + /** + * API Version used by the server this client is connected to + * + */ + public apiVersion: APIVersion = DEFAULT_API_VERSION + /** * Creates a new Client with a websocket connection to a rippled server. * @@ -307,7 +318,6 @@ class Client extends EventEmitter { * additional request body parameters. * * @category Network - * * @param req - Request to send to the server. * @returns The response from the server. * @@ -320,16 +330,20 @@ class Client extends EventEmitter { * console.log(response) * ``` */ - public async request>( - req: R, - ): Promise { - const response = await this.connection.request({ + public async request< + R extends Request, + V extends APIVersion = typeof DEFAULT_API_VERSION, + T = RequestResponseMap, + >(req: R): Promise { + const request = { ...req, - account: req.account - ? // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Must be string - ensureClassicAddress(req.account as string) - : undefined, - }) + account: + typeof req.account === 'string' + ? ensureClassicAddress(req.account) + : undefined, + api_version: req.api_version ?? this.apiVersion, + } + const response = await this.connection.request(request) // mutates `response` to add warnings handlePartialPayment(req.command, response) @@ -438,9 +452,10 @@ class Client extends EventEmitter { * const allResponses = await client.requestAll({ command: 'transaction_data' }); * console.log(allResponses); */ + public async requestAll< T extends MarkerRequest, - U = RequestAllResponseMap, + U = RequestAllResponseMap, >(request: T, collect?: string): Promise { /* * The data under collection is keyed based on the command. Fail if command @@ -468,7 +483,7 @@ class Client extends EventEmitter { // eslint-disable-next-line no-await-in-loop -- Necessary for this, it really has to wait const singleResponse = await this.connection.request(repeatProps) // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Should be true - const singleResult = (singleResponse as MarkerResponse).result + const singleResult = (singleResponse as MarkerResponse).result if (!(collectKey in singleResult)) { throw new XrplError(`${collectKey} not in result`) } diff --git a/packages/xrpl/src/client/partialPayment.ts b/packages/xrpl/src/client/partialPayment.ts index a21448e9cc..fa22d68f68 100644 --- a/packages/xrpl/src/client/partialPayment.ts +++ b/packages/xrpl/src/client/partialPayment.ts @@ -2,13 +2,16 @@ import BigNumber from 'bignumber.js' import { decode } from 'ripple-binary-codec' import type { - AccountTxResponse, TransactionEntryResponse, TransactionStream, TxResponse, } from '..' -import type { Amount } from '../models/common' -import type { RequestResponseMap } from '../models/methods' +import type { Amount, APIVersion, DEFAULT_API_VERSION } 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 } from '../models/transactions' import type { TransactionMetadata } from '../models/transactions/metadata' @@ -63,7 +66,10 @@ function isPartialPayment( } const delivered = meta.delivered_amount - const amount = tx.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 @@ -73,23 +79,36 @@ function isPartialPayment( } function txHasPartialPayment(response: TxResponse): boolean { - return isPartialPayment(response.result, response.result.meta) + 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(response: AccountTxResponse): boolean { +function accountTxHasPartialPayment< + Version extends APIVersion = typeof DEFAULT_API_VERSION, +>(response: AccountTxVersionResponseMap): boolean { const { transactions } = response.result - const foo = transactions.some((tx) => isPartialPayment(tx.tx, tx.meta)) + 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>( - command: string, - response: T, -): boolean { +function hasPartialPayment< + R extends BaseRequest, + V extends APIVersion = typeof DEFAULT_API_VERSION, + T = RequestResponseMap, +>(command: string, response: T): boolean { /* eslint-disable @typescript-eslint/consistent-type-assertions -- Request type is known at runtime from command */ switch (command) { case 'tx': @@ -97,7 +116,9 @@ function hasPartialPayment>( case 'transaction_entry': return txEntryHasPartialPayment(response as TransactionEntryResponse) case 'account_tx': - return accountTxHasPartialPayment(response as AccountTxResponse) + return accountTxHasPartialPayment( + response as AccountTxVersionResponseMap, + ) default: return false } @@ -112,7 +133,7 @@ function hasPartialPayment>( */ export function handlePartialPayment< R extends BaseRequest, - T = RequestResponseMap, + T = RequestResponseMap, >(command: string, response: T): void { if (hasPartialPayment(command, response)) { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- We are checking dynamically and safely. diff --git a/packages/xrpl/src/models/common/index.ts b/packages/xrpl/src/models/common/index.ts index 5b240c93aa..98298d978f 100644 --- a/packages/xrpl/src/models/common/index.ts +++ b/packages/xrpl/src/models/common/index.ts @@ -1,3 +1,7 @@ +export const RIPPLED_API_V1 = 1 +export const RIPPLED_API_V2 = 2 +export const DEFAULT_API_VERSION = RIPPLED_API_V2 +export type APIVersion = typeof RIPPLED_API_V1 | typeof RIPPLED_API_V2 export type LedgerIndex = number | ('validated' | 'closed' | 'current') export interface XRP { @@ -104,6 +108,10 @@ export interface ResponseOnlyTxInfo { * 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. */ diff --git a/packages/xrpl/src/models/ledger/Ledger.ts b/packages/xrpl/src/models/ledger/Ledger.ts index fe9d956c4f..bcff437391 100644 --- a/packages/xrpl/src/models/ledger/Ledger.ts +++ b/packages/xrpl/src/models/ledger/Ledger.ts @@ -1,14 +1,14 @@ +import { APIVersion, DEFAULT_API_VERSION, RIPPLED_API_V1 } from '../common' import { Transaction, TransactionMetadata } from '../transactions' import { LedgerEntry } from './LedgerEntry' /** - * A ledger is a block of transactions and shared state data. It has a unique - * header that describes its contents using cryptographic hashes. + * Common properties for ledger entries. * * @category Ledger Entries */ -export default interface Ledger { +interface BaseLedger { /** The SHA-512Half of this ledger's state tree information. */ account_hash: string /** All the state information in this ledger. Admin only. */ @@ -38,11 +38,6 @@ export default interface Ledger { * for this ledger and all its contents. */ ledger_hash: string - /** - * The ledger index of the ledger. Some API methods display this as a quoted - * integer; some display it as a native JSON number. - */ - ledger_index: string /** The approximate time at which the previous ledger was closed. */ parent_close_time: number /** @@ -63,3 +58,40 @@ export default interface Ledger { */ transactions?: Array } + +/** + * 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 RIPPLED_API_V1 ? LedgerV1 : Ledger diff --git a/packages/xrpl/src/models/ledger/index.ts b/packages/xrpl/src/models/ledger/index.ts index 1cf9263b37..4433c2cc8a 100644 --- a/packages/xrpl/src/models/ledger/index.ts +++ b/packages/xrpl/src/models/ledger/index.ts @@ -15,7 +15,7 @@ import FeeSettings, { FeeSettingsPostAmendmentFields, FEE_SETTINGS_ID, } from './FeeSettings' -import Ledger from './Ledger' +import { Ledger, LedgerV1 } from './Ledger' import { LedgerEntry, LedgerEntryFilter } from './LedgerEntry' import LedgerHashes from './LedgerHashes' import NegativeUNL, { NEGATIVE_UNL_ID } from './NegativeUNL' @@ -48,6 +48,7 @@ export { FeeSettingsPreAmendmentFields, FeeSettingsPostAmendmentFields, Ledger, + LedgerV1, LedgerEntryFilter, LedgerEntry, LedgerHashes, diff --git a/packages/xrpl/src/models/methods/accountInfo.ts b/packages/xrpl/src/models/methods/accountInfo.ts index d872b4a100..9d31f81faa 100644 --- a/packages/xrpl/src/models/methods/accountInfo.ts +++ b/packages/xrpl/src/models/methods/accountInfo.ts @@ -1,3 +1,4 @@ +import { APIVersion, DEFAULT_API_VERSION, RIPPLED_API_V1 } from '../common' import { AccountRoot, SignerList } from '../ledger' import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod' @@ -133,23 +134,13 @@ export interface AccountInfoAccountFlags { allowTrustLineClawback: boolean } -/** - * Response expected from an {@link AccountInfoRequest}. - * - * @category Responses - */ -export interface AccountInfoResponse extends BaseResponse { +interface BaseAccountInfoResponse extends BaseResponse { 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: AccountRoot & { signer_lists?: SignerList[] } - + account_data: AccountRoot /** * A map of account flags parsed out. This will only be available for rippled nodes 1.11.0 and higher. */ @@ -180,3 +171,58 @@ export interface AccountInfoResponse extends BaseResponse { 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 RIPPLED_API_V1 + ? AccountInfoV1Response + : AccountInfoResponse diff --git a/packages/xrpl/src/models/methods/accountTx.ts b/packages/xrpl/src/models/methods/accountTx.ts index 8a654945d5..ccff2279eb 100644 --- a/packages/xrpl/src/models/methods/accountTx.ts +++ b/packages/xrpl/src/models/methods/accountTx.ts @@ -1,4 +1,10 @@ -import { ResponseOnlyTxInfo } from '../common' +import { + APIVersion, + DEFAULT_API_VERSION, + RIPPLED_API_V1, + RIPPLED_API_V2, + ResponseOnlyTxInfo, +} from '../common' import { Transaction, TransactionMetadata } from '../transactions' import { BaseRequest, BaseResponse, LookupByLedgerRequest } from './baseMethod' @@ -49,7 +55,9 @@ export interface AccountTxRequest extends BaseRequest, LookupByLedgerRequest { marker?: unknown } -export interface AccountTxTransaction { +export interface AccountTxTransaction< + Version extends APIVersion = typeof DEFAULT_API_VERSION, +> { /** The ledger index of the ledger version that included this transaction. */ ledger_index: number /** @@ -58,7 +66,15 @@ export interface AccountTxTransaction { */ meta: string | TransactionMetadata /** JSON object defining the transaction. */ - tx?: Transaction & ResponseOnlyTxInfo + tx_json?: Version extends typeof RIPPLED_API_V2 + ? Transaction & ResponseOnlyTxInfo + : never + /** JSON object defining the transaction in rippled API v1. */ + tx?: Version extends typeof RIPPLED_API_V1 + ? Transaction & ResponseOnlyTxInfo + : never + /** The hash of the transaction. */ + hash?: Version extends typeof RIPPLED_API_V2 ? string : never /** Unique hashed String representing the transaction. */ tx_blob?: string /** @@ -69,11 +85,11 @@ export interface AccountTxTransaction { } /** - * Expected response from an {@link AccountTxRequest}. - * - * @category Responses + * Base interface for account transaction responses. */ -export interface AccountTxResponse extends BaseResponse { +interface AccountTxResponseBase< + Version extends APIVersion = typeof DEFAULT_API_VERSION, +> extends BaseResponse { result: { /** Unique Address identifying the related account. */ account: string @@ -98,7 +114,7 @@ export interface AccountTxResponse extends BaseResponse { * Array of transactions matching the request's criteria, as explained * below. */ - transactions: AccountTxTransaction[] + transactions: Array> /** * If included and set to true, the information in this response comes from * a validated ledger version. Otherwise, the information is subject to @@ -107,3 +123,28 @@ export interface AccountTxResponse extends BaseResponse { 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 RIPPLED_API_V1 + ? AccountTxV1Response + : AccountTxResponse diff --git a/packages/xrpl/src/models/methods/index.ts b/packages/xrpl/src/models/methods/index.ts index a704cabd97..15d95dce0f 100644 --- a/packages/xrpl/src/models/methods/index.ts +++ b/packages/xrpl/src/models/methods/index.ts @@ -1,5 +1,7 @@ /* 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, @@ -13,6 +15,8 @@ import { AccountInfoAccountFlags, AccountInfoRequest, AccountInfoResponse, + AccountInfoV1Response, + AccountInfoVersionResponseMap, AccountQueueData, AccountQueueTransaction, } from './accountInfo' @@ -40,6 +44,8 @@ import { import { AccountTxRequest, AccountTxResponse, + AccountTxV1Response, + AccountTxVersionResponseMap, AccountTxTransaction, } from './accountTx' import { AMMInfoRequest, AMMInfoResponse } from './ammInfo' @@ -76,11 +82,13 @@ import { LedgerQueueData, LedgerRequest, LedgerResponse, + LedgerV1Response, LedgerRequestExpandedTransactionsOnly, LedgerResponseExpanded, LedgerRequestExpandedAccountsAndTransactions, LedgerRequestExpandedAccountsOnly, LedgerRequestExpandedTransactionsBinary, + LedgerVersionResponseMap, } from './ledger' import { LedgerClosedRequest, LedgerClosedResponse } from './ledgerClosed' import { LedgerCurrentRequest, LedgerCurrentResponse } from './ledgerCurrent' @@ -136,6 +144,8 @@ import { SubmitRequest, SubmitResponse } from './submit' import { SubmitMultisignedRequest, SubmitMultisignedResponse, + SubmitMultisignedV1Response, + SubmitMultisignedVersionResponseMap, } from './submitMultisigned' import { BooksSnapshot, @@ -156,7 +166,7 @@ import { TransactionEntryRequest, TransactionEntryResponse, } from './transactionEntry' -import { TxRequest, TxResponse } from './tx' +import { TxRequest, TxResponse, TxV1Response, TxVersionResponseMap } from './tx' import { UnsubscribeBook, UnsubscribeRequest, @@ -222,27 +232,27 @@ type Request = /** * @category Responses */ -type Response = +type Response = // account methods | AccountChannelsResponse | AccountCurrenciesResponse - | AccountInfoResponse + | AccountInfoVersionResponseMap | AccountLinesResponse | AccountNFTsResponse | AccountObjectsResponse | AccountOffersResponse - | AccountTxResponse + | AccountTxVersionResponseMap | GatewayBalancesResponse | NoRippleCheckResponse // ledger methods - | LedgerResponse + | LedgerVersionResponseMap | LedgerClosedResponse | LedgerCurrentResponse | LedgerDataResponse | LedgerEntryResponse // transaction methods | SubmitResponse - | SubmitMultisignedResponse + | SubmitMultisignedVersionResponseMap | TransactionEntryResponse | TxResponse // path and order book methods @@ -276,12 +286,15 @@ type Response = // Price Oracle methods | GetAggregatePriceResponse -export type RequestResponseMap = T extends AccountChannelsRequest +export type RequestResponseMap< + T, + Version extends APIVersion = typeof DEFAULT_API_VERSION, +> = T extends AccountChannelsRequest ? AccountChannelsResponse : T extends AccountCurrenciesRequest ? AccountCurrenciesResponse : T extends AccountInfoRequest - ? AccountInfoResponse + ? AccountInfoVersionResponseMap : T extends AccountLinesRequest ? AccountLinesResponse : T extends AccountNFTsRequest @@ -291,7 +304,7 @@ export type RequestResponseMap = T extends AccountChannelsRequest : T extends AccountOffersRequest ? AccountOffersResponse : T extends AccountTxRequest - ? AccountTxResponse + ? AccountTxVersionResponseMap : T extends AMMInfoRequest ? AMMInfoResponse : T extends GatewayBalancesRequest @@ -357,15 +370,15 @@ export type RequestResponseMap = T extends AccountChannelsRequest // then we'd get the wrong response type, LedgerResponse, instead of // LedgerResponseExpanded. T extends LedgerRequestExpandedTransactionsBinary - ? LedgerResponse + ? LedgerVersionResponseMap : T extends LedgerRequestExpandedAccountsAndTransactions - ? LedgerResponseExpanded + ? LedgerResponseExpanded : T extends LedgerRequestExpandedTransactionsOnly - ? LedgerResponseExpanded + ? LedgerResponseExpanded : T extends LedgerRequestExpandedAccountsOnly - ? LedgerResponseExpanded + ? LedgerResponseExpanded : T extends LedgerRequest - ? LedgerResponse + ? LedgerVersionResponseMap : T extends LedgerClosedRequest ? LedgerClosedResponse : T extends LedgerCurrentRequest @@ -377,11 +390,11 @@ export type RequestResponseMap = T extends AccountChannelsRequest : T extends SubmitRequest ? SubmitResponse : T extends SubmitMultisignedRequest - ? SubmitMultisignedResponse + ? SubmitMultisignedVersionResponseMap : T extends TransactionEntryRequest ? TransactionEntryResponse : T extends TxRequest - ? TxResponse + ? TxVersionResponseMap : T extends BookOffersRequest ? BookOffersResponse : T extends DepositAuthorizedRequest @@ -420,20 +433,25 @@ export type RequestResponseMap = T extends AccountChannelsRequest ? NFTsByIssuerResponse : T extends NFTHistoryRequest ? NFTHistoryResponse - : Response + : Response export type MarkerRequest = Request & { limit?: number marker?: unknown } -export type MarkerResponse = Response & { +export type MarkerResponse< + Version extends APIVersion = typeof DEFAULT_API_VERSION, +> = Response & { result: { marker?: unknown } } -export type RequestAllResponseMap = T extends AccountChannelsRequest +export type RequestAllResponseMap< + T, + Version extends APIVersion = typeof DEFAULT_API_VERSION, +> = T extends AccountChannelsRequest ? AccountChannelsResponse : T extends AccountLinesRequest ? AccountLinesResponse @@ -442,14 +460,12 @@ export type RequestAllResponseMap = T extends AccountChannelsRequest : T extends AccountOffersRequest ? AccountOffersResponse : T extends AccountTxRequest - ? AccountTxResponse + ? AccountTxVersionResponseMap : T extends LedgerDataRequest ? LedgerDataResponse - : T extends AccountTxRequest - ? AccountTxResponse : T extends BookOffersRequest ? BookOffersResponse - : MarkerResponse + : MarkerResponse export { // Allow users to define their own requests and responses. This is useful for releasing experimental versions @@ -467,6 +483,7 @@ export { AccountInfoAccountFlags, AccountInfoRequest, AccountInfoResponse, + AccountInfoV1Response, AccountQueueData, AccountQueueTransaction, AccountLinesRequest, @@ -484,6 +501,7 @@ export { AccountOffersResponse, AccountTxRequest, AccountTxResponse, + AccountTxV1Response, AccountTxTransaction, GatewayBalance, GatewayBalancesRequest, @@ -495,6 +513,7 @@ export { // ledger methods LedgerRequest, LedgerResponse, + LedgerV1Response, LedgerQueueData, LedgerBinary, LedgerModifiedOfferCreateTransaction, @@ -514,10 +533,12 @@ export { SubmitResponse, SubmitMultisignedRequest, SubmitMultisignedResponse, + SubmitMultisignedV1Response, TransactionEntryRequest, TransactionEntryResponse, TxRequest, TxResponse, + TxV1Response, // path and order book methods with types BookOffersRequest, BookOffer, diff --git a/packages/xrpl/src/models/methods/ledger.ts b/packages/xrpl/src/models/methods/ledger.ts index 1ead273505..07c72cc27e 100644 --- a/packages/xrpl/src/models/methods/ledger.ts +++ b/packages/xrpl/src/models/methods/ledger.ts @@ -1,4 +1,5 @@ -import { Ledger } from '../ledger' +import { APIVersion, DEFAULT_API_VERSION, RIPPLED_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' @@ -207,6 +208,12 @@ export interface LedgerBinary transactions?: string[] } +export interface LedgerBinaryV1 + extends Omit, 'accountState'> { + accountState?: string[] + transactions?: string[] +} + interface LedgerResponseBase { /** Unique identifying hash of the entire ledger. */ ledger_hash: string @@ -231,6 +238,11 @@ interface LedgerResponseResult extends LedgerResponseBase { 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. @@ -241,9 +253,31 @@ export interface LedgerResponse extends BaseResponse { result: LedgerResponseResult } -interface LedgerResponseExpandedResult extends LedgerResponseBase { +/** + * 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 RIPPLED_API_V1 ? LedgerV1Response : LedgerResponse + +interface LedgerResponseExpandedResult< + Version extends APIVersion = typeof DEFAULT_API_VERSION, +> extends LedgerResponseBase { /** The complete header data of this {@link Ledger}. */ - ledger: Ledger + ledger: LedgerVersionMap } /** @@ -254,6 +288,8 @@ interface LedgerResponseExpandedResult extends LedgerResponseBase { * * @category Responses */ -export interface LedgerResponseExpanded extends BaseResponse { - result: LedgerResponseExpandedResult +export interface LedgerResponseExpanded< + Version extends APIVersion = typeof DEFAULT_API_VERSION, +> extends BaseResponse { + result: LedgerResponseExpandedResult } diff --git a/packages/xrpl/src/models/methods/submitMultisigned.ts b/packages/xrpl/src/models/methods/submitMultisigned.ts index 245541799c..da220bdc0c 100644 --- a/packages/xrpl/src/models/methods/submitMultisigned.ts +++ b/packages/xrpl/src/models/methods/submitMultisigned.ts @@ -1,3 +1,4 @@ +import { APIVersion, DEFAULT_API_VERSION, RIPPLED_API_V1 } from '../common' import { Transaction } from '../transactions' import { BaseRequest, BaseResponse } from './baseMethod' @@ -24,28 +25,59 @@ export interface SubmitMultisignedRequest extends BaseRequest { 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: { - /** - * 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. */ + 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 RIPPLED_API_V1 + ? SubmitMultisignedV1Response + : SubmitMultisignedResponse diff --git a/packages/xrpl/src/models/methods/tx.ts b/packages/xrpl/src/models/methods/tx.ts index 8c5839d675..c8df683fb2 100644 --- a/packages/xrpl/src/models/methods/tx.ts +++ b/packages/xrpl/src/models/methods/tx.ts @@ -1,3 +1,9 @@ +import { + APIVersion, + DEFAULT_API_VERSION, + RIPPLED_API_V1, + RIPPLED_API_V2, +} from '../common' import { Transaction, TransactionMetadata } from '../transactions' import { BaseTransaction } from '../transactions/common' @@ -41,6 +47,47 @@ export interface TxRequest extends BaseRequest { 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 RIPPLED_API_V2 + ? TransactionMetadata | string + : never + /** Transaction metadata, which describes the results of the transaction. + * Can be undefined if a transaction has not been validated yet. */ + meta?: TransactionMetadata | 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}. * @@ -48,28 +95,7 @@ export interface TxRequest extends BaseRequest { */ export interface TxResponse extends BaseResponse { - result: { - /** 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 - /** Transaction metadata, which describes the results of the transaction. - * Can be undefined if a transaction has not been validated yet. */ - meta?: TransactionMetadata | string - /** - * If true, this data comes from a validated ledger version; if omitted or. - * Set to false, this data is not final. - */ - validated?: boolean - /** - * This number measures the number of seconds since the "Ripple Epoch" of January 1, 2000 (00:00 UTC) - */ - date?: number - } & T + result: BaseTxResult & { 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 @@ -78,3 +104,29 @@ export interface TxResponse */ searched_all?: boolean } + +/** + * Response expected from a {@link TxRequest} using API version 1. + * + * @category ResponsesV1 + */ +export interface TxV1Response + extends BaseResponse { + result: BaseTxResult & 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 RIPPLED_API_V1 ? TxV1Response : TxResponse diff --git a/packages/xrpl/src/sugar/autofill.ts b/packages/xrpl/src/sugar/autofill.ts index 60e1da7f51..ca0757611d 100644 --- a/packages/xrpl/src/sugar/autofill.ts +++ b/packages/xrpl/src/sugar/autofill.ts @@ -1,7 +1,7 @@ import BigNumber from 'bignumber.js' import { xAddressToClassicAddress, isValidXAddress } from 'ripple-address-codec' -import type { Client } from '..' +import { type Client } from '..' import { ValidationError, XrplError } from '../errors' import { AccountInfoRequest, AccountObjectsRequest } from '../models/methods' import { Transaction } from '../models/transactions' diff --git a/packages/xrpl/src/sugar/getFeeXrp.ts b/packages/xrpl/src/sugar/getFeeXrp.ts index 0285d821ba..24c4c99a9f 100644 --- a/packages/xrpl/src/sugar/getFeeXrp.ts +++ b/packages/xrpl/src/sugar/getFeeXrp.ts @@ -1,6 +1,6 @@ import BigNumber from 'bignumber.js' -import type { Client } from '..' +import { type Client } from '..' import { XrplError } from '../errors' const NUM_DECIMAL_PLACES = 6 @@ -20,8 +20,11 @@ export default async function getFeeXrp( ): Promise { const feeCushion = cushion ?? client.feeCushion - const serverInfo = (await client.request({ command: 'server_info' })).result - .info + const serverInfo = ( + await client.request({ + command: 'server_info', + }) + ).result.info const baseFee = serverInfo.validated_ledger?.base_fee_xrp diff --git a/packages/xrpl/src/sugar/submit.ts b/packages/xrpl/src/sugar/submit.ts index ee5e0406f8..423a863cf8 100644 --- a/packages/xrpl/src/sugar/submit.ts +++ b/packages/xrpl/src/sugar/submit.ts @@ -10,7 +10,7 @@ import type { } from '..' import { ValidationError, XrplError } from '../errors' import { Signer } from '../models/common' -import { TxRequest, TxResponse } from '../models/methods' +import { TxResponse } from '../models/methods' import { BaseTransaction } from '../models/transactions/common' /** Approximate time for a ledger to close, in milliseconds */ @@ -129,7 +129,7 @@ export async function waitForFinalTransactionOutcome< } const txResponse = await client - .request>({ + .request({ command: 'tx', transaction: txHash, }) @@ -153,7 +153,9 @@ export async function waitForFinalTransactionOutcome< }) if (txResponse.result.validated) { - return txResponse + // TODO: resolve the type assertion below + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- we know that txResponse is of type TxResponse + return txResponse as TxResponse } return waitForFinalTransactionOutcome( diff --git a/packages/xrpl/src/utils/hashes/hashLedger.ts b/packages/xrpl/src/utils/hashes/hashLedger.ts index 1cf9997226..e9cb9329b6 100644 --- a/packages/xrpl/src/utils/hashes/hashLedger.ts +++ b/packages/xrpl/src/utils/hashes/hashLedger.ts @@ -8,8 +8,9 @@ import BigNumber from 'bignumber.js' import { decode, encode } from 'ripple-binary-codec' import { ValidationError, XrplError } from '../../errors' -import type { Ledger } from '../../models/ledger' +import { APIVersion } from '../../models' import { LedgerEntry } from '../../models/ledger' +import { LedgerVersionMap } from '../../models/ledger/Ledger' import { Transaction, TransactionMetadata } from '../../models/transactions' import HashPrefix from './HashPrefix' @@ -99,7 +100,9 @@ export function hashSignedTx(tx: Transaction | string): string { * @returns The hash of the ledger. * @category Utilities */ -export function hashLedgerHeader(ledgerHeader: Ledger): string { +export function hashLedgerHeader( + ledgerHeader: LedgerVersionMap, +): string { const prefix = HashPrefix.LEDGER.toString(HEX).toUpperCase() const ledger = @@ -158,7 +161,7 @@ export function hashStateTree(entries: LedgerEntry[]): string { } function computeTransactionHash( - ledger: Ledger, + ledger: LedgerVersionMap, options: HashLedgerHeaderOptions, ): string { const { transaction_hash } = ledger @@ -188,7 +191,7 @@ function computeTransactionHash( } function computeStateHash( - ledger: Ledger, + ledger: LedgerVersionMap, options: HashLedgerHeaderOptions, ): string { const { account_hash } = ledger @@ -222,7 +225,7 @@ function computeStateHash( * @category Utilities */ function hashLedger( - ledger: Ledger, + ledger: LedgerVersionMap, options: { computeTreeHashes?: boolean } = {}, diff --git a/packages/xrpl/src/utils/index.ts b/packages/xrpl/src/utils/index.ts index c96afd4151..2304384e90 100644 --- a/packages/xrpl/src/utils/index.ts +++ b/packages/xrpl/src/utils/index.ts @@ -23,6 +23,7 @@ import { } from 'ripple-binary-codec' import { verify as verifyKeypairSignature } from 'ripple-keypairs' +import type { APIVersion } from '../models' import { LedgerEntry } from '../models/ledger' import { Response } from '../models/methods' import { PaymentChannelClaim } from '../models/transactions/paymentChannelClaim' @@ -157,7 +158,7 @@ function isValidAddress(address: string): boolean { * @returns Whether the response has more pages of data. * @category Utilities */ -function hasNextPage(response: Response): boolean { +function hasNextPage(response: Response): boolean { // eslint-disable-next-line @typescript-eslint/dot-notation -- only checking if it exists return Boolean(response.result['marker']) } diff --git a/packages/xrpl/test/client/partialPayments.test.ts b/packages/xrpl/test/client/partialPayments.test.ts index b052a530e1..ad9633438f 100644 --- a/packages/xrpl/test/client/partialPayments.test.ts +++ b/packages/xrpl/test/client/partialPayments.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any -- required for formatting transactions */ import { expect } from 'chai' import cloneDeep from 'lodash/cloneDeep' @@ -24,7 +23,7 @@ describe('client handling of tfPartialPayments', function () { testContext.mockRippled!.addResponse('tx', rippled.tx.Payment) const resp = await testContext.client.request({ command: 'tx', - transaction: rippled.tx.Payment.result.hash, + transaction: rippled.tx.Payment.result.tx_json.hash, }) expect(resp.warnings).to.equal(undefined) @@ -35,7 +34,7 @@ describe('client handling of tfPartialPayments', function () { testContext.mockRippled!.addResponse('tx', mockResponse) const resp = await testContext.client.request({ command: 'tx', - transaction: mockResponse.result.hash, + transaction: mockResponse.result.tx_json.hash, }) expect(resp.warnings).to.deep.equal([ @@ -51,7 +50,7 @@ describe('client handling of tfPartialPayments', function () { testContext.mockRippled!.addResponse('tx', mockResponse) const resp = await testContext.client.request({ command: 'tx', - transaction: mockResponse.result.hash, + transaction: mockResponse.result.tx_json.hash, }) expect(resp.warnings).to.deep.equal([ @@ -82,8 +81,10 @@ describe('client handling of tfPartialPayments', function () { } const mockResponse = rippled.account_tx.normal mockResponse.result.transactions.push({ - tx: partial.result, + tx_json: partial.result.tx_json, meta: partial.result.meta, + validated: true, + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- we are mocking the response } as any) testContext.mockRippled!.addResponse('account_tx', mockResponse) @@ -105,8 +106,10 @@ describe('client handling of tfPartialPayments', function () { const partial = { ...rippled.tx.Payment, result: partialPaymentXRP } const mockResponse = rippled.account_tx.normal mockResponse.result.transactions.push({ - tx: partial.result, + tx_json: partial.result.tx_json, meta: partial.result.meta, + validated: true, + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- we are mocking the response } as any) testContext.mockRippled!.addResponse('account_tx', mockResponse) @@ -138,7 +141,7 @@ describe('client handling of tfPartialPayments', function () { it('transaction_entry with XRP tfPartialPayment', async function () { const mockResponse = cloneDeep(rippled.transaction_entry) - mockResponse.result.tx_json.Amount = '1000' + mockResponse.result.tx_json.DeliverMax = '1000' testContext.mockRippled!.addResponse('transaction_entry', mockResponse) const resp = await testContext.client.request({ command: 'transaction_entry', diff --git a/packages/xrpl/test/fixtures/rippled/accountTx.json b/packages/xrpl/test/fixtures/rippled/accountTx.json index 707334fa47..8156789a1b 100644 --- a/packages/xrpl/test/fixtures/rippled/accountTx.json +++ b/packages/xrpl/test/fixtures/rippled/accountTx.json @@ -91,7 +91,7 @@ "TransactionIndex": 12, "TransactionResult": "tesSUCCESS" }, - "tx": { + "tx_json": { "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Destination": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX", "DestinationTag": 13, @@ -169,7 +169,7 @@ "TransactionIndex": 59, "TransactionResult": "tesSUCCESS" }, - "tx": { + "tx_json": { "Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", "Authorize": "ra5nK24KXen9AHvsdFTKHSANinZseWnPcX", "Fee": "10", diff --git a/packages/xrpl/test/fixtures/rippled/partialPaymentIOU.json b/packages/xrpl/test/fixtures/rippled/partialPaymentIOU.json index 711f57f2ff..ffd8784704 100644 --- a/packages/xrpl/test/fixtures/rippled/partialPaymentIOU.json +++ b/packages/xrpl/test/fixtures/rippled/partialPaymentIOU.json @@ -1,18 +1,20 @@ { - "Account": "rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX", - "Amount": { - "currency": "USD", - "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", - "value": "10" + "tx_json": { + "Account": "rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX", + "DeliverMax": { + "currency": "USD", + "issuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", + "value": "10" + }, + "Destination": "rNNuQMuExCiEjeZ4h9JJnj5PSWypdMXDj4", + "Fee": "10000", + "Flags": 131072, + "Sequence": 23295, + "SigningPubKey": "02B205F4B92351AC0EEB04254B636F4C49EF922CFA3CAAD03C6477DA1E04E94B53", + "TransactionType": "Payment", + "TxnSignature": "3045022100FAF247A836D601DE74A515B2AADE31186D8B0DA9C23DE489E09753F5CF4BB81F0220477C5B5BC3AC89F2347744F9E00CCA62267E198489D747578162C4C7D156211D", + "hash": "A0A074D10355223CBE2520A42F93A52E3CC8B4D692570EB4841084F9BBB39F7A" }, - "Destination": "rNNuQMuExCiEjeZ4h9JJnj5PSWypdMXDj4", - "Fee": "10000", - "Flags": 131072, - "Sequence": 23295, - "SigningPubKey": "02B205F4B92351AC0EEB04254B636F4C49EF922CFA3CAAD03C6477DA1E04E94B53", - "TransactionType": "Payment", - "TxnSignature": "3045022100FAF247A836D601DE74A515B2AADE31186D8B0DA9C23DE489E09753F5CF4BB81F0220477C5B5BC3AC89F2347744F9E00CCA62267E198489D747578162C4C7D156211D", - "hash": "A0A074D10355223CBE2520A42F93A52E3CC8B4D692570EB4841084F9BBB39F7A", "meta": { "AffectedNodes": [ { diff --git a/packages/xrpl/test/fixtures/rippled/partialPaymentXRP.json b/packages/xrpl/test/fixtures/rippled/partialPaymentXRP.json index cca0515ce6..ee6e4c424e 100644 --- a/packages/xrpl/test/fixtures/rippled/partialPaymentXRP.json +++ b/packages/xrpl/test/fixtures/rippled/partialPaymentXRP.json @@ -1,14 +1,16 @@ { - "Account": "rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX", - "Amount": "2000000", - "Destination": "rNNuQMuExCiEjeZ4h9JJnj5PSWypdMXDj4", - "Fee": "10000", - "Flags": 131072, - "Sequence": 23295, - "SigningPubKey": "02B205F4B92351AC0EEB04254B636F4C49EF922CFA3CAAD03C6477DA1E04E94B53", - "TransactionType": "Payment", - "TxnSignature": "3045022100FAF247A836D601DE74A515B2AADE31186D8B0DA9C23DE489E09753F5CF4BB81F0220477C5B5BC3AC89F2347744F9E00CCA62267E198489D747578162C4C7D156211D", - "hash": "A0A074D10355223CBE2520A42F93A52E3CC8B4D692570EB4841084F9BBB39F7A", + "tx_json": { + "Account": "rGFuMiw48HdbnrUbkRYuitXTmfrDBNTCnX", + "DeliverMax": "2000000", + "Destination": "rNNuQMuExCiEjeZ4h9JJnj5PSWypdMXDj4", + "Fee": "10000", + "Flags": 131072, + "Sequence": 23295, + "SigningPubKey": "02B205F4B92351AC0EEB04254B636F4C49EF922CFA3CAAD03C6477DA1E04E94B53", + "TransactionType": "Payment", + "TxnSignature": "3045022100FAF247A836D601DE74A515B2AADE31186D8B0DA9C23DE489E09753F5CF4BB81F0220477C5B5BC3AC89F2347744F9E00CCA62267E198489D747578162C4C7D156211D", + "hash": "A0A074D10355223CBE2520A42F93A52E3CC8B4D692570EB4841084F9BBB39F7A" + }, "meta": { "AffectedNodes": [ { diff --git a/packages/xrpl/test/fixtures/rippled/transactionEntry.json b/packages/xrpl/test/fixtures/rippled/transactionEntry.json index 5f922f6fd1..dc29197b54 100644 --- a/packages/xrpl/test/fixtures/rippled/transactionEntry.json +++ b/packages/xrpl/test/fixtures/rippled/transactionEntry.json @@ -50,7 +50,7 @@ }, "tx_json": { "Account": "rLSn6Z3T8uCxbcd1oxwfGQN1Fdn5CyGujK", - "Amount": "104169972", + "DeliverMax": "104169972", "Destination": "rEb8TK3gBgk5auZkwc6sHnwrGVJH8DuaLh", "DestinationTag": 109735445, "Fee": "6000", diff --git a/packages/xrpl/test/fixtures/rippled/tx/payment.json b/packages/xrpl/test/fixtures/rippled/tx/payment.json index 327982d98d..075e35d680 100644 --- a/packages/xrpl/test/fixtures/rippled/tx/payment.json +++ b/packages/xrpl/test/fixtures/rippled/tx/payment.json @@ -3,41 +3,43 @@ "status": "success", "type": "response", "result": { - "Account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", - "Amount": { - "currency": "USD", - "issuer": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", - "value": "0.001" + "tx_json": { + "Account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59", + "DeliverMax": { + "currency": "USD", + "issuer": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", + "value": "0.001" + }, + "Destination": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", + "Fee": "10", + "Flags": 0, + "Paths": [ + [ + { + "currency": "USD", + "issuer": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", + "type": 48, + "type_hex": "0000000000000030" + }, + { + "account": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", + "currency": "USD", + "issuer": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", + "type": 49, + "type_hex": "0000000000000031" + } + ] + ], + "SendMax": "1112209", + "Sequence": 4, + "SigningPubKey": "02BC8C02199949B15C005B997E7C8594574E9B02BA2D0628902E0532989976CF9D", + "TransactionType": "Payment", + "TxnSignature": "304502204EE3E9D1B01D8959B08450FCA9E22025AF503DEF310E34A93863A85CAB3C0BC5022100B61F5B567F77026E8DEED89EED0B7CAF0E6C96C228A2A65216F0DC2D04D52083", + "date": 416447810, + "inLedger": 348860, + "ledger_index": 348860, + "hash": "F4AB442A6D4CBB935D66E1DA7309A5FC71C7143ED4049053EC14E3875B0CF9BF" }, - "Destination": "rMH4UxPrbuMa1spCBR98hLLyNJp4d8p4tM", - "Fee": "10", - "Flags": 0, - "Paths": [ - [ - { - "currency": "USD", - "issuer": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", - "type": 48, - "type_hex": "0000000000000030" - }, - { - "account": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", - "currency": "USD", - "issuer": "rpZc4mVfWUif9CRoHRKKcmhu1nx2xktxBo", - "type": 49, - "type_hex": "0000000000000031" - } - ] - ], - "SendMax": "1112209", - "Sequence": 4, - "SigningPubKey": "02BC8C02199949B15C005B997E7C8594574E9B02BA2D0628902E0532989976CF9D", - "TransactionType": "Payment", - "TxnSignature": "304502204EE3E9D1B01D8959B08450FCA9E22025AF503DEF310E34A93863A85CAB3C0BC5022100B61F5B567F77026E8DEED89EED0B7CAF0E6C96C228A2A65216F0DC2D04D52083", - "date": 416447810, - "hash": "F4AB442A6D4CBB935D66E1DA7309A5FC71C7143ED4049053EC14E3875B0CF9BF", - "inLedger": 348860, - "ledger_index": 348860, "meta": { "AffectedNodes": [ { diff --git a/packages/xrpl/test/integration/README.md b/packages/xrpl/test/integration/README.md index d9754fca98..e4bdec3e33 100644 --- a/packages/xrpl/test/integration/README.md +++ b/packages/xrpl/test/integration/README.md @@ -1,7 +1,7 @@ To run integration tests: 1. Run rippled in standalone node, either in a docker container (preferred) or by installing rippled. * Go to the top-level of the `xrpl.js` repo, just above the `packages` folder. - * With docker, run `docker run -p 6006:6006 --interactive -t --volume $PWD/.ci-config:/config/ xrpllabsofficial/1.12.0-b1 -a --start` + * With docker, run `docker run -p 6006:6006 --interactive -t --volume $PWD/.ci-config:/opt/ripple/etc/ --platform linux/amd64 rippleci/rippled:2.2.0-b3 /opt/ripple/bin/rippled -a --conf /opt/ripple/etc/rippled.cfg` * Or [download and build rippled](https://xrpl.org/install-rippled.html) and run `./rippled -a --start` * If you'd like to use the latest rippled amendments, you should modify your `rippled.cfg` file to enable amendments in the `[amendments]` section. You can view `.ci-config/rippled.cfg` in the top level folder as an example of this. 2. Run `npm run test:integration` or `npm run test:browser` diff --git a/packages/xrpl/test/integration/requests/accountInfo.test.ts b/packages/xrpl/test/integration/requests/accountInfo.test.ts index c31a097506..35484f4f0c 100644 --- a/packages/xrpl/test/integration/requests/accountInfo.test.ts +++ b/packages/xrpl/test/integration/requests/accountInfo.test.ts @@ -80,4 +80,105 @@ describe('account_info', function () { }, TIMEOUT, ) + + it( + 'uses api_version 1', + async () => { + const request: AccountInfoRequest = { + command: 'account_info', + account: testContext.wallet.classicAddress, + strict: true, + ledger_index: 'validated', + api_version: 1, + } + const response = await testContext.client.request(request) + const expected = { + id: 0, + result: { + account_data: { + Account: testContext.wallet.classicAddress, + Balance: '400000000', + Flags: 0, + LedgerEntryType: 'AccountRoot', + OwnerCount: 0, + PreviousTxnID: + '19A8211695785A3A02C1C287D93C2B049E83A9CD609825E721052D63FF4F0EC8', + PreviousTxnLgrSeq: 582, + Sequence: 283, + index: + 'BD4815E6EB304136E6044F778FB68D4E464CC8DFC59B8F6CC93D90A3709AE194', + }, + ledger_hash: + 'F0DEEC46A7185BBB535517EE38CF2025973022D5B0532B36407F492521FDB0C6', + ledger_index: 582, + validated: true, + }, + type: 'response', + } + assert.equal(response.type, expected.type) + assert.equal(response.result.validated, expected.result.validated) + assert.equal(typeof response.result.ledger_index, 'number') + assert.equal(typeof response.result.account_data.PreviousTxnID, 'string') + assert.equal(typeof response.result.account_data.index, 'string') + assert.equal( + typeof response.result.account_data.PreviousTxnLgrSeq, + 'number', + ) + assert.equal(typeof response.result.account_data.Sequence, 'number') + assert.deepEqual( + omit(response.result.account_data, [ + 'PreviousTxnID', + 'PreviousTxnLgrSeq', + 'Sequence', + 'index', + ]), + omit(expected.result.account_data, [ + 'PreviousTxnID', + 'PreviousTxnLgrSeq', + 'Sequence', + 'index', + ]), + ) + }, + TIMEOUT, + ) + + it( + 'signer_list using api_version 1', + async () => { + const request: AccountInfoRequest = { + command: 'account_info', + account: testContext.wallet.classicAddress, + strict: true, + ledger_index: 'validated', + signer_lists: true, + api_version: 1, + } + const response = await testContext.client.request( + request, + ) + expect(response.result.account_data.signer_lists).toEqual([]) + // @ts-expect-error -- signer_lists is expected to be undefined + expect(response.result.signer_lists).toBeUndefined() + }, + TIMEOUT, + ) + + it( + 'signer_list using api_version 2', + async () => { + const request: AccountInfoRequest = { + command: 'account_info', + account: testContext.wallet.classicAddress, + strict: true, + ledger_index: 'validated', + signer_lists: true, + } + const response = await testContext.client.request(request) + // @ts-expect-error -- signer_lists is expected to be undefined + expect(response.result.account_data.signer_lists).toBeUndefined() + expect(response.result.signer_lists).toEqual([]) + }, + TIMEOUT, + ) }) diff --git a/packages/xrpl/test/integration/requests/accountTx.test.ts b/packages/xrpl/test/integration/requests/accountTx.test.ts index 64c340c2b6..a0af38444b 100644 --- a/packages/xrpl/test/integration/requests/accountTx.test.ts +++ b/packages/xrpl/test/integration/requests/accountTx.test.ts @@ -32,6 +32,102 @@ describe('account_tx', function () { ledger_index: 'validated', } const response = await testContext.client.request(request) + const expected = { + result: { + account: testContext.wallet.classicAddress, + limit: 400, + transactions: [ + { + tx_json: { + Account: 'rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh', + DeliverMax: '400000000', + Destination: testContext.wallet.classicAddress, + Fee: '12', + Flags: 0, + LastLedgerSequence: 1753, + Sequence: 843, + SigningPubKey: + '0330E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020', + TransactionType: 'Payment', + TxnSignature: + '30440220693D244BC13967E3DA67BDC974096784ED03DD4ACE6F36645E5176988452AFCF02200F8AB172432913899F27EC5523829AEDAD00CC2445690400E294EDF652A85945', + date: 685747005, + hash: '2E68BC15813B4A836FAC4D80E42E6FDA6410E99AB973937DEA5E6C2E9A116BAB', + ledger_index: 1734, + }, + }, + ], + }, + type: 'response', + } + assert.equal(response.type, expected.type) + assert.equal(response.result.account, expected.result.account) + assert.equal( + (response.result.transactions[0].meta as TransactionMetadata) + .TransactionResult, + 'tesSUCCESS', + ) + assert.equal( + typeof response.result.transactions[0].tx_json?.LastLedgerSequence, + 'number', + ) + assert.equal( + typeof response.result.transactions[0].tx_json?.Sequence, + 'number', + ) + assert.equal( + typeof response.result.transactions[0].tx_json?.SigningPubKey, + 'string', + ) + assert.equal( + typeof response.result.transactions[0].tx_json?.TxnSignature, + 'string', + ) + assert.equal( + typeof response.result.transactions[0].tx_json?.Fee, + 'string', + ) + assert.equal(typeof response.result.transactions[0].hash, 'string') + assert.equal( + typeof response.result.transactions[0].tx_json?.ledger_index, + 'number', + ) + + const responseTx = response.result.transactions[0].tx_json as Payment + const expectedTx = expected.result.transactions[0].tx_json + assert.deepEqual( + [ + responseTx.Flags, + responseTx.TransactionType, + responseTx.Account, + // @ts-expect-error -- DeliverMax is a valid field on Payment response + responseTx.DeliverMax, + responseTx.Destination, + ], + [ + expectedTx.Flags, + expectedTx.TransactionType, + expectedTx.Account, + expectedTx.DeliverMax, + expectedTx.Destination, + ], + ) + }, + TIMEOUT, + ) + + it( + 'uses api_version 1', + async () => { + const request: AccountTxRequest = { + command: 'account_tx', + account: testContext.wallet.classicAddress, + ledger_index: 'validated', + api_version: 1, + } + const response = await testContext.client.request( + request, + ) const expected = { result: { account: testContext.wallet.classicAddress, diff --git a/packages/xrpl/test/integration/requests/bookOffers.test.ts b/packages/xrpl/test/integration/requests/bookOffers.test.ts index b2cdbdcf00..84c2b748cc 100644 --- a/packages/xrpl/test/integration/requests/bookOffers.test.ts +++ b/packages/xrpl/test/integration/requests/bookOffers.test.ts @@ -35,6 +35,7 @@ describe('book_offers', function () { const response = await testContext.client.request(bookOffer) const expectedResponse: BookOffersResponse = { + api_version: 2, id: response.id, type: 'response', result: { diff --git a/packages/xrpl/test/integration/requests/channelVerify.test.ts b/packages/xrpl/test/integration/requests/channelVerify.test.ts index facd738f17..d823c853f5 100644 --- a/packages/xrpl/test/integration/requests/channelVerify.test.ts +++ b/packages/xrpl/test/integration/requests/channelVerify.test.ts @@ -35,6 +35,7 @@ describe('channel_verify', function () { const response = await testContext.client.request(channelVerify) const expectedResponse: ChannelVerifyResponse = { + api_version: 2, id: response.id, type: 'response', result: { diff --git a/packages/xrpl/test/integration/requests/depositAuthorized.test.ts b/packages/xrpl/test/integration/requests/depositAuthorized.test.ts index 46ace268a1..2b929f27c6 100644 --- a/packages/xrpl/test/integration/requests/depositAuthorized.test.ts +++ b/packages/xrpl/test/integration/requests/depositAuthorized.test.ts @@ -36,6 +36,7 @@ describe('deposit_authorized', function () { const response = await testContext.client.request(depositAuthorized) const expectedResponse: DepositAuthorizedResponse = { + api_version: 2, id: response.id, type: 'response', result: { diff --git a/packages/xrpl/test/integration/requests/ledger.test.ts b/packages/xrpl/test/integration/requests/ledger.test.ts index 020e5d3198..7a365e1f64 100644 --- a/packages/xrpl/test/integration/requests/ledger.test.ts +++ b/packages/xrpl/test/integration/requests/ledger.test.ts @@ -1,7 +1,7 @@ import { assert } from 'chai' -import { LedgerRequest, LedgerResponse } from '../../../src' -import { Ledger } from '../../../src/models/ledger' +import { LedgerRequest } from '../../../src' +import { Ledger, LedgerV1 } from '../../../src/models/ledger' import serverUrl from '../serverUrl' import { setupClient, @@ -29,6 +29,7 @@ describe('ledger', function () { } const expected = { + api_version: 2, id: 0, result: { ledger: { @@ -45,7 +46,68 @@ describe('ledger', function () { type: 'response', } - const ledgerResponse: LedgerResponse = await testContext.client.request( + const ledgerResponse = await testContext.client.request(ledgerRequest) + + assert.equal(ledgerResponse.type, expected.type) + + assert.equal(ledgerResponse.result.validated, expected.result.validated) + assert.typeOf(ledgerResponse.result.ledger_hash, 'string') + assert.typeOf(ledgerResponse.result.ledger_index, 'number') + + const ledger = ledgerResponse.result.ledger as Ledger & { + accepted: boolean + hash: string + seqNum: string + } + assert.equal(ledger.closed, true) + const stringTypes = [ + 'account_hash', + 'close_time_human', + 'ledger_hash', + 'parent_hash', + 'total_coins', + 'transaction_hash', + ] + stringTypes.forEach((strType) => assert.typeOf(ledger[strType], 'string')) + const numTypes = [ + 'close_flags', + 'close_time', + 'close_time_resolution', + 'ledger_index', + 'parent_close_time', + ] + numTypes.forEach((numType) => assert.typeOf(ledger[numType], 'number')) + }, + TIMEOUT, + ) + + it( + 'uses api_version 1', + async () => { + const ledgerRequest: LedgerRequest = { + command: 'ledger', + ledger_index: 'validated', + api_version: 1, + } + + const expected = { + id: 0, + result: { + ledger: { + accepted: true, + account_hash: 'string', + close_flags: 0, + close_time: 0, + close_time_human: 'string', + }, + ledger_hash: 'string', + ledger_index: 1, + validated: true, + }, + type: 'response', + } + + const ledgerResponse = await testContext.client.request( ledgerRequest, ) @@ -55,7 +117,7 @@ describe('ledger', function () { assert.typeOf(ledgerResponse.result.ledger_hash, 'string') assert.typeOf(ledgerResponse.result.ledger_index, 'number') - const ledger = ledgerResponse.result.ledger as Ledger & { + const ledger = ledgerResponse.result.ledger as LedgerV1 & { accepted: boolean hash: string seqNum: string diff --git a/packages/xrpl/test/integration/requests/ledgerEntry.test.ts b/packages/xrpl/test/integration/requests/ledgerEntry.test.ts index f583b0206d..8eb34fcd62 100644 --- a/packages/xrpl/test/integration/requests/ledgerEntry.test.ts +++ b/packages/xrpl/test/integration/requests/ledgerEntry.test.ts @@ -40,6 +40,7 @@ describe('ledger_entry', function () { ) const expectedResponse: LedgerEntryResponse = { + api_version: 2, id: ledgerEntryResponse.id, type: 'response', result: { diff --git a/packages/xrpl/test/integration/requests/pathFind.test.ts b/packages/xrpl/test/integration/requests/pathFind.test.ts index bf9f884ba7..13008a3e2b 100644 --- a/packages/xrpl/test/integration/requests/pathFind.test.ts +++ b/packages/xrpl/test/integration/requests/pathFind.test.ts @@ -41,6 +41,7 @@ describe('path_find', function () { const response = await testContext.client.request(pathFind) const expectedResponse: PathFindResponse = { + api_version: 2, id: response.id, type: 'response', result: { @@ -98,6 +99,7 @@ describe('path_find', function () { const response = await testContext.client.request(pathFind) const expectedResponse: PathFindResponse = { + api_version: 2, id: response.id, type: 'response', result: { diff --git a/packages/xrpl/test/integration/requests/ripplePathFind.test.ts b/packages/xrpl/test/integration/requests/ripplePathFind.test.ts index d227c03935..6464f652c2 100644 --- a/packages/xrpl/test/integration/requests/ripplePathFind.test.ts +++ b/packages/xrpl/test/integration/requests/ripplePathFind.test.ts @@ -35,6 +35,7 @@ describe('ripple_path_find', function () { const response = await testContext.client.request(ripplePathFind) const expectedResponse: RipplePathFindResponse = { + api_version: 2, id: response.id, type: 'response', result: { diff --git a/packages/xrpl/test/integration/requests/submit.test.ts b/packages/xrpl/test/integration/requests/submit.test.ts index 0d46ba0676..a9f7c521b9 100644 --- a/packages/xrpl/test/integration/requests/submit.test.ts +++ b/packages/xrpl/test/integration/requests/submit.test.ts @@ -54,6 +54,7 @@ describe('submit', function () { ) const expectedResponse: SubmitResponse = { + api_version: 2, id: submitResponse.id, type: 'response', result: { diff --git a/packages/xrpl/test/integration/requests/submitMultisigned.test.ts b/packages/xrpl/test/integration/requests/submitMultisigned.test.ts index ec21a1b94e..90dfe99e4b 100644 --- a/packages/xrpl/test/integration/requests/submitMultisigned.test.ts +++ b/packages/xrpl/test/integration/requests/submitMultisigned.test.ts @@ -8,6 +8,8 @@ import { Transaction, SubmitMultisignedResponse, hashes, + SubmitMultisignedRequest, + SubmitMultisignedV1Response, } from '../../../src' import { convertStringToHex } from '../../../src/utils' import { multisign } from '../../../src/Wallet/signer' @@ -88,6 +90,81 @@ describe('submit_multisigned', function () { await verifySubmittedTransaction(testContext.client, multisigned) const expectedResponse: SubmitMultisignedResponse = { + api_version: 2, + id: submitResponse.id, + type: 'response', + result: { + engine_result: 'tesSUCCESS', + engine_result_code: 0, + engine_result_message: + 'The transaction was applied. Only final in a validated ledger.', + tx_blob: multisigned, + tx_json: { + ...(decode(multisigned) as unknown as Transaction), + }, + hash: hashSignedTx(multisigned), + }, + } + + assert.deepEqual(submitResponse, expectedResponse) + }, + TIMEOUT, + ) + + it( + 'submit_multisigned transaction using api_version 1', + async () => { + const client: Client = testContext.client + const signerWallet1 = await generateFundedWallet(testContext.client) + const signerWallet2 = await generateFundedWallet(testContext.client) + + // set up the multisigners for the account + const signerListSet: SignerListSet = { + TransactionType: 'SignerListSet', + Account: testContext.wallet.classicAddress, + SignerEntries: [ + { + SignerEntry: { + Account: signerWallet1.classicAddress, + SignerWeight: 1, + }, + }, + { + SignerEntry: { + Account: signerWallet2.classicAddress, + SignerWeight: 1, + }, + }, + ], + SignerQuorum: 2, + } + await testTransaction( + testContext.client, + signerListSet, + testContext.wallet, + ) + + // try to multisign + const accountSet: AccountSet = { + TransactionType: 'AccountSet', + Account: testContext.wallet.classicAddress, + Domain: convertStringToHex('example.com'), + } + const accountSetTx = await client.autofill(accountSet, 2) + const signed1 = signerWallet1.sign(accountSetTx, true) + const signed2 = signerWallet2.sign(accountSetTx, true) + const multisigned = multisign([signed1.tx_blob, signed2.tx_blob]) + const submitResponse = await client.request({ + command: 'submit_multisigned', + tx_json: decode(multisigned) as unknown as Transaction, + api_version: 1, + }) + await ledgerAccept(client) + assert.strictEqual(submitResponse.result.engine_result, 'tesSUCCESS') + await verifySubmittedTransaction(testContext.client, multisigned) + + const expectedResponse: SubmitMultisignedV1Response = { + api_version: 1, id: submitResponse.id, type: 'response', result: { diff --git a/packages/xrpl/test/integration/requests/tx.test.ts b/packages/xrpl/test/integration/requests/tx.test.ts index fbdb347dcb..14c3292d8f 100644 --- a/packages/xrpl/test/integration/requests/tx.test.ts +++ b/packages/xrpl/test/integration/requests/tx.test.ts @@ -1,6 +1,13 @@ import { assert } from 'chai' -import { AccountSet, hashes, SubmitResponse, TxResponse } from '../../../src' +import { + AccountSet, + hashes, + SubmitResponse, + TxRequest, + TxResponse, + TxV1Response, +} from '../../../src' import { convertStringToHex } from '../../../src/utils' import serverUrl from '../serverUrl' import { @@ -45,22 +52,71 @@ describe('tx', function () { }) const expectedResponse: TxResponse = { + api_version: 2, id: txResponse.id, type: 'response', + result: { + hash: hashSignedTx(response.result.tx_blob), + tx_json: { + ...accountSet, + Fee: txResponse.result.tx_json.Fee, + Flags: 0, + LastLedgerSequence: txResponse.result.tx_json.LastLedgerSequence, + Sequence: txResponse.result.tx_json.Sequence, + SigningPubKey: testContext.wallet.publicKey, + TxnSignature: txResponse.result.tx_json.TxnSignature, + }, + validated: false, + }, + } + + assert.deepEqual(txResponse, expectedResponse) + }, + TIMEOUT, + ) + + it( + 'uses api_version 1', + async () => { + const account = testContext.wallet.classicAddress + const accountSet: AccountSet = { + TransactionType: 'AccountSet', + Account: account, + Domain: convertStringToHex('example.com'), + } + + const response: SubmitResponse = await testContext.client.submit( + accountSet, + { + wallet: testContext.wallet, + }, + ) + + const hash = hashSignedTx(response.result.tx_blob) + const txV1Response = await testContext.client.request({ + command: 'tx', + transaction: hash, + api_version: 1, + }) + + const expectedResponse: TxV1Response = { + api_version: 1, + id: txV1Response.id, + type: 'response', result: { ...accountSet, - Fee: txResponse.result.Fee, + Fee: txV1Response.result.Fee, Flags: 0, - LastLedgerSequence: txResponse.result.LastLedgerSequence, - Sequence: txResponse.result.Sequence, + LastLedgerSequence: txV1Response.result.LastLedgerSequence, + Sequence: txV1Response.result.Sequence, SigningPubKey: testContext.wallet.publicKey, - TxnSignature: txResponse.result.TxnSignature, + TxnSignature: txV1Response.result.TxnSignature, hash: hashSignedTx(response.result.tx_blob), validated: false, }, } - assert.deepEqual(txResponse, expectedResponse) + assert.deepEqual(txV1Response, expectedResponse) }, TIMEOUT, ) diff --git a/packages/xrpl/test/integration/requests/utility.test.ts b/packages/xrpl/test/integration/requests/utility.test.ts index 115f5b20e3..8cbd98a76b 100644 --- a/packages/xrpl/test/integration/requests/utility.test.ts +++ b/packages/xrpl/test/integration/requests/utility.test.ts @@ -26,6 +26,7 @@ describe('Utility method integration tests', function () { command: 'ping', }) const expected: unknown = { + api_version: 2, result: { role: 'admin', unlimited: true }, type: 'response', } @@ -41,6 +42,7 @@ describe('Utility method integration tests', function () { command: 'random', }) const expected = { + api_version: 2, id: 0, result: { random: '[random string of 64 bytes]', diff --git a/packages/xrpl/test/integration/setup.ts b/packages/xrpl/test/integration/setup.ts index 9a3d85b5c4..40eb53fbef 100644 --- a/packages/xrpl/test/integration/setup.ts +++ b/packages/xrpl/test/integration/setup.ts @@ -158,8 +158,7 @@ export async function setupBridge(client: Client): Promise { account: doorAccount.classicAddress, signer_lists: true, }) - const signerListInfo = - signerAccountInfoResponse.result.account_data.signer_lists?.[0] + const signerListInfo = signerAccountInfoResponse.result.signer_lists?.[0] assert.deepEqual( signerListInfo?.SignerEntries, signerTx.SignerEntries, diff --git a/packages/xrpl/test/integration/transactions/escrowCancel.test.ts b/packages/xrpl/test/integration/transactions/escrowCancel.test.ts index 71d13f17ea..56548fe0c9 100644 --- a/packages/xrpl/test/integration/transactions/escrowCancel.test.ts +++ b/packages/xrpl/test/integration/transactions/escrowCancel.test.ts @@ -71,7 +71,7 @@ describe('EscrowCancel', function () { command: 'tx', transaction: accountObjects[0].PreviousTxnID, }) - ).result.Sequence + ).result.tx_json.Sequence if (!sequence) { throw new Error('sequence did not exist') diff --git a/packages/xrpl/test/integration/transactions/escrowFinish.test.ts b/packages/xrpl/test/integration/transactions/escrowFinish.test.ts index bd16f81cdd..3d11e3fd96 100644 --- a/packages/xrpl/test/integration/transactions/escrowFinish.test.ts +++ b/packages/xrpl/test/integration/transactions/escrowFinish.test.ts @@ -64,7 +64,7 @@ describe('EscrowFinish', function () { command: 'tx', transaction: accountObjects[0].PreviousTxnID, }) - ).result.Sequence + ).result.tx_json.Sequence const finishTx: EscrowFinish = { TransactionType: 'EscrowFinish', diff --git a/packages/xrpl/test/integration/transactions/nftokenMint.test.ts b/packages/xrpl/test/integration/transactions/nftokenMint.test.ts index e033369b29..daf1f29c00 100644 --- a/packages/xrpl/test/integration/transactions/nftokenMint.test.ts +++ b/packages/xrpl/test/integration/transactions/nftokenMint.test.ts @@ -85,7 +85,7 @@ describe('NFTokenMint', function () { assert.equal( nftokenID, - getNFTokenID(binaryTxResponse.result.meta) ?? 'undefined', + getNFTokenID(binaryTxResponse.result.meta_blob) ?? 'undefined', `getNFTokenID produced a different outcome when decoding the metadata in binary format.`, ) }, diff --git a/packages/xrpl/test/integration/transactions/signerListSet.test.ts b/packages/xrpl/test/integration/transactions/signerListSet.test.ts index dc022b6262..974f9fbabd 100644 --- a/packages/xrpl/test/integration/transactions/signerListSet.test.ts +++ b/packages/xrpl/test/integration/transactions/signerListSet.test.ts @@ -50,8 +50,7 @@ describe('SignerListSet', function () { account: testContext.wallet.classicAddress, signer_lists: true, }) - const signerListInfo = - accountInfoResponse.result.account_data.signer_lists?.[0] + const signerListInfo = accountInfoResponse.result.signer_lists?.[0] assert.deepEqual( signerListInfo?.SignerEntries, tx.SignerEntries, diff --git a/packages/xrpl/test/integration/transactions/xchainAddClaimAttestation.test.ts b/packages/xrpl/test/integration/transactions/xchainAddClaimAttestation.test.ts index e653938ad8..9462f0823f 100644 --- a/packages/xrpl/test/integration/transactions/xchainAddClaimAttestation.test.ts +++ b/packages/xrpl/test/integration/transactions/xchainAddClaimAttestation.test.ts @@ -191,8 +191,7 @@ describe('XChainCreateBridge', function () { account: testContext.wallet.classicAddress, signer_lists: true, }) - const signerListInfo = - signerAccountInfoResponse.result.account_data.signer_lists?.[0] + const signerListInfo = signerAccountInfoResponse.result.signer_lists?.[0] assert.deepEqual( signerListInfo?.SignerEntries, signerTx.SignerEntries, diff --git a/packages/xrpl/test/integration/utils.ts b/packages/xrpl/test/integration/utils.ts index f971291ff7..87f4529acb 100644 --- a/packages/xrpl/test/integration/utils.ts +++ b/packages/xrpl/test/integration/utils.ts @@ -198,11 +198,12 @@ export async function verifySubmittedTransaction( const decodedTx: any = typeof tx === 'string' ? decode(tx) : tx if (decodedTx.TransactionType === 'Payment') { decodedTx.DeliverMax = decodedTx.Amount + delete decodedTx.Amount } assert(data.result) assert.deepEqual( - omit(data.result, [ + omit(data.result.tx_json, [ 'ctid', 'date', 'hash',