From c52fc0c9af1874dff55d36684670f78f133f44d2 Mon Sep 17 00:00:00 2001 From: Tommy Verrall Date: Mon, 8 Jun 2026 12:29:19 +0200 Subject: [PATCH] Last round of fixes - Align `get_pending_delegation_events` with active delegation identity resolution: `get_node_information` + `delegation_node_identity` synthetic fallback on registry miss. - Stop hiding pending delegation rows in `shouldHideDelegationFromList` when bonded-registry identity lookup missed. --- .../src/operations/mixnet/delegate.rs | 26 +++-- .../Delegation/PendingDelegationCard.tsx | 95 +++++++++++-------- .../src/utils/delegationListVisibility.ts | 4 +- .../unbondedDelegation.acceptance.test.ts | 10 ++ .../src/utils/unbondedDelegation.fixture.ts | 18 +++- 5 files changed, 101 insertions(+), 52 deletions(-) diff --git a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs index 81ec42da5c..bc36a6a350 100644 --- a/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs +++ b/nym-wallet/src-tauri/src/operations/mixnet/delegate.rs @@ -48,13 +48,25 @@ pub async fn get_pending_delegation_events( let mut client_specific_events = Vec::new(); for delegation_event in delegation_events { if delegation_event.address_matches(client.nyxd.address().as_ref()) { - let node_identity = client - .nyxd - .get_mixnode_details(delegation_event.mix_id) - .await? - .mixnode_details - .map(|d| d.bond_information.mix_node.identity_key) - .unwrap_or_default(); + let mut error_strings = Vec::new(); + let node_identity = match get_node_information( + client, + delegation_event.mix_id, + &mut error_strings, + ) + .await + { + Ok(node_details) => { + delegation_node_identity(&node_details, delegation_event.mix_id) + } + Err(err) => { + log::error!( + "Failed to resolve node identity for pending event mix_id = {}. Error: {err}", + delegation_event.mix_id + ); + delegation_node_identity(&None, delegation_event.mix_id) + } + }; client_specific_events .push(WrappedDelegationEvent::new(delegation_event, node_identity)); diff --git a/nym-wallet/src/components/Delegation/PendingDelegationCard.tsx b/nym-wallet/src/components/Delegation/PendingDelegationCard.tsx index 6af5f797ec..7c4eac65d7 100644 --- a/nym-wallet/src/components/Delegation/PendingDelegationCard.tsx +++ b/nym-wallet/src/components/Delegation/PendingDelegationCard.tsx @@ -2,45 +2,58 @@ import React from 'react'; import { Box, Chip, Paper, Stack, Tooltip, Typography } from '@mui/material'; import { WrappedDelegationEvent } from '@nymproject/types'; import { TauriLink as Link } from 'src/components/TauriLinkWrapper'; +import { formatDelegationNodeIdentityForDisplay, isUnbondedNodeIdentity } from 'src/utils/delegationIdentity'; -export const PendingDelegationCard = ({ item, explorerUrl }: { item: WrappedDelegationEvent; explorerUrl: string }) => ( - (t.palette.mode === 'dark' ? 'nym.nymWallet.nav.background' : 'nym.nymWallet.background.subtle'), - borderColor: 'divider', - }} - > - - - - {item.event.amount?.amount} {item.event.amount?.denom?.toUpperCase() ?? 'NYM'} - - - - Your delegation of {item.event.amount?.amount} {item.event.amount?.denom} will take effect when the new - epoch starts. There is a new epoch every hour. - - } - arrow - PopperProps={{ - sx: { - '& .MuiTooltip-tooltip': { textAlign: 'left' }, - }, - }} - > - - - - -); +export const PendingDelegationCard = ({ item, explorerUrl }: { item: WrappedDelegationEvent; explorerUrl: string }) => { + const displayIdentity = formatDelegationNodeIdentityForDisplay(item.node_identity, item.event.mix_id); + const nodeIsUnbonded = isUnbondedNodeIdentity(item.node_identity); + + return ( + + t.palette.mode === 'dark' ? 'nym.nymWallet.nav.background' : 'nym.nymWallet.background.subtle', + borderColor: 'divider', + }} + > + + {nodeIsUnbonded ? ( + + {displayIdentity} + + ) : ( + + )} + + {item.event.amount?.amount} {item.event.amount?.denom?.toUpperCase() ?? 'NYM'} + + + + Your delegation of {item.event.amount?.amount} {item.event.amount?.denom} will take effect when the new + epoch starts. There is a new epoch every hour. + + } + arrow + PopperProps={{ + sx: { + '& .MuiTooltip-tooltip': { textAlign: 'left' }, + }, + }} + > + + + + + ); +}; diff --git a/nym-wallet/src/utils/delegationListVisibility.ts b/nym-wallet/src/utils/delegationListVisibility.ts index 548f6f0312..c241507077 100644 --- a/nym-wallet/src/utils/delegationListVisibility.ts +++ b/nym-wallet/src/utils/delegationListVisibility.ts @@ -17,9 +17,7 @@ export function shouldHideDelegationFromList(item: DelegationListItem): boolean } if (isPendingDelegationItem(item)) { - if ((!item.node_identity || item.node_identity === '') && item.event && item.event.kind === 'Undelegate') { - return true; - } + // Pending rows carry mix_id on the event; do not hide when bonded-registry identity lookup missed. return false; } diff --git a/nym-wallet/src/utils/unbondedDelegation.acceptance.test.ts b/nym-wallet/src/utils/unbondedDelegation.acceptance.test.ts index c7dd2df2a3..d85f461974 100644 --- a/nym-wallet/src/utils/unbondedDelegation.acceptance.test.ts +++ b/nym-wallet/src/utils/unbondedDelegation.acceptance.test.ts @@ -11,6 +11,7 @@ import { EXAMPLE_UNBONDED_MIX_ID, buildFixedUnbondedWalletDelegation, buildLegacyHiddenUnbondedWalletDelegation, + buildPendingUndelegateEvent, } from './unbondedDelegation.fixture'; describe('unbonded delegation wallet visibility acceptance', () => { @@ -55,6 +56,15 @@ describe('unbonded delegation wallet visibility acceptance', () => { expect(searchDelegations([legacyRow], 'nonexistent-needle')).toHaveLength(0); }); + it('shows pending undelegate events when node identity lookup missed', () => { + const emptyIdentityPending = buildPendingUndelegateEvent(''); + const syntheticPending = buildPendingUndelegateEvent(`unbonded:${EXAMPLE_UNBONDED_MIX_ID}`); + + expect(shouldHideDelegationFromList(emptyIdentityPending)).toBe(false); + expect(shouldHideDelegationFromList(syntheticPending)).toBe(false); + expect(filterVisibleDelegations([emptyIdentityPending, syntheticPending])).toHaveLength(2); + }); + it('finds the row by historical identity when the backend preserved it', () => { const fixedRow = buildFixedUnbondedWalletDelegation({ historicalNodeIdentity: EXAMPLE_HISTORICAL_NODE_IDENTITY, diff --git a/nym-wallet/src/utils/unbondedDelegation.fixture.ts b/nym-wallet/src/utils/unbondedDelegation.fixture.ts index 83fee7cd07..4b2befb28d 100644 --- a/nym-wallet/src/utils/unbondedDelegation.fixture.ts +++ b/nym-wallet/src/utils/unbondedDelegation.fixture.ts @@ -1,4 +1,4 @@ -import type { DelegationWithEverything } from '@nymproject/types'; +import type { DelegationWithEverything, WrappedDelegationEvent } from '@nymproject/types'; import { UNBONDED_NODE_IDENTITY_PREFIX } from './delegationIdentity'; /** Synthetic mix_id used in wallet unbonded-delegation tests. */ @@ -53,3 +53,19 @@ export function buildLegacyHiddenUnbondedWalletDelegation( mixnode_is_unbonding: null, }; } + +export function buildPendingUndelegateEvent( + nodeIdentity: string, + mixId: number = EXAMPLE_UNBONDED_MIX_ID, +): WrappedDelegationEvent { + return { + node_identity: nodeIdentity, + event: { + kind: 'Undelegate', + mix_id: mixId, + address: EXAMPLE_DELEGATOR_ADDRESS, + amount: { amount: '1000000', denom: 'nym' }, + proxy: null, + }, + }; +}