Skip to content

Commit

Permalink
Update addTxGasDefaults and _getDefaultGasFees to work correctly with…
Browse files Browse the repository at this point in the history
… all new gas fee estimate types
  • Loading branch information
danjm committed Jul 27, 2021
1 parent 266d26b commit 3d89cc0
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 7 deletions.
89 changes: 82 additions & 7 deletions app/scripts/controllers/transactions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -104,6 +108,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);
Expand Down Expand Up @@ -389,7 +394,14 @@ export default class TransactionController extends EventEmitter {
* @returns {Promise<object>} 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,
Expand All @@ -400,6 +412,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 &&
Expand All @@ -419,16 +459,51 @@ export default class TransactionController extends EventEmitter {
* @param {Object} txMeta - The txMeta object
* @returns {Promise<string|undefined>} 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)) };
}

/**
Expand Down Expand Up @@ -672,7 +747,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;
Expand Down
129 changes: 129 additions & 0 deletions app/scripts/controllers/transactions/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 '.';

Expand Down Expand Up @@ -62,6 +63,7 @@ describe('Transaction Controller', function () {
getCurrentChainId: () => currentChainId,
getParticipateInMetrics: () => false,
trackMetaMetricsEvent: () => undefined,
getEIP1559GasFeeEstimates: () => undefined,
});
txController.nonceTracker.getNonceLock = () =>
Promise.resolve({ nextNonce: 0, releaseLock: noop });
Expand Down Expand Up @@ -417,6 +419,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 () {
Expand Down
3 changes: 3 additions & 0 deletions app/scripts/metamask-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,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());

Expand Down
10 changes: 10 additions & 0 deletions shared/modules/conversion.utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -279,4 +288,5 @@ export {
conversionMax,
toNegative,
subtractCurrencies,
decGWEIToHexWEI,
};

0 comments on commit 3d89cc0

Please sign in to comment.