Skip to content

Commit

Permalink
chore: switch between evm<>non-evm networks within Bridge experience (#…
Browse files Browse the repository at this point in the history
…30595)

<!--
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**

Changes
- dedupes native assets in the Bridge destination asset list
- change account on network change when switching between evm and
non-evm networks

<!--
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?
-->

[![Open in GitHub
Codespaces](/~https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/30595?quickstart=1)

## **Related issues**

Fixes:

## **Manual testing steps**

1. Switch to Ethereum network and go to Bridge page
2. Select SOL as the src token
3. navigate back to the homepage
4. verify that the network and account are set to solana
5. Repeat 2,3,4 but from solana to ethereum

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **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.
  • Loading branch information
micaelae authored Feb 27, 2025
1 parent 4e6ff51 commit d9f27c4
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 71 deletions.
7 changes: 6 additions & 1 deletion shared/modules/bridge-utils/bridge.util.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ describe('Bridge utils', () => {
symbol: 'DEF',
},
{
address: '0x124',
address: 'NONevmTOken1324fgcdrskljffsiodujfkl,jfd',
symbol: 'JKL',
decimals: 16,
},
Expand Down Expand Up @@ -218,6 +218,11 @@ describe('Bridge utils', () => {
symbol: 'DEF',
aggregators: ['lifi'],
},
'NONevmTOken1324fgcdrskljffsiodujfkl,jfd': {
address: 'NONevmTOken1324fgcdrskljffsiodujfkl,jfd',
decimals: 16,
symbol: 'JKL',
},
'0x1f9840a85d5af5bf1d1762f925bdaddc4201f984': {
address: '0x1f9840a85d5af5bf1d1762f925bdaddc4201f984',
decimals: 16,
Expand Down
2 changes: 1 addition & 1 deletion shared/modules/bridge-utils/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const ASSET_VALIDATORS = [

export const TOKEN_VALIDATORS = [
{ property: 'decimals', type: 'number' },
{ property: 'address', type: 'string', validator: isValidHexAddress },
{ property: 'address', type: 'string', validator: isValidString },
{
property: 'symbol',
type: 'string',
Expand Down
84 changes: 45 additions & 39 deletions ui/hooks/bridge/__snapshots__/useTokensWithFiltering.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,6 @@ exports[`useTokensWithFiltering should fetch bridge tokens if cached tokens have
"tokenFiatAmount": null,
"type": "TOKEN",
},
{
"address": "0x0000000000000000000000000000000000000000",
"balance": "0",
"chainId": "0x1",
"decimals": 18,
"iconUrl": "./images/eth_logo.svg",
"image": "./images/eth_logo.svg",
"name": "Ether",
"string": "0",
"symbol": "ETH",
"type": "NATIVE",
},
{
"address": "0x6b3595068778dd592e39a122f4f5a5cf09c90fe2",
"aggregators": [],
Expand Down Expand Up @@ -113,7 +101,7 @@ exports[`useTokensWithFiltering should fetch bridge tokens if cached tokens have
"type": "TOKEN",
},
{
"address": "0x0000000000000000000000000000000000000000",
"address": null,
"balance": "0",
"chainId": "0x1",
"decimals": 18,
Expand All @@ -124,6 +112,20 @@ exports[`useTokensWithFiltering should fetch bridge tokens if cached tokens have
"symbol": "ETH",
"type": "NATIVE",
},
{
"address": "0x12652c6d93fdb6f4f37d48a8687783c782bb0d10",
"aggregators": [],
"balance": "",
"chainId": "0x1",
"decimals": 18,
"erc20": true,
"iconUrl": "images/contract/NGL.svg",
"image": "images/contract/NGL.svg",
"name": "Entangle",
"string": undefined,
"symbol": "NGL",
"type": "TOKEN",
},
]
`;

Expand Down Expand Up @@ -184,18 +186,6 @@ exports[`useTokensWithFiltering should return all tokens when chainId !== active
"tokenFiatAmount": null,
"type": "TOKEN",
},
{
"address": "0x0000000000000000000000000000000000000000",
"balance": "0",
"chainId": "0x1",
"decimals": 18,
"iconUrl": "./images/eth_logo.svg",
"image": "./images/eth_logo.svg",
"name": "Ether",
"string": "0",
"symbol": "ETH",
"type": "NATIVE",
},
{
"address": "0x6b3595068778dd592e39a122f4f5a5cf09c90fe2",
"aggregators": [],
Expand Down Expand Up @@ -240,7 +230,7 @@ exports[`useTokensWithFiltering should return all tokens when chainId !== active
"type": "TOKEN",
},
{
"address": "0x0000000000000000000000000000000000000000",
"address": null,
"balance": "0",
"chainId": "0x1",
"decimals": 18,
Expand All @@ -251,23 +241,25 @@ exports[`useTokensWithFiltering should return all tokens when chainId !== active
"symbol": "ETH",
"type": "NATIVE",
},
{
"address": "0x12652c6d93fdb6f4f37d48a8687783c782bb0d10",
"aggregators": [],
"balance": "",
"chainId": "0x1",
"decimals": 18,
"erc20": true,
"iconUrl": "images/contract/NGL.svg",
"image": "images/contract/NGL.svg",
"name": "Entangle",
"string": undefined,
"symbol": "NGL",
"type": "TOKEN",
},
]
`;

exports[`useTokensWithFiltering should return all tokens when chainId !== activeChainId and chainId has not been imported, sorted by balance 1`] = `
[
{
"address": "0x0000000000000000000000000000000000000000",
"balance": "0",
"chainId": "0x89",
"decimals": 18,
"iconUrl": "./images/pol-token.svg",
"image": "./images/pol-token.svg",
"name": "Polygon",
"string": "0",
"symbol": "POL",
"type": "NATIVE",
},
{
"address": "0x6b3595068778dd592e39a122f4f5a5cf09c90fe2",
"aggregators": [],
Expand Down Expand Up @@ -312,7 +304,7 @@ exports[`useTokensWithFiltering should return all tokens when chainId !== active
"type": "TOKEN",
},
{
"address": "0x0000000000000000000000000000000000000000",
"address": null,
"balance": "0",
"chainId": "0x89",
"decimals": 18,
Expand Down Expand Up @@ -393,5 +385,19 @@ exports[`useTokensWithFiltering should return all tokens when chainId !== active
"symbol": "SEED",
"type": "TOKEN",
},
{
"address": "0x487d62468282bd04ddf976631c23128a425555ee",
"aggregators": [],
"balance": "",
"chainId": "0x89",
"decimals": 5,
"erc20": true,
"iconUrl": "images/contract/UPC.svg",
"image": "images/contract/UPC.svg",
"name": "UPCX",
"string": undefined,
"symbol": "UPC",
"type": "TOKEN",
},
]
`;
31 changes: 5 additions & 26 deletions ui/hooks/bridge/useTokensWithFiltering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ import {
getAllDetectedTokensForSelectedAddress,
selectERC20TokensByChain,
} from '../../selectors';
import {
SWAPS_CHAINID_DEFAULT_TOKEN_MAP,
SwapsTokenObject,
} from '../../../shared/constants/swaps';
import { SwapsTokenObject } from '../../../shared/constants/swaps';
import {
AssetWithDisplayData,
ERC20Asset,
Expand Down Expand Up @@ -119,7 +116,7 @@ export const useTokensWithFiltering = (
return {
...sharedFields,
type: AssetType.native,
address: zeroAddress(),
address: token.address === zeroAddress() ? null : token.address,
image:
CHAIN_ID_TOKEN_IMAGE_MAP[
chainId as keyof typeof CHAIN_ID_TOKEN_IMAGE_MAP
Expand Down Expand Up @@ -203,25 +200,7 @@ export const useTokensWithFiltering = (
}
}

// Yield the native token for the selected chain
const nativeToken =
SWAPS_CHAINID_DEFAULT_TOKEN_MAP[
chainId as keyof typeof SWAPS_CHAINID_DEFAULT_TOKEN_MAP
];
if (
nativeToken &&
shouldAddToken(
nativeToken.symbol,
nativeToken.address ?? undefined,
chainId,
)
) {
const tokenWithData = buildTokenData(nativeToken);
if (tokenWithData) {
yield tokenWithData;
}
}

// Yield tokens for solana from TokenApi V3 then return
if (chainId === MultichainNetworks.SOLANA) {
// Yield topTokens from selected chain
for (const { address: tokenAddress } of topTokens) {
Expand All @@ -244,7 +223,7 @@ export const useTokensWithFiltering = (
}
}

// Yield other tokens from selected chain
// Yield Solana top tokens
for (const token_ of Object.values(tokenList)) {
if (
token_ &&
Expand All @@ -266,7 +245,7 @@ export const useTokensWithFiltering = (
return;
}

// Yield topTokens from selected chain
// Yield topTokens from selected EVM chain
for (const token_ of topTokens) {
const matchedToken = tokenList?.[token_.address];
if (
Expand Down
38 changes: 34 additions & 4 deletions ui/pages/bridge/prepare/prepare-bridge-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ import {
import { useI18nContext } from '../../../hooks/useI18nContext';
import { SWAPS_CHAINID_DEFAULT_TOKEN_MAP } from '../../../../shared/constants/swaps';
import { useTokensWithFiltering } from '../../../hooks/bridge/useTokensWithFiltering';
import { setActiveNetwork } from '../../../store/actions';
import {
setActiveNetwork,
setActiveNetworkWithError,
setSelectedAccount,
} from '../../../store/actions';
import type { GenericQuoteRequest } from '../../../../shared/types/bridge';
import { calcTokenValue } from '../../../../shared/lib/swaps-utils';
import {
Expand Down Expand Up @@ -99,9 +103,14 @@ import { BRIDGE_QUOTE_MAX_RETURN_DIFFERENCE_PERCENTAGE } from '../../../../share
import { getIntlLocale } from '../../../ducks/locale/locale';
import { useIsMultichainSwap } from '../hooks/useIsMultichainSwap';
import { useMultichainSelector } from '../../../hooks/useMultichainSelector';
import { getMultichainIsEvm } from '../../../selectors/multichain';
import {
getLastSelectedNonEvmAccount,
getMultichainIsEvm,
} from '../../../selectors/multichain';
import { MultichainBridgeQuoteCard } from '../quotes/multichain-bridge-quote-card';
import { BridgeQuoteCard } from '../quotes/bridge-quote-card';
import { MultichainNetworks } from '../../../../shared/constants/multichain/networks';
import { formatChainIdToCaip } from '../../../../shared/modules/bridge-utils/caip-formatters';
import { BridgeInputGroup } from './bridge-input-group';
import { BridgeCTAButton } from './bridge-cta-button';
import { DestinationAccountPicker } from './components/destination-account-picker';
Expand Down Expand Up @@ -156,6 +165,7 @@ const PrepareBridgePage = () => {

const isEvm = useMultichainSelector(getMultichainIsEvm);
const selectedEvmAccount = useSelector(getSelectedEvmInternalAccount);
const selectedSolanaAccount = useSelector(getLastSelectedNonEvmAccount);
const selectedMultichainAccount = useMultichainSelector(
getSelectedInternalAccount,
);
Expand Down Expand Up @@ -501,14 +511,21 @@ const PrepareBridgePage = () => {
dispatch(setToChainId(null));
dispatch(setToToken(null));
}
if (networkConfig.chainId === MultichainNetworks.SOLANA) {
dispatch(setSelectedAccount(selectedEvmAccount.address));
} else if (selectedSolanaAccount) {
dispatch(setSelectedAccount(selectedSolanaAccount.address));
}
if (isNetworkAdded(networkConfig)) {
dispatch(
setActiveNetwork(
setActiveNetworkWithError(
networkConfig.rpcEndpoints[
networkConfig.defaultRpcEndpointIndex
].networkClientId,
].networkClientId || networkConfig.chainId,
),
);
} else {
dispatch(setActiveNetworkWithError(networkConfig.chainId));
}
dispatch(setFromToken(null));
dispatch(setFromTokenInputValue(null));
Expand Down Expand Up @@ -593,6 +610,19 @@ const PrepareBridgePage = () => {
? toChain.rpcEndpoints[toChain.defaultRpcEndpointIndex]
.networkClientId
: undefined;
if (
toChain?.chainId &&
formatChainIdToCaip(toChain.chainId) ===
MultichainNetworks.SOLANA &&
selectedSolanaAccount
) {
dispatch(setSelectedAccount(selectedSolanaAccount.address));
setSelectedDestinationAccount(selectedEvmAccount);
} else {
dispatch(setSelectedAccount(selectedEvmAccount.address));
selectedSolanaAccount &&
setSelectedDestinationAccount(selectedSolanaAccount);
}
toChainClientId && dispatch(setActiveNetwork(toChainClientId));
fromChain?.chainId && dispatch(setToChainId(fromChain.chainId));
}
Expand Down

0 comments on commit d9f27c4

Please sign in to comment.