mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +01:00
Remove global transaction state from send flow (#14777)
* remove global transaction state from send slice * fixup new test
This commit is contained in:
parent
f4b25d7ea5
commit
94967072f7
@ -8,3 +8,16 @@ import contractMap from '@metamask/contract-metadata';
|
||||
export const LISTED_CONTRACT_ADDRESSES = Object.keys(
|
||||
contractMap,
|
||||
).map((address) => address.toLowerCase());
|
||||
|
||||
/**
|
||||
* @typedef {Object} TokenDetails
|
||||
* @property {string} address - The address of the selected 'TOKEN' or
|
||||
* 'COLLECTIBLE' contract.
|
||||
* @property {string} [symbol] - The symbol of the token.
|
||||
* @property {number} [decimals] - The number of decimals of the selected
|
||||
* 'ERC20' asset.
|
||||
* @property {number} [tokenId] - The id of the selected 'COLLECTIBLE' asset.
|
||||
* @property {TokenStandardStrings} [standard] - The standard of the selected
|
||||
* asset.
|
||||
* @property {boolean} [isERC721] - True when the asset is a ERC721 token.
|
||||
*/
|
||||
|
@ -1,3 +1,8 @@
|
||||
import {
|
||||
draftTransactionInitialState,
|
||||
initialState,
|
||||
} from '../../ui/ducks/send';
|
||||
|
||||
export const TOP_ASSETS_GET_RESPONSE = [
|
||||
{
|
||||
symbol: 'LINK',
|
||||
@ -103,3 +108,42 @@ export const createGasFeeEstimatesForFeeMarket = () => {
|
||||
estimatedBaseFee: '50',
|
||||
};
|
||||
};
|
||||
|
||||
export const INITIAL_SEND_STATE_FOR_EXISTING_DRAFT = {
|
||||
...initialState,
|
||||
currentTransactionUUID: 'test-uuid',
|
||||
draftTransactions: {
|
||||
'test-uuid': {
|
||||
...draftTransactionInitialState,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const getInitialSendStateWithExistingTxState = (draftTxState) => ({
|
||||
...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||
draftTransactions: {
|
||||
'test-uuid': {
|
||||
...draftTransactionInitialState,
|
||||
...draftTxState,
|
||||
amount: {
|
||||
...draftTransactionInitialState.amount,
|
||||
...draftTxState.amount,
|
||||
},
|
||||
asset: {
|
||||
...draftTransactionInitialState.asset,
|
||||
...draftTxState.asset,
|
||||
},
|
||||
gas: {
|
||||
...draftTransactionInitialState.gas,
|
||||
...draftTxState.gas,
|
||||
},
|
||||
recipient: {
|
||||
...draftTransactionInitialState.recipient,
|
||||
...draftTxState.recipient,
|
||||
},
|
||||
history: draftTxState.history ?? [],
|
||||
// Use this key if you want to console.log inside the send.js file.
|
||||
test: draftTxState.test ?? 'yo',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ import Tooltip from '../../ui/tooltip';
|
||||
import InfoIcon from '../../ui/icon/info-icon.component';
|
||||
import Button from '../../ui/button';
|
||||
import { useI18nContext } from '../../../hooks/useI18nContext';
|
||||
import { updateSendAsset } from '../../../ducks/send';
|
||||
import { startNewDraftTransaction } from '../../../ducks/send';
|
||||
import { SEND_ROUTE } from '../../../helpers/constants/routes';
|
||||
import { SEVERITIES } from '../../../helpers/constants/design-system';
|
||||
import { INVALID_ASSET_TYPE } from '../../../helpers/constants/error-keys';
|
||||
@ -74,7 +74,7 @@ const AssetListItem = ({
|
||||
});
|
||||
try {
|
||||
await dispatch(
|
||||
updateSendAsset({
|
||||
startNewDraftTransaction({
|
||||
type: ASSET_TYPES.TOKEN,
|
||||
details: {
|
||||
address: tokenAddress,
|
||||
|
@ -45,7 +45,7 @@ import { getEnvironmentType } from '../../../../app/scripts/lib/util';
|
||||
import { ENVIRONMENT_TYPE_POPUP } from '../../../../shared/constants/app';
|
||||
import CollectibleOptions from '../collectible-options/collectible-options';
|
||||
import Button from '../../ui/button';
|
||||
import { updateSendAsset } from '../../../ducks/send';
|
||||
import { startNewDraftTransaction } from '../../../ducks/send';
|
||||
import InfoTooltip from '../../ui/info-tooltip';
|
||||
import { ERC721 } from '../../../helpers/constants/common';
|
||||
import { usePrevious } from '../../../hooks/usePrevious';
|
||||
@ -120,7 +120,7 @@ export default function CollectibleDetails({ collectible }) {
|
||||
|
||||
const onSend = async () => {
|
||||
await dispatch(
|
||||
updateSendAsset({
|
||||
startNewDraftTransaction({
|
||||
type: ASSET_TYPES.COLLECTIBLE,
|
||||
details: collectible,
|
||||
}),
|
||||
|
@ -33,6 +33,8 @@ import { isHardwareKeyring } from '../../../helpers/utils/hardware';
|
||||
import { MetaMetricsContext } from '../../../contexts/metametrics';
|
||||
import { EVENT } from '../../../../shared/constants/metametrics';
|
||||
import Spinner from '../../ui/spinner';
|
||||
import { startNewDraftTransaction } from '../../../ducks/send';
|
||||
import { ASSET_TYPES } from '../../../../shared/constants/transaction';
|
||||
import WalletOverview from './wallet-overview';
|
||||
|
||||
const EthOverview = ({ className }) => {
|
||||
@ -131,7 +133,11 @@ const EthOverview = ({ className }) => {
|
||||
legacy_event: true,
|
||||
},
|
||||
});
|
||||
history.push(SEND_ROUTE);
|
||||
dispatch(
|
||||
startNewDraftTransaction({ type: ASSET_TYPES.NATIVE }),
|
||||
).then(() => {
|
||||
history.push(SEND_ROUTE);
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
} from '../../../helpers/constants/routes';
|
||||
import { useTokenTracker } from '../../../hooks/useTokenTracker';
|
||||
import { useTokenFiatAmount } from '../../../hooks/useTokenFiatAmount';
|
||||
import { updateSendAsset } from '../../../ducks/send';
|
||||
import { startNewDraftTransaction } from '../../../ducks/send';
|
||||
import { setSwapsFromToken } from '../../../ducks/swaps/swaps';
|
||||
import {
|
||||
getCurrentKeyring,
|
||||
@ -93,7 +93,7 @@ const TokenOverview = ({ className, token }) => {
|
||||
});
|
||||
try {
|
||||
await dispatch(
|
||||
updateSendAsset({
|
||||
startNewDraftTransaction({
|
||||
type: ASSET_TYPES.TOKEN,
|
||||
details: token,
|
||||
}),
|
||||
|
@ -118,7 +118,7 @@ export default class TokenInput extends PureComponent {
|
||||
isEqualCaseInsensitive(address, token.address),
|
||||
);
|
||||
|
||||
const tokenExchangeRate = tokenExchangeRates?.[existingToken.address] || 0;
|
||||
const tokenExchangeRate = tokenExchangeRates?.[existingToken?.address] ?? 0;
|
||||
let currency, numberOfDecimals;
|
||||
|
||||
if (hideConversion) {
|
||||
|
295
ui/ducks/send/helpers.js
Normal file
295
ui/ducks/send/helpers.js
Normal file
@ -0,0 +1,295 @@
|
||||
import { addHexPrefix } from 'ethereumjs-util';
|
||||
import abi from 'human-standard-token-abi';
|
||||
import { GAS_LIMITS } from '../../../shared/constants/gas';
|
||||
import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../../../shared/constants/network';
|
||||
import {
|
||||
ASSET_TYPES,
|
||||
TRANSACTION_ENVELOPE_TYPES,
|
||||
} from '../../../shared/constants/transaction';
|
||||
import { readAddressAsContract } from '../../../shared/modules/contract-utils';
|
||||
import {
|
||||
conversionUtil,
|
||||
multiplyCurrencies,
|
||||
} from '../../../shared/modules/conversion.utils';
|
||||
import { ETH, GWEI } from '../../helpers/constants/common';
|
||||
import { calcTokenAmount } from '../../helpers/utils/token-util';
|
||||
import { MIN_GAS_LIMIT_HEX } from '../../pages/send/send.constants';
|
||||
import {
|
||||
addGasBuffer,
|
||||
generateERC20TransferData,
|
||||
generateERC721TransferData,
|
||||
getAssetTransferData,
|
||||
} from '../../pages/send/send.utils';
|
||||
import { getGasPriceInHexWei } from '../../selectors';
|
||||
import { estimateGas } from '../../store/actions';
|
||||
|
||||
export async function estimateGasLimitForSend({
|
||||
selectedAddress,
|
||||
value,
|
||||
gasPrice,
|
||||
sendToken,
|
||||
to,
|
||||
data,
|
||||
isNonStandardEthChain,
|
||||
chainId,
|
||||
gasLimit,
|
||||
...options
|
||||
}) {
|
||||
let isSimpleSendOnNonStandardNetwork = false;
|
||||
|
||||
// blockGasLimit may be a falsy, but defined, value when we receive it from
|
||||
// state, so we use logical or to fall back to MIN_GAS_LIMIT_HEX. Some
|
||||
// network implementations check the gas parameter supplied to
|
||||
// eth_estimateGas for validity. For this reason, we set token sends
|
||||
// blockGasLimit default to a higher number. Note that the current gasLimit
|
||||
// on a BLOCK is 15,000,000 and will be 30,000,000 on mainnet after London.
|
||||
// Meanwhile, MIN_GAS_LIMIT_HEX is 0x5208.
|
||||
let blockGasLimit = MIN_GAS_LIMIT_HEX;
|
||||
if (options.blockGasLimit) {
|
||||
blockGasLimit = options.blockGasLimit;
|
||||
} else if (sendToken) {
|
||||
blockGasLimit = GAS_LIMITS.BASE_TOKEN_ESTIMATE;
|
||||
}
|
||||
|
||||
// The parameters below will be sent to our background process to estimate
|
||||
// how much gas will be used for a transaction. That background process is
|
||||
// located in tx-gas-utils.js in the transaction controller folder.
|
||||
const paramsForGasEstimate = { from: selectedAddress, value, gasPrice };
|
||||
|
||||
if (sendToken) {
|
||||
if (!to) {
|
||||
// If no to address is provided, we cannot generate the token transfer
|
||||
// hexData. hexData in a transaction largely dictates how much gas will
|
||||
// be consumed by a transaction. We must use our best guess, which is
|
||||
// represented in the gas shared constants.
|
||||
return GAS_LIMITS.BASE_TOKEN_ESTIMATE;
|
||||
}
|
||||
paramsForGasEstimate.value = '0x0';
|
||||
|
||||
// We have to generate the erc20/erc721 contract call to transfer tokens in
|
||||
// order to get a proper estimate for gasLimit.
|
||||
paramsForGasEstimate.data = getAssetTransferData({
|
||||
sendToken,
|
||||
fromAddress: selectedAddress,
|
||||
toAddress: to,
|
||||
amount: value,
|
||||
});
|
||||
|
||||
paramsForGasEstimate.to = sendToken.address;
|
||||
} else {
|
||||
if (!data) {
|
||||
// eth.getCode will return the compiled smart contract code at the
|
||||
// address. If this returns 0x, 0x0 or a nullish value then the address
|
||||
// is an externally owned account (NOT a contract account). For these
|
||||
// types of transactions the gasLimit will always be 21,000 or 0x5208
|
||||
const { isContractAddress } = to
|
||||
? await readAddressAsContract(global.eth, to)
|
||||
: {};
|
||||
if (!isContractAddress && !isNonStandardEthChain) {
|
||||
return GAS_LIMITS.SIMPLE;
|
||||
} else if (!isContractAddress && isNonStandardEthChain) {
|
||||
isSimpleSendOnNonStandardNetwork = true;
|
||||
}
|
||||
}
|
||||
|
||||
paramsForGasEstimate.data = data;
|
||||
|
||||
if (to) {
|
||||
paramsForGasEstimate.to = to;
|
||||
}
|
||||
|
||||
if (!value || value === '0') {
|
||||
// TODO: Figure out what's going on here. According to eth_estimateGas
|
||||
// docs this value can be zero, or undefined, yet we are setting it to a
|
||||
// value here when the value is undefined or zero. For more context:
|
||||
// https://github.com/MetaMask/metamask-extension/pull/6195
|
||||
paramsForGasEstimate.value = '0xff';
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSimpleSendOnNonStandardNetwork) {
|
||||
// If we do not yet have a gasLimit, we must call into our background
|
||||
// process to get an estimate for gasLimit based on known parameters.
|
||||
|
||||
paramsForGasEstimate.gas = addHexPrefix(
|
||||
multiplyCurrencies(blockGasLimit, 0.95, {
|
||||
multiplicandBase: 16,
|
||||
multiplierBase: 10,
|
||||
roundDown: '0',
|
||||
toNumericBase: 'hex',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// The buffer multipler reduces transaction failures by ensuring that the
|
||||
// estimated gas is always sufficient. Without the multiplier, estimates
|
||||
// for contract interactions can become inaccurate over time. This is because
|
||||
// gas estimation is non-deterministic. The gas required for the exact same
|
||||
// transaction call can change based on state of a contract or changes in the
|
||||
// contracts environment (blockchain data or contracts it interacts with).
|
||||
// Applying the 1.5 buffer has proven to be a useful guard against this non-
|
||||
// deterministic behaviour.
|
||||
//
|
||||
// Gas estimation of simple sends should, however, be deterministic. As such
|
||||
// no buffer is needed in those cases.
|
||||
let bufferMultiplier = 1.5;
|
||||
if (isSimpleSendOnNonStandardNetwork) {
|
||||
bufferMultiplier = 1;
|
||||
} else if (CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId]) {
|
||||
bufferMultiplier = CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId];
|
||||
}
|
||||
|
||||
try {
|
||||
// Call into the background process that will simulate transaction
|
||||
// execution on the node and return an estimate of gasLimit
|
||||
const estimatedGasLimit = await estimateGas(paramsForGasEstimate);
|
||||
const estimateWithBuffer = addGasBuffer(
|
||||
estimatedGasLimit,
|
||||
blockGasLimit,
|
||||
bufferMultiplier,
|
||||
);
|
||||
return addHexPrefix(estimateWithBuffer);
|
||||
} catch (error) {
|
||||
const simulationFailed =
|
||||
error.message.includes('Transaction execution error.') ||
|
||||
error.message.includes(
|
||||
'gas required exceeds allowance or always failing transaction',
|
||||
) ||
|
||||
(CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP[chainId] &&
|
||||
error.message.includes('gas required exceeds allowance'));
|
||||
if (simulationFailed) {
|
||||
const estimateWithBuffer = addGasBuffer(
|
||||
paramsForGasEstimate?.gas ?? gasLimit,
|
||||
blockGasLimit,
|
||||
bufferMultiplier,
|
||||
);
|
||||
return addHexPrefix(estimateWithBuffer);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a txParams from the send slice.
|
||||
*
|
||||
* @param {import('.').SendState} sendState - the state of the send slice
|
||||
* @returns {import(
|
||||
* '../../../shared/constants/transaction'
|
||||
* ).TxParams} A txParams object that can be used to create a transaction or
|
||||
* update an existing transaction.
|
||||
*/
|
||||
export function generateTransactionParams(sendState) {
|
||||
const draftTransaction =
|
||||
sendState.draftTransactions[sendState.currentTransactionUUID];
|
||||
const txParams = {
|
||||
// If the fromAccount has been specified we use that, if not we use the
|
||||
// selected account.
|
||||
from:
|
||||
draftTransaction.fromAccount?.address ||
|
||||
sendState.selectedAccount.address,
|
||||
// gasLimit always needs to be set regardless of the asset being sent
|
||||
// or the type of transaction.
|
||||
gas: draftTransaction.gas.gasLimit,
|
||||
};
|
||||
switch (draftTransaction.asset.type) {
|
||||
case ASSET_TYPES.TOKEN:
|
||||
// When sending a token the to address is the contract address of
|
||||
// the token being sent. The value is set to '0x0' and the data
|
||||
// is generated from the recipient address, token being sent and
|
||||
// amount.
|
||||
txParams.to = draftTransaction.asset.details.address;
|
||||
txParams.value = '0x0';
|
||||
txParams.data = generateERC20TransferData({
|
||||
toAddress: draftTransaction.recipient.address,
|
||||
amount: draftTransaction.amount.value,
|
||||
sendToken: draftTransaction.asset.details,
|
||||
});
|
||||
break;
|
||||
case ASSET_TYPES.COLLECTIBLE:
|
||||
// When sending a token the to address is the contract address of
|
||||
// the token being sent. The value is set to '0x0' and the data
|
||||
// is generated from the recipient address, token being sent and
|
||||
// amount.
|
||||
txParams.to = draftTransaction.asset.details.address;
|
||||
txParams.value = '0x0';
|
||||
txParams.data = generateERC721TransferData({
|
||||
toAddress: draftTransaction.recipient.address,
|
||||
fromAddress:
|
||||
draftTransaction.fromAccount?.address ??
|
||||
sendState.selectedAccount.address,
|
||||
tokenId: draftTransaction.asset.details.tokenId,
|
||||
});
|
||||
break;
|
||||
case ASSET_TYPES.NATIVE:
|
||||
default:
|
||||
// When sending native currency the to and value fields use the
|
||||
// recipient and amount values and the data key is either null or
|
||||
// populated with the user input provided in hex field.
|
||||
txParams.to = draftTransaction.recipient.address;
|
||||
txParams.value = draftTransaction.amount.value;
|
||||
txParams.data = draftTransaction.userInputHexData ?? undefined;
|
||||
}
|
||||
|
||||
// We need to make sure that we only include the right gas fee fields
|
||||
// based on the type of transaction the network supports. We will also set
|
||||
// the type param here.
|
||||
if (sendState.eip1559support) {
|
||||
txParams.type = TRANSACTION_ENVELOPE_TYPES.FEE_MARKET;
|
||||
|
||||
txParams.maxFeePerGas = draftTransaction.gas.maxFeePerGas;
|
||||
txParams.maxPriorityFeePerGas = draftTransaction.gas.maxPriorityFeePerGas;
|
||||
|
||||
if (!txParams.maxFeePerGas || txParams.maxFeePerGas === '0x0') {
|
||||
txParams.maxFeePerGas = draftTransaction.gas.gasPrice;
|
||||
}
|
||||
|
||||
if (
|
||||
!txParams.maxPriorityFeePerGas ||
|
||||
txParams.maxPriorityFeePerGas === '0x0'
|
||||
) {
|
||||
txParams.maxPriorityFeePerGas = txParams.maxFeePerGas;
|
||||
}
|
||||
} else {
|
||||
txParams.gasPrice = draftTransaction.gas.gasPrice;
|
||||
txParams.type = TRANSACTION_ENVELOPE_TYPES.LEGACY;
|
||||
}
|
||||
|
||||
return txParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to keep the original logic from the gas.duck.js file
|
||||
* after receiving a gasPrice from eth_gasPrice. First, the returned gasPrice
|
||||
* was converted to GWEI, then it was converted to a Number, then in the send
|
||||
* duck (here) we would use getGasPriceInHexWei to get back to hexWei. Now that
|
||||
* we receive a GWEI estimate from the controller, we still need to do this
|
||||
* weird conversion to get the proper rounding.
|
||||
*
|
||||
* @param {string} gasPriceEstimate
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getRoundedGasPrice(gasPriceEstimate) {
|
||||
const gasPriceInDecGwei = conversionUtil(gasPriceEstimate, {
|
||||
numberOfDecimals: 9,
|
||||
toDenomination: GWEI,
|
||||
fromNumericBase: 'dec',
|
||||
toNumericBase: 'dec',
|
||||
fromCurrency: ETH,
|
||||
fromDenomination: GWEI,
|
||||
});
|
||||
const gasPriceAsNumber = Number(gasPriceInDecGwei);
|
||||
return getGasPriceInHexWei(gasPriceAsNumber);
|
||||
}
|
||||
|
||||
export async function getERC20Balance(token, accountAddress) {
|
||||
const contract = global.eth.contract(abi).at(token.address);
|
||||
const usersToken = (await contract.balanceOf(accountAddress)) ?? null;
|
||||
if (!usersToken) {
|
||||
return '0x0';
|
||||
}
|
||||
const amount = calcTokenAmount(
|
||||
usersToken.balance.toString(),
|
||||
token.decimals,
|
||||
).toString(16);
|
||||
return addHexPrefix(amount);
|
||||
}
|
163
ui/ducks/send/helpers.test.js
Normal file
163
ui/ducks/send/helpers.test.js
Normal file
@ -0,0 +1,163 @@
|
||||
import { ethers } from 'ethers';
|
||||
import { GAS_LIMITS } from '../../../shared/constants/gas';
|
||||
import {
|
||||
ASSET_TYPES,
|
||||
TRANSACTION_ENVELOPE_TYPES,
|
||||
} from '../../../shared/constants/transaction';
|
||||
import { BURN_ADDRESS } from '../../../shared/modules/hexstring-utils';
|
||||
import { getInitialSendStateWithExistingTxState } from '../../../test/jest/mocks';
|
||||
import { TOKEN_STANDARDS } from '../../helpers/constants/common';
|
||||
import {
|
||||
generateERC20TransferData,
|
||||
generateERC721TransferData,
|
||||
} from '../../pages/send/send.utils';
|
||||
import { generateTransactionParams } from './helpers';
|
||||
|
||||
describe('Send Slice Helpers', () => {
|
||||
describe('generateTransactionParams', () => {
|
||||
it('should generate a txParams for a token transfer', () => {
|
||||
const tokenDetails = {
|
||||
address: '0xToken',
|
||||
symbol: 'SYMB',
|
||||
decimals: 18,
|
||||
};
|
||||
const txParams = generateTransactionParams(
|
||||
getInitialSendStateWithExistingTxState({
|
||||
fromAccount: {
|
||||
address: '0x00',
|
||||
},
|
||||
amount: {
|
||||
value: '0x1',
|
||||
},
|
||||
asset: {
|
||||
type: ASSET_TYPES.TOKEN,
|
||||
balance: '0xaf',
|
||||
details: tokenDetails,
|
||||
},
|
||||
recipient: {
|
||||
address: BURN_ADDRESS,
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(txParams).toStrictEqual({
|
||||
from: '0x00',
|
||||
data: generateERC20TransferData({
|
||||
toAddress: BURN_ADDRESS,
|
||||
amount: '0x1',
|
||||
sendToken: tokenDetails,
|
||||
}),
|
||||
to: '0xToken',
|
||||
type: '0x0',
|
||||
value: '0x0',
|
||||
gas: '0x0',
|
||||
gasPrice: '0x0',
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate a txParams for a collectible transfer', () => {
|
||||
const txParams = generateTransactionParams(
|
||||
getInitialSendStateWithExistingTxState({
|
||||
fromAccount: {
|
||||
address: '0x00',
|
||||
},
|
||||
amount: {
|
||||
value: '0x1',
|
||||
},
|
||||
asset: {
|
||||
type: ASSET_TYPES.COLLECTIBLE,
|
||||
balance: '0xaf',
|
||||
details: {
|
||||
address: '0xToken',
|
||||
standard: TOKEN_STANDARDS.ERC721,
|
||||
tokenId: ethers.BigNumber.from(15000).toString(),
|
||||
},
|
||||
},
|
||||
recipient: {
|
||||
address: BURN_ADDRESS,
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(txParams).toStrictEqual({
|
||||
from: '0x00',
|
||||
data: generateERC721TransferData({
|
||||
toAddress: BURN_ADDRESS,
|
||||
fromAddress: '0x00',
|
||||
tokenId: ethers.BigNumber.from(15000).toString(),
|
||||
}),
|
||||
to: '0xToken',
|
||||
type: '0x0',
|
||||
value: '0x0',
|
||||
gas: '0x0',
|
||||
gasPrice: '0x0',
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate a txParams for a native legacy transaction', () => {
|
||||
const txParams = generateTransactionParams(
|
||||
getInitialSendStateWithExistingTxState({
|
||||
fromAccount: {
|
||||
address: '0x00',
|
||||
},
|
||||
amount: {
|
||||
value: '0x1',
|
||||
},
|
||||
asset: {
|
||||
type: ASSET_TYPES.NATIVE,
|
||||
balance: '0xaf',
|
||||
details: null,
|
||||
},
|
||||
recipient: {
|
||||
address: BURN_ADDRESS,
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(txParams).toStrictEqual({
|
||||
from: '0x00',
|
||||
data: undefined,
|
||||
to: BURN_ADDRESS,
|
||||
type: '0x0',
|
||||
value: '0x1',
|
||||
gas: '0x0',
|
||||
gasPrice: '0x0',
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate a txParams for a native fee market transaction', () => {
|
||||
const txParams = generateTransactionParams({
|
||||
...getInitialSendStateWithExistingTxState({
|
||||
fromAccount: {
|
||||
address: '0x00',
|
||||
},
|
||||
amount: {
|
||||
value: '0x1',
|
||||
},
|
||||
asset: {
|
||||
type: ASSET_TYPES.NATIVE,
|
||||
balance: '0xaf',
|
||||
details: null,
|
||||
},
|
||||
recipient: {
|
||||
address: BURN_ADDRESS,
|
||||
},
|
||||
gas: {
|
||||
maxFeePerGas: '0x2',
|
||||
maxPriorityFeePerGas: '0x1',
|
||||
gasLimit: GAS_LIMITS.SIMPLE,
|
||||
},
|
||||
transactionType: TRANSACTION_ENVELOPE_TYPES.FEE_MARKET,
|
||||
}),
|
||||
eip1559support: true,
|
||||
});
|
||||
expect(txParams).toStrictEqual({
|
||||
from: '0x00',
|
||||
data: undefined,
|
||||
to: BURN_ADDRESS,
|
||||
type: '0x2',
|
||||
value: '0x1',
|
||||
gas: GAS_LIMITS.SIMPLE,
|
||||
maxFeePerGas: '0x2',
|
||||
maxPriorityFeePerGas: '0x1',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { compose } from 'redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { editTransaction } from '../../ducks/send';
|
||||
import { editExistingTransaction } from '../../ducks/send';
|
||||
import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck';
|
||||
import { ASSET_TYPES } from '../../../shared/constants/transaction';
|
||||
import ConfirmSendEther from './confirm-send-ether.component';
|
||||
@ -20,7 +20,9 @@ const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
editTransaction: async (txData) => {
|
||||
const { id } = txData;
|
||||
await dispatch(editTransaction(ASSET_TYPES.NATIVE, id.toString()));
|
||||
await dispatch(
|
||||
editExistingTransaction(ASSET_TYPES.NATIVE, id.toString()),
|
||||
);
|
||||
dispatch(clearConfirmTransaction());
|
||||
},
|
||||
};
|
||||
|
@ -6,14 +6,15 @@ import { SEND_ROUTE } from '../../helpers/constants/routes';
|
||||
export default class ConfirmSendToken extends Component {
|
||||
static propTypes = {
|
||||
history: PropTypes.object,
|
||||
editTransaction: PropTypes.func,
|
||||
editExistingTransaction: PropTypes.func,
|
||||
tokenAmount: PropTypes.string,
|
||||
};
|
||||
|
||||
handleEdit(confirmTransactionData) {
|
||||
const { editTransaction, history } = this.props;
|
||||
editTransaction(confirmTransactionData);
|
||||
history.push(SEND_ROUTE);
|
||||
const { editExistingTransaction, history } = this.props;
|
||||
editExistingTransaction(confirmTransactionData).then(() => {
|
||||
history.push(SEND_ROUTE);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -3,7 +3,7 @@ import { compose } from 'redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { clearConfirmTransaction } from '../../ducks/confirm-transaction/confirm-transaction.duck';
|
||||
import { showSendTokenPage } from '../../store/actions';
|
||||
import { editTransaction } from '../../ducks/send';
|
||||
import { editExistingTransaction } from '../../ducks/send';
|
||||
import { sendTokenTokenAmountAndToAddressSelector } from '../../selectors';
|
||||
import { ASSET_TYPES } from '../../../shared/constants/transaction';
|
||||
import ConfirmSendToken from './confirm-send-token.component';
|
||||
@ -18,18 +18,11 @@ const mapStateToProps = (state) => {
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
editTransaction: ({ txData, tokenData, tokenProps: assetDetails }) => {
|
||||
editExistingTransaction: async ({ txData }) => {
|
||||
const { id } = txData;
|
||||
dispatch(
|
||||
editTransaction(
|
||||
ASSET_TYPES.TOKEN,
|
||||
id.toString(),
|
||||
tokenData,
|
||||
assetDetails,
|
||||
),
|
||||
);
|
||||
dispatch(clearConfirmTransaction());
|
||||
dispatch(showSendTokenPage());
|
||||
await dispatch(editExistingTransaction(ASSET_TYPES.TOKEN, id.toString()));
|
||||
await dispatch(clearConfirmTransaction());
|
||||
await dispatch(showSendTokenPage());
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -4,7 +4,7 @@ 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 { editTransaction } from '../../ducks/send';
|
||||
import { editExistingTransaction } from '../../ducks/send';
|
||||
import {
|
||||
contractExchangeRateSelector,
|
||||
getCurrentCurrency,
|
||||
@ -35,27 +35,17 @@ export default function ConfirmSendToken({
|
||||
const dispatch = useDispatch();
|
||||
const history = useHistory();
|
||||
|
||||
const handleEditTransaction = ({
|
||||
txData,
|
||||
tokenData,
|
||||
tokenProps: assetDetails,
|
||||
}) => {
|
||||
const handleEditTransaction = async ({ txData }) => {
|
||||
const { id } = txData;
|
||||
dispatch(
|
||||
editTransaction(
|
||||
ASSET_TYPES.TOKEN,
|
||||
id.toString(),
|
||||
tokenData,
|
||||
assetDetails,
|
||||
),
|
||||
);
|
||||
await dispatch(editExistingTransaction(ASSET_TYPES.TOKEN, id.toString()));
|
||||
dispatch(clearConfirmTransaction());
|
||||
dispatch(showSendTokenPage());
|
||||
};
|
||||
|
||||
const handleEdit = (confirmTransactionData) => {
|
||||
handleEditTransaction(confirmTransactionData);
|
||||
history.push(SEND_ROUTE);
|
||||
handleEditTransaction(confirmTransactionData).then(() => {
|
||||
history.push(SEND_ROUTE);
|
||||
});
|
||||
};
|
||||
const conversionRate = useSelector(getConversionRate);
|
||||
const nativeCurrency = useSelector(getNativeCurrency);
|
||||
|
@ -3,9 +3,13 @@ import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { initialState, SEND_STATUSES } from '../../../../../ducks/send';
|
||||
import { AMOUNT_MODES, SEND_STATUSES } from '../../../../../ducks/send';
|
||||
import { renderWithProvider } from '../../../../../../test/jest';
|
||||
import { GAS_ESTIMATE_TYPES } from '../../../../../../shared/constants/gas';
|
||||
import {
|
||||
getInitialSendStateWithExistingTxState,
|
||||
INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||
} from '../../../../../../test/jest/mocks';
|
||||
import AmountMaxButton from './amount-max-button';
|
||||
|
||||
const middleware = [thunk];
|
||||
@ -22,7 +26,7 @@ describe('AmountMaxButton Component', () => {
|
||||
EIPS: {},
|
||||
},
|
||||
},
|
||||
send: initialState,
|
||||
send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||
}),
|
||||
);
|
||||
expect(getByText('Max')).toBeTruthy();
|
||||
@ -36,12 +40,14 @@ describe('AmountMaxButton Component', () => {
|
||||
EIPS: {},
|
||||
},
|
||||
},
|
||||
send: { ...initialState, status: SEND_STATUSES.VALID },
|
||||
send: getInitialSendStateWithExistingTxState({
|
||||
status: SEND_STATUSES.VALID,
|
||||
}),
|
||||
});
|
||||
const { getByText } = renderWithProvider(<AmountMaxButton />, store);
|
||||
|
||||
const expectedActions = [
|
||||
{ type: 'send/updateAmountMode', payload: 'MAX' },
|
||||
{ type: 'send/updateAmountMode', payload: AMOUNT_MODES.MAX },
|
||||
];
|
||||
|
||||
fireEvent.click(getByText('Max'), { bubbles: true });
|
||||
@ -58,9 +64,10 @@ describe('AmountMaxButton Component', () => {
|
||||
},
|
||||
},
|
||||
send: {
|
||||
...initialState,
|
||||
status: SEND_STATUSES.VALID,
|
||||
amount: { ...initialState.amount, mode: 'MAX' },
|
||||
...getInitialSendStateWithExistingTxState({
|
||||
status: SEND_STATUSES.VALID,
|
||||
}),
|
||||
amountMode: AMOUNT_MODES.MAX,
|
||||
},
|
||||
});
|
||||
const { getByText } = renderWithProvider(<AmountMaxButton />, store);
|
||||
|
@ -3,9 +3,13 @@ import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import { initialState, SEND_STAGES } from '../../../ducks/send';
|
||||
import { SEND_STAGES } from '../../../ducks/send';
|
||||
import { renderWithProvider } from '../../../../test/jest';
|
||||
import { ASSET_TYPES } from '../../../../shared/constants/transaction';
|
||||
import {
|
||||
getInitialSendStateWithExistingTxState,
|
||||
INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||
} from '../../../../test/jest/mocks';
|
||||
import SendHeader from './send-header.component';
|
||||
|
||||
const middleware = [thunk];
|
||||
@ -26,7 +30,7 @@ describe('SendHeader Component', () => {
|
||||
const { getByText, rerender } = renderWithProvider(
|
||||
<SendHeader />,
|
||||
configureMockStore(middleware)({
|
||||
send: initialState,
|
||||
send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||
gas: { basicEstimateStatus: 'LOADING' },
|
||||
history: { mostRecentOverviewPage: 'activity' },
|
||||
}),
|
||||
@ -35,7 +39,10 @@ describe('SendHeader Component', () => {
|
||||
rerender(
|
||||
<SendHeader />,
|
||||
configureMockStore(middleware)({
|
||||
send: { ...initialState, stage: SEND_STAGES.ADD_RECIPIENT },
|
||||
send: {
|
||||
...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||
stage: SEND_STAGES.ADD_RECIPIENT,
|
||||
},
|
||||
gas: { basicEstimateStatus: 'LOADING' },
|
||||
history: { mostRecentOverviewPage: 'activity' },
|
||||
}),
|
||||
@ -48,9 +55,12 @@ describe('SendHeader Component', () => {
|
||||
<SendHeader />,
|
||||
configureMockStore(middleware)({
|
||||
send: {
|
||||
...initialState,
|
||||
...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||
stage: SEND_STAGES.DRAFT,
|
||||
asset: { ...initialState.asset, type: ASSET_TYPES.NATIVE },
|
||||
asset: {
|
||||
...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT.asset,
|
||||
type: ASSET_TYPES.NATIVE,
|
||||
},
|
||||
},
|
||||
gas: { basicEstimateStatus: 'LOADING' },
|
||||
history: { mostRecentOverviewPage: 'activity' },
|
||||
@ -64,9 +74,12 @@ describe('SendHeader Component', () => {
|
||||
<SendHeader />,
|
||||
configureMockStore(middleware)({
|
||||
send: {
|
||||
...initialState,
|
||||
...getInitialSendStateWithExistingTxState({
|
||||
asset: {
|
||||
type: ASSET_TYPES.TOKEN,
|
||||
},
|
||||
}),
|
||||
stage: SEND_STAGES.DRAFT,
|
||||
asset: { ...initialState.asset, type: ASSET_TYPES.TOKEN },
|
||||
},
|
||||
gas: { basicEstimateStatus: 'LOADING' },
|
||||
history: { mostRecentOverviewPage: 'activity' },
|
||||
@ -80,7 +93,7 @@ describe('SendHeader Component', () => {
|
||||
<SendHeader />,
|
||||
configureMockStore(middleware)({
|
||||
send: {
|
||||
...initialState,
|
||||
...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||
stage: SEND_STAGES.EDIT,
|
||||
},
|
||||
gas: { basicEstimateStatus: 'LOADING' },
|
||||
@ -96,7 +109,7 @@ describe('SendHeader Component', () => {
|
||||
const { getByText } = renderWithProvider(
|
||||
<SendHeader />,
|
||||
configureMockStore(middleware)({
|
||||
send: initialState,
|
||||
send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||
gas: { basicEstimateStatus: 'LOADING' },
|
||||
history: { mostRecentOverviewPage: 'activity' },
|
||||
}),
|
||||
@ -108,7 +121,10 @@ describe('SendHeader Component', () => {
|
||||
const { getByText } = renderWithProvider(
|
||||
<SendHeader />,
|
||||
configureMockStore(middleware)({
|
||||
send: { ...initialState, stage: SEND_STAGES.EDIT },
|
||||
send: {
|
||||
...INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||
stage: SEND_STAGES.EDIT,
|
||||
},
|
||||
gas: { basicEstimateStatus: 'LOADING' },
|
||||
history: { mostRecentOverviewPage: 'activity' },
|
||||
}),
|
||||
@ -118,7 +134,7 @@ describe('SendHeader Component', () => {
|
||||
|
||||
it('resets send state when clicked', () => {
|
||||
const store = configureMockStore(middleware)({
|
||||
send: initialState,
|
||||
send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||
gas: { basicEstimateStatus: 'LOADING' },
|
||||
history: { mostRecentOverviewPage: 'activity' },
|
||||
});
|
||||
|
@ -7,14 +7,13 @@ import {
|
||||
getRecipient,
|
||||
getRecipientUserInput,
|
||||
getSendStage,
|
||||
initializeSendState,
|
||||
resetRecipientInput,
|
||||
resetSendState,
|
||||
SEND_STAGES,
|
||||
updateRecipient,
|
||||
updateRecipientUserInput,
|
||||
} from '../../ducks/send';
|
||||
import { getCurrentChainId, isCustomPriceExcessive } from '../../selectors';
|
||||
import { isCustomPriceExcessive } from '../../selectors';
|
||||
import { getSendHexDataFeatureFlagState } from '../../ducks/metamask/metamask';
|
||||
import { showQrScanner } from '../../store/actions';
|
||||
import { MetaMetricsContext } from '../../contexts/metametrics';
|
||||
@ -30,7 +29,6 @@ const sendSliceIsCustomPriceExcessive = (state) =>
|
||||
|
||||
export default function SendTransactionScreen() {
|
||||
const history = useHistory();
|
||||
const chainId = useSelector(getCurrentChainId);
|
||||
const stage = useSelector(getSendStage);
|
||||
const gasIsExcessive = useSelector(sendSliceIsCustomPriceExcessive);
|
||||
const isUsingMyAccountsForRecipientSearch = useSelector(
|
||||
@ -49,11 +47,8 @@ export default function SendTransactionScreen() {
|
||||
}, [dispatch]);
|
||||
|
||||
useEffect(() => {
|
||||
if (chainId !== undefined) {
|
||||
dispatch(initializeSendState());
|
||||
window.addEventListener('beforeunload', cleanup);
|
||||
}
|
||||
}, [chainId, dispatch, cleanup]);
|
||||
window.addEventListener('beforeunload', cleanup);
|
||||
}, [cleanup]);
|
||||
|
||||
useEffect(() => {
|
||||
if (location.search === '?scan=true') {
|
||||
|
@ -3,11 +3,12 @@ import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { initialState, SEND_STAGES } from '../../ducks/send';
|
||||
import { SEND_STAGES } from '../../ducks/send';
|
||||
import { ensInitialState } from '../../ducks/ens';
|
||||
import { renderWithProvider } from '../../../test/jest';
|
||||
import { RINKEBY_CHAIN_ID } from '../../../shared/constants/network';
|
||||
import { GAS_ESTIMATE_TYPES } from '../../../shared/constants/gas';
|
||||
import { INITIAL_SEND_STATE_FOR_EXISTING_DRAFT } from '../../../test/jest/mocks';
|
||||
import Send from './send';
|
||||
|
||||
const middleware = [thunk];
|
||||
@ -34,7 +35,7 @@ jest.mock(
|
||||
);
|
||||
|
||||
const baseStore = {
|
||||
send: initialState,
|
||||
send: INITIAL_SEND_STATE_FOR_EXISTING_DRAFT,
|
||||
ENS: ensInitialState,
|
||||
gas: {
|
||||
customData: { limit: null, price: null },
|
||||
@ -87,7 +88,7 @@ const baseStore = {
|
||||
|
||||
describe('Send Page', () => {
|
||||
describe('Send Flow Initialization', () => {
|
||||
it('should initialize the send, ENS, and gas slices on render', () => {
|
||||
it('should initialize the ENS slice on render', () => {
|
||||
const store = configureMockStore(middleware)(baseStore);
|
||||
renderWithProvider(<Send />, store);
|
||||
const actions = store.getActions();
|
||||
@ -96,9 +97,6 @@ describe('Send Page', () => {
|
||||
expect.objectContaining({
|
||||
type: 'ENS/enableEnsLookup',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
type: 'send/initializeSendState/pending',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
@ -113,9 +111,6 @@ describe('Send Page', () => {
|
||||
expect.objectContaining({
|
||||
type: 'ENS/enableEnsLookup',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
type: 'send/initializeSendState/pending',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
type: 'UI_MODAL_OPEN',
|
||||
payload: { name: 'QR_SCANNER' },
|
||||
|
@ -8,7 +8,7 @@ import { decEthToConvertedCurrency as ethTotalToConvertedCurrency } from '../hel
|
||||
import { formatETHFee } from '../helpers/utils/formatters';
|
||||
import { calcGasTotal } from '../pages/send/send.utils';
|
||||
|
||||
import { getGasPrice } from '../ducks/send';
|
||||
import { getGasLimit, getGasPrice } from '../ducks/send';
|
||||
import {
|
||||
GAS_ESTIMATE_TYPES as GAS_FEE_CONTROLLER_ESTIMATE_TYPES,
|
||||
GAS_LIMITS,
|
||||
@ -321,8 +321,9 @@ export function getRenderableEstimateDataForSmallButtonsFromGWEI(state) {
|
||||
return [];
|
||||
}
|
||||
const showFiat = getShouldShowFiat(state);
|
||||
|
||||
const gasLimit =
|
||||
state.send.gas.gasLimit || getCustomGasLimit(state) || GAS_LIMITS.SIMPLE;
|
||||
getGasLimit(state) ?? getCustomGasLimit(state) ?? GAS_LIMITS.SIMPLE;
|
||||
const { conversionRate } = state.metamask;
|
||||
const currentCurrency = getCurrentCurrency(state);
|
||||
const gasFeeEstimates = getGasFeeEstimates(state);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { GAS_ESTIMATE_TYPES, GAS_LIMITS } from '../../shared/constants/gas';
|
||||
import { getInitialSendStateWithExistingTxState } from '../../test/jest/mocks';
|
||||
import {
|
||||
getCustomGasLimit,
|
||||
getCustomGasPrice,
|
||||
@ -11,7 +12,9 @@ import {
|
||||
describe('custom-gas selectors', () => {
|
||||
describe('getCustomGasPrice()', () => {
|
||||
it('should return gas.customData.price', () => {
|
||||
const mockState = { gas: { customData: { price: 'mockPrice' } } };
|
||||
const mockState = {
|
||||
gas: { customData: { price: 'mockPrice' } },
|
||||
};
|
||||
expect(getCustomGasPrice(mockState)).toStrictEqual('mockPrice');
|
||||
});
|
||||
});
|
||||
@ -200,11 +203,11 @@ describe('custom-gas selectors', () => {
|
||||
EIPS: {},
|
||||
},
|
||||
},
|
||||
send: {
|
||||
send: getInitialSendStateWithExistingTxState({
|
||||
gas: {
|
||||
gasPrice: '0x28bed0160',
|
||||
},
|
||||
},
|
||||
}),
|
||||
gas: {
|
||||
customData: { price: null },
|
||||
},
|
||||
@ -222,11 +225,11 @@ describe('custom-gas selectors', () => {
|
||||
EIPS: {},
|
||||
},
|
||||
},
|
||||
send: {
|
||||
send: getInitialSendStateWithExistingTxState({
|
||||
gas: {
|
||||
gasPrice: '0x30e4f9b400',
|
||||
},
|
||||
},
|
||||
}),
|
||||
gas: {
|
||||
customData: { price: null },
|
||||
},
|
||||
@ -330,11 +333,11 @@ describe('custom-gas selectors', () => {
|
||||
chainId: '0x1',
|
||||
},
|
||||
},
|
||||
send: {
|
||||
send: getInitialSendStateWithExistingTxState({
|
||||
gas: {
|
||||
gasLimit: GAS_LIMITS.SIMPLE,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -379,11 +382,11 @@ describe('custom-gas selectors', () => {
|
||||
chainId: '0x4',
|
||||
},
|
||||
},
|
||||
send: {
|
||||
send: getInitialSendStateWithExistingTxState({
|
||||
gas: {
|
||||
gasLimit: GAS_LIMITS.SIMPLE,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -428,11 +431,11 @@ describe('custom-gas selectors', () => {
|
||||
chainId: '0x4',
|
||||
},
|
||||
},
|
||||
send: {
|
||||
send: getInitialSendStateWithExistingTxState({
|
||||
gas: {
|
||||
gasLimit: GAS_LIMITS.SIMPLE,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -477,11 +480,11 @@ describe('custom-gas selectors', () => {
|
||||
chainId: '0x1',
|
||||
},
|
||||
},
|
||||
send: {
|
||||
send: getInitialSendStateWithExistingTxState({
|
||||
gas: {
|
||||
gasLimit: GAS_LIMITS.SIMPLE,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
];
|
||||
@ -542,11 +545,11 @@ describe('custom-gas selectors', () => {
|
||||
chainId: '0x1',
|
||||
},
|
||||
},
|
||||
send: {
|
||||
send: getInitialSendStateWithExistingTxState({
|
||||
gas: {
|
||||
gasLimit: GAS_LIMITS.SIMPLE,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -591,11 +594,11 @@ describe('custom-gas selectors', () => {
|
||||
chainId: '0x1',
|
||||
},
|
||||
},
|
||||
send: {
|
||||
send: getInitialSendStateWithExistingTxState({
|
||||
gas: {
|
||||
gasLimit: GAS_LIMITS.SIMPLE,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -640,11 +643,11 @@ describe('custom-gas selectors', () => {
|
||||
chainId: '0x4',
|
||||
},
|
||||
},
|
||||
send: {
|
||||
send: getInitialSendStateWithExistingTxState({
|
||||
gas: {
|
||||
gasLimit: GAS_LIMITS.SIMPLE,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -689,11 +692,11 @@ describe('custom-gas selectors', () => {
|
||||
chainId: '0x4',
|
||||
},
|
||||
},
|
||||
send: {
|
||||
send: getInitialSendStateWithExistingTxState({
|
||||
gas: {
|
||||
gasLimit: GAS_LIMITS.SIMPLE,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -738,11 +741,11 @@ describe('custom-gas selectors', () => {
|
||||
chainId: '0x1',
|
||||
},
|
||||
},
|
||||
send: {
|
||||
send: getInitialSendStateWithExistingTxState({
|
||||
gas: {
|
||||
gasLimit: GAS_LIMITS.SIMPLE,
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -27,7 +27,11 @@ import {
|
||||
getNotifications,
|
||||
///: END:ONLY_INCLUDE_IN
|
||||
} from '../selectors';
|
||||
import { computeEstimatedGasLimit, resetSendState } from '../ducks/send';
|
||||
import {
|
||||
computeEstimatedGasLimit,
|
||||
initializeSendState,
|
||||
resetSendState,
|
||||
} from '../ducks/send';
|
||||
import { switchedToUnconnectedAccount } from '../ducks/alerts/unconnected-account';
|
||||
import { getUnconnectedAccountAlertEnabledness } from '../ducks/metamask/metamask';
|
||||
import { toChecksumHexAddress } from '../../shared/modules/hexstring-utils';
|
||||
@ -1443,6 +1447,11 @@ export function updateMetamaskState(newState) {
|
||||
type: actionConstants.CHAIN_CHANGED,
|
||||
payload: newProvider.chainId,
|
||||
});
|
||||
// We dispatch this action to ensure that the send state stays up to date
|
||||
// after the chain changes. This async thunk will fail gracefully in the
|
||||
// event that we are not yet on the send flow with a draftTransaction in
|
||||
// progress.
|
||||
dispatch(initializeSendState({ chainHasChanged: true }));
|
||||
}
|
||||
dispatch({
|
||||
type: actionConstants.UPDATE_METAMASK_STATE,
|
||||
|
Loading…
Reference in New Issue
Block a user