Skip to content

Commit 3143c90

Browse files
authored
chore: Add Solana devnet support (#20155)
<!-- 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** We are enabling Solana devnet support, its under the FF `solanaTestnetsEnabled` (already in main). https://github.com/user-attachments/assets/11b04e4d-43f8-49a2-8d15-8d9d9a686d9f <img height="700" alt="Simulator Screenshot - iPhone 15 - 2025-09-23 at 11 00 32" src="https://github.com/user-attachments/assets/7d79f6aa-c6cd-46ae-a203-44f30506cd1c" /> <img height="700" alt="Simulator Screenshot - iPhone 15 - 2025-09-23 at 11 00 22" src="https://github.com/user-attachments/assets/e04f02cc-0909-40ee-9952-0c42b3e77c3d" /> <img height="700" alt="Simulator Screenshot - iPhone 15 - 2025-09-23 at 11 06 58" src="https://github.com/user-attachments/assets/d7e33955-1e5c-4161-b9e8-9e685a1abc74" /> ## **Changelog** <!-- If this PR is not End-User-Facing and should not show up in the CHANGELOG, you can choose to either: 1. Write `CHANGELOG entry: null` 2. Label with `no-changelog` If this PR is End-User-Facing, please write a short User-Facing description in the past tense like: `CHANGELOG entry: Added a new tab for users to see their NFTs` `CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker` (This helps the Release Engineer do their job more quickly and accurately) --> CHANGELOG entry: Adds Solana devnet support, under FF. ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: my feature name Scenario: user [verb for user action] Given [describe expected initial app state] When user [verb for user action] Then [describe expected outcome] ``` ## **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 Mobile Coding Standards](https://github.com/MetaMask/metamask-mobile/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-mobile/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [x] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [x] 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 425d58d commit 3143c90

File tree

8 files changed

+66
-30
lines changed

8 files changed

+66
-30
lines changed

app/components/UI/CollectibleContracts/index.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import {
2727
multichainCollectibleContractsSelector,
2828
multichainCollectiblesSelector,
2929
multichainCollectiblesByEnabledNetworksSelector,
30-
multichainCollectibleContractsByEnabledNetworksSelector,
3130
} from '../../../reducers/collectibles';
3231
import { removeFavoriteCollectible } from '../../../actions/collectibles';
3332
import AppConstants from '../../../core/AppConstants';
@@ -669,11 +668,7 @@ const CollectibleContracts = ({
669668
</>
670669
}
671670
isDisabled={isDisabled}
672-
onPress={
673-
isEvmSelected || isMultichainAccountsState2Enabled
674-
? showFilterControls
675-
: () => null
676-
}
671+
onPress={showFilterControls}
677672
endIconName={
678673
isEvmSelected || isMultichainAccountsState2Enabled
679674
? IconName.ArrowDown

app/components/Views/NetworkSelector/NetworkSelector.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ import {
9797
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
9898
selectNonEvmNetworkConfigurationsByChainId,
9999
///: END:ONLY_INCLUDE_IF
100+
selectSelectedNonEvmNetworkChainId,
100101
} from '../../../selectors/multichainNetworkController';
101102
import { isNonEvmChainId } from '../../../core/Multichain/utils';
102103
import { MultichainNetworkConfiguration } from '@metamask/multichain-network-controller';
@@ -161,6 +162,7 @@ const NetworkSelector = () => {
161162
///: END:ONLY_INCLUDE_IF
162163

163164
const isEvmSelected = useSelector(selectIsEvmNetworkSelected);
165+
const selectedNonEvmChainId = useSelector(selectSelectedNonEvmNetworkChainId);
164166

165167
const route =
166168
useRoute<RouteProp<Record<string, NetworkSelectorRouteParams>, string>>();
@@ -360,7 +362,11 @@ const NetworkSelector = () => {
360362
return chainId === browserChainId;
361363
}
362364

363-
return !isEvmSelected ? false : chainId === selectedChainId;
365+
if (!isEvmSelected) {
366+
return chainId === selectedNonEvmChainId;
367+
}
368+
369+
return chainId === selectedChainId;
364370
};
365371

366372
const {
@@ -711,9 +717,7 @@ const NetworkSelector = () => {
711717
}
712718

713719
return networks.map((network) => {
714-
const isSelected =
715-
network.chainId === browserChainId ||
716-
(!isEvmSelected && !browserChainId);
720+
const isSelected = isNetworkSelected(network.chainId);
717721
return (
718722
<Cell
719723
key={network.chainId}

app/components/hooks/useCurrentNetworkInfo.ts

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
11
import { useMemo, useCallback } from 'react';
22
import { useSelector } from 'react-redux';
3-
import { KnownCaipNamespace } from '@metamask/utils';
43
import { formatChainIdToCaip } from '@metamask/bridge-controller';
5-
import {
6-
selectNetworkConfigurationsByCaipChainId,
7-
selectChainId,
8-
} from '../../selectors/networkController';
9-
import { selectIsEvmNetworkSelected } from '../../selectors/multichainNetworkController';
4+
import { selectNetworkConfigurationsByCaipChainId } from '../../selectors/networkController';
105
import { useNetworkEnablement } from './useNetworkEnablement/useNetworkEnablement';
116
import { selectMultichainAccountsState2Enabled } from '../../selectors/featureFlagController/multichainAccounts';
127

@@ -31,10 +26,6 @@ export const useCurrentNetworkInfo = (): CurrentNetworkInfo => {
3126
const networksByCaipChainId = useSelector(
3227
selectNetworkConfigurationsByCaipChainId,
3328
);
34-
const isEvmSelected = useSelector(selectIsEvmNetworkSelected);
35-
const selectedChainId = useSelector(selectChainId);
36-
const isSolanaSelected =
37-
selectedChainId?.includes(KnownCaipNamespace.Solana) ?? false;
3829
const isMultichainAccountsState2Enabled = useSelector(
3930
selectMultichainAccountsState2Enabled,
4031
);
@@ -94,11 +85,9 @@ export const useCurrentNetworkInfo = (): CurrentNetworkInfo => {
9485
[enabledNetworks, networksByCaipChainId],
9586
);
9687

97-
let isDisabled: boolean =
98-
Boolean(!isEvmSelected) && !isMultichainAccountsState2Enabled;
99-
// We don't have Solana testnet networks, so we disable the network selector if Solana is selected
100-
// TODO: Come back when we have Solana devnet available
101-
isDisabled = Boolean(isSolanaSelected) && !isMultichainAccountsState2Enabled;
88+
// For now there is no use case to have it disabled
89+
// but leaving it here since it might be useful
90+
const isDisabled: boolean = false;
10291

10392
const hasEnabledNetworks = enabledNetworks.length > 0;
10493

app/images/image-icons.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import LINEA_TESTNET from './linea-testnet-logo.png';
1212
import SEPOLIA from './sepolia-logo-dark.png';
1313
import LINEA_MAINNET from './linea-mainnet-logo.png';
1414
import SOLANA from './solana-logo.png';
15+
import SOLANA_DEVNET from './solana-devnet.jpg';
1516
import GRAVITY from './gravity.png';
1617
import KAIA_MAINNET from './kaia.png';
1718
import FOX_LOGO from '../../app/images/branding/tiny-logo.png';
@@ -64,6 +65,7 @@ export default {
6465
'KAIA-MAINNET': KAIA_MAINNET,
6566
'KAIA-KAIROS-TESTNET': KAIA_MAINNET,
6667
SOLANA,
68+
SOLANA_DEVNET,
6769
FOX_LOGO,
6870
BTC,
6971
'BTC-TESTNET': BTC_TESTNET,

app/images/solana-devnet.jpg

7.56 KB
Loading

app/selectors/multichainNetworkController/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export const selectNonEvmNetworkConfigurationsByChainId = createSelector(
7676
},
7777
[SolScope.Devnet]: {
7878
decimals: MULTICHAIN_NETWORK_DECIMAL_PLACES[SolScope.Devnet],
79-
imageSource: imageIcons.SOLANA,
79+
imageSource: imageIcons.SOLANA_DEVNET as ImageSourcePropType,
8080
ticker: MULTICHAIN_NETWORK_TICKER[SolScope.Devnet],
8181
isTestnet: true,
8282
name: 'Solana Devnet',

app/util/networks/customNetworks.test.ts

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
getNonEvmNetworkImageSourceByChainId,
44
} from './customNetworks';
55
import { toHex } from '@metamask/controller-utils';
6-
import { SolScope } from '@metamask/keyring-api';
6+
import { BtcScope, SolScope } from '@metamask/keyring-api';
77
import { CaipChainId } from '@metamask/utils';
88

99
describe('popularNetwork', () => {
@@ -27,9 +27,53 @@ describe('popularNetwork', () => {
2727
});
2828

2929
describe('getNonEvmNetworkImageSourceByChainId', () => {
30-
it('should return image source for valid non-EVM network chainId', () => {
31-
const imageSource = getNonEvmNetworkImageSourceByChainId(SolScope.Mainnet);
32-
expect(imageSource).toBeDefined();
30+
describe('Solana networks', () => {
31+
it('should return solana mainnet image for SolScope.Mainnet', () => {
32+
const imageSource = getNonEvmNetworkImageSourceByChainId(
33+
SolScope.Mainnet,
34+
);
35+
expect(imageSource).toBeDefined();
36+
});
37+
38+
it('should return solana devnet image for SolScope.Devnet', () => {
39+
const imageSource = getNonEvmNetworkImageSourceByChainId(SolScope.Devnet);
40+
expect(imageSource).toBeDefined();
41+
});
42+
});
43+
44+
describe('Bitcoin networks', () => {
45+
it('should return bitcoin mainnet image for BtcScope.Mainnet', () => {
46+
const imageSource = getNonEvmNetworkImageSourceByChainId(
47+
BtcScope.Mainnet,
48+
);
49+
expect(imageSource).toBeDefined();
50+
});
51+
52+
it('should return bitcoin testnet image for BtcScope.Testnet', () => {
53+
const imageSource = getNonEvmNetworkImageSourceByChainId(
54+
BtcScope.Testnet,
55+
);
56+
expect(imageSource).toBeDefined();
57+
});
58+
59+
it('should return bitcoin testnet image for BtcScope.Testnet4', () => {
60+
const imageSource = getNonEvmNetworkImageSourceByChainId(
61+
BtcScope.Testnet4,
62+
);
63+
expect(imageSource).toBeDefined();
64+
});
65+
66+
it('should return bitcoin testnet image for BtcScope.Regtest', () => {
67+
const imageSource = getNonEvmNetworkImageSourceByChainId(
68+
BtcScope.Regtest,
69+
);
70+
expect(imageSource).toBeDefined();
71+
});
72+
73+
it('should return bitcoin signet image for BtcScope.Signet', () => {
74+
const imageSource = getNonEvmNetworkImageSourceByChainId(BtcScope.Signet);
75+
expect(imageSource).toBeDefined();
76+
});
3377
});
3478

3579
it('should return undefined for invalid chainId', () => {

app/util/networks/customNetworks.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ export const getNonEvmNetworkImageSourceByChainId = (chainId: CaipChainId) => {
147147
switch (chainId) {
148148
case SolScope.Mainnet:
149149
return require('../../images/solana-logo.png');
150+
case SolScope.Devnet:
151+
return require('../../images/solana-devnet.jpg');
150152
case BtcScope.Mainnet:
151153
return require('../../images/bitcoin-logo.png');
152154
case BtcScope.Testnet:

0 commit comments

Comments
 (0)