mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
Enable Token search functionality on supported networks (#14034)
This commit is contained in:
parent
9361fab187
commit
9f7c4eb658
9
app/_locales/en/messages.json
generated
9
app/_locales/en/messages.json
generated
@ -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"
|
||||
},
|
||||
|
@ -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 }) =>
|
||||
|
@ -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>,
|
||||
);
|
||||
|
@ -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) => {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user