Skip to content

Commit

Permalink
Merge branch 'main' into refactor-test-rename-multichain-bitrise-stuf…
Browse files Browse the repository at this point in the history
…f-to-multichain-permissions
  • Loading branch information
EtherWizard33 committed Dec 20, 2024
2 parents a147cc4 + bea70f1 commit 4390ef0
Show file tree
Hide file tree
Showing 56 changed files with 1,621 additions and 320 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ module.exports = {
{
files: [
'app/components/UI/Name/**/*.{js,ts,tsx}',
'app/components/UI/SimulationDetails/**/*.{js,ts,tsx}',
'app/components/hooks/DisplayName/**/*.{js,ts,tsx}'
],
rules: {
Expand Down
5 changes: 4 additions & 1 deletion .js.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,10 @@ export MM_ENABLE_SETTINGS_PAGE_DEV_OPTIONS="true"
# Per dapp selected network (Amon Hen) feature flag
export MM_PER_DAPP_SELECTED_NETWORK=""

export MM_CHAIN_PERMISSIONS=""
# Multichain permissions now set to true in production via the CI
# MM_MULTICHAIN_V1_ENABLED is the UI, and MM_CHAIN_PERMISSIONS is the engine
export MM_MULTICHAIN_V1_ENABLED="true"
export MM_CHAIN_PERMISSIONS="true"

# Multichain feature flag specific to UI changes
export MM_MULTICHAIN_V1_ENABLED=""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,26 @@ import { ApprovalTypes } from '../../../core/RPCMethods/RPCMethodMiddleware';
import { ApprovalRequest } from '@metamask/approval-controller';
import SwitchChainApproval from './SwitchChainApproval';
import { networkSwitched } from '../../../actions/onboardNetwork';
// eslint-disable-next-line import/no-namespace
import * as networks from '../../../util/networks';
import Engine from '../../../core/Engine';
const { PreferencesController } = Engine.context;

jest.mock('../../Views/confirmations/hooks/useApprovalRequest');
jest.mock('../../../actions/onboardNetwork');

jest.mock('../../../core/Engine', () => ({
context: {
PreferencesController: {
setTokenNetworkFilter: jest.fn(),
},
},
}));

jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useDispatch: () => jest.fn(),
useSelector: jest.fn(),
}));

