diff --git a/package-lock.json b/package-lock.json index 518b385b5f..2eaede48f0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -99,6 +99,7 @@ "version": "7.28.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -602,6 +603,7 @@ "version": "4.5.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "escape-string-regexp": "^4.0.0", "ignore": "^5.2.4" @@ -714,6 +716,7 @@ "version": "3.3.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -769,6 +772,7 @@ "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -3278,6 +3282,7 @@ "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -3891,6 +3896,7 @@ "version": "9.6.1", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -4010,6 +4016,7 @@ "version": "18.19.121", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~5.26.4" } @@ -4099,6 +4106,7 @@ "integrity": "sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.52.0", "@typescript-eslint/types": "8.52.0", @@ -4888,6 +4896,7 @@ "version": "8.15.0", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5100,7 +5109,6 @@ "version": "5.3.2", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">= 0.4" } @@ -5172,7 +5180,6 @@ "version": "1.2.5", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -5246,7 +5253,6 @@ "version": "1.1.4", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -5291,8 +5297,7 @@ "node_modules/ast-types-flow": { "version": "0.0.8", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/async": { "version": "3.2.6", @@ -5334,7 +5339,6 @@ "version": "4.10.3", "dev": true, "license": "MPL-2.0", - "peer": true, "engines": { "node": ">=4" } @@ -5355,7 +5359,6 @@ "version": "4.1.0", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">= 0.4" } @@ -5708,6 +5711,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -6681,8 +6685,7 @@ "node_modules/damerau-levenshtein": { "version": "1.0.8", "dev": true, - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/dargs": { "version": "7.0.0", @@ -7093,8 +7096,7 @@ "node_modules/emoji-regex": { "version": "9.2.2", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/encodeurl": { "version": "1.0.2", @@ -7104,6 +7106,31 @@ "node": ">= 0.8" } }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", @@ -7355,7 +7382,6 @@ "version": "1.2.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -7467,6 +7493,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -7539,6 +7566,7 @@ "version": "10.1.8", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -7594,6 +7622,7 @@ "version": "4.4.4", "dev": true, "license": "ISC", + "peer": true, "dependencies": { "debug": "^4.4.1", "eslint-import-context": "^0.1.8", @@ -7653,6 +7682,7 @@ "integrity": "sha512-+OULB0IQdENBmBf8pHMPPObgV6QyfeXFin483jPonOaiurI9UFmc8UydWriK5f5Gel8xBhQLA6NzMwbck1BUJw==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -7684,6 +7714,7 @@ "version": "2.32.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -7752,6 +7783,7 @@ "version": "52.0.4", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "@es-joy/jsdoccomment": "~0.52.0", "are-docs-informative": "^0.0.2", @@ -7775,7 +7807,6 @@ "version": "6.10.2", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", @@ -7804,7 +7835,6 @@ "version": "1.1.12", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7814,7 +7844,6 @@ "version": "3.1.2", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7828,6 +7857,7 @@ "integrity": "sha512-68PealUpYoHOBh332JLLD9Sj7OQUDkFpmcfqt8R9sySfFSeuGJjMTJQvCRRB96zO3A/PELRLkPrzsHmzEFQQ5A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.5.0", "enhanced-resolve": "^5.17.1", @@ -7864,6 +7894,7 @@ "version": "5.5.4", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", "synckit": "^0.11.7" @@ -7893,7 +7924,6 @@ "version": "7.37.5", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -7937,7 +7967,6 @@ "version": "1.1.12", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7947,7 +7976,6 @@ "version": "3.1.2", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -7959,7 +7987,6 @@ "version": "2.0.0-next.5", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -7976,7 +8003,6 @@ "version": "6.3.1", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" } @@ -7987,6 +8013,7 @@ "integrity": "sha512-ush8ehCwub2rgE16OIgQPFyj/o0k3T8kL++9IrAI4knsmupNo8gvfO2ERgDHWWgTC5MglbwLVRswU93HyXqNpw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@microsoft/tsdoc": "0.16.0", "@microsoft/tsdoc-config": "0.18.0", @@ -9290,6 +9317,7 @@ "version": "16.3.0", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -10395,7 +10423,6 @@ "version": "1.1.5", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "define-data-property": "^1.1.4", "es-object-atoms": "^1.0.0", @@ -10451,6 +10478,7 @@ "version": "29.7.0", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -11304,7 +11332,6 @@ "version": "3.3.5", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "array-includes": "^3.1.6", "array.prototype.flat": "^1.3.1", @@ -11481,14 +11508,12 @@ "node_modules/language-subtag-registry": { "version": "0.3.23", "dev": true, - "license": "CC0-1.0", - "peer": true + "license": "CC0-1.0" }, "node_modules/language-tags": { "version": "1.0.9", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "language-subtag-registry": "^0.3.20" }, @@ -12225,7 +12250,6 @@ "version": "1.4.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -13774,7 +13798,6 @@ "version": "1.1.9", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", @@ -14550,6 +14573,7 @@ "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -14691,7 +14715,6 @@ "version": "15.8.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -14701,8 +14724,7 @@ "node_modules/prop-types/node_modules/react-is": { "version": "16.13.1", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/protocols": { "version": "2.0.2", @@ -15423,6 +15445,7 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -16118,7 +16141,6 @@ "version": "2.0.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1", @@ -16132,7 +16154,6 @@ "version": "4.0.12", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", @@ -16176,7 +16197,6 @@ "version": "1.0.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "define-properties": "^1.1.3", "es-abstract": "^1.17.5" @@ -16608,6 +16628,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -16837,6 +16858,7 @@ "version": "10.9.2", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -17066,6 +17088,7 @@ "version": "5.2.2", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -17080,6 +17103,7 @@ "integrity": "sha512-atlQQJ2YkO4pfTVQmQ+wvYQwexPDOIgo+RaVcD7gHgzy/IQA+XTyuxNM9M9TVXvttkF7koBHmcwisKdOAf2EcA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/eslint-plugin": "8.52.0", "@typescript-eslint/parser": "8.52.0", @@ -17218,6 +17242,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -17439,6 +17464,7 @@ "integrity": "sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", diff --git a/packages/xrpl/src/models/ledger/Ledger.ts b/packages/xrpl/src/models/ledger/Ledger.ts index 7ebc308800..0f2e8c4307 100644 --- a/packages/xrpl/src/models/ledger/Ledger.ts +++ b/packages/xrpl/src/models/ledger/Ledger.ts @@ -54,19 +54,30 @@ interface BaseLedger { 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 - } - > +} + +/** + * Expanded transaction format in API version 2. + * Transactions are returned as flat objects with the transaction fields + * directly on the object, plus `hash` and `metaData`. + */ +export type LedgerTransactionExpanded = Transaction & { + hash: string + metaData?: TransactionMetadata +} + +/** + * Expanded transaction format in API version 1. + * Transactions are wrapped in an object with `tx_json` and `meta` fields. + */ +export interface LedgerTransactionExpandedV1 { + tx_json: Transaction + meta: TransactionMetadata + hash: string + validated: boolean + ledger_index: number + close_time_iso: string + ledger_hash: string } /** @@ -80,6 +91,12 @@ export interface Ledger extends BaseLedger { * The ledger index of the ledger. Represented as a number. */ ledger_index: number + /** + * Transactions applied in this ledger version. When expanded, members are + * full representations of the transactions as flat objects with the + * transaction fields directly on the object, plus `hash` and `metaData`. + */ + transactions?: Array } /** @@ -95,6 +112,12 @@ export interface LedgerV1 extends BaseLedger { * integer; some display it as a number. */ ledger_index: string + /** + * Transactions applied in this ledger version. When expanded, members are + * full representations of the transactions wrapped in objects with + * `tx_json` and `meta` fields. + */ + transactions?: Array } /** diff --git a/packages/xrpl/src/models/ledger/index.ts b/packages/xrpl/src/models/ledger/index.ts index 3988698576..cc5363e181 100644 --- a/packages/xrpl/src/models/ledger/index.ts +++ b/packages/xrpl/src/models/ledger/index.ts @@ -17,7 +17,12 @@ import FeeSettings, { FeeSettingsPostAmendmentFields, FEE_SETTINGS_ID, } from './FeeSettings' -import { Ledger, LedgerV1 } from './Ledger' +import { + Ledger, + LedgerV1, + LedgerTransactionExpanded, + LedgerTransactionExpandedV1, +} from './Ledger' import { LedgerEntry, LedgerEntryFilter } from './LedgerEntry' import LedgerHashes from './LedgerHashes' import Loan, { LoanFlags } from './Loan' @@ -58,6 +63,8 @@ export { FeeSettingsPostAmendmentFields, Ledger, LedgerV1, + LedgerTransactionExpanded, + LedgerTransactionExpandedV1, LedgerEntryFilter, LedgerEntry, LedgerHashes, diff --git a/packages/xrpl/src/models/methods/ledger.ts b/packages/xrpl/src/models/methods/ledger.ts index c0dab28a71..72b90f1c61 100644 --- a/packages/xrpl/src/models/methods/ledger.ts +++ b/packages/xrpl/src/models/methods/ledger.ts @@ -144,7 +144,7 @@ export interface LedgerRequestExpandedAccountsOnly extends LedgerRequest { * * @category Requests */ -// eslint-disable-next-line max-len -- Disable for interface declaration. +// eslint-disable-next-line max-len -- interface name with extends exceeds 80 chars export interface LedgerRequestExpandedAccountsAndTransactions extends LedgerRequest { expand: true accounts: true diff --git a/packages/xrpl/src/utils/hashes/hashLedger.ts b/packages/xrpl/src/utils/hashes/hashLedger.ts index 8301dca50d..fa1c8dba24 100644 --- a/packages/xrpl/src/utils/hashes/hashLedger.ts +++ b/packages/xrpl/src/utils/hashes/hashLedger.ts @@ -10,8 +10,12 @@ import { decode, encode } from 'ripple-binary-codec' import { ValidationError, XrplError } from '../../errors' import { APIVersion } from '../../models' import { LedgerEntry } from '../../models/ledger' -import { LedgerVersionMap } from '../../models/ledger/Ledger' -import { Transaction, TransactionMetadata } from '../../models/transactions' +import { + LedgerVersionMap, + LedgerTransactionExpanded, + LedgerTransactionExpandedV1, +} from '../../models/ledger/Ledger' +import { Transaction } from '../../models/transactions' import { GlobalFlags } from '../../models/transactions/common' import { hasFlag } from '../../models/utils' @@ -130,9 +134,7 @@ export function hashLedgerHeader( * @returns The root hash of the SHAMap. * @category Utilities */ -export function hashTxTree( - transactions: Array, -): string { +export function hashTxTree(transactions: LedgerTransactionExpanded[]): string { const shamap = new SHAMap() for (const txJSON of transactions) { const txBlobHex = encode(txJSON) @@ -163,6 +165,30 @@ export function hashStateTree(entries: LedgerEntry[]): string { return shamap.hash } +/** + * Normalize a v1 wrapped transaction to v2 flat format. + * + * @param tx - The transaction to normalize. + * @returns The normalized v2 transaction. + */ +function normalizeToV2( + tx: LedgerTransactionExpanded | LedgerTransactionExpandedV1, +): LedgerTransactionExpanded { + if ('tx_json' in tx) { + // Transaction union type cannot be spread directly (TS2698), so we + // widen to a plain record first. + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- required to spread Transaction union + const txJson = tx.tx_json as Record + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- narrowing back after merge + return { + ...txJson, + hash: tx.hash, + metaData: tx.meta, + } as LedgerTransactionExpanded + } + return tx +} + function computeTransactionHash( ledger: LedgerVersionMap, options: HashLedgerHeaderOptions, @@ -177,7 +203,19 @@ function computeTransactionHash( throw new ValidationError('transactions is missing from the ledger') } - const transactionHash = hashTxTree(ledger.transactions) + // Normalize transactions to the v2 flat format expected by hashTxTree. + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- ledger is a version union + const txs = ledger.transactions as Array< + string | LedgerTransactionExpanded | LedgerTransactionExpandedV1 + > + const normalizedTransactions = txs + .filter( + (tx): tx is LedgerTransactionExpanded | LedgerTransactionExpandedV1 => + typeof tx !== 'string', + ) + .map(normalizeToV2) + + const transactionHash = hashTxTree(normalizedTransactions) if (transaction_hash !== transactionHash) { throw new ValidationError( diff --git a/packages/xrpl/test/models/LedgerTransactionExpanded.test.ts b/packages/xrpl/test/models/LedgerTransactionExpanded.test.ts new file mode 100644 index 0000000000..584d442e88 --- /dev/null +++ b/packages/xrpl/test/models/LedgerTransactionExpanded.test.ts @@ -0,0 +1,95 @@ +import { assert } from 'chai' + +import { + Ledger, + LedgerV1, + LedgerTransactionExpanded, + LedgerTransactionExpandedV1, +} from '../../src/models/ledger/Ledger' + +describe('LedgerTransactionExpanded types', function () { + it('Ledger (v2) transactions use flat format with metaData', function () { + const tx: LedgerTransactionExpanded = { + Account: 'rPrioTXJgZJF8bpdXq2X73PcVPfvYqjVKd', + Amount: '1000000', + Destination: 'rsRy14FvipgqudiGmptJBhr1RtpsgfzKMM', + TransactionType: 'Payment', + Fee: '11', + Sequence: 1, + Flags: 0, + SigningPubKey: '', + TxnSignature: '', + hash: '044314FE34236A262DA692789CE5B48CA1A3CEC078B1A4ECCD65F4B61A9EB0A7', + metaData: { + AffectedNodes: [], + TransactionIndex: 0, + TransactionResult: 'tesSUCCESS', + }, + } + + // Verify v2 expanded transactions are assignable to Ledger.transactions + const ledgerV2: Pick = { + transactions: [tx], + } + + assert.isArray(ledgerV2.transactions) + const firstTx = ledgerV2.transactions![0] + assert.notEqual(typeof firstTx, 'string') + if (typeof firstTx !== 'string') { + assert.strictEqual(firstTx.hash, tx.hash) + assert.isDefined(firstTx.metaData) + } + }) + + it('Ledger transactions can also be hash strings', function () { + const ledgerV2: Pick = { + transactions: [ + '044314FE34236A262DA692789CE5B48CA1A3CEC078B1A4ECCD65F4B61A9EB0A7', + ], + } + + assert.isArray(ledgerV2.transactions) + assert.strictEqual(typeof ledgerV2.transactions![0], 'string') + }) + + it('LedgerV1 transactions use wrapped format with tx_json and meta', function () { + const tx: LedgerTransactionExpandedV1 = { + tx_json: { + Account: 'rPrioTXJgZJF8bpdXq2X73PcVPfvYqjVKd', + Amount: '1000000', + Destination: 'rsRy14FvipgqudiGmptJBhr1RtpsgfzKMM', + TransactionType: 'Payment', + Fee: '11', + Sequence: 1, + Flags: 0, + SigningPubKey: '', + TxnSignature: '', + }, + meta: { + AffectedNodes: [], + TransactionIndex: 0, + TransactionResult: 'tesSUCCESS', + }, + hash: '044314FE34236A262DA692789CE5B48CA1A3CEC078B1A4ECCD65F4B61A9EB0A7', + validated: true, + ledger_index: 93637993, + close_time_iso: '2025-01-22T21:13:50Z', + ledger_hash: + '058FDA696458896EC515AC19C3EDC8CD0E163A3620CA6B314165E5BAED70846A', + } + + // Verify v1 expanded transactions are assignable to LedgerV1.transactions + const ledgerV1: Pick = { + transactions: [tx], + } + + assert.isArray(ledgerV1.transactions) + const firstTx = ledgerV1.transactions![0] + assert.notEqual(typeof firstTx, 'string') + if (typeof firstTx !== 'string') { + assert.strictEqual(firstTx.hash, tx.hash) + assert.isDefined(firstTx.tx_json) + assert.isDefined(firstTx.meta) + } + }) +})