Skip to content

Commit 8c5ee9b

Browse files
authored
fix: cp-7.54.0 update liveness logic (MetaMask#18364)
<!-- 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** Transitions swaps/bridge liveness check to LD flags instead of legacy swaps feature flags. <!-- 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? --> ## **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: ## **Related issues** Fixes: ## **Manual testing steps** ```gherkin Feature: swaps liveness check Scenario: user navigates to swap page Given swaps is live for that chain When user clicks any swap button (wallet view, wallet actions, asset page) Then button is enabled if swaps is live for that chain ``` ## **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** - [ ] 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 8f29d2f commit 8c5ee9b

12 files changed

Lines changed: 46 additions & 228 deletions

File tree

app/components/UI/Swaps/SwapsLiveness.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,20 @@ import { selectEvmChainId } from '../../../selectors/networkController';
44
import { AppState, AppStateStatus } from 'react-native';
55
import { useDispatch, useSelector } from 'react-redux';
66
import AppConstants from '../../../core/AppConstants';
7-
import {
8-
setSwapsLiveness,
9-
swapsLivenessSelector,
10-
} from '../../../reducers/swaps';
7+
import { setSwapsLiveness } from '../../../reducers/swaps';
118
import Logger from '../../../util/Logger';
129
import useInterval from '../../hooks/useInterval';
1310
import { isSwapsAllowed } from './utils';
11+
import { RootState } from '../../../reducers';
12+
import { selectIsSwapsLive } from '../../../core/redux/slices/bridge';
1413

1514
const POLLING_FREQUENCY = AppConstants.SWAPS.LIVENESS_POLLING_FREQUENCY;
1615

1716
function SwapLiveness() {
18-
const isLive = useSelector(swapsLivenessSelector);
1917
const chainId = useSelector(selectEvmChainId);
18+
const isLive = useSelector((state: RootState) =>
19+
selectIsSwapsLive(state, chainId),
20+
);
2021
const dispatch = useDispatch();
2122
const setLiveness = useCallback(
2223
(_chainId: string, featureFlags?: FeatureFlags | null) => {

app/components/Views/Asset/index.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,12 @@ import { isBridgeAllowed } from '../../UI/Bridge/utils';
8282
import { selectNonEvmTransactions } from '../../../selectors/multichain';
8383
import { isEvmAccountType } from '@metamask/keyring-api';
8484
///: END:ONLY_INCLUDE_IF
85-
import { getIsSwapsAssetAllowed, getSwapsIsLive } from './utils';
85+
import { getIsSwapsAssetAllowed } from './utils';
8686
import MultichainTransactionsView from '../MultichainTransactionsView/MultichainTransactionsView';
87-
import { selectIsUnifiedSwapsEnabled } from '../../../core/redux/slices/bridge';
87+
import {
88+
selectIsUnifiedSwapsEnabled,
89+
selectIsSwapsLive,
90+
} from '../../../core/redux/slices/bridge';
8891
import { AVAILABLE_MULTICHAIN_NETWORK_CONFIGURATIONS } from '@metamask/multichain-network-controller';
8992

9093
const createStyles = (colors) =>
@@ -778,7 +781,7 @@ const mapStateToProps = (state, { route }) => {
778781
///: END:ONLY_INCLUDE_IF
779782

780783
return {
781-
swapsIsLive: getSwapsIsLive(state, route.params.chainId),
784+
swapsIsLive: selectIsSwapsLive(state, route.params.chainId),
782785
swapsTokens: isPortfolioViewEnabled()
783786
? swapsTokensMultiChainObjectSelector(state)
784787
: swapsTokensObjectSelector(state),

app/components/Views/Asset/utils.test.ts

Lines changed: 1 addition & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1,165 +1,12 @@
11
import '../../UI/Bridge/_mocks_/initialState';
2-
import { deepClone } from '@metamask/snaps-utils';
3-
import { getSwapsIsLive, getIsSwapsAssetAllowed } from './utils';
4-
import { RootState } from '../../../reducers';
2+
import { getIsSwapsAssetAllowed } from './utils';
53
import { SolScope } from '@metamask/keyring-api';
6-
import { isPortfolioViewEnabled } from '../../../util/networks';
74

85
jest.mock('../../../util/networks', () => ({
96
...jest.requireActual('../../../util/networks'),
107
isPortfolioViewEnabled: jest.fn().mockReturnValue(true),
118
}));
129

13-
describe('getSwapsIsLive', () => {
14-
const mockState = {
15-
swaps: {
16-
isLive: true,
17-
'0x1': { isLive: true },
18-
},
19-
engine: {
20-
backgroundState: {
21-
RemoteFeatureFlagController: {
22-
remoteFeatureFlags: {
23-
bridgeConfig: {
24-
minimumVersion: '0.0.0',
25-
support: true,
26-
chains: {
27-
1: {
28-
isActiveDest: true,
29-
isActiveSrc: true,
30-
},
31-
1151111081099710: {
32-
isActiveDest: true,
33-
isActiveSrc: true,
34-
refreshRate: 10000,
35-
topAssets: [
36-
'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
37-
'6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN',
38-
'JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN',
39-
'7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxsDx8F8k8k3uYw1PDC',
40-
'3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y',
41-
'9zNQRsGLjNKwCUU5Gq5LR8beUCPzQMVMqKAi3SSZh54u',
42-
'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
43-
'rndrizKT3MK1iimdxRdWabcF7Zg7AR5T4nud4EkHBof',
44-
'21AErpiB8uSb94oQKRcwuHqyHF93njAxBSbdUrpupump',
45-
],
46-
},
47-
},
48-
maxRefreshCount: 5,
49-
refreshRate: 30000,
50-
},
51-
bridgeConfigV2: {
52-
minimumVersion: '0.0.0',
53-
support: true,
54-
chains: {
55-
1: {
56-
isActiveDest: true,
57-
isActiveSrc: true,
58-
},
59-
1151111081099710: {
60-
isActiveDest: true,
61-
isActiveSrc: true,
62-
refreshRate: 10000,
63-
topAssets: [
64-
'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
65-
'6p6xgHyF7AeE6TZkSmFsko444wqoP15icUSqi2jfGiPN',
66-
'JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN',
67-
'7vfCXTUXx5WJV5JADk17DUJ4ksgau7utNKj4b963voxsDx8F8k8k3uYw1PDC',
68-
'3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y',
69-
'9zNQRsGLjNKwCUU5Gq5LR8beUCPzQMVMqKAi3SSZh54u',
70-
'DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263',
71-
'rndrizKT3MK1iimdxRdWabcF7Zg7AR5T4nud4EkHBof',
72-
'21AErpiB8uSb94oQKRcwuHqyHF93njAxBSbdUrpupump',
73-
],
74-
},
75-
},
76-
maxRefreshCount: 5,
77-
refreshRate: 30000,
78-
},
79-
},
80-
},
81-
},
82-
},
83-
} as unknown as RootState;
84-
const mockChainId = '0x1';
85-
describe('EVM', () => {
86-
it('should return true for EVM chain when swaps is live', () => {
87-
const result = getSwapsIsLive(mockState, mockChainId);
88-
expect(result).toBe(true);
89-
});
90-
91-
it('should return false for EVM chain when swaps is not live', () => {
92-
const result = getSwapsIsLive(
93-
{
94-
...mockState,
95-
swaps: { ...mockState.swaps, '0x1': { isLive: false } },
96-
},
97-
mockChainId,
98-
);
99-
expect(result).toBe(false);
100-
});
101-
102-
it('should return false for EVM chain when swaps state is null', () => {
103-
const result = getSwapsIsLive(
104-
{
105-
...mockState,
106-
swaps: { ...mockState.swaps, '0x1': null },
107-
},
108-
mockChainId,
109-
);
110-
expect(result).toBe(false);
111-
});
112-
113-
it('should return the correct value for EVM chain when portfolio view is disabled', () => {
114-
(isPortfolioViewEnabled as jest.Mock).mockReturnValue(false);
115-
const result = getSwapsIsLive(mockState, mockChainId);
116-
expect(result).toBe(true);
117-
});
118-
});
119-
120-
describe('Solana', () => {
121-
it('should return true for Solana chain when bridge is enabled', () => {
122-
const result = getSwapsIsLive(mockState, SolScope.Mainnet);
123-
expect(result).toBe(true);
124-
});
125-
126-
it('should return false for Solana chain when bridge is not enabled', () => {
127-
const newState = deepClone(mockState);
128-
const remoteFeatureFlags =
129-
newState.engine.backgroundState.RemoteFeatureFlagController
130-
.remoteFeatureFlags;
131-
// @ts-expect-error - It's defined in the mock
132-
remoteFeatureFlags.bridgeConfig.chains[1151111081099710] = {
133-
minimumVersion: '0.0.0',
134-
isActiveDest: false,
135-
isActiveSrc: false,
136-
refreshRate: 10000,
137-
topAssets: [],
138-
};
139-
// @ts-expect-error - It's defined in the mock
140-
remoteFeatureFlags.bridgeConfigV2.chains[1151111081099710] = {
141-
minimumVersion: '0.0.0',
142-
isActiveDest: false,
143-
isActiveSrc: false,
144-
refreshRate: 10000,
145-
topAssets: [],
146-
};
147-
const result = getSwapsIsLive(newState, SolScope.Mainnet);
148-
expect(result).toBe(false);
149-
});
150-
});
151-
152-
it('should handle portfolio view enabled case', () => {
153-
const result = getSwapsIsLive(mockState, mockChainId);
154-
expect(result).toBe(true);
155-
});
156-
157-
it('should handle portfolio view disabled case', () => {
158-
const result = getSwapsIsLive(mockState, mockChainId);
159-
expect(result).toBe(true);
160-
});
161-
});
162-
16310
describe('getIsSwapsAssetAllowed', () => {
16411
const mockSwapsTokens = {
16512
'0xtoken1': { symbol: 'TOKEN1' },

app/components/Views/Asset/utils.ts

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,7 @@
11
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
22
import { SolScope } from '@metamask/keyring-api';
3-
import { selectIsBridgeEnabledSource } from '../../../core/redux/slices/bridge';
43
///: END:ONLY_INCLUDE_IF(keyring-snaps)
5-
import {
6-
swapsLivenessMultichainSelector,
7-
swapsLivenessSelector,
8-
} from '../../../reducers/swaps';
94
import { isAssetFromSearch } from '../../../selectors/tokenSearchDiscoveryDataController';
10-
import { isPortfolioViewEnabled } from '../../../util/networks';
11-
import { Hex, CaipChainId } from '@metamask/utils';
12-
import { RootState } from '../../../reducers';
13-
14-
export const getSwapsIsLive = (
15-
state: RootState,
16-
chainId: Hex | CaipChainId,
17-
) => {
18-
const evmSwapsIsLive = isPortfolioViewEnabled()
19-
? // @ts-expect-error issues with the type, it should have 2 args
20-
swapsLivenessMultichainSelector(state, chainId)
21-
: swapsLivenessSelector(state);
22-
let swapsIsLive = evmSwapsIsLive;
23-
24-
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
25-
if (chainId === SolScope.Mainnet) {
26-
const solanaSwapsIsLive = selectIsBridgeEnabledSource(state, chainId);
27-
swapsIsLive = solanaSwapsIsLive;
28-
}
29-
///: END:ONLY_INCLUDE_IF(keyring-snaps)
30-
return swapsIsLive;
31-
};
325

336
export const getIsSwapsAssetAllowed = ({
347
asset,

app/components/Views/Wallet/index.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,14 @@ import { QRTabSwitcherScreens } from '../QRTabSwitcher';
134134
import { newAssetTransaction } from '../../../actions/transaction';
135135
import { getEther } from '../../../util/transactions';
136136
import { swapsUtils } from '@metamask/swaps-controller';
137-
import { swapsLivenessSelector } from '../../../reducers/swaps';
138137
import { isSwapsAllowed } from '../../UI/Swaps/utils';
139138
import { isBridgeAllowed } from '../../UI/Bridge/utils';
140139
import AppConstants from '../../../core/AppConstants';
141140
import useRampNetwork from '../../UI/Ramp/Aggregator/hooks/useRampNetwork';
142-
import { selectIsUnifiedSwapsEnabled } from '../../../core/redux/slices/bridge';
141+
import {
142+
selectIsSwapsLive,
143+
selectIsUnifiedSwapsEnabled,
144+
} from '../../../core/redux/slices/bridge';
143145
///: BEGIN:ONLY_INCLUDE_IF(keyring-snaps)
144146
import { useSendNonEvmAsset } from '../../hooks/useSendNonEvmAsset';
145147
///: END:ONLY_INCLUDE_IF
@@ -352,7 +354,9 @@ const Wallet = ({
352354
);
353355

354356
const [isNetworkRampSupported] = useRampNetwork();
355-
const swapsIsLive = useSelector(swapsLivenessSelector);
357+
const swapsIsLive = useSelector((state: RootState) =>
358+
selectIsSwapsLive(state, chainId),
359+
);
356360
const isUnifiedSwapsEnabled = useSelector(selectIsUnifiedSwapsEnabled);
357361

358362
// Setup for AssetDetailsActions

app/components/Views/WalletActions/WalletActions.test.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ import { EarnTokenDetails } from '../../UI/Earn/types/lending.types';
2424
import WalletActions from './WalletActions';
2525
import { selectPerpsEnabledFlag } from '../../UI/Perps';
2626

27+
jest.mock('react-native-device-info', () => ({
28+
getVersion: jest.fn().mockReturnValue('1.0.0'),
29+
}));
30+
2731
jest.mock('../../UI/Perps', () => ({
2832
selectPerpsEnabledFlag: jest.fn(),
2933
}));
@@ -109,8 +113,6 @@ jest.mock('../../../selectors/tokenBalancesController', () => ({
109113

110114
jest.mock('../../../reducers/swaps', () => ({
111115
...jest.requireActual('../../../reducers/swaps'),
112-
swapsLivenessSelector: jest.fn().mockReturnValue(true),
113-
swapsLivenessMultichainSelector: jest.fn().mockReturnValue(true),
114116
swapsTokensWithBalanceSelector: jest.fn().mockReturnValue([]),
115117
swapsControllerAndUserTokens: jest.fn().mockReturnValue([]),
116118
}));
@@ -120,6 +122,7 @@ jest.mock('../../../core/redux/slices/bridge', () => ({
120122
selectAllBridgeableNetworks: jest.fn().mockReturnValue([]),
121123
selectIsBridgeEnabledSource: jest.fn().mockReturnValue(true),
122124
selectIsUnifiedSwapsEnabled: jest.fn().mockReturnValue(false),
125+
selectIsSwapsLive: jest.fn().mockReturnValue(true),
123126
}));
124127

125128
jest.mock('../../../selectors/tokenListController', () => ({

app/components/Views/WalletActions/WalletActions.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import BottomSheet, {
1010
} from '../../../component-library/components/BottomSheets/BottomSheet';
1111
import AppConstants from '../../../core/AppConstants';
1212
import { selectChainId } from '../../../selectors/networkController';
13-
import { swapsLivenessMultichainSelector } from '../../../reducers/swaps';
1413
import { isSwapsAllowed } from '../../../components/UI/Swaps/utils';
1514
import { MetaMetricsEvents } from '../../../core/Analytics';
1615
import { IconName } from '@metamask/design-system-react-native';
@@ -37,6 +36,8 @@ import {
3736
useSwapBridgeNavigation,
3837
SwapBridgeNavigationLocation,
3938
} from '../../UI/Bridge/hooks/useSwapBridgeNavigation';
39+
import { RootState } from '../../../reducers';
40+
import { selectIsSwapsLive } from '../../../core/redux/slices/bridge';
4041

4142
const WalletActions = () => {
4243
const { styles } = useStyles(styleSheet, {});
@@ -46,7 +47,9 @@ const WalletActions = () => {
4647
const { earnTokens } = useSelector(earnSelectors.selectEarnTokens);
4748

4849
const chainId = useSelector(selectChainId);
49-
const swapsIsLive = useSelector(swapsLivenessMultichainSelector);
50+
const swapsIsLive = useSelector((state: RootState) =>
51+
selectIsSwapsLive(state, chainId),
52+
);
5053
const isStablecoinLendingEnabled = useSelector(
5154
selectStablecoinLendingEnabledFlag,
5255
);

app/components/Views/confirmations/legacy/SendFlow/Amount/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ import {
9595
import { selectSelectedInternalAccountFormattedAddress } from '../../../../../../selectors/accountsController';
9696
import Routes from '../../../../../../constants/navigation/Routes';
9797
import { getRampNetworks } from '../../../../../../reducers/fiatOrders';
98-
import { swapsLivenessSelector } from '../../../../../../reducers/swaps';
9998
import { isSwapsAllowed } from '../../../../../UI/Swaps/utils';
10099
import { swapsUtils } from '@metamask/swaps-controller';
101100
import { regex } from '../../../../../../util/regex';
@@ -126,6 +125,7 @@ import { isRemoveGlobalNetworkSelectorEnabled } from '../../../../../../util/net
126125
import { setTransactionSendFlowContextualChainId } from '../../../../../../actions/sendFlow';
127126
import { selectNetworkImageSourceByChainId } from '../../../../../../selectors/networkInfos';
128127
import ContextualNetworkPicker from '../../../../../UI/ContextualNetworkPicker/ContextualNetworkPicker';
128+
import { selectIsSwapsLive } from '../../../../../../core/redux/slices/bridge';
129129

130130
const KEYBOARD_OFFSET = Device.isSmallDevice() ? 80 : 120;
131131

@@ -1862,7 +1862,7 @@ const mapStateToProps = (state, ownProps) => {
18621862
),
18631863
isRedesignedTransferConfirmationEnabledForTransfer:
18641864
selectConfirmationRedesignFlags(state).transfer,
1865-
swapsIsLive: swapsLivenessSelector(state),
1865+
swapsIsLive: selectIsSwapsLive(state, currentChainId),
18661866
globalChainId,
18671867
globalNetworkClientId,
18681868
contextualChainId,

app/components/Views/confirmations/legacy/SendFlow/Amount/index.test.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ import { backgroundState } from '../../../../../../util/test/initial-root-state'
1212
import { setMaxValueMode } from '../../../../../../actions/transaction';
1313
import Routes from '../../../../../../constants/navigation/Routes';
1414

15+
jest.mock('react-native-device-info', () => ({
16+
getVersion: jest.fn().mockReturnValue('1.0.0'),
17+
}));
18+
1519
const mockTransactionTypes = TransactionTypes;
1620

1721
const MOCK_NFTS = [

app/core/WalletConnect/wc-utils.test.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,6 @@ jest.mock('../../selectors/smartTransactionsController', () => ({
5757
selectSmartTransactionsEnabled: () => false,
5858
}));
5959

60-
jest.mock('../../reducers/swaps', () => ({
61-
swapsLivenessSelector: jest.fn().mockReturnValue({}),
62-
}));
63-
6460
jest.mock('../RPCMethods/lib/ethereum-chain-utils', () => ({
6561
findExistingNetwork: jest.fn(),
6662
switchToNetwork: jest.fn().mockResolvedValue(true),

0 commit comments

Comments
 (0)