1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-10-22 11:22:43 +02:00

Add gas utils shared module (#11452)

This commit is contained in:
Brad Decker 2021-07-07 11:13:40 -05:00 committed by GitHub
parent b1e2005a73
commit 9a6c222632
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 260 additions and 0 deletions

129
shared/modules/gas.utils.js Normal file
View File

@ -0,0 +1,129 @@
import { addHexPrefix } from 'ethereumjs-util';
import {
addCurrencies,
conversionGreaterThan,
multiplyCurrencies,
} from './conversion.utils';
/**
* Accepts an options bag containing gas fee parameters in hex format and
* returns a gasTotal parameter representing the maximum amount of wei the
* transaction will cost.
*
* @param {object} options - gas fee parameters object
* @param {string} [options.gasLimit] - the maximum amount of gas to allow this
* transaction to consume. Value is a hex string
* @param {string} [options.gasPrice] - The fee in wei to pay per gas used.
* gasPrice is only set on Legacy type transactions. Value is hex string
* @param {string} [options.maxFeePerGas] - The maximum fee in wei to pay per
* gas used. maxFeePerGas is introduced in EIP 1559 and represents the max
* total a user will pay per gas. Actual cost is determined by baseFeePerGas
* on the block + maxPriorityFeePerGas. Value is hex string
* @returns {string} - The maximum total cost of transaction in hex wei string
*/
export function getMaximumGasTotalInHexWei({
gasLimit = '0x0',
gasPrice,
maxFeePerGas,
} = {}) {
if (maxFeePerGas) {
return addHexPrefix(
multiplyCurrencies(gasLimit, maxFeePerGas, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
}),
);
}
if (!gasPrice) {
throw new Error(
'getMaximumGasTotalInHexWei requires gasPrice be provided to calculate legacy gas total',
);
}
return addHexPrefix(
multiplyCurrencies(gasLimit, gasPrice, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
}),
);
}
/**
* Accepts an options bag containing gas fee parameters in hex format and
* returns a gasTotal parameter representing the minimum amount of wei the
* transaction will cost. For gasPrice types this is the same as max.
*
* @param {object} options - gas fee parameters object
* @param {string} [options.gasLimit] - the maximum amount of gas to allow this
* transaction to consume. Value is a hex string
* @param {string} [options.gasPrice] - The fee in wei to pay per gas used.
* gasPrice is only set on Legacy type transactions. Value is hex string
* @param {string} [options.maxFeePerGas] - The maximum fee in wei to pay per
* gas used. maxFeePerGas is introduced in EIP 1559 and represents the max
* total a user will pay per gas. Actual cost is determined by baseFeePerGas
* on the block + maxPriorityFeePerGas. Value is hex string
* @param {string} [options.maxPriorityFeePerGas] - The maximum fee in wei to
* pay a miner to include this transaction.
* @param {string} [options.baseFeePerGas] - The estimated block baseFeePerGas
* that will be burned. Introduced in EIP 1559. Value in hex wei.
* @returns {string} - The minimum total cost of transaction in hex wei string
*/
export function getMinimumGasTotalInHexWei({
gasLimit = '0x0',
gasPrice,
maxPriorityFeePerGas,
maxFeePerGas,
baseFeePerGas,
} = {}) {
const isEIP1559Estimate = Boolean(
maxFeePerGas || maxPriorityFeePerGas || baseFeePerGas,
);
if (isEIP1559Estimate && gasPrice) {
throw new Error(
`getMinimumGasTotalInHexWei expects either gasPrice OR the EIP-1559 gas fields, but both were provided`,
);
}
if (isEIP1559Estimate === false && !gasPrice) {
throw new Error(
`getMinimumGasTotalInHexWei expects either gasPrice OR the EIP-1559 gas fields, but neither were provided`,
);
}
if (isEIP1559Estimate && !baseFeePerGas) {
throw new Error(
`getMinimumGasTotalInHexWei requires baseFeePerGas be provided when calculating EIP-1559 totals`,
);
}
if (isEIP1559Estimate && (!maxFeePerGas || !maxPriorityFeePerGas)) {
throw new Error(
`getMinimumGasTotalInHexWei requires maxFeePerGas and maxPriorityFeePerGas be provided when calculating EIP-1559 totals`,
);
}
if (isEIP1559Estimate === false) {
return getMaximumGasTotalInHexWei({ gasLimit, gasPrice });
}
const minimumFeePerGas = addCurrencies(baseFeePerGas, maxPriorityFeePerGas, {
toNumericBase: 'hex',
aBase: 16,
bBase: 16,
});
if (
conversionGreaterThan(
{ value: minimumFeePerGas, fromNumericBase: 'hex' },
{ value: maxFeePerGas, fromNumericBase: 'hex' },
)
) {
return getMaximumGasTotalInHexWei({ gasLimit, maxFeePerGas });
}
return addHexPrefix(
multiplyCurrencies(gasLimit, minimumFeePerGas, {
toNumericBase: 'hex',
multiplicandBase: 16,
multiplierBase: 16,
}),
);
}

