mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-22 09:57:02 +01:00
Unable to proceed with tx bc of inaccurate/overly aggressive "Insufficient funds for gas" warning #13087 (#14634)
Co-authored-by: VSaric <vladimir.saric@consensys.net>
This commit is contained in:
parent
f44af06f9b
commit
2680340e27
7
app/_locales/en/messages.json
generated
7
app/_locales/en/messages.json
generated
@ -978,6 +978,9 @@
|
|||||||
"deleteNetworkDescription": {
|
"deleteNetworkDescription": {
|
||||||
"message": "Are you sure you want to delete this network?"
|
"message": "Are you sure you want to delete this network?"
|
||||||
},
|
},
|
||||||
|
"deposit": {
|
||||||
|
"message": "Deposit"
|
||||||
|
},
|
||||||
"depositCrypto": {
|
"depositCrypto": {
|
||||||
"message": "Deposit $1",
|
"message": "Deposit $1",
|
||||||
"description": "$1 represents the crypto symbol to be purchased"
|
"description": "$1 represents the crypto symbol to be purchased"
|
||||||
@ -1756,6 +1759,10 @@
|
|||||||
"message": "You do not have enough $1 in your account to pay for transaction fees on $2 network. $3 or deposit from another account.",
|
"message": "You do not have enough $1 in your account to pay for transaction fees on $2 network. $3 or deposit from another account.",
|
||||||
"description": "$1 is the native currency of the network, $2 is the name of the current network, $3 is the key 'buy' + the ticker symbol of the native currency of the chain wrapped in a button"
|
"description": "$1 is the native currency of the network, $2 is the name of the current network, $3 is the key 'buy' + the ticker symbol of the native currency of the chain wrapped in a button"
|
||||||
},
|
},
|
||||||
|
"insufficientCurrencyBuyOrReceive": {
|
||||||
|
"message": "You do not have enough $1 in your account to pay for transaction fees on $2 network. $3 or $4 from another account.",
|
||||||
|
"description": "$1 is the native currency of the network, $2 is the name of the current network, $3 is the key 'buy' + the ticker symbol of the native currency of the chain wrapped in a button, $4 is the key 'deposit' button"
|
||||||
|
},
|
||||||
"insufficientCurrencyDeposit": {
|
"insufficientCurrencyDeposit": {
|
||||||
"message": "You do not have enough $1 in your account to pay for transaction fees on $2 network. Deposit $1 from another account.",
|
"message": "You do not have enough $1 in your account to pay for transaction fees on $2 network. Deposit $1 from another account.",
|
||||||
"description": "$1 is the native currency of the network, $2 is the name of the current network"
|
"description": "$1 is the native currency of the network, $2 is the name of the current network"
|
||||||
|
@ -37,8 +37,8 @@ describe('Send ETH from inside MetaMask using default gas', function () {
|
|||||||
const errorAmount = await driver.findElement('.send-v2__error-amount');
|
const errorAmount = await driver.findElement('.send-v2__error-amount');
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await errorAmount.getText(),
|
await errorAmount.getText(),
|
||||||
'Insufficient funds.',
|
'Insufficient funds for gas',
|
||||||
'send screen should render an insufficient fund error message',
|
'send screen should render an insufficient fund for gas error message',
|
||||||
);
|
);
|
||||||
|
|
||||||
await inputAmount.press(driver.Key.BACK_SPACE);
|
await inputAmount.press(driver.Key.BACK_SPACE);
|
||||||
|
@ -14,6 +14,7 @@ import {
|
|||||||
getGasFeeEstimates,
|
getGasFeeEstimates,
|
||||||
getIsGasEstimatesLoading,
|
getIsGasEstimatesLoading,
|
||||||
} from '../../../ducks/metamask/metamask';
|
} from '../../../ducks/metamask/metamask';
|
||||||
|
import { getEIP1559V2Enabled } from '../../../selectors';
|
||||||
|
|
||||||
import Typography from '../../ui/typography/typography';
|
import Typography from '../../ui/typography/typography';
|
||||||
import {
|
import {
|
||||||
@ -45,6 +46,7 @@ export default function GasTiming({
|
|||||||
const gasEstimateType = useSelector(getGasEstimateType);
|
const gasEstimateType = useSelector(getGasEstimateType);
|
||||||
const gasFeeEstimates = useSelector(getGasFeeEstimates);
|
const gasFeeEstimates = useSelector(getGasFeeEstimates);
|
||||||
const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading);
|
const isGasEstimatesLoading = useSelector(getIsGasEstimatesLoading);
|
||||||
|
const eip1559V2Enabled = useSelector(getEIP1559V2Enabled);
|
||||||
|
|
||||||
const [customEstimatedTime, setCustomEstimatedTime] = useState(null);
|
const [customEstimatedTime, setCustomEstimatedTime] = useState(null);
|
||||||
const t = useContext(I18nContext);
|
const t = useContext(I18nContext);
|
||||||
@ -195,8 +197,10 @@ export default function GasTiming({
|
|||||||
<Typography
|
<Typography
|
||||||
variant={TYPOGRAPHY.H7}
|
variant={TYPOGRAPHY.H7}
|
||||||
className={classNames('gas-timing', {
|
className={classNames('gas-timing', {
|
||||||
[`gas-timing--${attitude}`]: attitude && !supportsEIP1559V2,
|
[`gas-timing--${attitude}`]:
|
||||||
[`gas-timing--${attitude}-V2`]: attitude && supportsEIP1559V2,
|
attitude && (eip1559V2Enabled || !supportsEIP1559V2),
|
||||||
|
[`gas-timing--${attitude}-V2`]:
|
||||||
|
attitude && (eip1559V2Enabled || supportsEIP1559V2),
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{text}
|
{text}
|
||||||
|
@ -13,6 +13,7 @@ import { GAS_ESTIMATE_TYPES, GAS_LIMITS } from '../../../shared/constants/gas';
|
|||||||
import {
|
import {
|
||||||
CONTRACT_ADDRESS_ERROR,
|
CONTRACT_ADDRESS_ERROR,
|
||||||
INSUFFICIENT_FUNDS_ERROR,
|
INSUFFICIENT_FUNDS_ERROR,
|
||||||
|
INSUFFICIENT_FUNDS_FOR_GAS_ERROR,
|
||||||
INSUFFICIENT_TOKENS_ERROR,
|
INSUFFICIENT_TOKENS_ERROR,
|
||||||
INVALID_RECIPIENT_ADDRESS_ERROR,
|
INVALID_RECIPIENT_ADDRESS_ERROR,
|
||||||
INVALID_RECIPIENT_ADDRESS_NOT_ETH_NETWORK_ERROR,
|
INVALID_RECIPIENT_ADDRESS_NOT_ETH_NETWORK_ERROR,
|
||||||
@ -1277,7 +1278,7 @@ const slice = createSlice({
|
|||||||
const draftTransaction =
|
const draftTransaction =
|
||||||
state.draftTransactions[state.currentTransactionUUID];
|
state.draftTransactions[state.currentTransactionUUID];
|
||||||
switch (true) {
|
switch (true) {
|
||||||
// set error to INSUFFICIENT_FUNDS_ERROR if the account balance is lower
|
// set error to INSUFFICIENT_FUNDS_FOR_GAS_ERROR if the account balance is lower
|
||||||
// than the total price of the transaction inclusive of gas fees.
|
// than the total price of the transaction inclusive of gas fees.
|
||||||
case draftTransaction.asset.type === ASSET_TYPES.NATIVE &&
|
case draftTransaction.asset.type === ASSET_TYPES.NATIVE &&
|
||||||
!isBalanceSufficient({
|
!isBalanceSufficient({
|
||||||
@ -1285,9 +1286,9 @@ const slice = createSlice({
|
|||||||
balance: draftTransaction.asset.balance,
|
balance: draftTransaction.asset.balance,
|
||||||
gasTotal: draftTransaction.gas.gasTotal ?? '0x0',
|
gasTotal: draftTransaction.gas.gasTotal ?? '0x0',
|
||||||
}):
|
}):
|
||||||
draftTransaction.amount.error = INSUFFICIENT_FUNDS_ERROR;
|
draftTransaction.amount.error = INSUFFICIENT_FUNDS_FOR_GAS_ERROR;
|
||||||
break;
|
break;
|
||||||
// set error to INSUFFICIENT_FUNDS_ERROR if the token balance is lower
|
// set error to INSUFFICIENT_TOKENS_ERROR if the token balance is lower
|
||||||
// than the amount of token the user is attempting to send.
|
// than the amount of token the user is attempting to send.
|
||||||
case draftTransaction.asset.type === ASSET_TYPES.TOKEN &&
|
case draftTransaction.asset.type === ASSET_TYPES.TOKEN &&
|
||||||
!isTokenBalanceSufficient({
|
!isTokenBalanceSufficient({
|
||||||
|
@ -5,6 +5,7 @@ import { ethers } from 'ethers';
|
|||||||
import {
|
import {
|
||||||
CONTRACT_ADDRESS_ERROR,
|
CONTRACT_ADDRESS_ERROR,
|
||||||
INSUFFICIENT_FUNDS_ERROR,
|
INSUFFICIENT_FUNDS_ERROR,
|
||||||
|
INSUFFICIENT_FUNDS_FOR_GAS_ERROR,
|
||||||
INSUFFICIENT_TOKENS_ERROR,
|
INSUFFICIENT_TOKENS_ERROR,
|
||||||
INVALID_RECIPIENT_ADDRESS_ERROR,
|
INVALID_RECIPIENT_ADDRESS_ERROR,
|
||||||
KNOWN_RECIPIENT_ADDRESS_WARNING,
|
KNOWN_RECIPIENT_ADDRESS_WARNING,
|
||||||
@ -856,7 +857,7 @@ describe('Send Slice', () => {
|
|||||||
const draftTransaction = getTestUUIDTx(result);
|
const draftTransaction = getTestUUIDTx(result);
|
||||||
|
|
||||||
expect(draftTransaction.amount.error).toStrictEqual(
|
expect(draftTransaction.amount.error).toStrictEqual(
|
||||||
INSUFFICIENT_FUNDS_ERROR,
|
INSUFFICIENT_FUNDS_FOR_GAS_ERROR,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
@import 'create-account/index';
|
@import 'create-account/index';
|
||||||
@import 'error/index';
|
@import 'error/index';
|
||||||
@import 'first-time-flow/index';
|
@import 'first-time-flow/index';
|
||||||
|
@import 'send/gas-display/index';
|
||||||
@import 'home/index';
|
@import 'home/index';
|
||||||
@import 'keychains/index';
|
@import 'keychains/index';
|
||||||
@import 'permissions-connect/index';
|
@import 'permissions-connect/index';
|
||||||
|
500
ui/pages/send/gas-display/gas-display.js
Normal file
500
ui/pages/send/gas-display/gas-display.js
Normal file
@ -0,0 +1,500 @@
|
|||||||
|
import React, { useContext, useState } from 'react';
|
||||||
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { I18nContext } from '../../../contexts/i18n';
|
||||||
|
import { useGasFeeContext } from '../../../contexts/gasFee';
|
||||||
|
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
|
||||||
|
import { isLegacyTransaction } from '../../../helpers/utils/transactions.util';
|
||||||
|
import { hexWEIToDecGWEI } from '../../../../shared/lib/transactions-controller-utils';
|
||||||
|
import UserPreferencedCurrencyDisplay from '../../../components/app/user-preferenced-currency-display';
|
||||||
|
import GasTiming from '../../../components/app/gas-timing';
|
||||||
|
import InfoTooltip from '../../../components/ui/info-tooltip';
|
||||||
|
import Typography from '../../../components/ui/typography';
|
||||||
|
import Button from '../../../components/ui/button';
|
||||||
|
import Box from '../../../components/ui/box';
|
||||||
|
import {
|
||||||
|
TYPOGRAPHY,
|
||||||
|
DISPLAY,
|
||||||
|
FLEX_DIRECTION,
|
||||||
|
BLOCK_SIZES,
|
||||||
|
COLORS,
|
||||||
|
FONT_STYLE,
|
||||||
|
FONT_WEIGHT,
|
||||||
|
} from '../../../helpers/constants/design-system';
|
||||||
|
import {
|
||||||
|
ERC1155,
|
||||||
|
ERC20,
|
||||||
|
ERC721,
|
||||||
|
} from '../../../../shared/constants/transaction';
|
||||||
|
import LoadingHeartBeat from '../../../components/ui/loading-heartbeat';
|
||||||
|
import TransactionDetailItem from '../../../components/app/transaction-detail-item';
|
||||||
|
import { NETWORK_TO_NAME_MAP } from '../../../../shared/constants/network';
|
||||||
|
import TransactionDetail from '../../../components/app/transaction-detail';
|
||||||
|
import ActionableMessage from '../../../components/ui/actionable-message';
|
||||||
|
import DepositPopover from '../../../components/app/deposit-popover';
|
||||||
|
import {
|
||||||
|
getProvider,
|
||||||
|
getPreferences,
|
||||||
|
getIsBuyableChain,
|
||||||
|
transactionFeeSelector,
|
||||||
|
getIsMainnet,
|
||||||
|
getEIP1559V2Enabled,
|
||||||
|
checkNetworkAndAccountSupports1559,
|
||||||
|
} from '../../../selectors';
|
||||||
|
|
||||||
|
import {
|
||||||
|
hexWEIToDecETH,
|
||||||
|
addHexes,
|
||||||
|
} from '../../../helpers/utils/conversions.util';
|
||||||
|
import { INSUFFICIENT_TOKENS_ERROR } from '../send.constants';
|
||||||
|
import { getCurrentDraftTransaction } from '../../../ducks/send';
|
||||||
|
import { showModal } from '../../../store/actions';
|
||||||
|
|
||||||
|
export default function GasDisplay({ gasError }) {
|
||||||
|
const t = useContext(I18nContext);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { estimateUsed } = useGasFeeContext();
|
||||||
|
const [showDepositPopover, setShowDepositPopover] = useState(false);
|
||||||
|
const currentProvider = useSelector(getProvider);
|
||||||
|
const isMainnet = useSelector(getIsMainnet);
|
||||||
|
const isBuyableChain = useSelector(getIsBuyableChain);
|
||||||
|
const draftTransaction = useSelector(getCurrentDraftTransaction);
|
||||||
|
const eip1559V2Enabled = useSelector(getEIP1559V2Enabled);
|
||||||
|
const networkAndAccountSupports1559 = useSelector(
|
||||||
|
checkNetworkAndAccountSupports1559,
|
||||||
|
);
|
||||||
|
const { useNativeCurrencyAsPrimaryCurrency } = useSelector(getPreferences);
|
||||||
|
const { nativeCurrency, provider, unapprovedTxs } = useSelector(
|
||||||
|
(state) => state.metamask,
|
||||||
|
);
|
||||||
|
const { confirmTransaction } = useSelector((state) => state);
|
||||||
|
const { txData } = confirmTransaction;
|
||||||
|
const { txParams = {} } = txData;
|
||||||
|
const supportsEIP1559 =
|
||||||
|
networkAndAccountSupports1559 && !isLegacyTransaction(txParams);
|
||||||
|
const { chainId } = provider;
|
||||||
|
const networkName = NETWORK_TO_NAME_MAP[chainId];
|
||||||
|
const isInsufficientTokenError =
|
||||||
|
draftTransaction?.amount.error === INSUFFICIENT_TOKENS_ERROR;
|
||||||
|
const editingTransaction = unapprovedTxs[draftTransaction.id];
|
||||||
|
const supportsEIP1559V2 = eip1559V2Enabled && supportsEIP1559;
|
||||||
|
|
||||||
|
const transactionData = {
|
||||||
|
txParams: {
|
||||||
|
gasPrice: draftTransaction.gas?.gasPrice,
|
||||||
|
gas: editingTransaction?.userEditedGasLimit
|
||||||
|
? editingTransaction?.txParams?.gas
|
||||||
|
: draftTransaction.gas?.gasLimit,
|
||||||
|
maxFeePerGas: editingTransaction?.txParams?.maxFeePerGas
|
||||||
|
? editingTransaction?.txParams?.maxFeePerGas
|
||||||
|
: draftTransaction.gas?.maxFeePerGas,
|
||||||
|
maxPriorityFeePerGas: editingTransaction?.txParams?.maxPriorityFeePerGas
|
||||||
|
? editingTransaction?.txParams?.maxPriorityFeePerGas
|
||||||
|
: draftTransaction.gas?.maxPriorityFeePerGas,
|
||||||
|
value: draftTransaction.amount?.value,
|
||||||
|
type: draftTransaction.transactionType,
|
||||||
|
},
|
||||||
|
userFeeLevel: editingTransaction?.userFeeLevel,
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
hexMinimumTransactionFee,
|
||||||
|
hexMaximumTransactionFee,
|
||||||
|
hexTransactionTotal,
|
||||||
|
} = useSelector((state) => transactionFeeSelector(state, transactionData));
|
||||||
|
|
||||||
|
let title;
|
||||||
|
if (
|
||||||
|
draftTransaction?.asset.details?.standard === ERC721 ||
|
||||||
|
draftTransaction?.asset.details?.standard === ERC1155
|
||||||
|
) {
|
||||||
|
title = draftTransaction?.asset.details?.name;
|
||||||
|
} else if (draftTransaction?.asset.details?.standard === ERC20) {
|
||||||
|
title = `${hexWEIToDecETH(draftTransaction.amount.value)} ${
|
||||||
|
draftTransaction?.asset.details?.symbol
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ethTransactionTotalMaxAmount = Number(
|
||||||
|
hexWEIToDecETH(hexMaximumTransactionFee),
|
||||||
|
);
|
||||||
|
|
||||||
|
const primaryTotalTextOverrideMaxAmount = `${title} + ${ethTransactionTotalMaxAmount} ${nativeCurrency}`;
|
||||||
|
|
||||||
|
let detailTotal, maxAmount;
|
||||||
|
|
||||||
|
if (draftTransaction?.asset.type === 'NATIVE') {
|
||||||
|
detailTotal = (
|
||||||
|
<Box
|
||||||
|
height={BLOCK_SIZES.MAX}
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
className="gas-display__total-value"
|
||||||
|
>
|
||||||
|
<LoadingHeartBeat estimateUsed={transactionData?.userFeeLevel} />
|
||||||
|
<UserPreferencedCurrencyDisplay
|
||||||
|
type={PRIMARY}
|
||||||
|
key="total-detail-value"
|
||||||
|
value={hexTransactionTotal}
|
||||||
|
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
maxAmount = (
|
||||||
|
<UserPreferencedCurrencyDisplay
|
||||||
|
type={PRIMARY}
|
||||||
|
key="total-max-amount"
|
||||||
|
value={addHexes(
|
||||||
|
draftTransaction.amount.value,
|
||||||
|
hexMaximumTransactionFee,
|
||||||
|
)}
|
||||||
|
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (useNativeCurrencyAsPrimaryCurrency) {
|
||||||
|
detailTotal = primaryTotalTextOverrideMaxAmount;
|
||||||
|
maxAmount = primaryTotalTextOverrideMaxAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{showDepositPopover && (
|
||||||
|
<DepositPopover onClose={() => setShowDepositPopover(false)} />
|
||||||
|
)}
|
||||||
|
<Box className="gas-display">
|
||||||
|
<TransactionDetail
|
||||||
|
userAcknowledgedGasMissing={false}
|
||||||
|
rows={[
|
||||||
|
supportsEIP1559V2 ? (
|
||||||
|
<TransactionDetailItem
|
||||||
|
key="gas-item"
|
||||||
|
detailTitle={
|
||||||
|
<Box display={DISPLAY.FLEX}>
|
||||||
|
<Box marginRight={1}>{t('gas')}</Box>
|
||||||
|
<Typography
|
||||||
|
as="span"
|
||||||
|
marginTop={0}
|
||||||
|
color={COLORS.TEXT_MUTED}
|
||||||
|
fontStyle={FONT_STYLE.ITALIC}
|
||||||
|
fontWeight={FONT_WEIGHT.NORMAL}
|
||||||
|
className="gas-display__title__estimate"
|
||||||
|
>
|
||||||
|
({t('transactionDetailGasInfoV2')})
|
||||||
|
</Typography>
|
||||||
|
<InfoTooltip
|
||||||
|
contentText={
|
||||||
|
<>
|
||||||
|
<Typography variant={TYPOGRAPHY.H7}>
|
||||||
|
{t('transactionDetailGasTooltipIntro', [
|
||||||
|
isMainnet ? t('networkNameEthereum') : '',
|
||||||
|
])}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant={TYPOGRAPHY.H7}>
|
||||||
|
{t('transactionDetailGasTooltipExplanation')}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant={TYPOGRAPHY.H7}>
|
||||||
|
<a
|
||||||
|
href="https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{t('transactionDetailGasTooltipConversion')}
|
||||||
|
</a>
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
position="right"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
detailTitleColor={COLORS.TEXT_DEFAULT}
|
||||||
|
detailText={
|
||||||
|
<Box className="gas-display__currency-container">
|
||||||
|
<LoadingHeartBeat estimateUsed={estimateUsed} />
|
||||||
|
<UserPreferencedCurrencyDisplay
|
||||||
|
type={SECONDARY}
|
||||||
|
value={hexMinimumTransactionFee}
|
||||||
|
hideLabel={Boolean(useNativeCurrencyAsPrimaryCurrency)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
detailTotal={
|
||||||
|
<Box className="gas-display__currency-container">
|
||||||
|
<LoadingHeartBeat estimateUsed={estimateUsed} />
|
||||||
|
<UserPreferencedCurrencyDisplay
|
||||||
|
type={PRIMARY}
|
||||||
|
value={hexMinimumTransactionFee}
|
||||||
|
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
subText={
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
key="editGasSubTextFeeLabel"
|
||||||
|
display={DISPLAY.INLINE_FLEX}
|
||||||
|
className={classNames('gas-display__gas-fee-label', {
|
||||||
|
'gas-display__gas-fee-warning': estimateUsed === 'high',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<LoadingHeartBeat estimateUsed={estimateUsed} />
|
||||||
|
<Box marginRight={1}>
|
||||||
|
<strong>
|
||||||
|
{estimateUsed === 'high' && '⚠ '}
|
||||||
|
{t('editGasSubTextFeeLabel')}
|
||||||
|
</strong>
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
key="editGasSubTextFeeValue"
|
||||||
|
className="gas-display__currency-container"
|
||||||
|
>
|
||||||
|
<LoadingHeartBeat estimateUsed={estimateUsed} />
|
||||||
|
<UserPreferencedCurrencyDisplay
|
||||||
|
key="editGasSubTextFeeAmount"
|
||||||
|
type={PRIMARY}
|
||||||
|
value={hexMaximumTransactionFee}
|
||||||
|
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
subTitle={
|
||||||
|
<GasTiming
|
||||||
|
maxPriorityFeePerGas={hexWEIToDecGWEI(
|
||||||
|
draftTransaction.gas.maxPriorityFeePerGas,
|
||||||
|
)}
|
||||||
|
maxFeePerGas={hexWEIToDecGWEI(
|
||||||
|
draftTransaction.gas.maxFeePerGas,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<TransactionDetailItem
|
||||||
|
key="gas-item"
|
||||||
|
detailTitle={
|
||||||
|
<>
|
||||||
|
{t('transactionDetailGasHeading')}
|
||||||
|
<InfoTooltip
|
||||||
|
contentText={
|
||||||
|
<>
|
||||||
|
<p>
|
||||||
|
{t('transactionDetailGasTooltipIntro', [
|
||||||
|
isMainnet ? t('networkNameEthereum') : '',
|
||||||
|
])}
|
||||||
|
</p>
|
||||||
|
<p>{t('transactionDetailGasTooltipExplanation')}</p>
|
||||||
|
<p>
|
||||||
|
<a
|
||||||
|
href="https://community.metamask.io/t/what-is-gas-why-do-transactions-take-so-long/3172"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
{t('transactionDetailGasTooltipConversion')}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
position="right"
|
||||||
|
>
|
||||||
|
<i className="fa fa-info-circle" />
|
||||||
|
</InfoTooltip>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
detailText={
|
||||||
|
<Box
|
||||||
|
height={BLOCK_SIZES.MAX}
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
className="gas-display__currency-container"
|
||||||
|
>
|
||||||
|
<LoadingHeartBeat
|
||||||
|
estimateUsed={transactionData?.userFeeLevel}
|
||||||
|
/>
|
||||||
|
<UserPreferencedCurrencyDisplay
|
||||||
|
type={SECONDARY}
|
||||||
|
value={hexMinimumTransactionFee}
|
||||||
|
hideLabel={Boolean(useNativeCurrencyAsPrimaryCurrency)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
detailTotal={
|
||||||
|
<Box
|
||||||
|
height={BLOCK_SIZES.MAX}
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
className="gas-display__currency-container"
|
||||||
|
>
|
||||||
|
<LoadingHeartBeat
|
||||||
|
estimateUsed={transactionData?.userFeeLevel}
|
||||||
|
/>
|
||||||
|
<UserPreferencedCurrencyDisplay
|
||||||
|
type={PRIMARY}
|
||||||
|
value={hexMinimumTransactionFee}
|
||||||
|
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
||||||
|
numberOfDecimals={6}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
subText={
|
||||||
|
<>
|
||||||
|
<strong key="editGasSubTextFeeLabel">
|
||||||
|
{t('editGasSubTextFeeLabel')}
|
||||||
|
</strong>
|
||||||
|
<Box
|
||||||
|
height={BLOCK_SIZES.MAX}
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
className="gas-display__currency-container"
|
||||||
|
>
|
||||||
|
<LoadingHeartBeat
|
||||||
|
estimateUsed={
|
||||||
|
transactionData?.userFeeLevel ?? estimateUsed
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<UserPreferencedCurrencyDisplay
|
||||||
|
key="editGasSubTextFeeAmount"
|
||||||
|
type={PRIMARY}
|
||||||
|
value={hexMaximumTransactionFee}
|
||||||
|
hideLabel={!useNativeCurrencyAsPrimaryCurrency}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
subTitle={
|
||||||
|
<>
|
||||||
|
<GasTiming
|
||||||
|
maxPriorityFeePerGas={hexWEIToDecGWEI(
|
||||||
|
draftTransaction.gas.maxPriorityFeePerGas,
|
||||||
|
)}
|
||||||
|
maxFeePerGas={hexWEIToDecGWEI(
|
||||||
|
draftTransaction.gas.maxFeePerGas,
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
(gasError || isInsufficientTokenError) && (
|
||||||
|
<TransactionDetailItem
|
||||||
|
key="total-item"
|
||||||
|
detailTitle={t('total')}
|
||||||
|
detailText={
|
||||||
|
<Box
|
||||||
|
height={BLOCK_SIZES.MAX}
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
className="gas-display__total-value"
|
||||||
|
>
|
||||||
|
<LoadingHeartBeat
|
||||||
|
estimateUsed={transactionData?.userFeeLevel}
|
||||||
|
/>
|
||||||
|
<UserPreferencedCurrencyDisplay
|
||||||
|
type={SECONDARY}
|
||||||
|
key="total-detail-text"
|
||||||
|
value={hexTransactionTotal}
|
||||||
|
hideLabel={Boolean(useNativeCurrencyAsPrimaryCurrency)}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
detailTotal={detailTotal}
|
||||||
|
subTitle={t('transactionDetailGasTotalSubtitle')}
|
||||||
|
subText={
|
||||||
|
<Box
|
||||||
|
height={BLOCK_SIZES.MAX}
|
||||||
|
display={DISPLAY.FLEX}
|
||||||
|
flexDirection={FLEX_DIRECTION.COLUMN}
|
||||||
|
className="gas-display__total-amount"
|
||||||
|
>
|
||||||
|
<LoadingHeartBeat
|
||||||
|
estimateUsed={
|
||||||
|
transactionData?.userFeeLevel ?? estimateUsed
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<strong key="editGasSubTextAmountLabel">
|
||||||
|
{t('editGasSubTextAmountLabel')}
|
||||||
|
</strong>{' '}
|
||||||
|
{maxAmount}
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
{(gasError || isInsufficientTokenError) && (
|
||||||
|
<Box className="gas-display__warning-message">
|
||||||
|
<Box
|
||||||
|
paddingTop={0}
|
||||||
|
paddingRight={4}
|
||||||
|
paddingBottom={4}
|
||||||
|
paddingLeft={4}
|
||||||
|
className="gas-display__confirm-approve-content__warning"
|
||||||
|
>
|
||||||
|
<ActionableMessage
|
||||||
|
message={
|
||||||
|
isBuyableChain && draftTransaction.asset.type === 'NATIVE' ? (
|
||||||
|
<Typography variant={TYPOGRAPHY.H7} align="left">
|
||||||
|
{t('insufficientCurrencyBuyOrReceive', [
|
||||||
|
nativeCurrency,
|
||||||
|
networkName ?? currentProvider.nickname,
|
||||||
|
<Button
|
||||||
|
type="inline"
|
||||||
|
className="confirm-page-container-content__link"
|
||||||
|
onClick={() => {
|
||||||
|
setShowDepositPopover(true);
|
||||||
|
}}
|
||||||
|
key={`${nativeCurrency}-buy-button`}
|
||||||
|
>
|
||||||
|
{t('buyAsset', [nativeCurrency])}
|
||||||
|
</Button>,
|
||||||
|
<Button
|
||||||
|
type="inline"
|
||||||
|
className="gas-display__link"
|
||||||
|
onClick={() =>
|
||||||
|
dispatch(showModal({ name: 'ACCOUNT_DETAILS' }))
|
||||||
|
}
|
||||||
|
key="receive-button"
|
||||||
|
>
|
||||||
|
{t('deposit')}
|
||||||
|
</Button>,
|
||||||
|
])}
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
<Typography variant={TYPOGRAPHY.H7} align="left">
|
||||||
|
{t('insufficientCurrencyBuyOrReceive', [
|
||||||
|
draftTransaction.asset.details?.symbol ?? nativeCurrency,
|
||||||
|
networkName ?? currentProvider.nickname,
|
||||||
|
`${t('buyAsset', [
|
||||||
|
draftTransaction.asset.details?.symbol ??
|
||||||
|
nativeCurrency,
|
||||||
|
])}`,
|
||||||
|
<Button
|
||||||
|
type="inline"
|
||||||
|
className="gas-display__link"
|
||||||
|
onClick={() =>
|
||||||
|
dispatch(showModal({ name: 'ACCOUNT_DETAILS' }))
|
||||||
|
}
|
||||||
|
key="receive-button"
|
||||||
|
>
|
||||||
|
{t('deposit')}
|
||||||
|
</Button>,
|
||||||
|
])}
|
||||||
|
</Typography>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
useIcon
|
||||||
|
iconFillColor="var(--color-error-default)"
|
||||||
|
type="danger"
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
GasDisplay.propTypes = {
|
||||||
|
gasError: PropTypes.string,
|
||||||
|
};
|
1
ui/pages/send/gas-display/index.js
Normal file
1
ui/pages/send/gas-display/index.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './gas-display';
|
58
ui/pages/send/gas-display/index.scss
Normal file
58
ui/pages/send/gas-display/index.scss
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
.gas-display {
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.transaction-detail-rows {
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--color-border-default);
|
||||||
|
margin: 16px 16px;
|
||||||
|
|
||||||
|
.transaction-detail-item {
|
||||||
|
&:not(:first-child) {
|
||||||
|
border-top: 1px solid var(--color-border-default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
&__estimate {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__gas-fee-warning {
|
||||||
|
color: var(--color-warning-default);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__gas-fee-label {
|
||||||
|
position: relative;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__warning-message {
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__currency-container,
|
||||||
|
&__total-amount,
|
||||||
|
&__total-value {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__confirm-approve-content {
|
||||||
|
&__warning {
|
||||||
|
@media screen and (max-width: $break-small) {
|
||||||
|
padding: 0 32px 16px 16px;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 80px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link {
|
||||||
|
text-transform: lowercase;
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,7 @@ import {
|
|||||||
} from '../../../helpers/constants/error-keys';
|
} from '../../../helpers/constants/error-keys';
|
||||||
import { ASSET_TYPES } from '../../../../shared/constants/transaction';
|
import { ASSET_TYPES } from '../../../../shared/constants/transaction';
|
||||||
import { CONTRACT_ADDRESS_LINK } from '../../../helpers/constants/common';
|
import { CONTRACT_ADDRESS_LINK } from '../../../helpers/constants/common';
|
||||||
|
import GasDisplay from '../gas-display';
|
||||||
import SendAmountRow from './send-amount-row';
|
import SendAmountRow from './send-amount-row';
|
||||||
import SendHexDataRow from './send-hex-data-row';
|
import SendHexDataRow from './send-hex-data-row';
|
||||||
import SendAssetRow from './send-asset-row';
|
import SendAssetRow from './send-asset-row';
|
||||||
@ -81,7 +82,6 @@ export default class SendContent extends Component {
|
|||||||
<PageContainerContent>
|
<PageContainerContent>
|
||||||
<div className="send-v2__form">
|
<div className="send-v2__form">
|
||||||
{assetError ? this.renderError(assetError) : null}
|
{assetError ? this.renderError(assetError) : null}
|
||||||
{gasError ? this.renderError(gasError) : null}
|
|
||||||
{isEthGasPrice
|
{isEthGasPrice
|
||||||
? this.renderWarning(ETH_GAS_PRICE_FETCH_WARNING_KEY)
|
? this.renderWarning(ETH_GAS_PRICE_FETCH_WARNING_KEY)
|
||||||
: null}
|
: null}
|
||||||
@ -97,6 +97,7 @@ export default class SendContent extends Component {
|
|||||||
<SendAmountRow />
|
<SendAmountRow />
|
||||||
{networkOrAccountNotSupports1559 ? <SendGasRow /> : null}
|
{networkOrAccountNotSupports1559 ? <SendGasRow /> : null}
|
||||||
{showHexData ? <SendHexDataRow /> : null}
|
{showHexData ? <SendHexDataRow /> : null}
|
||||||
|
<GasDisplay gasError={gasError} />
|
||||||
</div>
|
</div>
|
||||||
</PageContainerContent>
|
</PageContainerContent>
|
||||||
);
|
);
|
||||||
|
@ -150,21 +150,6 @@ describe('SendContent Component', () => {
|
|||||||
).toStrictEqual(true);
|
).toStrictEqual(true);
|
||||||
expect(wrapper.find(Dialog)).toHaveLength(0);
|
expect(wrapper.find(Dialog)).toHaveLength(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render insufficient gas dialog', () => {
|
|
||||||
wrapper.setProps({
|
|
||||||
showHexData: false,
|
|
||||||
getIsBalanceInsufficient: true,
|
|
||||||
});
|
|
||||||
const PageContainerContentChild = wrapper
|
|
||||||
.find(PageContainerContent)
|
|
||||||
.children();
|
|
||||||
const errorDialogProps = PageContainerContentChild.childAt(0).props();
|
|
||||||
expect(errorDialogProps.className).toStrictEqual('send__error-dialog');
|
|
||||||
expect(errorDialogProps.children).toStrictEqual(
|
|
||||||
'insufficientFundsForGas_t',
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not render the asset dropdown if token length is 0', () => {
|
it('should not render the asset dropdown if token length is 0', () => {
|
||||||
|
@ -15,7 +15,6 @@ import {
|
|||||||
acknowledgeRecipientWarning,
|
acknowledgeRecipientWarning,
|
||||||
getRecipientWarningAcknowledgement,
|
getRecipientWarningAcknowledgement,
|
||||||
} from '../../../ducks/send';
|
} from '../../../ducks/send';
|
||||||
|
|
||||||
import SendContent from './send-content.component';
|
import SendContent from './send-content.component';
|
||||||
|
|
||||||
function mapStateToProps(state) {
|
function mapStateToProps(state) {
|
||||||
@ -24,6 +23,7 @@ function mapStateToProps(state) {
|
|||||||
const recipient = getRecipient(state);
|
const recipient = getRecipient(state);
|
||||||
const recipientWarningAcknowledged =
|
const recipientWarningAcknowledged =
|
||||||
getRecipientWarningAcknowledgement(state);
|
getRecipientWarningAcknowledgement(state);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isOwnedAccount: Boolean(
|
isOwnedAccount: Boolean(
|
||||||
ownedAccounts.find(
|
ownedAccounts.find(
|
||||||
|
@ -31,6 +31,7 @@ const TOKEN_TRANSFER_FUNCTION_SIGNATURE = '0xa9059cbb';
|
|||||||
const COLLECTIBLE_TRANSFER_FROM_FUNCTION_SIGNATURE = '0x23b872dd';
|
const COLLECTIBLE_TRANSFER_FROM_FUNCTION_SIGNATURE = '0x23b872dd';
|
||||||
|
|
||||||
const INSUFFICIENT_FUNDS_ERROR = 'insufficientFunds';
|
const INSUFFICIENT_FUNDS_ERROR = 'insufficientFunds';
|
||||||
|
const INSUFFICIENT_FUNDS_FOR_GAS_ERROR = 'insufficientFundsForGas';
|
||||||
const INSUFFICIENT_TOKENS_ERROR = 'insufficientTokens';
|
const INSUFFICIENT_TOKENS_ERROR = 'insufficientTokens';
|
||||||
const NEGATIVE_ETH_ERROR = 'negativeETH';
|
const NEGATIVE_ETH_ERROR = 'negativeETH';
|
||||||
const INVALID_RECIPIENT_ADDRESS_ERROR = 'invalidAddressRecipient';
|
const INVALID_RECIPIENT_ADDRESS_ERROR = 'invalidAddressRecipient';
|
||||||
@ -56,6 +57,7 @@ export {
|
|||||||
MAX_GAS_LIMIT_DEC,
|
MAX_GAS_LIMIT_DEC,
|
||||||
HIGH_FEE_WARNING_MULTIPLIER,
|
HIGH_FEE_WARNING_MULTIPLIER,
|
||||||
INSUFFICIENT_FUNDS_ERROR,
|
INSUFFICIENT_FUNDS_ERROR,
|
||||||
|
INSUFFICIENT_FUNDS_FOR_GAS_ERROR,
|
||||||
INSUFFICIENT_TOKENS_ERROR,
|
INSUFFICIENT_TOKENS_ERROR,
|
||||||
INVALID_RECIPIENT_ADDRESS_ERROR,
|
INVALID_RECIPIENT_ADDRESS_ERROR,
|
||||||
KNOWN_RECIPIENT_ADDRESS_WARNING,
|
KNOWN_RECIPIENT_ADDRESS_WARNING,
|
||||||
|
@ -4,8 +4,11 @@ import thunk from 'redux-thunk';
|
|||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import { SEND_STAGES, startNewDraftTransaction } from '../../ducks/send';
|
import { SEND_STAGES, startNewDraftTransaction } from '../../ducks/send';
|
||||||
import { domainInitialState } from '../../ducks/domains';
|
import { domainInitialState } from '../../ducks/domains';
|
||||||
import { renderWithProvider } from '../../../test/jest';
|
|
||||||
import { CHAIN_IDS } from '../../../shared/constants/network';
|
import { CHAIN_IDS } from '../../../shared/constants/network';
|
||||||
|
import {
|
||||||
|
renderWithProvider,
|
||||||
|
setBackgroundConnection,
|
||||||
|
} from '../../../test/jest';
|
||||||
import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas';
|
import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas';
|
||||||
import { KEYRING_TYPES } from '../../../shared/constants/keyrings';
|
import { KEYRING_TYPES } from '../../../shared/constants/keyrings';
|
||||||
import { INITIAL_SEND_STATE_FOR_EXISTING_DRAFT } from '../../../test/jest/mocks';
|
import { INITIAL_SEND_STATE_FOR_EXISTING_DRAFT } from '../../../test/jest/mocks';
|
||||||
@ -38,6 +41,12 @@ jest.mock('react-router-dom', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setBackgroundConnection({
|
||||||
|
getGasFeeTimeEstimate: jest.fn(),
|
||||||
|
getGasFeeEstimatesAndStartPolling: jest.fn(),
|
||||||
|
promisifiedBackground: jest.fn(),
|
||||||
|
});
|
||||||
|
|
||||||
jest.mock('ethers', () => {
|
jest.mock('ethers', () => {
|
||||||
const originalModule = jest.requireActual('ethers');
|
const originalModule = jest.requireActual('ethers');
|
||||||
return {
|
return {
|
||||||
@ -60,6 +69,14 @@ const baseStore = {
|
|||||||
},
|
},
|
||||||
history: { mostRecentOverviewPage: 'activity' },
|
history: { mostRecentOverviewPage: 'activity' },
|
||||||
metamask: {
|
metamask: {
|
||||||
|
unapprovedTxs: {
|
||||||
|
1: {
|
||||||
|
id: 1,
|
||||||
|
txParams: {
|
||||||
|
value: 'oldTxValue',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY,
|
gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY,
|
||||||
gasFeeEstimates: {
|
gasFeeEstimates: {
|
||||||
low: '0',
|
low: '0',
|
||||||
@ -204,6 +221,27 @@ describe('Send Page', () => {
|
|||||||
const store = configureMockStore(middleware)({
|
const store = configureMockStore(middleware)({
|
||||||
...baseStore,
|
...baseStore,
|
||||||
send: { ...baseStore.send, stage: SEND_STAGES.DRAFT },
|
send: { ...baseStore.send, stage: SEND_STAGES.DRAFT },
|
||||||
|
confirmTransaction: {
|
||||||
|
txData: {
|
||||||
|
id: 3111025347726181,
|
||||||
|
time: 1620723786838,
|
||||||
|
status: 'unapproved',
|
||||||
|
metamaskNetworkId: '5',
|
||||||
|
chainId: '0x5',
|
||||||
|
loadingDefaults: false,
|
||||||
|
txParams: {
|
||||||
|
from: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4',
|
||||||
|
to: '0xaD6D458402F60fD3Bd25163575031ACDce07538D',
|
||||||
|
value: '0x0',
|
||||||
|
data: '0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170',
|
||||||
|
gas: '0xea60',
|
||||||
|
gasPrice: '0x4a817c800',
|
||||||
|
},
|
||||||
|
type: 'transfer',
|
||||||
|
origin: 'https://metamask.github.io',
|
||||||
|
transactionCategory: 'approve',
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const { getByText } = renderWithProvider(<Send />, store);
|
const { getByText } = renderWithProvider(<Send />, store);
|
||||||
expect(getByText('Send')).toBeTruthy();
|
expect(getByText('Send')).toBeTruthy();
|
||||||
@ -221,6 +259,27 @@ describe('Send Page', () => {
|
|||||||
const store = configureMockStore(middleware)({
|
const store = configureMockStore(middleware)({
|
||||||
...baseStore,
|
...baseStore,
|
||||||
send: { ...baseStore.send, stage: SEND_STAGES.DRAFT },
|
send: { ...baseStore.send, stage: SEND_STAGES.DRAFT },
|
||||||
|
confirmTransaction: {
|
||||||
|
txData: {
|
||||||
|
id: 3111025347726181,
|
||||||
|
time: 1620723786838,
|
||||||
|
status: 'unapproved',
|
||||||
|
metamaskNetworkId: '5',
|
||||||
|
chainId: '0x5',
|
||||||
|
loadingDefaults: false,
|
||||||
|
txParams: {
|
||||||
|
from: '0x64a845a5b02460acf8a3d84503b0d68d028b4bb4',
|
||||||
|
to: '0xaD6D458402F60fD3Bd25163575031ACDce07538D',
|
||||||
|
value: '0x0',
|
||||||
|
data: '0x095ea7b30000000000000000000000009bc5baf874d2da8d216ae9f137804184ee5afef40000000000000000000000000000000000000000000000000000000000011170',
|
||||||
|
gas: '0xea60',
|
||||||
|
gasPrice: '0x4a817c800',
|
||||||
|
},
|
||||||
|
type: 'transfer',
|
||||||
|
origin: 'https://metamask.github.io',
|
||||||
|
transactionCategory: 'approve',
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
const { getByText } = renderWithProvider(<Send />, store);
|
const { getByText } = renderWithProvider(<Send />, store);
|
||||||
expect(getByText('Next')).toBeTruthy();
|
expect(getByText('Next')).toBeTruthy();
|
||||||
|
Loading…
Reference in New Issue
Block a user