const URL_MOCK = 'test.com';
Expand All @@ -32,6 +45,7 @@ const mockApprovalRequest = (approvalRequest?: ApprovalRequest<any>) => {
describe('SwitchChainApproval', () => {
beforeEach(() => {
jest.resetAllMocks();
jest.spyOn(networks, 'isPortfolioViewEnabled').mockReturnValue(false);
});

it('renders', () => {
Expand Down Expand Up @@ -81,4 +95,29 @@ describe('SwitchChainApproval', () => {
networkStatus: true,
});
});

it('invokes network switched on confirm when portfolio view is enabled', () => {
jest.spyOn(networks, 'isPortfolioViewEnabled').mockReturnValue(true);
const tokenNetworkFilterSpy = jest.spyOn(
PreferencesController,
'setTokenNetworkFilter',
);
mockApprovalRequest({
type: ApprovalTypes.SWITCH_ETHEREUM_CHAIN,
requestData: {
rpcUrl: URL_MOCK,
},
} as ApprovalRequest<{
rpcUrl: string;
}>);

const wrapper = shallow(<SwitchChainApproval />);
wrapper.find('SwitchCustomNetwork').simulate('confirm');
expect(tokenNetworkFilterSpy).toHaveBeenCalledTimes(1);
expect(networkSwitched).toHaveBeenCalledTimes(1);
expect(networkSwitched).toHaveBeenCalledWith({
networkUrl: URL_MOCK,
networkStatus: true,
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { ApprovalTypes } from '../../../core/RPCMethods/RPCMethodMiddleware';
import ApprovalModal from '../ApprovalModal';
import SwitchCustomNetwork from '../../UI/SwitchCustomNetwork';
import { networkSwitched } from '../../../actions/onboardNetwork';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import Engine from '../../../core/Engine';
import { selectIsAllNetworks } from '../../../selectors/networkController';
import { selectTokenNetworkFilter } from '../../../selectors/preferencesController';
import { isPortfolioViewEnabled } from '../../../util/networks';

const SwitchChainApproval = () => {
const {
Expand All @@ -15,17 +19,34 @@ const SwitchChainApproval = () => {
} = useApprovalRequest();

const dispatch = useDispatch();
const isAllNetworks = useSelector(selectIsAllNetworks);
const tokenNetworkFilter = useSelector(selectTokenNetworkFilter);

const onConfirm = useCallback(() => {
defaultOnConfirm();

// If portfolio view is enabled should set network filter
if (isPortfolioViewEnabled()) {
const { PreferencesController } = Engine.context;
PreferencesController.setTokenNetworkFilter({
...(isAllNetworks ? tokenNetworkFilter : {}),
[approvalRequest?.requestData?.chainId]: true,
});
}

dispatch(
networkSwitched({
networkUrl: approvalRequest?.requestData?.rpcUrl,
networkStatus: true,
}),
);
}, [approvalRequest, defaultOnConfirm, dispatch]);
}, [
approvalRequest,
defaultOnConfirm,
dispatch,
isAllNetworks,
tokenNetworkFilter,
]);

if (approvalRequest?.type !== ApprovalTypes.SWITCH_ETHEREUM_CHAIN)
return null;
Expand Down
12 changes: 10 additions & 2 deletions app/components/UI/SimulationDetails/AmountPill/AmountPill.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,33 @@ import AmountPill from './AmountPill';
import {
AssetIdentifier,
AssetType,
NATIVE_ASSET_IDENTIFIER,
NativeAssetIdentifier,
TokenAssetIdentifier,
} from '../types';

const TOKEN_ID_MOCK = '0xabc';
const CHAIN_ID_MOCK = '0x123';

const ERC20_ASSET_MOCK: TokenAssetIdentifier = {
type: AssetType.ERC20,
address: '0x456',
chainId: CHAIN_ID_MOCK,
};
const ERC721_ASSET_MOCK: TokenAssetIdentifier = {
type: AssetType.ERC721,
address: '0x123',
tokenId: TOKEN_ID_MOCK,
chainId: CHAIN_ID_MOCK,
};
const ERC1155_ASSET_MOCK: TokenAssetIdentifier = {
type: AssetType.ERC1155,
address: '0x789',
tokenId: TOKEN_ID_MOCK,
chainId: CHAIN_ID_MOCK,
};
const NATIVE_ASSET_MOCK: NativeAssetIdentifier = {
type: AssetType.Native,
chainId: CHAIN_ID_MOCK,
};

const renderAndExpect = (
Expand Down Expand Up @@ -83,7 +91,7 @@ describe('AmountPill', () => {
it.each(nativeAndErc20Cases)(
'renders the correct sign and amount for $amount',
({ amount, expected }) => {
renderAndExpect(NATIVE_ASSET_IDENTIFIER, amount, expected);
renderAndExpect(NATIVE_ASSET_MOCK, amount, expected);
},
);
});
Expand Down
51 changes: 31 additions & 20 deletions app/components/UI/SimulationDetails/AssetPill/AssetPill.test.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import React from 'react';
import { render } from '@testing-library/react-native';

import AssetPill from './AssetPill';
import {
selectChainId,
selectTicker,
} from '../../../../selectors/networkController';
import { AssetType, AssetIdentifier } from '../types';
import renderWithProvider from '../../../../util/test/renderWithProvider';
import { mockNetworkState } from '../../../../util/test/network';

jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useSelector: jest.fn().mockImplementation((selector) => selector()),
}));
jest.mock('../../../../selectors/networkController');
jest.mock(
'../../../../component-library/components/Avatars/Avatar/variants/AvatarNetwork',
() => 'AvatarNetwork',
Expand All @@ -22,18 +14,33 @@ jest.mock('../../../hooks/useStyles', () => ({
useStyles: () => ({ styles: {} }),
}));

describe('AssetPill', () => {
const selectChainIdMock = jest.mocked(selectChainId);
const selectTickerMock = jest.mocked(selectTicker);
const CHAIN_ID_MOCK = '0x123';

beforeAll(() => {
selectChainIdMock.mockReturnValue('0x1');
selectTickerMock.mockReturnValue('ETH');
});
const STATE_MOCK = {
engine: {
backgroundState: {
NetworkController: {
...mockNetworkState({
chainId: CHAIN_ID_MOCK,
}),
},
},
},
};

describe('AssetPill', () => {
it('renders correctly for native assets', () => {
const asset = { type: AssetType.Native } as AssetIdentifier;
const { getByText, getByTestId } = render(<AssetPill asset={asset} />);
const asset = {
type: AssetType.Native,
chainId: CHAIN_ID_MOCK,
} as AssetIdentifier;

const { getByText, getByTestId } = renderWithProvider(
<AssetPill asset={asset} />,
{
state: STATE_MOCK,
},
);

expect(
getByTestId('simulation-details-asset-pill-avatar-network'),
Expand All @@ -45,8 +52,12 @@ describe('AssetPill', () => {
const asset = {
type: AssetType.ERC20,
address: '0xabc123',
chainId: CHAIN_ID_MOCK,
} as AssetIdentifier;
const { getByTestId } = render(<AssetPill asset={asset} />);

const { getByTestId } = renderWithProvider(<AssetPill asset={asset} />, {
state: STATE_MOCK,
});

expect(getByTestId('simulation-details-asset-pill-name')).toBeTruthy();
});
Expand Down
29 changes: 14 additions & 15 deletions app/components/UI/SimulationDetails/AssetPill/AssetPill.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,13 @@ import Text, {
} from '../../../../component-library/components/Texts/Text';
import AvatarNetwork from '../../../../component-library/components/Avatars/Avatar/variants/AvatarNetwork';
import { AvatarSize } from '../../../../component-library/components/Avatars/Avatar/Avatar.types';
import {
selectChainId,
selectTicker,
} from '../../../../selectors/networkController';
import { NetworkList } from '../../../../util/networks';
import { useStyles } from '../../../hooks/useStyles';
import Name from '../../Name/Name';
import { NameType } from '../../Name/Name.types';
import { AssetIdentifier, AssetType } from '../types';
import styleSheet from './AssetPill.styles';
import { selectNetworkConfigurations } from '../../../../selectors/networkController';

interface AssetPillProperties extends ViewProps {
asset: AssetIdentifier;
Expand All @@ -35,42 +32,44 @@ const getNetworkImage = (chainId: Hex) => {
return network?.imageSource || null;
};

const NativeAssetPill: React.FC = () => {
const NativeAssetPill: React.FC<AssetPillProperties> = ({ asset }) => {
const { styles } = useStyles(styleSheet, {});
const ticker = useSelector(selectTicker);
const chainId = useSelector(selectChainId);
const imageSource = getNetworkImage(chainId);
const imageSource = getNetworkImage(asset.chainId);

const networkConfigurationsByChainId = useSelector(
selectNetworkConfigurations,
);

const { nativeCurrency } =
networkConfigurationsByChainId[asset.chainId] || {};

return (
<View style={styles.nativeAssetPill}>
<AvatarNetwork
testID="simulation-details-asset-pill-avatar-network"
size={AvatarSize.Xs}
name={ticker}
name={nativeCurrency}
imageSource={imageSource}
/>
<Text variant={TextVariant.BodyMD}>{ticker}</Text>
<Text variant={TextVariant.BodyMD}>{nativeCurrency}</Text>
</View>
);
};

const AssetPill: React.FC<AssetPillProperties> = ({ asset }) => {
const { styles } = useStyles(styleSheet, {});

// TODO: Remove global network selector usage once simulations refactored.
const chainId = useSelector(selectChainId);

return (
<View style={styles.assetPill}>
{asset.type === AssetType.Native ? (
<NativeAssetPill />
<NativeAssetPill asset={asset} />
) : (
<Name
preferContractSymbol
testID="simulation-details-asset-pill-name"
type={NameType.EthereumAddress}
value={asset.address}
variation={chainId}
variation={asset.chainId}
/>
)}
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ jest.mock('../FiatDisplay/FiatDisplay', () => ({
TotalFiatDisplay: 'TotalFiatDisplay',
}));

const CHAIN_ID_MOCK = '0x123';

const balanceChangesMock = [
{
asset: {
type: 'ERC20',
address: '0xabc123',
chainId: CHAIN_ID_MOCK,
},
amount: new BigNumber(100),
fiatAmount: 100,
Expand Down Expand Up @@ -68,6 +71,7 @@ describe('BalanceChangeList', () => {
asset: {
type: 'ERC20',
address: '0xabc123',
chainId: CHAIN_ID_MOCK,
},
amount: new BigNumber(100),
fiatAmount: 100,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,24 @@ import { BigNumber } from 'bignumber.js';
import BalanceChangeRow from './BalanceChangeRow';
import { AssetType, BalanceChange } from '../types';

jest.mock('../AmountPill/AmountPill', () => 'AmountPill');
jest.mock('../AssetPill/AssetPill', () => 'AssetPill');
jest.mock('../FiatDisplay/FiatDisplay', () => ({
IndividualFiatDisplay: 'IndividualFiatDisplay',
}));

const CHAIN_ID_MOCK = '0x123';

const balanceChangeMock: BalanceChange = {
asset: {
type: AssetType.ERC20,
address: '0xabc123',
chainId: CHAIN_ID_MOCK,
},
amount: new BigNumber(100),
fiatAmount: 0,
} as BalanceChange;

jest.mock('../AmountPill/AmountPill', () => 'AmountPill');
jest.mock('../AssetPill/AssetPill', () => 'AssetPill');

describe('BalanceChangeList', () => {
it('renders a balance change row', () => {
const { getByText, getByTestId } = render(
Expand Down
Loading

0 comments on commit 4390ef0

Please sign in to comment.