1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 01:47:00 +01:00

Enable Token search functionality on supported networks (#14034)

This commit is contained in:
Niranjana Binoy 2022-03-31 09:48:05 -04:00 committed by GitHub
parent 9361fab187
commit 9f7c4eb658
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 170 additions and 43 deletions

View File

@ -733,6 +733,12 @@
"customToken": {
"message": "Custom Token"
},
"customTokenWarningInNonTokenDetectionNetwork": {
"message": "Token detection is not available on this network yet. Please import token manually and make sure you trust it. Learn about $1"
},
"customTokenWarningInTokenDetectionNetwork": {
"message": "Before manually importing a token, make sure you trust it. Learn about $1."
},
"customerSupport": {
"message": "customer support"
},
@ -3583,6 +3589,9 @@
"tokenDetection": {
"message": "Token detection"
},
"tokenDetectionAlertMessage": {
"message": "Token detection is currently available on $1. $2"
},
"tokenDetectionAnnouncement": {
"message": "New! Improved token detection is available on Ethereum Mainnet as an experimental feature. $1"
},

View File

@ -39,6 +39,9 @@ export const KOVAN_DISPLAY_NAME = 'Kovan';
export const MAINNET_DISPLAY_NAME = 'Ethereum Mainnet';
export const GOERLI_DISPLAY_NAME = 'Goerli';
export const LOCALHOST_DISPLAY_NAME = 'Localhost 8545';
export const BSC_DISPLAY_NAME = 'Binance Smart Chain';
export const POLYGON_DISPLAY_NAME = 'Polygon';
export const AVALANCHE_DISPLAY_NAME = 'Avalanche';
const infuraProjectId = process.env.INFURA_PROJECT_ID;
export const getRpcUrl = ({ network, excludeProjectId = false }) =>

View File

@ -12,6 +12,7 @@ import {
ADD_COLLECTIBLE_ROUTE,
CONFIRM_IMPORT_TOKEN_ROUTE,
EXPERIMENTAL_ROUTE,
ADVANCED_ROUTE,
} from '../../helpers/constants/routes';
import TextField from '../../components/ui/text-field';
import PageContainer from '../../components/ui/page-container';
@ -25,6 +26,9 @@ import Button from '../../components/ui/button';
import TokenSearch from './token-search';
import TokenList from './token-list';
/*eslint-disable prefer-destructuring*/
const TOKEN_DETECTION_V2 = process.env.TOKEN_DETECTION_V2;
const emptyAddr = '0x0000000000000000000000000000000000000000';
const MIN_DECIMAL_VALUE = 0;
@ -108,6 +112,8 @@ class ImportToken extends Component {
* The currently selected active address.
*/
selectedAddress: PropTypes.string,
isTokenDetectionSupported: PropTypes.bool.isRequired,
networkName: PropTypes.string.isRequired,
};
static defaultProps = {
@ -382,6 +388,7 @@ class ImportToken extends Component {
}
renderCustomTokenForm() {
const { t } = this.context;
const {
customAddress,
customSymbol,
@ -396,7 +403,7 @@ class ImportToken extends Component {
collectibleAddressError,
} = this.state;
const { chainId, rpcPrefs } = this.props;
const { chainId, rpcPrefs, isTokenDetectionSupported } = this.props;
const blockExplorerTokenLink = getTokenTrackerLink(
customAddress,
chainId,
@ -406,31 +413,61 @@ class ImportToken extends Component {
);
const blockExplorerLabel = rpcPrefs?.blockExplorerUrl
? getURLHostName(blockExplorerTokenLink)
: this.context.t('etherscan');
: t('etherscan');
return (
<div className="import-token__custom-token-form">
<ActionableMessage
message={this.context.t('fakeTokenWarning', [
<Button
type="link"
key="import-token-fake-token-warning"
className="import-token__link"
rel="noopener noreferrer"
target="_blank"
href={ZENDESK_URLS.TOKEN_SAFETY_PRACTICES}
>
{this.context.t('learnScamRisk')}
</Button>,
])}
type="warning"
withRightButton
useIcon
iconFillColor="var(--color-warning-default)"
/>
{TOKEN_DETECTION_V2 ? (
<ActionableMessage
type={isTokenDetectionSupported ? 'warning' : 'info'}
message={t(
isTokenDetectionSupported
? 'customTokenWarningInTokenDetectionNetwork'
: 'customTokenWarningInNonTokenDetectionNetwork',
[
<Button
type="link"
key="import-token-fake-token-warning"
className="import-token__link"
rel="noopener noreferrer"
target="_blank"
href={ZENDESK_URLS.TOKEN_SAFETY_PRACTICES}
>
{t('learnScamRisk')}
</Button>,
],
)}
withRightButton
useIcon
iconFillColor={
isTokenDetectionSupported
? 'var(--color-warning-default)'
: 'var(--color-info-default)'
}
/>
) : (
<ActionableMessage
message={this.context.t('fakeTokenWarning', [
<Button
type="link"
key="import-token-fake-token-warning"
className="import-token__link"
rel="noopener noreferrer"
target="_blank"
href={ZENDESK_URLS.TOKEN_SAFETY_PRACTICES}
>
{this.context.t('learnScamRisk')}
</Button>,
])}
type="warning"
withRightButton
useIcon
iconFillColor="var(--color-warning-default)"
/>
)}
<TextField
id="custom-address"
label={this.context.t('tokenContractAddress')}
label={t('tokenContractAddress')}
type="text"
value={customAddress}
onChange={(e) => this.handleCustomAddressChange(e.target.value)}
@ -446,14 +483,14 @@ class ImportToken extends Component {
label={
<div className="import-token__custom-symbol__label-wrapper">
<span className="import-token__custom-symbol__label">
{this.context.t('tokenSymbol')}
{t('tokenSymbol')}
</span>
{symbolAutoFilled && !forceEditSymbol && (
<div
className="import-token__custom-symbol__edit"
onClick={() => this.setState({ forceEditSymbol: true })}
>
{this.context.t('edit')}
{t('edit')}
</div>
)}
</div>
@ -468,7 +505,7 @@ class ImportToken extends Component {
/>
<TextField
id="custom-decimals"
label={this.context.t('decimal')}
label={t('decimal')}
type="number"
value={customDecimals}
onChange={(e) => this.handleCustomDecimalsChange(e.target.value)}
@ -487,13 +524,13 @@ class ImportToken extends Component {
variant={TYPOGRAPHY.H7}
fontWeight={FONT_WEIGHT.BOLD}
>
{this.context.t('tokenDecimalFetchFailed')}
{t('tokenDecimalFetchFailed')}
</Typography>
<Typography
variant={TYPOGRAPHY.H7}
fontWeight={FONT_WEIGHT.NORMAL}
>
{this.context.t('verifyThisTokenDecimalOn', [
{t('verifyThisTokenDecimalOn', [
<Button
type="link"
key="import-token-verify-token-decimal"
@ -518,22 +555,39 @@ class ImportToken extends Component {
}
renderSearchToken() {
const { tokenList, history, useTokenDetection } = this.props;
const { t } = this.context;
const { tokenList, history, useTokenDetection, networkName } = this.props;
const { tokenSelectorError, selectedTokens, searchResults } = this.state;
return (
<div className="import-token__search-token">
{!useTokenDetection && (
<ActionableMessage
message={this.context.t('tokenDetectionAnnouncement', [
<Button
type="link"
key="token-detection-announcement"
className="import-token__link"
onClick={() => history.push(EXPERIMENTAL_ROUTE)}
>
{this.context.t('enableFromSettings')}
</Button>,
])}
message={
TOKEN_DETECTION_V2
? t('tokenDetectionAlertMessage', [
networkName,
<Button
type="link"
key="token-detection-announcement"
className="import-token__link"
onClick={() =>
history.push(`${ADVANCED_ROUTE}#token-description`)
}
>
{t('enableFromSettings')}
</Button>,
])
: this.context.t('tokenDetectionAnnouncement', [
<Button
type="link"
key="token-detection-announcement"
className="import-token__link"
onClick={() => history.push(`${EXPERIMENTAL_ROUTE}`)}
>
{t('enableFromSettings')}
</Button>,
])
}
withRightButton
useIcon
iconFillColor="var(--color-primary-default)"
@ -559,18 +613,19 @@ class ImportToken extends Component {
}
renderTabs() {
const { t } = this.context;
const { showSearchTab } = this.props;
const tabs = [];
if (showSearchTab) {
tabs.push(
<Tab name={this.context.t('search')} key="search-tab">
<Tab name={t('search')} key="search-tab">
{this.renderSearchToken()}
</Tab>,
);
}
tabs.push(
<Tab name={this.context.t('customToken')} key="custom-tab">
<Tab name={t('customToken')} key="custom-tab">
{this.renderCustomTokenForm()}
</Tab>,
);

View File

@ -8,7 +8,8 @@ import {
import { getMostRecentOverviewPage } from '../../ducks/history/history';
import {
getRpcPrefsForCurrentProvider,
getIsMainnet,
getIsTokenDetectionSupported,
getTokenDetectionSupportNetworkByChainId,
} from '../../selectors/selectors';
import ImportToken from './import-token.component';
@ -24,10 +25,8 @@ const mapStateToProps = (state) => {
selectedAddress,
},
} = state;
const showSearchTabCustomNetwork =
useTokenDetection && Boolean(Object.keys(tokenList).length);
const showSearchTab =
getIsMainnet(state) || showSearchTabCustomNetwork || process.env.IN_TEST;
getIsTokenDetectionSupported(state) || process.env.IN_TEST;
return {
identities,
mostRecentOverviewPage: getMostRecentOverviewPage(state),
@ -39,6 +38,8 @@ const mapStateToProps = (state) => {
tokenList,
useTokenDetection,
selectedAddress,
isTokenDetectionSupported: getIsTokenDetectionSupported(state),
networkName: getTokenDetectionSupportNetworkByChainId(state),
};
};
const mapDispatchToProps = (dispatch) => {

View File

@ -60,6 +60,14 @@
margin-top: 0;
}
&__close {
color: var(--color-icon-default);
background: none;
flex: 0;
align-self: flex-start;
padding-right: 0;
}
&__collectible-address-error-link {
color: var(--color-primary-default);
cursor: pointer;

View File

@ -11,6 +11,13 @@ import {
OPTIMISM_CHAIN_ID,
OPTIMISM_TESTNET_CHAIN_ID,
BUYABLE_CHAINS_MAP,
MAINNET_DISPLAY_NAME,
BSC_CHAIN_ID,
POLYGON_CHAIN_ID,
AVALANCHE_CHAIN_ID,
BSC_DISPLAY_NAME,
POLYGON_DISPLAY_NAME,
AVALANCHE_DISPLAY_NAME,
} from '../../shared/constants/network';
import {
KEYRING_TYPES,
@ -901,3 +908,47 @@ export function getIsAdvancedGasFeeDefault(state) {
Boolean(advancedGasFee?.maxBaseFee) && Boolean(advancedGasFee?.priorityFee)
);
}
/**
* @param state
* @returns string e.g. ethereum, bsc or polygon
*/
export const getTokenDetectionSupportNetworkByChainId = (state) => {
const chainId = getCurrentChainId(state);
switch (chainId) {
case MAINNET_CHAIN_ID:
return MAINNET_DISPLAY_NAME;
case BSC_CHAIN_ID:
return BSC_DISPLAY_NAME;
case POLYGON_CHAIN_ID:
return POLYGON_DISPLAY_NAME;
case AVALANCHE_CHAIN_ID:
return AVALANCHE_DISPLAY_NAME;
default:
return '';
}
};
/**
* To check for the chainId that supports token detection ,
* currently it returns true for Ethereum Mainnet, Polygon, BSC and Avalanche
*
* @param {*} state
* @returns Boolean
*/
export function getIsTokenDetectionSupported(state) {
const chainId = getCurrentChainId(state);
return [
MAINNET_CHAIN_ID,
BSC_CHAIN_ID,
POLYGON_CHAIN_ID,
AVALANCHE_CHAIN_ID,
].includes(chainId);
}
export function getTokenDetectionNoticeDismissed(state) {
return state.metamask.tokenDetectionNoticeDismissed;
}
export function getTokenDetectionWarningDismissed(state) {
return state.metamask.tokenDetectionWarningDismissed;
}