Skip to content

Commit

Permalink
Merge branch 'main' into feat-3800-settings-drawer-redesign
Browse files Browse the repository at this point in the history
  • Loading branch information
vinnyhoward authored Jan 15, 2025
2 parents 54cf116 + b047169 commit 91215f7
Show file tree
Hide file tree
Showing 106 changed files with 1,341 additions and 552 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ module.exports = {
'selectProviderType',
'selectRpcUrl',
'selectSelectedNetworkClientId',
'selectTicker'
'selectTicker',
]
.map((method) => `(${method})`)
.join('|')}/]`,
Expand Down
5 changes: 1 addition & 4 deletions app/components/UI/AssetOverview/Balance/Balance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import Text, {
} from '../../../../component-library/components/Texts/Text';
import { TokenI } from '../../Tokens/types';
import { useNavigation } from '@react-navigation/native';
import { isPooledStakingFeatureEnabled } from '../../Stake/constants';
import StakingBalance from '../../Stake/components/StakingBalance/StakingBalance';
import {
PopularList,
Expand Down Expand Up @@ -167,9 +166,7 @@ const Balance = ({ asset, mainBalance, secondaryBalance }: BalanceProps) => {
{asset.name || asset.symbol}
</Text>
</AssetElement>
{isPooledStakingFeatureEnabled() && asset?.isETH && (
<StakingBalance asset={asset} />
)}
{asset?.isETH && <StakingBalance asset={asset} />}
</View>
);
};
Expand Down
74 changes: 66 additions & 8 deletions app/components/UI/AssetOverview/Balance/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,21 @@ const mockETH = {
isNative: true,
};

const mockInitialState = {
engine: {
backgroundState,
jest.mock('../../../../core/Engine', () => ({
context: {
NetworkController: {
getNetworkClientById: () => ({
configuration: {
chainId: '0x1',
rpcUrl: 'https://mainnet.infura.io/v3',
ticker: 'ETH',
type: 'custom',
},
}),
findNetworkClientIdByChainId: () => 'mainnet',
},
},
};
}));

jest.mock('../../../../util/networks', () => ({
...jest.requireActual('../../../../util/networks'),
Expand All @@ -74,6 +84,48 @@ jest.mock('../../../../util/networks', () => ({
isPortfolioViewEnabled: jest.fn(),
}));

jest.mock('../../Stake/hooks/usePooledStakes', () => ({
__esModule: true,
default: () => ({
pooledStakesData: {
account: '0xabc',
assets: '10000000000000000',
exitRequests: [],
lifetimeRewards: '100000000000000',
},
exchangeRate: 1.018,
hasStakedPositions: true,
hasEthToUnstake: true,
isLoadingPooledStakesData: false,
}),
}));

jest.mock('../../Stake/hooks/useVaultData', () => ({
__esModule: true,
default: () => ({
vaultData: {
apy: '2.437033146840025387168141592920355',
capacity: '1000000000000000000000000000000000000000000000000000000000000',
feePercent: 1500,
totalAssets: '10000000000000000000000',
vaultAddress: '0xdef',
},
}),
}));

jest.mock('../../Stake/hooks/useStakingEligibility', () => ({
__esModule: true,
default: () => ({
isEligible: true,
}),
}));

const mockInitialState = {
engine: {
backgroundState,
},
};

describe('Balance', () => {
const mockStore = configureMockStore();
const store = mockStore(mockInitialState);
Expand Down Expand Up @@ -131,13 +183,19 @@ describe('Balance', () => {
});

it('should not fire navigation event for native tokens', () => {
const { queryByTestId } = render(
const { queryAllByTestId } = render(
<Provider store={store}>
<Balance asset={mockETH} mainBalance="100" secondaryBalance="200" />
<Balance asset={mockETH} mainBalance="100" secondaryBalance="200" />,
</Provider>,
);
const assetElement = queryByTestId('asset-ETH');
fireEvent.press(assetElement);

// Includes native ETH and staked ETH
const ethElements = queryAllByTestId('asset-ETH');

ethElements.forEach((ethElement) => {
fireEvent.press(ethElement);
});

expect(mockNavigate).toHaveBeenCalledTimes(0);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import Logger from '../../../../util/Logger';
import TokenDetailsList from './TokenDetailsList';
import MarketDetailsList from './MarketDetailsList';
import { TokenI } from '../../Tokens/types';
import { isPooledStakingFeatureEnabled } from '../../Stake/constants';
import StakingEarnings from '../../Stake/components/StakingEarnings';
import { isPortfolioViewEnabled } from '../../../../util/networks';

Expand Down Expand Up @@ -147,7 +146,7 @@ const TokenDetails: React.FC<TokenDetailsProps> = ({ asset }) => {

return (
<View style={styles.tokenDetailsContainer}>
{asset.isETH && isPooledStakingFeatureEnabled() && <StakingEarnings />}
{asset.isETH && <StakingEarnings asset={asset} />}
{(asset.isETH || tokenMetadata) && (
<TokenDetailsList tokenDetails={tokenDetails} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,43 @@ describe('NetworkVerificationInfo', () => {

expect(getByText('10')).toBeTruthy();
});

it('should not render Network URL warning banner when the custom rpc url has all ascii characters', () => {
(useSelector as jest.Mock).mockReturnValue(true);
const { getByText } = render(
<NetworkVerificationInfo
customNetworkInformation={mockNetworkInfo}
onReject={() => undefined}
onConfirm={() => undefined}
/>,
);

expect(() =>
getByText(
"Attackers sometimes mimic sites by making small changes to the site address. Make sure you're interacting with the intended Network URL before you continue. Punycode version: https://xn--ifura-dig.io/gnosis",
),
).toThrow('Unable to find an element with text');
});

describe('when the custom rpc url has non-ascii characters', () => {
it('should render Network URL warning banner and display punycode encoded version', () => {
(useSelector as jest.Mock).mockReturnValue(true);
const { getByText } = render(
<NetworkVerificationInfo
customNetworkInformation={{
...mockNetworkInfo,
rpcUrl: 'https://iոfura.io/gnosis',
}}
onReject={() => undefined}
onConfirm={() => undefined}
/>,
);

expect(
getByText(
"Attackers sometimes mimic sites by making small changes to the site address. Make sure you're interacting with the intended Network URL before you continue. Punycode version: https://xn--ifura-dig.io/gnosis",
),
).toBeTruthy();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { toggleUseSafeChainsListValidation } from '../../../util/networks/engine
import { NetworkApprovalBottomSheetSelectorsIDs } from '../../../../e2e/selectors/Network/NetworkApprovalBottomSheet.selectors';
import hideKeyFromUrl from '../../../util/hideKeyFromUrl';
import { convertHexToDecimal } from '@metamask/controller-utils';
import { isValidASCIIURL, toPunycodeURL } from '../../../util/url';

interface Alert {
alertError: string;
Expand Down Expand Up @@ -83,6 +84,8 @@ const NetworkVerificationInfo = ({
const showReviewDefaultRpcUrlChangesModal = () =>
setShowReviewDefaultRpcUrlChanges(!showReviewDefaultRpcUrlChanges);

const customRpcUrl = customNetworkInformation.rpcUrl;

const goToLearnMore = () => {
Linking.openURL(
'https://support.metamask.io/networks-and-sidechains/managing-networks/verifying-custom-network-information/',
Expand Down Expand Up @@ -236,9 +239,7 @@ const NetworkVerificationInfo = ({
: strings('add_custom_network.network_url')}
</Text>
)}
<Text style={styles.textSection}>
{hideKeyFromUrl(customNetworkInformation.rpcUrl)}
</Text>
<Text style={styles.textSection}>{hideKeyFromUrl(customRpcUrl)}</Text>

<Accordion
title={strings('spend_limit_edition.view_details')}
Expand Down Expand Up @@ -291,6 +292,27 @@ const NetworkVerificationInfo = ({
return null;
};

const renderBannerNetworkUrlNonAsciiDetected = () => {
if (!customRpcUrl || isValidASCIIURL(customRpcUrl)) {
return null;
}
const punycodeUrl = toPunycodeURL(customRpcUrl);

return (
<View style={styles.alertBar}>
<Banner
severity={BannerAlertSeverity.Warning}
variant={BannerVariant.Alert}
description={
strings('networks.network_rpc_url_warning_punycode') +
'\n' +
punycodeUrl
}
/>
</View>
);
};

const renderCustomNetworkBanner = () => (
<View style={styles.alertBar}>
<Banner
Expand Down Expand Up @@ -331,9 +353,7 @@ const NetworkVerificationInfo = ({
<Text variant={TextVariant.BodyMDBold}>
{strings('networks.current_label')}
</Text>
<Text style={styles.textSection}>
{customNetworkInformation.rpcUrl}
</Text>
<Text style={styles.textSection}>{customRpcUrl}</Text>
<Text variant={TextVariant.BodyMDBold}>
{strings('networks.new_label')}
</Text>
Expand Down Expand Up @@ -442,6 +462,7 @@ const NetworkVerificationInfo = ({
/>
{renderAlerts()}
{renderBanner()}
{renderBannerNetworkUrlNonAsciiDetected()}
{isMultichainVersion1Enabled &&
isCustomNetwork &&
renderCustomNetworkBanner()}
Expand Down
8 changes: 7 additions & 1 deletion app/components/UI/PermissionsSummary/PermissionsSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,13 @@ const PermissionsSummary = ({
</TextComponent>
<View style={styles.permissionRequestAccountInfo}>
<View style={styles.permissionRequestAccountName}>
<TextComponent numberOfLines={1} ellipsizeMode="tail">
<TextComponent
testID={
PermissionSummaryBottomSheetSelectorsIDs.ACCOUNT_PERMISSION_CONTAINER
}
numberOfLines={1}
ellipsizeMode="tail"
>
<TextComponent variant={TextVariant.BodySM}>
{getAccountLabel()}
</TextComponent>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ exports[`PermissionsSummary should render correctly 1`] = `
"lineHeight": 22,
}
}
testID="permission-summary-account-text"
>
<Text
accessibilityRole="text"
Expand Down
25 changes: 14 additions & 11 deletions app/components/UI/Stake/__mocks__/mockData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import { Contract } from 'ethers';
import { Stake } from '../sdk/stakeSdkProvider';

