Skip to content

Commit 3bebb5b

Browse files
committed
feat: invert fromToken and fromChain dependency
1 parent b8d2773 commit 3bebb5b

File tree

5 files changed

+142
-35
lines changed

5 files changed

+142
-35
lines changed

test/data/bridge/mock-bridge-store.ts

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,47 @@ export const MOCK_SOLANA_ACCOUNT = {
7575
},
7676
};
7777

78+
export const MOCK_BITCOIN_ACCOUNT = {
79+
type: 'bip122:p2wpkh',
80+
scopes: ['bip122:000000000019d6689c085ae165831e93'],
81+
id: '40b25442-ed7e-4b94-9f9f-ea8ff06d03b3',
82+
address: 'bc1q2pxsagdzfdn6k6umvf9gj3eme7a27p7acym9g2',
83+
options: {
84+
entropySource: '01K8NT6Z7XDKEKX9MFSZ5EPFMW',
85+
exportable: false,
86+
entropy: {
87+
type: 'mnemonic',
88+
id: '01K8NT6Z7XDKEKX9MFSZ5EPFMW',
89+
derivationPath: "m/84'/0'/0'",
90+
groupIndex: 0,
91+
},
92+
},
93+
methods: [
94+
'signPsbt',
95+
'computeFee',
96+
'fillPsbt',
97+
'broadcastPsbt',
98+
'sendTransfer',
99+
'getUtxo',
100+
'listUtxos',
101+
'publicDescriptor',
102+
'signMessage',
103+
],
104+
metadata: {
105+
name: 'Snap Account 9',
106+
importTime: 1763417984346,
107+
keyring: {
108+
type: 'Snap Keyring',
109+
},
110+
snap: {
111+
id: 'npm:@metamask/bitcoin-wallet-snap',
112+
name: 'Bitcoin',
113+
enabled: true,
114+
},
115+
lastSelected: 1764203245474,
116+
},
117+
};
118+
78119
export const MOCK_EVM_ACCOUNT = {
79120
address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
80121
id: 'cf8dace4-9439-4bd4-b3a8-88c821c8fcb3',
@@ -163,6 +204,7 @@ export const createBridgeMockStore = ({
163204
...(internalAccountsOverrides?.accounts ?? {}),
164205
[MOCK_LEDGER_ACCOUNT.id]: MOCK_LEDGER_ACCOUNT,
165206
[MOCK_SOLANA_ACCOUNT.id]: MOCK_SOLANA_ACCOUNT,
207+
[MOCK_BITCOIN_ACCOUNT.id]: MOCK_BITCOIN_ACCOUNT,
166208
[MOCK_EVM_ACCOUNT.id]: MOCK_EVM_ACCOUNT,
167209
[MOCK_EVM_ACCOUNT_2.id]: MOCK_EVM_ACCOUNT_2,
168210
},
@@ -259,7 +301,11 @@ export const createBridgeMockStore = ({
259301
groupIndex: 0,
260302
},
261303
},
262-
accounts: [MOCK_EVM_ACCOUNT.id, MOCK_SOLANA_ACCOUNT.id],
304+
accounts: [
305+
MOCK_EVM_ACCOUNT.id,
306+
MOCK_SOLANA_ACCOUNT.id,
307+
MOCK_BITCOIN_ACCOUNT.id,
308+
],
263309
},
264310
'entropy:01K2FF18CTTXJYD34R78X4N1N1/1': {
265311
type: 'multichain-account',
@@ -325,6 +371,14 @@ export const createBridgeMockStore = ({
325371
name: '',
326372
},
327373
},
374+
{
375+
type: KeyringType.snap,
376+
accounts: [MOCK_BITCOIN_ACCOUNT.address],
377+
metadata: {
378+
id: '01K6GQ6SXDB9GKP6CAPSRV5AJG',
379+
name: '',
380+
},
381+
},
328382
{
329383
type: KeyringType.ledger,
330384
accounts: [MOCK_LEDGER_ACCOUNT.address],

ui/ducks/bridge/selectors.test.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import {
99
} from '@metamask/bridge-controller';
1010
import { toEvmCaipChainId } from '@metamask/multichain-network-controller';
1111
import { SolAccountType, SolScope } from '@metamask/keyring-api';
12-
import { createBridgeMockStore } from '../../../test/data/bridge/mock-bridge-store';
12+
import {
13+
createBridgeMockStore,
14+
MOCK_BITCOIN_ACCOUNT,
15+
} from '../../../test/data/bridge/mock-bridge-store';
1316
import { CHAIN_IDS, FEATURED_RPCS } from '../../../shared/constants/network';
1417
import { ALLOWED_BRIDGE_CHAIN_IDS } from '../../../shared/constants/bridge';
1518
import { mockNetworkState } from '../../../test/stub/networks';
@@ -466,7 +469,7 @@ describe('Bridge selectors', () => {
466469
it('returns selected toToken', () => {
467470
const state = createBridgeMockStore({
468471
bridgeSliceOverrides: {
469-
fromToken: { address: '0x123', symbol: 'TEST' },
472+
fromToken: { address: '0x123', symbol: 'TEST', chainId: '0x1' },
470473
toChainId: formatChainIdToCaip(1),
471474
toToken: { address: '0x567', symbol: 'DEST' },
472475
},
@@ -487,7 +490,7 @@ describe('Bridge selectors', () => {
487490
it('returns default token if toToken is not set', () => {
488491
const state = createBridgeMockStore({
489492
bridgeSliceOverrides: {
490-
fromToken: { address: '0x123', symbol: 'TEST' },
493+
fromToken: { address: '0x123', symbol: 'TEST', chainId: '0x1' },
491494
toChainId: formatChainIdToCaip(1),
492495
},
493496
featureFlagOverrides: {
@@ -575,6 +578,10 @@ describe('Bridge selectors', () => {
575578
isActiveSrc: true,
576579
isActiveDest: true,
577580
},
581+
[MultichainNetworks.BITCOIN]: {
582+
isActiveSrc: true,
583+
isActiveDest: true,
584+
},
578585
},
579586
bip44DefaultPairs: {
580587
bip122: {
@@ -587,6 +594,18 @@ describe('Bridge selectors', () => {
587594
},
588595
},
589596
},
597+
metamaskStateOverrides: {
598+
internalAccounts: {
599+
selectedAccount: MOCK_BITCOIN_ACCOUNT.id,
600+
},
601+
balances: {
602+
[MOCK_BITCOIN_ACCOUNT.id]: {
603+
[getNativeAssetForChainId(ChainId.BTC).assetId]: {
604+
amount: '2',
605+
},
606+
},
607+
},
608+
},
590609
});
591610
const result = getToToken(state as never);
592611

@@ -1919,6 +1938,7 @@ describe('Bridge selectors', () => {
19191938
fromToken: {
19201939
address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
19211940
decimals: 6,
1941+
chainId: '0x1',
19221942
},
19231943
},
19241944
featureFlagOverrides: {
@@ -1954,6 +1974,7 @@ describe('Bridge selectors', () => {
19541974
fromToken: {
19551975
address: zeroAddress(),
19561976
decimals: 18,
1977+
chainId: '0x1',
19571978
},
19581979
},
19591980
featureFlagOverrides: {
@@ -2011,6 +2032,7 @@ describe('Bridge selectors', () => {
20112032
decimals: 6,
20122033
assetId:
20132034
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
2035+
chainId: MultichainNetworks.SOLANA,
20142036
},
20152037
},
20162038
featureFlagOverrides: {
@@ -2064,6 +2086,7 @@ describe('Bridge selectors', () => {
20642086
fromToken: {
20652087
address: zeroAddress(),
20662088
decimals: 18,
2089+
chainId: MultichainNetworks.SOLANA,
20672090
},
20682091
},
20692092
featureFlagOverrides: {

ui/ducks/bridge/selectors.ts

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -315,16 +315,53 @@ export const getLastSelectedChainId = createSelector(
315315
);
316316

317317
// This returns undefined if the selected chain is not supported by swap/bridge (i.e, testnets)
318-
export const getFromChain = createDeepEqualSelector(
319-
[getFromChains, getMultichainProviderConfig],
320-
(fromChains, providerConfig) => {
318+
export const getFromToken = createSelector(
319+
[
320+
(state: BridgeAppState) => state.bridge.fromToken,
321+
getFromChains,
322+
getMultichainProviderConfig,
323+
],
324+
(fromToken, fromChains, providerConfig) => {
321325
// When the page loads the global network always matches the network filter
322326
// Because useBridging checks whether the lastSelectedNetwork matches the provider config
323327
// Then useBridgeQueryParams sets the global network to lastSelectedNetwork as needed
324328
// TODO remove providerConfig references and just use getLastSelectedChainId
325-
return fromChains.find(
329+
const fromChain = fromChains.find(
326330
({ chainId }) => chainId === providerConfig?.chainId,
327331
);
332+
if (!fromChain) {
333+
return null;
334+
}
335+
const fromChainId = fromChain.chainId;
336+
if (
337+
fromToken &&
338+
fromToken.address &&
339+
!isCrossChain(fromToken.chainId, fromChainId)
340+
) {
341+
return fromToken;
342+
}
343+
const { iconUrl, ...nativeAsset } = getNativeAssetForChainId(fromChainId);
344+
const newToToken = toBridgeToken(nativeAsset);
345+
return newToToken
346+
? {
347+
...newToToken,
348+
chainId: formatChainIdToCaip(fromChainId),
349+
}
350+
: newToToken;
351+
},
352+
);
353+
354+
// Return's the chain matching the fromToken's chainId
355+
export const getFromChain = createSelector(
356+
[getFromToken, getFromChains],
357+
(fromToken, fromChains) => {
358+
if (!fromToken) {
359+
return undefined;
360+
}
361+
return fromChains.find(
362+
({ chainId }) =>
363+
formatChainIdToCaip(chainId) === formatChainIdToCaip(fromToken.chainId),
364+
);
328365
},
329366
);
330367

@@ -418,29 +455,6 @@ export const getToChain = createSelector(
418455
},
419456
);
420457

421-
export const getFromToken = createSelector(
422-
[
423-
(state: BridgeAppState) => state.bridge.fromToken,
424-
(state) => getFromChain(state)?.chainId,
425-
],
426-
(fromToken, fromChainId) => {
427-
if (!fromChainId) {
428-
return null;
429-
}
430-
if (fromToken?.address) {
431-
return fromToken;
432-
}
433-
const { iconUrl, ...nativeAsset } = getNativeAssetForChainId(fromChainId);
434-
const newToToken = toBridgeToken(nativeAsset);
435-
return newToToken
436-
? {
437-
...newToToken,
438-
chainId: formatChainIdToCaip(fromChainId),
439-
}
440-
: newToToken;
441-
},
442-
);
443-
444458
export const getToToken = createSelector(
445459
[getFromToken, getToChain, (state) => state.bridge.toToken],
446460
(fromToken, toChain, toToken) => {
@@ -727,10 +741,7 @@ export const getToTokenConversionRate = createDeepEqualSelector(
727741
}
728742
if (toChain?.chainId && toToken) {
729743
const nativeAssetId = getNativeAssetForChainId(toChain.chainId)?.assetId;
730-
const tokenAssetId = toAssetId(
731-
toToken.address,
732-
formatChainIdToCaip(toChain.chainId),
733-
);
744+
const tokenAssetId = toToken.assetId;
734745

735746
// For non-EVM tokens (Solana, Bitcoin, Tron), we use the conversion rates provided by the multichain rates controller
736747
if (isNonEvmChain(toChain.chainId) && nativeAssetId && tokenAssetId) {

ui/hooks/bridge/useBridgeQueryParams.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,30 @@ describe('useBridgeQueryParams', () => {
5959
featureFlagOverrides: {
6060
bridgeConfig: {
6161
chains: {
62+
[CHAIN_IDS.MAINNET]: {
63+
isActiveSrc: true,
64+
isActiveDest: true,
65+
},
6266
[ChainId.SOLANA]: {
6367
isActiveSrc: true,
6468
isActiveDest: true,
6569
},
6670
},
6771
},
6872
},
73+
metamaskStateOverrides: {
74+
internalAccounts: {
75+
selectedAccount: 'bf13d52c-d6e8-40ea-9726-07d7149a3ca5',
76+
},
77+
balances: {
78+
'bf13d52c-d6e8-40ea-9726-07d7149a3ca5': {
79+
[bridgeControllerUtils.getNativeAssetForChainId(ChainId.SOLANA)
80+
.assetId]: {
81+
amount: '2',
82+
},
83+
},
84+
},
85+
},
6986
});
7087

7188
const searchParams = new URLSearchParams({

ui/hooks/bridge/useTokenAlerts.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,14 @@ describe('useTokenAlerts', () => {
5151
bridgeSliceOverrides: {
5252
fromToken: {
5353
address: '0x3fa807b6f8d4c407e6e605368f4372d14658b38c',
54+
chainId: CHAIN_IDS.MAINNET,
5455
},
5556
fromChain: {
5657
chainId: CHAIN_IDS.MAINNET,
5758
},
5859
toToken: {
5960
address: '6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN',
61+
chainId: MultichainNetworks.SOLANA,
6062
},
6163
toChainId: MultichainNetworks.SOLANA,
6264
},

0 commit comments

Comments
 (0)