View File

@ -0,0 +1,131 @@
const { addHexPrefix } = require('ethereumjs-util');
const { conversionUtil } = require('./conversion.utils');
const {
getMaximumGasTotalInHexWei,
getMinimumGasTotalInHexWei,
} = require('./gas.utils');
const feesToTest = [10, 24, 90];
const tipsToTest = [2, 10, 50];
const baseFeesToTest = [8, 12, 24];
const gasLimitsToTest = [21000, 100000];
describe('gas utils', () => {
describe('when using EIP 1559 fields', () => {
describe('getMaximumGasTotalInHexWei', () => {
feesToTest.forEach((maxFeePerGas) => {
describe(`when maxFeePerGas is ${maxFeePerGas}`, () => {
gasLimitsToTest.forEach((gasLimit) => {
const expectedResult = (gasLimit * maxFeePerGas).toString();
const gasLimitHex = addHexPrefix(gasLimit.toString(16));
const result = conversionUtil(
getMaximumGasTotalInHexWei({
gasLimit: gasLimitHex,
maxFeePerGas: addHexPrefix(maxFeePerGas.toString(16)),
}),
{ fromNumericBase: 'hex', toNumericBase: 'dec' },
);
it(`returns ${expectedResult} when provided gasLimit: ${gasLimit}`, () => {
expect(result).toStrictEqual(expectedResult);
});
});
});
});
});
describe('getMinimumGasTotalInHexWei', () => {
feesToTest.forEach((maxFeePerGas) => {
tipsToTest.forEach((maxPriorityFeePerGas) => {
baseFeesToTest.forEach((baseFeePerGas) => {
describe(`when baseFee is ${baseFeePerGas}, maxFeePerGas is ${maxFeePerGas} and tip is ${maxPriorityFeePerGas}`, () => {
const maximum = maxFeePerGas;
const minimum = baseFeePerGas + maxPriorityFeePerGas;
const expectedEffectiveGasPrice =
minimum < maximum ? minimum : maximum;
const results = gasLimitsToTest.map((gasLimit) => {
const gasLimitHex = addHexPrefix(gasLimit.toString(16));
const result = conversionUtil(
getMinimumGasTotalInHexWei({
gasLimit: gasLimitHex,
maxFeePerGas: addHexPrefix(maxFeePerGas.toString(16)),
maxPriorityFeePerGas: addHexPrefix(
maxPriorityFeePerGas.toString(16),
),
baseFeePerGas: addHexPrefix(baseFeePerGas.toString(16)),
}),
{ fromNumericBase: 'hex', toNumericBase: 'dec' },
);
return { result, gasLimit };
});
it(`should use an effective gasPrice of ${expectedEffectiveGasPrice}`, () => {
expect(
results.every(({ result, gasLimit }) => {
const effectiveGasPrice = Number(result) / gasLimit;
return effectiveGasPrice === expectedEffectiveGasPrice;
}),
).toBe(true);
});
results.forEach(({ result, gasLimit }) => {
const expectedResult = (
expectedEffectiveGasPrice * gasLimit
).toString();
it(`returns ${expectedResult} when provided gasLimit: ${gasLimit}`, () => {
expect(result).toStrictEqual(expectedResult);
});
});
});
});
});
});
});
});
describe('when using legacy fields', () => {
describe('getMaximumGasTotalInHexWei', () => {
feesToTest.forEach((gasPrice) => {
describe(`when gasPrice is ${gasPrice}`, () => {
gasLimitsToTest.forEach((gasLimit) => {
const expectedResult = (gasLimit * gasPrice).toString();
const gasLimitHex = addHexPrefix(gasLimit.toString(16));
it(`returns ${expectedResult} when provided gasLimit of ${gasLimit}`, () => {
expect(
conversionUtil(
getMaximumGasTotalInHexWei({
gasLimit: gasLimitHex,
gasPrice: addHexPrefix(gasPrice.toString(16)),
}),
{ fromNumericBase: 'hex', toNumericBase: 'dec' },
),
).toStrictEqual(expectedResult);
});
});
});
});
});
describe('getMinimumGasTotalInHexWei', () => {
feesToTest.forEach((gasPrice) => {
describe(`when gasPrice is ${gasPrice}`, () => {
gasLimitsToTest.forEach((gasLimit) => {
const expectedResult = (gasLimit * gasPrice).toString();
const gasLimitHex = addHexPrefix(gasLimit.toString(16));
it(`returns ${expectedResult} when provided gasLimit of ${gasLimit}`, () => {
expect(
conversionUtil(
getMinimumGasTotalInHexWei({
gasLimit: gasLimitHex,
gasPrice: addHexPrefix(gasPrice.toString(16)),
}),
{
fromNumericBase: 'hex',
toNumericBase: 'dec',
},
),
).toStrictEqual(expectedResult);
});
});
});
});
});
});
});