diff --git a/app/components/Views/ActivityList/ActivityList.test.tsx b/app/components/Views/ActivityList/ActivityList.test.tsx
index d05d9d19ce3..f162b6db203 100644
--- a/app/components/Views/ActivityList/ActivityList.test.tsx
+++ b/app/components/Views/ActivityList/ActivityList.test.tsx
@@ -326,18 +326,6 @@ jest.mock('../../UI/ActivityListItemRow/ActivityListItemRow', () => ({
resolveActivityListItemTitle: jest.fn(() => 'Activity title'),
}));
-jest.mock('../../UI/MultichainBridgeTransactionListItem', () => {
- const { Text, View } = jest.requireActual('react-native');
- return {
- __esModule: true,
- default: ({ transaction }: { transaction: { id: string } }) => (
-
- Bridge tx
-
- ),
- };
-});
-
jest.mock('../../UI/Transactions/TransactionsFooter', () => {
const { Text, TouchableOpacity } = jest.requireActual('react-native');
return {
@@ -382,7 +370,13 @@ jest.mock('../../UI/Bridge/hooks/useBridgeHistoryItemBySrcTxHash', () => ({
useBridgeHistoryItemBySrcTxHash: jest.fn(() => ({
bridgeHistoryItemsBySrcTxHash: {
'0xconfirmed': { title: 'bridge-history' },
+ // Same-chain swap (no quote → not a cross-chain bridge).
solanaBridge: { title: 'solana-bridge' },
+ // Cross-chain bridge (src and dest chains differ).
+ solanaCross: {
+ title: 'solana-cross-bridge',
+ quote: { srcChainId: 'solana:mainnet', destChainId: 1 },
+ },
},
})),
}));
@@ -390,6 +384,11 @@ jest.mock('../../UI/Bridge/hooks/useBridgeHistoryItemBySrcTxHash', () => ({
jest.mock('../../UI/Bridge/utils/transaction-history', () => ({
getSwapBridgeTxActivityTitle: jest.fn(() => 'Bridge title'),
handleUnifiedSwapsTxHistoryItemClick: jest.fn(),
+ isBridgeTxHistoryItemBridge: jest.fn(
+ (item: { quote?: { srcChainId?: unknown; destChainId?: unknown } }) =>
+ item?.quote !== undefined &&
+ item.quote.srcChainId !== item.quote.destChainId,
+ ),
}));
jest.mock('../../../util/multichain/multichainTransactionTokenScan', () => ({
@@ -1711,7 +1710,7 @@ describe('ActivityList', () => {
});
});
- it('renders non-EVM bridge rows and footer when only non-EVM chains are enabled', () => {
+ it('renders non-EVM swap/bridge rows through ActivityListItemRow with the bridge-history title', () => {
selectorValues.enabledEvm = [];
selectorValues.enabledNonEvm = ['solana:mainnet'];
selectorValues.nonEvmState = {
@@ -1732,11 +1731,53 @@ describe('ActivityList', () => {
render();
- expect(screen.getByTestId('bridge-solanaBridge')).toBeOnTheScreen();
+ expect(screen.getByTestId('row-solanaBridge')).toBeOnTheScreen();
+ expect(screen.getByText('Bridge title')).toBeOnTheScreen();
fireEvent.press(screen.getByTestId('non-evm-footer'));
expect(mockNavigate).toHaveBeenCalledWith('Webview', {
params: { url: 'https://solana.explorer/address/sol' },
screen: 'SimpleWebview',
});
});
+
+ it('routes non-EVM cross-chain bridge taps to the unified swaps detail screen', () => {
+ selectorValues.enabledNonEvm = ['solana:mainnet'];
+ selectorValues.nonEvmState = {
+ transactions: [{ chain: 'solana:mainnet', id: 'solanaCross' }],
+ };
+
+ render(>} />);
+
+ fireEvent.press(screen.getByTestId('row-solanaCross'));
+
+ expect(handleUnifiedSwapsTxHistoryItemClick).toHaveBeenCalledWith({
+ navigation: expect.any(Object),
+ multiChainTx: { chain: 'solana:mainnet', id: 'solanaCross' },
+ bridgeTxHistoryItem: expect.objectContaining({
+ title: 'solana-cross-bridge',
+ }),
+ });
+ expect(mockNavigate).not.toHaveBeenCalled();
+ });
+
+ it('opens the multichain details sheet for non-EVM same-chain swaps with bridge history', () => {
+ selectorValues.enabledNonEvm = ['solana:mainnet'];
+ selectorValues.nonEvmState = {
+ transactions: [
+ { chain: 'solana:mainnet', id: 'solanaBridge', from: [], to: [] },
+ ],
+ };
+
+ render(>} />);
+
+ fireEvent.press(screen.getByTestId('row-solanaBridge'));
+
+ expect(handleUnifiedSwapsTxHistoryItemClick).not.toHaveBeenCalled();
+ expect(mockNavigate).toHaveBeenCalledWith(
+ Routes.MODAL.ROOT_MODAL_FLOW,
+ expect.objectContaining({
+ screen: Routes.SHEET.MULTICHAIN_TRANSACTION_DETAILS,
+ }),
+ );
+ });
});
diff --git a/app/components/Views/ActivityList/ActivityList.tsx b/app/components/Views/ActivityList/ActivityList.tsx
index 57adf4be0c6..d18540de37e 100644
--- a/app/components/Views/ActivityList/ActivityList.tsx
+++ b/app/components/Views/ActivityList/ActivityList.tsx
@@ -72,8 +72,8 @@ import { useBridgeHistoryItemBySrcTxHash } from '../../UI/Bridge/hooks/useBridge
import {
getSwapBridgeTxActivityTitle,
handleUnifiedSwapsTxHistoryItemClick,
+ isBridgeTxHistoryItemBridge,
} from '../../UI/Bridge/utils/transaction-history';
-import MultichainBridgeTransactionListItem from '../../UI/MultichainBridgeTransactionListItem';
import TransactionsFooter from '../../UI/Transactions/TransactionsFooter';
// eslint-disable-next-line import-x/no-restricted-paths -- TODO(ADR-0020): route-isolation backlog
import MultichainTransactionsFooter from '../MultichainTransactionsView/MultichainTransactionsFooter';
@@ -82,7 +82,6 @@ import { getAddressUrl } from '../../../core/Multichain/utils';
import { CancelSpeedupModal } from '../confirmations/components/modals/cancel-speedup-modal';
import styleSheet from './ActivityList.styles';
import { useUnifiedTxActions } from './useUnifiedTxActions';
-import { TransactionDetailLocation } from '../../../core/Analytics/events/transactions';
import { useTransactionAutoScroll } from './useTransactionAutoScroll';
import useBlockExplorer from '../../hooks/useBlockExplorer';
import { selectBridgeHistoryForAccount } from '../../../selectors/bridgeStatusController';
@@ -172,7 +171,6 @@ interface ActivityListProps {
header?: React.ReactElement;
tabLabel?: string;
chainId?: string; // used by non-EVM list items for explorer links
- location?: TransactionDetailLocation;
/**
* Shared value updated on the UI thread with the list's vertical scroll
* offset, for driving scroll-linked animations in the parent.
@@ -190,15 +188,7 @@ export interface ActivityListHandle {
const ActivityList = forwardRef(
(
- {
- header,
- chainId,
- location,
- scrollY,
- typeFilter,
- networkFilter,
- subFilterKinds,
- },
+ { header, chainId, scrollY, typeFilter, networkFilter, subFilterKinds },
ref,
) => {
const navigation = useNavigation();
@@ -818,6 +808,27 @@ const ActivityList = forwardRef(
const { raw } = item;
if (!raw) return;
+ // Non-EVM swaps/bridges submitted from this device carry a
+ // bridge-history entry. Cross-chain bridges keep their dedicated
+ // bridge-status screen, mirroring hasDedicatedDetailScreen for local
+ // EVM bridges; same-chain swaps fall through to the shared detail flows.
+ if (raw.type === 'keyringTransaction') {
+ const keyringBridgeHistoryItem = getBridgeHistoryItemByHash(
+ item.hash,
+ );
+ if (
+ keyringBridgeHistoryItem &&
+ isBridgeTxHistoryItemBridge(keyringBridgeHistoryItem)
+ ) {
+ handleUnifiedSwapsTxHistoryItemClick({
+ navigation,
+ multiChainTx: raw.data,
+ bridgeTxHistoryItem: keyringBridgeHistoryItem,
+ });
+ return;
+ }
+ }
+
if (isTransactionsRedesignEnabled) {
const detailsRoute = getActivityDetailsRoute(item);
if (detailsRoute) {
@@ -1219,30 +1230,9 @@ const ActivityList = forwardRef(
}
const { item } = groupedItem;
- const raw = item.raw;
-
- // Non-EVM bridge transactions: route to MultichainBridgeTransactionListItem.
- if (raw?.type === 'keyringTransaction') {
- const srcTxHash = raw.data.id;
- const bridgeHistoryItem = getBridgeHistoryItemByHash(srcTxHash);
- if (bridgeHistoryItem) {
- return (
-
- );
- }
- }
- // All other items (API EVM confirmed, completed local EVM, non-EVM non-bridge):
- // render from the shared ActivityListItem shape.
+ // All items (API EVM confirmed, completed local EVM, non-EVM) render from
+ // the shared ActivityListItem shape.
//
// Preserve the legacy Activity title for swap/bridge rows (e.g.
// "Swap ETH to USDC", "Bridge to Optimism") by deriving it from bridge