From 414d6da5276f669d6ab8f32d32ac5798d15b1f39 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Tue, 21 Jul 2026 11:20:23 -0700 Subject: [PATCH 01/10] Add Sponsored fees & reserves section to account page Surfaces XLS-68 sponsorship data on the account detail page: whether another account is sponsoring this account's base reserve (AccountRoot.Sponsor) and/or its transaction fees (Sponsorship ledger object), matching the Figma spec for this feature. --- public/locales/en-US/translations.json | 6 ++ .../Accounts/SponsoredFeesReserves/index.tsx | 64 ++++++++++++++ .../SponsoredFeesReserves/styles.scss | 43 ++++++++++ .../test/SponsoredFeesReserves.test.tsx | 85 +++++++++++++++++++ src/containers/Accounts/index.tsx | 2 + src/rippled/accountState.ts | 11 +++ src/rippled/lib/rippled.ts | 48 +++++++++++ src/rippled/lib/test/rippled.test.ts | 78 +++++++++++++++++ src/rippled/lib/utils.ts | 3 + 9 files changed, 340 insertions(+) create mode 100644 src/containers/Accounts/SponsoredFeesReserves/index.tsx create mode 100644 src/containers/Accounts/SponsoredFeesReserves/styles.scss create mode 100644 src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index bf49cd4b1..0a60ce3a7 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -729,6 +729,12 @@ "account_page_payment_channels": "Payment Channels", "account_page_payment_channels_text": "{{currency}} available in {{number}} channel(s)", "account_page_nft_minter": "NFT Minter", + "account_page_sponsored_fees_reserves_title": "Sponsored fees & reserves", + "account_page_sponsored_scope": "Scope", + "account_page_sponsored_by": "Sponsored by", + "account_page_sponsored_scope_transaction_fees": "Transaction Fees", + "account_page_sponsored_scope_base_reserve": "Base Reserve", + "account_page_sponsored_status_active": "Active", "account_page_asset_held_title": "Assets Held", "account_page_asset_issued_title": "Assets Issued", "account_page_asset_tab_iou": "IOUs ({{count}})", diff --git a/src/containers/Accounts/SponsoredFeesReserves/index.tsx b/src/containers/Accounts/SponsoredFeesReserves/index.tsx new file mode 100644 index 000000000..3a3266014 --- /dev/null +++ b/src/containers/Accounts/SponsoredFeesReserves/index.tsx @@ -0,0 +1,64 @@ +import { useTranslation } from 'react-i18next' +import { Account } from '../../shared/components/Account' +import { shortenAccount } from '../../shared/utils' +import './styles.scss' + +interface Props { + account: any +} + +export const SponsoredFeesReserves = ({ account }: Props) => { + const { t } = useTranslation() + + const transactionFeesSponsor = account.sponsorship?.owner + const baseReserveSponsor = account.info?.sponsor + + if (!transactionFeesSponsor && !baseReserveSponsor) { + return null + } + + return ( +
+

+ {t('account_page_sponsored_fees_reserves_title')} +

