From 93cf7ec375abda699a5855d151a694b0a844e1bd Mon Sep 17 00:00:00 2001 From: Brad Decker Date: Fri, 16 Jul 2021 14:42:36 -0500 Subject: [PATCH 1/7] Fix typo Remove console.log in send duck --- .../confirm-transaction-base.component.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js index 4e6c601b1b72..a4229709a62a 100644 --- a/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js +++ b/ui/pages/confirm-transaction-base/confirm-transaction-base.component.js @@ -808,7 +808,7 @@ export default class ConfirmTransactionBase extends Component { } /** - * Thismakes a request to get estimates and begin polling, keeping track of the poll + * This makes a request to get estimates and begin polling, keeping track of the poll * token in component state. * It then disconnects polling upon componentWillUnmount. If the hook is unmounted * while waiting for `getGasFeeEstimatesAndStartPolling` to resolve, the `_isMounted` From 7d125ead1c9dba0f783f6360baae5d3a9a7ca4b4 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Mon, 26 Jul 2021 09:59:58 -0230 Subject: [PATCH 2/7] Update addTxGasDefaults and _getDefaultGasFees to work correctly with all new gas fee estimate types --- app/scripts/controllers/transactions/index.js | 89 +++++++++++- .../controllers/transactions/index.test.js | 129 ++++++++++++++++++ app/scripts/metamask-controller.js | 3 + shared/modules/conversion.utils.js | 10 ++ 4 files changed, 224 insertions(+), 7 deletions(-) diff --git a/app/scripts/controllers/transactions/index.js b/app/scripts/controllers/transactions/index.js index f46d6cdb5d6b..7a279f36b13c 100644 --- a/app/scripts/controllers/transactions/index.js +++ b/app/scripts/controllers/transactions/index.js @@ -27,7 +27,11 @@ import { TRANSACTION_ENVELOPE_TYPES, } from '../../../../shared/constants/transaction'; import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller'; -import { GAS_LIMITS } from '../../../../shared/constants/gas'; +import { + GAS_LIMITS, + GAS_ESTIMATE_TYPES, +} from '../../../../shared/constants/gas'; +import { decGWEIToHexWEI } from '../../../../shared/modules/conversion.utils'; import { HARDFORKS, MAINNET, @@ -107,6 +111,7 @@ export default class TransactionController extends EventEmitter { this.inProcessOfSigning = new Set(); this._trackMetaMetricsEvent = opts.trackMetaMetricsEvent; this._getParticipateInMetrics = opts.getParticipateInMetrics; + this._getEIP1559GasFeeEstimates = opts.getEIP1559GasFeeEstimates; this.memStore = new ObservableStore({}); this.query = new EthQuery(this.provider); @@ -400,7 +405,14 @@ export default class TransactionController extends EventEmitter { * @returns {Promise} resolves with txMeta */ async addTxGasDefaults(txMeta, getCodeResponse) { - const defaultGasPrice = await this._getDefaultGasPrice(txMeta); + const eip1559Compatibility = await this.getEIP1559Compatibility(); + + const { + gasPrice: defaultGasPrice, + maxFeePerGas: defaultMaxFeePerGas, + maxPriorityFeePerGas: defaultMaxPriorityFeePerGas, + } = await this._getDefaultGasFees(txMeta, eip1559Compatibility); + const { gasLimit: defaultGasLimit, simulationFails, @@ -411,6 +423,34 @@ export default class TransactionController extends EventEmitter { if (simulationFails) { txMeta.simulationFails = simulationFails; } + + if (eip1559Compatibility) { + if ( + txMeta.txParams.gasPrice && + !txMeta.txParams.maxFeePerGas && + !txMeta.txParams.maxPriorityFeePerGas + ) { + txMeta.txParams.maxFeePerGas = txMeta.txParams.gasPrice; + txMeta.txParams.maxPriorityFeePerGas = txMeta.txParams.gasPrice; + } + + if (defaultMaxFeePerGas && !txMeta.txParams.maxFeePerGas) { + txMeta.txParams.maxFeePerGas = defaultMaxFeePerGas; + } + + if ( + defaultMaxPriorityFeePerGas && + !txMeta.txParams.maxPriorityFeePerGas + ) { + txMeta.txParams.maxPriorityFeePerGas = defaultMaxPriorityFeePerGas; + } + + delete txMeta.txParams.gasPrice; + } else { + delete txMeta.txParams.maxPriorityFeePerGas; + delete txMeta.txParams.maxFeePerGas; + } + if ( defaultGasPrice && !txMeta.txParams.gasPrice && @@ -430,16 +470,51 @@ export default class TransactionController extends EventEmitter { * @param {Object} txMeta - The txMeta object * @returns {Promise} The default gas price */ - async _getDefaultGasPrice(txMeta) { + async _getDefaultGasFees(txMeta, eip1559Compatibility) { if ( - txMeta.txParams.gasPrice || + (!eip1559Compatibility && txMeta.txParams.gasPrice) || (txMeta.txParams.maxFeePerGas && txMeta.txParams.maxPriorityFeePerGas) ) { - return undefined; + return {}; + } + + try { + const { + gasFeeEstimates, + gasEstimateType, + } = await this._getEIP1559GasFeeEstimates(); + if ( + eip1559Compatibility && + gasEstimateType === GAS_ESTIMATE_TYPES.FEE_MARKET + ) { + const { + medium: { suggestedMaxPriorityFeePerGas, suggestedMaxFeePerGas } = {}, + } = gasFeeEstimates; + + if (suggestedMaxPriorityFeePerGas && suggestedMaxFeePerGas) { + return { + maxFeePerGas: decGWEIToHexWEI(suggestedMaxFeePerGas), + maxPriorityFeePerGas: decGWEIToHexWEI( + suggestedMaxPriorityFeePerGas, + ), + }; + } + } else if (gasEstimateType === GAS_ESTIMATE_TYPES.LEGACY) { + return { + gasPrice: decGWEIToHexWEI(gasFeeEstimates.medium), + }; + } else if (gasEstimateType === GAS_ESTIMATE_TYPES.ETH_GASPRICE) { + return { + gasPrice: decGWEIToHexWEI(gasFeeEstimates.gasPrice), + }; + } + } catch (e) { + console.error(e); } + const gasPrice = await this.query.gasPrice(); - return addHexPrefix(gasPrice.toString(16)); + return { gasPrice: addHexPrefix(gasPrice.toString(16)) }; } /** @@ -683,7 +758,7 @@ export default class TransactionController extends EventEmitter { this.txStateManager.setTxStatusApproved(txId); // get next nonce const txMeta = this.txStateManager.getTransaction(txId); - console.log(txMeta); + const fromAddress = txMeta.txParams.from; // wait for a nonce let { customNonceValue } = txMeta; diff --git a/app/scripts/controllers/transactions/index.test.js b/app/scripts/controllers/transactions/index.test.js index 9a415b280717..cbfd987cadf6 100644 --- a/app/scripts/controllers/transactions/index.test.js +++ b/app/scripts/controllers/transactions/index.test.js @@ -14,6 +14,7 @@ import { TRANSACTION_TYPES, } from '../../../../shared/constants/transaction'; import { SECOND } from '../../../../shared/constants/time'; +import { GAS_ESTIMATE_TYPES } from '../../../../shared/constants/gas'; import { METAMASK_CONTROLLER_EVENTS } from '../../metamask-controller'; import TransactionController, { TRANSACTION_EVENTS } from '.'; @@ -64,6 +65,7 @@ describe('Transaction Controller', function () { getCurrentChainId: () => currentChainId, getParticipateInMetrics: () => false, trackMetaMetricsEvent: () => undefined, + getEIP1559GasFeeEstimates: () => undefined, }); txController.nonceTracker.getNonceLock = () => Promise.resolve({ nextNonce: 0, releaseLock: noop }); @@ -419,6 +421,133 @@ describe('Transaction Controller', function () { 'should have added the gas field', ); }); + + it('should add EIP1559 tx defaults', async function () { + const TEST_MAX_FEE_PER_GAS = '0x12a05f200'; + const TEST_MAX_PRIORITY_FEE_PER_GAS = '0x77359400'; + + const stub1 = sinon + .stub(txController, 'getEIP1559Compatibility') + .returns(true); + + const stub2 = sinon + .stub(txController, '_getDefaultGasFees') + .callsFake(() => ({ + maxFeePerGas: TEST_MAX_FEE_PER_GAS, + maxPriorityFeePerGas: TEST_MAX_PRIORITY_FEE_PER_GAS, + })); + + txController.txStateManager._addTransactionsToState([ + { + id: 1, + status: TRANSACTION_STATUSES.UNAPPROVED, + metamaskNetworkId: currentNetworkId, + txParams: { + to: VALID_ADDRESS, + from: VALID_ADDRESS_TWO, + }, + history: [{}], + }, + ]); + const txMeta = { + id: 1, + txParams: { + from: '0xc684832530fcbddae4b4230a47e991ddcec2831d', + to: '0xc684832530fcbddae4b4230a47e991ddcec2831d', + }, + history: [{}], + }; + providerResultStub.eth_getBlockByNumber = { gasLimit: '47b784' }; + providerResultStub.eth_estimateGas = '5209'; + + const txMetaWithDefaults = await txController.addTxGasDefaults(txMeta); + + assert.equal( + txMetaWithDefaults.txParams.maxFeePerGas, + TEST_MAX_FEE_PER_GAS, + 'should have added the correct max fee per gas', + ); + assert.equal( + txMetaWithDefaults.txParams.maxPriorityFeePerGas, + TEST_MAX_PRIORITY_FEE_PER_GAS, + 'should have added the correct max priority fee per gas', + ); + stub1.restore(); + stub2.restore(); + }); + }); + + describe('_getDefaultGasFees', function () { + let getGasFeeStub; + + beforeEach(function () { + getGasFeeStub = sinon.stub(txController, '_getEIP1559GasFeeEstimates'); + }); + + afterEach(function () { + getGasFeeStub.restore(); + }); + + it('should return the correct fee data when the gas estimate type is FEE_MARKET', async function () { + const EXPECTED_MAX_FEE_PER_GAS = '12a05f200'; + const EXPECTED_MAX_PRIORITY_FEE_PER_GAS = '77359400'; + + getGasFeeStub.callsFake(() => ({ + gasFeeEstimates: { + medium: { + suggestedMaxPriorityFeePerGas: '2', + suggestedMaxFeePerGas: '5', + }, + }, + gasEstimateType: GAS_ESTIMATE_TYPES.FEE_MARKET, + })); + + const defaultGasFees = await txController._getDefaultGasFees( + { txParams: {} }, + true, + ); + + assert.deepEqual(defaultGasFees, { + maxPriorityFeePerGas: EXPECTED_MAX_PRIORITY_FEE_PER_GAS, + maxFeePerGas: EXPECTED_MAX_FEE_PER_GAS, + }); + }); + + it('should return the correct fee data when the gas estimate type is LEGACY', async function () { + const EXPECTED_GAS_PRICE = '77359400'; + + getGasFeeStub.callsFake(() => ({ + gasFeeEstimates: { medium: '2' }, + gasEstimateType: GAS_ESTIMATE_TYPES.LEGACY, + })); + + const defaultGasFees = await txController._getDefaultGasFees( + { txParams: {} }, + false, + ); + + assert.deepEqual(defaultGasFees, { + gasPrice: EXPECTED_GAS_PRICE, + }); + }); + + it('should return the correct fee data when the gas estimate type is ETH_GASPRICE', async function () { + const EXPECTED_GAS_PRICE = '77359400'; + + getGasFeeStub.callsFake(() => ({ + gasFeeEstimates: { gasPrice: '2' }, + gasEstimateType: GAS_ESTIMATE_TYPES.ETH_GASPRICE, + })); + + const defaultGasFees = await txController._getDefaultGasFees( + { txParams: {} }, + false, + ); + + assert.deepEqual(defaultGasFees, { + gasPrice: EXPECTED_GAS_PRICE, + }); + }); }); describe('#addTransaction', function () { diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 5e095e2a565e..bb17887e0d49 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -423,6 +423,9 @@ export default class MetamaskController extends EventEmitter { ), getParticipateInMetrics: () => this.metaMetricsController.state.participateInMetaMetrics, + getEIP1559GasFeeEstimates: this.gasFeeController.fetchGasFeeEstimates.bind( + this.gasFeeController, + ), }); this.txController.on('newUnapprovedTx', () => opts.showUserConfirmation()); diff --git a/shared/modules/conversion.utils.js b/shared/modules/conversion.utils.js index 0b550e67bfd4..bad872787a78 100644 --- a/shared/modules/conversion.utils.js +++ b/shared/modules/conversion.utils.js @@ -268,6 +268,15 @@ const toNegative = (n, options = {}) => { return multiplyCurrencies(n, -1, options); }; +function decGWEIToHexWEI(decGWEI) { + return conversionUtil(decGWEI, { + fromNumericBase: 'dec', + toNumericBase: 'hex', + fromDenomination: 'GWEI', + toDenomination: 'WEI', + }); +} + export { conversionUtil, addCurrencies, @@ -279,4 +288,5 @@ export { conversionMax, toNegative, subtractCurrencies, + decGWEIToHexWEI, }; From 0f95edfebba6bdcc0dfd4ba04bb590a7a86c1234 Mon Sep 17 00:00:00 2001 From: Dan Miller Date: Tue, 27 Jul 2021 15:41:21 -0230 Subject: [PATCH 3/7] Don't show gas timing support when not on eip1559 compatible network --- .../app/edit-gas-display/edit-gas-display.component.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ui/components/app/edit-gas-display/edit-gas-display.component.js b/ui/components/app/edit-gas-display/edit-gas-display.component.js index f9417ae4c5e8..068111b0f25c 100644 --- a/ui/components/app/edit-gas-display/edit-gas-display.component.js +++ b/ui/components/app/edit-gas-display/edit-gas-display.component.js @@ -2,6 +2,7 @@ import React, { useContext } from 'react'; import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; +import { useSelector } from 'react-redux'; import { GAS_RECOMMENDATIONS, EDIT_GAS_MODES, @@ -11,6 +12,8 @@ import { isEIP1559Network } from '../../../ducks/metamask/metamask'; import Button from '../../ui/button'; import Typography from '../../ui/typography/typography'; +import { isEIP1559Network } from '../../../ducks/metamask/metamask'; + import { COLORS, TYPOGRAPHY, @@ -62,6 +65,7 @@ export default function EditGasDisplay({ onManualChange, }) { const t = useContext(I18nContext); + const supportsEIP1559 = useSelector(isEIP1559Network); const dappSuggestedAndTxParamGasFeesAreTheSame = areDappSuggestedAndTxParamGasFeesTheSame( transaction, @@ -128,7 +132,11 @@ export default function EditGasDisplay({ , ]) } - timing={} + timing={ + supportsEIP1559 && ( + + ) + } /> {requireDappAcknowledgement && (