export const MOCK_STAKED_ETH_ASSET = {
chainId: '0x1',
balance: '4.9999 ETH',
balanceFiat: '$13,292.20',
name: 'Staked Ethereum',
symbol: 'ETH',
isETH: true,
} as TokenI;

export const MOCK_GET_POOLED_STAKES_API_RESPONSE: PooledStakes = {
Expand Down Expand Up @@ -67,17 +69,18 @@ export const MOCK_GET_POOLED_STAKES_API_RESPONSE: PooledStakes = {
exchangeRate: '1.010906701603882254',
};

export const MOCK_GET_POOLED_STAKES_API_RESPONSE_HIGH_ASSETS_AMOUNT: PooledStakes = {
accounts: [
{
account: '0x0111111111abcdef2222222222abcdef33333333',
lifetimeRewards: '0',
assets: '99999999990000000000000',
exitRequests: [],
},
],
exchangeRate: '1.010906701603882254',
};
export const MOCK_GET_POOLED_STAKES_API_RESPONSE_HIGH_ASSETS_AMOUNT: PooledStakes =
{
accounts: [
{
account: '0x0111111111abcdef2222222222abcdef33333333',
lifetimeRewards: '0',
assets: '99999999990000000000000',
exitRequests: [],
},
],
exchangeRate: '1.010906701603882254',
};

export const MOCK_GET_VAULT_RESPONSE: VaultData = {
apy: '2.853065141088762750393474836309926',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,6 @@ jest.mock('@react-navigation/native', () => {
};
});

jest.mock('../../constants', () => ({
isPooledStakingFeatureEnabled: jest.fn().mockReturnValue(true),
}));

jest.mock('../../../../hooks/useMetrics');

(useMetrics as jest.MockedFn<typeof useMetrics>).mockReturnValue({
Expand Down
18 changes: 10 additions & 8 deletions app/components/UI/Stake/components/StakeButton/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import { TokenI, BrowserTab } from '../../../Tokens/types';
import { useNavigation } from '@react-navigation/native';
import { isPooledStakingFeatureEnabled } from '../../constants';
import Routes from '../../../../../constants/navigation/Routes';
import { useSelector } from 'react-redux';
import AppConstants from '../../../../../core/AppConstants';
Expand All @@ -26,6 +25,8 @@ import { RootState } from '../../../../../reducers';
import useStakingEligibility from '../../hooks/useStakingEligibility';
import { StakeSDKProvider } from '../../sdk/stakeSdkProvider';
import { EVENT_LOCATIONS } from '../../constants/events';
import useStakingChain from '../../hooks/useStakingChain';
import Engine from '../../../../../core/Engine';

interface StakeButtonProps {
asset: TokenI;
Expand All @@ -38,12 +39,15 @@ const StakeButtonContent = ({ asset }: StakeButtonProps) => {

const browserTabs = useSelector((state: RootState) => state.browser.tabs);
const chainId = useSelector(selectChainId);

const { refreshPooledStakingEligibility } = useStakingEligibility();
const { isEligible } = useStakingEligibility();
const { isStakingSupportedChain } = useStakingChain();

const onStakeButtonPress = async () => {
const { isEligible } = await refreshPooledStakingEligibility();
if (isPooledStakingFeatureEnabled() && isEligible) {
if (!isStakingSupportedChain) {
const { NetworkController } = Engine.context;
await NetworkController.setActiveNetwork('mainnet');
}
if (isEligible) {
navigation.navigate('StakeScreens', { screen: Routes.STAKING.STAKE });
} else {
const existingStakeTab = browserTabs.find((tab: BrowserTab) =>
Expand Down Expand Up @@ -88,9 +92,7 @@ const StakeButtonContent = ({ asset }: StakeButtonProps) => {
<Text variant={TextVariant.BodyLGMedium}>
{' • '}
<Text color={TextColor.Primary} variant={TextVariant.BodyLGMedium}>
{isPooledStakingFeatureEnabled()
? `${strings('stake.earn')} `
: `${strings('stake.stake')} `}
{`${strings('stake.earn')} `}
</Text>
</Text>
<Icon
Expand Down
Loading

0 comments on commit 91215f7

Please sign in to comment.