+
+ + + + + + + + + + {transactionFeesSponsor && ( + + + + + + )} + {baseReserveSponsor && ( + + + + + + )} + +
{t('account_page_sponsored_scope')}{t('account_page_sponsored_by')}{t('status')}
{t('account_page_sponsored_scope_transaction_fees')} + + {t('account_page_sponsored_status_active')}
{t('account_page_sponsored_scope_base_reserve')} + + {t('account_page_sponsored_status_active')}
+
+
+ ) +} diff --git a/src/containers/Accounts/SponsoredFeesReserves/styles.scss b/src/containers/Accounts/SponsoredFeesReserves/styles.scss new file mode 100644 index 000000000..7f37b361e --- /dev/null +++ b/src/containers/Accounts/SponsoredFeesReserves/styles.scss @@ -0,0 +1,43 @@ +@use '../../shared/css/variables' as *; + +.sponsored-fees-reserves-section { + padding: 24px 0; + + .sponsored-fees-reserves-title { + @include bold; + + margin: 0 0 20px; + font-size: 20px; + } +} + +.sponsored-fees-reserves-table-wrapper { + -webkit-overflow-scrolling: touch; + overflow-x: auto; +} + +.sponsored-fees-reserves-table { + width: 100%; + border-collapse: collapse; + + thead th { + @include semibold; + + padding: 10px 12px; + color: $black-50; + font-size: 12px; + text-align: left; + text-transform: uppercase; + } + + tbody td { + padding: 10px 12px; + border-bottom: 1px solid $black-80; + color: $white; + font-size: 14px; + } + + tbody tr:first-child td { + border-top: 1px solid $black-80; + } +} diff --git a/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx b/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx new file mode 100644 index 000000000..914f95efe --- /dev/null +++ b/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx @@ -0,0 +1,85 @@ +import { render, screen } from '@testing-library/react' +import { I18nextProvider } from 'react-i18next' +import { BrowserRouter as Router } from 'react-router' +import i18n from '../../../../i18n/testConfigEnglish' +import { SponsoredFeesReserves } from '../index' + +jest.mock('../../../shared/components/Account', () => ({ + Account: ({ account }: { account: string }) => ( + {account} + ), +})) + +const TestWrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + +) + +describe('SponsoredFeesReserves Component', () => { + it('renders nothing when the account has no sponsorship', () => { + const account = { info: {} } + + const { container } = render( + + + , + ) + + expect(container).toBeEmptyDOMElement() + }) + + it('renders only the Base Reserve row when only the reserve is sponsored', () => { + const account = { info: { sponsor: 'rBaseReserveSponsor11111111111111' } } + + render( + + + , + ) + + expect(screen.getByText('Sponsored fees & reserves')).toBeInTheDocument() + expect(screen.getByText('Base Reserve')).toBeInTheDocument() + expect(screen.queryByText('Transaction Fees')).not.toBeInTheDocument() + expect(screen.getByTestId('account-component')).toHaveTextContent( + 'rBaseReserveSponsor11111111111111', + ) + }) + + it('renders only the Transaction Fees row when only fees are sponsored', () => { + const account = { + info: {}, + sponsorship: { owner: 'rFeeSponsor2222222222222222222222' }, + } + + render( + + + , + ) + + expect(screen.getByText('Transaction Fees')).toBeInTheDocument() + expect(screen.queryByText('Base Reserve')).not.toBeInTheDocument() + expect(screen.getByTestId('account-component')).toHaveTextContent( + 'rFeeSponsor2222222222222222222222', + ) + }) + + it('renders both rows when both fees and reserve are sponsored', () => { + const account = { + info: { sponsor: 'rBaseReserveSponsor11111111111111' }, + sponsorship: { owner: 'rFeeSponsor2222222222222222222222' }, + } + + render( + + + , + ) + + expect(screen.getByText('Transaction Fees')).toBeInTheDocument() + expect(screen.getByText('Base Reserve')).toBeInTheDocument() + expect(screen.getAllByTestId('account-component')).toHaveLength(2) + expect(screen.getAllByText('Active')).toHaveLength(2) + }) +}) diff --git a/src/containers/Accounts/index.tsx b/src/containers/Accounts/index.tsx index b2c65fe78..a143ce474 100644 --- a/src/containers/Accounts/index.tsx +++ b/src/containers/Accounts/index.tsx @@ -15,6 +15,7 @@ import { AccountSummary } from './AccountSummary' import { useXRPToUSDRate } from '../shared/hooks/useXRPToUSDRate' import AccountAsset from './AccountAsset' import AccountHeader from './AccountHeader' +import { SponsoredFeesReserves } from './SponsoredFeesReserves' export const Accounts = () => { const { trackScreenLoaded, trackException } = useAnalytics() @@ -65,6 +66,7 @@ export const Accounts = () => { {showAccount && ( <> + ({ account: info.Account as string, info: formatAccountInfo(info, data[1].info.validated_ledger), @@ -97,6 +107,7 @@ async function getAccountState( ? formatSignerList(info.signer_lists[0]) : undefined, paychannels: data[1], + sponsorship: data[2], xAddress: decomposedAddress || undefined, deleted: false, })), diff --git a/src/rippled/lib/rippled.ts b/src/rippled/lib/rippled.ts index 487b6f380..12e2aa415 100644 --- a/src/rippled/lib/rippled.ts +++ b/src/rippled/lib/rippled.ts @@ -27,6 +27,14 @@ const formatPaychannel = (d: any) => ({ settleDelay: d.SettleDelay, }) +const formatSponsorship = (d: any) => ({ + owner: d.Owner, + sponsee: d.Sponsee, + feeAmount: d.FeeAmount, + maxFee: d.MaxFee, + reserveCount: d.ReserveCount, +}) + const executeQuery = async ( rippledSocket: XrplClient, params: any, @@ -366,6 +374,45 @@ const getAccountBridges = async ( return undefined } +// get the sponsorship covering this account's fees/reserves, if any +const getAccountSponsorship = async ( + rippledSocket: ExplorerXrplClient, + account: string, + ledgerIndex: string | number = 'validated', +): Promise => { + const resp = await query(rippledSocket, { + command: 'account_objects', + account, + ledger_index: ledgerIndex, + type: 'sponsorship', + limit: 400, + }) + if (resp.error === 'actNotFound') { + throw new Error('account not found', 404) + } + if (resp.error === 'invalidParams') { + // thrown when the Sponsorship amendment is not activated + // TODO: remove this when XLS-68 is live in mainnet + return undefined + } + + if (resp.error_message) { + throw new Error(resp.error_message, 500) + } + + if (!resp.account_objects.length) { + return undefined + } + + // A Sponsorship object is linked into both the sponsor's and sponsee's + // owner directories, so only keep the one where this account is sponsored. + const sponsorship = resp.account_objects.find( + (d: any) => d.Sponsee === account, + ) + + return sponsorship ? formatSponsorship(sponsorship) : undefined +} + // get Token balance summary const getBalances = async ( rippledSocket: ExplorerXrplClient, @@ -929,6 +976,7 @@ export { getAccountEscrows, getAccountPaychannels, getAccountBridges, + getAccountSponsorship, getAccountNFTs, getAccountObjects, getNFTsIssuedByAccount, diff --git a/src/rippled/lib/test/rippled.test.ts b/src/rippled/lib/test/rippled.test.ts index e12d5303f..2d680b424 100644 --- a/src/rippled/lib/test/rippled.test.ts +++ b/src/rippled/lib/test/rippled.test.ts @@ -3,6 +3,7 @@ import { getLoanBroker, getMPTIssuance, getNegativeUNL, + getAccountSponsorship, } from '../rippled' const VAULT_INDEX = @@ -226,3 +227,80 @@ describe('getNegativeUNL', () => { await expect(getNegativeUNL(socket)).resolves.toEqual([]) }) }) + +describe('getAccountSponsorship', () => { + const ACCOUNT = 'rSponsee11111111111111111111111111' + const SPONSOR = 'rSponsor2222222222222222222222222' + + it('returns the formatted sponsorship when this account is the sponsee', async () => { + const socket = makeSocket({ + account_objects: [ + { + LedgerEntryType: 'Sponsorship', + Owner: SPONSOR, + Sponsee: ACCOUNT, + FeeAmount: '1000', + MaxFee: '5000', + ReserveCount: 2, + }, + ], + }) + + await expect(getAccountSponsorship(socket, ACCOUNT)).resolves.toEqual({ + owner: SPONSOR, + sponsee: ACCOUNT, + feeAmount: '1000', + maxFee: '5000', + reserveCount: 2, + }) + expect(socket.send).toHaveBeenCalledWith({ + command: 'account_objects', + account: ACCOUNT, + ledger_index: 'validated', + type: 'sponsorship', + limit: 400, + }) + }) + + it('ignores Sponsorship objects where this account is the sponsor, not the sponsee', async () => { + const socket = makeSocket({ + account_objects: [ + { + LedgerEntryType: 'Sponsorship', + Owner: ACCOUNT, + Sponsee: 'rSomeoneElse33333333333333333333', + FeeAmount: '1000', + }, + ], + }) + + await expect( + getAccountSponsorship(socket, ACCOUNT), + ).resolves.toBeUndefined() + }) + + it('returns undefined when the account has no sponsorship objects', async () => { + const socket = makeSocket({ account_objects: [] }) + + await expect( + getAccountSponsorship(socket, ACCOUNT), + ).resolves.toBeUndefined() + }) + + it('returns undefined when the Sponsorship amendment is not enabled', async () => { + const socket = makeSocket({ error: 'invalidParams' }) + + await expect( + getAccountSponsorship(socket, ACCOUNT), + ).resolves.toBeUndefined() + }) + + it('throws when the account is not found', async () => { + const socket = makeSocket({ error: 'actNotFound' }) + + await expect(getAccountSponsorship(socket, ACCOUNT)).rejects.toMatchObject({ + message: 'account not found', + code: 404, + }) + }) +}) diff --git a/src/rippled/lib/utils.ts b/src/rippled/lib/utils.ts index 823870688..e37e25220 100644 --- a/src/rippled/lib/utils.ts +++ b/src/rippled/lib/utils.ts @@ -125,6 +125,7 @@ interface AccountInfo { PreviousTxnID: string PreviousTxnLgrSeq: number NFTokenMinter?: string + Sponsor?: string } interface ServerInfoValidated { @@ -147,6 +148,7 @@ interface FormattedAccountInfo { previousTxn: string previousLedger: number nftMinter?: string + sponsor?: string } const formatAccountInfo = ( @@ -171,6 +173,7 @@ const formatAccountInfo = ( previousTxn: info.PreviousTxnID, previousLedger: info.PreviousTxnLgrSeq, nftMinter: info.NFTokenMinter, + sponsor: info.Sponsor, }) const formatTransaction = (tx: any): any => { From 60878e1a4805cfaaaa9fa0b6f35da11f37488ec8 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Tue, 21 Jul 2026 14:33:42 -0700 Subject: [PATCH 02/10] Simplify SponsoredFeesReserves and match sibling conventions Build table rows from an array instead of duplicated JSX, use the existing AccountState type instead of any, and move the show-if-sponsored guard out to the call site to match how SignersCard/nftMinter/paychannels are conditionally rendered elsewhere on the account page. --- .../Accounts/SponsoredFeesReserves/index.tsx | 51 ++++++++++--------- .../test/SponsoredFeesReserves.test.tsx | 50 +++++++++++++----- src/containers/Accounts/index.tsx | 4 +- 3 files changed, 65 insertions(+), 40 deletions(-) diff --git a/src/containers/Accounts/SponsoredFeesReserves/index.tsx b/src/containers/Accounts/SponsoredFeesReserves/index.tsx index 3a3266014..147c77e35 100644 --- a/src/containers/Accounts/SponsoredFeesReserves/index.tsx +++ b/src/containers/Accounts/SponsoredFeesReserves/index.tsx @@ -1,21 +1,34 @@ import { useTranslation } from 'react-i18next' import { Account } from '../../shared/components/Account' import { shortenAccount } from '../../shared/utils' +import type { AccountState } from '../../../rippled/accountState' import './styles.scss' interface Props { - account: any + account: AccountState } +type ScopeKey = + | 'account_page_sponsored_scope_transaction_fees' + | 'account_page_sponsored_scope_base_reserve' + export const SponsoredFeesReserves = ({ account }: Props) => { const { t } = useTranslation() - const transactionFeesSponsor = account.sponsorship?.owner - const baseReserveSponsor = account.info?.sponsor - - if (!transactionFeesSponsor && !baseReserveSponsor) { - return null - } + const rows: { scopeKey: ScopeKey; sponsor: string }[] = ( + [ + { + scopeKey: 'account_page_sponsored_scope_transaction_fees', + sponsor: account.sponsorship?.owner, + }, + { + scopeKey: 'account_page_sponsored_scope_base_reserve', + sponsor: account.info?.sponsor, + }, + ] as { scopeKey: ScopeKey; sponsor: string | undefined }[] + ).filter((row): row is { scopeKey: ScopeKey; sponsor: string } => + Boolean(row.sponsor), + ) return (
@@ -32,30 +45,18 @@ export const SponsoredFeesReserves = ({ account }: Props) => { - {transactionFeesSponsor && ( - - {t('account_page_sponsored_scope_transaction_fees')} - - - - {t('account_page_sponsored_status_active')} - - )} - {baseReserveSponsor && ( - - {t('account_page_sponsored_scope_base_reserve')} + {rows.map(({ scopeKey, sponsor }) => ( + + {t(scopeKey)} {t('account_page_sponsored_status_active')} - )} + ))}
diff --git a/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx b/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx index 914f95efe..efbfe4358 100644 --- a/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx +++ b/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx @@ -3,6 +3,7 @@ import { I18nextProvider } from 'react-i18next' import { BrowserRouter as Router } from 'react-router' import i18n from '../../../../i18n/testConfigEnglish' import { SponsoredFeesReserves } from '../index' +import type { AccountState } from '../../../../rippled/accountState' jest.mock('../../../shared/components/Account', () => ({ Account: ({ account }: { account: string }) => ( @@ -16,21 +17,33 @@ const TestWrapper = ({ children }: { children: React.ReactNode }) => ( ) -describe('SponsoredFeesReserves Component', () => { - it('renders nothing when the account has no sponsorship', () => { - const account = { info: {} } +const baseAccount: AccountState = { + account: 'rAccount1111111111111111111111111', + info: { ticketCount: 0, flags: [] }, + deleted: false, +} - const { container } = render( +describe('SponsoredFeesReserves Component', () => { + it('renders no rows when the account has no sponsorship', () => { + render( - + , ) - expect(container).toBeEmptyDOMElement() + expect(screen.getByText('Sponsored fees & reserves')).toBeInTheDocument() + expect(screen.queryByText('Transaction Fees')).not.toBeInTheDocument() + expect(screen.queryByText('Base Reserve')).not.toBeInTheDocument() }) it('renders only the Base Reserve row when only the reserve is sponsored', () => { - const account = { info: { sponsor: 'rBaseReserveSponsor11111111111111' } } + const account: AccountState = { + ...baseAccount, + info: { + ...baseAccount.info, + sponsor: 'rBaseReserveSponsor11111111111111', + }, + } render( @@ -38,7 +51,6 @@ describe('SponsoredFeesReserves Component', () => { , ) - expect(screen.getByText('Sponsored fees & reserves')).toBeInTheDocument() expect(screen.getByText('Base Reserve')).toBeInTheDocument() expect(screen.queryByText('Transaction Fees')).not.toBeInTheDocument() expect(screen.getByTestId('account-component')).toHaveTextContent( @@ -47,9 +59,12 @@ describe('SponsoredFeesReserves Component', () => { }) it('renders only the Transaction Fees row when only fees are sponsored', () => { - const account = { - info: {}, - sponsorship: { owner: 'rFeeSponsor2222222222222222222222' }, + const account: AccountState = { + ...baseAccount, + sponsorship: { + owner: 'rFeeSponsor2222222222222222222222', + sponsee: baseAccount.account, + }, } render( @@ -66,9 +81,16 @@ describe('SponsoredFeesReserves Component', () => { }) it('renders both rows when both fees and reserve are sponsored', () => { - const account = { - info: { sponsor: 'rBaseReserveSponsor11111111111111' }, - sponsorship: { owner: 'rFeeSponsor2222222222222222222222' }, + const account: AccountState = { + ...baseAccount, + info: { + ...baseAccount.info, + sponsor: 'rBaseReserveSponsor11111111111111', + }, + sponsorship: { + owner: 'rFeeSponsor2222222222222222222222', + sponsee: baseAccount.account, + }, } render( diff --git a/src/containers/Accounts/index.tsx b/src/containers/Accounts/index.tsx index a143ce474..8ee6ee285 100644 --- a/src/containers/Accounts/index.tsx +++ b/src/containers/Accounts/index.tsx @@ -66,7 +66,9 @@ export const Accounts = () => { {showAccount && ( <> - + {(account.sponsorship?.owner || account.info?.sponsor) && ( + + )} Date: Fri, 14 Aug 2026 13:49:23 -0400 Subject: [PATCH 03/10] Support multiple simultaneous fee sponsors on the account page An account can have more than one active Sponsorship object; getAccountSponsorship() previously used .find() and silently dropped all but the first, so the account page only ever showed one fee sponsor. --- .../Accounts/SponsoredFeesReserves/index.tsx | 30 +++++------ .../test/SponsoredFeesReserves.test.tsx | 51 ++++++++++++++++--- src/containers/Accounts/index.tsx | 2 +- src/rippled/accountState.ts | 2 +- src/rippled/lib/rippled.ts | 6 +-- src/rippled/lib/test/rippled.test.ts | 41 ++++++++++++--- 6 files changed, 97 insertions(+), 35 deletions(-) diff --git a/src/containers/Accounts/SponsoredFeesReserves/index.tsx b/src/containers/Accounts/SponsoredFeesReserves/index.tsx index 147c77e35..0d4a2a4e9 100644 --- a/src/containers/Accounts/SponsoredFeesReserves/index.tsx +++ b/src/containers/Accounts/SponsoredFeesReserves/index.tsx @@ -15,20 +15,20 @@ type ScopeKey = export const SponsoredFeesReserves = ({ account }: Props) => { const { t } = useTranslation() - const rows: { scopeKey: ScopeKey; sponsor: string }[] = ( - [ - { - scopeKey: 'account_page_sponsored_scope_transaction_fees', - sponsor: account.sponsorship?.owner, - }, - { - scopeKey: 'account_page_sponsored_scope_base_reserve', - sponsor: account.info?.sponsor, - }, - ] as { scopeKey: ScopeKey; sponsor: string | undefined }[] - ).filter((row): row is { scopeKey: ScopeKey; sponsor: string } => - Boolean(row.sponsor), - ) + const rows: { scopeKey: ScopeKey; sponsor: string }[] = [ + ...(account.sponsorship ?? []).map(({ owner }) => ({ + scopeKey: 'account_page_sponsored_scope_transaction_fees' as ScopeKey, + sponsor: owner, + })), + ...(account.info?.sponsor + ? [ + { + scopeKey: 'account_page_sponsored_scope_base_reserve' as ScopeKey, + sponsor: account.info.sponsor, + }, + ] + : []), + ] return (
@@ -46,7 +46,7 @@ export const SponsoredFeesReserves = ({ account }: Props) => { {rows.map(({ scopeKey, sponsor }) => ( - + {t(scopeKey)} { it('renders only the Transaction Fees row when only fees are sponsored', () => { const account: AccountState = { ...baseAccount, - sponsorship: { - owner: 'rFeeSponsor2222222222222222222222', - sponsee: baseAccount.account, - }, + sponsorship: [ + { + owner: 'rFeeSponsor2222222222222222222222', + sponsee: baseAccount.account, + }, + ], } render( @@ -87,10 +89,12 @@ describe('SponsoredFeesReserves Component', () => { ...baseAccount.info, sponsor: 'rBaseReserveSponsor11111111111111', }, - sponsorship: { - owner: 'rFeeSponsor2222222222222222222222', - sponsee: baseAccount.account, - }, + sponsorship: [ + { + owner: 'rFeeSponsor2222222222222222222222', + sponsee: baseAccount.account, + }, + ], } render( @@ -104,4 +108,35 @@ describe('SponsoredFeesReserves Component', () => { expect(screen.getAllByTestId('account-component')).toHaveLength(2) expect(screen.getAllByText('Active')).toHaveLength(2) }) + + it('renders one Transaction Fees row per sponsor when there are multiple fee sponsors', () => { + const account: AccountState = { + ...baseAccount, + sponsorship: [ + { + owner: 'rFeeSponsor2222222222222222222222', + sponsee: baseAccount.account, + }, + { + owner: 'rFeeSponsor3333333333333333333333', + sponsee: baseAccount.account, + }, + ], + } + + render( + + + , + ) + + expect(screen.getAllByText('Transaction Fees')).toHaveLength(2) + expect(screen.getAllByTestId('account-component')).toHaveLength(2) + expect(screen.getAllByTestId('account-component')[0]).toHaveTextContent( + 'rFeeSponsor2222222222222222222222', + ) + expect(screen.getAllByTestId('account-component')[1]).toHaveTextContent( + 'rFeeSponsor3333333333333333333333', + ) + }) }) diff --git a/src/containers/Accounts/index.tsx b/src/containers/Accounts/index.tsx index c56e9d988..10904fe03 100644 --- a/src/containers/Accounts/index.tsx +++ b/src/containers/Accounts/index.tsx @@ -67,7 +67,7 @@ export const Accounts = () => { {showAccount && ( <> - {(account.sponsorship?.owner || account.info?.sponsor) && ( + {(account.sponsorship?.length || account.info?.sponsor) && ( )} diff --git a/src/rippled/accountState.ts b/src/rippled/accountState.ts index 0cd5474df..9242d98d3 100644 --- a/src/rippled/accountState.ts +++ b/src/rippled/accountState.ts @@ -41,7 +41,7 @@ export interface AccountState { feeAmount?: string maxFee?: string reserveCount?: number - } + }[] info: { accountTransactionID?: string reserve?: number diff --git a/src/rippled/lib/rippled.ts b/src/rippled/lib/rippled.ts index 12e2aa415..144dc4b41 100644 --- a/src/rippled/lib/rippled.ts +++ b/src/rippled/lib/rippled.ts @@ -405,12 +405,12 @@ const getAccountSponsorship = async ( } // A Sponsorship object is linked into both the sponsor's and sponsee's - // owner directories, so only keep the one where this account is sponsored. - const sponsorship = resp.account_objects.find( + // owner directories, so only keep the ones where this account is sponsored. + const sponsorships = resp.account_objects.filter( (d: any) => d.Sponsee === account, ) - return sponsorship ? formatSponsorship(sponsorship) : undefined + return sponsorships.length ? sponsorships.map(formatSponsorship) : undefined } // get Token balance summary diff --git a/src/rippled/lib/test/rippled.test.ts b/src/rippled/lib/test/rippled.test.ts index 2d680b424..5e0a6f5f3 100644 --- a/src/rippled/lib/test/rippled.test.ts +++ b/src/rippled/lib/test/rippled.test.ts @@ -246,13 +246,15 @@ describe('getAccountSponsorship', () => { ], }) - await expect(getAccountSponsorship(socket, ACCOUNT)).resolves.toEqual({ - owner: SPONSOR, - sponsee: ACCOUNT, - feeAmount: '1000', - maxFee: '5000', - reserveCount: 2, - }) + await expect(getAccountSponsorship(socket, ACCOUNT)).resolves.toEqual([ + { + owner: SPONSOR, + sponsee: ACCOUNT, + feeAmount: '1000', + maxFee: '5000', + reserveCount: 2, + }, + ]) expect(socket.send).toHaveBeenCalledWith({ command: 'account_objects', account: ACCOUNT, @@ -262,6 +264,31 @@ describe('getAccountSponsorship', () => { }) }) + it('returns all sponsorships when this account has multiple sponsors', async () => { + const SPONSOR_2 = 'rSponsor3333333333333333333333333' + const socket = makeSocket({ + account_objects: [ + { + LedgerEntryType: 'Sponsorship', + Owner: SPONSOR, + Sponsee: ACCOUNT, + FeeAmount: '1000', + }, + { + LedgerEntryType: 'Sponsorship', + Owner: SPONSOR_2, + Sponsee: ACCOUNT, + FeeAmount: '2000', + }, + ], + }) + + await expect(getAccountSponsorship(socket, ACCOUNT)).resolves.toEqual([ + { owner: SPONSOR, sponsee: ACCOUNT, feeAmount: '1000' }, + { owner: SPONSOR_2, sponsee: ACCOUNT, feeAmount: '2000' }, + ]) + }) + it('ignores Sponsorship objects where this account is the sponsor, not the sponsee', async () => { const socket = makeSocket({ account_objects: [ From cb5a90c3b20844420f2272a97dbc316fe0c99c00 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Wed, 19 Aug 2026 10:45:43 -0400 Subject: [PATCH 04/10] Show full sponsor account address on the account page The Sponsored fees & reserves table truncated sponsor addresses via shortenAccount, but full addresses are more useful for verifying who is sponsoring an account. --- src/containers/Accounts/SponsoredFeesReserves/index.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/containers/Accounts/SponsoredFeesReserves/index.tsx b/src/containers/Accounts/SponsoredFeesReserves/index.tsx index 0d4a2a4e9..ab50a4e77 100644 --- a/src/containers/Accounts/SponsoredFeesReserves/index.tsx +++ b/src/containers/Accounts/SponsoredFeesReserves/index.tsx @@ -1,6 +1,5 @@ import { useTranslation } from 'react-i18next' import { Account } from '../../shared/components/Account' -import { shortenAccount } from '../../shared/utils' import type { AccountState } from '../../../rippled/accountState' import './styles.scss' @@ -49,10 +48,7 @@ export const SponsoredFeesReserves = ({ account }: Props) => { {t(scopeKey)} - + {t('account_page_sponsored_status_active')} From 6766a5293caa03e98e178645e3f51c55f0a4fd37 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Thu, 20 Aug 2026 10:40:21 -0400 Subject: [PATCH 05/10] Add SponsorshipSet/SponsorshipTransfer transaction views and generic Sponsor field display Adds transaction detail rendering (Description/Simple/TableDetail) for the SponsorshipSet and SponsorshipTransfer transaction types, following the existing per-transaction-type component pattern, plus generic support for the transaction-common Sponsor/SponsorFlags/SponsorSignature fields so any co-sponsored transaction shows who is sponsoring its fee and/or reserve. --- public/locales/en-US/translations.json | 24 ++++++- .../Transactions/DetailTab/index.tsx | 29 ++++++++ src/containers/Transactions/SimpleTab.tsx | 23 ++++++- .../components/Transaction/DefaultSimple.tsx | 3 + .../SponsorshipSet/Description.tsx | 23 +++++++ .../Transaction/SponsorshipSet/Simple.tsx | 68 +++++++++++++++++++ .../SponsorshipSet/TableDetail.tsx | 49 +++++++++++++ .../Transaction/SponsorshipSet/index.ts | 18 +++++ .../Transaction/SponsorshipSet/parser.ts | 34 ++++++++++ .../test/SponsorshipSetDescription.test.tsx | 25 +++++++ .../test/SponsorshipSetSimple.test.tsx | 38 +++++++++++ .../test/SponsorshipSetTableDetail.test.tsx | 44 ++++++++++++ .../test/mock_data/SponsorshipSet.json | 23 +++++++ .../test/mock_data/SponsorshipSetDelete.json | 20 ++++++ .../Transaction/SponsorshipSet/types.ts | 9 +++ .../SponsorshipTransfer/Description.tsx | 56 +++++++++++++++ .../SponsorshipTransfer/Simple.tsx | 42 ++++++++++++ .../SponsorshipTransfer/TableDetail.tsx | 48 +++++++++++++ .../Transaction/SponsorshipTransfer/index.ts | 18 +++++ .../Transaction/SponsorshipTransfer/parser.ts | 24 +++++++ .../SponsorshipTransferDescription.test.tsx | 34 ++++++++++ .../test/SponsorshipTransferSimple.test.tsx | 57 ++++++++++++++++ .../SponsorshipTransferTableDetail.test.tsx | 33 +++++++++ .../mock_data/SponsorshipTransferCreate.json | 21 ++++++ .../mock_data/SponsorshipTransferEnd.json | 20 ++++++ .../SponsorshipTransferReassign.json | 22 ++++++ .../Transaction/SponsorshipTransfer/types.ts | 8 +++ .../shared/components/Transaction/index.ts | 4 ++ src/containers/shared/css/simpleTab.scss | 9 +++ src/containers/shared/transactionUtils.ts | 33 +++++++++ 30 files changed, 857 insertions(+), 2 deletions(-) create mode 100644 src/containers/shared/components/Transaction/SponsorshipSet/Description.tsx create mode 100644 src/containers/shared/components/Transaction/SponsorshipSet/Simple.tsx create mode 100644 src/containers/shared/components/Transaction/SponsorshipSet/TableDetail.tsx create mode 100644 src/containers/shared/components/Transaction/SponsorshipSet/index.ts create mode 100644 src/containers/shared/components/Transaction/SponsorshipSet/parser.ts create mode 100644 src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetDescription.test.tsx create mode 100644 src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetSimple.test.tsx create mode 100644 src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetTableDetail.test.tsx create mode 100644 src/containers/shared/components/Transaction/SponsorshipSet/test/mock_data/SponsorshipSet.json create mode 100644 src/containers/shared/components/Transaction/SponsorshipSet/test/mock_data/SponsorshipSetDelete.json create mode 100644 src/containers/shared/components/Transaction/SponsorshipSet/types.ts create mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/Description.tsx create mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/Simple.tsx create mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/TableDetail.tsx create mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/index.ts create mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts create mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/test/SponsorshipTransferDescription.test.tsx create mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/test/SponsorshipTransferSimple.test.tsx create mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/test/SponsorshipTransferTableDetail.test.tsx create mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferCreate.json create mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferEnd.json create mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferReassign.json create mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/types.ts diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index 8cedb2b1a..91f490653 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -952,5 +952,27 @@ "registered": "Registered", "included": "Included", "maintenance_banner.notice": "Scheduled maintenance: {{window}} (~{{duration}} min downtime).", - "maintenance_banner.countdown_prefix": "Starts in" + "maintenance_banner.countdown_prefix": "Starts in", + "sponsor": "Sponsor", + "sponsee": "Sponsee", + "new_sponsor": "New Sponsor", + "fee_amount": "Fee Amount", + "max_fee": "Max Fee", + "reserve_count": "Reserve Count", + "require_sign_for_fee": "Require Sign For Fee", + "require_sign_for_reserve": "Require Sign For Reserve", + "sponsorship_deleted": "Sponsorship Deleted", + "operation": "Operation", + "object_id": "Object ID", + "sponsorship_operation_create": "Create", + "sponsorship_operation_reassign": "Reassign", + "sponsorship_operation_end": "End", + "sponsorship_set_description": " sponsors transaction fees for ", + "sponsorship_set_delete": " ends the fee sponsorship for ", + "sponsorship_transfer_create": " assigns as its reserve sponsor", + "sponsorship_transfer_reassign": " reassigns reserve sponsorship to ", + "sponsorship_transfer_end_other": " ends reserve sponsorship for ", + "sponsorship_transfer_end_self": " ends its reserve sponsorship", + "sponsor_detail": " is sponsoring this transaction", + "sponsor_co_signed": "The sponsor co-signed this transaction" } diff --git a/src/containers/Transactions/DetailTab/index.tsx b/src/containers/Transactions/DetailTab/index.tsx index 030d117e3..afbb0ad62 100644 --- a/src/containers/Transactions/DetailTab/index.tsx +++ b/src/containers/Transactions/DetailTab/index.tsx @@ -11,6 +11,8 @@ import { XRP_BASE, buildFlags, buildMemos, + getSponsorScopes, + SPONSOR_SCOPE_LABEL_KEYS, } from '../../shared/transactionUtils' import './detailTab.scss' import { useLanguage } from '../../shared/hooks' @@ -109,6 +111,32 @@ export const DetailTab: FC<{ data: any }> = ({ data }) => { ) : null } + const renderSponsor = () => { + if (!data.tx.Sponsor) return null + const scopes = getSponsorScopes(data.tx.SponsorFlags) + return ( +
+
{t('sponsor')}
+
+ }} + /> +
+ {scopes.length > 0 && ( +
+ {scopes + .map((scope) => t(SPONSOR_SCOPE_LABEL_KEYS[scope])) + .join(', ')} +
+ )} + {data.tx.SponsorSignature && ( +
{t('sponsor_co_signed')}
+ )} +
+ ) + } + const renderSigners = () => data.tx.Signers ? (
@@ -128,6 +156,7 @@ export const DetailTab: FC<{ data: any }> = ({ data }) => { {renderStatus()} {renderSigners()} + {renderSponsor()} {renderFlags()} {renderFee()} diff --git a/src/containers/Transactions/SimpleTab.tsx b/src/containers/Transactions/SimpleTab.tsx index b0d7fe6de..767be1732 100644 --- a/src/containers/Transactions/SimpleTab.tsx +++ b/src/containers/Transactions/SimpleTab.tsx @@ -7,7 +7,12 @@ import { Simple } from './Simple' import { useLanguage } from '../shared/hooks' import { RouteLink } from '../shared/routing' -import { CURRENCY_OPTIONS, XRP_BASE } from '../shared/transactionUtils' +import { + CURRENCY_OPTIONS, + XRP_BASE, + getSponsorScopes, + SPONSOR_SCOPE_LABEL_KEYS, +} from '../shared/transactionUtils' import { SimpleRow } from '../shared/components/Transaction/SimpleRow' import '../shared/css/simpleTab.scss' import './simpleTab.scss' @@ -41,6 +46,8 @@ export const SimpleTab: FC<{ data: any; width: number }> = ({ sequence, ticketSequence, isHook, + sponsor, + sponsorFlags, ) => ( <> = ({ )} + {sponsor && ( + + + {getSponsorScopes(sponsorFlags).length > 0 && ( +
+ {getSponsorScopes(sponsorFlags) + .map((scope) => t(SPONSOR_SCOPE_LABEL_KEYS[scope])) + .join(', ')} +
+ )} +
+ )} = ({ processed.tx.Sequence, processed.tx.TicketSequence, !!processed.tx.EmitDetails, + processed.tx.Sponsor, + processed.tx.SponsorFlags, ) return ( diff --git a/src/containers/shared/components/Transaction/DefaultSimple.tsx b/src/containers/shared/components/Transaction/DefaultSimple.tsx index 481616688..070fc07ca 100644 --- a/src/containers/shared/components/Transaction/DefaultSimple.tsx +++ b/src/containers/shared/components/Transaction/DefaultSimple.tsx @@ -20,6 +20,9 @@ const DEFAULT_TX_ELEMENTS = [ 'NetworkID', 'Sequence', 'SigningPubKey', + 'Sponsor', + 'SponsorFlags', + 'SponsorSignature', 'TransactionType', 'TxnSignature', 'ctid', diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/Description.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/Description.tsx new file mode 100644 index 000000000..2125e1c11 --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipSet/Description.tsx @@ -0,0 +1,23 @@ +import { Trans } from 'react-i18next' +import { TransactionDescriptionProps } from '../types' +import { Account } from '../../Account' +import { SponsorshipSet } from './types' +import { parser } from './parser' + +export const Description = ({ + data, +}: TransactionDescriptionProps) => { + const { sponsor, sponsee, isDelete } = parser(data.tx) + + return ( + , + Sponsee: , + }} + /> + ) +} diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/Simple.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/Simple.tsx new file mode 100644 index 000000000..20a67dcba --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipSet/Simple.tsx @@ -0,0 +1,68 @@ +import { useTranslation } from 'react-i18next' +import { TransactionSimpleComponent, TransactionSimpleProps } from '../types' +import { SimpleRow } from '../SimpleRow' +import { Account } from '../../Account' +import { Amount } from '../../Amount' + +export const Simple: TransactionSimpleComponent = ({ + data, +}: TransactionSimpleProps) => { + const { t } = useTranslation() + const { + sponsor, + sponsee, + isDelete, + feeAmount, + maxFee, + reserveCount, + requireSignForFee, + requireSignForReserve, + } = data.instructions + + return ( + <> + + + + + + + {isDelete && ( + + {t('sponsorship_deleted')} + + )} + {!isDelete && feeAmount && ( + + + + )} + {!isDelete && maxFee && ( + + + + )} + {!isDelete && reserveCount !== undefined && ( + + {reserveCount} + + )} + {!isDelete && requireSignForFee && ( + + {t('yes')} + + )} + {!isDelete && requireSignForReserve && ( + + {t('yes')} + + )} + + ) +} diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/TableDetail.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/TableDetail.tsx new file mode 100644 index 000000000..5609e6b15 --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipSet/TableDetail.tsx @@ -0,0 +1,49 @@ +import { useTranslation } from 'react-i18next' +import { TransactionTableDetailProps } from '../types' +import { Account } from '../../Account' +import { Amount } from '../../Amount' + +export const TableDetail = ({ instructions }: TransactionTableDetailProps) => { + const { t } = useTranslation() + const { sponsor, sponsee, isDelete, feeAmount, maxFee, reserveCount } = + instructions + + return ( +
+
+ {t('sponsor')} + +
+
+ {t('sponsee')} + +
+ {isDelete && ( +
+ {t('status')} + + {t('sponsorship_deleted')} + +
+ )} + {!isDelete && feeAmount && ( +
+ {t('fee_amount')} + +
+ )} + {!isDelete && maxFee && ( +
+ {t('max_fee')} + +
+ )} + {!isDelete && reserveCount !== undefined && ( +
+ {t('reserve_count')} + {reserveCount} +
+ )} +
+ ) +} diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/index.ts b/src/containers/shared/components/Transaction/SponsorshipSet/index.ts new file mode 100644 index 000000000..ffe94a69a --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipSet/index.ts @@ -0,0 +1,18 @@ +import { + TransactionAction, + TransactionCategory, + TransactionMapping, +} from '../types' +import { Simple } from './Simple' +import { Description } from './Description' +import { TableDetail } from './TableDetail' +import { parser } from './parser' + +export const SponsorshipSetTransaction: TransactionMapping = { + Description, + Simple, + TableDetail, + parser, + action: TransactionAction.MODIFY, + category: TransactionCategory.ACCOUNT, +} diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts b/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts new file mode 100644 index 000000000..30bcf1b47 --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts @@ -0,0 +1,34 @@ +import { formatAmount } from '../../../../../rippled/lib/txSummary/formatAmount' +import { SponsorshipSet } from './types' + +const TF_DELETE_OBJECT = 0x00100000 +const TF_SET_REQUIRE_SIGN_FOR_FEE = 0x00010000 +const TF_CLEAR_REQUIRE_SIGN_FOR_FEE = 0x00020000 +const TF_SET_REQUIRE_SIGN_FOR_RESERVE = 0x00040000 +const TF_CLEAR_REQUIRE_SIGN_FOR_RESERVE = 0x00080000 + +export function parser(tx: SponsorshipSet) { + const flags = tx.Flags || 0 + // If CounterpartySponsor is given, this account is the sponsee; if Sponsee + // is given, this account is the sponsor. + const sponsor = tx.CounterpartySponsor ?? tx.Account + const sponsee = tx.Sponsee ?? tx.Account + + return { + sponsor, + sponsee, + isDelete: Boolean(flags & TF_DELETE_OBJECT), + feeAmount: + tx.FeeAmount !== undefined ? formatAmount(tx.FeeAmount) : undefined, + maxFee: tx.MaxFee !== undefined ? formatAmount(tx.MaxFee) : undefined, + reserveCount: tx.ReserveCount, + requireSignForFee: + Boolean(flags & TF_SET_REQUIRE_SIGN_FOR_FEE) || undefined, + clearRequireSignForFee: + Boolean(flags & TF_CLEAR_REQUIRE_SIGN_FOR_FEE) || undefined, + requireSignForReserve: + Boolean(flags & TF_SET_REQUIRE_SIGN_FOR_RESERVE) || undefined, + clearRequireSignForReserve: + Boolean(flags & TF_CLEAR_REQUIRE_SIGN_FOR_RESERVE) || undefined, + } +} diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetDescription.test.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetDescription.test.tsx new file mode 100644 index 000000000..6099ac70b --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetDescription.test.tsx @@ -0,0 +1,25 @@ +import i18n from '../../../../../../i18n/testConfigEnglish' +import { createDescriptionRenderFactory } from '../../test' +import { Description } from '../Description' +import SponsorshipSet from './mock_data/SponsorshipSet.json' +import SponsorshipSetDelete from './mock_data/SponsorshipSetDelete.json' + +const renderComponent = createDescriptionRenderFactory(Description, i18n) + +describe('SponsorshipSet: Description', () => { + it('describes a fee sponsorship being set', () => { + const { container, unmount } = renderComponent(SponsorshipSet) + expect(container).toHaveTextContent( + 'rFeeSponsorAlpha11111111111111111 sponsors transaction fees for rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM', + ) + unmount() + }) + + it('describes a fee sponsorship being ended', () => { + const { container, unmount } = renderComponent(SponsorshipSetDelete) + expect(container).toHaveTextContent( + 'rFeeSponsorAlpha11111111111111111 ends the fee sponsorship for rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM', + ) + unmount() + }) +}) diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetSimple.test.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetSimple.test.tsx new file mode 100644 index 000000000..77770c96a --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetSimple.test.tsx @@ -0,0 +1,38 @@ +import { createSimpleRenderFactory, expectSimpleRowText } from '../../test' +import { Simple } from '../Simple' +import i18n from '../../../../../../i18n/testConfigEnglish' +import SponsorshipSet from './mock_data/SponsorshipSet.json' +import SponsorshipSetDelete from './mock_data/SponsorshipSetDelete.json' + +const renderComponent = createSimpleRenderFactory(Simple, i18n) + +describe('SponsorshipSet: Simple', () => { + it('renders fee sponsorship fields', () => { + const { container, unmount } = renderComponent(SponsorshipSet) + + expectSimpleRowText( + container, + 'sponsor', + 'rFeeSponsorAlpha11111111111111111', + ) + expectSimpleRowText( + container, + 'sponsee', + 'rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM', + ) + expectSimpleRowText(container, 'fee-amount', '1.00 XRP') + expectSimpleRowText(container, 'max-fee', '0.001 XRP') + expectSimpleRowText(container, 'reserve-count', '5') + unmount() + }) + + it('renders deletion state without fee fields', () => { + const { container, unmount } = renderComponent(SponsorshipSetDelete) + + expectSimpleRowText(container, 'sponsorship-deleted', 'Sponsorship Deleted') + expect( + container.querySelector('[data-testid="fee-amount"]'), + ).not.toBeInTheDocument() + unmount() + }) +}) diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetTableDetail.test.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetTableDetail.test.tsx new file mode 100644 index 000000000..0bed1b839 --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetTableDetail.test.tsx @@ -0,0 +1,44 @@ +import { createTableDetailRenderFactory } from '../../test' +import { TableDetail } from '../TableDetail' +import i18n from '../../../../../../i18n/testConfigEnglish' +import SponsorshipSet from './mock_data/SponsorshipSet.json' +import SponsorshipSetDelete from './mock_data/SponsorshipSetDelete.json' + +const renderComponent = createTableDetailRenderFactory(TableDetail, i18n) + +describe('SponsorshipSetTableDetail', () => { + it('renders fee sponsorship details', () => { + const { container, unmount } = renderComponent(SponsorshipSet) + + expect(container.querySelectorAll('.account')[0]).toHaveTextContent( + 'rFeeSponsorAlpha11111111111111111', + ) + expect(container.querySelectorAll('.account')[1]).toHaveTextContent( + 'rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM', + ) + expect( + container.querySelectorAll('[data-testid="amount"]')[0], + ).toHaveTextContent('1.00 XRP') + expect( + container.querySelectorAll('[data-testid="amount"]')[1], + ).toHaveTextContent('0.001 XRP') + expect(container.querySelector('.sponsorship-set')).toHaveTextContent( + 'Reserve Count5', + ) + + unmount() + }) + + it('renders deletion state', () => { + const { container, unmount } = renderComponent(SponsorshipSetDelete) + + expect( + container.querySelector('[data-testid="sponsorship-deleted"]'), + ).toHaveTextContent('Sponsorship Deleted') + expect(container.querySelector('.sponsorship-set')).not.toHaveTextContent( + 'Fee Amount', + ) + + unmount() + }) +}) diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/test/mock_data/SponsorshipSet.json b/src/containers/shared/components/Transaction/SponsorshipSet/test/mock_data/SponsorshipSet.json new file mode 100644 index 000000000..91464622d --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipSet/test/mock_data/SponsorshipSet.json @@ -0,0 +1,23 @@ +{ + "hash": "A1B2C3D4E5F6070809101112131415161718192021222324252627282930313A", + "ledger_index": 46635300, + "date": "2026-08-14T13:49:23+00:00", + "tx": { + "Account": "rFeeSponsorAlpha11111111111111111", + "Sponsee": "rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM", + "FeeAmount": "1000000", + "MaxFee": "1000", + "ReserveCount": 5, + "Fee": "12", + "Flags": 0, + "Sequence": 42, + "SigningPubKey": "ED5063FC56A0BD4398964FFD55B3FC353C291C34B81A60BFD33FDC0B036C4D403E", + "TransactionType": "SponsorshipSet", + "TxnSignature": "E98B5560AEE64C27C680E7B9A83239DB3035958761626186455E56C841EBD88438057A0B355C9410E6E5B1A494BFE957ADD8B4873FC9D4700A3BA1041451A905" + }, + "meta": { + "AffectedNodes": [], + "TransactionIndex": 0, + "TransactionResult": "tesSUCCESS" + } +} diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/test/mock_data/SponsorshipSetDelete.json b/src/containers/shared/components/Transaction/SponsorshipSet/test/mock_data/SponsorshipSetDelete.json new file mode 100644 index 000000000..1525cfa2a --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipSet/test/mock_data/SponsorshipSetDelete.json @@ -0,0 +1,20 @@ +{ + "hash": "B1B2C3D4E5F6070809101112131415161718192021222324252627282930313B", + "ledger_index": 46635301, + "date": "2026-08-14T13:49:23+00:00", + "tx": { + "Account": "rFeeSponsorAlpha11111111111111111", + "Sponsee": "rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM", + "Fee": "12", + "Flags": 1048576, + "Sequence": 43, + "SigningPubKey": "ED5063FC56A0BD4398964FFD55B3FC353C291C34B81A60BFD33FDC0B036C4D403E", + "TransactionType": "SponsorshipSet", + "TxnSignature": "E98B5560AEE64C27C680E7B9A83239DB3035958761626186455E56C841EBD88438057A0B355C9410E6E5B1A494BFE957ADD8B4873FC9D4700A3BA1041451A905" + }, + "meta": { + "AffectedNodes": [], + "TransactionIndex": 0, + "TransactionResult": "tesSUCCESS" + } +} diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/types.ts b/src/containers/shared/components/Transaction/SponsorshipSet/types.ts new file mode 100644 index 000000000..1376ce7a9 --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipSet/types.ts @@ -0,0 +1,9 @@ +import { TransactionCommonFields } from '../types' + +export interface SponsorshipSet extends TransactionCommonFields { + CounterpartySponsor?: string + Sponsee?: string + FeeAmount?: string + MaxFee?: string + ReserveCount?: number +} diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/Description.tsx b/src/containers/shared/components/Transaction/SponsorshipTransfer/Description.tsx new file mode 100644 index 000000000..0a79bfc4c --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/Description.tsx @@ -0,0 +1,56 @@ +import { Trans } from 'react-i18next' +import { TransactionDescriptionProps } from '../types' +import { Account } from '../../Account' +import { SponsorshipTransfer } from './types' +import { parser } from './parser' + +export const Description = ({ + data, +}: TransactionDescriptionProps) => { + const { operation, account, sponsor, sponsee } = parser(data.tx) + + if (operation === 'create' && sponsor) { + return ( + , + Sponsor: , + }} + /> + ) + } + + if (operation === 'reassign' && sponsor) { + return ( + , + Sponsor: , + }} + /> + ) + } + + if (operation === 'end' && sponsee) { + return ( + , + Sponsee: , + }} + /> + ) + } + + return ( + , + }} + /> + ) +} diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/Simple.tsx b/src/containers/shared/components/Transaction/SponsorshipTransfer/Simple.tsx new file mode 100644 index 000000000..f5ddb6e0c --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/Simple.tsx @@ -0,0 +1,42 @@ +import { useTranslation } from 'react-i18next' +import { TransactionSimpleComponent, TransactionSimpleProps } from '../types' +import { SimpleRow } from '../SimpleRow' +import { Account } from '../../Account' + +const OPERATION_LABEL_KEYS = { + create: 'sponsorship_operation_create', + reassign: 'sponsorship_operation_reassign', + end: 'sponsorship_operation_end', +} as const + +export const Simple: TransactionSimpleComponent = ({ + data, +}: TransactionSimpleProps) => { + const { t } = useTranslation() + const { operation, objectId, sponsor, sponsee } = data.instructions + + return ( + <> + {operation && ( + + {t(OPERATION_LABEL_KEYS[operation])} + + )} + {objectId && ( + + {objectId} + + )} + {sponsor && ( + + + + )} + {sponsee && ( + + + + )} + + ) +} diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/TableDetail.tsx b/src/containers/shared/components/Transaction/SponsorshipTransfer/TableDetail.tsx new file mode 100644 index 000000000..816be7542 --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/TableDetail.tsx @@ -0,0 +1,48 @@ +import { useTranslation } from 'react-i18next' +import { TransactionTableDetailProps } from '../types' +import { Account } from '../../Account' +import { shortenTxHash } from '../../../utils' + +const OPERATION_LABEL_KEYS = { + create: 'sponsorship_operation_create', + reassign: 'sponsorship_operation_reassign', + end: 'sponsorship_operation_end', +} as const + +export const TableDetail = ({ instructions }: TransactionTableDetailProps) => { + const { t } = useTranslation() + const { operation, objectId, sponsor, sponsee } = instructions + + return ( +
+ {operation && ( +
+ {t('operation')} + + {t(OPERATION_LABEL_KEYS[operation])} + +
+ )} + {objectId && ( +
+ {t('object_id')} + + {shortenTxHash(objectId)} + +
+ )} + {sponsor && ( +
+ {t('new_sponsor')} + +
+ )} + {sponsee && ( +
+ {t('sponsee')} + +
+ )} +
+ ) +} diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/index.ts b/src/containers/shared/components/Transaction/SponsorshipTransfer/index.ts new file mode 100644 index 000000000..df713c406 --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/index.ts @@ -0,0 +1,18 @@ +import { + TransactionAction, + TransactionCategory, + TransactionMapping, +} from '../types' +import { Simple } from './Simple' +import { Description } from './Description' +import { TableDetail } from './TableDetail' +import { parser } from './parser' + +export const SponsorshipTransferTransaction: TransactionMapping = { + Description, + Simple, + TableDetail, + parser, + action: TransactionAction.MODIFY, + category: TransactionCategory.ACCOUNT, +} diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts b/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts new file mode 100644 index 000000000..a48fb99b5 --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts @@ -0,0 +1,24 @@ +import { SponsorshipTransfer } from './types' + +const TF_END = 0x00000001 +const TF_CREATE = 0x00000002 +const TF_REASSIGN = 0x00000004 + +export type SponsorshipTransferOperation = 'create' | 'reassign' | 'end' + +function getOperation(flags: number): SponsorshipTransferOperation | undefined { + if (flags & TF_CREATE) return 'create' + if (flags & TF_REASSIGN) return 'reassign' + if (flags & TF_END) return 'end' + return undefined +} + +export function parser(tx: SponsorshipTransfer) { + return { + operation: getOperation(tx.Flags || 0), + account: tx.Account, + objectId: tx.ObjectID, + sponsor: tx.Sponsor, + sponsee: tx.Sponsee, + } +} diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/test/SponsorshipTransferDescription.test.tsx b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/SponsorshipTransferDescription.test.tsx new file mode 100644 index 000000000..99eec68e2 --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/SponsorshipTransferDescription.test.tsx @@ -0,0 +1,34 @@ +import i18n from '../../../../../../i18n/testConfigEnglish' +import { createDescriptionRenderFactory } from '../../test' +import { Description } from '../Description' +import SponsorshipTransferCreate from './mock_data/SponsorshipTransferCreate.json' +import SponsorshipTransferReassign from './mock_data/SponsorshipTransferReassign.json' +import SponsorshipTransferEnd from './mock_data/SponsorshipTransferEnd.json' + +const renderComponent = createDescriptionRenderFactory(Description, i18n) + +describe('SponsorshipTransfer: Description', () => { + it('describes a create operation', () => { + const { container, unmount } = renderComponent(SponsorshipTransferCreate) + expect(container).toHaveTextContent( + 'rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM assigns rBaseReserveSponsor1111111111111 as its reserve sponsor', + ) + unmount() + }) + + it('describes a reassign operation', () => { + const { container, unmount } = renderComponent(SponsorshipTransferReassign) + expect(container).toHaveTextContent( + 'rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM reassigns reserve sponsorship to rBaseReserveSponsor2222222222222', + ) + unmount() + }) + + it('describes an end operation on behalf of a sponsee', () => { + const { container, unmount } = renderComponent(SponsorshipTransferEnd) + expect(container).toHaveTextContent( + 'rBaseReserveSponsor1111111111111 ends reserve sponsorship for rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM', + ) + unmount() + }) +}) diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/test/SponsorshipTransferSimple.test.tsx b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/SponsorshipTransferSimple.test.tsx new file mode 100644 index 000000000..52f5d8aea --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/SponsorshipTransferSimple.test.tsx @@ -0,0 +1,57 @@ +import { createSimpleRenderFactory, expectSimpleRowText } from '../../test' +import { Simple } from '../Simple' +import i18n from '../../../../../../i18n/testConfigEnglish' +import SponsorshipTransferCreate from './mock_data/SponsorshipTransferCreate.json' +import SponsorshipTransferReassign from './mock_data/SponsorshipTransferReassign.json' +import SponsorshipTransferEnd from './mock_data/SponsorshipTransferEnd.json' + +const renderComponent = createSimpleRenderFactory(Simple, i18n) + +describe('SponsorshipTransfer: Simple', () => { + it('renders a create operation', () => { + const { container, unmount } = renderComponent(SponsorshipTransferCreate) + + expectSimpleRowText(container, 'operation', 'Create') + expectSimpleRowText( + container, + 'new-sponsor', + 'rBaseReserveSponsor1111111111111', + ) + expect( + container.querySelector('[data-testid="object-id"]'), + ).not.toBeInTheDocument() + unmount() + }) + + it('renders a reassign operation with an object id', () => { + const { container, unmount } = renderComponent(SponsorshipTransferReassign) + + expectSimpleRowText(container, 'operation', 'Reassign') + expectSimpleRowText( + container, + 'object-id', + '04F57D5C7F5B660C187BB55227DFC703AEFE482AEA8EEAF4C7FA5ED9BFF3403E', + ) + expectSimpleRowText( + container, + 'new-sponsor', + 'rBaseReserveSponsor2222222222222', + ) + unmount() + }) + + it('renders an end operation with the sponsee', () => { + const { container, unmount } = renderComponent(SponsorshipTransferEnd) + + expectSimpleRowText(container, 'operation', 'End') + expectSimpleRowText( + container, + 'sponsee', + 'rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM', + ) + expect( + container.querySelector('[data-testid="new-sponsor"]'), + ).not.toBeInTheDocument() + unmount() + }) +}) diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/test/SponsorshipTransferTableDetail.test.tsx b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/SponsorshipTransferTableDetail.test.tsx new file mode 100644 index 000000000..7c12017cf --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/SponsorshipTransferTableDetail.test.tsx @@ -0,0 +1,33 @@ +import { createTableDetailRenderFactory } from '../../test' +import { TableDetail } from '../TableDetail' +import i18n from '../../../../../../i18n/testConfigEnglish' +import SponsorshipTransferCreate from './mock_data/SponsorshipTransferCreate.json' +import SponsorshipTransferReassign from './mock_data/SponsorshipTransferReassign.json' + +const renderComponent = createTableDetailRenderFactory(TableDetail, i18n) + +describe('SponsorshipTransferTableDetail', () => { + it('renders a create operation', () => { + const { container, unmount } = renderComponent(SponsorshipTransferCreate) + + expect( + container.querySelector('[data-testid="operation"]'), + ).toHaveTextContent('Create') + expect(container.querySelectorAll('.account')[0]).toHaveTextContent( + 'rBaseReserveSponsor1111111111111', + ) + unmount() + }) + + it('renders a reassign operation with a shortened object id', () => { + const { container, unmount } = renderComponent(SponsorshipTransferReassign) + + expect( + container.querySelector('[data-testid="operation"]'), + ).toHaveTextContent('Reassign') + expect( + container.querySelector('[data-testid="object-id"]'), + ).toHaveTextContent('04F57D...F3403E') + unmount() + }) +}) diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferCreate.json b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferCreate.json new file mode 100644 index 000000000..18b18a3d0 --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferCreate.json @@ -0,0 +1,21 @@ +{ + "hash": "C1B2C3D4E5F6070809101112131415161718192021222324252627282930313C", + "ledger_index": 46635302, + "date": "2026-08-14T13:49:23+00:00", + "tx": { + "Account": "rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM", + "Sponsor": "rBaseReserveSponsor1111111111111", + "SponsorFlags": 2, + "Fee": "12", + "Flags": 2, + "Sequence": 44, + "SigningPubKey": "ED5063FC56A0BD4398964FFD55B3FC353C291C34B81A60BFD33FDC0B036C4D403E", + "TransactionType": "SponsorshipTransfer", + "TxnSignature": "E98B5560AEE64C27C680E7B9A83239DB3035958761626186455E56C841EBD88438057A0B355C9410E6E5B1A494BFE957ADD8B4873FC9D4700A3BA1041451A905" + }, + "meta": { + "AffectedNodes": [], + "TransactionIndex": 0, + "TransactionResult": "tesSUCCESS" + } +} diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferEnd.json b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferEnd.json new file mode 100644 index 000000000..ed1e6e04f --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferEnd.json @@ -0,0 +1,20 @@ +{ + "hash": "E1B2C3D4E5F6070809101112131415161718192021222324252627282930313E", + "ledger_index": 46635304, + "date": "2026-08-14T13:49:23+00:00", + "tx": { + "Account": "rBaseReserveSponsor1111111111111", + "Sponsee": "rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM", + "Fee": "12", + "Flags": 1, + "Sequence": 46, + "SigningPubKey": "ED5063FC56A0BD4398964FFD55B3FC353C291C34B81A60BFD33FDC0B036C4D403E", + "TransactionType": "SponsorshipTransfer", + "TxnSignature": "E98B5560AEE64C27C680E7B9A83239DB3035958761626186455E56C841EBD88438057A0B355C9410E6E5B1A494BFE957ADD8B4873FC9D4700A3BA1041451A905" + }, + "meta": { + "AffectedNodes": [], + "TransactionIndex": 0, + "TransactionResult": "tesSUCCESS" + } +} diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferReassign.json b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferReassign.json new file mode 100644 index 000000000..d2f023e3f --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferReassign.json @@ -0,0 +1,22 @@ +{ + "hash": "D1B2C3D4E5F6070809101112131415161718192021222324252627282930313D", + "ledger_index": 46635303, + "date": "2026-08-14T13:49:23+00:00", + "tx": { + "Account": "rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM", + "ObjectID": "04F57D5C7F5B660C187BB55227DFC703AEFE482AEA8EEAF4C7FA5ED9BFF3403E", + "Sponsor": "rBaseReserveSponsor2222222222222", + "SponsorFlags": 2, + "Fee": "12", + "Flags": 4, + "Sequence": 45, + "SigningPubKey": "ED5063FC56A0BD4398964FFD55B3FC353C291C34B81A60BFD33FDC0B036C4D403E", + "TransactionType": "SponsorshipTransfer", + "TxnSignature": "E98B5560AEE64C27C680E7B9A83239DB3035958761626186455E56C841EBD88438057A0B355C9410E6E5B1A494BFE957ADD8B4873FC9D4700A3BA1041451A905" + }, + "meta": { + "AffectedNodes": [], + "TransactionIndex": 0, + "TransactionResult": "tesSUCCESS" + } +} diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/types.ts b/src/containers/shared/components/Transaction/SponsorshipTransfer/types.ts new file mode 100644 index 000000000..4f89d8b78 --- /dev/null +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/types.ts @@ -0,0 +1,8 @@ +import { TransactionCommonFields } from '../types' + +export interface SponsorshipTransfer extends TransactionCommonFields { + ObjectID?: string + Sponsor?: string + SponsorFlags?: number + Sponsee?: string +} diff --git a/src/containers/shared/components/Transaction/index.ts b/src/containers/shared/components/Transaction/index.ts index 42ff43b4d..8a4faf61a 100644 --- a/src/containers/shared/components/Transaction/index.ts +++ b/src/containers/shared/components/Transaction/index.ts @@ -38,6 +38,8 @@ import { SetFeeTransaction as SetFee } from './SetFee' import { SetHookTransaction as SetHook } from './SetHook' import { SetRegularKeyTransaction as SetRegularKey } from './SetRegularKey' import { SignerListSetTransaction as SignerListSet } from './SignerListSet' +import { SponsorshipSetTransaction as SponsorshipSet } from './SponsorshipSet' +import { SponsorshipTransferTransaction as SponsorshipTransfer } from './SponsorshipTransfer' import { XChainAccountCreateCommitTransaction as XChainAccountCreateCommit } from './XChainAccountCreateCommit' import { XChainAddAccountCreateAttestationTransaction as XChainAddAccountCreateAttestation } from './XChainAddAccountCreateAttestation' import { XChainAddClaimAttestationTransaction as XChainAddClaimAttestation } from './XChainAddClaimAttestation' @@ -120,6 +122,8 @@ export const transactionTypes: { [key: string]: TransactionMapping } = { SetHook, SetRegularKey, SignerListSet, + SponsorshipSet, + SponsorshipTransfer, XChainAccountCreateCommit, XChainAddAccountCreateAttestation, XChainAddClaimAttestation, diff --git a/src/containers/shared/css/simpleTab.scss b/src/containers/shared/css/simpleTab.scss index bda6231af..dec4980d7 100644 --- a/src/containers/shared/css/simpleTab.scss +++ b/src/containers/shared/css/simpleTab.scss @@ -39,6 +39,15 @@ $index-width: 324px; &.account { font-size: 12px; } + + .sponsor-scopes { + overflow: visible; + margin-top: 4px; + color: $black-40; + font-size: 12px; + text-overflow: unset; + white-space: normal; + } } } } diff --git a/src/containers/shared/transactionUtils.ts b/src/containers/shared/transactionUtils.ts index 6b3c4900c..00f749760 100644 --- a/src/containers/shared/transactionUtils.ts +++ b/src/containers/shared/transactionUtils.ts @@ -13,6 +13,27 @@ export const XRP_BASE = 1000000 export const hexMatch = /^(0x)?[0-9A-Fa-f]+$/ export const ACCOUNT_ZERO = 'rrrrrrrrrrrrrrrrrrrrrhoLvTp' +// Common `SponsorFlags` values (also reused by SponsorshipTransfer.SponsorFlags) +const SPF_SPONSOR_FEE = 0x00000001 +const SPF_SPONSOR_RESERVE = 0x00000002 + +export type SponsorScope = 'fee' | 'reserve' + +export const getSponsorScopes = ( + sponsorFlags: number | undefined, +): SponsorScope[] => { + const flags = sponsorFlags || 0 + const scopes: SponsorScope[] = [] + if (flags & SPF_SPONSOR_FEE) scopes.push('fee') + if (flags & SPF_SPONSOR_RESERVE) scopes.push('reserve') + return scopes +} + +export const SPONSOR_SCOPE_LABEL_KEYS = { + fee: 'account_page_sponsored_scope_transaction_fees', + reserve: 'account_page_sponsored_scope_base_reserve', +} as const + export const TX_FLAGS: Record> = { all: { 0x80000000: 'tfFullyCanonicalSig', @@ -50,6 +71,18 @@ export const TX_FLAGS: Record> = { LoanSet: { 0x00010000: 'tfLoanOverpayment', }, + SponsorshipSet: { + 0x00010000: 'tfSponsorshipSetRequireSignForFee', + 0x00020000: 'tfSponsorshipClearRequireSignForFee', + 0x00040000: 'tfSponsorshipSetRequireSignForReserve', + 0x00080000: 'tfSponsorshipClearRequireSignForReserve', + 0x00100000: 'tfDeleteObject', + }, + SponsorshipTransfer: { + 0x00000001: 'tfSponsorshipEnd', + 0x00000002: 'tfSponsorshipCreate', + 0x00000004: 'tfSponsorshipReassign', + }, LoanManage: { 0x00010000: 'tfLoanDefault', 0x00020000: 'tfLoanImpair', From 4f7f90c9eb560bc7d09b351efebeb6ffe9bff245 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Mon, 24 Aug 2026 16:28:31 -0700 Subject: [PATCH 06/10] Polish Sponsored Fees & Reserves UI Match sibling account-page sections: capitalize the title, make it collapsible (collapsed by default) like Account Properties/Assets Held, drop the redundant Status column, and show "No Sponsors" instead of hiding the section entirely when an account has none. Also drop the sponsor-scope text from the transaction Simple tab's Sponsor row to keep the sidebar compact. --- public/locales/en-US/translations.json | 4 +- .../Accounts/SponsoredFeesReserves/index.tsx | 38 +++++++++++-------- .../SponsoredFeesReserves/styles.scss | 14 +++---- .../test/SponsoredFeesReserves.test.tsx | 34 +++++++++++++++-- src/containers/Accounts/index.tsx | 4 +- src/containers/Transactions/SimpleTab.tsx | 16 +------- src/containers/shared/css/simpleTab.scss | 9 ----- 7 files changed, 64 insertions(+), 55 deletions(-) diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index 91f490653..d5fe2b14a 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -730,12 +730,12 @@ "account_page_payment_channels": "Payment Channels", "account_page_payment_channels_text": "{{currency}} available in {{number}} channel(s)", "account_page_nft_minter": "NFT Minter", - "account_page_sponsored_fees_reserves_title": "Sponsored fees & reserves", + "account_page_sponsored_fees_reserves_title": "Sponsored Fees & Reserves", "account_page_sponsored_scope": "Scope", "account_page_sponsored_by": "Sponsored by", "account_page_sponsored_scope_transaction_fees": "Transaction Fees", "account_page_sponsored_scope_base_reserve": "Base Reserve", - "account_page_sponsored_status_active": "Active", + "account_page_sponsored_none": "No Sponsors", "account_page_asset_held_title": "Assets Held", "account_page_asset_issued_title": "Assets Issued", "account_page_asset_tab_iou": "IOUs ({{count}})", diff --git a/src/containers/Accounts/SponsoredFeesReserves/index.tsx b/src/containers/Accounts/SponsoredFeesReserves/index.tsx index ab50a4e77..68d9f3d1f 100644 --- a/src/containers/Accounts/SponsoredFeesReserves/index.tsx +++ b/src/containers/Accounts/SponsoredFeesReserves/index.tsx @@ -1,5 +1,7 @@ import { useTranslation } from 'react-i18next' import { Account } from '../../shared/components/Account' +import { CollapsibleSection } from '../../shared/components/CollapsibleSection' +import { EmptyMessageTableRow } from '../../shared/EmptyMessageTableRow' import type { AccountState } from '../../../rippled/accountState' import './styles.scss' @@ -30,32 +32,38 @@ export const SponsoredFeesReserves = ({ account }: Props) => { ] return ( -
-

- {t('account_page_sponsored_fees_reserves_title')} -

+
- - {rows.map(({ scopeKey, sponsor }) => ( - - - - - - ))} + {rows.length === 0 ? ( + + {t('account_page_sponsored_none')} + + ) : ( + rows.map(({ scopeKey, sponsor }) => ( + + + + + )) + )}
{t('account_page_sponsored_scope')} {t('account_page_sponsored_by')}{t('status')}
{t(scopeKey)} - - {t('account_page_sponsored_status_active')}
{t(scopeKey)} + +
-
+ ) } diff --git a/src/containers/Accounts/SponsoredFeesReserves/styles.scss b/src/containers/Accounts/SponsoredFeesReserves/styles.scss index 7f37b361e..90251641b 100644 --- a/src/containers/Accounts/SponsoredFeesReserves/styles.scss +++ b/src/containers/Accounts/SponsoredFeesReserves/styles.scss @@ -2,13 +2,6 @@ .sponsored-fees-reserves-section { padding: 24px 0; - - .sponsored-fees-reserves-title { - @include bold; - - margin: 0 0 20px; - font-size: 20px; - } } .sponsored-fees-reserves-table-wrapper { @@ -35,6 +28,13 @@ border-bottom: 1px solid $black-80; color: $white; font-size: 14px; + + &.empty-message { + padding: 16px; + color: $black-40; + font-size: 16px; + text-align: center; + } } tbody tr:first-child td { diff --git a/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx b/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx index 9c4b39063..ba68ef194 100644 --- a/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx +++ b/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx @@ -1,4 +1,4 @@ -import { render, screen } from '@testing-library/react' +import { render, screen, fireEvent } from '@testing-library/react' import { I18nextProvider } from 'react-i18next' import { BrowserRouter as Router } from 'react-router' import i18n from '../../../../i18n/testConfigEnglish' @@ -23,15 +23,34 @@ const baseAccount: AccountState = { deleted: false, } +const openSection = () => { + fireEvent.click( + screen.getByLabelText('Toggle sponsored fees & reserves section'), + ) +} + describe('SponsoredFeesReserves Component', () => { - it('renders no rows when the account has no sponsorship', () => { + it('renders the title and starts collapsed', () => { render( , ) - expect(screen.getByText('Sponsored fees & reserves')).toBeInTheDocument() + expect(screen.getByText('Sponsored Fees & Reserves')).toBeInTheDocument() + expect(screen.queryByText('No Sponsors')).not.toBeInTheDocument() + }) + + it('shows "No Sponsors" when the account has no sponsorship', () => { + render( + + + , + ) + + openSection() + + expect(screen.getByText('No Sponsors')).toBeInTheDocument() expect(screen.queryByText('Transaction Fees')).not.toBeInTheDocument() expect(screen.queryByText('Base Reserve')).not.toBeInTheDocument() }) @@ -51,6 +70,8 @@ describe('SponsoredFeesReserves Component', () => { , ) + openSection() + expect(screen.getByText('Base Reserve')).toBeInTheDocument() expect(screen.queryByText('Transaction Fees')).not.toBeInTheDocument() expect(screen.getByTestId('account-component')).toHaveTextContent( @@ -75,6 +96,8 @@ describe('SponsoredFeesReserves Component', () => { , ) + openSection() + expect(screen.getByText('Transaction Fees')).toBeInTheDocument() expect(screen.queryByText('Base Reserve')).not.toBeInTheDocument() expect(screen.getByTestId('account-component')).toHaveTextContent( @@ -103,10 +126,11 @@ describe('SponsoredFeesReserves Component', () => { , ) + openSection() + expect(screen.getByText('Transaction Fees')).toBeInTheDocument() expect(screen.getByText('Base Reserve')).toBeInTheDocument() expect(screen.getAllByTestId('account-component')).toHaveLength(2) - expect(screen.getAllByText('Active')).toHaveLength(2) }) it('renders one Transaction Fees row per sponsor when there are multiple fee sponsors', () => { @@ -130,6 +154,8 @@ describe('SponsoredFeesReserves Component', () => { , ) + openSection() + expect(screen.getAllByText('Transaction Fees')).toHaveLength(2) expect(screen.getAllByTestId('account-component')).toHaveLength(2) expect(screen.getAllByTestId('account-component')[0]).toHaveTextContent( diff --git a/src/containers/Accounts/index.tsx b/src/containers/Accounts/index.tsx index 10904fe03..4b68172fd 100644 --- a/src/containers/Accounts/index.tsx +++ b/src/containers/Accounts/index.tsx @@ -67,9 +67,7 @@ export const Accounts = () => { {showAccount && ( <> - {(account.sponsorship?.length || account.info?.sponsor) && ( - - )} + = ({ ticketSequence, isHook, sponsor, - sponsorFlags, ) => ( <> = ({ {sponsor && ( - {getSponsorScopes(sponsorFlags).length > 0 && ( -
- {getSponsorScopes(sponsorFlags) - .map((scope) => t(SPONSOR_SCOPE_LABEL_KEYS[scope])) - .join(', ')} -
- )}
)} @@ -119,7 +106,6 @@ export const SimpleTab: FC<{ data: any; width: number }> = ({ processed.tx.TicketSequence, !!processed.tx.EmitDetails, processed.tx.Sponsor, - processed.tx.SponsorFlags, ) return ( diff --git a/src/containers/shared/css/simpleTab.scss b/src/containers/shared/css/simpleTab.scss index dec4980d7..bda6231af 100644 --- a/src/containers/shared/css/simpleTab.scss +++ b/src/containers/shared/css/simpleTab.scss @@ -39,15 +39,6 @@ $index-width: 324px; &.account { font-size: 12px; } - - .sponsor-scopes { - overflow: visible; - margin-top: 4px; - color: $black-40; - font-size: 12px; - text-overflow: unset; - white-space: normal; - } } } } From 9032e91fde197f950ec5ada09713d399e0c5bb53 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Thu, 27 Aug 2026 16:03:48 -0400 Subject: [PATCH 07/10] Align sponsorship fields with latest XLS-68 spec revision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cross-referenced against the pinned spec commit (XRPLF/XRPL-Standards@2733d3a, branch 68-updates) and found three inconsistencies: - SponsorshipTransfer's tfSponsorshipEnd/Create/Reassign flags reverted back to the upper-16-bit values (0x10000/0x20000/0x40000) per the latest spec revision. - SponsorshipSet's FeeAmount/ReserveCount fields are now FeeAmountDelta/RemainingOwnerCountDelta and represent deltas applied to the existing Sponsorship object, not absolute values — relabeled as "Fee Amount Change"/"Reserve Count Change" with explicit sign. - The Sponsorship ledger entry's own field is RemainingOwnerCount, not ReserveCount — fixes a pre-existing bug in the account page's fee sponsorship display from before this set of changes. --- public/locales/en-US/translations.json | 4 +-- .../Transaction/SponsorshipSet/Simple.tsx | 21 +++++++++------ .../SponsorshipSet/TableDetail.tsx | 26 +++++++++++++------ .../Transaction/SponsorshipSet/parser.ts | 8 +++--- .../test/SponsorshipSetSimple.test.tsx | 6 ++--- .../test/SponsorshipSetTableDetail.test.tsx | 4 +-- .../test/mock_data/SponsorshipSet.json | 4 +-- .../Transaction/SponsorshipSet/types.ts | 4 +-- .../Transaction/SponsorshipTransfer/parser.ts | 6 ++--- .../mock_data/SponsorshipTransferCreate.json | 2 +- .../mock_data/SponsorshipTransferEnd.json | 2 +- .../SponsorshipTransferReassign.json | 2 +- src/containers/shared/transactionUtils.ts | 6 ++--- src/rippled/lib/rippled.ts | 2 +- src/rippled/lib/test/rippled.test.ts | 2 +- 15 files changed, 58 insertions(+), 41 deletions(-) diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index d5fe2b14a..e5e11c037 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -956,9 +956,9 @@ "sponsor": "Sponsor", "sponsee": "Sponsee", "new_sponsor": "New Sponsor", - "fee_amount": "Fee Amount", + "fee_amount_delta": "Fee Amount Change", "max_fee": "Max Fee", - "reserve_count": "Reserve Count", + "reserve_count_delta": "Reserve Count Change", "require_sign_for_fee": "Require Sign For Fee", "require_sign_for_reserve": "Require Sign For Reserve", "sponsorship_deleted": "Sponsorship Deleted", diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/Simple.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/Simple.tsx index 20a67dcba..afbcfbf36 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/Simple.tsx +++ b/src/containers/shared/components/Transaction/SponsorshipSet/Simple.tsx @@ -12,9 +12,9 @@ export const Simple: TransactionSimpleComponent = ({ sponsor, sponsee, isDelete, - feeAmount, + feeAmountDelta, maxFee, - reserveCount, + remainingOwnerCountDelta, requireSignForFee, requireSignForReserve, } = data.instructions @@ -32,9 +32,9 @@ export const Simple: TransactionSimpleComponent = ({ {t('sponsorship_deleted')} )} - {!isDelete && feeAmount && ( - - + {!isDelete && feeAmountDelta && ( + + )} {!isDelete && maxFee && ( @@ -42,9 +42,14 @@ export const Simple: TransactionSimpleComponent = ({ )} - {!isDelete && reserveCount !== undefined && ( - - {reserveCount} + {!isDelete && remainingOwnerCountDelta !== undefined && ( + + {remainingOwnerCountDelta > 0 + ? `+${remainingOwnerCountDelta}` + : remainingOwnerCountDelta} )} {!isDelete && requireSignForFee && ( diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/TableDetail.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/TableDetail.tsx index 5609e6b15..ec8e06a6c 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/TableDetail.tsx +++ b/src/containers/shared/components/Transaction/SponsorshipSet/TableDetail.tsx @@ -5,8 +5,14 @@ import { Amount } from '../../Amount' export const TableDetail = ({ instructions }: TransactionTableDetailProps) => { const { t } = useTranslation() - const { sponsor, sponsee, isDelete, feeAmount, maxFee, reserveCount } = - instructions + const { + sponsor, + sponsee, + isDelete, + feeAmountDelta, + maxFee, + remainingOwnerCountDelta, + } = instructions return (
@@ -26,10 +32,10 @@ export const TableDetail = ({ instructions }: TransactionTableDetailProps) => {
)} - {!isDelete && feeAmount && ( + {!isDelete && feeAmountDelta && (
- {t('fee_amount')} - + {t('fee_amount_delta')} +
)} {!isDelete && maxFee && ( @@ -38,10 +44,14 @@ export const TableDetail = ({ instructions }: TransactionTableDetailProps) => {
)} - {!isDelete && reserveCount !== undefined && ( + {!isDelete && remainingOwnerCountDelta !== undefined && (
- {t('reserve_count')} - {reserveCount} + {t('reserve_count_delta')} + + {remainingOwnerCountDelta > 0 + ? `+${remainingOwnerCountDelta}` + : remainingOwnerCountDelta} +
)}
diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts b/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts index 30bcf1b47..2a7ce10b6 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts +++ b/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts @@ -18,10 +18,12 @@ export function parser(tx: SponsorshipSet) { sponsor, sponsee, isDelete: Boolean(flags & TF_DELETE_OBJECT), - feeAmount: - tx.FeeAmount !== undefined ? formatAmount(tx.FeeAmount) : undefined, + feeAmountDelta: + tx.FeeAmountDelta !== undefined + ? formatAmount(tx.FeeAmountDelta) + : undefined, maxFee: tx.MaxFee !== undefined ? formatAmount(tx.MaxFee) : undefined, - reserveCount: tx.ReserveCount, + remainingOwnerCountDelta: tx.RemainingOwnerCountDelta, requireSignForFee: Boolean(flags & TF_SET_REQUIRE_SIGN_FOR_FEE) || undefined, clearRequireSignForFee: diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetSimple.test.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetSimple.test.tsx index 77770c96a..20892f5f9 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetSimple.test.tsx +++ b/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetSimple.test.tsx @@ -20,9 +20,9 @@ describe('SponsorshipSet: Simple', () => { 'sponsee', 'rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM', ) - expectSimpleRowText(container, 'fee-amount', '1.00 XRP') + expectSimpleRowText(container, 'fee-amount-delta', '1.00 XRP') expectSimpleRowText(container, 'max-fee', '0.001 XRP') - expectSimpleRowText(container, 'reserve-count', '5') + expectSimpleRowText(container, 'reserve-count-delta', '+5') unmount() }) @@ -31,7 +31,7 @@ describe('SponsorshipSet: Simple', () => { expectSimpleRowText(container, 'sponsorship-deleted', 'Sponsorship Deleted') expect( - container.querySelector('[data-testid="fee-amount"]'), + container.querySelector('[data-testid="fee-amount-delta"]'), ).not.toBeInTheDocument() unmount() }) diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetTableDetail.test.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetTableDetail.test.tsx index 0bed1b839..92ab88755 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetTableDetail.test.tsx +++ b/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetTableDetail.test.tsx @@ -23,7 +23,7 @@ describe('SponsorshipSetTableDetail', () => { container.querySelectorAll('[data-testid="amount"]')[1], ).toHaveTextContent('0.001 XRP') expect(container.querySelector('.sponsorship-set')).toHaveTextContent( - 'Reserve Count5', + 'Reserve Count Change+5', ) unmount() @@ -36,7 +36,7 @@ describe('SponsorshipSetTableDetail', () => { container.querySelector('[data-testid="sponsorship-deleted"]'), ).toHaveTextContent('Sponsorship Deleted') expect(container.querySelector('.sponsorship-set')).not.toHaveTextContent( - 'Fee Amount', + 'Fee Amount Change', ) unmount() diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/test/mock_data/SponsorshipSet.json b/src/containers/shared/components/Transaction/SponsorshipSet/test/mock_data/SponsorshipSet.json index 91464622d..e906662fc 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/test/mock_data/SponsorshipSet.json +++ b/src/containers/shared/components/Transaction/SponsorshipSet/test/mock_data/SponsorshipSet.json @@ -5,9 +5,9 @@ "tx": { "Account": "rFeeSponsorAlpha11111111111111111", "Sponsee": "rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM", - "FeeAmount": "1000000", + "FeeAmountDelta": "1000000", "MaxFee": "1000", - "ReserveCount": 5, + "RemainingOwnerCountDelta": 5, "Fee": "12", "Flags": 0, "Sequence": 42, diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/types.ts b/src/containers/shared/components/Transaction/SponsorshipSet/types.ts index 1376ce7a9..1a26b697c 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/types.ts +++ b/src/containers/shared/components/Transaction/SponsorshipSet/types.ts @@ -3,7 +3,7 @@ import { TransactionCommonFields } from '../types' export interface SponsorshipSet extends TransactionCommonFields { CounterpartySponsor?: string Sponsee?: string - FeeAmount?: string + FeeAmountDelta?: string MaxFee?: string - ReserveCount?: number + RemainingOwnerCountDelta?: number } diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts b/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts index a48fb99b5..db2db1665 100644 --- a/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts @@ -1,8 +1,8 @@ import { SponsorshipTransfer } from './types' -const TF_END = 0x00000001 -const TF_CREATE = 0x00000002 -const TF_REASSIGN = 0x00000004 +const TF_END = 0x00010000 +const TF_CREATE = 0x00020000 +const TF_REASSIGN = 0x00040000 export type SponsorshipTransferOperation = 'create' | 'reassign' | 'end' diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferCreate.json b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferCreate.json index 18b18a3d0..b4ad9cc9f 100644 --- a/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferCreate.json +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferCreate.json @@ -7,7 +7,7 @@ "Sponsor": "rBaseReserveSponsor1111111111111", "SponsorFlags": 2, "Fee": "12", - "Flags": 2, + "Flags": 131072, "Sequence": 44, "SigningPubKey": "ED5063FC56A0BD4398964FFD55B3FC353C291C34B81A60BFD33FDC0B036C4D403E", "TransactionType": "SponsorshipTransfer", diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferEnd.json b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferEnd.json index ed1e6e04f..f27d41868 100644 --- a/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferEnd.json +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferEnd.json @@ -6,7 +6,7 @@ "Account": "rBaseReserveSponsor1111111111111", "Sponsee": "rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM", "Fee": "12", - "Flags": 1, + "Flags": 65536, "Sequence": 46, "SigningPubKey": "ED5063FC56A0BD4398964FFD55B3FC353C291C34B81A60BFD33FDC0B036C4D403E", "TransactionType": "SponsorshipTransfer", diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferReassign.json b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferReassign.json index d2f023e3f..98c3ee378 100644 --- a/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferReassign.json +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferReassign.json @@ -8,7 +8,7 @@ "Sponsor": "rBaseReserveSponsor2222222222222", "SponsorFlags": 2, "Fee": "12", - "Flags": 4, + "Flags": 262144, "Sequence": 45, "SigningPubKey": "ED5063FC56A0BD4398964FFD55B3FC353C291C34B81A60BFD33FDC0B036C4D403E", "TransactionType": "SponsorshipTransfer", diff --git a/src/containers/shared/transactionUtils.ts b/src/containers/shared/transactionUtils.ts index 00f749760..ee94f0660 100644 --- a/src/containers/shared/transactionUtils.ts +++ b/src/containers/shared/transactionUtils.ts @@ -79,9 +79,9 @@ export const TX_FLAGS: Record> = { 0x00100000: 'tfDeleteObject', }, SponsorshipTransfer: { - 0x00000001: 'tfSponsorshipEnd', - 0x00000002: 'tfSponsorshipCreate', - 0x00000004: 'tfSponsorshipReassign', + 0x00010000: 'tfSponsorshipEnd', + 0x00020000: 'tfSponsorshipCreate', + 0x00040000: 'tfSponsorshipReassign', }, LoanManage: { 0x00010000: 'tfLoanDefault', diff --git a/src/rippled/lib/rippled.ts b/src/rippled/lib/rippled.ts index 144dc4b41..6b993f3cd 100644 --- a/src/rippled/lib/rippled.ts +++ b/src/rippled/lib/rippled.ts @@ -32,7 +32,7 @@ const formatSponsorship = (d: any) => ({ sponsee: d.Sponsee, feeAmount: d.FeeAmount, maxFee: d.MaxFee, - reserveCount: d.ReserveCount, + reserveCount: d.RemainingOwnerCount, }) const executeQuery = async ( diff --git a/src/rippled/lib/test/rippled.test.ts b/src/rippled/lib/test/rippled.test.ts index 5e0a6f5f3..f8df040df 100644 --- a/src/rippled/lib/test/rippled.test.ts +++ b/src/rippled/lib/test/rippled.test.ts @@ -241,7 +241,7 @@ describe('getAccountSponsorship', () => { Sponsee: ACCOUNT, FeeAmount: '1000', MaxFee: '5000', - ReserveCount: 2, + RemainingOwnerCount: 2, }, ], }) From 24db7ae48f45558dafdc7a781b1a3e5a03d825e9 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Mon, 31 Aug 2026 11:31:10 -0700 Subject: [PATCH 08/10] Address remaining PR review comments on sponsorship display - Add transaction_type_name_SponsorshipSet/SponsorshipTransfer to en-US (translated) and all other locale files (null placeholders), fixing TxLabel's fallback to raw camelCase type names. - Add missing Payment.tfSponsorCreatedAccount flag to TX_FLAGS. - Fix SponsorshipTransfer's Description defaulting to sponsorship_transfer_end_self for any unrecognized operation, not just a genuine end-on-self case. - Show an explicit +/- sign on SponsorshipSet's fee amount delta, matching the reserve count delta's existing sign handling. - Rename accountState/rippled's reserveCount field to remainingOwnerCount to match the actual XRPL field name. - Fix getAccountSponsorship to page through account_objects via marker instead of only examining the first 400 objects, which could silently miss a sponsee's Sponsorship entries entirely. - Filter SponsoredFeesReserves' fee sponsor rows by an actual positive FeeAmount instead of assuming any sponsorship entry implies active fee sponsorship. - Vary SponsorshipSet's description text based on whether the transaction is adjusting the fee budget, the reserve budget, both, or neither, instead of always saying "sponsors transaction fees." - Remove clearRequireSignForFee/clearRequireSignForReserve from SponsorshipSet's parser output, which were computed but never rendered anywhere. --- public/locales/ca-CA/translations.json | 2 + public/locales/en-US/translations.json | 7 +- public/locales/es-ES/translations.json | 2 + public/locales/fr-FR/translations.json | 2 + public/locales/ja-JP/translations.json | 2 + public/locales/ko-KR/translations.json | 2 + public/locales/my-MM/translations.json | 2 + .../Accounts/SponsoredFeesReserves/index.tsx | 10 ++- .../test/SponsoredFeesReserves.test.tsx | 28 +++++++ .../SponsorshipSet/Description.tsx | 29 +++++++- .../Transaction/SponsorshipSet/Simple.tsx | 5 +- .../SponsorshipSet/TableDetail.tsx | 5 +- .../Transaction/SponsorshipSet/parser.ts | 24 +++--- .../test/SponsorshipSetDescription.test.tsx | 51 ++++++++++++- .../SponsorshipTransfer/Description.tsx | 26 ++++--- src/containers/shared/transactionUtils.ts | 1 + src/rippled/accountState.ts | 2 +- src/rippled/lib/rippled.ts | 74 ++++++++++++------- src/rippled/lib/test/rippled.test.ts | 36 ++++++++- 19 files changed, 247 insertions(+), 63 deletions(-) diff --git a/public/locales/ca-CA/translations.json b/public/locales/ca-CA/translations.json index bc4553c11..59207d471 100644 --- a/public/locales/ca-CA/translations.json +++ b/public/locales/ca-CA/translations.json @@ -208,6 +208,8 @@ "transaction_type_name_SetHook": "Establir Hook", "transaction_type_name_SetRegularKey": "Establir Clau Regular", "transaction_type_name_SignerListSet": "Etablir Llista de Signantst", + "transaction_type_name_SponsorshipSet": null, + "transaction_type_name_SponsorshipTransfer": null, "transaction_type_name_TicketCreate": "Crear tiquet", "transaction_type_name_TrustSet": "Establir Confiança", "transaction_type_name_VaultCreate": null, diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index e5e11c037..15afc600d 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -209,6 +209,8 @@ "transaction_type_name_SetHook": "Set Hook", "transaction_type_name_SetRegularKey": "Set Regular Key", "transaction_type_name_SignerListSet": "Signer List Set", + "transaction_type_name_SponsorshipSet": "Sponsorship Set", + "transaction_type_name_SponsorshipTransfer": "Sponsorship Transfer", "transaction_type_name_TicketCreate": "Ticket Create", "transaction_type_name_TrustSet": "Trust Set", "transaction_type_name_VaultCreate": "Vault Create", @@ -967,7 +969,10 @@ "sponsorship_operation_create": "Create", "sponsorship_operation_reassign": "Reassign", "sponsorship_operation_end": "End", - "sponsorship_set_description": " sponsors transaction fees for ", + "sponsorship_set_description_fee": " sponsors transaction fees for ", + "sponsorship_set_description_reserve": " sponsors reserves for ", + "sponsorship_set_description_fee_reserve": " sponsors transaction fees and reserves for ", + "sponsorship_set_description_generic": " updates its sponsorship terms for ", "sponsorship_set_delete": " ends the fee sponsorship for ", "sponsorship_transfer_create": " assigns as its reserve sponsor", "sponsorship_transfer_reassign": " reassigns reserve sponsorship to ", diff --git a/public/locales/es-ES/translations.json b/public/locales/es-ES/translations.json index 12e1a48f4..b2ac260d6 100644 --- a/public/locales/es-ES/translations.json +++ b/public/locales/es-ES/translations.json @@ -209,6 +209,8 @@ "transaction_type_name_SetHook": "Añadir Hook", "transaction_type_name_SetRegularKey": "Configurar Clave Normal", "transaction_type_name_SignerListSet": "Configurar Lista de Firmantes", + "transaction_type_name_SponsorshipSet": null, + "transaction_type_name_SponsorshipTransfer": null, "transaction_type_name_TicketCreate": "Creación de Ticket", "transaction_type_name_TrustSet": "Configurar Confianza", "transaction_type_name_VaultCreate": null, diff --git a/public/locales/fr-FR/translations.json b/public/locales/fr-FR/translations.json index 99bb71f9e..64d716b88 100644 --- a/public/locales/fr-FR/translations.json +++ b/public/locales/fr-FR/translations.json @@ -209,6 +209,8 @@ "transaction_type_name_SetHook": "Crochet enregistré", "transaction_type_name_SetRegularKey": "Clé régulière définie", "transaction_type_name_SignerListSet": "Liste de signataires établie", + "transaction_type_name_SponsorshipSet": null, + "transaction_type_name_SponsorshipTransfer": null, "transaction_type_name_TicketCreate": "Ticket créé", "transaction_type_name_TrustSet": "Ligne de confiance créée", "transaction_type_name_VaultCreate": null, diff --git a/public/locales/ja-JP/translations.json b/public/locales/ja-JP/translations.json index 8229d9e98..f8b78782d 100644 --- a/public/locales/ja-JP/translations.json +++ b/public/locales/ja-JP/translations.json @@ -209,6 +209,8 @@ "transaction_type_name_SetHook": "Hookの設定", "transaction_type_name_SetRegularKey": "レギュラーキーの設定", "transaction_type_name_SignerListSet": "署名者リスト設定", + "transaction_type_name_SponsorshipSet": null, + "transaction_type_name_SponsorshipTransfer": null, "transaction_type_name_TicketCreate": "チケットの作成", "transaction_type_name_TrustSet": "トラスト設定", "transaction_type_name_VaultCreate": null, diff --git a/public/locales/ko-KR/translations.json b/public/locales/ko-KR/translations.json index d02b1dd5c..3571a6034 100644 --- a/public/locales/ko-KR/translations.json +++ b/public/locales/ko-KR/translations.json @@ -209,6 +209,8 @@ "transaction_type_name_SetHook": "Hook 설정", "transaction_type_name_SetRegularKey": "일반 키 설정", "transaction_type_name_SignerListSet": "서명자 목록 설정", + "transaction_type_name_SponsorshipSet": null, + "transaction_type_name_SponsorshipTransfer": null, "transaction_type_name_TicketCreate": "티켓 생성", "transaction_type_name_TrustSet": "신뢰 설정", "transaction_type_name_VaultCreate": null, diff --git a/public/locales/my-MM/translations.json b/public/locales/my-MM/translations.json index c1ef818ab..4a7835958 100644 --- a/public/locales/my-MM/translations.json +++ b/public/locales/my-MM/translations.json @@ -209,6 +209,8 @@ "transaction_type_name_SetHook": "Hook သတ်မှတ်ရန်", "transaction_type_name_SetRegularKey": "ပုံမှန်သော့ သတ်မှတ်ရန်", "transaction_type_name_SignerListSet": "လက်မှတ်ထိုးသူစာရင်း သတ်မှတ်ရန်", + "transaction_type_name_SponsorshipSet": null, + "transaction_type_name_SponsorshipTransfer": null, "transaction_type_name_TicketCreate": "လက်မှတ် ဖန်တီးရန်", "transaction_type_name_TrustSet": "ယုံကြည်မှု သတ်မှတ်ရန်", "transaction_type_name_VaultCreate": null, diff --git a/src/containers/Accounts/SponsoredFeesReserves/index.tsx b/src/containers/Accounts/SponsoredFeesReserves/index.tsx index 68d9f3d1f..2101a74e6 100644 --- a/src/containers/Accounts/SponsoredFeesReserves/index.tsx +++ b/src/containers/Accounts/SponsoredFeesReserves/index.tsx @@ -17,10 +17,12 @@ export const SponsoredFeesReserves = ({ account }: Props) => { const { t } = useTranslation() const rows: { scopeKey: ScopeKey; sponsor: string }[] = [ - ...(account.sponsorship ?? []).map(({ owner }) => ({ - scopeKey: 'account_page_sponsored_scope_transaction_fees' as ScopeKey, - sponsor: owner, - })), + ...(account.sponsorship ?? []) + .filter(({ feeAmount }) => Number(feeAmount) > 0) + .map(({ owner }) => ({ + scopeKey: 'account_page_sponsored_scope_transaction_fees' as ScopeKey, + sponsor: owner, + })), ...(account.info?.sponsor ? [ { diff --git a/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx b/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx index ba68ef194..9372794d9 100644 --- a/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx +++ b/src/containers/Accounts/SponsoredFeesReserves/test/SponsoredFeesReserves.test.tsx @@ -86,6 +86,7 @@ describe('SponsoredFeesReserves Component', () => { { owner: 'rFeeSponsor2222222222222222222222', sponsee: baseAccount.account, + feeAmount: '1000000', }, ], } @@ -105,6 +106,30 @@ describe('SponsoredFeesReserves Component', () => { ) }) + it('omits the Transaction Fees row when the sponsorship has no fee budget', () => { + const account: AccountState = { + ...baseAccount, + sponsorship: [ + { + owner: 'rFeeSponsor2222222222222222222222', + sponsee: baseAccount.account, + feeAmount: '0', + }, + ], + } + + render( + + + , + ) + + openSection() + + expect(screen.getByText('No Sponsors')).toBeInTheDocument() + expect(screen.queryByText('Transaction Fees')).not.toBeInTheDocument() + }) + it('renders both rows when both fees and reserve are sponsored', () => { const account: AccountState = { ...baseAccount, @@ -116,6 +141,7 @@ describe('SponsoredFeesReserves Component', () => { { owner: 'rFeeSponsor2222222222222222222222', sponsee: baseAccount.account, + feeAmount: '1000000', }, ], } @@ -140,10 +166,12 @@ describe('SponsoredFeesReserves Component', () => { { owner: 'rFeeSponsor2222222222222222222222', sponsee: baseAccount.account, + feeAmount: '1000000', }, { owner: 'rFeeSponsor3333333333333333333333', sponsee: baseAccount.account, + feeAmount: '2000000', }, ], } diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/Description.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/Description.tsx index 2125e1c11..a47ec7581 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/Description.tsx +++ b/src/containers/shared/components/Transaction/SponsorshipSet/Description.tsx @@ -4,16 +4,37 @@ import { Account } from '../../Account' import { SponsorshipSet } from './types' import { parser } from './parser' +function getDescriptionKey( + isDelete: boolean, + hasFeeDelta: boolean, + hasReserveDelta: boolean, +) { + if (isDelete) return 'sponsorship_set_delete' + if (hasFeeDelta && hasReserveDelta) + return 'sponsorship_set_description_fee_reserve' + if (hasReserveDelta) return 'sponsorship_set_description_reserve' + if (hasFeeDelta) return 'sponsorship_set_description_fee' + return 'sponsorship_set_description_generic' +} + export const Description = ({ data, }: TransactionDescriptionProps) => { - const { sponsor, sponsee, isDelete } = parser(data.tx) + const { + sponsor, + sponsee, + isDelete, + feeAmountDelta, + remainingOwnerCountDelta, + } = parser(data.tx) return ( , Sponsee: , diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/Simple.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/Simple.tsx index afbcfbf36..0d4cbe115 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/Simple.tsx +++ b/src/containers/shared/components/Transaction/SponsorshipSet/Simple.tsx @@ -34,7 +34,10 @@ export const Simple: TransactionSimpleComponent = ({ )} {!isDelete && feeAmountDelta && ( - + )} {!isDelete && maxFee && ( diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/TableDetail.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/TableDetail.tsx index ec8e06a6c..b9bf60a42 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/TableDetail.tsx +++ b/src/containers/shared/components/Transaction/SponsorshipSet/TableDetail.tsx @@ -35,7 +35,10 @@ export const TableDetail = ({ instructions }: TransactionTableDetailProps) => { {!isDelete && feeAmountDelta && (
{t('fee_amount_delta')} - +
)} {!isDelete && maxFee && ( diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts b/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts index 2a7ce10b6..71e23ea8d 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts +++ b/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts @@ -3,9 +3,20 @@ import { SponsorshipSet } from './types' const TF_DELETE_OBJECT = 0x00100000 const TF_SET_REQUIRE_SIGN_FOR_FEE = 0x00010000 -const TF_CLEAR_REQUIRE_SIGN_FOR_FEE = 0x00020000 const TF_SET_REQUIRE_SIGN_FOR_RESERVE = 0x00040000 -const TF_CLEAR_REQUIRE_SIGN_FOR_RESERVE = 0x00080000 + +// Signs the formatted delta amount explicitly, since a negative amount +// (a withdrawal) would otherwise read identically to a positive one at a +// glance in the UI. +function getSignedDelta(delta: string | undefined) { + if (delta === undefined) return undefined + const formatted = formatAmount(delta) + const amount = Number(formatted.amount) + return { + value: { ...formatted, amount: Math.abs(amount) }, + modifier: amount < 0 ? ('-' as const) : ('+' as const), + } +} export function parser(tx: SponsorshipSet) { const flags = tx.Flags || 0 @@ -18,19 +29,12 @@ export function parser(tx: SponsorshipSet) { sponsor, sponsee, isDelete: Boolean(flags & TF_DELETE_OBJECT), - feeAmountDelta: - tx.FeeAmountDelta !== undefined - ? formatAmount(tx.FeeAmountDelta) - : undefined, + feeAmountDelta: getSignedDelta(tx.FeeAmountDelta), maxFee: tx.MaxFee !== undefined ? formatAmount(tx.MaxFee) : undefined, remainingOwnerCountDelta: tx.RemainingOwnerCountDelta, requireSignForFee: Boolean(flags & TF_SET_REQUIRE_SIGN_FOR_FEE) || undefined, - clearRequireSignForFee: - Boolean(flags & TF_CLEAR_REQUIRE_SIGN_FOR_FEE) || undefined, requireSignForReserve: Boolean(flags & TF_SET_REQUIRE_SIGN_FOR_RESERVE) || undefined, - clearRequireSignForReserve: - Boolean(flags & TF_CLEAR_REQUIRE_SIGN_FOR_RESERVE) || undefined, } } diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetDescription.test.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetDescription.test.tsx index 6099ac70b..7619b8bec 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetDescription.test.tsx +++ b/src/containers/shared/components/Transaction/SponsorshipSet/test/SponsorshipSetDescription.test.tsx @@ -6,11 +6,56 @@ import SponsorshipSetDelete from './mock_data/SponsorshipSetDelete.json' const renderComponent = createDescriptionRenderFactory(Description, i18n) +const SPONSOR = 'rFeeSponsorAlpha11111111111111111' +const SPONSEE = 'rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM' + describe('SponsorshipSet: Description', () => { - it('describes a fee sponsorship being set', () => { + it('describes fee and reserve sponsorship being set together', () => { const { container, unmount } = renderComponent(SponsorshipSet) expect(container).toHaveTextContent( - 'rFeeSponsorAlpha11111111111111111 sponsors transaction fees for rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM', + `${SPONSOR} sponsors transaction fees and reserves for ${SPONSEE}`, + ) + unmount() + }) + + it('describes only fee sponsorship being set', () => { + const { container, unmount } = renderComponent({ + tx: { + Account: SPONSOR, + Sponsee: SPONSEE, + FeeAmountDelta: '1000000', + }, + }) + expect(container).toHaveTextContent( + `${SPONSOR} sponsors transaction fees for ${SPONSEE}`, + ) + unmount() + }) + + it('describes only reserve sponsorship being set', () => { + const { container, unmount } = renderComponent({ + tx: { + Account: SPONSOR, + Sponsee: SPONSEE, + RemainingOwnerCountDelta: 5, + }, + }) + expect(container).toHaveTextContent( + `${SPONSOR} sponsors reserves for ${SPONSEE}`, + ) + unmount() + }) + + it('falls back to a generic description when neither delta is present', () => { + const { container, unmount } = renderComponent({ + tx: { + Account: SPONSOR, + Sponsee: SPONSEE, + MaxFee: '1000', + }, + }) + expect(container).toHaveTextContent( + `${SPONSOR} updates its sponsorship terms for ${SPONSEE}`, ) unmount() }) @@ -18,7 +63,7 @@ describe('SponsorshipSet: Description', () => { it('describes a fee sponsorship being ended', () => { const { container, unmount } = renderComponent(SponsorshipSetDelete) expect(container).toHaveTextContent( - 'rFeeSponsorAlpha11111111111111111 ends the fee sponsorship for rncKvRcdDq9hVJpdLdTcKoxsS3NSkXsvfM', + `${SPONSOR} ends the fee sponsorship for ${SPONSEE}`, ) unmount() }) diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/Description.tsx b/src/containers/shared/components/Transaction/SponsorshipTransfer/Description.tsx index 0a79bfc4c..d084f0efc 100644 --- a/src/containers/shared/components/Transaction/SponsorshipTransfer/Description.tsx +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/Description.tsx @@ -33,24 +33,28 @@ export const Description = ({ ) } - if (operation === 'end' && sponsee) { + if (operation === 'end') { + if (sponsee) { + return ( + , + Sponsee: , + }} + /> + ) + } + return ( , - Sponsee: , }} /> ) } - return ( - , - }} - /> - ) + return null } diff --git a/src/containers/shared/transactionUtils.ts b/src/containers/shared/transactionUtils.ts index ee94f0660..a1532d110 100644 --- a/src/containers/shared/transactionUtils.ts +++ b/src/containers/shared/transactionUtils.ts @@ -130,6 +130,7 @@ export const TX_FLAGS: Record> = { 0x00010000: 'tfNoDirectRipple', 0x00020000: 'tfPartialPayment', 0x00040000: 'tfLimitQuality', + 0x00080000: 'tfSponsorCreatedAccount', }, PaymentChannelClaim: { 0x00010000: 'tfRenew', diff --git a/src/rippled/accountState.ts b/src/rippled/accountState.ts index 9242d98d3..4f26ac438 100644 --- a/src/rippled/accountState.ts +++ b/src/rippled/accountState.ts @@ -40,7 +40,7 @@ export interface AccountState { sponsee: string feeAmount?: string maxFee?: string - reserveCount?: number + remainingOwnerCount?: number }[] info: { accountTransactionID?: string diff --git a/src/rippled/lib/rippled.ts b/src/rippled/lib/rippled.ts index 6b993f3cd..174043b2d 100644 --- a/src/rippled/lib/rippled.ts +++ b/src/rippled/lib/rippled.ts @@ -32,7 +32,7 @@ const formatSponsorship = (d: any) => ({ sponsee: d.Sponsee, feeAmount: d.FeeAmount, maxFee: d.MaxFee, - reserveCount: d.RemainingOwnerCount, + remainingOwnerCount: d.RemainingOwnerCount, }) const executeQuery = async ( @@ -374,43 +374,65 @@ const getAccountBridges = async ( return undefined } +// `limit` on account_objects bounds how many objects rippled examines per +// page, not how many `type`-filtered objects are returned, so a sponsee's +// Sponsorship objects could be missed entirely if only the first page were +// read. Page through via `marker` until either everything has been examined +// or a safety cap is hit. +const SPONSORSHIP_PAGE_SIZE = 400 +const SPONSORSHIP_MAX_EXAMINED = 4000 + // get the sponsorship covering this account's fees/reserves, if any const getAccountSponsorship = async ( rippledSocket: ExplorerXrplClient, account: string, ledgerIndex: string | number = 'validated', ): Promise => { - const resp = await query(rippledSocket, { - command: 'account_objects', - account, - ledger_index: ledgerIndex, - type: 'sponsorship', - limit: 400, - }) - if (resp.error === 'actNotFound') { - throw new Error('account not found', 404) - } - if (resp.error === 'invalidParams') { - // thrown when the Sponsorship amendment is not activated - // TODO: remove this when XLS-68 is live in mainnet - return undefined - } + const found: any[] = [] - if (resp.error_message) { - throw new Error(resp.error_message, 500) + const fetchPage = async ( + marker: any, + examined: number, + ): Promise => { + const resp = await query(rippledSocket, { + command: 'account_objects', + account, + ledger_index: ledgerIndex, + type: 'sponsorship', + limit: SPONSORSHIP_PAGE_SIZE, + marker, + }) + if (resp.error === 'actNotFound') { + throw new Error('account not found', 404) + } + if (resp.error === 'invalidParams') { + // thrown when the Sponsorship amendment is not activated + // TODO: remove this when XLS-68 is live in mainnet + return undefined + } + if (resp.error_message) { + throw new Error(resp.error_message, 500) + } + + // A Sponsorship object is linked into both the sponsor's and sponsee's + // owner directories, so only keep the ones where this account is sponsored. + found.push( + ...resp.account_objects.filter((d: any) => d.Sponsee === account), + ) + + const totalExamined = examined + resp.account_objects.length + if (resp.marker && totalExamined < SPONSORSHIP_MAX_EXAMINED) { + return fetchPage(resp.marker, totalExamined) + } + return found } - if (!resp.account_objects.length) { + const result = await fetchPage(undefined, 0) + if (result === undefined) { return undefined } - // A Sponsorship object is linked into both the sponsor's and sponsee's - // owner directories, so only keep the ones where this account is sponsored. - const sponsorships = resp.account_objects.filter( - (d: any) => d.Sponsee === account, - ) - - return sponsorships.length ? sponsorships.map(formatSponsorship) : undefined + return found.length ? found.map(formatSponsorship) : undefined } // get Token balance summary diff --git a/src/rippled/lib/test/rippled.test.ts b/src/rippled/lib/test/rippled.test.ts index f8df040df..54fbd8faf 100644 --- a/src/rippled/lib/test/rippled.test.ts +++ b/src/rippled/lib/test/rippled.test.ts @@ -252,7 +252,7 @@ describe('getAccountSponsorship', () => { sponsee: ACCOUNT, feeAmount: '1000', maxFee: '5000', - reserveCount: 2, + remainingOwnerCount: 2, }, ]) expect(socket.send).toHaveBeenCalledWith({ @@ -289,6 +289,40 @@ describe('getAccountSponsorship', () => { ]) }) + it('pages through account_objects via marker to find sponsorships past the first page', async () => { + const socket = { + send: jest + .fn() + .mockResolvedValueOnce({ + account_objects: [], + marker: 'page-2', + }) + .mockResolvedValueOnce({ + account_objects: [ + { + LedgerEntryType: 'Sponsorship', + Owner: SPONSOR, + Sponsee: ACCOUNT, + FeeAmount: '1000', + }, + ], + }), + } as any + + await expect(getAccountSponsorship(socket, ACCOUNT)).resolves.toEqual([ + { owner: SPONSOR, sponsee: ACCOUNT, feeAmount: '1000' }, + ]) + expect(socket.send).toHaveBeenCalledTimes(2) + expect(socket.send).toHaveBeenNthCalledWith(2, { + command: 'account_objects', + account: ACCOUNT, + ledger_index: 'validated', + type: 'sponsorship', + limit: 400, + marker: 'page-2', + }) + }) + it('ignores Sponsorship objects where this account is the sponsor, not the sponsee', async () => { const socket = makeSocket({ account_objects: [ From e5ca919e70e9ee907e939f07e912dffba3995a39 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Mon, 31 Aug 2026 15:18:58 -0700 Subject: [PATCH 09/10] Fix SponsorshipTransfer/Sponsor field collision with generic co-sponsor UI SponsorshipTransfer's Sponsor field is the same field as the generic transaction-common Sponsor field, but on create/reassign it means "new sponsor of the target object," not "who's co-sponsoring this outer transaction." Exclude the generic Sponsor row/section in that case. On tfSponsorshipEnd, the spec requires Sponsor to be omitted for the transfer's own purpose, so if it's present there it can only be genuine outer co-sponsorship and is still shown. Exports getOperation from SponsorshipTransfer/parser.ts for reuse in SimpleTab/DetailTab, and adds test coverage for all three cases. --- .../Transactions/DetailTab/index.tsx | 11 +++++ src/containers/Transactions/SimpleTab.tsx | 14 +++++- .../Transactions/test/DetailTab.test.tsx | 23 ++++++++++ .../Transactions/test/SimpleTab.test.tsx | 43 +++++++++++++++++++ .../Transaction/SponsorshipTransfer/parser.ts | 4 +- 5 files changed, 93 insertions(+), 2 deletions(-) diff --git a/src/containers/Transactions/DetailTab/index.tsx b/src/containers/Transactions/DetailTab/index.tsx index afbb0ad62..393d5e4c4 100644 --- a/src/containers/Transactions/DetailTab/index.tsx +++ b/src/containers/Transactions/DetailTab/index.tsx @@ -19,6 +19,7 @@ import { useLanguage } from '../../shared/hooks' import { HookDetails } from './HookDetails' import { RouteLink } from '../../shared/routing' import { LEDGER_ROUTE } from '../../App/routes' +import { getOperation } from '../../shared/components/Transaction/SponsorshipTransfer/parser' export const DetailTab: FC<{ data: any }> = ({ data }) => { const { t } = useTranslation() @@ -112,6 +113,16 @@ export const DetailTab: FC<{ data: any }> = ({ data }) => { } const renderSponsor = () => { + // On create/reassign, SponsorshipTransfer reuses the common Sponsor + // field for its own "new sponsor of the target object" meaning, not for + // co-sponsoring this outer transaction, so it's excluded from the + // generic Sponsor section there. On tfSponsorshipEnd the spec requires + // Sponsor to be omitted for that purpose, so if it's present it can + // only be genuine outer co-sponsorship. + const isNewSponsorField = + data.tx.TransactionType === 'SponsorshipTransfer' && + getOperation(data.tx.Flags || 0) !== 'end' + if (isNewSponsorField) return null if (!data.tx.Sponsor) return null const scopes = getSponsorScopes(data.tx.SponsorFlags) return ( diff --git a/src/containers/Transactions/SimpleTab.tsx b/src/containers/Transactions/SimpleTab.tsx index df92a9b4a..6cd599b27 100644 --- a/src/containers/Transactions/SimpleTab.tsx +++ b/src/containers/Transactions/SimpleTab.tsx @@ -8,6 +8,7 @@ import { Simple } from './Simple' import { useLanguage } from '../shared/hooks' import { RouteLink } from '../shared/routing' import { CURRENCY_OPTIONS, XRP_BASE } from '../shared/transactionUtils' +import { getOperation } from '../shared/components/Transaction/SponsorshipTransfer/parser' import { SimpleRow } from '../shared/components/Transaction/SimpleRow' import '../shared/css/simpleTab.scss' import './simpleTab.scss' @@ -96,6 +97,17 @@ export const SimpleTab: FC<{ data: any; width: number }> = ({ ) : 0 + // On create/reassign, SponsorshipTransfer reuses the common Sponsor field + // for its own "new sponsor of the target object" meaning, not for + // co-sponsoring this outer transaction, so it's excluded from the generic + // Sponsor row there. On tfSponsorshipEnd the spec requires Sponsor to be + // omitted for that purpose, so if it's present it can only be genuine + // outer co-sponsorship. + const isNewSponsorField = + processed.tx.TransactionType === 'SponsorshipTransfer' && + getOperation(processed.tx.Flags || 0) !== 'end' + const sponsor = isNewSponsorField ? undefined : processed.tx.Sponsor + const rowIndex = renderRowIndex( time, ledgerIndex, @@ -105,7 +117,7 @@ export const SimpleTab: FC<{ data: any; width: number }> = ({ processed.tx.Sequence, processed.tx.TicketSequence, !!processed.tx.EmitDetails, - processed.tx.Sponsor, + sponsor, ) return ( diff --git a/src/containers/Transactions/test/DetailTab.test.tsx b/src/containers/Transactions/test/DetailTab.test.tsx index fb678634b..98ab0012a 100644 --- a/src/containers/Transactions/test/DetailTab.test.tsx +++ b/src/containers/Transactions/test/DetailTab.test.tsx @@ -7,6 +7,7 @@ import FailedTransaction from '../../shared/components/Transaction/SignerListSet import HookPayment from './mock_data/HookPayment.json' import EmittedPayment from './mock_data/EmittedPayment.json' import TrustSet from './mock_data/TrustSet.json' +import SponsorshipTransferCreate from '../../shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferCreate.json' import { DetailTab } from '../DetailTab' import i18n from '../../../i18n/testConfigEnglish' import { convertHexToString } from '../../../rippled/lib/utils' @@ -151,6 +152,28 @@ describe('DetailTab container', () => { expect(detailLines?.[3]).toHaveTextContent('Emitted 0 transactions') }) + it('renders a Sponsor section for a co-sponsored transaction', () => { + const sponsoredTransaction = { + ...Transaction, + tx: { + ...Transaction.tx, + Sponsor: 'rSponsor1111111111111111111111111', + SponsorFlags: 1, + }, + } + const { container } = renderDetailTab(sponsoredTransaction) + expect( + container.querySelector('[data-testid="sponsor-section"]'), + ).toBeInTheDocument() + }) + + it('does not render a Sponsor section for SponsorshipTransfer, which reuses the field for its own meaning', () => { + const { container } = renderDetailTab(SponsorshipTransferCreate) + expect( + container.querySelector('[data-testid="sponsor-section"]'), + ).not.toBeInTheDocument() + }) + it('renders flags', () => { const { container } = renderDetailTab(TrustSet) const expectedFlags = new Set([ diff --git a/src/containers/Transactions/test/SimpleTab.test.tsx b/src/containers/Transactions/test/SimpleTab.test.tsx index 05f22a133..2efd9a1ac 100644 --- a/src/containers/Transactions/test/SimpleTab.test.tsx +++ b/src/containers/Transactions/test/SimpleTab.test.tsx @@ -6,6 +6,8 @@ import { QueryClientProvider } from 'react-query' import EnableAmendment from './mock_data/EnableAmendment.json' import Payment from '../../shared/components/Transaction/Payment/test/mock_data/Payment.json' import DelegatePayment from './mock_data/DelegatePayment.json' +import SponsorshipTransferCreate from '../../shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferCreate.json' +import SponsorshipTransferEnd from '../../shared/components/Transaction/SponsorshipTransfer/test/mock_data/SponsorshipTransferEnd.json' import { SimpleTab } from '../SimpleTab' import summarize from '../../../rippled/lib/txSummary' import i18n from '../../../i18n/testConfig' @@ -79,4 +81,45 @@ describe('SimpleTab container', () => { expectSimpleRowText(container, 'sequence', '2947132') expectSimpleRowText(container, 'tx-cost', '\uE9000.000001') }) + + it('renders a Sponsor row for a co-sponsored transaction', () => { + const sponsoredPayment = { + ...Payment, + tx: { + ...Payment.tx, + Sponsor: 'rSponsor1111111111111111111111111', + SponsorFlags: 1, + }, + } + const { container } = renderSimpleTab(sponsoredPayment) + expectSimpleRowText( + container, + 'sponsor', + 'rSponsor1111111111111111111111111', + ) + }) + + it('does not render a Sponsor row for SponsorshipTransfer create/reassign, which reuses the field for its own meaning', () => { + const { container } = renderSimpleTab(SponsorshipTransferCreate) + expect( + container.querySelector('[data-testid="sponsor"]'), + ).not.toBeInTheDocument() + }) + + it('renders a Sponsor row for SponsorshipTransfer end, where the field is free for genuine co-sponsorship', () => { + const coSponsoredEnd = { + ...SponsorshipTransferEnd, + tx: { + ...SponsorshipTransferEnd.tx, + Sponsor: 'rSponsor1111111111111111111111111', + SponsorFlags: 1, + }, + } + const { container } = renderSimpleTab(coSponsoredEnd) + expectSimpleRowText( + container, + 'sponsor', + 'rSponsor1111111111111111111111111', + ) + }) }) diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts b/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts index db2db1665..88dfda2e2 100644 --- a/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts @@ -6,7 +6,9 @@ const TF_REASSIGN = 0x00040000 export type SponsorshipTransferOperation = 'create' | 'reassign' | 'end' -function getOperation(flags: number): SponsorshipTransferOperation | undefined { +export function getOperation( + flags: number, +): SponsorshipTransferOperation | undefined { if (flags & TF_CREATE) return 'create' if (flags & TF_REASSIGN) return 'reassign' if (flags & TF_END) return 'end' From 7f40865d1d0d7eed53aab84867a883bcf90bfc24 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Mon, 31 Aug 2026 17:01:31 -0700 Subject: [PATCH 10/10] Upgrade xrpl to 5.1.0 and use its official Sponsorship types xrpl@5.1.0 now ships real SponsorshipSet/SponsorshipTransfer types matching XLS-68, so the hand-rolled types.ts files in each of those transaction folders are removed in favor of importing directly from 'xrpl', matching the convention used by every other transaction type in this codebase (e.g. EscrowCreate). Explorer only ever uses xrpl.js for type-only imports (never Wallet or Client), so the 5.0.0 breaking changes around signing defaults and connection error handling don't apply here. Flags is now typed as `number | GlobalFlagsInterface` on BaseTransaction, so both parsers narrow it defensively (`typeof tx.Flags === 'number'`) before doing bitwise flag checks, matching the existing pattern in PaymentChannelClaim's parser. --- package-lock.json | 530 +++++------------- package.json | 2 +- .../SponsorshipSet/Description.tsx | 2 +- .../Transaction/SponsorshipSet/parser.ts | 4 +- .../Transaction/SponsorshipSet/types.ts | 9 - .../SponsorshipTransfer/Description.tsx | 2 +- .../Transaction/SponsorshipTransfer/parser.ts | 4 +- .../Transaction/SponsorshipTransfer/types.ts | 8 - 8 files changed, 151 insertions(+), 410 deletions(-) delete mode 100644 src/containers/shared/components/Transaction/SponsorshipSet/types.ts delete mode 100644 src/containers/shared/components/Transaction/SponsorshipTransfer/types.ts diff --git a/package-lock.json b/package-lock.json index 9ea2d4cdc..2e3f60c25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -101,7 +101,7 @@ "ts-jest": "^29.4.1", "ts-node": "^10.9.2", "typescript": "^5.9.3", - "xrpl": "^4.5.0" + "xrpl": "^5.1.0" }, "engines": { "node": ">=22.0.0 <23", @@ -162,6 +162,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -786,6 +787,7 @@ "integrity": "sha512-D+OrJumc9McXNEBI/JmFnc/0uCM2/Y3PEBG3gfV3QIYkKv5pvnpzFrl1kYCrcHJP8nOeFB/SHi1IHz29pNGuew==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, @@ -1711,6 +1713,7 @@ "integrity": "sha512-61bxqhiRfAACulXSLd/GxqmAedUSrRZIu/cbaT18T1CetkTmtDN15it7i80ru4DVqRK1WMxQhXs+Lf9kajm5Ow==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.27.3", "@babel/helper-module-imports": "^7.28.6", @@ -2240,8 +2243,7 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@cacheable/memory": { "version": "2.0.8", @@ -2279,6 +2281,7 @@ "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } @@ -2412,6 +2415,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -2459,6 +2463,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -3171,7 +3176,6 @@ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -3190,7 +3194,6 @@ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3204,7 +3207,6 @@ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.2.2" }, @@ -3248,7 +3250,6 @@ "integrity": "sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -3267,7 +3268,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -3281,7 +3281,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -3300,8 +3299,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@jest/console/node_modules/jest-util": { "version": "30.3.0", @@ -3309,7 +3307,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -3328,7 +3325,6 @@ "integrity": "sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/console": "30.3.0", "@jest/pattern": "30.0.1", @@ -3376,7 +3372,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -3390,7 +3385,6 @@ "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.27.4", "@jest/types": "30.3.0", @@ -3417,7 +3411,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -3436,8 +3429,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@jest/core/node_modules/ansi-styles": { "version": "5.2.0", @@ -3445,7 +3437,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -3459,7 +3450,6 @@ "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "workspaces": [ "test/babel-8" ], @@ -3480,7 +3470,6 @@ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", @@ -3498,7 +3487,6 @@ "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -3524,7 +3512,6 @@ "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -3535,7 +3522,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -3554,7 +3540,6 @@ "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", @@ -3569,8 +3554,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@jest/core/node_modules/semver": { "version": "7.7.4", @@ -3578,7 +3562,6 @@ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -3592,7 +3575,6 @@ "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" @@ -3748,7 +3730,6 @@ "integrity": "sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "expect": "30.3.0", "jest-snapshot": "30.3.0" @@ -3856,7 +3837,6 @@ "integrity": "sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "30.3.0", "@jest/expect": "30.3.0", @@ -3873,7 +3853,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -3887,7 +3866,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -3906,8 +3884,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@jest/pattern": { "version": "30.0.1", @@ -3937,7 +3914,6 @@ "integrity": "sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "30.3.0", @@ -3981,7 +3957,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -3995,7 +3970,6 @@ "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.27.4", "@jest/types": "30.3.0", @@ -4022,7 +3996,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -4041,8 +4014,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@jest/reporters/node_modules/babel-plugin-istanbul": { "version": "7.0.1", @@ -4050,7 +4022,6 @@ "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "workspaces": [ "test/babel-8" ], @@ -4071,7 +4042,6 @@ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", @@ -4089,7 +4059,6 @@ "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -4115,7 +4084,6 @@ "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -4126,7 +4094,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -4145,7 +4112,6 @@ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -4159,7 +4125,6 @@ "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" @@ -4187,7 +4152,6 @@ "integrity": "sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "chalk": "^4.1.2", @@ -4204,7 +4168,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -4218,7 +4181,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -4237,8 +4199,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@jest/source-map": { "version": "30.0.1", @@ -4246,7 +4207,6 @@ "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "callsites": "^3.1.0", @@ -4262,7 +4222,6 @@ "integrity": "sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/console": "30.3.0", "@jest/types": "30.3.0", @@ -4279,7 +4238,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -4293,7 +4251,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -4312,8 +4269,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@jest/test-sequencer": { "version": "30.3.0", @@ -4321,7 +4277,6 @@ "integrity": "sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/test-result": "30.3.0", "graceful-fs": "^4.2.11", @@ -4338,7 +4293,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -4352,7 +4306,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -4371,8 +4324,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@jest/test-sequencer/node_modules/jest-haste-map": { "version": "30.3.0", @@ -4380,7 +4332,6 @@ "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -4406,7 +4357,6 @@ "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -4417,7 +4367,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -4561,29 +4510,28 @@ } }, "node_modules/@noble/curves": { - "version": "1.9.7", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", - "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "version": "2.4.0", + "resolved": "https://artifactory.ops.ripple.com/artifactory/api/npm/ripple-npm/@noble/curves/-/curves-2.4.0.tgz", + "integrity": "sha512-P4/62zrgfH33CneE3Dn4WhJVA22YUU0eR51wKIan4NVRvwsA0YnPTwWGpNbpuacSujmSFLvyzpyuR30+fbq2Ew==", "dev": true, "license": "MIT", "dependencies": { - "@noble/hashes": "1.8.0" + "@noble/hashes": "2.4.0" }, "engines": { - "node": "^14.21.3 || >=16" + "node": ">= 20.19.0" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "dev": true, + "version": "2.4.0", + "resolved": "https://artifactory.ops.ripple.com/artifactory/api/npm/ripple-npm/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", "license": "MIT", "engines": { - "node": "^14.21.3 || >=16" + "node": ">= 20.19.0" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -4941,7 +4889,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=14" } @@ -5349,39 +5296,37 @@ "license": "MIT" }, "node_modules/@scure/base": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", - "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", - "dev": true, + "version": "2.4.0", + "resolved": "https://artifactory.ops.ripple.com/artifactory/api/npm/ripple-npm/@scure/base/-/base-2.4.0.tgz", + "integrity": "sha512-thZ1TuJwFwBblOhgsjDKvvGirBxNp+wSvY/DR6tJBJOTDhdAAcHJ8Vbr2eFnqaxeca4+t0i9KBf+uHYGWwZORg==", "license": "MIT", "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip32": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", - "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "version": "2.4.0", + "resolved": "https://artifactory.ops.ripple.com/artifactory/api/npm/ripple-npm/@scure/bip32/-/bip32-2.4.0.tgz", + "integrity": "sha512-i3DS0CptAocyvqE4n3SUkpzeQK4vJMFwWLofTwRiiKo2aWojBOfyMCgfKw9HVpO6fSY5AK86sHS/Uzn8kK9Few==", "dev": true, "license": "MIT", "dependencies": { - "@noble/curves": "~1.9.0", - "@noble/hashes": "~1.8.0", - "@scure/base": "~1.2.5" + "@noble/curves": "2.4.0", + "@noble/hashes": "2.4.0", + "@scure/base": "2.4.0" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/@scure/bip39": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", - "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "version": "2.4.0", + "resolved": "https://artifactory.ops.ripple.com/artifactory/api/npm/ripple-npm/@scure/bip39/-/bip39-2.4.0.tgz", + "integrity": "sha512-82dxFbZUYboyOf0AXiydsQrFQ5Q4h9mX+O2UkE91ROYmsc0BKMGZLwDmy96Jpa2+vrtoxomjUhy1RPIgH/r2nA==", "dev": true, "license": "MIT", "dependencies": { - "@noble/hashes": "~1.8.0", - "@scure/base": "~1.2.5" + "@noble/hashes": "2.4.0" }, "funding": { "url": "https://paulmillr.com/funding/" @@ -6120,6 +6065,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -6144,6 +6090,7 @@ "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -6410,6 +6357,7 @@ "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.58.2", "@typescript-eslint/types": "8.58.2", @@ -6931,16 +6879,14 @@ "node": ">=18.0.0" } }, - "node_modules/@xrplf/isomorphic/node_modules/@noble/hashes": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.3.0.tgz", - "integrity": "sha512-oN+QwyX7VSHotibwubG3kpzbwKrfnyR6OOO+3Nk/53ADL7FmgHHz4TgrbaYKvvOw09u6QTx0oiH1cNCIOuN0CQ==", - "license": "MIT", + "node_modules/@xrplf/mpt-crypto": { + "version": "0.1.1", + "resolved": "https://artifactory.ops.ripple.com/artifactory/api/npm/ripple-npm/@xrplf/mpt-crypto/-/mpt-crypto-0.1.1.tgz", + "integrity": "sha512-Wv9wbk60DRGWvI+B9DITwfWygCWDfEjv8yWxiLJyDf/G8UPkfhVlUKIflF9nMaBFMygPRkXrpO1jNqTj6/vJow==", + "dev": true, + "license": "ISC", "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" + "node": ">= 18" } }, "node_modules/@xrplf/prettier-config": { @@ -6951,14 +6897,14 @@ "license": "ISC" }, "node_modules/@xrplf/secret-numbers": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@xrplf/secret-numbers/-/secret-numbers-2.0.0.tgz", - "integrity": "sha512-z3AOibRTE9E8MbjgzxqMpG1RNaBhQ1jnfhNCa1cGf2reZUJzPMYs4TggQTc7j8+0WyV3cr7y/U8Oz99SXIkN5Q==", + "version": "3.0.0", + "resolved": "https://artifactory.ops.ripple.com/artifactory/api/npm/ripple-npm/@xrplf/secret-numbers/-/secret-numbers-3.0.0.tgz", + "integrity": "sha512-qpGhAZXv5noMDjCtfzq5NK0y5rrdwTVjKhhPcAYSE+a/gogBOgqdpCKyieprVVPCnmVmJnGeRoZKBAqpCGegsA==", "dev": true, "license": "ISC", "dependencies": { - "@xrplf/isomorphic": "^1.0.1", - "ripple-keypairs": "^2.0.0" + "@xrplf/isomorphic": "^1.0.2", + "ripple-keypairs": "^3.0.0" } }, "node_modules/accepts": { @@ -6988,6 +6934,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -7050,7 +6997,6 @@ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "type-fest": "^0.21.3" }, @@ -7067,7 +7013,6 @@ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=10" }, @@ -7496,6 +7441,7 @@ "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", @@ -7752,14 +7698,10 @@ } }, "node_modules/bignumber.js": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } + "version": "10.0.2", + "resolved": "https://artifactory.ops.ripple.com/artifactory/api/npm/ripple-npm/bignumber.js/-/bignumber.js-10.0.2.tgz", + "integrity": "sha512-E8Wp9O06QA6lneJ4aRUXKYf/1GIomqUEmUMwtIOMtDxf1U52ffJY+y7JBk/8wRafA8qOIqLnXQGqonYXZdBnFQ==", + "license": "MIT" }, "node_modules/binary-extensions": { "version": "2.3.0", @@ -7879,6 +7821,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -8141,7 +8084,6 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -8204,8 +8146,7 @@ "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/classnames": { "version": "2.5.1", @@ -8327,7 +8268,6 @@ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -8342,8 +8282,7 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/cliui/node_modules/is-fullwidth-code-point": { "version": "3.0.0", @@ -8351,7 +8290,6 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -8362,7 +8300,6 @@ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -8378,7 +8315,6 @@ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8406,7 +8342,6 @@ "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "iojs": ">= 1.0.0", "node": ">= 0.12.0" @@ -8417,8 +8352,7 @@ "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/color-convert": { "version": "2.0.1", @@ -8700,6 +8634,7 @@ "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "mdn-data": "2.27.1", "source-map-js": "^1.2.1" @@ -9107,6 +9042,7 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", + "peer": true, "engines": { "node": ">=12" } @@ -9300,7 +9236,6 @@ "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", "dev": true, "license": "MIT", - "peer": true, "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, @@ -9356,7 +9291,6 @@ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -9459,7 +9393,6 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -9668,8 +9601,7 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/ee-first": { "version": "1.1.1", @@ -9704,7 +9636,6 @@ "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -10082,6 +10013,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -10180,6 +10112,7 @@ "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -10222,6 +10155,7 @@ "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", "@typescript-eslint/scope-manager": "5.62.0", @@ -10257,6 +10191,7 @@ "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "5.62.0", "@typescript-eslint/types": "5.62.0", @@ -10653,6 +10588,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -10741,6 +10677,7 @@ "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", @@ -10843,6 +10780,7 @@ "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -10876,6 +10814,7 @@ "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -11456,7 +11395,6 @@ "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -11480,8 +11418,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/exit-x": { "version": "0.2.2", @@ -11489,7 +11426,6 @@ "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8.0" } @@ -11938,7 +11874,6 @@ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -12165,7 +12100,6 @@ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -12211,7 +12145,6 @@ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -12245,8 +12178,7 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/glob/node_modules/brace-expansion": { "version": "2.1.0", @@ -12254,7 +12186,6 @@ "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -12265,7 +12196,6 @@ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^2.0.2" }, @@ -12625,8 +12555,7 @@ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/html-minifier-terser": { "version": "6.1.0", @@ -12732,7 +12661,6 @@ "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=10.17.0" } @@ -12756,6 +12684,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.23.2" } @@ -12865,7 +12794,6 @@ "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -13229,7 +13157,6 @@ "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -13412,7 +13339,6 @@ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" }, @@ -13582,7 +13508,6 @@ "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", @@ -13598,7 +13523,6 @@ "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.23", "debug": "^4.1.1", @@ -13614,7 +13538,6 @@ "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -13647,7 +13570,6 @@ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, "license": "BlueOak-1.0.0", - "peer": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -13681,7 +13603,6 @@ "integrity": "sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.3.0", "@jest/types": "30.3.0", @@ -13709,7 +13630,6 @@ "integrity": "sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "execa": "^5.1.1", "jest-util": "30.3.0", @@ -13725,7 +13645,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -13739,7 +13658,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -13758,8 +13676,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-changed-files/node_modules/jest-util": { "version": "30.3.0", @@ -13767,7 +13684,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -13786,7 +13702,6 @@ "integrity": "sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "30.3.0", "@jest/expect": "30.3.0", @@ -13819,7 +13734,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -13833,7 +13747,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -13852,8 +13765,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-circus/node_modules/ansi-styles": { "version": "5.2.0", @@ -13861,7 +13773,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -13875,7 +13786,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -13894,7 +13804,6 @@ "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", @@ -13909,8 +13818,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-cli": { "version": "30.3.0", @@ -13918,7 +13826,6 @@ "integrity": "sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "30.3.0", "@jest/test-result": "30.3.0", @@ -13952,7 +13859,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -13966,7 +13872,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -13985,8 +13890,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-cli/node_modules/jest-util": { "version": "30.3.0", @@ -13994,7 +13898,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -14013,7 +13916,6 @@ "integrity": "sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.27.4", "@jest/get-type": "30.1.0", @@ -14065,7 +13967,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -14079,7 +13980,6 @@ "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.27.4", "@jest/types": "30.3.0", @@ -14106,7 +14006,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -14125,8 +14024,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-config/node_modules/ansi-styles": { "version": "5.2.0", @@ -14134,7 +14032,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -14148,7 +14045,6 @@ "integrity": "sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/transform": "30.3.0", "@types/babel__core": "^7.20.5", @@ -14171,7 +14067,6 @@ "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "workspaces": [ "test/babel-8" ], @@ -14192,7 +14087,6 @@ "integrity": "sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/babel__core": "^7.20.5" }, @@ -14206,7 +14100,6 @@ "integrity": "sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "babel-plugin-jest-hoist": "30.3.0", "babel-preset-current-node-syntax": "^1.2.0" @@ -14224,7 +14117,6 @@ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", @@ -14242,7 +14134,6 @@ "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -14268,7 +14159,6 @@ "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -14279,7 +14169,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -14298,7 +14187,6 @@ "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", @@ -14313,8 +14201,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-config/node_modules/semver": { "version": "7.7.4", @@ -14322,7 +14209,6 @@ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -14336,7 +14222,6 @@ "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" @@ -14422,7 +14307,6 @@ "integrity": "sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "detect-newline": "^3.1.0" }, @@ -14436,7 +14320,6 @@ "integrity": "sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/get-type": "30.1.0", "@jest/types": "30.3.0", @@ -14454,7 +14337,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -14468,7 +14350,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -14487,8 +14368,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-each/node_modules/ansi-styles": { "version": "5.2.0", @@ -14496,7 +14376,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -14510,7 +14389,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -14529,7 +14407,6 @@ "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", @@ -14544,8 +14421,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-environment-jsdom": { "version": "30.3.0", @@ -14575,7 +14451,6 @@ "integrity": "sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "30.3.0", "@jest/fake-timers": "30.3.0", @@ -14595,7 +14470,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -14609,7 +14483,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -14628,8 +14501,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-environment-node/node_modules/jest-util": { "version": "30.3.0", @@ -14637,7 +14509,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -14724,7 +14595,6 @@ "integrity": "sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/get-type": "30.1.0", "pretty-format": "30.3.0" @@ -14739,7 +14609,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -14752,8 +14621,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-leak-detector/node_modules/ansi-styles": { "version": "5.2.0", @@ -14761,7 +14629,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -14775,7 +14642,6 @@ "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", @@ -14790,8 +14656,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-matcher-utils": { "version": "30.3.0", @@ -15025,7 +14890,6 @@ "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" }, @@ -15054,7 +14918,6 @@ "integrity": "sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chalk": "^4.1.2", "graceful-fs": "^4.2.11", @@ -15075,7 +14938,6 @@ "integrity": "sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "jest-regex-util": "30.0.1", "jest-snapshot": "30.3.0" @@ -15090,7 +14952,6 @@ "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -15101,7 +14962,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -15115,7 +14975,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -15134,8 +14993,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-resolve/node_modules/jest-haste-map": { "version": "30.3.0", @@ -15143,7 +15001,6 @@ "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -15169,7 +15026,6 @@ "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -15180,7 +15036,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -15199,7 +15054,6 @@ "integrity": "sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/console": "30.3.0", "@jest/environment": "30.3.0", @@ -15234,7 +15088,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -15248,7 +15101,6 @@ "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.27.4", "@jest/types": "30.3.0", @@ -15275,7 +15127,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -15294,8 +15145,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-runner/node_modules/babel-plugin-istanbul": { "version": "7.0.1", @@ -15303,7 +15153,6 @@ "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "workspaces": [ "test/babel-8" ], @@ -15324,7 +15173,6 @@ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", @@ -15342,7 +15190,6 @@ "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -15368,7 +15215,6 @@ "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -15379,7 +15225,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -15398,7 +15243,6 @@ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -15412,7 +15256,6 @@ "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" @@ -15427,7 +15270,6 @@ "integrity": "sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/environment": "30.3.0", "@jest/fake-timers": "30.3.0", @@ -15462,7 +15304,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -15476,7 +15317,6 @@ "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.27.4", "@jest/types": "30.3.0", @@ -15503,7 +15343,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -15522,8 +15361,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-runtime/node_modules/babel-plugin-istanbul": { "version": "7.0.1", @@ -15531,7 +15369,6 @@ "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "workspaces": [ "test/babel-8" ], @@ -15552,7 +15389,6 @@ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", @@ -15570,7 +15406,6 @@ "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -15596,7 +15431,6 @@ "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -15607,7 +15441,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -15626,7 +15459,6 @@ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -15640,7 +15472,6 @@ "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" @@ -15655,7 +15486,6 @@ "integrity": "sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", @@ -15689,7 +15519,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -15703,7 +15532,6 @@ "integrity": "sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.27.4", "@jest/types": "30.3.0", @@ -15730,7 +15558,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -15749,8 +15576,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-snapshot/node_modules/ansi-styles": { "version": "5.2.0", @@ -15758,7 +15584,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -15772,7 +15597,6 @@ "integrity": "sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "workspaces": [ "test/babel-8" ], @@ -15793,7 +15617,6 @@ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", @@ -15811,7 +15634,6 @@ "integrity": "sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -15837,7 +15659,6 @@ "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -15848,7 +15669,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -15867,7 +15687,6 @@ "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", @@ -15882,8 +15701,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-snapshot/node_modules/semver": { "version": "7.7.4", @@ -15891,7 +15709,6 @@ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -15905,7 +15722,6 @@ "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" @@ -15967,7 +15783,6 @@ "integrity": "sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/get-type": "30.1.0", "@jest/types": "30.3.0", @@ -15986,7 +15801,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -16000,7 +15814,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -16019,8 +15832,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-validate/node_modules/ansi-styles": { "version": "5.2.0", @@ -16028,7 +15840,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -16042,7 +15853,6 @@ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -16056,7 +15866,6 @@ "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", @@ -16071,8 +15880,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-watcher": { "version": "30.3.0", @@ -16080,7 +15888,6 @@ "integrity": "sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/test-result": "30.3.0", "@jest/types": "30.3.0", @@ -16101,7 +15908,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -16115,7 +15921,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -16134,8 +15939,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-watcher/node_modules/jest-util": { "version": "30.3.0", @@ -16143,7 +15947,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -16224,7 +16027,6 @@ "integrity": "sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "@ungap/structured-clone": "^1.3.0", @@ -16242,7 +16044,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -16256,7 +16057,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -16275,8 +16075,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/jest-worker/node_modules/jest-util": { "version": "30.3.0", @@ -16284,7 +16083,6 @@ "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/types": "30.3.0", "@types/node": "*", @@ -16303,7 +16101,6 @@ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -16320,7 +16117,6 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -16334,7 +16130,6 @@ "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -16353,8 +16148,7 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/js-sha3": { "version": "0.8.0", @@ -16387,6 +16181,7 @@ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "license": "MIT", + "peer": true, "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", @@ -16560,7 +16355,6 @@ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -17178,7 +16972,6 @@ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "semver": "^7.5.3" }, @@ -17195,7 +16988,6 @@ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -17401,7 +17193,6 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -17461,7 +17252,6 @@ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "dev": true, "license": "BlueOak-1.0.0", - "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -18134,7 +17924,6 @@ "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "path-key": "^3.0.0" }, @@ -18337,7 +18126,6 @@ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -18461,8 +18249,7 @@ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, - "license": "BlueOak-1.0.0", - "peer": true + "license": "BlueOak-1.0.0" }, "node_modules/param-case": { "version": "3.0.4", @@ -18577,7 +18364,6 @@ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "license": "BlueOak-1.0.0", - "peer": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -18594,8 +18380,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/path-to-regexp": { "version": "0.1.13", @@ -18675,7 +18460,6 @@ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "find-up": "^4.0.0" }, @@ -18711,6 +18495,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -18794,6 +18579,7 @@ "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -18834,6 +18620,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -18955,8 +18742,7 @@ "url": "https://opencollective.com/fast-check" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/qified": { "version": "0.9.1", @@ -19042,6 +18828,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -19054,6 +18841,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -19520,7 +19308,6 @@ "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "resolve-from": "^5.0.0" }, @@ -19676,19 +19463,10 @@ "node": ">= 18" } }, - "node_modules/ripple-address-codec/node_modules/@scure/base": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@scure/base/-/base-2.3.0.tgz", - "integrity": "sha512-NsG6Y03tY6R5BUis4FdVtHVkur0U6FOzskgs9ZXNl78CUc9fkZ78HmENUle1nSOkCasDmbubmWD9qwB7mm4PZA==", - "license": "MIT", - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, "node_modules/ripple-binary-codec": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/ripple-binary-codec/-/ripple-binary-codec-2.9.0.tgz", - "integrity": "sha512-DOv4CPwm2B5mjAJNHE4tfes7tIqefi85iun8BskS7/i1PvLIwVydHIbTV5hbQ7eT69Zg4K5dtO1CzfuvQVAYvQ==", + "version": "2.10.0", + "resolved": "https://artifactory.ops.ripple.com/artifactory/api/npm/ripple-npm/ripple-binary-codec/-/ripple-binary-codec-2.10.0.tgz", + "integrity": "sha512-H4pBm2WdOMmVnA0HGUdWAShmUFdmqavWAsi2WlMCzOknc1XjrZssa/RxEyziaPww8MXe1OpgGU7uCs8G2nYe/A==", "license": "ISC", "dependencies": { "@xrplf/isomorphic": "^1.0.2", @@ -19699,25 +19477,19 @@ "node": ">= 18" } }, - "node_modules/ripple-binary-codec/node_modules/bignumber.js": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-10.0.2.tgz", - "integrity": "sha512-E8Wp9O06QA6lneJ4aRUXKYf/1GIomqUEmUMwtIOMtDxf1U52ffJY+y7JBk/8wRafA8qOIqLnXQGqonYXZdBnFQ==", - "license": "MIT" - }, "node_modules/ripple-keypairs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ripple-keypairs/-/ripple-keypairs-2.0.0.tgz", - "integrity": "sha512-b5rfL2EZiffmklqZk1W+dvSy97v3V/C7936WxCCgDynaGPp7GE6R2XO7EU9O2LlM/z95rj870IylYnOQs+1Rag==", + "version": "3.0.0", + "resolved": "https://artifactory.ops.ripple.com/artifactory/api/npm/ripple-npm/ripple-keypairs/-/ripple-keypairs-3.0.0.tgz", + "integrity": "sha512-lE69pD0E8hFNCqZoVXRyY45Yi8Ku+Qw7Rf1qRwPj4nOi34vp9NAuwzfiJH1IwXGWNCfEkwVfctG99CPTEoUf+g==", "dev": true, "license": "ISC", "dependencies": { - "@noble/curves": "^1.0.0", - "@xrplf/isomorphic": "^1.0.0", - "ripple-address-codec": "^5.0.0" + "@noble/curves": "^2.0.1", + "@xrplf/isomorphic": "^1.0.2", + "ripple-address-codec": "^5.0.1" }, "engines": { - "node": ">= 16" + "node": ">= 18" } }, "node_modules/robust-predicates": { @@ -19731,6 +19503,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -19898,6 +19671,7 @@ "integrity": "sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "chokidar": "^4.0.0", "immutable": "^5.1.5", @@ -20493,7 +20267,6 @@ "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -20505,7 +20278,6 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -20623,7 +20395,6 @@ "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" @@ -20645,7 +20416,6 @@ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -20665,7 +20435,6 @@ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -20680,8 +20449,7 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", @@ -20689,7 +20457,6 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -20700,7 +20467,6 @@ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -20714,7 +20480,6 @@ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.2.2" }, @@ -20877,7 +20642,6 @@ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -20891,7 +20655,6 @@ "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -20902,7 +20665,6 @@ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -20949,6 +20711,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@csstools/css-calc": "^3.1.1", "@csstools/css-parser-algorithms": "^4.0.0", @@ -21180,6 +20943,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" }, @@ -21203,6 +20967,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=20.19.0" } @@ -21977,6 +21742,7 @@ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -22247,6 +22013,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -22393,6 +22160,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "napi-postinstall": "^0.3.0" }, @@ -22531,7 +22299,6 @@ "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", @@ -22588,6 +22355,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -23462,7 +23230,6 @@ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -23482,7 +23249,6 @@ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -23500,8 +23266,7 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", @@ -23509,7 +23274,6 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -23520,7 +23284,6 @@ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -23536,7 +23299,6 @@ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -23550,7 +23312,6 @@ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -23564,7 +23325,6 @@ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.2.2" }, @@ -23639,25 +23399,26 @@ "license": "MIT" }, "node_modules/xrpl": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/xrpl/-/xrpl-4.6.0.tgz", - "integrity": "sha512-0nXZfqDHRJ6bsDv1WtA9MdCYalMtXuxVa9mtLdqT3xypRKf2LwT5DbuGL/kHcVfuqk3B+ly+SFARlrnX+LHtRQ==", + "version": "5.1.0", + "resolved": "https://artifactory.ops.ripple.com/artifactory/api/npm/ripple-npm/xrpl/-/xrpl-5.1.0.tgz", + "integrity": "sha512-wYTiyrFqV22kwSX2lrUCD10Ns8Zyv2zz8rZ2Pi0KOVm5Sc7/8m7rrca2dAZy1vGhOt36tkvWyX4aC2SQveyT9w==", "dev": true, "license": "ISC", "dependencies": { - "@scure/bip32": "^1.3.1", - "@scure/bip39": "^1.2.1", - "@xrplf/isomorphic": "^1.0.1", - "@xrplf/secret-numbers": "^2.0.0", - "bignumber.js": "^9.0.0", + "@scure/bip32": "^2.0.1", + "@scure/bip39": "^2.0.1", + "@xrplf/isomorphic": "^1.0.2", + "@xrplf/mpt-crypto": "^0.1.1", + "@xrplf/secret-numbers": "^3.0.0", + "bignumber.js": "^10.0.2", "eventemitter3": "^5.0.1", "fast-json-stable-stringify": "^2.1.0", - "ripple-address-codec": "^5.0.0", - "ripple-binary-codec": "^2.7.0", - "ripple-keypairs": "^2.0.0" + "ripple-address-codec": "^5.0.1", + "ripple-binary-codec": "^2.10.0", + "ripple-keypairs": "^3.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.19.0" } }, "node_modules/xrpl-client": { @@ -23702,6 +23463,7 @@ "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "devOptional": true, "license": "ISC", + "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -23718,7 +23480,6 @@ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -23747,8 +23508,7 @@ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/yargs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", @@ -23756,7 +23516,6 @@ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -23767,7 +23526,6 @@ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", diff --git a/package.json b/package.json index cc8d56238..3c11e9c5e 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "ts-jest": "^29.4.1", "ts-node": "^10.9.2", "typescript": "^5.9.3", - "xrpl": "^4.5.0" + "xrpl": "^5.1.0" }, "resolutions": { "jest-environment-jsdom": "29.3.1", diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/Description.tsx b/src/containers/shared/components/Transaction/SponsorshipSet/Description.tsx index a47ec7581..f40b9baad 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/Description.tsx +++ b/src/containers/shared/components/Transaction/SponsorshipSet/Description.tsx @@ -1,7 +1,7 @@ import { Trans } from 'react-i18next' +import type { SponsorshipSet } from 'xrpl' import { TransactionDescriptionProps } from '../types' import { Account } from '../../Account' -import { SponsorshipSet } from './types' import { parser } from './parser' function getDescriptionKey( diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts b/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts index 71e23ea8d..148bd85aa 100644 --- a/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts +++ b/src/containers/shared/components/Transaction/SponsorshipSet/parser.ts @@ -1,5 +1,5 @@ +import type { SponsorshipSet } from 'xrpl' import { formatAmount } from '../../../../../rippled/lib/txSummary/formatAmount' -import { SponsorshipSet } from './types' const TF_DELETE_OBJECT = 0x00100000 const TF_SET_REQUIRE_SIGN_FOR_FEE = 0x00010000 @@ -19,7 +19,7 @@ function getSignedDelta(delta: string | undefined) { } export function parser(tx: SponsorshipSet) { - const flags = tx.Flags || 0 + const flags = typeof tx.Flags === 'number' ? tx.Flags : 0 // If CounterpartySponsor is given, this account is the sponsee; if Sponsee // is given, this account is the sponsor. const sponsor = tx.CounterpartySponsor ?? tx.Account diff --git a/src/containers/shared/components/Transaction/SponsorshipSet/types.ts b/src/containers/shared/components/Transaction/SponsorshipSet/types.ts deleted file mode 100644 index 1a26b697c..000000000 --- a/src/containers/shared/components/Transaction/SponsorshipSet/types.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { TransactionCommonFields } from '../types' - -export interface SponsorshipSet extends TransactionCommonFields { - CounterpartySponsor?: string - Sponsee?: string - FeeAmountDelta?: string - MaxFee?: string - RemainingOwnerCountDelta?: number -} diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/Description.tsx b/src/containers/shared/components/Transaction/SponsorshipTransfer/Description.tsx index d084f0efc..4b826eb59 100644 --- a/src/containers/shared/components/Transaction/SponsorshipTransfer/Description.tsx +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/Description.tsx @@ -1,7 +1,7 @@ import { Trans } from 'react-i18next' +import type { SponsorshipTransfer } from 'xrpl' import { TransactionDescriptionProps } from '../types' import { Account } from '../../Account' -import { SponsorshipTransfer } from './types' import { parser } from './parser' export const Description = ({ diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts b/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts index 88dfda2e2..81e96e64c 100644 --- a/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts +++ b/src/containers/shared/components/Transaction/SponsorshipTransfer/parser.ts @@ -1,4 +1,4 @@ -import { SponsorshipTransfer } from './types' +import type { SponsorshipTransfer } from 'xrpl' const TF_END = 0x00010000 const TF_CREATE = 0x00020000 @@ -17,7 +17,7 @@ export function getOperation( export function parser(tx: SponsorshipTransfer) { return { - operation: getOperation(tx.Flags || 0), + operation: getOperation(typeof tx.Flags === 'number' ? tx.Flags : 0), account: tx.Account, objectId: tx.ObjectID, sponsor: tx.Sponsor, diff --git a/src/containers/shared/components/Transaction/SponsorshipTransfer/types.ts b/src/containers/shared/components/Transaction/SponsorshipTransfer/types.ts deleted file mode 100644 index 4f89d8b78..000000000 --- a/src/containers/shared/components/Transaction/SponsorshipTransfer/types.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { TransactionCommonFields } from '../types' - -export interface SponsorshipTransfer extends TransactionCommonFields { - ObjectID?: string - Sponsor?: string - SponsorFlags?: number - Sponsee?: string -}