1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Refactor token send/method confirmation flow (trimmed down) (#13788)

* make use of getTokenStandardAndDetails method exposed on assetsContractController to determine how to represent the contract being interacted with in token contract method calls
This commit is contained in:
Alex Donesky 2022-03-09 08:38:12 -06:00 committed by GitHub
parent bc62e77cc3
commit 3747ace06b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 782 additions and 348 deletions

View File

@ -58,6 +58,10 @@
"message": "$1 may access and spend up to this max amount",
"description": "$1 is the url of the site requesting ability to spend"
},
"accessAndSpendNoticeNFT": {
"message": "$1 may access and spend this asset",
"description": "$1 is the url of the site requesting ability to spend"
},
"accessingYourCamera": {
"message": "Accessing your camera..."
},
@ -279,6 +283,9 @@
"approvedAmountWithColon": {
"message": "Approved amount:"
},
"approvedAsset": {
"message": "Approved asset"
},
"areYouDeveloper": {
"message": "Are you a developer?"
},

View File

@ -1271,7 +1271,6 @@ export default class MetamaskController extends EventEmitter {
appStateController,
collectiblesController,
collectibleDetectionController,
assetsContractController,
currencyRateController,
detectTokensController,
ensController,
@ -1427,9 +1426,7 @@ export default class MetamaskController extends EventEmitter {
setTheme: preferencesController.setTheme.bind(preferencesController),
// AssetsContractController
getTokenStandardAndDetails: assetsContractController.getTokenStandardAndDetails.bind(
assetsContractController,
),
getTokenStandardAndDetails: this.getTokenStandardAndDetails.bind(this),
// CollectiblesController
addCollectible: collectiblesController.addCollectible.bind(
@ -1758,6 +1755,19 @@ export default class MetamaskController extends EventEmitter {
};
}
async getTokenStandardAndDetails(address, userAddress, tokenId) {
const details = await this.assetsContractController.getTokenStandardAndDetails(
address,
userAddress,
tokenId,
);
return {
...details,
decimals: details?.decimals?.toString(10),
balance: details?.balance?.toString(10),
};
}
//=============================================================================
// VAULT / KEYRING RELATED METHODS
//=============================================================================

View File

@ -16,6 +16,7 @@ const { ensureXServerIsRunning } = require('./x-server');
const tinyDelayMs = 200;
const regularDelayMs = tinyDelayMs * 2;
const largeDelayMs = regularDelayMs * 2;
const veryLargeDelayMs = largeDelayMs * 2;
const dappPort = 8080;
const convertToHexValue = (val) => `0x${new BigNumber(val, 10).toString(16)}`;
@ -276,6 +277,7 @@ module.exports = {
tinyDelayMs,
regularDelayMs,
largeDelayMs,
veryLargeDelayMs,
withFixtures,
connectDappWithExtensionPopup,
completeImportSRPOnboardingFlow,

View File

@ -3,7 +3,12 @@ const path = require('path');
const enLocaleMessages = require('../../app/_locales/en/messages.json');
const createStaticServer = require('../../development/create-static-server');
const { tinyDelayMs, regularDelayMs, largeDelayMs } = require('./helpers');
const {
tinyDelayMs,
regularDelayMs,
largeDelayMs,
veryLargeDelayMs,
} = require('./helpers');
const { buildWebDriver } = require('./webdriver');
const Ganache = require('./ganache');
const { ensureXServerIsRunning } = require('./x-server');
@ -272,7 +277,7 @@ describe('MetaMask', function () {
const gasPriceInput = inputs[1];
await gasLimitInput.fill('4700000');
await gasPriceInput.fill('20');
await driver.delay(1000);
await driver.delay(veryLargeDelayMs);
await driver.clickElement({ text: 'Save', tag: 'button' });
await driver.clickElement({ text: 'Confirm', tag: 'button' });
@ -343,10 +348,11 @@ describe('MetaMask', function () {
// Continue to next screen
await driver.delay(largeDelayMs);
await driver.clickElement({ text: 'Next', tag: 'button' });
await driver.delay(regularDelayMs);
await driver.delay(largeDelayMs);
});
it('displays the token transfer data', async function () {
await driver.delay(largeDelayMs);
await driver.clickElement({ text: 'Hex', tag: 'button' });
await driver.delay(regularDelayMs);
@ -386,7 +392,7 @@ describe('MetaMask', function () {
const gasPriceInput = inputs[1];
await gasLimitInput.fill('100000');
await gasPriceInput.fill('100');
await driver.delay(1000);
await driver.delay(veryLargeDelayMs);
await driver.clickElement({ text: 'Save', tag: 'button' });
});
@ -449,19 +455,20 @@ describe('MetaMask', function () {
});
it('customizes gas', async function () {
await driver.delay(veryLargeDelayMs);
await driver.clickElement({ text: 'Edit', tag: 'button' });
await driver.delay(largeDelayMs);
await driver.delay(veryLargeDelayMs);
await driver.clickElement(
{ text: 'Edit suggested gas fee', tag: 'button' },
10000,
);
await driver.delay(1000);
await driver.delay(veryLargeDelayMs);
const inputs = await driver.findElements('input[type="number"]');
const gasLimitInput = inputs[0];
const gasPriceInput = inputs[1];
await gasLimitInput.fill('60000');
await gasPriceInput.fill('10');
await driver.delay(1000);
await driver.delay(veryLargeDelayMs);
await driver.clickElement({ text: 'Save', tag: 'button' });
await driver.findElement({ tag: 'span', text: '0.0006' });
});
@ -586,7 +593,7 @@ describe('MetaMask', function () {
await gasLimitInput.fill('60001');
await driver.delay(1000);
await driver.delay(veryLargeDelayMs);
await driver.clickElement({ text: 'Save', tag: 'button' });
@ -752,7 +759,7 @@ describe('MetaMask', function () {
});
it('submits the transaction', async function () {
await driver.delay(1000);
await driver.delay(veryLargeDelayMs);
await driver.clickElement({ text: 'Confirm', tag: 'button' });
await driver.delay(regularDelayMs);
});

View File

@ -11,6 +11,8 @@ import useAddressDetails from '../../../../../hooks/useAddressDetails';
import Identicon from '../../../../ui/identicon';
import InfoTooltip from '../../../../ui/info-tooltip';
import NicknamePopovers from '../../../modals/nickname-popovers';
import Typography from '../../../../ui/typography';
import { TYPOGRAPHY } from '../../../../../helpers/constants/design-system';
const ConfirmPageContainerSummary = (props) => {
const {
@ -116,9 +118,15 @@ const ConfirmPageContainerSummary = (props) => {
<div className="confirm-page-container-summary__title">
{renderImage()}
{!hideTitle ? (
<div className="confirm-page-container-summary__title-text">
<Typography
className="confirm-page-container-summary__title-text"
variant={
title && title.length < 10 ? TYPOGRAPHY.H1 : TYPOGRAPHY.H3
}
title={title}
>
{titleComponent || title}
</div>
</Typography>
) : null}
</div>
{hideSubtitle ? null : (

View File

@ -77,6 +77,14 @@
text-overflow: ellipsis;
}
&__title-text-long {
@include H3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&__subtitle {
@include H5;

View File

@ -82,6 +82,7 @@ export default function Typography({
fontStyle = 'normal',
align,
overflowWrap,
title,
tag,
margin = [1, 0],
boxProps = {},
@ -117,7 +118,10 @@ export default function Typography({
return (
<Box margin={margin} {...boxProps}>
{(boxClassName) => (
<Tag className={classnames(boxClassName, computedClassName)}>
<Tag
className={classnames(boxClassName, computedClassName)}
title={title}
>
{children}
</Tag>
)}
@ -175,6 +179,10 @@ Typography.propTypes = {
* Additional className to assign the Typography component
*/
className: PropTypes.string,
/**
* Title attribute to include on the element. Will show as tooltip on hover.
*/
title: PropTypes.string,
/**
* The text content of the Typography component
*/

View File

@ -4,8 +4,12 @@ import {
conversionUtil,
multiplyCurrencies,
} from '../../../shared/modules/conversion.utils';
import { getTokenStandardAndDetails } from '../../store/actions';
import { ERC1155, ERC721 } from '../constants/common';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
import * as util from './util';
import { formatCurrency } from './confirm-tx.util';
import { getTransactionData } from './transactions.util';
const DEFAULT_SYMBOL = '';
@ -212,3 +216,48 @@ export function getTokenFiatAmount(
}
return result;
}
export async function getAssetDetails(
tokenAddress,
currentUserAddress,
transactionData,
existingCollectibles,
) {
const tokenData = getTransactionData(transactionData);
if (!tokenData) {
throw new Error('Unable to detect valid token data');
}
const tokenId = getTokenValueParam(tokenData);
let tokenDetails;
try {
tokenDetails = await getTokenStandardAndDetails(
tokenAddress,
currentUserAddress,
tokenId,
);
} catch (error) {
log.warn(error);
return {};
}
if (tokenDetails?.standard) {
const { standard } = tokenDetails;
if (standard === ERC721 || standard === ERC1155) {
const existingCollectible = existingCollectibles.find(({ address }) =>
isEqualCaseInsensitive(tokenAddress, address),
);
if (existingCollectible) {
return {
...existingCollectible,
standard,
};
}
}
// else if not a collectible already in state or standard === ERC20 just return tokenDetails as it contains all required data
return tokenDetails;
}
return {};
}

118
ui/hooks/useAssetDetails.js Normal file
View File

@ -0,0 +1,118 @@
import { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { getCollectibles, getTokens } from '../ducks/metamask/metamask';
import { ERC1155, ERC721, ERC20 } from '../helpers/constants/common';
import {
calcTokenAmount,
getAssetDetails,
getTokenAddressParam,
getTokenValueParam,
} from '../helpers/utils/token-util';
import { getTransactionData } from '../helpers/utils/transactions.util';
import { getTokenList } from '../selectors';
import { hideLoadingIndication, showLoadingIndication } from '../store/actions';
import { usePrevious } from './usePrevious';
export function useAssetDetails(tokenAddress, userAddress, transactionData) {
const dispatch = useDispatch();
// state selectors
const tokens = useSelector(getTokens);
const collectibles = useSelector(getCollectibles);
const tokenList = useSelector(getTokenList);
// in-hook state
const [currentAsset, setCurrentAsset] = useState(null);
// previous state checkers
const prevTokenAddress = usePrevious(tokenAddress);
const prevUserAddress = usePrevious(userAddress);
const prevTransactionData = usePrevious(transactionData);
useEffect(() => {
async function getAndSetAssetDetails() {
dispatch(showLoadingIndication());
const assetDetails = await getAssetDetails(
tokenAddress,
userAddress,
transactionData,
collectibles,
tokens,
tokenList,
);
setCurrentAsset(assetDetails);
dispatch(hideLoadingIndication());
}
if (
tokenAddress !== prevTokenAddress ||
userAddress !== prevUserAddress ||
transactionData !== prevTransactionData
) {
getAndSetAssetDetails();
}
}, [
dispatch,
prevTokenAddress,
prevTransactionData,
prevUserAddress,
tokenAddress,
userAddress,
transactionData,
collectibles,
tokens,
tokenList,
]);
let assetStandard,
assetName,
assetAddress,
tokenSymbol,
decimals,
tokenImage,
userBalance,
tokenValue,
toAddress,
tokenAmount,
tokenId;
if (currentAsset) {
const {
standard,
symbol,
image,
name,
balance,
decimals: currentAssetDecimals,
} = currentAsset;
const tokenData = getTransactionData(transactionData);
assetStandard = standard;
assetAddress = tokenAddress;
tokenSymbol = symbol;
tokenImage = image;
toAddress = getTokenAddressParam(tokenData);
if (assetStandard === ERC721 || assetStandard === ERC1155) {
assetName = name;
tokenId = getTokenValueParam(tokenData);
}
if (assetStandard === ERC20) {
userBalance = balance;
decimals = Number(currentAssetDecimals?.toString(10));
tokenAmount =
tokenData &&
calcTokenAmount(getTokenValueParam(tokenData), decimals).toString(10);
}
}
return {
assetStandard,
assetName,
assetAddress,
userBalance,
tokenSymbol,
decimals,
tokenImage,
tokenValue,
toAddress,
tokenAmount,
tokenId,
};
}

View File

@ -28,6 +28,7 @@ import { SECOND } from '../../../../shared/constants/time';
import { ConfirmPageContainerWarning } from '../../../components/app/confirm-page-container/confirm-page-container-content';
import GasDetailsItem from '../../../components/app/gas-details-item';
import LedgerInstructionField from '../../../components/app/ledger-instruction-field';
import { ERC1155, ERC20, ERC721 } from '../../../helpers/constants/common';
export default class ConfirmApproveContent extends Component {
static contextTypes = {
@ -60,13 +61,15 @@ export default class ConfirmApproveContent extends Component {
warning: PropTypes.string,
txData: PropTypes.object,
fromAddressIsLedger: PropTypes.bool,
tokenImage: PropTypes.string,
chainId: PropTypes.string,
rpcPrefs: PropTypes.object,
isContract: PropTypes.bool,
hexTransactionTotal: PropTypes.string,
isMultiLayerFeeNetwork: PropTypes.bool,
supportsEIP1559V2: PropTypes.bool,
assetName: PropTypes.string,
tokenId: PropTypes.string,
assetStandard: PropTypes.string,
};
state = {
@ -178,7 +181,60 @@ export default class ConfirmApproveContent extends Component {
);
}
renderPermissionContent() {
renderERC721OrERC1155PermissionContent() {
const { t } = this.context;
const { origin, toAddress, isContract, assetName, tokenId } = this.props;
const displayedAddress = isContract
? `${t('contract')} (${addressSummary(toAddress)})`
: addressSummary(toAddress);
return (
<div className="flex-column">
<div className="confirm-approve-content__small-text">
{t('accessAndSpendNoticeNFT', [origin])}
</div>
<div className="flex-row">
<div className="confirm-approve-content__label">
{t('approvedAsset')}:
</div>
<div className="confirm-approve-content__medium-text">
{`${assetName} #${tokenId}`}
</div>
</div>
<div className="flex-row">
<div className="confirm-approve-content__label">
{t('grantedToWithColon')}
</div>
<div className="confirm-approve-content__medium-text">
{displayedAddress}
</div>
<div className="confirm-approve-content__medium-text">
<Button
type="link"
className="confirm-approve-content__copy-address"
onClick={() => {
this.setState({ copied: true });
this.copyTimeout = setTimeout(
() => this.setState({ copied: false }),
SECOND * 3,
);
copyToClipboard(toAddress);
}}
title={
this.state.copied
? t('copiedExclamation')
: t('copyToClipboard')
}
>
<CopyIcon size={14} color="#6a737d" />
</Button>
</div>
</div>
</div>
);
}
renderERC20PermissionContent() {
const { t } = this.context;
const {
customTokenAmount,
@ -188,6 +244,7 @@ export default class ConfirmApproveContent extends Component {
toAddress,
isContract,
} = this.props;
const displayedAddress = isContract
? `${t('contract')} (${addressSummary(toAddress)})`
: addressSummary(toAddress);
@ -252,6 +309,75 @@ export default class ConfirmApproveContent extends Component {
);
}
renderFullDetails() {
const { t } = this.context;
const {
assetStandard,
showEditApprovalPermissionModal,
customTokenAmount,
tokenAmount,
decimals,
origin,
setCustomAmount,
tokenSymbol,
tokenBalance,
} = this.props;
if (assetStandard === ERC20) {
return (
<div className="confirm-approve-content__full-tx-content">
<div className="confirm-approve-content__permission">
{this.renderApproveContentCard({
symbol: <img src="./images/user-check.svg" alt="" />,
title: t('permissionRequest'),
content: this.renderERC20PermissionContent(),
showEdit: true,
onEditClick: () =>
showEditApprovalPermissionModal({
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenSymbol,
tokenBalance,
}),
})}
</div>
<div className="confirm-approve-content__data">
{this.renderApproveContentCard({
symbol: <i className="fa fa-file" />,
title: 'Data',
content: this.renderDataContent(),
noBorder: true,
})}
</div>
</div>
);
} else if (assetStandard === ERC721 || assetStandard === ERC1155) {
return (
<div className="confirm-approve-content__full-tx-content">
<div className="confirm-approve-content__permission">
{this.renderApproveContentCard({
symbol: <img src="./images/user-check.svg" alt="" />,
title: t('permissionRequest'),
content: this.renderERC721OrERC1155PermissionContent(),
showEdit: false,
})}
</div>
<div className="confirm-approve-content__data">
{this.renderApproveContentCard({
symbol: <i className="fa fa-file" />,
title: t('data'),
content: this.renderDataContent(),
noBorder: true,
})}
</div>
</div>
);
}
return null;
}
renderCustomNonceContent() {
const { t } = this.context;
const {
@ -321,11 +447,13 @@ export default class ConfirmApproveContent extends Component {
warning,
txData,
fromAddressIsLedger,
tokenImage,
toAddress,
chainId,
rpcPrefs,
isContract,
assetStandard,
tokenId,
assetName,
} = this.props;
const { showFullTxDetails } = this.state;
@ -368,7 +496,11 @@ export default class ConfirmApproveContent extends Component {
</Box>
</Box>
<div className="confirm-approve-content__title">
{t('allowSpendToken', [tokenSymbol])}
{t('allowSpendToken', [
assetStandard === ERC20
? tokenSymbol
: `${assetName} (#${tokenId})`,
])}
</div>
<div className="confirm-approve-content__description">
{t('trustSiteApprovePermission', [
@ -383,7 +515,6 @@ export default class ConfirmApproveContent extends Component {
className="confirm-approve-content__address-identicon"
diameter={20}
address={toAddress}
image={tokenImage}
/>
<Typography
variant={TYPOGRAPHY.H6}
@ -438,24 +569,26 @@ export default class ConfirmApproveContent extends Component {
</Button>
</Box>
</Box>
<div className="confirm-approve-content__edit-submission-button-container">
<div
className="confirm-approve-content__medium-link-text cursor-pointer"
onClick={() =>
showEditApprovalPermissionModal({
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenSymbol,
tokenBalance,
})
}
>
{t('editPermission')}
{assetStandard === ERC20 ? (
<div className="confirm-approve-content__edit-submission-button-container">
<div
className="confirm-approve-content__medium-link-text cursor-pointer"
onClick={() =>
showEditApprovalPermissionModal({
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenSymbol,
tokenBalance,
})
}
>
{t('editPermission')}
</div>
</div>
</div>
) : null}
<div className="confirm-approve-content__card-wrapper">
{this.renderApproveContentCard({
symbol: <i className="fa fa-tag" />,
@ -527,36 +660,7 @@ export default class ConfirmApproveContent extends Component {
</div>
) : null}
{showFullTxDetails ? (
<div className="confirm-approve-content__full-tx-content">
<div className="confirm-approve-content__permission">
{this.renderApproveContentCard({
symbol: <img src="./images/user-check.svg" alt="" />,
title: t('permissionRequest'),
content: this.renderPermissionContent(),
showEdit: true,
onEditClick: () =>
showEditApprovalPermissionModal({
customTokenAmount,
decimals,
origin,
setCustomAmount,
tokenAmount,
tokenSymbol,
tokenBalance,
}),
})}
</div>
<div className="confirm-approve-content__data">
{this.renderApproveContentCard({
symbol: <i className="fa fa-file" />,
title: 'Data',
content: this.renderDataContent(),
noBorder: true,
})}
</div>
</div>
) : null}
{showFullTxDetails ? this.renderFullDetails() : null}
</div>
);
}

View File

@ -2,6 +2,7 @@ import React from 'react';
import configureMockStore from 'redux-mock-store';
import { fireEvent } from '@testing-library/react';
import { renderWithProvider } from '../../../../test/jest/rendering';
import { ERC20 } from '../../../helpers/constants/common';
import ConfirmApproveContent from '.';
const renderComponent = (props) => {
@ -16,6 +17,7 @@ const props = {
tokenAmount: '10',
origin: 'https://metamask.github.io/test-dapp/',
tokenSymbol: 'TST',
assetStandard: ERC20,
tokenImage: 'https://metamask.github.io/test-dapp/metamask-fox.svg',
tokenBalance: '15',
showCustomizeGasModal: jest.fn(),

View File

@ -1,6 +1,6 @@
import React, { useEffect, useRef, useState, useCallback } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import ConfirmTransactionBase from '../confirm-transaction-base';
import { EDIT_GAS_MODES } from '../../../shared/constants/gas';
import {
@ -8,24 +8,15 @@ import {
updateCustomNonce,
getNextNonce,
} from '../../store/actions';
import { getTransactionData } from '../../helpers/utils/transactions.util';
import {
calcTokenAmount,
getTokenAddressParam,
getTokenValueParam,
} from '../../helpers/utils/token-util';
import { calcTokenAmount } from '../../helpers/utils/token-util';
import { readAddressAsContract } from '../../../shared/modules/contract-utils';
import { GasFeeContextProvider } from '../../contexts/gasFee';
import { TransactionModalContextProvider } from '../../contexts/transaction-modal';
import { useTokenTracker } from '../../hooks/useTokenTracker';
import {
getTokens,
getNativeCurrency,
isAddressLedger,
} from '../../ducks/metamask/metamask';
import {
transactionFeeSelector,
txDataSelector,
getCurrentCurrency,
getSubjectMetadata,
getUseNonceField,
@ -38,12 +29,11 @@ import {
getEIP1559V2Enabled,
} from '../../selectors';
import { useApproveTransaction } from '../../hooks/useApproveTransaction';
import { currentNetworkTxListSelector } from '../../selectors/transactions';
import AdvancedGasFeePopover from '../../components/app/advanced-gas-fee-popover';
import EditGasFeePopover from '../../components/app/edit-gas-fee-popover';
import EditGasPopover from '../../components/app/edit-gas-popover/edit-gas-popover.component';
import Loading from '../../components/ui/loading-screen';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
import { ERC20, ERC1155, ERC721 } from '../../helpers/constants/common';
import { getCustomTxParamsData } from './confirm-approve.util';
import ConfirmApproveContent from './confirm-approve-content';
@ -51,19 +41,28 @@ const isAddressLedgerByFromAddress = (address) => (state) => {
return isAddressLedger(state, address);
};
export default function ConfirmApprove() {
export default function ConfirmApprove({
assetStandard,
assetName,
userBalance,
tokenSymbol,
decimals,
tokenImage,
tokenAmount,
tokenId,
userAddress,
toAddress,
transaction,
ethTransactionTotal,
fiatTransactionTotal,
hexTransactionTotal,
}) {
const dispatch = useDispatch();
const { id: paramsTransactionId } = useParams();
const {
id: transactionId,
txParams: { to: tokenAddress, data, from } = {},
} = useSelector(txDataSelector);
const { txParams: { data: transactionData } = {} } = transaction;
const currentCurrency = useSelector(getCurrentCurrency);
const nativeCurrency = useSelector(getNativeCurrency);
const currentNetworkTxList = useSelector(currentNetworkTxListSelector);
const subjectMetadata = useSelector(getSubjectMetadata);
const tokens = useSelector(getTokens);
const useNonceField = useSelector(getUseNonceField);
const nextNonce = useSelector(getNextSuggestedNonce);
const customNonceValue = useSelector(getCustomNonceValue);
@ -73,45 +72,17 @@ export default function ConfirmApprove() {
const networkAndAccountSupports1559 = useSelector(
checkNetworkAndAccountSupports1559,
);
const fromAddressIsLedger = useSelector(isAddressLedgerByFromAddress(from));
const transaction =
currentNetworkTxList.find(
({ id }) => id === (Number(paramsTransactionId) || transactionId),
) || {};
const {
ethTransactionTotal,
fiatTransactionTotal,
hexTransactionTotal,
} = useSelector((state) => transactionFeeSelector(state, transaction));
const fromAddressIsLedger = useSelector(
isAddressLedgerByFromAddress(userAddress),
);
const [customPermissionAmount, setCustomPermissionAmount] = useState('');
const [submitWarning, setSubmitWarning] = useState('');
const [isContract, setIsContract] = useState(false);
const eip1559V2Enabled = useSelector(getEIP1559V2Enabled);
const supportsEIP1559V2 = eip1559V2Enabled && networkAndAccountSupports1559;
const currentToken = (tokens &&
tokens.find(({ address }) =>
isEqualCaseInsensitive(tokenAddress, address),
)) || {
address: tokenAddress,
};
const { tokensWithBalances } = useTokenTracker([currentToken]);
const tokenTrackerBalance = tokensWithBalances[0]?.balance || '';
const tokenSymbol = currentToken?.symbol;
const decimals = Number(currentToken?.decimals);
const tokenImage = currentToken?.image;
const tokenData = getTransactionData(data);
const tokenValue = getTokenValueParam(tokenData);
const toAddress = getTokenAddressParam(tokenData);
const tokenAmount =
tokenData && calcTokenAmount(tokenValue, decimals).toString(10);
const [customPermissionAmount, setCustomPermissionAmount] = useState('');
const previousTokenAmount = useRef(tokenAmount);
const {
approveTransaction,
showCustomizeGasPopover,
@ -125,7 +96,6 @@ export default function ConfirmApprove() {
previousTokenAmount.current = tokenAmount;
}, [customPermissionAmount, tokenAmount]);
const [submitWarning, setSubmitWarning] = useState('');
const prevNonce = useRef(nextNonce);
const prevCustomNonce = useRef(customNonceValue);
useEffect(() => {
@ -145,7 +115,6 @@ export default function ConfirmApprove() {
prevNonce.current = nextNonce;
}, [customNonceValue, nextNonce]);
const [isContract, setIsContract] = useState(false);
const checkIfContract = useCallback(async () => {
const { isContractAddress } = await readAddressAsContract(
global.eth,
@ -153,6 +122,7 @@ export default function ConfirmApprove() {
);
setIsContract(isContractAddress);
}, [setIsContract, toAddress]);
useEffect(() => {
checkIfContract();
}, [checkIfContract]);
@ -162,21 +132,30 @@ export default function ConfirmApprove() {
const { iconUrl: siteImage = '' } = subjectMetadata[origin] || {};
const tokensText = `${Number(tokenAmount)} ${tokenSymbol}`;
const tokenBalance = tokenTrackerBalance
? calcTokenAmount(tokenTrackerBalance, decimals).toString(10)
let tokensText;
if (assetStandard === ERC20) {
tokensText = `${Number(tokenAmount)} ${tokenSymbol}`;
} else if (assetStandard === ERC721 || assetStandard === ERC1155) {
tokensText = assetName;
}
const tokenBalance = userBalance
? calcTokenAmount(userBalance, decimals).toString(10)
: '';
const customData = customPermissionAmount
? getCustomTxParamsData(data, { customPermissionAmount, decimals })
? getCustomTxParamsData(transactionData, {
customPermissionAmount,
decimals,
})
: null;
return tokenSymbol === undefined ? (
return tokenSymbol === undefined && assetName === undefined ? (
<Loading />
) : (
<GasFeeContextProvider transaction={transaction}>
<ConfirmTransactionBase
toAddress={toAddress}
identiconAddress={tokenAddress}
identiconAddress={toAddress}
showAccountInHeader
title={tokensText}
contentComponent={
@ -191,6 +170,9 @@ export default function ConfirmApprove() {
tokenSymbol={tokenSymbol}
tokenImage={tokenImage}
tokenBalance={tokenBalance}
tokenId={tokenId}
assetName={assetName}
assetStandard={assetStandard}
showCustomizeGasModal={approveTransaction}
showEditApprovalPermissionModal={({
/* eslint-disable no-shadow */
@ -213,10 +195,12 @@ export default function ConfirmApprove() {
tokenAmount,
tokenBalance,
tokenSymbol,
tokenId,
assetStandard,
}),
)
}
data={customData || data}
data={customData || transactionData}
toAddress={toAddress}
currentCurrency={currentCurrency}
nativeCurrency={nativeCurrency}
@ -280,3 +264,27 @@ export default function ConfirmApprove() {
</GasFeeContextProvider>
);
}
ConfirmApprove.propTypes = {
assetStandard: PropTypes.string,
assetName: PropTypes.string,
userBalance: PropTypes.string,
tokenSymbol: PropTypes.string,
decimals: PropTypes.string,
tokenImage: PropTypes.string,
tokenAmount: PropTypes.string,
tokenId: PropTypes.string,
userAddress: PropTypes.string,
toAddress: PropTypes.string,
transaction: PropTypes.shape({
origin: PropTypes.string,
txParams: PropTypes.shape({
data: PropTypes.string,
to: PropTypes.string,
from: PropTypes.string,
}),
}),
ethTransactionTotal: PropTypes.string,
fiatTransactionTotal: PropTypes.string,
hexTransactionTotal: PropTypes.string,
};

View File

@ -0,0 +1,118 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import ConfirmTokenTransactionBase from '../confirm-token-transaction-base/confirm-token-transaction-base';
import { SEND_ROUTE } from '../../helpers/constants/routes';
import { ASSET_TYPES, editTransaction } from '../../ducks/send';
import {
contractExchangeRateSelector,
getCurrentCurrency,
} from '../../selectors';
import {
getConversionRate,
getNativeCurrency,
} from '../../ducks/metamask/metamask';
import { ERC20, ERC721 } from '../../helpers/constants/common';
import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck';
import { showSendTokenPage } from '../../store/actions';
export default function ConfirmSendToken({
assetStandard,
toAddress,
tokenAddress,
assetName,
tokenSymbol,
tokenAmount,
tokenId,
transaction,
image,
ethTransactionTotal,
fiatTransactionTotal,
hexMaximumTransactionFee,
}) {
const dispatch = useDispatch();
const history = useHistory();
const handleEditTransaction = ({
txData,
tokenData,
tokenProps: assetDetails,
}) => {
const { id } = txData;
dispatch(
editTransaction(
ASSET_TYPES.TOKEN,
id.toString(),
tokenData,
assetDetails,
),
);
dispatch(clearConfirmTransaction());
dispatch(showSendTokenPage());
};
const handleEdit = (confirmTransactionData) => {
handleEditTransaction(confirmTransactionData);
history.push(SEND_ROUTE);
};
const conversionRate = useSelector(getConversionRate);
const nativeCurrency = useSelector(getNativeCurrency);
const currentCurrency = useSelector(getCurrentCurrency);
const contractExchangeRate = useSelector(contractExchangeRateSelector);
let title, subtitle;
if (assetStandard === ERC721) {
title = assetName;
subtitle = `#${tokenId}`;
} else if (assetStandard === ERC20) {
title = `${tokenAmount} ${tokenSymbol}`;
}
return (
<ConfirmTokenTransactionBase
onEdit={handleEdit}
conversionRate={conversionRate}
currentCurrency={currentCurrency}
nativeCurrency={nativeCurrency}
contractExchangeRate={contractExchangeRate}
title={title}
subtitle={subtitle}
assetStandard={assetStandard}
assetName={assetName}
tokenSymbol={tokenSymbol}
tokenAmount={tokenAmount}
tokenId={tokenId}
transaction={transaction}
image={image}
toAddress={toAddress}
tokenAddress={tokenAddress}
ethTransactionTotal={ethTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal}
hexMaximumTransactionFee={hexMaximumTransactionFee}
/>
);
}
ConfirmSendToken.propTypes = {
tokenAmount: PropTypes.string,
assetStandard: PropTypes.string,
assetName: PropTypes.string,
tokenSymbol: PropTypes.string,
image: PropTypes.string,
tokenId: PropTypes.string,
toAddress: PropTypes.string,
tokenAddress: PropTypes.string,
transaction: PropTypes.shape({
origin: PropTypes.string,
txParams: PropTypes.shape({
data: PropTypes.string,
to: PropTypes.string,
from: PropTypes.string,
}),
}),
ethTransactionTotal: PropTypes.string,
fiatTransactionTotal: PropTypes.string,
hexMaximumTransactionFee: PropTypes.string,
};

View File

@ -1 +1 @@
export { default } from './confirm-send-token.container';
export { default } from './confirm-send-token';

View File

@ -1,112 +0,0 @@
import { connect } from 'react-redux';
import { compose } from 'redux';
import { withRouter } from 'react-router-dom';
import {
contractExchangeRateSelector,
transactionFeeSelector,
} from '../../selectors';
import { getCollectibles, getTokens } from '../../ducks/metamask/metamask';
import { getTransactionData } from '../../helpers/utils/transactions.util';
import {
calcTokenAmount,
getTokenAddressParam,
getTokenValueParam,
} from '../../helpers/utils/token-util';
import { hexWEIToDecETH } from '../../helpers/utils/conversions.util';
import { isEqualCaseInsensitive } from '../../../shared/modules/string-utils';
import ConfirmTokenTransactionBase from './confirm-token-transaction-base.component';
const mapStateToProps = (state, ownProps) => {
const {
match: { params = {} },
} = ownProps;
const { id: paramsTransactionId } = params;
const {
confirmTransaction,
metamask: {
currentCurrency,
conversionRate,
currentNetworkTxList,
nativeCurrency,
},
} = state;
const {
txData: {
id: transactionId,
txParams: { to: tokenAddress, data } = {},
} = {},
} = confirmTransaction;
const transaction =
currentNetworkTxList.find(
({ id }) => id === (Number(paramsTransactionId) || transactionId),
) || {};
const {
ethTransactionTotal,
fiatTransactionTotal,
hexMaximumTransactionFee,
} = transactionFeeSelector(state, transaction);
const tokens = getTokens(state);
const collectibles = getCollectibles(state);
const transactionData = getTransactionData(data);
const toAddress = getTokenAddressParam(transactionData);
const tokenAmountOrTokenId = getTokenValueParam(transactionData);
const ethTransactionTotalMaxAmount = Number(
hexWEIToDecETH(hexMaximumTransactionFee),
).toFixed(6);
const currentToken = tokens?.find(({ address }) =>
isEqualCaseInsensitive(tokenAddress, address),
);
const currentCollectible = collectibles?.find(
({ address, tokenId }) =>
isEqualCaseInsensitive(tokenAddress, address) &&
tokenId === tokenAmountOrTokenId,
);
let image,
tokenId,
collectibleName,
tokenAmount,
contractExchangeRate,
title,
subtitle;
if (currentCollectible) {
({ image, tokenId, name: collectibleName } = currentCollectible || {});
title = collectibleName;
subtitle = `#${tokenId}`;
} else if (currentToken) {
const { decimals, symbol: tokenSymbol } = currentToken || {};
tokenAmount =
transactionData &&
calcTokenAmount(tokenAmountOrTokenId, decimals).toFixed();
contractExchangeRate = contractExchangeRateSelector(state);
title = `${tokenAmount} ${tokenSymbol}`;
}
return {
title,
subtitle,
image,
toAddress,
tokenAddress,
tokenAmount,
currentCurrency,
conversionRate,
contractExchangeRate,
fiatTransactionTotal,
ethTransactionTotal,
ethTransactionTotalMaxAmount,
nativeCurrency,
};
};
export default compose(
withRouter,
connect(mapStateToProps),
)(ConfirmTokenTransactionBase);

View File

@ -1,6 +1,7 @@
import React, { useContext, useMemo } from 'react';
import PropTypes from 'prop-types';
import BigNumber from 'bignumber.js';
import { useSelector } from 'react-redux';
import { I18nContext } from '../../contexts/i18n';
import ConfirmTransactionBase from '../confirm-transaction-base';
import UserPreferencedCurrencyDisplay from '../../components/app/user-preferenced-currency-display';
@ -10,26 +11,57 @@ import {
addFiat,
roundExponential,
} from '../../helpers/utils/confirm-tx.util';
import { getWeiHexFromDecimalValue } from '../../helpers/utils/conversions.util';
import { ETH, PRIMARY } from '../../helpers/constants/common';
import {
getWeiHexFromDecimalValue,
hexWEIToDecETH,
} from '../../helpers/utils/conversions.util';
import {
ERC1155,
ERC20,
ERC721,
ETH,
PRIMARY,
} from '../../helpers/constants/common';
import {
contractExchangeRateSelector,
getCurrentCurrency,
} from '../../selectors';
import {
getConversionRate,
getNativeCurrency,
} from '../../ducks/metamask/metamask';
export default function ConfirmTokenTransactionBase({
image,
title,
subtitle,
image = '',
assetName,
toAddress,
tokenAddress,
tokenAmount = '0',
fiatTransactionTotal,
ethTransactionTotal,
ethTransactionTotalMaxAmount,
contractExchangeRate,
conversionRate,
currentCurrency,
nativeCurrency,
tokenSymbol,
tokenId,
assetStandard,
onEdit,
ethTransactionTotal,
fiatTransactionTotal,
hexMaximumTransactionFee,
}) {
const t = useContext(I18nContext);
const contractExchangeRate = useSelector(contractExchangeRateSelector);
const nativeCurrency = useSelector(getNativeCurrency);
const currentCurrency = useSelector(getCurrentCurrency);
const conversionRate = useSelector(getConversionRate);
const ethTransactionTotalMaxAmount = Number(
hexWEIToDecETH(hexMaximumTransactionFee),
);
let title, subtitle;
if (assetStandard === ERC721 || assetStandard === ERC1155) {
title = assetName;
subtitle = `#${tokenId}`;
} else if (assetStandard === ERC20) {
title = `${tokenAmount} ${tokenSymbol}`;
}
const hexWeiValue = useMemo(() => {
if (tokenAmount === '0' || !contractExchangeRate) {
@ -105,17 +137,15 @@ export default function ConfirmTokenTransactionBase({
ConfirmTokenTransactionBase.propTypes = {
image: PropTypes.string,
title: PropTypes.string,
subtitle: PropTypes.string,
tokenAddress: PropTypes.string,
assetName: PropTypes.string,
toAddress: PropTypes.string,
tokenAddress: PropTypes.string,
tokenAmount: PropTypes.string,
fiatTransactionTotal: PropTypes.string,
ethTransactionTotal: PropTypes.string,
contractExchangeRate: PropTypes.number,
conversionRate: PropTypes.number,
currentCurrency: PropTypes.string,
tokenSymbol: PropTypes.string,
tokenId: PropTypes.string,
assetStandard: PropTypes.string,
onEdit: PropTypes.func,
nativeCurrency: PropTypes.string,
ethTransactionTotalMaxAmount: PropTypes.string,
ethTransactionTotal: PropTypes.string,
fiatTransactionTotal: PropTypes.string,
hexMaximumTransactionFee: PropTypes.string,
};

View File

@ -1,6 +1,6 @@
import React from 'react';
import { store } from '../../../.storybook/preview';
import ConfirmTokenTransactionBase from './confirm-token-transaction-base.component';
import ConfirmTokenTransactionBase from './confirm-token-transaction-base';
export default {
title: 'Pages/ConfirmTokenTransactionBase',

View File

@ -1,2 +1 @@
export { default } from './confirm-token-transaction-base.container';
export { default as ConfirmTokenTransactionBase } from './confirm-token-transaction-base.component';
export { default } from './confirm-token-transaction-base';

View File

@ -174,7 +174,7 @@ const mapStateToProps = (state, ownProps) => {
}
const isCollectibleTransfer = Boolean(
allCollectibleContracts?.[selectedAddress]?.[chainId].find((contract) => {
allCollectibleContracts?.[selectedAddress]?.[chainId]?.find((contract) => {
return isEqualCaseInsensitive(contract.address, fullTxData.txParams.to);
}),
);

View File

@ -0,0 +1,125 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { Switch, Route } from 'react-router-dom';
import {
CONFIRM_APPROVE_PATH,
CONFIRM_SEND_TOKEN_PATH,
CONFIRM_TRANSACTION_ROUTE,
CONFIRM_TRANSFER_FROM_PATH,
} from '../../helpers/constants/routes';
import { transactionFeeSelector } from '../../selectors';
import ConfirmApprove from '../confirm-approve';
import ConfirmSendToken from '../confirm-send-token';
import ConfirmTokenTransactionBase from '../confirm-token-transaction-base';
import ConfirmTransactionSwitch from '../confirm-transaction-switch';
import { useAssetDetails } from '../../hooks/useAssetDetails';
export default function ConfirmTokenTransactionSwitch({ transaction }) {
const {
txParams: { data, to: tokenAddress, from: userAddress } = {},
} = transaction;
const {
assetStandard,
assetName,
userBalance,
tokenSymbol,
decimals,
tokenImage,
tokenAmount,
tokenId,
toAddress,
} = useAssetDetails(tokenAddress, userAddress, data);
const {
ethTransactionTotal,
fiatTransactionTotal,
hexTransactionTotal,
hexMaximumTransactionFee,
} = useSelector((state) => transactionFeeSelector(state, transaction));
return (
<Switch>
<Route
exact
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_APPROVE_PATH}`}
render={() => (
<ConfirmApprove
assetStandard={assetStandard}
assetName={assetName}
userBalance={userBalance}
tokenSymbol={tokenSymbol}
decimals={decimals}
tokenImage={tokenImage}
tokenAmount={tokenAmount}
tokenId={tokenId}
userAddress={userAddress}
tokenAddress={tokenAddress}
toAddress={toAddress}
transaction={transaction}
ethTransactionTotal={ethTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal}
hexTransactionTotal={hexTransactionTotal}
/>
)}
/>
<Route
exact
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_TRANSFER_FROM_PATH}`}
render={() => (
<ConfirmTokenTransactionBase
assetStandard={assetStandard}
assetName={assetName}
userBalance={userBalance}
tokenSymbol={tokenSymbol}
decimals={decimals}
image={tokenImage}
tokenAddress={tokenAddress}
toAddress={toAddress}
tokenAmount={tokenAmount}
tokenId={tokenId}
userAddress={userAddress}
transaction={transaction}
ethTransactionTotal={ethTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal}
hexTransactionTotal={hexTransactionTotal}
/>
)}
/>
<Route
exact
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_SEND_TOKEN_PATH}`}
render={() => (
<ConfirmSendToken
assetStandard={assetStandard}
assetName={assetName}
tokenSymbol={tokenSymbol}
image={tokenImage}
tokenAddress={tokenAddress}
toAddress={toAddress}
tokenAmount={tokenAmount}
tokenId={tokenId}
transaction={transaction}
ethTransactionTotal={ethTransactionTotal}
fiatTransactionTotal={fiatTransactionTotal}
hexMaximumTransactionFee={hexMaximumTransactionFee}
/>
)}
/>
<Route path="*" component={ConfirmTransactionSwitch} />
</Switch>
);
}
ConfirmTokenTransactionSwitch.propTypes = {
transaction: PropTypes.shape({
origin: PropTypes.string,
txParams: PropTypes.shape({
data: PropTypes.string,
to: PropTypes.string,
from: PropTypes.string,
}),
}),
};

View File

@ -5,10 +5,7 @@ import Loading from '../../components/ui/loading-screen';
import ConfirmTransactionSwitch from '../confirm-transaction-switch';
import ConfirmTransactionBase from '../confirm-transaction-base';
import ConfirmSendEther from '../confirm-send-ether';
import ConfirmSendToken from '../confirm-send-token';
import ConfirmDeployContract from '../confirm-deploy-contract';
import ConfirmApprove from '../confirm-approve';
import ConfirmTokenTransactionBaseContainer from '../confirm-token-transaction-base';
import ConfirmDecryptMessage from '../confirm-decrypt-message';
import ConfirmEncryptionPublicKey from '../confirm-encryption-public-key';
@ -16,9 +13,6 @@ import {
CONFIRM_TRANSACTION_ROUTE,
CONFIRM_DEPLOY_CONTRACT_PATH,
CONFIRM_SEND_ETHER_PATH,
CONFIRM_SEND_TOKEN_PATH,
CONFIRM_APPROVE_PATH,
CONFIRM_TRANSFER_FROM_PATH,
CONFIRM_TOKEN_METHOD_PATH,
SIGNATURE_REQUEST_PATH,
DECRYPT_MESSAGE_REQUEST_PATH,
@ -31,6 +25,7 @@ import {
addPollingTokenToAppState,
removePollingTokenFromAppState,
} from '../../store/actions';
import ConfirmTokenTransactionSwitch from './confirm-token-transaction-switch';
import ConfTx from './conf-tx';
export default class ConfirmTransaction extends Component {
@ -49,7 +44,6 @@ export default class ConfirmTransaction extends Component {
getContractMethodData: PropTypes.func,
transactionId: PropTypes.string,
paramsTransactionId: PropTypes.string,
getTokenParams: PropTypes.func,
isTokenMethodAction: PropTypes.bool,
setDefaultHomeActiveTabName: PropTypes.func,
};
@ -74,12 +68,10 @@ export default class ConfirmTransaction extends Component {
sendTo,
history,
mostRecentOverviewPage,
transaction: { txParams: { data, to } = {} } = {},
transaction: { txParams: { data } = {} } = {},
getContractMethodData,
transactionId,
paramsTransactionId,
getTokenParams,
isTokenMethodAction,
} = this.props;
getGasFeeEstimatesAndStartPolling().then((pollingToken) => {
@ -100,9 +92,7 @@ export default class ConfirmTransaction extends Component {
}
getContractMethodData(data);
if (isTokenMethodAction) {
getTokenParams(to);
}
const txId = transactionId || paramsTransactionId;
if (txId) {
this.props.setTransactionToConfirm(txId);
@ -154,23 +144,30 @@ export default class ConfirmTransaction extends Component {
}
render() {
const { transactionId, paramsTransactionId } = this.props;
const {
transactionId,
paramsTransactionId,
isTokenMethodAction,
transaction,
} = this.props;
const validTransactionId =
transactionId &&
(!paramsTransactionId || paramsTransactionId === transactionId);
if (isTokenMethodAction && validTransactionId) {
return <ConfirmTokenTransactionSwitch transaction={transaction} />;
}
// Show routes when state.confirmTransaction has been set and when either the ID in the params
// isn't specified or is specified and matches the ID in state.confirmTransaction in order to
// support URLs of /confirm-transaction or /confirm-transaction/<transactionId>
return transactionId &&
(!paramsTransactionId || paramsTransactionId === transactionId) ? (
return validTransactionId ? (
<Switch>
<Route
exact
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_DEPLOY_CONTRACT_PATH}`}
component={ConfirmDeployContract}
/>
<Route
exact
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_TOKEN_METHOD_PATH}`}
component={ConfirmTransactionBase}
/>
<Route
exact
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_SEND_ETHER_PATH}`}
@ -178,18 +175,8 @@ export default class ConfirmTransaction extends Component {
/>
<Route
exact
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_SEND_TOKEN_PATH}`}
component={ConfirmSendToken}
/>
<Route
exact
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_APPROVE_PATH}`}
component={ConfirmApprove}
/>
<Route
exact
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_TRANSFER_FROM_PATH}`}
component={ConfirmTokenTransactionBaseContainer}
path={`${CONFIRM_TRANSACTION_ROUTE}/:id?${CONFIRM_TOKEN_METHOD_PATH}`}
component={ConfirmTransactionBase}
/>
<Route
exact

View File

@ -9,7 +9,6 @@ import { isTokenMethodAction } from '../../helpers/utils/transactions.util';
import {
getContractMethodData,
getTokenParams,
setDefaultHomeActiveTabName,
} from '../../store/actions';
import { unconfirmedTransactionsListSelector } from '../../selectors';
@ -54,7 +53,6 @@ const mapDispatchToProps = (dispatch) => {
},
clearConfirmTransaction: () => dispatch(clearConfirmTransaction()),
getContractMethodData: (data) => dispatch(getContractMethodData(data)),
getTokenParams: (tokenAddress) => dispatch(getTokenParams(tokenAddress)),
setDefaultHomeActiveTabName: (tabName) =>
dispatch(setDefaultHomeActiveTabName(tabName)),
};

View File

@ -8,7 +8,6 @@ import {
loadRelativeTimeFormatLocaleData,
} from '../helpers/utils/i18n-helper';
import { getMethodDataAsync } from '../helpers/utils/transactions.util';
import { getSymbolAndDecimals } from '../helpers/utils/token-util';
import switchDirection from '../helpers/utils/switch-direction';
import {
ENVIRONMENT_TYPE_NOTIFICATION,
@ -22,7 +21,6 @@ import {
getMetaMaskAccounts,
getPermittedAccountsForCurrentTab,
getSelectedAddress,
getTokenList,
} from '../selectors';
import { computeEstimatedGasLimit, resetSendState } from '../ducks/send';
import { switchedToUnconnectedAccount } from '../ducks/alerts/unconnected-account';
@ -2871,46 +2869,6 @@ export function loadingTokenParamsFinished() {
};
}
export function getTokenParams(address) {
return (dispatch, getState) => {
const tokenList = getTokenList(getState());
const existingTokens = getState().metamask.tokens;
const { selectedAddress } = getState().metamask;
const { chainId } = getState().metamask.provider;
const existingCollectibles = getState().metamask?.allCollectibles?.[
selectedAddress
]?.[chainId];
const existingToken = existingTokens.find(({ address: tokenAddress }) =>
isEqualCaseInsensitive(address, tokenAddress),
);
const existingCollectible = existingCollectibles?.find(
({ address: collectibleAddress }) =>
isEqualCaseInsensitive(address, collectibleAddress),
);
if (existingCollectible) {
return null;
}
if (existingToken) {
return Promise.resolve({
symbol: existingToken.symbol,
decimals: existingToken.decimals,
});
}
dispatch(loadingTokenParamsStarted());
log.debug(`loadingTokenParams`);
return getSymbolAndDecimals(address, tokenList).then(
({ symbol, decimals }) => {
dispatch(addToken(address, symbol, Number(decimals)));
dispatch(loadingTokenParamsFinished());
},
);
};
}
export function setSeedPhraseBackedUp(seedPhraseBackupState) {
return (dispatch) => {
log.debug(`background.setSeedPhraseBackedUp`);