From 251803ed7e52df7712a3cb556c0808192a153867 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Fri, 11 Sep 2026 13:51:43 -0700 Subject: [PATCH 1/6] Add support for upcoming Single Asset Vault deposit-blocking flags/fields Adds Vault ledger flag decoding (lsfVaultDepositBlocked, lsfVaultOwnerCanBlockDeposit) and a "Deposits Allowed" indicator on the Vault page, new tf* flag entries for VaultCreate/VaultSet/VaultDeposit, and rendering for VaultDelete's new MemoData field, matching the upstream xrpl4j changes for DGE-7974. Also fixes long Vault ID/Owner values being truncated or cut off on the Vault page and across the Vault transaction Simple views, since there's normally enough room to show them in full. Co-Authored-By: Claude Sonnet 5 --- public/locales/en-US/translations.json | 3 + src/containers/Transactions/simpleTab.scss | 3 +- src/containers/Vault/VaultHeader/index.tsx | 42 +++++--- src/containers/Vault/VaultHeader/styles.scss | 12 +++ .../VaultHeader/test/VaultHeader.test.tsx | 95 ++++++++++++++++--- .../components/CopyableText/styles.scss | 3 + .../Transaction/VaultClawback/Simple.tsx | 6 +- .../Transaction/VaultDelete/Description.tsx | 29 ++++-- .../Transaction/VaultDelete/Simple.tsx | 24 ++++- .../test/VaultDeleteDescription.test.tsx | 3 + .../test/VaultDeleteSimple.test.tsx | 1 + .../test/mock_data/VaultDelete.json | 1 + .../Transaction/VaultDeposit/Simple.tsx | 6 +- .../Transaction/VaultSet/Simple.tsx | 6 +- .../Transaction/VaultWithdraw/Simple.tsx | 6 +- src/containers/shared/transactionUtils.ts | 9 ++ 16 files changed, 210 insertions(+), 39 deletions(-) diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index 8c55a6a7f..aeb3ae39b 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -671,6 +671,8 @@ "account_clawbacks_from_vault": " clawbacks from ", "account_clawbacks_from_vault_amount_omitted": " clawbacks all remaining funds from ", "account_deletes_vault": " deleted a vault with ID ", + "vault_delete_memo_data": "It sets the Vault deletion MemoData to ", + "memo_data": "Memo Data", "vault_create_table_detail": "vault for ", "withdraws": "withdraws", "deletes": "deletes", @@ -825,6 +827,7 @@ "check_vault_id": "Please check the vault ID", "get_vault_failed": "Unable to load vault information at this time. Please try again later.", "private_vault": "Private Vault", + "deposits_allowed": "Deposits Allowed", "perm_domain_id": "Permissioned Domain ID", "total_value_locked": "Total Value Locked (TVL)", "shares": "Shares", diff --git a/src/containers/Transactions/simpleTab.scss b/src/containers/Transactions/simpleTab.scss index 9f2681031..f300a5529 100644 --- a/src/containers/Transactions/simpleTab.scss +++ b/src/containers/Transactions/simpleTab.scss @@ -114,7 +114,8 @@ $subdued-color: $black-40; &.condition, &.fulfillment, &.tx, - &.channel { + &.channel, + &.vault-id { width: calc(100% - 120px); color: $subdued-color; font-size: 14px; diff --git a/src/containers/Vault/VaultHeader/index.tsx b/src/containers/Vault/VaultHeader/index.tsx index 0b20da374..d31afdb0c 100644 --- a/src/containers/Vault/VaultHeader/index.tsx +++ b/src/containers/Vault/VaultHeader/index.tsx @@ -10,12 +10,7 @@ import { MPT_ROUTE } from '../../App/routes' import SocketContext from '../../shared/SocketContext' import { getMPTIssuance } from '../../../rippled/lib/rippled' import { parseVaultWebsite } from '../utils' -import { - shortenVaultID, - shortenAccount, - getCurrencySymbol, - isCurrencyExoticSymbol, -} from '../../shared/utils' +import { getCurrencySymbol, isCurrencyExoticSymbol } from '../../shared/utils' import './styles.scss' import { useAnalytics } from '../../shared/analytics' import { parseAmount } from '../../shared/NumberFormattingUtils' @@ -52,6 +47,8 @@ interface Props { // Vault flags from XLS-65d spec const VAULT_FLAGS = { lsfVaultPrivate: 0x00010000, + lsfVaultDepositBlocked: 0x00020000, + lsfVaultOwnerCanBlockDeposit: 0x00040000, } // Withdrawal policy values from XLS-65d spec @@ -146,6 +143,11 @@ export const VaultHeader = ({ data, vaultId, displayCurrency }: Props) => { const isPrivate = flags !== undefined && (flags & VAULT_FLAGS.lsfVaultPrivate) !== 0 + const canBlockDeposit = + flags !== undefined && + (flags & VAULT_FLAGS.lsfVaultOwnerCanBlockDeposit) !== 0 + const isDepositAllowed = + flags === undefined || (flags & VAULT_FLAGS.lsfVaultDepositBlocked) === 0 const decodedData = convertHexToString(vaultDataRaw) const vaultWebsite = parseVaultWebsite(vaultDataRaw) @@ -185,7 +187,7 @@ export const VaultHeader = ({ data, vaultId, displayCurrency }: Props) => { value={ } @@ -193,12 +195,7 @@ export const VaultHeader = ({ data, vaultId, displayCurrency }: Props) => { {owner && ( - } + value={} /> )} { } /> + {canBlockDeposit && ( + + + {t('yes')} + + + {t('no')} + + + } + /> + )} {vaultCredential && ( { ).toBeInTheDocument() }) - it('displays the vault ID with truncation', () => { + it('displays the full vault ID, not a truncated form', () => { const vaultId = 'ABC123DEF456GHI789JKL012' const vaultData = { Owner: 'rTestOwner123', @@ -170,14 +170,12 @@ describe('VaultHeader Component', () => { , ) - // Vault ID should be truncated for display: first 8 chars + "..." + last 6 chars - // This makes long IDs more readable while still being identifiable const vaultIdLabel = screen.getByText('Vault ID') expect(vaultIdLabel).toBeInTheDocument() - // Verify the truncated format: ABC123DE...KL012 (8 chars + ... + 6 chars) - const expectedTruncated = `${vaultId.substring(0, 8)}...${vaultId.substring(vaultId.length - 6)}` - expect(screen.getByText(expectedTruncated)).toBeInTheDocument() + // The full ID should be shown in the details table; there's plenty of + // width available and the value wraps rather than being truncated + expect(screen.getByText(vaultId)).toBeInTheDocument() }) it('displays the owner as a clickable account link', () => { @@ -200,11 +198,8 @@ describe('VaultHeader Component', () => { // Owner label should be present expect(screen.getByText('Owner')).toBeInTheDocument() - // Owner value should display the exact account address as link text - // The Account component displays the full address for short accounts - const ownerLink = screen.getByRole('link', { - name: `${owner.slice(0, 7)}...${owner.slice(-5)}`, - }) + // Owner value should display the full, untruncated account address as link text + const ownerLink = screen.getByRole('link', { name: owner }) expect(ownerLink.getAttribute('href')).toBe(`/accounts/${owner}`) }) }) @@ -324,6 +319,84 @@ describe('VaultHeader Component', () => { }) }) + /** + * ========================================= + * SECTION 2b: Deposit Blocked Flag Tests + * ========================================= + * When lsfVaultOwnerCanBlockDeposit (0x00040000) is set, the vault owner may + * block/unblock deposits, and the current state is reflected by + * lsfVaultDepositBlocked (0x00020000). The UI shows this as "Deposits + * Allowed" (the inverse of the raw flag) since "blocked" reads as if + * existing deposits are frozen, rather than new deposits being disallowed. + */ + describe('Deposit Allowed Flag', () => { + it('does not render the row when the owner cannot block deposits', () => { + const vaultData = { + Owner: 'rTestOwner', + Asset: { currency: 'XRP' }, + Flags: 0, + } + + render( + + + , + ) + + expect(screen.queryByText('Deposits Allowed')).not.toBeInTheDocument() + }) + + it('displays YES as active when deposits are unblocked', () => { + const vaultData = { + Owner: 'rTestOwner', + Asset: { currency: 'XRP' }, + Flags: 0x00040000, // lsfVaultOwnerCanBlockDeposit, deposits not blocked + } + + render( + + + , + ) + + const row = screen.getByText('Deposits Allowed').closest('tr')! + const togglePills = row.querySelectorAll('.toggle-pill') + expect(togglePills[0]).toHaveClass('active') + expect(togglePills[1]).not.toHaveClass('active') + }) + + it('displays NO as active when deposits are blocked', () => { + const vaultData = { + Owner: 'rTestOwner', + Asset: { currency: 'XRP' }, + Flags: 0x00040000 | 0x00020000, // can block + currently blocked + } + + render( + + + , + ) + + const row = screen.getByText('Deposits Allowed').closest('tr')! + const togglePills = row.querySelectorAll('.toggle-pill') + expect(togglePills[0]).not.toHaveClass('active') + expect(togglePills[1]).toHaveClass('active') + }) + }) + /** * ========================================= * SECTION 3: Asset Type Display Tests diff --git a/src/containers/shared/components/CopyableText/styles.scss b/src/containers/shared/components/CopyableText/styles.scss index eedd2400d..b2440e2c6 100644 --- a/src/containers/shared/components/CopyableText/styles.scss +++ b/src/containers/shared/components/CopyableText/styles.scss @@ -35,10 +35,13 @@ .copyable-text-with-icon { display: inline-flex; + min-width: 0; + max-width: 100%; align-items: center; .copyable-text-value { color: $white; + overflow-wrap: anywhere; } .copy-icon-button { diff --git a/src/containers/shared/components/Transaction/VaultClawback/Simple.tsx b/src/containers/shared/components/Transaction/VaultClawback/Simple.tsx index 2ecd4a971..8b4828359 100644 --- a/src/containers/shared/components/Transaction/VaultClawback/Simple.tsx +++ b/src/containers/shared/components/Transaction/VaultClawback/Simple.tsx @@ -14,7 +14,11 @@ export const Simple: TransactionSimpleComponent = ( const { VaultID: vaultId, Holder: holder, Amount: amount } = data.instructions return ( <> - + {vaultId} diff --git a/src/containers/shared/components/Transaction/VaultDelete/Description.tsx b/src/containers/shared/components/Transaction/VaultDelete/Description.tsx index aec505bfa..560d3f7d7 100644 --- a/src/containers/shared/components/Transaction/VaultDelete/Description.tsx +++ b/src/containers/shared/components/Transaction/VaultDelete/Description.tsx @@ -2,19 +2,34 @@ import { Trans } from 'react-i18next' import type { VaultDelete } from 'xrpl' import { TransactionDescriptionProps } from '../types' import { Account } from '../../Account' +import { decodeHex, hexMatch } from '../../../transactionUtils' export const Description = ({ data, }: TransactionDescriptionProps) => { const { tx } = data const { Account: account, VaultID: vaultId } = tx + // MemoData is not yet part of the xrpl package's VaultDelete type + const memoData = (tx as any).MemoData as string | undefined return ( - , - VaultID: {vaultId}, - }} - /> + <> + , + VaultID: {vaultId}, + }} + /> + {memoData && hexMatch.test(memoData) && ( +
+ {decodeHex(memoData)}, + }} + /> +
+ )} + ) } diff --git a/src/containers/shared/components/Transaction/VaultDelete/Simple.tsx b/src/containers/shared/components/Transaction/VaultDelete/Simple.tsx index 3d92f77a9..732d4f257 100644 --- a/src/containers/shared/components/Transaction/VaultDelete/Simple.tsx +++ b/src/containers/shared/components/Transaction/VaultDelete/Simple.tsx @@ -2,6 +2,7 @@ import { useTranslation } from 'react-i18next' import type { VaultDelete } from 'xrpl' import { TransactionSimpleComponent, TransactionSimpleProps } from '../types' import { SimpleRow } from '../SimpleRow' +import { decodeHex, hexMatch } from '../../../transactionUtils' export const Simple: TransactionSimpleComponent = ( props: TransactionSimpleProps, @@ -9,9 +10,26 @@ export const Simple: TransactionSimpleComponent = ( const { t } = useTranslation() const { data } = props const { VaultID: vaultId } = data.instructions + // MemoData is not yet part of the xrpl package's VaultDelete type + const memoData = (data.instructions as any).MemoData as string | undefined return ( - - {vaultId} - + <> + + {vaultId} + + {memoData && hexMatch.test(memoData) && ( + + {decodeHex(memoData)} + + )} + ) } diff --git a/src/containers/shared/components/Transaction/VaultDelete/test/VaultDeleteDescription.test.tsx b/src/containers/shared/components/Transaction/VaultDelete/test/VaultDeleteDescription.test.tsx index 961aa1f09..c6e4af1b7 100644 --- a/src/containers/shared/components/Transaction/VaultDelete/test/VaultDeleteDescription.test.tsx +++ b/src/containers/shared/components/Transaction/VaultDelete/test/VaultDeleteDescription.test.tsx @@ -11,6 +11,9 @@ describe('VaultDelete: Description', () => { expect(container).toHaveTextContent( 'rLR12AgChXxLoQsuLCizNCgh5pt5jPheo1 deleted a vault with ID 2AA88C4CA646645E35E38B8D51CD2CA50BDE14A3F3FFE3838F2C8DCE95C2BABD', ) + expect(container).toHaveTextContent( + 'It sets the Vault deletion MemoData to vault closed voluntarily', + ) unmount() }) }) diff --git a/src/containers/shared/components/Transaction/VaultDelete/test/VaultDeleteSimple.test.tsx b/src/containers/shared/components/Transaction/VaultDelete/test/VaultDeleteSimple.test.tsx index 3880d761c..9e21bb0d4 100644 --- a/src/containers/shared/components/Transaction/VaultDelete/test/VaultDeleteSimple.test.tsx +++ b/src/containers/shared/components/Transaction/VaultDelete/test/VaultDeleteSimple.test.tsx @@ -12,6 +12,7 @@ describe('VaultDelete: Simple', () => { 'vault_id', '2AA88C4CA646645E35E38B8D51CD2CA50BDE14A3F3FFE3838F2C8DCE95C2BABD', ) + expectSimpleRowText(container, 'memo_data', 'vault closed voluntarily') unmount() }) }) diff --git a/src/containers/shared/components/Transaction/VaultDelete/test/mock_data/VaultDelete.json b/src/containers/shared/components/Transaction/VaultDelete/test/mock_data/VaultDelete.json index 853977db5..62d56f5c5 100644 --- a/src/containers/shared/components/Transaction/VaultDelete/test/mock_data/VaultDelete.json +++ b/src/containers/shared/components/Transaction/VaultDelete/test/mock_data/VaultDelete.json @@ -180,6 +180,7 @@ "TransactionType": "VaultDelete", "TxnSignature": "D6A3F0AADD0ECEE2D51E051157EF582D8C1EFD84897A912557148FC963D9B93DFD725996F90ED2DF7F0922B34E47D28869E408EDD9F7CA2CEA681023EE57B70D", "VaultID": "2AA88C4CA646645E35E38B8D51CD2CA50BDE14A3F3FFE3838F2C8DCE95C2BABD", + "MemoData": "7661756C7420636C6F73656420766F6C756E746172696C79", "ctid": "C00626A700000C96", "date": 807127481, "ledger_index": 403111 diff --git a/src/containers/shared/components/Transaction/VaultDeposit/Simple.tsx b/src/containers/shared/components/Transaction/VaultDeposit/Simple.tsx index dc19a7ab5..014a169e8 100644 --- a/src/containers/shared/components/Transaction/VaultDeposit/Simple.tsx +++ b/src/containers/shared/components/Transaction/VaultDeposit/Simple.tsx @@ -13,7 +13,11 @@ export const Simple: TransactionSimpleComponent = ( const { VaultID: vaultId, Amount: amount } = data.instructions return ( <> - + {vaultId} diff --git a/src/containers/shared/components/Transaction/VaultSet/Simple.tsx b/src/containers/shared/components/Transaction/VaultSet/Simple.tsx index 525f8e527..9777eefbc 100644 --- a/src/containers/shared/components/Transaction/VaultSet/Simple.tsx +++ b/src/containers/shared/components/Transaction/VaultSet/Simple.tsx @@ -13,7 +13,11 @@ export const Simple: TransactionSimpleComponent = ( const { VaultID, Data, AssetsMaximum, DomainID } = data.instructions return ( <> - + {VaultID} {AssetsMaximum && ( diff --git a/src/containers/shared/components/Transaction/VaultWithdraw/Simple.tsx b/src/containers/shared/components/Transaction/VaultWithdraw/Simple.tsx index 594d15cf0..5a34dbdac 100644 --- a/src/containers/shared/components/Transaction/VaultWithdraw/Simple.tsx +++ b/src/containers/shared/components/Transaction/VaultWithdraw/Simple.tsx @@ -18,7 +18,11 @@ export const Simple: TransactionSimpleComponent = ( } = data.instructions return ( <> - + {vaultId} diff --git a/src/containers/shared/transactionUtils.ts b/src/containers/shared/transactionUtils.ts index 6b3c4900c..a2a5e96d7 100644 --- a/src/containers/shared/transactionUtils.ts +++ b/src/containers/shared/transactionUtils.ts @@ -16,6 +16,7 @@ export const ACCOUNT_ZERO = 'rrrrrrrrrrrrrrrrrrrrrhoLvTp' export const TX_FLAGS: Record> = { all: { 0x80000000: 'tfFullyCanonicalSig', + 0x40000000: 'tfInnerBatchTxn', }, AccountSet: { 0x00010000: 'tfRequireDestTag', @@ -113,6 +114,14 @@ export const TX_FLAGS: Record> = { }, VaultCreate: { 1: 'vaultStrategyFirstComeFirstServe', + 0x00040000: 'tfVaultOwnerCanBlockDeposit', + }, + VaultDeposit: { + 0x00010000: 'tfVaultDonate', + }, + VaultSet: { + 0x00010000: 'tfVaultDepositBlock', + 0x00020000: 'tfVaultDepositUnblock', }, XChainModifyBridge: { 0x00010000: 'tfClearAccountCreateAmount', From 502ca1970a15eec12f41a5e0fc9ba0272b664c93 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Fri, 25 Sep 2026 13:05:29 -0700 Subject: [PATCH 2/6] Add closed-ended vault fields and VaultWithdraw credentials Covers the LendingProtocolV1_1 additions that were missing alongside the deposit-blocking work already on this branch: - VaultKind, SubscriptionDate and RedemptionDate on the Vault page and the VaultCreate simple view. Dates are rendered with the same convertRippleDate + localizeDate + DATE_OPTIONS treatment Escrow uses, so they read as timestamps rather than raw Ripple-epoch seconds. - CredentialIDs on VaultWithdraw, reusing the shared CredentialIDs row component already used by EscrowFinish and PaymentChannelClaim. VaultKind values live in a new shared vaultUtils so the Vault page and the transaction view agree on the labels. Fields absent from the xrpl package's types are read through a cast, matching the existing MemoData pattern. LEVersion is intentionally not surfaced: it selects legacy vs. cash-basis accounting for libraries decoding the ledger entry, and is noise for a human reading the explorer. --- public/locales/en-US/translations.json | 5 + src/containers/Vault/VaultHeader/index.tsx | 48 ++++- .../VaultHeader/test/VaultHeader.test.tsx | 98 +++++++++ .../Transaction/VaultCreate/Simple.tsx | 39 +++- .../test/VaultCreateSimple.test.tsx | 23 +++ .../mock_data/VaultCreateClosedEnded.json | 167 +++++++++++++++ .../Transaction/VaultWithdraw/Simple.tsx | 8 + .../test/VaultWithdrawSimple.test.tsx | 21 ++ .../VaultWithdrawWithCredentials.json | 194 ++++++++++++++++++ src/containers/shared/vaultUtils.ts | 20 ++ 10 files changed, 620 insertions(+), 3 deletions(-) create mode 100644 src/containers/shared/components/Transaction/VaultCreate/test/mock_data/VaultCreateClosedEnded.json create mode 100644 src/containers/shared/components/Transaction/VaultWithdraw/test/mock_data/VaultWithdrawWithCredentials.json create mode 100644 src/containers/shared/vaultUtils.ts diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index aeb3ae39b..61b48ed0c 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -639,6 +639,9 @@ "assets_maximum": "Assets Maximum", "mptoken_metadata": "MPToken Metadata", "withdrawal_policy": "Withdrawal Policy", + "vault_kind": "Vault Kind", + "subscription_date": "Subscription Date", + "redemption_date": "Redemption Date", "account_creates_vault": " created a vault for ", "vault_id": "Vault ID", "single_asset_vault": "Single Asset Vault", @@ -838,6 +841,8 @@ "available_to_borrow": "Available to Borrow", "not_available": "Not available", "first_come_first_served": "First Come First Served", + "open_ended": "Open Ended", + "closed_ended": "Closed Ended", "loans": "Loans", "loan_broker": "Loan Broker", "total_debt": "Total Debt", diff --git a/src/containers/Vault/VaultHeader/index.tsx b/src/containers/Vault/VaultHeader/index.tsx index d31afdb0c..246f9e67e 100644 --- a/src/containers/Vault/VaultHeader/index.tsx +++ b/src/containers/Vault/VaultHeader/index.tsx @@ -10,7 +10,11 @@ import { MPT_ROUTE } from '../../App/routes' import SocketContext from '../../shared/SocketContext' import { getMPTIssuance } from '../../../rippled/lib/rippled' import { parseVaultWebsite } from '../utils' -import { getCurrencySymbol, isCurrencyExoticSymbol } from '../../shared/utils' +import { + getCurrencySymbol, + isCurrencyExoticSymbol, + localizeDate, +} from '../../shared/utils' import './styles.scss' import { useAnalytics } from '../../shared/analytics' import { parseAmount } from '../../shared/NumberFormattingUtils' @@ -18,6 +22,10 @@ import { convertHexToString } from '../../../rippled/lib/utils' import { Metadata } from '../../Token/MPT/Header/Metadata' import Currency from '../../shared/components/Currency' import { parseMPTokenMetadata } from '../../shared/mptUtils' +import { VAULT_KINDS } from '../../shared/vaultUtils' +import { convertRippleDate } from '../../../rippled/lib/convertRippleDate' +import { DATE_OPTIONS } from '../../shared/transactionUtils' +import { useLanguage } from '../../shared/hooks' interface VaultData { Owner?: string @@ -36,6 +44,9 @@ interface VaultData { WithdrawalPolicy?: number Data?: string ShareMPTID?: string + VaultKind?: number + SubscriptionDate?: number + RedemptionDate?: number } interface Props { @@ -58,6 +69,7 @@ const WITHDRAWAL_POLICIES: { [key: number]: string } = { export const VaultHeader = ({ data, vaultId, displayCurrency }: Props) => { const { t } = useTranslation() + const language = useLanguage() const { trackException } = useAnalytics() const rippledSocket = useContext(SocketContext) const { rate: tokenToUsdRate } = useTokenToUSDRate( @@ -77,6 +89,9 @@ export const VaultHeader = ({ data, vaultId, displayCurrency }: Props) => { WithdrawalPolicy: withdrawalPolicy, Data: vaultDataRaw, ShareMPTID: vaultShareMptId, + VaultKind: vaultKind, + SubscriptionDate: subscriptionDate, + RedemptionDate: redemptionDate, } = data // Converts amount to USD if displayCurrency is 'USD', otherwise returns as-is @@ -160,6 +175,19 @@ export const VaultHeader = ({ data, vaultId, displayCurrency }: Props) => { return t(policyKey as 'first_come_first_served') } + const getVaultKindText = () => { + if (vaultKind === undefined) return '-' + const kindKey = VAULT_KINDS[vaultKind] + if (!kindKey) return String(vaultKind) + // Use type assertion for dynamic translation keys + return t(kindKey as 'open_ended') + } + + const formatVaultDate = (date: number) => + `${localizeDate(new Date(convertRippleDate(date)), language, DATE_OPTIONS)} ${ + DATE_OPTIONS.timeZone + }` + const renderMPTSharesLink = () => { if (!vaultShareMptId) return '-' return ( @@ -278,6 +306,24 @@ export const VaultHeader = ({ data, vaultId, displayCurrency }: Props) => { label={t('withdrawal_policy')} value={getWithdrawalPolicyText()} /> + {vaultKind !== undefined && ( + + )} + {subscriptionDate !== undefined && ( + + )} + {redemptionDate !== undefined && ( + + )} diff --git a/src/containers/Vault/VaultHeader/test/VaultHeader.test.tsx b/src/containers/Vault/VaultHeader/test/VaultHeader.test.tsx index 22b83c3fe..160bb1386 100644 --- a/src/containers/Vault/VaultHeader/test/VaultHeader.test.tsx +++ b/src/containers/Vault/VaultHeader/test/VaultHeader.test.tsx @@ -1442,4 +1442,102 @@ describe('VaultHeader Component', () => { expect(tvlRow).toHaveTextContent('Total Value Locked (TVL)$2.00M USD') }) }) + + describe('Closed-Ended Vault Fields', () => { + it('omits the kind and date rows for a vault with no VaultKind', () => { + const vaultData = { + Owner: 'rTestOwner', + Asset: { currency: 'XRP' }, + } + + render( + + + , + ) + + expect(screen.queryByText('Vault Kind')).not.toBeInTheDocument() + expect(screen.queryByText('Subscription Date')).not.toBeInTheDocument() + expect(screen.queryByText('Redemption Date')).not.toBeInTheDocument() + }) + + it('displays Open Ended for VaultKind 0 without dates', () => { + const vaultData = { + Owner: 'rTestOwner', + Asset: { currency: 'XRP' }, + VaultKind: 0, + } + + render( + + + , + ) + + expect(screen.getByText('Vault Kind').closest('tr')).toHaveTextContent( + 'Vault KindOpen Ended', + ) + expect(screen.queryByText('Subscription Date')).not.toBeInTheDocument() + }) + + it('displays Closed Ended with formatted subscription and redemption dates', () => { + const vaultData = { + Owner: 'rTestOwner', + Asset: { currency: 'XRP' }, + VaultKind: 1, + SubscriptionDate: 807030000, + RedemptionDate: 807030300, + } + + render( + + + , + ) + + expect(screen.getByText('Vault Kind').closest('tr')).toHaveTextContent( + 'Vault KindClosed Ended', + ) + expect( + screen.getByText('Subscription Date').closest('tr'), + ).toHaveTextContent('Subscription DateJuly 28, 2025 at 3:00:00 PM UTC') + expect( + screen.getByText('Redemption Date').closest('tr'), + ).toHaveTextContent('Redemption DateJuly 28, 2025 at 3:05:00 PM UTC') + }) + + it('falls back to the raw value for an unknown VaultKind', () => { + const vaultData = { + Owner: 'rTestOwner', + Asset: { currency: 'XRP' }, + VaultKind: 7, + } + + render( + + + , + ) + + expect(screen.getByText('Vault Kind').closest('tr')).toHaveTextContent( + 'Vault Kind7', + ) + }) + }) }) diff --git a/src/containers/shared/components/Transaction/VaultCreate/Simple.tsx b/src/containers/shared/components/Transaction/VaultCreate/Simple.tsx index 20cf97a51..c82b6a981 100644 --- a/src/containers/shared/components/Transaction/VaultCreate/Simple.tsx +++ b/src/containers/shared/components/Transaction/VaultCreate/Simple.tsx @@ -3,15 +3,19 @@ import type { VaultCreate } from 'xrpl' import { TransactionSimpleComponent, TransactionSimpleProps } from '../types' import { SimpleRow } from '../SimpleRow' import Currency from '../../Currency' -import { isValidJsonString } from '../../../utils' +import { isValidJsonString, localizeDate } from '../../../utils' import { JsonView } from '../../JsonView' import { MPTokenLink } from '../../MPTokenLink' -import { TX_FLAGS } from '../../../transactionUtils' +import { TX_FLAGS, DATE_OPTIONS } from '../../../transactionUtils' +import { useLanguage } from '../../../hooks' +import { convertRippleDate } from '../../../../../rippled/lib/convertRippleDate' +import { VAULT_KINDS } from '../../../vaultUtils' export const Simple: TransactionSimpleComponent = ( props: TransactionSimpleProps, ) => { const { t } = useTranslation() + const language = useLanguage() const { data } = props const { Asset, @@ -21,6 +25,19 @@ export const Simple: TransactionSimpleComponent = ( WithdrawalPolicy, DomainID, } = data.instructions + // VaultKind, SubscriptionDate and RedemptionDate are not yet part of the + // xrpl package's VaultCreate type. + const vaultKind = (data.instructions as any).VaultKind as number | undefined + const subscriptionDate = (data.instructions as any).SubscriptionDate as + | number + | undefined + const redemptionDate = (data.instructions as any).RedemptionDate as + | number + | undefined + const formatVaultDate = (date: number) => + `${localizeDate(new Date(convertRippleDate(date)), language, DATE_OPTIONS)} ${ + DATE_OPTIONS.timeZone + }` // @ts-expect-error -- necessary to check for MPT const mptIssuanceId = Asset.mpt_issuance_id const isMPT = mptIssuanceId != null @@ -83,6 +100,24 @@ export const Simple: TransactionSimpleComponent = ( DomainID )} + {vaultKind !== undefined && ( + + {VAULT_KINDS[vaultKind] ? t(VAULT_KINDS[vaultKind]) : vaultKind} + + )} + {subscriptionDate !== undefined && ( + + {formatVaultDate(subscriptionDate)} + + )} + {redemptionDate !== undefined && ( + + {formatVaultDate(redemptionDate)} + + )} ) } diff --git a/src/containers/shared/components/Transaction/VaultCreate/test/VaultCreateSimple.test.tsx b/src/containers/shared/components/Transaction/VaultCreate/test/VaultCreateSimple.test.tsx index 3f5ffaebf..b1569c441 100644 --- a/src/containers/shared/components/Transaction/VaultCreate/test/VaultCreateSimple.test.tsx +++ b/src/containers/shared/components/Transaction/VaultCreate/test/VaultCreateSimple.test.tsx @@ -5,6 +5,7 @@ import { } from '../../test' import { Simple } from '../Simple' import mockVaultCreate from './mock_data/VaultCreate.json' +import mockVaultCreateClosedEnded from './mock_data/VaultCreateClosedEnded.json' const renderComponent = createSimpleRenderFactory(Simple) @@ -29,6 +30,28 @@ describe('VaultCreate: Simple', () => { 'vaultStrategyFirstComeFirstServe', ) expectSimpleRowNotToExist(container, 'domain_id') + // An open-ended vault omits VaultKind entirely, so no closed-ended rows. + expectSimpleRowNotToExist(container, 'vault_kind') + expectSimpleRowNotToExist(container, 'subscription_date') + expectSimpleRowNotToExist(container, 'redemption_date') + + unmount() + }) + + it('renders a closed-ended vault', () => { + const { container, unmount } = renderComponent(mockVaultCreateClosedEnded) + + expectSimpleRowText(container, 'vault_kind', 'closed_ended') + expectSimpleRowText( + container, + 'subscription_date', + 'July 28, 2025 at 3:00:00 PM UTC', + ) + expectSimpleRowText( + container, + 'redemption_date', + 'July 28, 2025 at 3:05:00 PM UTC', + ) unmount() }) diff --git a/src/containers/shared/components/Transaction/VaultCreate/test/mock_data/VaultCreateClosedEnded.json b/src/containers/shared/components/Transaction/VaultCreate/test/mock_data/VaultCreateClosedEnded.json new file mode 100644 index 000000000..9326c1ab9 --- /dev/null +++ b/src/containers/shared/components/Transaction/VaultCreate/test/mock_data/VaultCreateClosedEnded.json @@ -0,0 +1,167 @@ +{ + "close_time_iso": "2025-07-28T14:33:32Z", + "ctid": "C005A5B800000C96", + "hash": "C3A66689AAD6A7B8BFF535C125DC251E5CC91DAEB9A1212295635219CA814F87", + "ledger_hash": "D2FB9809B0A876D649AC6638EB27AB714C2104D63395F465BC83D0419E02859E", + "ledger_index": 370104, + "meta": { + "AffectedNodes": [ + { + "CreatedNode": { + "LedgerEntryType": "DirectoryNode", + "LedgerIndex": "100ECAFCB2612246420AF018FC3D0136387BB973D791895D1A70B399210C898C", + "NewFields": { + "Owner": "raKXA6BCRNbJyuzSB9AJ9Ykx9wi1mc4uTf", + "RootIndex": "100ECAFCB2612246420AF018FC3D0136387BB973D791895D1A70B399210C898C" + } + } + }, + { + "CreatedNode": { + "LedgerEntryType": "RippleState", + "LedgerIndex": "110E0CBEC6D61A84A4D9D99720D467F9784D721F4F936BCDEBC37E9FBC0A552F", + "NewFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "0" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rJCPrRU8kcLfqCKob1j9EivLa4wG5pF4C2", + "value": "0" + }, + "LowLimit": { + "currency": "USD", + "issuer": "raKXA6BCRNbJyuzSB9AJ9Ykx9wi1mc4uTf", + "value": "0" + } + } + } + }, + { + "CreatedNode": { + "LedgerEntryType": "DirectoryNode", + "LedgerIndex": "3C4E60B37395E1247CAF623F33591E7B0C46B9D4C87FF5B25090D3554BFCC509", + "NewFields": { + "Owner": "rpZtrvuiVDhtSDZPm7axXgNB7iW3J4avwQ", + "RootIndex": "3C4E60B37395E1247CAF623F33591E7B0C46B9D4C87FF5B25090D3554BFCC509" + } + } + }, + { + "ModifiedNode": { + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "73B52BD94D8E722BECD2F89407DBBD850D878EFA5388D33C18213E8ED44BE4B2", + "PreviousTxnID": "E41237E69B2A721B37F954C056BBEFA71DD7CA91E2CBD387406B4A76C210065E", + "PreviousTxnLgrSeq": 370102 + } + }, + { + "CreatedNode": { + "LedgerEntryType": "MPTokenIssuance", + "LedgerIndex": "9450FA79302D108934FF1C28C90DF11D35AAE86156B311A3648D4A018CCFDB20", + "NewFields": { + "Flags": 56, + "Issuer": "raKXA6BCRNbJyuzSB9AJ9Ykx9wi1mc4uTf", + "MPTokenMetadata": "7368617265206D65746164617461", + "Sequence": 1 + } + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rpZtrvuiVDhtSDZPm7axXgNB7iW3J4avwQ", + "Balance": "95000000", + "Flags": 0, + "OwnerCount": 1, + "Sequence": 370095 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "A3AA84035B223DAF7E06CB3C6624DEC258E6A5223A4F5A14B06F20C06871593F", + "PreviousFields": { + "Balance": "100000000", + "OwnerCount": 0, + "Sequence": 370094 + }, + "PreviousTxnID": "39A35785F06858EC1989F0494B548AE24D6CA29362B5269BEFE18BD6015DA19D", + "PreviousTxnLgrSeq": 370094 + } + }, + { + "CreatedNode": { + "LedgerEntryType": "Vault", + "LedgerIndex": "A441A9B4AA29709F483813F86D0B3CA680EEBA1C9B27A13900F65EC31FA929B1", + "NewFields": { + "Account": "raKXA6BCRNbJyuzSB9AJ9Ykx9wi1mc4uTf", + "Asset": { + "currency": "USD", + "issuer": "rJCPrRU8kcLfqCKob1j9EivLa4wG5pF4C2" + }, + "AssetsMaximum": "500", + "Data": "7661756C74206D65746164617461", + "Owner": "rpZtrvuiVDhtSDZPm7axXgNB7iW3J4avwQ", + "Sequence": 370094, + "ShareMPTID": "000000013A59F2358FED3D0478E4588C67B162EFCE92E279", + "WithdrawalPolicy": 1 + } + } + }, + { + "CreatedNode": { + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "B19746AAD9D08D9621E833D1535938491FDC1F636A23ED5C46479128357200CF", + "NewFields": { + "Account": "raKXA6BCRNbJyuzSB9AJ9Ykx9wi1mc4uTf", + "Flags": 26214400, + "OwnerCount": 2, + "VaultID": "A441A9B4AA29709F483813F86D0B3CA680EEBA1C9B27A13900F65EC31FA929B1" + } + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Flags": 0, + "Owner": "rJCPrRU8kcLfqCKob1j9EivLa4wG5pF4C2", + "RootIndex": "E2464506C72BCF889380065B34A557024598A33171769A0DC9950F00CF922F27" + }, + "LedgerEntryType": "DirectoryNode", + "LedgerIndex": "E2464506C72BCF889380065B34A557024598A33171769A0DC9950F00CF922F27", + "PreviousTxnID": "E142ECAE46CE053B3A7D0EA753464665FC8EE6F83375C6DBCAA308071C5050E6", + "PreviousTxnLgrSeq": 370100 + } + } + ], + "TransactionIndex": 0, + "TransactionResult": "tesSUCCESS" + }, + "tx": { + "Account": "rpZtrvuiVDhtSDZPm7axXgNB7iW3J4avwQ", + "Asset": { + "currency": "USD", + "issuer": "rJCPrRU8kcLfqCKob1j9EivLa4wG5pF4C2" + }, + "AssetsMaximum": "500", + "Data": "7661756C74206D65746164617461", + "Fee": "5000000", + "Flags": 0, + "LastLedgerSequence": 370122, + "MPTokenMetadata": "7368617265206D65746164617461", + "NetworkID": 3222, + "Sequence": 370094, + "SigningPubKey": "EDFDA5D690EF90A4F0C9815422C19E059FA20882EF84F2B92993A094858546AEA7", + "TransactionType": "VaultCreate", + "TxnSignature": "F2C2AD6C9E97C012DDF32635FE83D1C284EB826244BFC7F93BFE8D6A30E360220CE3EE14FB5117EFF00B69C347D8963AEA2AB3501F1DB6A25B0BEC9A539BE50F", + "WithdrawalPolicy": 1, + "ctid": "C005A5B800000C96", + "date": 807028412, + "ledger_index": 370104, + "VaultKind": 1, + "SubscriptionDate": 807030000, + "RedemptionDate": 807030300 + }, + "validated": true +} diff --git a/src/containers/shared/components/Transaction/VaultWithdraw/Simple.tsx b/src/containers/shared/components/Transaction/VaultWithdraw/Simple.tsx index 5a34dbdac..b3cb3a79d 100644 --- a/src/containers/shared/components/Transaction/VaultWithdraw/Simple.tsx +++ b/src/containers/shared/components/Transaction/VaultWithdraw/Simple.tsx @@ -5,6 +5,7 @@ import { SimpleRow } from '../SimpleRow' import { Amount } from '../../Amount' import { formatAmount } from '../../../../../rippled/lib/txSummary/formatAmount' import { Account } from '../../Account' +import { CredentialIDs } from '../CredentialIDs' export const Simple: TransactionSimpleComponent = ( props: TransactionSimpleProps, @@ -16,6 +17,10 @@ export const Simple: TransactionSimpleComponent = ( Amount: amount, Destination: destination, } = data.instructions + // CredentialIDs is not yet part of the xrpl package's VaultWithdraw type. + const credentialIds = (data.instructions as any).CredentialIDs as + | string[] + | undefined return ( <> )} + {credentialIds && credentialIds.length > 0 && ( + + )} ) } diff --git a/src/containers/shared/components/Transaction/VaultWithdraw/test/VaultWithdrawSimple.test.tsx b/src/containers/shared/components/Transaction/VaultWithdraw/test/VaultWithdrawSimple.test.tsx index 4874c7e94..afeb39e2e 100644 --- a/src/containers/shared/components/Transaction/VaultWithdraw/test/VaultWithdrawSimple.test.tsx +++ b/src/containers/shared/components/Transaction/VaultWithdraw/test/VaultWithdrawSimple.test.tsx @@ -1,6 +1,7 @@ import { expectSimpleRowText, createSimpleRenderFactory } from '../../test' import { Simple } from '../Simple' import mockVaultWithdraw from './mock_data/VaultWithdraw.json' +import mockVaultWithdrawWithCredentials from './mock_data/VaultWithdrawWithCredentials.json' const renderComponent = createSimpleRenderFactory(Simple) @@ -17,6 +18,26 @@ describe('VaultWithdraw: Simple', () => { 'amount', '$5.00 USD.rMab3itPzruo5HLEVherc93Prf4tg5d7dx', ) + expect( + container.querySelector('[data-testid="credential-id-0"]'), + ).not.toBeInTheDocument() + unmount() + }) + + it('renders credential IDs for a permissioned-domain withdrawal', () => { + const { container, unmount } = renderComponent( + mockVaultWithdrawWithCredentials, + ) + expect( + container.querySelector('[data-testid="credential-id-0"]'), + ).toHaveTextContent( + 'EA85E9D5B4F0D8E36C5F1A4B8C2D3E4F5061728394A5B6C7D8E9F0A1B2C3D4E5', + ) + expect( + container.querySelector('[data-testid="credential-id-1"]'), + ).toHaveTextContent( + 'FB96FAE6C501E9F47D6F2B5C9D3E4F5061728394A5B6C7D8E9F0A1B2C3D4E5F6', + ) unmount() }) }) diff --git a/src/containers/shared/components/Transaction/VaultWithdraw/test/mock_data/VaultWithdrawWithCredentials.json b/src/containers/shared/components/Transaction/VaultWithdraw/test/mock_data/VaultWithdrawWithCredentials.json new file mode 100644 index 000000000..3f4261dbb --- /dev/null +++ b/src/containers/shared/components/Transaction/VaultWithdraw/test/mock_data/VaultWithdrawWithCredentials.json @@ -0,0 +1,194 @@ +{ + "close_time_iso": "2025-07-29T16:47:21Z", + "ctid": "C006209F00000C96", + "hash": "372CD0ABA6ED9E375A3042AC8F47D5C6A25D4C88077547C1FE2991B3CF3A1382", + "ledger_hash": "6DBE1C014D47E0D8BA4267C1D54E51B539EAA01AAAD417403B78DA474C850485", + "ledger_index": 401567, + "meta": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Flags": 56, + "Issuer": "rER4URMRoAvPPbPVWqycZdK85rhJwRnQCB", + "MPTokenMetadata": "7368617265206D65746164617461", + "OutstandingAmount": "5", + "OwnerNode": "0", + "Sequence": 1 + }, + "LedgerEntryType": "MPTokenIssuance", + "LedgerIndex": "259D6E5DB97AC2329F153BD53077B9EF08C73ED6A8F95C88DBDA9FAF60E18DA4", + "PreviousFields": { + "OutstandingAmount": "10" + }, + "PreviousTxnID": "2FED432AF524C5CD3D54EC462FD5ADFC77757A8CFFBF1448621AA46F2F941DF5", + "PreviousTxnLgrSeq": 401565 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rQEzyNVohiNuWwYFnwUMEWeyK3KvACUbyB", + "Flags": 0, + "MPTAmount": "5", + "MPTokenIssuanceID": "000000019E2D7036879DFC217D7D2A92CC7349EEB0D703AA", + "OwnerNode": "0" + }, + "LedgerEntryType": "MPToken", + "LedgerIndex": "267C99D9B622D5F661117E51E0F58BDB03D3F077F381EB395CCD6AC8BE882AE9", + "PreviousFields": { + "MPTAmount": "10" + }, + "PreviousTxnID": "2FED432AF524C5CD3D54EC462FD5ADFC77757A8CFFBF1448621AA46F2F941DF5", + "PreviousTxnLgrSeq": 401565 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rQEzyNVohiNuWwYFnwUMEWeyK3KvACUbyB", + "Balance": "89999988", + "Flags": 0, + "OwnerCount": 2, + "Sequence": 401557 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "544FECF19CF124E5D93D1A7D4973BDAA536C6D3DF8579A3E975445AD27542F52", + "PreviousFields": { + "Balance": "94999988", + "Sequence": 401556 + }, + "PreviousTxnID": "2FED432AF524C5CD3D54EC462FD5ADFC77757A8CFFBF1448621AA46F2F941DF5", + "PreviousTxnLgrSeq": 401565 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-995" + }, + "Flags": 131072, + "HighLimit": { + "currency": "USD", + "issuer": "rQEzyNVohiNuWwYFnwUMEWeyK3KvACUbyB", + "value": "1000" + }, + "HighNode": "0", + "LowLimit": { + "currency": "USD", + "issuer": "rMab3itPzruo5HLEVherc93Prf4tg5d7dx", + "value": "0" + }, + "LowNode": "0" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "62D2CA23FC334A9E56B4EEC9E1F069862D31ED8BEB2A0DBB4A0FAD56E08B8DD4", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-990" + } + }, + "PreviousTxnID": "2FED432AF524C5CD3D54EC462FD5ADFC77757A8CFFBF1448621AA46F2F941DF5", + "PreviousTxnLgrSeq": 401565 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "5" + }, + "Flags": 1114112, + "HighLimit": { + "currency": "USD", + "issuer": "rMab3itPzruo5HLEVherc93Prf4tg5d7dx", + "value": "0" + }, + "HighNode": "0", + "LowLimit": { + "currency": "USD", + "issuer": "rER4URMRoAvPPbPVWqycZdK85rhJwRnQCB", + "value": "0" + }, + "LowNode": "0" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "69D9A5B5C98036058C4BF3FA2C18469268E377416535A7EC57D6AB795F6600D3", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "10" + } + }, + "PreviousTxnID": "2FED432AF524C5CD3D54EC462FD5ADFC77757A8CFFBF1448621AA46F2F941DF5", + "PreviousTxnLgrSeq": 401565 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rER4URMRoAvPPbPVWqycZdK85rhJwRnQCB", + "Asset": { + "currency": "USD", + "issuer": "rMab3itPzruo5HLEVherc93Prf4tg5d7dx" + }, + "AssetsAvailable": "5", + "AssetsMaximum": "1000", + "AssetsTotal": "5", + "Data": "75706461746564206D65746164617461", + "Flags": 0, + "LossUnrealized": "0", + "Owner": "rEBAjhUfR1dPfiBKbBtYqyRrXTpyPLFBcM", + "OwnerNode": "0", + "Sequence": 401553, + "ShareMPTID": "000000019E2D7036879DFC217D7D2A92CC7349EEB0D703AA", + "WithdrawalPolicy": 1 + }, + "LedgerEntryType": "Vault", + "LedgerIndex": "FCC4FB21E6F5B3E60661730C7F6F13A100E1E89FF4CF854D9A9B2F3DF967FD77", + "PreviousFields": { + "AssetsAvailable": "10", + "AssetsTotal": "10" + }, + "PreviousTxnID": "2FED432AF524C5CD3D54EC462FD5ADFC77757A8CFFBF1448621AA46F2F941DF5", + "PreviousTxnLgrSeq": 401565 + } + } + ], + "TransactionIndex": 0, + "TransactionResult": "tesSUCCESS" + }, + "tx": { + "Account": "rQEzyNVohiNuWwYFnwUMEWeyK3KvACUbyB", + "Amount": { + "currency": "USD", + "issuer": "rMab3itPzruo5HLEVherc93Prf4tg5d7dx", + "value": "5" + }, + "Fee": "5000000", + "Flags": 0, + "LastLedgerSequence": 401585, + "NetworkID": 3222, + "Sequence": 401556, + "SigningPubKey": "ED7FCFBD5471DAD24425FE859842671D2285F8653790892F87E1394C0BB8BEE7E9", + "TransactionType": "VaultWithdraw", + "TxnSignature": "052F1DEE5A92AF3DF48EB252CBA8FC53DFB966C1B90E323CE5C7FCDFCDCB00761A4B462B115F2BAF9FA511BEFDDA1F4F33BACDCF88D03A29094189BF893C7A07", + "VaultID": "FCC4FB21E6F5B3E60661730C7F6F13A100E1E89FF4CF854D9A9B2F3DF967FD77", + "ctid": "C006209F00000C96", + "date": 807122841, + "ledger_index": 401567, + "CredentialIDs": [ + "EA85E9D5B4F0D8E36C5F1A4B8C2D3E4F5061728394A5B6C7D8E9F0A1B2C3D4E5", + "FB96FAE6C501E9F47D6F2B5C9D3E4F5061728394A5B6C7D8E9F0A1B2C3D4E5F6" + ] + }, + "validated": true +} diff --git a/src/containers/shared/vaultUtils.ts b/src/containers/shared/vaultUtils.ts new file mode 100644 index 000000000..f731432d0 --- /dev/null +++ b/src/containers/shared/vaultUtils.ts @@ -0,0 +1,20 @@ +/** + * Shared Single Asset Vault helpers. + * + * VaultKind distinguishes a closed-ended vault from the default open-ended + * kind, and is gated behind the `LendingProtocolV1_1` amendment. A closed-ended + * vault also carries a SubscriptionDate/RedemptionDate pair, and is the only + * kind a LoanBroker may be attached to. + */ + +// Translation keys, so the caller can render these via t(). +export const VAULT_KIND_OPEN_ENDED = 'open_ended' +export const VAULT_KIND_CLOSED_ENDED = 'closed_ended' + +// VaultKind values from the XLS-65d spec. +export const VAULT_KINDS: { + [key: number]: typeof VAULT_KIND_OPEN_ENDED | typeof VAULT_KIND_CLOSED_ENDED +} = { + 0: VAULT_KIND_OPEN_ENDED, + 1: VAULT_KIND_CLOSED_ENDED, +} From e1592ddeb0627ef84926b1fb7f866e2c71e0547f Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Tue, 13 Oct 2026 14:08:33 -0700 Subject: [PATCH 3/6] Fix Prettier formatting in VaultCreate/VaultWithdraw Simple views Collapse the multi-line union type casts flagged by prettier/prettier in the lint CI job. --- .../shared/components/Transaction/VaultCreate/Simple.tsx | 6 ++---- .../shared/components/Transaction/VaultWithdraw/Simple.tsx | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/containers/shared/components/Transaction/VaultCreate/Simple.tsx b/src/containers/shared/components/Transaction/VaultCreate/Simple.tsx index c82b6a981..9291d53c4 100644 --- a/src/containers/shared/components/Transaction/VaultCreate/Simple.tsx +++ b/src/containers/shared/components/Transaction/VaultCreate/Simple.tsx @@ -29,11 +29,9 @@ export const Simple: TransactionSimpleComponent = ( // xrpl package's VaultCreate type. const vaultKind = (data.instructions as any).VaultKind as number | undefined const subscriptionDate = (data.instructions as any).SubscriptionDate as - | number - | undefined + number | undefined const redemptionDate = (data.instructions as any).RedemptionDate as - | number - | undefined + number | undefined const formatVaultDate = (date: number) => `${localizeDate(new Date(convertRippleDate(date)), language, DATE_OPTIONS)} ${ DATE_OPTIONS.timeZone diff --git a/src/containers/shared/components/Transaction/VaultWithdraw/Simple.tsx b/src/containers/shared/components/Transaction/VaultWithdraw/Simple.tsx index b3cb3a79d..8c08cab83 100644 --- a/src/containers/shared/components/Transaction/VaultWithdraw/Simple.tsx +++ b/src/containers/shared/components/Transaction/VaultWithdraw/Simple.tsx @@ -19,8 +19,7 @@ export const Simple: TransactionSimpleComponent = ( } = data.instructions // CredentialIDs is not yet part of the xrpl package's VaultWithdraw type. const credentialIds = (data.instructions as any).CredentialIDs as - | string[] - | undefined + string[] | undefined return ( <> Date: Thu, 15 Oct 2026 13:20:02 -0700 Subject: [PATCH 4/6] Use real xrpl types for VaultKind/SubscriptionDate/RedemptionDate/MemoData/CredentialIDs xrpl@5.3.0 already ships types for these fields (from xrpl.js#3456), so the `as any` casts and "not yet part of the xrpl package" comments are stale. --- .../components/Transaction/VaultCreate/Simple.tsx | 10 +++------- .../components/Transaction/VaultDelete/Description.tsx | 4 +--- .../components/Transaction/VaultDelete/Simple.tsx | 4 +--- .../components/Transaction/VaultWithdraw/Simple.tsx | 4 +--- 4 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/containers/shared/components/Transaction/VaultCreate/Simple.tsx b/src/containers/shared/components/Transaction/VaultCreate/Simple.tsx index 9291d53c4..477475b60 100644 --- a/src/containers/shared/components/Transaction/VaultCreate/Simple.tsx +++ b/src/containers/shared/components/Transaction/VaultCreate/Simple.tsx @@ -24,14 +24,10 @@ export const Simple: TransactionSimpleComponent = ( MPTokenMetadata, WithdrawalPolicy, DomainID, + VaultKind: vaultKind, + SubscriptionDate: subscriptionDate, + RedemptionDate: redemptionDate, } = data.instructions - // VaultKind, SubscriptionDate and RedemptionDate are not yet part of the - // xrpl package's VaultCreate type. - const vaultKind = (data.instructions as any).VaultKind as number | undefined - const subscriptionDate = (data.instructions as any).SubscriptionDate as - number | undefined - const redemptionDate = (data.instructions as any).RedemptionDate as - number | undefined const formatVaultDate = (date: number) => `${localizeDate(new Date(convertRippleDate(date)), language, DATE_OPTIONS)} ${ DATE_OPTIONS.timeZone diff --git a/src/containers/shared/components/Transaction/VaultDelete/Description.tsx b/src/containers/shared/components/Transaction/VaultDelete/Description.tsx index 560d3f7d7..96579d517 100644 --- a/src/containers/shared/components/Transaction/VaultDelete/Description.tsx +++ b/src/containers/shared/components/Transaction/VaultDelete/Description.tsx @@ -8,9 +8,7 @@ export const Description = ({ data, }: TransactionDescriptionProps) => { const { tx } = data - const { Account: account, VaultID: vaultId } = tx - // MemoData is not yet part of the xrpl package's VaultDelete type - const memoData = (tx as any).MemoData as string | undefined + const { Account: account, VaultID: vaultId, MemoData: memoData } = tx return ( <> { const { t } = useTranslation() const { data } = props - const { VaultID: vaultId } = data.instructions - // MemoData is not yet part of the xrpl package's VaultDelete type - const memoData = (data.instructions as any).MemoData as string | undefined + const { VaultID: vaultId, MemoData: memoData } = data.instructions return ( <> Date: Mon, 19 Oct 2026 11:01:15 -0700 Subject: [PATCH 5/6] fix: address PR review comments on vault flags, credential formatting - Remove outdated Vault/VaultCreate/VaultDeposit/VaultSet flags not present in rippled (lsfVaultDepositBlocked, lsfVaultOwnerCanBlockDeposit, tfVaultDonate, tfVaultDepositBlock/Unblock) along with the deposit-blocked UI built on them - Add word-break styling to CredentialIDs rows to match other long-hex fields - Add missing CredentialIDs field/rendering to LoanBrokerCoverWithdraw --- src/containers/Transactions/simpleTab.scss | 3 +- src/containers/Vault/VaultHeader/index.tsx | 26 ------- .../VaultHeader/test/VaultHeader.test.tsx | 78 ------------------- .../components/Transaction/CredentialIDs.tsx | 1 + .../LoanBrokerCoverWithdraw/Simple.tsx | 11 ++- .../LoanBrokerCoverWithdraw/types.ts | 1 + src/containers/shared/transactionUtils.ts | 8 -- 7 files changed, 14 insertions(+), 114 deletions(-) diff --git a/src/containers/Transactions/simpleTab.scss b/src/containers/Transactions/simpleTab.scss index f300a5529..410f1c176 100644 --- a/src/containers/Transactions/simpleTab.scss +++ b/src/containers/Transactions/simpleTab.scss @@ -115,7 +115,8 @@ $subdued-color: $black-40; &.fulfillment, &.tx, &.channel, - &.vault-id { + &.vault-id, + &.credential-id { width: calc(100% - 120px); color: $subdued-color; font-size: 14px; diff --git a/src/containers/Vault/VaultHeader/index.tsx b/src/containers/Vault/VaultHeader/index.tsx index 246f9e67e..8a8777816 100644 --- a/src/containers/Vault/VaultHeader/index.tsx +++ b/src/containers/Vault/VaultHeader/index.tsx @@ -58,8 +58,6 @@ interface Props { // Vault flags from XLS-65d spec const VAULT_FLAGS = { lsfVaultPrivate: 0x00010000, - lsfVaultDepositBlocked: 0x00020000, - lsfVaultOwnerCanBlockDeposit: 0x00040000, } // Withdrawal policy values from XLS-65d spec @@ -158,11 +156,6 @@ export const VaultHeader = ({ data, vaultId, displayCurrency }: Props) => { const isPrivate = flags !== undefined && (flags & VAULT_FLAGS.lsfVaultPrivate) !== 0 - const canBlockDeposit = - flags !== undefined && - (flags & VAULT_FLAGS.lsfVaultOwnerCanBlockDeposit) !== 0 - const isDepositAllowed = - flags === undefined || (flags & VAULT_FLAGS.lsfVaultDepositBlocked) === 0 const decodedData = convertHexToString(vaultDataRaw) const vaultWebsite = parseVaultWebsite(vaultDataRaw) @@ -243,25 +236,6 @@ export const VaultHeader = ({ data, vaultId, displayCurrency }: Props) => { } /> - {canBlockDeposit && ( - - - {t('yes')} - - - {t('no')} - - - } - /> - )} {vaultCredential && ( { }) }) - /** - * ========================================= - * SECTION 2b: Deposit Blocked Flag Tests - * ========================================= - * When lsfVaultOwnerCanBlockDeposit (0x00040000) is set, the vault owner may - * block/unblock deposits, and the current state is reflected by - * lsfVaultDepositBlocked (0x00020000). The UI shows this as "Deposits - * Allowed" (the inverse of the raw flag) since "blocked" reads as if - * existing deposits are frozen, rather than new deposits being disallowed. - */ - describe('Deposit Allowed Flag', () => { - it('does not render the row when the owner cannot block deposits', () => { - const vaultData = { - Owner: 'rTestOwner', - Asset: { currency: 'XRP' }, - Flags: 0, - } - - render( - - - , - ) - - expect(screen.queryByText('Deposits Allowed')).not.toBeInTheDocument() - }) - - it('displays YES as active when deposits are unblocked', () => { - const vaultData = { - Owner: 'rTestOwner', - Asset: { currency: 'XRP' }, - Flags: 0x00040000, // lsfVaultOwnerCanBlockDeposit, deposits not blocked - } - - render( - - - , - ) - - const row = screen.getByText('Deposits Allowed').closest('tr')! - const togglePills = row.querySelectorAll('.toggle-pill') - expect(togglePills[0]).toHaveClass('active') - expect(togglePills[1]).not.toHaveClass('active') - }) - - it('displays NO as active when deposits are blocked', () => { - const vaultData = { - Owner: 'rTestOwner', - Asset: { currency: 'XRP' }, - Flags: 0x00040000 | 0x00020000, // can block + currently blocked - } - - render( - - - , - ) - - const row = screen.getByText('Deposits Allowed').closest('tr')! - const togglePills = row.querySelectorAll('.toggle-pill') - expect(togglePills[0]).not.toHaveClass('active') - expect(togglePills[1]).toHaveClass('active') - }) - }) - /** * ========================================= * SECTION 3: Asset Type Display Tests diff --git a/src/containers/shared/components/Transaction/CredentialIDs.tsx b/src/containers/shared/components/Transaction/CredentialIDs.tsx index 9dcd5f859..d039fc164 100644 --- a/src/containers/shared/components/Transaction/CredentialIDs.tsx +++ b/src/containers/shared/components/Transaction/CredentialIDs.tsx @@ -35,6 +35,7 @@ export const CredentialIDs = ({ {id} diff --git a/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/Simple.tsx b/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/Simple.tsx index e84085c9f..a912b730a 100644 --- a/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/Simple.tsx +++ b/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/Simple.tsx @@ -5,13 +5,19 @@ import { Amount } from '../../Amount' import { Account } from '../../Account' import { formatAmount } from '../../../../../rippled/lib/txSummary/formatAmount' import { LoanBrokerCoverWithdraw } from './types' +import { CredentialIDs } from '../CredentialIDs' export const Simple: TransactionSimpleComponent = ( props: TransactionSimpleProps, ) => { const { t } = useTranslation() const { data } = props - const { LoanBrokerID, Amount: amount, Destination } = data.instructions + const { + LoanBrokerID, + Amount: amount, + Destination, + CredentialIDs: credentialIds, + } = data.instructions return ( <> @@ -26,6 +32,9 @@ export const Simple: TransactionSimpleComponent = ( )} + {credentialIds && credentialIds.length > 0 && ( + + )} ) } diff --git a/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/types.ts b/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/types.ts index 6d642429f..4a35ac422 100644 --- a/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/types.ts +++ b/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/types.ts @@ -6,4 +6,5 @@ export interface LoanBrokerCoverWithdraw extends TransactionCommonFields { LoanBrokerID: string Amount: Amount Destination?: string + CredentialIDs?: string[] } diff --git a/src/containers/shared/transactionUtils.ts b/src/containers/shared/transactionUtils.ts index a4190f97f..0fdc807ea 100644 --- a/src/containers/shared/transactionUtils.ts +++ b/src/containers/shared/transactionUtils.ts @@ -157,14 +157,6 @@ export const TX_FLAGS: Record> = { }, VaultCreate: { 1: 'vaultStrategyFirstComeFirstServe', - 0x00040000: 'tfVaultOwnerCanBlockDeposit', - }, - VaultDeposit: { - 0x00010000: 'tfVaultDonate', - }, - VaultSet: { - 0x00010000: 'tfVaultDepositBlock', - 0x00020000: 'tfVaultDepositUnblock', }, XChainModifyBridge: { 0x00010000: 'tfClearAccountCreateAmount', From 67076a1b831290f3d68ecd6c2aa3fd3052a95bc6 Mon Sep 17 00:00:00 2001 From: Cybele Reed Date: Thu, 29 Oct 2026 13:37:49 -0700 Subject: [PATCH 6/6] fix: address remaining PR review comments - Remove unused `deposits_allowed` translation key - Add null placeholders for new vault-related keys across all non-English locales (vault_kind, subscription_date, redemption_date, vault_delete_memo_data, memo_data, open_ended, closed_ended) - Add Simple test coverage for CredentialIDs on LoanBrokerCoverWithdraw --- public/locales/ca-CA/translations.json | 7 + public/locales/en-US/translations.json | 1 - public/locales/es-ES/translations.json | 7 + public/locales/fr-FR/translations.json | 7 + public/locales/ja-JP/translations.json | 7 + public/locales/ko-KR/translations.json | 7 + public/locales/my-MM/translations.json | 7 + .../LoanBrokerCoverWithdrawSimple.test.tsx | 21 ++ ...oanBrokerCoverWithdrawWithCredentials.json | 281 ++++++++++++++++++ 9 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/test/mock_data/LoanBrokerCoverWithdrawWithCredentials.json diff --git a/public/locales/ca-CA/translations.json b/public/locales/ca-CA/translations.json index d7cc7909c..0d1f33337 100644 --- a/public/locales/ca-CA/translations.json +++ b/public/locales/ca-CA/translations.json @@ -641,6 +641,9 @@ "assets_maximum": null, "mptoken_metadata": null, "withdrawal_policy": null, + "vault_kind": null, + "subscription_date": null, + "redemption_date": null, "account_creates_vault": null, "vault_id": null, "single_asset_vault": null, @@ -673,6 +676,8 @@ "account_clawbacks_from_vault": null, "account_clawbacks_from_vault_amount_omitted": null, "account_deletes_vault": null, + "vault_delete_memo_data": null, + "memo_data": null, "vault_create_table_detail": null, "withdraws": null, "deletes": null, @@ -839,6 +844,8 @@ "available_to_borrow": null, "not_available": null, "first_come_first_served": null, + "open_ended": null, + "closed_ended": null, "loans": null, "loan_broker": null, "total_debt": null, diff --git a/public/locales/en-US/translations.json b/public/locales/en-US/translations.json index 5299eb166..97570e82a 100644 --- a/public/locales/en-US/translations.json +++ b/public/locales/en-US/translations.json @@ -839,7 +839,6 @@ "check_vault_id": "Please check the vault ID", "get_vault_failed": "Unable to load vault information at this time. Please try again later.", "private_vault": "Private Vault", - "deposits_allowed": "Deposits Allowed", "perm_domain_id": "Permissioned Domain ID", "total_value_locked": "Total Value Locked (TVL)", "shares": "Shares", diff --git a/public/locales/es-ES/translations.json b/public/locales/es-ES/translations.json index d7d75d8e9..0f47648a1 100644 --- a/public/locales/es-ES/translations.json +++ b/public/locales/es-ES/translations.json @@ -642,6 +642,9 @@ "assets_maximum": null, "mptoken_metadata": null, "withdrawal_policy": null, + "vault_kind": null, + "subscription_date": null, + "redemption_date": null, "account_creates_vault": null, "vault_id": null, "single_asset_vault": null, @@ -674,6 +677,8 @@ "account_clawbacks_from_vault": null, "account_clawbacks_from_vault_amount_omitted": null, "account_deletes_vault": null, + "vault_delete_memo_data": null, + "memo_data": null, "vault_create_table_detail": null, "withdraws": null, "deletes": null, @@ -840,6 +845,8 @@ "available_to_borrow": null, "not_available": null, "first_come_first_served": null, + "open_ended": null, + "closed_ended": null, "loans": null, "loan_broker": null, "total_debt": null, diff --git a/public/locales/fr-FR/translations.json b/public/locales/fr-FR/translations.json index f3cb3948b..4dc2df5aa 100644 --- a/public/locales/fr-FR/translations.json +++ b/public/locales/fr-FR/translations.json @@ -642,6 +642,9 @@ "assets_maximum": null, "mptoken_metadata": null, "withdrawal_policy": null, + "vault_kind": null, + "subscription_date": null, + "redemption_date": null, "account_creates_vault": null, "vault_id": null, "single_asset_vault": null, @@ -674,6 +677,8 @@ "account_clawbacks_from_vault": null, "account_clawbacks_from_vault_amount_omitted": null, "account_deletes_vault": null, + "vault_delete_memo_data": null, + "memo_data": null, "vault_create_table_detail": null, "withdraws": null, "deletes": null, @@ -840,6 +845,8 @@ "available_to_borrow": null, "not_available": null, "first_come_first_served": null, + "open_ended": null, + "closed_ended": null, "loans": null, "loan_broker": null, "total_debt": null, diff --git a/public/locales/ja-JP/translations.json b/public/locales/ja-JP/translations.json index 4911aad81..714c992ed 100644 --- a/public/locales/ja-JP/translations.json +++ b/public/locales/ja-JP/translations.json @@ -642,6 +642,9 @@ "assets_maximum": null, "mptoken_metadata": null, "withdrawal_policy": null, + "vault_kind": null, + "subscription_date": null, + "redemption_date": null, "account_creates_vault": null, "vault_id": null, "single_asset_vault": null, @@ -674,6 +677,8 @@ "account_clawbacks_from_vault": null, "account_clawbacks_from_vault_amount_omitted": null, "account_deletes_vault": null, + "vault_delete_memo_data": null, + "memo_data": null, "vault_create_table_detail": null, "withdraws": null, "deletes": null, @@ -840,6 +845,8 @@ "available_to_borrow": null, "not_available": null, "first_come_first_served": null, + "open_ended": null, + "closed_ended": null, "loans": null, "loan_broker": null, "total_debt": null, diff --git a/public/locales/ko-KR/translations.json b/public/locales/ko-KR/translations.json index a9b58ef47..f163796ca 100644 --- a/public/locales/ko-KR/translations.json +++ b/public/locales/ko-KR/translations.json @@ -642,6 +642,9 @@ "assets_maximum": null, "mptoken_metadata": null, "withdrawal_policy": null, + "vault_kind": null, + "subscription_date": null, + "redemption_date": null, "account_creates_vault": null, "vault_id": null, "single_asset_vault": null, @@ -674,6 +677,8 @@ "account_clawbacks_from_vault": null, "account_clawbacks_from_vault_amount_omitted": null, "account_deletes_vault": null, + "vault_delete_memo_data": null, + "memo_data": null, "vault_create_table_detail": null, "withdraws": null, "deletes": null, @@ -840,6 +845,8 @@ "available_to_borrow": null, "not_available": null, "first_come_first_served": null, + "open_ended": null, + "closed_ended": null, "loans": null, "loan_broker": null, "total_debt": null, diff --git a/public/locales/my-MM/translations.json b/public/locales/my-MM/translations.json index 7b56d8313..c97c83a07 100644 --- a/public/locales/my-MM/translations.json +++ b/public/locales/my-MM/translations.json @@ -642,6 +642,9 @@ "assets_maximum": null, "mptoken_metadata": null, "withdrawal_policy": null, + "vault_kind": null, + "subscription_date": null, + "redemption_date": null, "account_creates_vault": null, "vault_id": null, "single_asset_vault": null, @@ -674,6 +677,8 @@ "account_clawbacks_from_vault": null, "account_clawbacks_from_vault_amount_omitted": null, "account_deletes_vault": null, + "vault_delete_memo_data": null, + "memo_data": null, "vault_create_table_detail": null, "withdraws": null, "deletes": null, @@ -840,6 +845,8 @@ "available_to_borrow": null, "not_available": null, "first_come_first_served": null, + "open_ended": null, + "closed_ended": null, "loans": null, "loan_broker": null, "total_debt": null, diff --git a/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/test/LoanBrokerCoverWithdrawSimple.test.tsx b/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/test/LoanBrokerCoverWithdrawSimple.test.tsx index 4d67776a7..3fe7c3689 100644 --- a/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/test/LoanBrokerCoverWithdrawSimple.test.tsx +++ b/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/test/LoanBrokerCoverWithdrawSimple.test.tsx @@ -1,6 +1,7 @@ import { createSimpleRenderFactory, expectSimpleRowText } from '../../test' import { Simple } from '../Simple' import LoanBrokerCoverWithdraw from './mock_data/LoanBrokerCoverWithdraw.json' +import LoanBrokerCoverWithdrawWithCredentials from './mock_data/LoanBrokerCoverWithdrawWithCredentials.json' const renderComponent = createSimpleRenderFactory(Simple) @@ -22,6 +23,26 @@ describe('LoanBrokerCoverWithdraw: Simple', () => { 'destination', 'rH4absn9JcB8m943YRMNJpuR9HQs56hkr8', ) + expect( + container.querySelector('[data-testid="credential-id-0"]'), + ).not.toBeInTheDocument() + unmount() + }) + + it('renders credential IDs when present', () => { + const { container, unmount } = renderComponent( + LoanBrokerCoverWithdrawWithCredentials, + ) + expect( + container.querySelector('[data-testid="credential-id-0"]'), + ).toHaveTextContent( + 'EA85E9D5B4F0D8E36C5F1A4B8C2D3E4F5061728394A5B6C7D8E9F0A1B2C3D4E5', + ) + expect( + container.querySelector('[data-testid="credential-id-1"]'), + ).toHaveTextContent( + 'FB96FAE6C501E9F47D6F2B5C9D3E4F5061728394A5B6C7D8E9F0A1B2C3D4E5F6', + ) unmount() }) }) diff --git a/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/test/mock_data/LoanBrokerCoverWithdrawWithCredentials.json b/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/test/mock_data/LoanBrokerCoverWithdrawWithCredentials.json new file mode 100644 index 000000000..ca6fe3609 --- /dev/null +++ b/src/containers/shared/components/Transaction/LoanBrokerCoverWithdraw/test/mock_data/LoanBrokerCoverWithdrawWithCredentials.json @@ -0,0 +1,281 @@ +{ + "tx": { + "Account": "rfS5JMvtQk6w5tZbwpSnpx15U8qrXWW8yi", + "Amount": { + "currency": "USD", + "issuer": "ra8dG1xwi5dQTJx1fRNCc8gjSAdQMX3vV7", + "value": "5" + }, + "Destination": "rH4absn9JcB8m943YRMNJpuR9HQs56hkr8", + "Fee": "2000000", + "Flags": 0, + "LastLedgerSequence": 291195, + "LoanBrokerID": "7B3AF305C92293AF3F01088298E354E7B649F963427FA4B7F5414EF1383CB80B", + "NetworkID": 3222, + "Sequence": 291160, + "SigningPubKey": "ED99153B1435C4D98191569187E7AF9DF00319794C1A82366E6E2F87B741BA1BBA", + "TransactionType": "LoanBrokerCoverWithdraw", + "TxnSignature": "FFECC1F5D224CC74FA3FC30ED19CD9013461DD265F0B43F98D38FBF577E6B540F9C3EB952962233562556D219516B5D52E09733B4CA155636AE7391452934D06", + "ctid": "C004716800000C96", + "date": 1762469400000, + "hash": "04139F851E529667FE6038628B9ED7960C8225BBCA0E16512C6CB4E22A2FD0BA", + "inLedger": 291176, + "ledger_index": 291176, + "meta": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Account": "rBuMfvezoska1LReG1CBoayi8esBgqzpQ4", + "CoverAvailable": "5", + "CoverRateLiquidation": 5000, + "CoverRateMinimum": 1000, + "Data": "7B226D657461223A224C6F616E42726F6B6572204D65746164617461227D", + "DebtMaximum": "1000000000000000e-4", + "DebtTotal": "5.84883123436912", + "Flags": 0, + "LoanSequence": 2, + "ManagementFeeRate": 1000, + "Owner": "rfS5JMvtQk6w5tZbwpSnpx15U8qrXWW8yi", + "OwnerCount": 1, + "OwnerNode": "0", + "Sequence": 291154, + "VaultID": "AE7952AFEE76456A1ECA877E1797E9FF842E7FD87D1F2C856B7B1EE10C9654D7", + "VaultNode": "0" + }, + "LedgerEntryType": "LoanBroker", + "LedgerIndex": "7B3AF305C92293AF3F01088298E354E7B649F963427FA4B7F5414EF1383CB80B", + "PreviousFields": { + "CoverAvailable": "10" + }, + "PreviousTxnID": "C56440AE7007618356BA3E7C87E4822684156D08E6FC69194C864DBC624DFDA2", + "PreviousTxnLgrSeq": 291170 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rfS5JMvtQk6w5tZbwpSnpx15U8qrXWW8yi", + "Balance": "80000000", + "Flags": 0, + "OwnerCount": 5, + "Sequence": 291161 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "D3A16624DDD503F0F01A1C777E597D3513B30E06576BD5DB5C59FBC604BCEBFC", + "PreviousFields": { + "Balance": "82000000", + "Sequence": 291160 + }, + "PreviousTxnID": "680925BDAE5F9F25A12095390EE0F443A898A40B8A5ADAC273A1F83814B15A17", + "PreviousTxnLgrSeq": 291174 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-9.32219481235746" + }, + "Flags": 131072, + "HighLimit": { + "currency": "USD", + "issuer": "rH4absn9JcB8m943YRMNJpuR9HQs56hkr8", + "value": "1000000" + }, + "HighNode": "0", + "LowLimit": { + "currency": "USD", + "issuer": "ra8dG1xwi5dQTJx1fRNCc8gjSAdQMX3vV7", + "value": "0" + }, + "LowNode": "0" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "D9A3D6C6DFE031982ECF8591700201F7A0F62E4D74564D3810F90B622DC31168", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-4.32219481235746" + } + }, + "PreviousTxnID": "C56440AE7007618356BA3E7C87E4822684156D08E6FC69194C864DBC624DFDA2", + "PreviousTxnLgrSeq": 291170 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-5" + }, + "Flags": 2228224, + "HighLimit": { + "currency": "USD", + "issuer": "rBuMfvezoska1LReG1CBoayi8esBgqzpQ4", + "value": "0" + }, + "HighNode": "0", + "LowLimit": { + "currency": "USD", + "issuer": "ra8dG1xwi5dQTJx1fRNCc8gjSAdQMX3vV7", + "value": "0" + }, + "LowNode": "0" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "FDC8A4899C79C91D4329811A45695C59B8060F28EA7D923EB6F47E15F42FC312", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-10" + } + }, + "PreviousTxnID": "61300C6409AA68AEFFBEED82B1A446AADA8FFE11152E901C36C524D62F242B54", + "PreviousTxnLgrSeq": 291167 + } + } + ], + "TransactionIndex": 0, + "TransactionResult": "tesSUCCESS" + }, + "validated": true, + "CredentialIDs": [ + "EA85E9D5B4F0D8E36C5F1A4B8C2D3E4F5061728394A5B6C7D8E9F0A1B2C3D4E5", + "FB96FAE6C501E9F47D6F2B5C9D3E4F5061728394A5B6C7D8E9F0A1B2C3D4E5F6" + ] + }, + "meta": { + "AffectedNodes": [ + { + "ModifiedNode": { + "FinalFields": { + "Account": "rBuMfvezoska1LReG1CBoayi8esBgqzpQ4", + "CoverAvailable": "5", + "CoverRateLiquidation": 5000, + "CoverRateMinimum": 1000, + "Data": "7B226D657461223A224C6F616E42726F6B6572204D65746164617461227D", + "DebtMaximum": "1000000000000000e-4", + "DebtTotal": "5.84883123436912", + "Flags": 0, + "LoanSequence": 2, + "ManagementFeeRate": 1000, + "Owner": "rfS5JMvtQk6w5tZbwpSnpx15U8qrXWW8yi", + "OwnerCount": 1, + "OwnerNode": "0", + "Sequence": 291154, + "VaultID": "AE7952AFEE76456A1ECA877E1797E9FF842E7FD87D1F2C856B7B1EE10C9654D7", + "VaultNode": "0" + }, + "LedgerEntryType": "LoanBroker", + "LedgerIndex": "7B3AF305C92293AF3F01088298E354E7B649F963427FA4B7F5414EF1383CB80B", + "PreviousFields": { + "CoverAvailable": "10" + }, + "PreviousTxnID": "C56440AE7007618356BA3E7C87E4822684156D08E6FC69194C864DBC624DFDA2", + "PreviousTxnLgrSeq": 291170 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Account": "rfS5JMvtQk6w5tZbwpSnpx15U8qrXWW8yi", + "Balance": "80000000", + "Flags": 0, + "OwnerCount": 5, + "Sequence": 291161 + }, + "LedgerEntryType": "AccountRoot", + "LedgerIndex": "D3A16624DDD503F0F01A1C777E597D3513B30E06576BD5DB5C59FBC604BCEBFC", + "PreviousFields": { + "Balance": "82000000", + "Sequence": 291160 + }, + "PreviousTxnID": "680925BDAE5F9F25A12095390EE0F443A898A40B8A5ADAC273A1F83814B15A17", + "PreviousTxnLgrSeq": 291174 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-9.32219481235746" + }, + "Flags": 131072, + "HighLimit": { + "currency": "USD", + "issuer": "rH4absn9JcB8m943YRMNJpuR9HQs56hkr8", + "value": "1000000" + }, + "HighNode": "0", + "LowLimit": { + "currency": "USD", + "issuer": "ra8dG1xwi5dQTJx1fRNCc8gjSAdQMX3vV7", + "value": "0" + }, + "LowNode": "0" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "D9A3D6C6DFE031982ECF8591700201F7A0F62E4D74564D3810F90B622DC31168", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-4.32219481235746" + } + }, + "PreviousTxnID": "C56440AE7007618356BA3E7C87E4822684156D08E6FC69194C864DBC624DFDA2", + "PreviousTxnLgrSeq": 291170 + } + }, + { + "ModifiedNode": { + "FinalFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-5" + }, + "Flags": 2228224, + "HighLimit": { + "currency": "USD", + "issuer": "rBuMfvezoska1LReG1CBoayi8esBgqzpQ4", + "value": "0" + }, + "HighNode": "0", + "LowLimit": { + "currency": "USD", + "issuer": "ra8dG1xwi5dQTJx1fRNCc8gjSAdQMX3vV7", + "value": "0" + }, + "LowNode": "0" + }, + "LedgerEntryType": "RippleState", + "LedgerIndex": "FDC8A4899C79C91D4329811A45695C59B8060F28EA7D923EB6F47E15F42FC312", + "PreviousFields": { + "Balance": { + "currency": "USD", + "issuer": "rrrrrrrrrrrrrrrrrrrrBZbvji", + "value": "-10" + } + }, + "PreviousTxnID": "61300C6409AA68AEFFBEED82B1A446AADA8FFE11152E901C36C524D62F242B54", + "PreviousTxnLgrSeq": 291167 + } + } + ], + "TransactionIndex": 0, + "TransactionResult": "tesSUCCESS" + }, + "hash": "04139F851E529667FE6038628B9ED7960C8225BBCA0E16512C6CB4E22A2FD0BA", + "ledger_index": 291176, + "date": 1762469400000 +}