;
+ };
+};
+
+const MOCK_EVM_STATE: TestState = {
+ metamask: {
+ preferences: {
+ showFiatInTestnets: false,
+ },
+ providerConfig: {
+ ticker: 'ETH',
+ chainId: '0x1',
+ },
+ currentCurrency: 'ETH',
+ currencyRates: {
+ ETH: {
+ conversionRate: 'usd',
+ },
+ },
+ internalAccounts: {
+ selectedAccount: MOCK_ACCOUNT_EOA.id,
+ accounts: MOCK_ACCOUNTS,
+ },
+ },
+};
+
+const MOCK_NON_EVM_STATE: AccountsState = {
+ metamask: {
+ ...MOCK_EVM_STATE.metamask,
+ internalAccounts: {
+ selectedAccount: MOCK_ACCOUNT_BIP122_P2WPKH.id,
+ accounts: MOCK_ACCOUNTS,
+ },
+ },
+};
+
+function getBip122ProviderConfig(): MultichainProviderConfig {
+ // For now, we only have Bitcoin non-EVM network, so we are expecting to have
+ // this one with `bip122:*` account type
+ return MULTICHAIN_PROVIDER_CONFIGS[MultichainNetworks.BITCOIN];
+}
+
+describe('Multichain Selectors', () => {
+ describe('getMultichainNetworkProviders', () => {
+ it('has some providers', () => {
+ const state = MOCK_EVM_STATE;
+
+ const networkProviders = getMultichainNetworkProviders(state);
+ expect(Array.isArray(networkProviders)).toBe(true);
+ expect(networkProviders.length).toBeGreaterThan(0);
+ });
+ });
+
+ describe('getMultichainNetwork', () => {
+ it('returns an EVM network provider if account is EVM', () => {
+ const state = MOCK_EVM_STATE;
+
+ const network = getMultichainNetwork(state);
+ expect(network.isEvmNetwork).toBe(true);
+ });
+
+ it('returns an non-EVM network provider if account is non-EVM', () => {
+ const state = MOCK_NON_EVM_STATE;
+
+ const network = getMultichainNetwork(state);
+ expect(network.isEvmNetwork).toBe(false);
+ });
+ });
+
+ describe('getMultichainIsEvm', () => {
+ it('returns true if selected account is EVM compatible', () => {
+ const state = MOCK_EVM_STATE;
+
+ expect(getMultichainIsEvm(state)).toBe(true);
+ });
+
+ it('returns false if selected account is not EVM compatible', () => {
+ const state = MOCK_NON_EVM_STATE;
+
+ expect(getMultichainIsEvm(state)).toBe(false);
+ });
+ });
+
+ describe('getMultichain{ProviderConfig,CurrentNetwork}', () => {
+ it('returns a ProviderConfig if account is EVM', () => {
+ const state = MOCK_EVM_STATE;
+
+ expect(getMultichainProviderConfig(state)).toBe(getProviderConfig(state));
+ });
+
+ it('returns a MultichainProviderConfig if account is non-EVM (bip122:*)', () => {
+ const state = MOCK_NON_EVM_STATE;
+
+ const bip122ProviderConfig = getBip122ProviderConfig();
+ expect(getMultichainProviderConfig(state)).toBe(bip122ProviderConfig);
+ });
+ });
+
+ describe('getMultichainNativeCurrency', () => {
+ it('returns same native currency if account is EVM', () => {
+ const state = MOCK_EVM_STATE;
+
+ expect(getMultichainNativeCurrency(state)).toBe(getNativeCurrency(state));
+ });
+
+ it('returns MultichainProviderConfig.ticker if account is non-EVM (bip122:*)', () => {
+ const state = MOCK_NON_EVM_STATE;
+
+ const bip122ProviderConfig = getBip122ProviderConfig();
+ expect(getMultichainNativeCurrency(state)).toBe(
+ bip122ProviderConfig.ticker,
+ );
+ });
+ });
+
+ describe('getMultichainCurrentCurrency', () => {
+ it('returns same currency currency if account is EVM', () => {
+ const state = MOCK_EVM_STATE;
+
+ expect(getMultichainCurrentCurrency(state)).toBe(
+ getCurrentCurrency(state),
+ );
+ });
+
+ // @ts-expect-error This is missing from the Mocha type definitions
+ it.each(['usd', 'ETH'])(
+ "returns current currency '%s' if account is EVM",
+ (currency: string) => {
+ const state = MOCK_EVM_STATE;
+
+ state.metamask.currentCurrency = currency;
+ expect(getCurrentCurrency(state)).toBe(currency);
+ expect(getMultichainCurrentCurrency(state)).toBe(currency);
+ },
+ );
+
+ it('fallbacks to ticker as currency if account is non-EVM (bip122:*)', () => {
+ const state = MOCK_NON_EVM_STATE; // .currentCurrency = 'ETH'
+
+ const bip122ProviderConfig = getBip122ProviderConfig();
+ expect(getCurrentCurrency(state).toLowerCase()).not.toBe('usd');
+ expect(getMultichainCurrentCurrency(state)).toBe(
+ bip122ProviderConfig.ticker,
+ );
+ });
+ });
+
+ describe('getMultichainShouldShowFiat', () => {
+ it('returns same value as getShouldShowFiat if account is EVM', () => {
+ const state = MOCK_EVM_STATE;
+
+ expect(getMultichainShouldShowFiat(state)).toBe(getShouldShowFiat(state));
+ });
+
+ it('returns true if account is non-EVM', () => {
+ const state = MOCK_NON_EVM_STATE;
+
+ expect(getMultichainShouldShowFiat(state)).toBe(true);
+ });
+ });
+
+ describe('getMultichainDefaultToken', () => {
+ it('returns ETH if account is EVM', () => {
+ const state = MOCK_EVM_STATE;
+
+ expect(getMultichainDefaultToken(state)).toEqual({
+ symbol: 'ETH',
+ });
+ });
+
+ it('returns true if account is non-EVM (bip122:*)', () => {
+ const state = MOCK_NON_EVM_STATE;
+
+ const bip122ProviderConfig = getBip122ProviderConfig();
+ expect(getMultichainDefaultToken(state)).toEqual({
+ symbol: bip122ProviderConfig.ticker,
+ });
+ });
+ });
+});
diff --git a/ui/selectors/multichain.ts b/ui/selectors/multichain.ts
new file mode 100644
index 000000000000..e97b08b85030
--- /dev/null
+++ b/ui/selectors/multichain.ts
@@ -0,0 +1,154 @@
+import { isEvmAccountType } from '@metamask/keyring-api';
+import { ProviderConfig } from '@metamask/network-controller';
+import {
+ CaipChainId,
+ KnownCaipNamespace,
+ parseCaipChainId,
+} from '@metamask/utils';
+import {
+ MultichainProviderConfig,
+ MULTICHAIN_PROVIDER_CONFIGS,
+} from '../../shared/constants/multichain/networks';
+import {
+ getNativeCurrency,
+ getProviderConfig,
+} from '../ducks/metamask/metamask';
+import { AccountsState } from './accounts';
+import {
+ getAllNetworks,
+ getCurrentCurrency,
+ getNativeCurrencyImage,
+ getSelectedInternalAccount,
+ getShouldShowFiat,
+} from '.';
+
+export type MultichainState = AccountsState & {
+ metamask: {
+ // TODO: Use states from new {Rates,Balances,Chain}Controller
+ };
+};
+
+export type MultichainNetwork = {
+ nickname: string;
+ isEvmNetwork: boolean;
+ chainId?: CaipChainId;
+ network?: ProviderConfig | MultichainProviderConfig;
+};
+
+export function getMultichainNetworkProviders(
+ _state: MultichainState,
+): MultichainProviderConfig[] {
+ // TODO: need state from the ChainController?
+ return Object.values(MULTICHAIN_PROVIDER_CONFIGS);
+}
+
+export function getMultichainNetwork(
+ state: MultichainState,
+): MultichainNetwork {
+ const selectedAccount = getSelectedInternalAccount(state);
+ const isEvm = isEvmAccountType(selectedAccount.type);
+
+ // EVM networks
+ const evmNetworks: ProviderConfig[] = getAllNetworks(state);
+ const evmProvider: ProviderConfig = getProviderConfig(state);
+
+ if (isEvm) {
+ const evmChainId =
+ `${KnownCaipNamespace.Eip155}:${evmProvider.chainId}` as CaipChainId;
+ const evmNetwork = evmNetworks.find(
+ (network) => network.chainId === evmProvider.chainId,
+ );
+
+ return {
+ nickname: 'Ethereum',
+ isEvmNetwork: true,
+ chainId: evmChainId,
+ network: evmNetwork,
+ };
+ }
+
+ // Non-EVM networks
+ // (Hardcoded for testing)
+ // HACK: For now, we rely on the account type being "sort-of" CAIP compliant, so use
+ // this as a CAIP-2 namespace and apply our filter with it
+ const nonEvmNetworks = getMultichainNetworkProviders(state);
+ const nonEvmNetwork = nonEvmNetworks.find((provider) => {
+ const { namespace } = parseCaipChainId(provider.chainId);
+ return selectedAccount.type.startsWith(namespace);
+ });
+
+ return {
+ // TODO: Adapt this for other non-EVM networks
+ // TODO: We need to have a way of setting nicknames of other non-EVM networks
+ nickname: 'Bitcoin',
+ isEvmNetwork: false,
+ // FIXME: We should use CAIP-2 chain ID here, and not only the reference part
+ chainId: nonEvmNetwork?.chainId,
+ network: nonEvmNetwork,
+ };
+}
+
+// FIXME: All the following might have side-effect, like if the current account is a bitcoin one and that
+// a popup (for ethereum related stuffs) is being shown (and uses this function), then the native
+// currency will be BTC..
+
+export function getMultichainIsEvm(state: MultichainState) {
+ const selectedAccount = getSelectedInternalAccount(state);
+
+ // There are no selected account during onboarding. we default to the current EVM provider.
+ return !selectedAccount || isEvmAccountType(selectedAccount.type);
+}
+
+export function getMultichainProviderConfig(
+ state: MultichainState,
+): ProviderConfig | MultichainProviderConfig {
+ return getMultichainIsEvm(state)
+ ? getProviderConfig(state)
+ : getMultichainNetwork(state).network;
+}
+
+export function getMultichainCurrentNetwork(state: MultichainState) {
+ return getMultichainProviderConfig(state);
+}
+
+export function getMultichainNativeCurrency(state: MultichainState) {
+ return getMultichainIsEvm(state)
+ ? getNativeCurrency(state)
+ : getMultichainProviderConfig(state).ticker;
+}
+
+export function getMultichainCurrentCurrency(state: MultichainState) {
+ const currentCurrency = getCurrentCurrency(state).toLowerCase();
+
+ // To mimic `getCurrentCurrency` we only consider fiat values, otherwise we
+ // fallback to the current ticker symbol value
+ return currentCurrency === 'usd'
+ ? 'usd'
+ : getMultichainProviderConfig(state).ticker;
+}
+
+export function getMultichainCurrencyImage(state: MultichainState) {
+ if (getMultichainIsEvm(state)) {
+ return getNativeCurrencyImage(state);
+ }
+
+ const provider = getMultichainProviderConfig(
+ state,
+ ) as MultichainProviderConfig;
+ return provider.rpcPrefs?.imageUrl;
+}
+
+export function getMultichainShouldShowFiat(state: MultichainState) {
+ return getMultichainIsEvm(state)
+ ? getShouldShowFiat(state)
+ : // For now we force this for non-EVM
+ true;
+}
+
+export function getMultichainDefaultToken(state: MultichainState) {
+ const symbol = getMultichainIsEvm(state)
+ ? getProviderConfig(state).ticker
+ : getMultichainProviderConfig(state).ticker;
+
+ return { symbol };
+}
diff --git a/ui/store/background-connection.ts b/ui/store/background-connection.ts
index 5c7222c040e6..3928675419a8 100644
--- a/ui/store/background-connection.ts
+++ b/ui/store/background-connection.ts
@@ -41,6 +41,7 @@ type CallbackMethod = (error?: unknown, result?: R) => void;
* [Deprecated] Callback-style call to background method
* invokes promisifiedBackground method directly.
*
+ * @deprecated Use async `submitRequestToBackground` function instead.
* @param method - name of the background method
* @param [args] - arguments to that method, if any
* @param callback - Node style (error, result) callback for finishing the operation
diff --git a/yarn.lock b/yarn.lock
index f68f0999f41c..cf50e0a9db6d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1710,10 +1710,10 @@ __metadata:
languageName: node
linkType: hard
-"@blockaid/ppom_release@npm:^1.4.6":
- version: 1.4.6
- resolution: "@blockaid/ppom_release@npm:1.4.6"
- checksum: 10/53b9774f97ba24d98dee4a66653c83223afd2db8598d8b631ee4851731b1f68a99d05a8a2b68584d7b9a8e71370a09e79aba024d171af8a823c1053371009756
+"@blockaid/ppom_release@npm:^1.4.7":
+ version: 1.4.7
+ resolution: "@blockaid/ppom_release@npm:1.4.7"
+ checksum: 10/2d376796afe4dc4e008418120977f22202b52d32ab2d036e8ab511a7673afeffff8b36e9b194cceeb4ffceb3808dcc6a276e1e55fd39e37833b3e7c288fec295
languageName: node
linkType: hard
@@ -4975,10 +4975,10 @@ __metadata:
languageName: node
linkType: hard
-"@metamask/design-tokens@npm:^3.0.0":
- version: 3.0.0
- resolution: "@metamask/design-tokens@npm:3.0.0"
- checksum: 10/65c809fb5877398a0e45f3a22c09c8f7b64972961497f465a4c85eb6492e7e7d1168d4718cd21442dfb54a1f4b79d0287c6d5f5f5faa652dfcc5ababb1894095
+"@metamask/design-tokens@npm:^4.0.0":
+ version: 4.0.0
+ resolution: "@metamask/design-tokens@npm:4.0.0"
+ checksum: 10/337968d86bf963ccdf7ab416cc8f87ec1d35d9fb56f686dea954964edd6f5cb0067a920cb2c1f9008150d3decac51cb1b392b3e67dc1d46ca308b503ffe7eabd
languageName: node
linkType: hard
@@ -24838,7 +24838,7 @@ __metadata:
"@babel/preset-typescript": "npm:^7.23.2"
"@babel/register": "npm:^7.22.15"
"@babel/runtime": "patch:@babel/runtime@npm%3A7.24.0#~/.yarn/patches/@babel-runtime-npm-7.24.0-7eb1dd11a2.patch"
- "@blockaid/ppom_release": "npm:^1.4.6"
+ "@blockaid/ppom_release": "npm:^1.4.7"
"@contentful/rich-text-html-renderer": "npm:^16.3.5"
"@ensdomains/content-hash": "npm:^2.5.7"
"@ethereumjs/tx": "npm:^4.1.1"
@@ -24877,7 +24877,7 @@ __metadata:
"@metamask/build-utils": "npm:^1.0.0"
"@metamask/contract-metadata": "npm:^2.5.0"
"@metamask/controller-utils": "npm:^10.0.0"
- "@metamask/design-tokens": "npm:^3.0.0"
+ "@metamask/design-tokens": "npm:^4.0.0"
"@metamask/ens-controller": "npm:^10.0.1"
"@metamask/eslint-config": "npm:^9.0.0"
"@metamask/eslint-config-jest": "npm:^9.0.0"