From dc25a24de3535116bd032b2eeb416a85cf2891e2 Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Fri, 16 Jul 2021 11:06:32 -0500 Subject: [PATCH] rely upon gas fee controller for gas price estimates (#11511) --- app/scripts/controllers/network/network.js | 3 + app/scripts/metamask-controller.js | 16 +- ...gas-modal-page-container-component.test.js | 40 +-- .../gas-modal-page-container.component.js | 28 +- .../gas-modal-page-container.container.js | 2 - ui/ducks/gas/gas-action-constants.js | 5 - ui/ducks/gas/gas-duck.test.js | 155 +-------- ui/ducks/gas/gas.duck.js | 176 ---------- ui/ducks/send/send.js | 204 ++++++------ ui/ducks/send/send.test.js | 57 +--- ui/hooks/useCancelTransaction.js | 7 +- ui/hooks/useGasFeeEstimates.js | 8 +- ui/hooks/useRetryTransaction.js | 10 +- ui/hooks/useRetryTransaction.test.js | 4 + .../confirm-send-ether.component.js | 5 +- .../confirm-send-ether.container.js | 4 +- .../confirm-transaction.component.js | 28 +- .../confirm-transaction.container.js | 2 - .../amount-max-button.test.js | 22 +- ui/pages/send/send.test.js | 24 +- ui/selectors/custom-gas.js | 139 ++++---- ui/selectors/custom-gas.test.js | 312 +++++++++++------- 22 files changed, 525 insertions(+), 726 deletions(-) diff --git a/app/scripts/controllers/network/network.js b/app/scripts/controllers/network/network.js index 2056cc154..f9b4f52a9 100644 --- a/app/scripts/controllers/network/network.js +++ b/app/scripts/controllers/network/network.js @@ -162,6 +162,9 @@ export default class NetworkController extends EventEmitter { */ async getEIP1559Compatibility() { const { EIPS } = this.networkDetails.getState(); + if (process.env.SHOW_EIP_1559_UI === false) { + return false; + } if (EIPS[1559] !== undefined) { return EIPS[1559]; } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b45a1a613..1f9aeca20 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -196,11 +196,17 @@ export default class MetamaskController extends EventEmitter { getCurrentAccountEIP1559Compatibility: this.getCurrentAccountEIP1559Compatibility.bind( this, ), - getCurrentNetworkLegacyGasAPICompatibility: () => - this.networkController.getCurrentChainId() === MAINNET_CHAIN_ID, - getChainId: this.networkController.getCurrentChainId.bind( - this.networkController, - ), + legacyAPIEndpoint: `https://gas-api.metaswap.codefi.network/networks//gasPrices`, + EIP1559APIEndpoint: `https://gas-api.metaswap.codefi.network/networks//suggestedGasFees`, + getCurrentNetworkLegacyGasAPICompatibility: () => { + const chainId = this.networkController.getCurrentChainId(); + return process.env.IN_TEST || chainId === MAINNET_CHAIN_ID; + }, + getChainId: () => { + return process.env.IN_TEST + ? MAINNET_CHAIN_ID + : this.networkController.getCurrentChainId(); + }, }); this.appStateController = new AppStateController({ diff --git a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-component.test.js b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-component.test.js index d88a3ab98..5b8f5c665 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-component.test.js +++ b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container-component.test.js @@ -1,22 +1,23 @@ import React from 'react'; import sinon from 'sinon'; import { shallowWithContext } from '../../../../../test/lib/render-helpers'; +import { getGasFeeEstimatesAndStartPolling } from '../../../../store/actions'; import PageContainer from '../../../ui/page-container'; import { Tab } from '../../../ui/tabs'; import GasModalPageContainer from './gas-modal-page-container.component'; -const mockBasicGasEstimates = { - average: '20', -}; +jest.mock('../../../../store/actions', () => ({ + disconnectGasFeeEstimatePoller: jest.fn(), + getGasFeeEstimatesAndStartPolling: jest + .fn() + .mockImplementation(() => Promise.resolve()), +})); const propsMethodSpies = { cancelAndClose: sinon.spy(), onSubmit: sinon.spy(), - fetchBasicGasEstimates: sinon - .stub() - .returns(Promise.resolve(mockBasicGasEstimates)), }; const mockGasPriceButtonGroupProps = { @@ -67,7 +68,6 @@ describe('GasModalPageContainer Component', () => { 'mockupdateCustomGasPrice'} updateCustomGasLimit={() => 'mockupdateCustomGasLimit'} gasPriceButtonGroupProps={mockGasPriceButtonGroupProps} @@ -83,18 +83,15 @@ describe('GasModalPageContainer Component', () => { afterEach(() => { propsMethodSpies.cancelAndClose.resetHistory(); + jest.clearAllMocks(); }); describe('componentDidMount', () => { - it('should call props.fetchBasicGasEstimates', () => { - propsMethodSpies.fetchBasicGasEstimates.resetHistory(); - expect(propsMethodSpies.fetchBasicGasEstimates.callCount).toStrictEqual( - 0, - ); + it('should call getGasFeeEstimatesAndStartPolling', () => { + jest.clearAllMocks(); + expect(getGasFeeEstimatesAndStartPolling).not.toHaveBeenCalled(); wrapper.instance().componentDidMount(); - expect(propsMethodSpies.fetchBasicGasEstimates.callCount).toStrictEqual( - 1, - ); + expect(getGasFeeEstimatesAndStartPolling).toHaveBeenCalled(); }); }); @@ -120,20 +117,18 @@ describe('GasModalPageContainer Component', () => { }); it('should pass the correct renderTabs property to PageContainer', () => { - sinon.stub(GP, 'renderTabs').returns('mockTabs'); + jest + .spyOn(GasModalPageContainer.prototype, 'renderTabs') + .mockImplementation(() => 'mockTabs'); const renderTabsWrapperTester = shallowWithContext( - , + , { context: { t: (str1, str2) => (str2 ? str1 + str2 : str1) } }, ); const { tabsComponent } = renderTabsWrapperTester .find(PageContainer) .props(); expect(tabsComponent).toStrictEqual('mockTabs'); - GasModalPageContainer.prototype.renderTabs.restore(); + GasModalPageContainer.prototype.renderTabs.mockClear(); }); }); @@ -195,7 +190,6 @@ describe('GasModalPageContainer Component', () => { 'mockupdateCustomGasPrice'} updateCustomGasLimit={() => 'mockupdateCustomGasLimit'} gasPriceButtonGroupProps={mockGasPriceButtonGroupProps} diff --git a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js index ea59b7882..aaa28a056 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js +++ b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.component.js @@ -2,6 +2,10 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import PageContainer from '../../../ui/page-container'; import { Tabs, Tab } from '../../../ui/tabs'; +import { + disconnectGasFeeEstimatePoller, + getGasFeeEstimatesAndStartPolling, +} from '../../../../store/actions'; import AdvancedTabContent from './advanced-tab-content'; import BasicTabContent from './basic-tab-content'; @@ -17,7 +21,6 @@ export default class GasModalPageContainer extends Component { updateCustomGasPrice: PropTypes.func, updateCustomGasLimit: PropTypes.func, insufficientBalance: PropTypes.bool, - fetchBasicGasEstimates: PropTypes.func, gasPriceButtonGroupProps: PropTypes.object, infoRowProps: PropTypes.shape({ originalTotalFiat: PropTypes.string, @@ -38,8 +41,29 @@ export default class GasModalPageContainer extends Component { customPriceIsExcessive: PropTypes.bool.isRequired, }; + constructor(props) { + super(props); + this.state = { + pollingToken: undefined, + }; + } + componentDidMount() { - this.props.fetchBasicGasEstimates(); + this._isMounted = true; + getGasFeeEstimatesAndStartPolling().then((pollingToken) => { + if (this._isMounted) { + this.setState({ pollingToken }); + } else { + disconnectGasFeeEstimatePoller(pollingToken); + } + }); + } + + componentWillUnmount() { + this._isMounted = false; + if (this.state.pollingToken) { + disconnectGasFeeEstimatePoller(this.state.pollingToken); + } } renderBasicTabContent(gasPriceButtonGroupProps) { diff --git a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js index a1a262589..571544cf0 100644 --- a/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js +++ b/ui/components/app/gas-customization/gas-modal-page-container/gas-modal-page-container.container.js @@ -10,7 +10,6 @@ import { setCustomGasPrice, setCustomGasLimit, resetCustomData, - fetchBasicGasEstimates, } from '../../../../ducks/gas/gas.duck'; import { getSendMaxModeState, @@ -225,7 +224,6 @@ const mapDispatchToProps = (dispatch) => { return dispatch(createSpeedUpTransaction(txId, customGasSettings)); }, hideSidebar: () => dispatch(hideSidebar()), - fetchBasicGasEstimates: () => dispatch(fetchBasicGasEstimates()), }; }; diff --git a/ui/ducks/gas/gas-action-constants.js b/ui/ducks/gas/gas-action-constants.js index 19cb16ee7..18d1d8dc8 100644 --- a/ui/ducks/gas/gas-action-constants.js +++ b/ui/ducks/gas/gas-action-constants.js @@ -4,11 +4,6 @@ // untangling is having the constants separate. // Actions -export const BASIC_GAS_ESTIMATE_STATUS = - 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS'; export const RESET_CUSTOM_DATA = 'metamask/gas/RESET_CUSTOM_DATA'; -export const SET_BASIC_GAS_ESTIMATE_DATA = - 'metamask/gas/SET_BASIC_GAS_ESTIMATE_DATA'; export const SET_CUSTOM_GAS_LIMIT = 'metamask/gas/SET_CUSTOM_GAS_LIMIT'; export const SET_CUSTOM_GAS_PRICE = 'metamask/gas/SET_CUSTOM_GAS_PRICE'; -export const SET_ESTIMATE_SOURCE = 'metamask/gas/SET_ESTIMATE_SOURCE'; diff --git a/ui/ducks/gas/gas-duck.test.js b/ui/ducks/gas/gas-duck.test.js index 221e4dbd8..f5998012a 100644 --- a/ui/ducks/gas/gas-duck.test.js +++ b/ui/ducks/gas/gas-duck.test.js @@ -1,36 +1,14 @@ -import nock from 'nock'; import sinon from 'sinon'; -import BN from 'bn.js'; -import GasReducer, { - setBasicEstimateStatus, - setBasicGasEstimateData, - setCustomGasPrice, - setCustomGasLimit, - fetchBasicGasEstimates, -} from './gas.duck'; +import GasReducer, { setCustomGasPrice, setCustomGasLimit } from './gas.duck'; import { - BASIC_GAS_ESTIMATE_STATUS, - SET_BASIC_GAS_ESTIMATE_DATA, SET_CUSTOM_GAS_PRICE, SET_CUSTOM_GAS_LIMIT, - SET_ESTIMATE_SOURCE, } from './gas-action-constants'; -jest.mock('../../helpers/utils/storage-helpers.js', () => ({ - getStorageItem: jest.fn(), - setStorageItem: jest.fn(), -})); - describe('Gas Duck', () => { let tempDateNow; - const mockGasPriceApiResponse = { - SafeGasPrice: 10, - ProposeGasPrice: 20, - FastGasPrice: 30, - }; - beforeEach(() => { tempDateNow = global.Date.now; @@ -51,22 +29,6 @@ describe('Gas Duck', () => { price: null, limit: null, }, - basicEstimates: { - average: null, - fast: null, - safeLow: null, - }, - basicEstimateStatus: 'LOADING', - estimateSource: '', - }; - - const providerState = { - chainId: '0x1', - nickname: '', - rpcPrefs: {}, - rpcUrl: '', - ticker: 'ETH', - type: 'mainnet', }; describe('GasReducer()', () => { @@ -83,27 +45,6 @@ describe('Gas Duck', () => { ).toStrictEqual(mockState); }); - it('should set basicEstimateStatus to LOADING when receiving a BASIC_GAS_ESTIMATE_STATUS action with value LOADING', () => { - expect( - GasReducer(mockState, { - type: BASIC_GAS_ESTIMATE_STATUS, - value: 'LOADING', - }), - ).toStrictEqual({ basicEstimateStatus: 'LOADING', ...mockState }); - }); - - it('should set basicEstimates when receiving a SET_BASIC_GAS_ESTIMATE_DATA action', () => { - expect( - GasReducer(mockState, { - type: SET_BASIC_GAS_ESTIMATE_DATA, - value: { someProp: 'someData123' }, - }), - ).toStrictEqual({ - basicEstimates: { someProp: 'someData123' }, - ...mockState, - }); - }); - it('should set customData.price when receiving a SET_CUSTOM_GAS_PRICE action', () => { expect( GasReducer(mockState, { @@ -123,100 +64,6 @@ describe('Gas Duck', () => { }); }); - it('should set estimateSource to Metaswaps when receiving a SET_ESTIMATE_SOURCE action with value Metaswaps', () => { - expect( - GasReducer(mockState, { type: SET_ESTIMATE_SOURCE, value: 'Metaswaps' }), - ).toStrictEqual({ estimateSource: 'Metaswaps', ...mockState }); - }); - - describe('basicEstimateStatus', () => { - it('should create the correct action', () => { - expect(setBasicEstimateStatus('LOADING')).toStrictEqual({ - type: BASIC_GAS_ESTIMATE_STATUS, - value: 'LOADING', - }); - }); - }); - - describe('fetchBasicGasEstimates', () => { - it('should call fetch with the expected params', async () => { - const mockDistpatch = sinon.spy(); - const windowFetchSpy = sinon.spy(window, 'fetch'); - - nock('https://api.metaswap.codefi.network') - .get('/gasPrices') - .reply(200, mockGasPriceApiResponse); - - await fetchBasicGasEstimates()(mockDistpatch, () => ({ - gas: { ...initState }, - metamask: { provider: { ...providerState } }, - })); - - expect(mockDistpatch.getCall(0).args).toStrictEqual([ - { type: 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS', value: 'LOADING' }, - ]); - - expect( - windowFetchSpy - .getCall(0) - .args[0].startsWith('https://api.metaswap.codefi.network/gasPrices'), - ).toStrictEqual(true); - - expect(mockDistpatch.getCall(2).args).toStrictEqual([ - { type: 'metamask/gas/SET_ESTIMATE_SOURCE', value: 'MetaSwaps' }, - ]); - - expect(mockDistpatch.getCall(4).args).toStrictEqual([ - { type: 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS', value: 'READY' }, - ]); - }); - - it('should call fetch with the expected params for test network', async () => { - global.eth = { gasPrice: sinon.fake.returns(new BN(48199313, 10)) }; - - const mockDistpatch = sinon.spy(); - const providerStateForTestNetwork = { - chainId: '0x5', - nickname: '', - rpcPrefs: {}, - rpcUrl: '', - ticker: 'ETH', - type: 'goerli', - }; - - await fetchBasicGasEstimates()(mockDistpatch, () => ({ - gas: { ...initState, basicPriceAEstimatesLastRetrieved: 1000000 }, - metamask: { provider: { ...providerStateForTestNetwork } }, - })); - expect(mockDistpatch.getCall(0).args).toStrictEqual([ - { type: 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS', value: 'LOADING' }, - ]); - expect(mockDistpatch.getCall(1).args).toStrictEqual([ - { type: 'metamask/gas/SET_ESTIMATE_SOURCE', value: 'eth_gasprice' }, - ]); - expect(mockDistpatch.getCall(2).args).toStrictEqual([ - { - type: SET_BASIC_GAS_ESTIMATE_DATA, - value: { - average: 0.0482, - }, - }, - ]); - expect(mockDistpatch.getCall(3).args).toStrictEqual([ - { type: 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS', value: 'READY' }, - ]); - }); - }); - - describe('setBasicGasEstimateData', () => { - it('should create the correct action', () => { - expect(setBasicGasEstimateData('mockBasicEstimatData')).toStrictEqual({ - type: SET_BASIC_GAS_ESTIMATE_DATA, - value: 'mockBasicEstimatData', - }); - }); - }); - describe('setCustomGasPrice', () => { it('should create the correct action', () => { expect(setCustomGasPrice('mockCustomGasPrice')).toStrictEqual({ diff --git a/ui/ducks/gas/gas.duck.js b/ui/ducks/gas/gas.duck.js index a41c313c5..ea045a2c4 100644 --- a/ui/ducks/gas/gas.duck.js +++ b/ui/ducks/gas/gas.duck.js @@ -1,62 +1,20 @@ import { cloneDeep } from 'lodash'; -import BigNumber from 'bignumber.js'; import { - getStorageItem, - setStorageItem, -} from '../../helpers/utils/storage-helpers'; -import { - decGWEIToHexWEI, - getValueFromWeiHex, -} from '../../helpers/utils/conversions.util'; -import { getIsMainnet, getCurrentChainId } from '../../selectors'; -import fetchWithCache from '../../helpers/utils/fetch-with-cache'; -import { - BASIC_GAS_ESTIMATE_STATUS, RESET_CUSTOM_DATA, - SET_BASIC_GAS_ESTIMATE_DATA, SET_CUSTOM_GAS_LIMIT, SET_CUSTOM_GAS_PRICE, - SET_ESTIMATE_SOURCE, } from './gas-action-constants'; -export const BASIC_ESTIMATE_STATES = { - LOADING: 'LOADING', - FAILED: 'FAILED', - READY: 'READY', -}; - -export const GAS_SOURCE = { - METASWAPS: 'MetaSwaps', - ETHGASPRICE: 'eth_gasprice', -}; - const initState = { customData: { price: null, limit: null, }, - basicEstimates: { - safeLow: null, - average: null, - fast: null, - }, - basicEstimateStatus: BASIC_ESTIMATE_STATES.LOADING, - estimateSource: '', }; // Reducer export default function reducer(state = initState, action) { switch (action.type) { - case BASIC_GAS_ESTIMATE_STATUS: - return { - ...state, - basicEstimateStatus: action.value, - }; - case SET_BASIC_GAS_ESTIMATE_DATA: - return { - ...state, - basicEstimates: action.value, - }; case SET_CUSTOM_GAS_PRICE: return { ...state, @@ -78,138 +36,11 @@ export default function reducer(state = initState, action) { ...state, customData: cloneDeep(initState.customData), }; - case SET_ESTIMATE_SOURCE: - return { - ...state, - estimateSource: action.value, - }; default: return state; } } -// Action Creators -export function setBasicEstimateStatus(status) { - return { - type: BASIC_GAS_ESTIMATE_STATUS, - value: status, - }; -} - -async function basicGasPriceQuery() { - const url = `https://api.metaswap.codefi.network/gasPrices`; - return await fetchWithCache( - url, - { - referrer: url, - referrerPolicy: 'no-referrer-when-downgrade', - method: 'GET', - mode: 'cors', - }, - { cacheRefreshTime: 75000 }, - ); -} - -export function fetchBasicGasEstimates() { - return async (dispatch, getState) => { - const isMainnet = getIsMainnet(getState()); - - dispatch(setBasicEstimateStatus(BASIC_ESTIMATE_STATES.LOADING)); - let basicEstimates; - try { - dispatch(setEstimateSource(GAS_SOURCE.ETHGASPRICE)); - if (isMainnet || process.env.IN_TEST) { - try { - basicEstimates = await fetchExternalBasicGasEstimates(); - dispatch(setEstimateSource(GAS_SOURCE.METASWAPS)); - } catch (error) { - basicEstimates = await fetchEthGasPriceEstimates(getState()); - } - } else { - basicEstimates = await fetchEthGasPriceEstimates(getState()); - } - dispatch(setBasicGasEstimateData(basicEstimates)); - dispatch(setBasicEstimateStatus(BASIC_ESTIMATE_STATES.READY)); - } catch (error) { - dispatch(setBasicEstimateStatus(BASIC_ESTIMATE_STATES.FAILED)); - } - - return basicEstimates; - }; -} - -async function fetchExternalBasicGasEstimates() { - const { - SafeGasPrice, - ProposeGasPrice, - FastGasPrice, - } = await basicGasPriceQuery(); - - const [safeLow, average, fast] = [ - SafeGasPrice, - ProposeGasPrice, - FastGasPrice, - ].map((price) => new BigNumber(price, 10).toNumber()); - - const basicEstimates = { - safeLow, - average, - fast, - }; - - return basicEstimates; -} - -async function fetchEthGasPriceEstimates(state) { - const chainId = getCurrentChainId(state); - const [cachedTimeLastRetrieved, cachedBasicEstimates] = await Promise.all([ - getStorageItem(`${chainId}_BASIC_PRICE_ESTIMATES_LAST_RETRIEVED`), - getStorageItem(`${chainId}_BASIC_PRICE_ESTIMATES`), - ]); - const timeLastRetrieved = cachedTimeLastRetrieved || 0; - if (cachedBasicEstimates && Date.now() - timeLastRetrieved < 75000) { - return cachedBasicEstimates; - } - const gasPrice = await global.eth.gasPrice(); - const averageGasPriceInDecGWEI = getValueFromWeiHex({ - value: gasPrice.toString(16), - numberOfDecimals: 4, - toDenomination: 'GWEI', - }); - const basicEstimates = { - average: Number(averageGasPriceInDecGWEI), - }; - const timeRetrieved = Date.now(); - - await Promise.all([ - setStorageItem(`${chainId}_BASIC_PRICE_ESTIMATES`, basicEstimates), - setStorageItem( - `${chainId}_BASIC_PRICE_ESTIMATES_LAST_RETRIEVED`, - timeRetrieved, - ), - ]); - - return basicEstimates; -} - -export function setCustomGasPriceForRetry(newPrice) { - return async (dispatch) => { - if (newPrice === '0x0') { - const { fast } = await fetchExternalBasicGasEstimates(); - dispatch(setCustomGasPrice(decGWEIToHexWEI(fast))); - } else { - dispatch(setCustomGasPrice(newPrice)); - } - }; -} - -export function setBasicGasEstimateData(basicGasEstimateData) { - return { - type: SET_BASIC_GAS_ESTIMATE_DATA, - value: basicGasEstimateData, - }; -} - export function setCustomGasPrice(newPrice) { return { type: SET_CUSTOM_GAS_PRICE, @@ -227,10 +58,3 @@ export function setCustomGasLimit(newLimit) { export function resetCustomData() { return { type: RESET_CUSTOM_DATA }; } - -export function setEstimateSource(estimateSource) { - return { - type: SET_ESTIMATE_SOURCE, - value: estimateSource, - }; -} diff --git a/ui/ducks/send/send.js b/ui/ducks/send/send.js index 238230986..675117d45 100644 --- a/ui/ducks/send/send.js +++ b/ui/ducks/send/send.js @@ -50,15 +50,7 @@ import { updateTokenType, updateTransaction, } from '../../store/actions'; -import { - fetchBasicGasEstimates, - setCustomGasLimit, - BASIC_ESTIMATE_STATES, -} from '../gas/gas.duck'; -import { - SET_BASIC_GAS_ESTIMATE_DATA, - BASIC_GAS_ESTIMATE_STATUS, -} from '../gas/gas-action-constants'; +import { setCustomGasLimit } from '../gas/gas.duck'; import { QR_CODE_DETECTED, SELECTED_ACCOUNT_CHANGED, @@ -77,13 +69,18 @@ import { isOriginContractAddress, isValidDomainName, } from '../../helpers/utils/util'; -import { getTokens, getUnapprovedTxs } from '../metamask/metamask'; +import { + getGasEstimateType, + getTokens, + getUnapprovedTxs, +} from '../metamask/metamask'; import { resetEnsResolution } from '../ens'; import { isBurnAddress, isValidHexAddress, } from '../../../shared/modules/hexstring-utils'; import { CHAIN_ID_TO_GAS_LIMIT_BUFFER_MAP } from '../../../shared/constants/network'; +import { ETH, GWEI } from '../../helpers/constants/common'; // typedefs /** @@ -186,8 +183,19 @@ async function estimateGasLimitForSend({ 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. - const blockGasLimit = options.blockGasLimit || MIN_GAS_LIMIT_HEX; + // 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. @@ -342,7 +350,7 @@ export const computeEstimatedGasLimit = createAsyncThunk( if (send.stage !== SEND_STAGES.EDIT) { const gasLimit = await estimateGasLimitForSend({ gasPrice: send.gas.gasPrice, - blockGasLimit: metamask.blockGasLimit, + blockGasLimit: metamask.currentBlockGasLimit, selectedAddress: metamask.selectedAddress, sendToken: send.asset.details, to: send.recipient.address?.toLowerCase(), @@ -360,6 +368,29 @@ export const computeEstimatedGasLimit = createAsyncThunk( }, ); +/** + * 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 {T} gasPriceEstimate + * @returns + */ +function getRoundedGasPrice(gasPriceEstimate) { + const gasPriceInDecGwei = conversionUtil(gasPriceEstimate, { + numberOfDecimals: 4, + toDenomination: GWEI, + fromNumericBase: 'dec', + toNumericBase: 'dec', + fromCurrency: ETH, + fromDenomination: GWEI, + }); + const gasPriceAsNumber = Number(gasPriceInDecGwei); + return getGasPriceInHexWei(gasPriceAsNumber); +} + /** * Responsible for initializing required state for the send slice. * This method is dispatched from the send page in the componentDidMount @@ -395,48 +426,31 @@ export const initializeSendState = createAsyncThunk( // Default gasPrice to 1 gwei if all estimation fails let gasPrice = '0x1'; - let basicEstimateStatus = BASIC_ESTIMATE_STATES.LOADING; let gasEstimatePollToken = null; - if (Boolean(process.env.SHOW_EIP_1559_UI) === false) { - // Initiate gas slices work to fetch gasPrice estimates. We need to get the - // new state after this is set to determine if initialization can proceed. - await thunkApi.dispatch(fetchBasicGasEstimates()); - const { - gas: { basicEstimates, basicEstimateStatus: apiBasicEstimateStatus }, - } = thunkApi.getState(); + // Instruct the background process that polling for gas prices should begin + gasEstimatePollToken = await getGasFeeEstimatesAndStartPolling(); + const { + metamask: { gasFeeEstimates, gasEstimateType }, + } = thunkApi.getState(); - basicEstimateStatus = apiBasicEstimateStatus; - - if (basicEstimateStatus === BASIC_ESTIMATE_STATES.READY) { - gasPrice = getGasPriceInHexWei(basicEstimates.average); - } - } else { - // Instruct the background process that polling for gas prices should begin - gasEstimatePollToken = await getGasFeeEstimatesAndStartPolling(); - const { - metamask: { gasFeeEstimates, gasEstimateType }, - } = thunkApi.getState(); - - if (gasEstimateType === GAS_ESTIMATE_TYPES.LEGACY) { - gasPrice = getGasPriceInHexWei(gasFeeEstimates.medium); - } else if (gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE) { - gasPrice = getGasPriceInHexWei(gasFeeEstimates.gasPrice); - } else if (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) { - gasPrice = getGasPriceInHexWei( - gasFeeEstimates.medium.suggestedMaxFeePerGas, - ); - } - - basicEstimateStatus = BASIC_ESTIMATE_STATES.READY; + if (gasEstimateType === GAS_ESTIMATE_TYPES.LEGACY) { + gasPrice = getGasPriceInHexWei(gasFeeEstimates.medium); + } else if (gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE) { + gasPrice = getRoundedGasPrice(gasFeeEstimates.gasPrice); + } else if (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) { + gasPrice = getGasPriceInHexWei( + gasFeeEstimates.medium.suggestedMaxFeePerGas, + ); } + // Set a basic gasLimit in the event that other estimation fails let gasLimit = asset.type === ASSET_TYPES.TOKEN ? GAS_LIMITS.BASE_TOKEN_ESTIMATE : GAS_LIMITS.SIMPLE; if ( - basicEstimateStatus === BASIC_ESTIMATE_STATES.READY && + gasEstimateType !== GAS_ESTIMATE_TYPES.NONE && stage !== SEND_STAGES.EDIT && recipient.address ) { @@ -444,7 +458,7 @@ export const initializeSendState = createAsyncThunk( // required gas. If this value isn't nullish, set it as the new gasLimit const estimatedGasLimit = await estimateGasLimitForSend({ gasPrice, - blockGasLimit: metamask.blockGasLimit, + blockGasLimit: metamask.currentBlockGasLimit, selectedAddress: fromAddress, sendToken: asset.details, to: recipient.address.toLowerCase(), @@ -508,9 +522,12 @@ export const initialState = { isCustomGasSet: false, // maximum gas needed for tx gasLimit: '0x0', - // price in gwei to pay per gas + // price in wei to pay per gas gasPrice: '0x0', - // maximum total price in gwei to pay + // expected price in wei necessary to pay per gas used for a transaction + // to be included in a reasonable timeframe. Comes from GasFeeController. + gasPriceEstimate: '0x0', + // maximum total price in wei to pay gasTotal: '0x0', // minimum supported gasLimit minimumGasLimit: GAS_LIMITS.SIMPLE, @@ -761,7 +778,9 @@ const slice = createSlice({ // We keep a copy of txParams in state that could be submitted to the // network if the form state is valid. if (state.status === SEND_STATUSES.VALID) { - state.draftTransaction.txParams.from = state.account.address; + if (state.stage !== SEND_STAGES.EDIT) { + state.draftTransaction.txParams.from = state.account.address; + } switch (state.asset.type) { case ASSET_TYPES.TOKEN: // When sending a token the to address is the contract address of @@ -926,7 +945,7 @@ const slice = createSlice({ break; case state.asset.type === ASSET_TYPES.TOKEN && state.asset.details.isERC721 === true: - state.state = SEND_STATUSES.INVALID; + state.status = SEND_STATUSES.INVALID; break; default: state.status = SEND_STATUSES.VALID; @@ -1063,58 +1082,36 @@ const slice = createSlice({ }); } }) - .addCase(SET_BASIC_GAS_ESTIMATE_DATA, (state, action) => { - // When we receive a new gasPrice via the gas duck we need to update - // the gasPrice in our slice. We call into the caseReducer - // updateGasPrice to also tap into the appropriate follow up checks - // and gasTotal calculation. - if (Boolean(process.env.SHOW_EIP_1559_UI) === false) { - slice.caseReducers.updateGasPrice(state, { - payload: getGasPriceInHexWei(action.value.average), - }); - } - }) - .addCase(BASIC_GAS_ESTIMATE_STATUS, (state, action) => { - // When we fetch gas prices we should temporarily set the form invalid - // Once the price updates we get that value in the - // SET_BASIC_GAS_ESTIMATE_DATA extraReducer above. Finally as long as - // the state is 'READY' we will revalidate the form. - switch (action.value) { - case BASIC_ESTIMATE_STATES.FAILED: - state.status = SEND_STATUSES.INVALID; - state.gas.isGasEstimateLoading = true; - break; - case BASIC_ESTIMATE_STATES.LOADING: - state.status = SEND_STATUSES.INVALID; - state.gas.isGasEstimateLoading = true; - break; - case BASIC_ESTIMATE_STATES.READY: - default: - state.gas.isGasEstimateLoading = false; - slice.caseReducers.validateSendState(state); - } - }) .addCase(GAS_FEE_ESTIMATES_UPDATED, (state, action) => { // When the gasFeeController updates its gas fee estimates we need to // update and validate state based on those new values - if (process.env.SHOW_EIP_1559_UI) { - const { gasFeeEstimates, gasEstimateType } = action.payload; - let payload = null; - if (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) { - payload = getGasPriceInHexWei( - gasFeeEstimates.medium.suggestedMaxFeePerGas, - ); - } else if (gasEstimateType === GAS_ESTIMATE_TYPES.LEGACY) { - payload = getGasPriceInHexWei(gasFeeEstimates.medium); - } else if (gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE) { - payload = getGasPriceInHexWei(gasFeeEstimates.gasPrice); - } - if (payload) { - slice.caseReducers.updateGasPrice(state, { - payload, - }); - } + const { gasFeeEstimates, gasEstimateType } = action.payload; + let payload = null; + if (gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET) { + payload = getGasPriceInHexWei( + gasFeeEstimates.medium.suggestedMaxFeePerGas, + ); + } else if (gasEstimateType === GAS_ESTIMATE_TYPES.LEGACY) { + payload = getGasPriceInHexWei(gasFeeEstimates.medium); + } else if (gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE) { + payload = getRoundedGasPrice(gasFeeEstimates.gasPrice); } + // If a new gasPrice can be derived, and either the gasPriceEstimate + // was '0x0' or the gasPrice selected matches the previous estimate, + // update the gasPrice. This will ensure that we only update the + // gasPrice if the user is using our previous estimated value. + if ( + payload && + (state.gas.gasPriceEstimate === '0x0' || + state.gas.gasPrice === state.gas.gasPriceEstimate) + ) { + slice.caseReducers.updateGasPrice(state, { + payload, + }); + } + + // Record the latest gasPriceEstimate for future comparisons + state.gas.gasPriceEstimate = payload ?? state.gas.gasPriceEstimate; }); }, }); @@ -1487,6 +1484,7 @@ export function getMinimumGasLimitForSend(state) { export function getGasInputMode(state) { const isMainnet = getIsMainnet(state); + const gasEstimateType = getGasEstimateType(state); const showAdvancedGasFields = getAdvancedInlineGasShown(state); if (state[name].gas.isCustomGasSet) { return GAS_INPUT_MODES.CUSTOM; @@ -1494,6 +1492,16 @@ export function getGasInputMode(state) { if ((!isMainnet && !process.env.IN_TEST) || showAdvancedGasFields) { return GAS_INPUT_MODES.INLINE; } + + // We get eth_gasPrice estimation if the legacy API fails but we need to + // instruct the UI to render the INLINE inputs in this case, only on + // mainnet or IN_TEST. + if ( + (isMainnet || process.env.IN_TEST) && + gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE + ) { + return GAS_INPUT_MODES.INLINE; + } return GAS_INPUT_MODES.BASIC; } diff --git a/ui/ducks/send/send.test.js b/ui/ducks/send/send.test.js index 7038b11e8..76267f393 100644 --- a/ui/ducks/send/send.test.js +++ b/ui/ducks/send/send.test.js @@ -10,9 +10,8 @@ import { KNOWN_RECIPIENT_ADDRESS_WARNING, NEGATIVE_ETH_ERROR, } from '../../pages/send/send.constants'; -import { BASIC_ESTIMATE_STATES } from '../gas/gas.duck'; import { RINKEBY_CHAIN_ID } from '../../../shared/constants/network'; -import { GAS_LIMITS } from '../../../shared/constants/gas'; +import { GAS_ESTIMATE_TYPES, GAS_LIMITS } from '../../../shared/constants/gas'; import { TRANSACTION_TYPES } from '../../../shared/constants/transaction'; import sendReducer, { initialState, @@ -953,6 +952,11 @@ describe('Send Slice', () => { it('should dispatch async action thunk first with pending, then finally fulfilling from minimal state', async () => { getState = jest.fn().mockReturnValue({ metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.NONE, + gasFeeEstimates: {}, + networkDetails: { + EIPS: {}, + }, accounts: { '0xAddress': { address: '0xAddress', @@ -970,6 +974,7 @@ describe('Send Slice', () => { }, }, send: initialState, + gas: { basicEstimateStatus: 'LOADING', basicEstimatesStatus: { @@ -983,12 +988,12 @@ describe('Send Slice', () => { const action = initializeSendState(); await action(dispatchSpy, getState, undefined); - expect(dispatchSpy).toHaveBeenCalledTimes(4); + expect(dispatchSpy).toHaveBeenCalledTimes(3); expect(dispatchSpy.mock.calls[0][0].type).toStrictEqual( 'send/initializeSendState/pending', ); - expect(dispatchSpy.mock.calls[3][0].type).toStrictEqual( + expect(dispatchSpy.mock.calls[2][0].type).toStrictEqual( 'send/initializeSendState/fulfilled', ); }); @@ -1000,6 +1005,7 @@ describe('Send Slice', () => { ...initialState, gas: { gasPrice: '0x0', + gasPriceEstimate: '0x0', gasLimit: '0x5208', gasTotal: '0x0', minimumGasLimit: '0x5208', @@ -1007,9 +1013,12 @@ describe('Send Slice', () => { }; const action = { - type: 'metamask/gas/SET_BASIC_GAS_ESTIMATE_DATA', - value: { - average: '1', + type: 'GAS_FEE_ESTIMATES_UPDATED', + payload: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + medium: '1', + }, }, }; @@ -1020,40 +1029,6 @@ describe('Send Slice', () => { expect(result.gas.gasTotal).toStrictEqual('0x1319718a5000'); }); }); - - describe('BASIC_GAS_ESTIMATE_STATUS', () => { - it('should invalidate the send status when status is LOADING', () => { - const validSendStatusState = { - ...initialState, - status: SEND_STATUSES.VALID, - }; - - const action = { - type: 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS', - value: BASIC_ESTIMATE_STATES.LOADING, - }; - - const result = sendReducer(validSendStatusState, action); - - expect(result.status).not.toStrictEqual(validSendStatusState.status); - }); - - it('should invalidate the send status when status is FAILED and use INLINE gas input mode', () => { - const validSendStatusState = { - ...initialState, - status: SEND_STATUSES.VALID, - }; - - const action = { - type: 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS', - value: BASIC_ESTIMATE_STATES.FAILED, - }; - - const result = sendReducer(validSendStatusState, action); - - expect(result.status).not.toStrictEqual(validSendStatusState.status); - }); - }); }); describe('Action Creators', () => { diff --git a/ui/hooks/useCancelTransaction.js b/ui/hooks/useCancelTransaction.js index e0ee0cd4d..9bd19cc87 100644 --- a/ui/hooks/useCancelTransaction.js +++ b/ui/hooks/useCancelTransaction.js @@ -5,10 +5,7 @@ import { isBalanceSufficient } from '../pages/send/send.utils'; import { getSelectedAccount, getIsMainnet } from '../selectors'; import { getConversionRate } from '../ducks/metamask/metamask'; -import { - setCustomGasLimit, - setCustomGasPriceForRetry, -} from '../ducks/gas/gas.duck'; +import { setCustomGasLimit, setCustomGasPrice } from '../ducks/gas/gas.duck'; import { GAS_LIMITS } from '../../shared/constants/gas'; import { isLegacyTransaction } from '../../shared/modules/transaction.utils'; import { getMaximumGasTotalInHexWei } from '../../shared/modules/gas.utils'; @@ -51,7 +48,7 @@ export function useCancelTransaction(transactionGroup) { // To support the current process of cancelling or speeding up // a transaction, we have to inform the custom gas state of the new // gasPrice/gasLimit to start at. - dispatch(setCustomGasPriceForRetry(customGasSettings.gasPrice)); + dispatch(setCustomGasPrice(customGasSettings.gasPrice)); dispatch(setCustomGasLimit(GAS_LIMITS.SIMPLE)); } const tx = { diff --git a/ui/hooks/useGasFeeEstimates.js b/ui/hooks/useGasFeeEstimates.js index 82ae44f7b..ec378b134 100644 --- a/ui/hooks/useGasFeeEstimates.js +++ b/ui/hooks/useGasFeeEstimates.js @@ -44,11 +44,17 @@ export function useGasFeeEstimates() { const gasFeeEstimates = useSelector(getGasFeeEstimates); const estimatedGasFeeTimeBounds = useSelector(getEstimatedGasFeeTimeBounds); useEffect(() => { + let active = true; let pollToken; getGasFeeEstimatesAndStartPolling().then((newPollToken) => { - pollToken = newPollToken; + if (active) { + pollToken = newPollToken; + } else { + disconnectGasFeeEstimatePoller(newPollToken); + } }); return () => { + active = false; if (pollToken) { disconnectGasFeeEstimatePoller(pollToken); } diff --git a/ui/hooks/useRetryTransaction.js b/ui/hooks/useRetryTransaction.js index f19c1000a..9f6b1680c 100644 --- a/ui/hooks/useRetryTransaction.js +++ b/ui/hooks/useRetryTransaction.js @@ -2,11 +2,7 @@ import { useDispatch, useSelector } from 'react-redux'; import { useCallback, useState } from 'react'; import { showSidebar } from '../store/actions'; -import { - fetchBasicGasEstimates, - setCustomGasPriceForRetry, - setCustomGasLimit, -} from '../ducks/gas/gas.duck'; +import { setCustomGasLimit, setCustomGasPrice } from '../ducks/gas/gas.duck'; import { getIsMainnet } from '../selectors'; import { isLegacyTransaction } from '../../shared/modules/transaction.utils'; import { useMetricEvent } from './useMetricEvent'; @@ -29,6 +25,7 @@ import { useIncrementedGasFees } from './useIncrementedGasFees'; export function useRetryTransaction(transactionGroup) { const { primaryTransaction } = transactionGroup; const isMainnet = useSelector(getIsMainnet); + const hideBasic = !(isMainnet || process.env.IN_TEST); const customGasSettings = useIncrementedGasFees(transactionGroup); const trackMetricsEvent = useMetricEvent({ @@ -51,12 +48,11 @@ export function useRetryTransaction(transactionGroup) { if (process.env.SHOW_EIP_1559_UI) { setShowRetryEditGasPopover(true); } else { - await dispatch(fetchBasicGasEstimates); if (isLegacyTransaction(primaryTransaction)) { // To support the current process of cancelling or speeding up // a transaction, we have to inform the custom gas state of the new // gasPrice to start at. - dispatch(setCustomGasPriceForRetry(customGasSettings.gasPrice)); + dispatch(setCustomGasPrice(customGasSettings.gasPrice)); dispatch(setCustomGasLimit(primaryTransaction.txParams.gas)); } diff --git a/ui/hooks/useRetryTransaction.test.js b/ui/hooks/useRetryTransaction.test.js index b6b554a75..2f5ec0be9 100644 --- a/ui/hooks/useRetryTransaction.test.js +++ b/ui/hooks/useRetryTransaction.test.js @@ -8,6 +8,10 @@ import * as methodDataHook from './useMethodData'; import * as metricEventHook from './useMetricEvent'; import { useRetryTransaction } from './useRetryTransaction'; +jest.mock('./useGasFeeEstimates', () => ({ + useGasFeeEstimates: jest.fn(), +})); + describe('useRetryTransaction', () => { describe('when transaction meets retry enabled criteria', () => { let useSelector; diff --git a/ui/pages/confirm-send-ether/confirm-send-ether.component.js b/ui/pages/confirm-send-ether/confirm-send-ether.component.js index cbdbebb91..a1166fb03 100644 --- a/ui/pages/confirm-send-ether/confirm-send-ether.component.js +++ b/ui/pages/confirm-send-ether/confirm-send-ether.component.js @@ -16,8 +16,9 @@ export default class ConfirmSendEther extends Component { handleEdit({ txData }) { const { editTransaction, history } = this.props; - editTransaction(txData); - history.push(SEND_ROUTE); + editTransaction(txData).then(() => { + history.push(SEND_ROUTE); + }); } shouldHideData() { diff --git a/ui/pages/confirm-send-ether/confirm-send-ether.container.js b/ui/pages/confirm-send-ether/confirm-send-ether.container.js index 5f7527226..a637fbb12 100644 --- a/ui/pages/confirm-send-ether/confirm-send-ether.container.js +++ b/ui/pages/confirm-send-ether/confirm-send-ether.container.js @@ -17,9 +17,9 @@ const mapStateToProps = (state) => { const mapDispatchToProps = (dispatch) => { return { - editTransaction: (txData) => { + editTransaction: async (txData) => { const { id } = txData; - dispatch(editTransaction(ASSET_TYPES.NATIVE, id.toString())); + await dispatch(editTransaction(ASSET_TYPES.NATIVE, id.toString())); dispatch(clearConfirmTransaction()); }, }; diff --git a/ui/pages/confirm-transaction/confirm-transaction.component.js b/ui/pages/confirm-transaction/confirm-transaction.component.js index 5bc374edc..2988739e6 100644 --- a/ui/pages/confirm-transaction/confirm-transaction.component.js +++ b/ui/pages/confirm-transaction/confirm-transaction.component.js @@ -25,6 +25,10 @@ import { ENCRYPTION_PUBLIC_KEY_REQUEST_PATH, DEFAULT_ROUTE, } from '../../helpers/constants/routes'; +import { + disconnectGasFeeEstimatePoller, + getGasFeeEstimatesAndStartPolling, +} from '../../store/actions'; import ConfTx from './conf-tx'; export default class ConfirmTransaction extends Component { @@ -38,7 +42,6 @@ export default class ConfirmTransaction extends Component { sendTo: PropTypes.string, setTransactionToConfirm: PropTypes.func, clearConfirmTransaction: PropTypes.func, - fetchBasicGasEstimates: PropTypes.func, mostRecentOverviewPage: PropTypes.string.isRequired, transaction: PropTypes.object, getContractMethodData: PropTypes.func, @@ -49,14 +52,19 @@ export default class ConfirmTransaction extends Component { setDefaultHomeActiveTabName: PropTypes.func, }; + constructor(props) { + super(props); + this.state = {}; + } + componentDidMount() { + this._isMounted = true; const { totalUnapprovedCount = 0, sendTo, history, mostRecentOverviewPage, transaction: { txParams: { data, to } = {} } = {}, - fetchBasicGasEstimates, getContractMethodData, transactionId, paramsTransactionId, @@ -64,12 +72,19 @@ export default class ConfirmTransaction extends Component { isTokenMethodAction, } = this.props; + getGasFeeEstimatesAndStartPolling().then((pollingToken) => { + if (this._isMounted) { + this.setState({ pollingToken }); + } else { + disconnectGasFeeEstimatePoller(pollingToken); + } + }); + if (!totalUnapprovedCount && !sendTo) { history.replace(mostRecentOverviewPage); return; } - fetchBasicGasEstimates(); getContractMethodData(data); if (isTokenMethodAction) { getTokenParams(to); @@ -80,6 +95,13 @@ export default class ConfirmTransaction extends Component { } } + componentWillUnmount() { + this._isMounted = false; + if (this.state.pollingToken) { + disconnectGasFeeEstimatePoller(this.state.pollingToken); + } + } + componentDidUpdate(prevProps) { const { setTransactionToConfirm, diff --git a/ui/pages/confirm-transaction/confirm-transaction.container.js b/ui/pages/confirm-transaction/confirm-transaction.container.js index bf0020c64..da7b04f18 100644 --- a/ui/pages/confirm-transaction/confirm-transaction.container.js +++ b/ui/pages/confirm-transaction/confirm-transaction.container.js @@ -6,7 +6,6 @@ import { clearConfirmTransaction, } from '../../ducks/confirm-transaction/confirm-transaction.duck'; import { isTokenMethodAction } from '../../helpers/utils/transactions.util'; -import { fetchBasicGasEstimates } from '../../ducks/gas/gas.duck'; import { getContractMethodData, @@ -54,7 +53,6 @@ const mapDispatchToProps = (dispatch) => { dispatch(setTransactionToConfirm(transactionId)); }, clearConfirmTransaction: () => dispatch(clearConfirmTransaction()), - fetchBasicGasEstimates: () => dispatch(fetchBasicGasEstimates()), getContractMethodData: (data) => dispatch(getContractMethodData(data)), getTokenParams: (tokenAddress) => dispatch(getTokenParams(tokenAddress)), setDefaultHomeActiveTabName: (tabName) => diff --git a/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.test.js b/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.test.js index 7f4482517..a163a13a0 100644 --- a/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.test.js +++ b/ui/pages/send/send-content/send-amount-row/amount-max-button/amount-max-button.test.js @@ -5,6 +5,7 @@ import thunk from 'redux-thunk'; import { fireEvent } from '@testing-library/react'; import { initialState, SEND_STATUSES } from '../../../../../ducks/send'; import { renderWithProvider } from '../../../../../../test/jest'; +import { GAS_ESTIMATE_TYPES } from '../../../../../../shared/constants/gas'; import AmountMaxButton from './amount-max-button'; const middleware = [thunk]; @@ -15,8 +16,13 @@ describe('AmountMaxButton Component', () => { const { getByText } = renderWithProvider( , configureMockStore(middleware)({ + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.NONE, + networkDetails: { + EIPS: {}, + }, + }, send: initialState, - gas: { basicEstimateStatus: 'LOADING' }, }), ); expect(getByText('Max')).toBeTruthy(); @@ -24,8 +30,13 @@ describe('AmountMaxButton Component', () => { it('should dispatch action to set mode to MAX', () => { const store = configureMockStore(middleware)({ + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE, + networkDetails: { + EIPS: {}, + }, + }, send: { ...initialState, status: SEND_STATUSES.VALID }, - gas: { basicEstimateStatus: 'READY' }, }); const { getByText } = renderWithProvider(, store); @@ -40,12 +51,17 @@ describe('AmountMaxButton Component', () => { it('should dispatch action to set amount mode to INPUT', () => { const store = configureMockStore(middleware)({ + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE, + networkDetails: { + EIPS: {}, + }, + }, send: { ...initialState, status: SEND_STATUSES.VALID, amount: { ...initialState.amount, mode: 'MAX' }, }, - gas: { basicEstimateStatus: 'READY' }, }); const { getByText } = renderWithProvider(, store); diff --git a/ui/pages/send/send.test.js b/ui/pages/send/send.test.js index 07bef5b26..65276d864 100644 --- a/ui/pages/send/send.test.js +++ b/ui/pages/send/send.test.js @@ -8,6 +8,7 @@ import { initialState, 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 Send from './send'; const middleware = [thunk]; @@ -37,12 +38,19 @@ const baseStore = { send: initialState, ENS: ensInitialState, gas: { - basicEstimateStatus: 'READY', - basicEstimates: { slow: '0x0', average: '0x1', fast: '0x2' }, customData: { limit: null, price: null }, }, history: { mostRecentOverviewPage: 'activity' }, metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '0', + medium: '1', + fast: '2', + }, + networkDetails: { + EIPS: {}, + }, tokens: [], preferences: { useNativeCurrencyAsPrimaryCurrency: false, @@ -82,12 +90,6 @@ describe('Send Page', () => { expect.objectContaining({ type: 'send/initializeSendState/pending', }), - expect.objectContaining({ - type: 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS', - }), - expect.objectContaining({ - type: 'metamask/gas/SET_ESTIMATE_SOURCE', - }), ]), ); }); @@ -105,12 +107,6 @@ describe('Send Page', () => { expect.objectContaining({ type: 'send/initializeSendState/pending', }), - expect.objectContaining({ - type: 'metamask/gas/BASIC_GAS_ESTIMATE_STATUS', - }), - expect.objectContaining({ - type: 'metamask/gas/SET_ESTIMATE_SOURCE', - }), expect.objectContaining({ type: 'UI_MODAL_OPEN', payload: { name: 'QR_SCANNER' }, diff --git a/ui/selectors/custom-gas.js b/ui/selectors/custom-gas.js index 5b0647ffe..ea72bf9ae 100644 --- a/ui/selectors/custom-gas.js +++ b/ui/selectors/custom-gas.js @@ -8,10 +8,17 @@ import { decEthToConvertedCurrency as ethTotalToConvertedCurrency } from '../hel import { formatETHFee } from '../helpers/utils/formatters'; import { calcGasTotal } from '../pages/send/send.utils'; -import { GAS_ESTIMATE_TYPES } from '../helpers/constants/common'; import { getGasPrice } from '../ducks/send'; -import { BASIC_ESTIMATE_STATES, GAS_SOURCE } from '../ducks/gas/gas.duck'; -import { GAS_LIMITS } from '../../shared/constants/gas'; +import { + GAS_ESTIMATE_TYPES as GAS_FEE_CONTROLLER_ESTIMATE_TYPES, + GAS_LIMITS, +} from '../../shared/constants/gas'; +import { + getGasEstimateType, + getGasFeeEstimates, + isEIP1559Network, +} from '../ducks/metamask/metamask'; +import { GAS_ESTIMATE_TYPES } from '../helpers/constants/common'; import { getCurrentCurrency, getIsMainnet, getShouldShowFiat } from '.'; const NUMBER_OF_DECIMALS_SM_BTNS = 5; @@ -25,13 +32,12 @@ export function getCustomGasPrice(state) { } export function getBasicGasEstimateLoadingStatus(state) { - return state.gas.basicEstimateStatus === 'LOADING'; + return getIsGasEstimatesFetched(state) === false; } export function getAveragePriceEstimateInHexWEI(state) { - const averagePriceEstimate = state.gas.basicEstimates - ? state.gas.basicEstimates.average - : '0x0'; + const averagePriceEstimate = getAverageEstimate(state); + return getGasPriceInHexWei(averagePriceEstimate); } @@ -51,23 +57,31 @@ export function getDefaultActiveButtonIndex( } export function getSafeLowEstimate(state) { - const { - gas: { - basicEstimates: { safeLow }, - }, - } = state; + const gasFeeEstimates = getGasFeeEstimates(state); + const gasEstimateType = getGasEstimateType(state); - return safeLow; + return gasEstimateType === GAS_FEE_CONTROLLER_ESTIMATE_TYPES.LEGACY + ? gasFeeEstimates?.low + : null; +} + +export function getAverageEstimate(state) { + const gasFeeEstimates = getGasFeeEstimates(state); + const gasEstimateType = getGasEstimateType(state); + + return gasEstimateType === GAS_FEE_CONTROLLER_ESTIMATE_TYPES.LEGACY + ? gasFeeEstimates?.medium + : null; } export function getFastPriceEstimate(state) { - const { - gas: { - basicEstimates: { fast }, - }, - } = state; + const gasFeeEstimates = getGasFeeEstimates(state); - return fast; + const gasEstimateType = getGasEstimateType(state); + + return gasEstimateType === GAS_FEE_CONTROLLER_ESTIMATE_TYPES.LEGACY + ? gasFeeEstimates?.high + : null; } export function isCustomPriceSafe(state) { @@ -97,7 +111,7 @@ export function isCustomPriceSafe(state) { } export function isCustomPriceSafeForCustomNetwork(state) { - const estimatedPrice = state.gas.basicEstimates.average; + const estimatedPrice = getAverageEstimate(state); const customGasPrice = getCustomGasPrice(state); @@ -219,61 +233,56 @@ export function getRenderableGasButtonData( currentCurrency, nativeCurrency, ) { - const { safeLow, average, fast } = estimates; + const { low, medium, high } = estimates; const slowEstimateData = { gasEstimateType: GAS_ESTIMATE_TYPES.SLOW, - feeInPrimaryCurrency: getRenderableEthFee( - safeLow, - gasLimit, - 9, - nativeCurrency, - ), + feeInPrimaryCurrency: getRenderableEthFee(low, gasLimit, 9, nativeCurrency), feeInSecondaryCurrency: showFiat ? getRenderableConvertedCurrencyFee( - safeLow, + low, gasLimit, currentCurrency, conversionRate, ) : '', - priceInHexWei: getGasPriceInHexWei(safeLow), + priceInHexWei: getGasPriceInHexWei(low), }; const averageEstimateData = { gasEstimateType: GAS_ESTIMATE_TYPES.AVERAGE, feeInPrimaryCurrency: getRenderableEthFee( - average, + medium, gasLimit, 9, nativeCurrency, ), feeInSecondaryCurrency: showFiat ? getRenderableConvertedCurrencyFee( - average, + medium, gasLimit, currentCurrency, conversionRate, ) : '', - priceInHexWei: getGasPriceInHexWei(average), + priceInHexWei: getGasPriceInHexWei(medium), }; const fastEstimateData = { gasEstimateType: GAS_ESTIMATE_TYPES.FAST, feeInPrimaryCurrency: getRenderableEthFee( - fast, + high, gasLimit, 9, nativeCurrency, ), feeInSecondaryCurrency: showFiat ? getRenderableConvertedCurrencyFee( - fast, + high, gasLimit, currentCurrency, conversionRate, ) : '', - priceInHexWei: getGasPriceInHexWei(fast), + priceInHexWei: getGasPriceInHexWei(high), }; return { @@ -297,7 +306,7 @@ export function getRenderableBasicEstimateData(state, gasLimit) { averageEstimateData, fastEstimateData, } = getRenderableGasButtonData( - state.gas.basicEstimates, + getGasFeeEstimates(state), gasLimit, showFiat, conversionRate, @@ -308,7 +317,7 @@ export function getRenderableBasicEstimateData(state, gasLimit) { } export function getRenderableEstimateDataForSmallButtonsFromGWEI(state) { - if (getBasicGasEstimateLoadingStatus(state)) { + if (getIsGasEstimatesFetched(state) === false) { return []; } const showFiat = getShouldShowFiat(state); @@ -316,94 +325,88 @@ export function getRenderableEstimateDataForSmallButtonsFromGWEI(state) { state.send.gas.gasLimit || getCustomGasLimit(state) || GAS_LIMITS.SIMPLE; const { conversionRate } = state.metamask; const currentCurrency = getCurrentCurrency(state); - const { - gas: { - basicEstimates: { safeLow, average, fast }, - }, - } = state; + const gasFeeEstimates = getGasFeeEstimates(state); return [ { gasEstimateType: GAS_ESTIMATE_TYPES.SLOW, feeInSecondaryCurrency: showFiat ? getRenderableConvertedCurrencyFee( - safeLow, + gasFeeEstimates.low, gasLimit, currentCurrency, conversionRate, ) : '', feeInPrimaryCurrency: getRenderableEthFee( - safeLow, + gasFeeEstimates.low, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, ), - priceInHexWei: getGasPriceInHexWei(safeLow, true), + priceInHexWei: getGasPriceInHexWei(gasFeeEstimates.low, true), }, { gasEstimateType: GAS_ESTIMATE_TYPES.AVERAGE, feeInSecondaryCurrency: showFiat ? getRenderableConvertedCurrencyFee( - average, + gasFeeEstimates.medium, gasLimit, currentCurrency, conversionRate, ) : '', feeInPrimaryCurrency: getRenderableEthFee( - average, + gasFeeEstimates.medium, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, ), - priceInHexWei: getGasPriceInHexWei(average, true), + priceInHexWei: getGasPriceInHexWei(gasFeeEstimates.medium, true), }, { gasEstimateType: GAS_ESTIMATE_TYPES.FAST, feeInSecondaryCurrency: showFiat ? getRenderableConvertedCurrencyFee( - fast, + gasFeeEstimates.high, gasLimit, currentCurrency, conversionRate, ) : '', feeInPrimaryCurrency: getRenderableEthFee( - fast, + gasFeeEstimates.high, gasLimit, NUMBER_OF_DECIMALS_SM_BTNS, ), - priceInHexWei: getGasPriceInHexWei(fast, true), + priceInHexWei: getGasPriceInHexWei(gasFeeEstimates.high, true), }, ]; } export function getIsEthGasPriceFetched(state) { - const gasState = state.gas; - return Boolean( - gasState.estimateSource === GAS_SOURCE.ETHGASPRICE && - gasState.basicEstimateStatus === BASIC_ESTIMATE_STATES.READY && - getIsMainnet(state), + const gasEstimateType = getGasEstimateType(state); + return ( + gasEstimateType === GAS_FEE_CONTROLLER_ESTIMATE_TYPES.ETH_GASPRICE && + getIsMainnet(state) ); } export function getIsCustomNetworkGasPriceFetched(state) { - const gasState = state.gas; - return Boolean( - gasState.estimateSource === GAS_SOURCE.ETHGASPRICE && - gasState.basicEstimateStatus === BASIC_ESTIMATE_STATES.READY && - !getIsMainnet(state), + const gasEstimateType = getGasEstimateType(state); + return ( + gasEstimateType === GAS_FEE_CONTROLLER_ESTIMATE_TYPES.ETH_GASPRICE && + !getIsMainnet(state) ); } export function getNoGasPriceFetched(state) { - const gasState = state.gas; - return Boolean(gasState.basicEstimateStatus === BASIC_ESTIMATE_STATES.FAILED); + const gasEstimateType = getGasEstimateType(state); + return gasEstimateType === GAS_FEE_CONTROLLER_ESTIMATE_TYPES.NONE; } export function getIsGasEstimatesFetched(state) { - const gasState = state.gas; - return Boolean( - gasState.estimateSource === GAS_SOURCE.METASWAPS && - gasState.basicEstimateStatus === BASIC_ESTIMATE_STATES.READY, - ); + const gasEstimateType = getGasEstimateType(state); + if (isEIP1559Network(state)) { + return false; + } + return gasEstimateType !== GAS_FEE_CONTROLLER_ESTIMATE_TYPES.NONE; } diff --git a/ui/selectors/custom-gas.test.js b/ui/selectors/custom-gas.test.js index fb383248d..b20a9ad57 100644 --- a/ui/selectors/custom-gas.test.js +++ b/ui/selectors/custom-gas.test.js @@ -1,4 +1,4 @@ -import { GAS_LIMITS } from '../../shared/constants/gas'; +import { GAS_ESTIMATE_TYPES, GAS_LIMITS } from '../../shared/constants/gas'; import { getCustomGasLimit, getCustomGasPrice, @@ -18,36 +18,68 @@ describe('custom-gas selectors', () => { describe('isCustomGasPriceSafe()', () => { it('should return true for gas.customData.price 0x77359400', () => { const mockState = { + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '1', + }, + networkDetails: { + EIPS: {}, + }, + }, gas: { customData: { price: '0x77359400' }, - basicEstimates: { safeLow: 1 }, }, }; expect(isCustomPriceSafe(mockState)).toStrictEqual(true); }); it('should return true for gas.customData.price null', () => { const mockState = { + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '1', + }, + networkDetails: { + EIPS: {}, + }, + }, gas: { customData: { price: null }, - basicEstimates: { safeLow: 1 }, }, }; expect(isCustomPriceSafe(mockState)).toStrictEqual(true); }); it('should return true gas.customData.price undefined', () => { const mockState = { + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '1', + }, + networkDetails: { + EIPS: {}, + }, + }, gas: { customData: { price: undefined }, - basicEstimates: { safeLow: 1 }, }, }; expect(isCustomPriceSafe(mockState)).toStrictEqual(true); }); it('should return false gas.basicEstimates.safeLow undefined', () => { const mockState = { + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.NONE, + gasFeeEstimates: { + low: undefined, + }, + networkDetails: { + EIPS: {}, + }, + }, gas: { customData: { price: '0x77359400' }, - basicEstimates: { safeLow: undefined }, }, }; expect(isCustomPriceSafe(mockState)).toStrictEqual(false); @@ -57,60 +89,117 @@ describe('custom-gas selectors', () => { describe('isCustomPriceExcessive()', () => { it('should return false for gas.customData.price null', () => { const mockState = { + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + high: '150', + }, + networkDetails: { + EIPS: {}, + }, + }, gas: { customData: { price: null }, - basicEstimates: { fast: 150 }, }, }; expect(isCustomPriceExcessive(mockState)).toStrictEqual(false); }); it('should return false gas.basicEstimates.fast undefined', () => { const mockState = { + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + high: undefined, + }, + networkDetails: { + EIPS: {}, + }, + }, gas: { customData: { price: '0x77359400' }, - basicEstimates: { fast: undefined }, }, }; expect(isCustomPriceExcessive(mockState)).toStrictEqual(false); }); it('should return false gas.basicEstimates.price 0x205d0bae00 (139)', () => { const mockState = { + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + high: '139', + }, + networkDetails: { + EIPS: {}, + }, + }, gas: { customData: { price: '0x205d0bae00' }, - basicEstimates: { fast: 139 }, }, }; expect(isCustomPriceExcessive(mockState)).toStrictEqual(false); }); it('should return false gas.basicEstimates.price 0x1bf08eb000 (120)', () => { const mockState = { + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + high: '139', + }, + networkDetails: { + EIPS: {}, + }, + }, gas: { customData: { price: '0x1bf08eb000' }, - basicEstimates: { fast: 139 }, }, }; expect(isCustomPriceExcessive(mockState)).toStrictEqual(false); }); it('should return false gas.basicEstimates.price 0x28bed01600 (175)', () => { const mockState = { + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + high: '139', + }, + networkDetails: { + EIPS: {}, + }, + }, gas: { customData: { price: '0x28bed01600' }, - basicEstimates: { fast: 139 }, }, }; expect(isCustomPriceExcessive(mockState)).toStrictEqual(false); }); it('should return true gas.basicEstimates.price 0x30e4f9b400 (210)', () => { const mockState = { + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + high: '139', + }, + networkDetails: { + EIPS: {}, + }, + }, gas: { customData: { price: '0x30e4f9b400' }, - basicEstimates: { fast: 139 }, }, }; expect(isCustomPriceExcessive(mockState)).toStrictEqual(true); }); it('should return false gas.basicEstimates.price 0x28bed01600 (175) (checkSend=true)', () => { const mockState = { + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + high: '139', + }, + networkDetails: { + EIPS: {}, + }, + }, send: { gas: { gasPrice: '0x28bed0160', @@ -118,13 +207,21 @@ describe('custom-gas selectors', () => { }, gas: { customData: { price: null }, - basicEstimates: { fast: 139 }, }, }; expect(isCustomPriceExcessive(mockState, true)).toStrictEqual(false); }); it('should return true gas.basicEstimates.price 0x30e4f9b400 (210) (checkSend=true)', () => { const mockState = { + metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + high: '139', + }, + networkDetails: { + EIPS: {}, + }, + }, send: { gas: { gasPrice: '0x30e4f9b400', @@ -132,7 +229,6 @@ describe('custom-gas selectors', () => { }, gas: { customData: { price: null }, - basicEstimates: { fast: 139 }, }, }; expect(isCustomPriceExcessive(mockState, true)).toStrictEqual(true); @@ -171,6 +267,15 @@ describe('custom-gas selectors', () => { ], mockState: { metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '2.5', + medium: '4', + high: '5', + }, + networkDetails: { + EIPS: {}, + }, conversionRate: 255.71, currentCurrency: 'usd', preferences: { @@ -181,19 +286,6 @@ describe('custom-gas selectors', () => { chainId: '0x1', }, }, - gas: { - basicEstimates: { - blockTime: 14.16326530612245, - safeLow: 2.5, - safeLowWait: 6.6, - average: 4, - avgWait: 5.3, - fast: 5, - fastWait: 3.3, - fastest: 10, - fastestWait: 0.5, - }, - }, }, }, { @@ -219,6 +311,15 @@ describe('custom-gas selectors', () => { ], mockState: { metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '5', + medium: '7', + high: '10', + }, + networkDetails: { + EIPS: {}, + }, conversionRate: 2557.1, currentCurrency: 'usd', preferences: { @@ -234,19 +335,6 @@ describe('custom-gas selectors', () => { gasLimit: GAS_LIMITS.SIMPLE, }, }, - gas: { - basicEstimates: { - blockTime: 14.16326530612245, - safeLow: 5, - safeLowWait: 13.2, - average: 7, - avgWait: 10.1, - fast: 10, - fastWait: 6.6, - fastest: 20, - fastestWait: 1.0, - }, - }, }, }, { @@ -272,6 +360,15 @@ describe('custom-gas selectors', () => { ], mockState: { metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '5', + medium: '7', + high: '10', + }, + networkDetails: { + EIPS: {}, + }, conversionRate: 2557.1, currentCurrency: 'usd', preferences: { @@ -287,19 +384,6 @@ describe('custom-gas selectors', () => { gasLimit: GAS_LIMITS.SIMPLE, }, }, - gas: { - basicEstimates: { - blockTime: 14.16326530612245, - safeLow: 5, - safeLowWait: 13.2, - average: 7, - avgWait: 10.1, - fast: 10, - fastWait: 6.6, - fastest: 20, - fastestWait: 1.0, - }, - }, }, }, { @@ -325,6 +409,15 @@ describe('custom-gas selectors', () => { ], mockState: { metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '5', + medium: '7', + high: '10', + }, + networkDetails: { + EIPS: {}, + }, conversionRate: 2557.1, currentCurrency: 'usd', preferences: { @@ -340,13 +433,6 @@ describe('custom-gas selectors', () => { gasLimit: GAS_LIMITS.SIMPLE, }, }, - gas: { - basicEstimates: { - safeLow: 5, - average: 7, - fast: 10, - }, - }, }, }, { @@ -372,6 +458,15 @@ describe('custom-gas selectors', () => { ], mockState: { metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '5', + medium: '7', + high: '10', + }, + networkDetails: { + EIPS: {}, + }, conversionRate: 2557.1, currentCurrency: 'usd', preferences: { @@ -387,13 +482,6 @@ describe('custom-gas selectors', () => { gasLimit: GAS_LIMITS.SIMPLE, }, }, - gas: { - basicEstimates: { - safeLow: 5, - average: 7, - fast: 10, - }, - }, }, }, ]; @@ -435,6 +523,15 @@ describe('custom-gas selectors', () => { ], mockState: { metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '25', + medium: '30', + high: '50', + }, + networkDetails: { + EIPS: {}, + }, conversionRate: 255.71, currentCurrency: 'usd', preferences: { @@ -450,13 +547,6 @@ describe('custom-gas selectors', () => { gasLimit: GAS_LIMITS.SIMPLE, }, }, - gas: { - basicEstimates: { - safeLow: 25, - average: 30, - fast: 50, - }, - }, }, }, { @@ -482,6 +572,15 @@ describe('custom-gas selectors', () => { ], mockState: { metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '50', + medium: '75', + high: '100', + }, + networkDetails: { + EIPS: {}, + }, conversionRate: 2557.1, currentCurrency: 'usd', preferences: { @@ -497,19 +596,6 @@ describe('custom-gas selectors', () => { gasLimit: GAS_LIMITS.SIMPLE, }, }, - gas: { - basicEstimates: { - blockTime: 14.16326530612245, - safeLow: 50, - safeLowWait: 13.2, - average: 75, - avgWait: 9.6, - fast: 100, - fastWait: 6.6, - fastest: 200, - fastestWait: 1.0, - }, - }, }, }, { @@ -535,6 +621,15 @@ describe('custom-gas selectors', () => { ], mockState: { metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '50', + medium: '75', + high: '100', + }, + networkDetails: { + EIPS: {}, + }, conversionRate: 2557.1, currentCurrency: 'usd', preferences: { @@ -550,19 +645,6 @@ describe('custom-gas selectors', () => { gasLimit: GAS_LIMITS.SIMPLE, }, }, - gas: { - basicEstimates: { - blockTime: 14.16326530612245, - safeLow: 50, - safeLowWait: 13.2, - average: 75, - avgWait: 9.6, - fast: 100, - fastWait: 6.6, - fastest: 200, - fastestWait: 1.0, - }, - }, }, }, { @@ -588,6 +670,15 @@ describe('custom-gas selectors', () => { ], mockState: { metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '50', + medium: '75', + high: '100', + }, + networkDetails: { + EIPS: {}, + }, conversionRate: 2557.1, currentCurrency: 'usd', preferences: { @@ -603,13 +694,6 @@ describe('custom-gas selectors', () => { gasLimit: GAS_LIMITS.SIMPLE, }, }, - gas: { - basicEstimates: { - safeLow: 50, - average: 75, - fast: 100, - }, - }, }, }, { @@ -635,6 +719,15 @@ describe('custom-gas selectors', () => { ], mockState: { metamask: { + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + gasFeeEstimates: { + low: '50', + medium: '75', + high: '100', + }, + networkDetails: { + EIPS: {}, + }, conversionRate: 2557.1, currentCurrency: 'usd', preferences: { @@ -650,13 +743,6 @@ describe('custom-gas selectors', () => { gasLimit: GAS_LIMITS.SIMPLE, }, }, - gas: { - basicEstimates: { - safeLow: 50, - average: 75, - fast: 100, - }, - }, }, }, ];