Skip to content

Commit e144891

Browse files
authored
fix: cp-12.14.0 bridge status validation causes transactions to be pending indefinitely (#30918)
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> ## **Description** <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> Depends on bridge-api update: consensys-vertical-apps/va-mmcx-bridge-api#182 Addresses these issues - getTxStatus reponses get rejected due to EVM-specific validation - Activity details for bridging from EVM > Solana has missing network info [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/30918?quickstart=1) ## **Related issues** Fixes: #30885 , #30801 ## **Manual testing steps** 1. Bridge EVM <> EVM 2. Bridge EVM <> Solana 3. Bridge Solana <> EVM 4. After 5 mins, verify that there are no failing getTxStatus calls in the background 5. Verify that polling for these new transactions stop eventually ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ![Screenshot 2025-03-11 at 7 14 05 PM](https://github.com/user-attachments/assets/ff080c6a-8791-4194-bf2b-e3224e7bb745) ![Screenshot 2025-03-11 at 7 14 13 PM](https://github.com/user-attachments/assets/b3c74449-55f4-4ef5-a6a1-82162a7561ac) ### **After** <!-- [screenshots/recordings] --> ![Screenshot 2025-03-11 at 7 09 47 PM](https://github.com/user-attachments/assets/b2688e28-bc2c-453f-b721-7ba326989d0c) ![Screenshot 2025-03-11 at 7 09 55 PM](https://github.com/user-attachments/assets/12c28cec-00df-4141-9c4d-82e598464e7f) ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.
1 parent fd94e80 commit e144891

File tree

5 files changed

+70
-55
lines changed

5 files changed

+70
-55
lines changed

app/scripts/controllers/bridge-status/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const getStatusRequestDto = (
1919
const statusRequestNoQuoteFormatted = Object.fromEntries(
2020
Object.entries(statusRequestNoQuote).map(([key, value]) => [
2121
key,
22-
value.toString(),
22+
value?.toString(),
2323
]),
2424
) as unknown as Omit<StatusRequestDto, 'requestId'>;
2525

app/scripts/controllers/bridge-status/validators.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import { validHex, validateData } from '../../../../shared/lib/swaps-utils';
2-
import { isValidHexAddress } from '../../../../shared/modules/hexstring-utils';
1+
import { truthyString, validateData } from '../../../../shared/lib/swaps-utils';
32
import {
43
BridgeId,
54
DestChainStatus,
@@ -35,7 +34,7 @@ const assetValidators = [
3534
{
3635
property: 'address',
3736
type: 'string',
38-
validator: (v: unknown): v is string => isValidHexAddress(v as string),
37+
validator: (v: unknown): v is string => truthyString(v as string),
3938
},
4039
{
4140
property: 'symbol',
@@ -75,7 +74,7 @@ const srcChainStatusValidators = [
7574
{
7675
property: 'txHash',
7776
type: 'string',
78-
validator: validHex,
77+
validator: truthyString,
7978
},
8079
{
8180
property: 'amount',
@@ -159,7 +158,7 @@ export const validators = [
159158
property: 'bridge',
160159
type: 'string|undefined',
161160
validator: (v: unknown): v is BridgeId | undefined =>
162-
v === undefined || Object.values(BridgeId).includes(v as BridgeId),
161+
v === undefined || typeof v === 'string',
163162
},
164163
{
165164
property: 'isExpectedToken',

shared/modules/bridge-utils/caip-formatters.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,10 @@ export const formatAddressToString = (address: string) => {
9797
}
9898
return addressWithoutPrefix;
9999
};
100+
101+
export const formatChainIdToHexOrCaip = (chainId: number) => {
102+
if (chainId === ChainId.SOLANA) {
103+
return MultichainNetworks.SOLANA;
104+
}
105+
return formatChainIdToHex(chainId);
106+
};

ui/hooks/bridge/useBridgeChainInfo.ts

Lines changed: 37 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,29 @@
11
import { useSelector } from 'react-redux';
22
import {
3-
TransactionMeta,
3+
type TransactionMeta,
44
TransactionType,
55
} from '@metamask/transaction-controller';
6-
import { Hex } from '@metamask/utils';
7-
import { NetworkConfiguration } from '@metamask/network-controller';
8-
import { Numeric } from '../../../shared/modules/Numeric';
9-
import { getNetworkConfigurationsByChainId } from '../../../shared/modules/selectors/networks';
10-
import { BridgeHistoryItem } from '../../../shared/types/bridge-status';
6+
import type { NetworkConfiguration } from '@metamask/network-controller';
7+
import type { Hex } from '@metamask/utils';
8+
import type { BridgeHistoryItem } from '../../../shared/types/bridge-status';
119
import {
1210
CHAIN_ID_TO_CURRENCY_SYMBOL_MAP,
1311
NETWORK_TO_NAME_MAP,
1412
} from '../../../shared/constants/network';
1513
import { CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP } from '../../../shared/constants/common';
14+
import { getMultichainNetworkConfigurationsByChainId } from '../../selectors';
15+
import { formatChainIdToHexOrCaip } from '../../../shared/modules/bridge-utils/caip-formatters';
1616

1717
const getSourceAndDestChainIds = ({
1818
bridgeHistoryItem,
1919
}: UseBridgeChainInfoProps) => {
20-
const hexSrcChainId = bridgeHistoryItem
21-
? (new Numeric(
22-
bridgeHistoryItem.quote.srcChainId,
23-
10,
24-
).toPrefixedHexString() as Hex)
25-
: undefined;
26-
const hexDestChainId = bridgeHistoryItem
27-
? (new Numeric(
28-
bridgeHistoryItem.quote.destChainId,
29-
10,
30-
).toPrefixedHexString() as Hex)
31-
: undefined;
32-
3320
return {
34-
hexSrcChainId,
35-
hexDestChainId,
21+
srcChainId: bridgeHistoryItem
22+
? formatChainIdToHexOrCaip(bridgeHistoryItem.quote.srcChainId)
23+
: undefined,
24+
destChainId: bridgeHistoryItem
25+
? formatChainIdToHexOrCaip(bridgeHistoryItem.quote.destChainId)
26+
: undefined,
3627
};
3728
};
3829

@@ -45,9 +36,8 @@ export default function useBridgeChainInfo({
4536
bridgeHistoryItem,
4637
srcTxMeta,
4738
}: UseBridgeChainInfoProps) {
48-
const networkConfigurationsByChainId = useSelector(
49-
getNetworkConfigurationsByChainId,
50-
);
39+
const [networkConfigurationsByChainId] =
40+
useSelector(getMultichainNetworkConfigurationsByChainId) ?? [];
5141

5242
if (srcTxMeta?.type !== TransactionType.bridge) {
5343
return {
@@ -56,51 +46,55 @@ export default function useBridgeChainInfo({
5646
};
5747
}
5848

59-
const { hexSrcChainId, hexDestChainId } = getSourceAndDestChainIds({
49+
const { srcChainId, destChainId } = getSourceAndDestChainIds({
6050
bridgeHistoryItem,
6151
});
6252

63-
if (!hexSrcChainId || !hexDestChainId) {
53+
if (!srcChainId || !destChainId) {
6454
return {
6555
srcNetwork: undefined,
6656
destNetwork: undefined,
6757
};
6858
}
6959

7060
// Source chain info
71-
const srcNetwork = networkConfigurationsByChainId[hexSrcChainId]
72-
? networkConfigurationsByChainId[hexSrcChainId]
61+
const srcNetwork = networkConfigurationsByChainId[
62+
srcChainId as keyof typeof networkConfigurationsByChainId
63+
]
64+
? networkConfigurationsByChainId[
65+
srcChainId as keyof typeof networkConfigurationsByChainId
66+
]
7367
: undefined;
74-
const fallbackSrcNetwork: NetworkConfiguration = {
75-
chainId: hexSrcChainId,
76-
name: NETWORK_TO_NAME_MAP[
77-
hexSrcChainId as keyof typeof NETWORK_TO_NAME_MAP
78-
],
68+
const fallbackSrcNetwork = {
69+
chainId: srcChainId,
70+
name: NETWORK_TO_NAME_MAP[srcChainId as keyof typeof NETWORK_TO_NAME_MAP],
7971
nativeCurrency:
8072
CHAIN_ID_TO_CURRENCY_SYMBOL_MAP[
81-
hexSrcChainId as keyof typeof CHAIN_ID_TO_CURRENCY_SYMBOL_MAP
73+
srcChainId as keyof typeof CHAIN_ID_TO_CURRENCY_SYMBOL_MAP
8274
],
8375
defaultBlockExplorerUrlIndex: 0,
84-
blockExplorerUrls: [CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[hexSrcChainId]],
76+
blockExplorerUrls: [CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[srcChainId]],
8577
defaultRpcEndpointIndex: 0,
8678
rpcEndpoints: [],
8779
};
8880

8981
// Dest chain info
90-
const destNetwork = networkConfigurationsByChainId[hexDestChainId]
91-
? networkConfigurationsByChainId[hexDestChainId]
82+
const destNetwork = networkConfigurationsByChainId[
83+
destChainId as keyof typeof networkConfigurationsByChainId
84+
]
85+
? networkConfigurationsByChainId[
86+
destChainId as keyof typeof networkConfigurationsByChainId
87+
]
9288
: undefined;
9389
const fallbackDestNetwork: NetworkConfiguration = {
94-
chainId: hexDestChainId,
95-
name: NETWORK_TO_NAME_MAP[
96-
hexDestChainId as keyof typeof NETWORK_TO_NAME_MAP
97-
],
90+
chainId: destChainId as Hex,
91+
name: NETWORK_TO_NAME_MAP[destChainId as keyof typeof NETWORK_TO_NAME_MAP],
9892
nativeCurrency:
9993
CHAIN_ID_TO_CURRENCY_SYMBOL_MAP[
100-
hexDestChainId as keyof typeof CHAIN_ID_TO_CURRENCY_SYMBOL_MAP
94+
destChainId as keyof typeof CHAIN_ID_TO_CURRENCY_SYMBOL_MAP
10195
],
10296
defaultBlockExplorerUrlIndex: 0,
103-
blockExplorerUrls: [CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[hexDestChainId]],
97+
blockExplorerUrls: [CHAINID_DEFAULT_BLOCK_EXPLORER_URL_MAP[destChainId]],
10498
defaultRpcEndpointIndex: 0,
10599
rpcEndpoints: [],
106100
};

ui/pages/bridge/transaction-details/transaction-details.tsx

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import React, { useContext } from 'react';
22
import { useSelector } from 'react-redux';
33
import { useHistory, useParams, useLocation } from 'react-router-dom';
4-
import { NetworkConfiguration } from '@metamask/network-controller';
54
import { TransactionMeta } from '@metamask/transaction-controller';
65
import { BigNumber } from 'bignumber.js';
6+
import type { EvmNetworkConfiguration } from '@metamask/multichain-network-controller';
7+
import { isStrictHexString } from '@metamask/utils';
78
import {
89
AvatarNetwork,
910
AvatarNetworkSize,
@@ -69,7 +70,7 @@ import BridgeExplorerLinks from './bridge-explorer-links';
6970
import BridgeStepList from './bridge-step-list';
7071

7172
const getBlockExplorerUrl = (
72-
networkConfiguration: NetworkConfiguration | undefined,
73+
networkConfiguration: EvmNetworkConfiguration | undefined,
7374
txHash: string | undefined,
7475
) => {
7576
if (!networkConfiguration || !txHash) {
@@ -195,10 +196,16 @@ const CrossChainSwapTxDetails = () => {
195196
});
196197

197198
const srcTxHash = srcChainTxMeta?.hash;
198-
const srcBlockExplorerUrl = getBlockExplorerUrl(srcNetwork, srcTxHash);
199+
const srcBlockExplorerUrl = getBlockExplorerUrl(
200+
srcNetwork as EvmNetworkConfiguration,
201+
srcTxHash,
202+
);
199203

200204
const destTxHash = bridgeHistoryItem?.status.destChain?.txHash;
201-
const destBlockExplorerUrl = getBlockExplorerUrl(destNetwork, destTxHash);
205+
const destBlockExplorerUrl = getBlockExplorerUrl(
206+
destNetwork as EvmNetworkConfiguration,
207+
destTxHash,
208+
);
202209

203210
const status = bridgeHistoryItem
204211
? bridgeHistoryItem?.status.status
@@ -333,8 +340,16 @@ const CrossChainSwapTxDetails = () => {
333340

334341
{/* Links to block explorers */}
335342
<BridgeExplorerLinks
336-
srcChainId={srcNetwork?.chainId}
337-
destChainId={destNetwork?.chainId}
343+
srcChainId={
344+
isStrictHexString(srcNetwork?.chainId)
345+
? srcNetwork?.chainId
346+
: undefined
347+
}
348+
destChainId={
349+
isStrictHexString(destNetwork?.chainId)
350+
? destNetwork?.chainId
351+
: undefined
352+
}
338353
srcBlockExplorerUrl={srcBlockExplorerUrl}
339354
destBlockExplorerUrl={destBlockExplorerUrl}
340355
/>

0 commit comments

Comments
 (0)