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

Fix fallback gas estimation (#19746)

* Fix fallback gas estimation

Our fallback gas estimation was failing due to a bug in the
`@metamask/controller-utils` package. This was causing gas estimation
to fail completely on certain networks (those not supported by our gas
estimation APIs and non EIP-1559 compatibile), and it was causing the
fallback gas estimation operation (in case our API was down) to fail
across all networks.

Fixes https://github.com/MetaMask/metamask-extension/issues/19735

* Add e2e tests

E2E tests have been added to capture gas estimation. Cases are added
for our API, for the fallback estimate, and for non-EIP-1559 estimates.

As part of this work, the legacy gas API had to be disabled. This was
being used in e2e tests but was dead code in production. It needed to
be disabled to ensure the code under test was reachable.

* Fix gas API referenced in e2e test

* Update unit test snapshots
This commit is contained in:
Mark Stacey 2023-06-26 16:13:16 -02:30 committed by GitHub
parent 0c047acdbf
commit ec7e7fdf6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 210 additions and 16 deletions

View File

@ -568,12 +568,8 @@ export default class MetamaskController extends EventEmitter {
),
getCurrentAccountEIP1559Compatibility:
this.getCurrentAccountEIP1559Compatibility.bind(this),
legacyAPIEndpoint: `${gasApiBaseUrl}/networks/<chain_id>/gasPrices`,
EIP1559APIEndpoint: `${gasApiBaseUrl}/networks/<chain_id>/suggestedGasFees`,
getCurrentNetworkLegacyGasAPICompatibility: () => {
const { chainId } = this.networkController.state.providerConfig;
return process.env.IN_TEST || chainId === CHAIN_IDS.MAINNET;
},
getCurrentNetworkLegacyGasAPICompatibility: () => false,
getChainId: () => this.networkController.state.providerConfig.chainId,
});

View File

@ -229,7 +229,7 @@
"@metamask/base-controller": "^3.0.0",
"@metamask/browser-passworder": "^4.1.0",
"@metamask/contract-metadata": "^2.3.1",
"@metamask/controller-utils": "^4.0.0",
"@metamask/controller-utils": "^4.0.1",
"@metamask/design-tokens": "^1.9.0",
"@metamask/desktop": "^0.3.0",
"@metamask/eth-json-rpc-middleware": "^11.0.0",

View File

@ -0,0 +1,171 @@
const {
convertToHexValue,
withFixtures,
logInWithBalanceValidation,
} = require('../helpers');
const FixtureBuilder = require('../fixture-builder');
describe('Gas estimates generated by MetaMask', function () {
const baseGanacheOptions = {
accounts: [
{
secretKey:
'0x7C9529A67102755B7E6102D6D950AC5D5863C98713805CEC576B945B15B71EAC',
balance: convertToHexValue(25000000000000000000),
},
],
};
const preLondonGanacheOptions = {
...baseGanacheOptions,
hardfork: 'berlin',
};
const postLondonGanacheOptions = {
...baseGanacheOptions,
hardfork: 'london',
};
describe('Send on a network that is EIP-1559 compatible', function () {
it('show expected gas defaults', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
ganacheOptions: postLondonGanacheOptions,
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
await driver.navigate();
await logInWithBalanceValidation(driver, ganacheServer);
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill(
'input[placeholder="Enter public address (0x) or ENS name"]',
'0x2f318C334780961FB129D2a6c30D0763d9a5C970',
);
await driver.fill('.unit-input__input', '1');
// Check that the gas estimation is what we expect
await driver.findElement({
cass: '[data-testid="confirm-gas-display"]',
text: '0.00043983',
});
},
);
});
it('show expected gas defaults when API is down', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
ganacheOptions: postLondonGanacheOptions,
testSpecificMock: (mockServer) => {
mockServer
.forGet(
'https://gas-api.metaswap.codefi.network/networks/1337/suggestedGasFees',
)
.thenCallback(() => {
return {
json: {
error: 'cannot get gas prices for chain id 1337',
},
statusCode: 503,
};
});
},
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
await driver.navigate();
await logInWithBalanceValidation(driver, ganacheServer);
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill(
'input[placeholder="Enter public address (0x) or ENS name"]',
'0x2f318C334780961FB129D2a6c30D0763d9a5C970',
);
await driver.fill('.unit-input__input', '1');
// Check that the gas estimation is what we expect
await driver.findElement({
cass: '[data-testid="confirm-gas-display"]',
text: '0.00043983',
});
},
);
});
it('show expected gas defaults when the network is not supported', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
ganacheOptions: postLondonGanacheOptions,
testSpecificMock: (mockServer) => {
mockServer
.forGet(
'https://gas-api.metaswap.codefi.network/networks/1337/suggestedGasFees',
)
.thenCallback(() => {
return {
statusCode: 422,
};
});
},
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
await driver.navigate();
await logInWithBalanceValidation(driver, ganacheServer);
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill(
'input[placeholder="Enter public address (0x) or ENS name"]',
'0x2f318C334780961FB129D2a6c30D0763d9a5C970',
);
await driver.fill('.unit-input__input', '1');
// Check that the gas estimation is what we expect
await driver.findElement({
cass: '[data-testid="confirm-gas-display"]',
text: '0.00043983',
});
},
);
});
});
describe('Send on a network that is not EIP-1559 compatible', function () {
it('show expected gas defaults', async function () {
await withFixtures(
{
fixtures: new FixtureBuilder().build(),
ganacheOptions: preLondonGanacheOptions,
title: this.test.title,
},
async ({ driver, ganacheServer }) => {
await driver.navigate();
await logInWithBalanceValidation(driver, ganacheServer);
await driver.clickElement('[data-testid="eth-overview-send"]');
await driver.fill(
'input[placeholder="Enter public address (0x) or ENS name"]',
'0x2f318C334780961FB129D2a6c30D0763d9a5C970',
);
await driver.fill('.unit-input__input', '1');
// Check that the gas estimation is what we expect
await driver.findElement({
cass: '[data-testid="confirm-gas-display"]',
text: '0.000042',
});
},
);
});
});
});

View File

@ -4,6 +4,7 @@ exports[`ConfirmGasDisplay should match snapshot 1`] = `
<div>
<div
class="transaction-detail-item"
data-testid="confirm-gas-display"
>
<div
class="transaction-detail-item__row"

View File

@ -28,11 +28,15 @@ const ConfirmGasDisplay = ({ userAcknowledgedGasMissing = false }) => {
checkNetworkAndAccountSupports1559,
);
const supportsEIP1559 = networkAndAccountSupports1559 && !isLegacyTxn;
const dataTestId = 'confirm-gas-display';
return supportsEIP1559 ? (
<GasDetailsItem userAcknowledgedGasMissing={userAcknowledgedGasMissing} />
<GasDetailsItem
data-testid={dataTestId}
userAcknowledgedGasMissing={userAcknowledgedGasMissing}
/>
) : (
<ConfirmLegacyGasDisplay />
<ConfirmLegacyGasDisplay data-testid={dataTestId} />
);
};

View File

@ -1,4 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { useI18nContext } from '../../../../hooks/useI18nContext';
@ -31,7 +32,7 @@ import { Icon, IconName } from '../../../component-library';
const renderHeartBeatIfNotInTest = () =>
process.env.IN_TEST ? null : <LoadingHeartBeat />;
const ConfirmLegacyGasDisplay = () => {
const ConfirmLegacyGasDisplay = ({ 'data-testid': dataTestId } = {}) => {
const t = useI18nContext();
// state selectors
@ -55,6 +56,7 @@ const ConfirmLegacyGasDisplay = () => {
return [
<TransactionDetailItem
key="legacy-total-item"
data-testid={dataTestId}
detailTitle={t('transactionDetailLayer2GasHeading')}
detailTotal={
<UserPreferencedCurrencyDisplay
@ -88,6 +90,7 @@ const ConfirmLegacyGasDisplay = () => {
return (
<TransactionDetailItem
key="legacy-gas-details"
data-testid={dataTestId}
detailTitle={
dappSuggestedGasFees ? (
<>
@ -186,4 +189,8 @@ const ConfirmLegacyGasDisplay = () => {
);
};
ConfirmLegacyGasDisplay.propTypes = {
'data-testid': PropTypes.string,
};
export default ConfirmLegacyGasDisplay;

View File

@ -24,7 +24,10 @@ import { useDraftTransactionWithTxParams } from '../../../hooks/useDraftTransact
import { PriorityLevels } from '../../../../shared/constants/gas';
import GasDetailsItemTitle from './gas-details-item-title';
const GasDetailsItem = ({ userAcknowledgedGasMissing = false }) => {
const GasDetailsItem = ({
'data-testid': dataTestId,
userAcknowledgedGasMissing = false,
}) => {
const t = useI18nContext();
const draftTransaction = useSelector(getCurrentDraftTransaction);
const transactionData = useDraftTransactionWithTxParams();
@ -64,6 +67,7 @@ const GasDetailsItem = ({ userAcknowledgedGasMissing = false }) => {
return (
<TransactionDetailItem
key="gas-details-item"
data-testid={dataTestId}
detailTitle={<GasDetailsItemTitle />}
detailTitleColor={TextColor.textDefault}
detailText={
@ -137,6 +141,7 @@ const GasDetailsItem = ({ userAcknowledgedGasMissing = false }) => {
};
GasDetailsItem.propTypes = {
'data-testid': PropTypes.string,
userAcknowledgedGasMissing: PropTypes.bool,
};

View File

@ -14,6 +14,7 @@ import {
} from '../../../helpers/constants/design-system';
export default function TransactionDetailItem({
'data-testid': dataTestId,
detailTitle = '',
detailText,
detailTitleColor = Color.textDefault,
@ -24,7 +25,7 @@ export default function TransactionDetailItem({
flexWidthValues = false,
}) {
return (
<div className="transaction-detail-item">
<div className="transaction-detail-item" data-testid={dataTestId}>
<div className="transaction-detail-item__row">
<Typography
color={detailTitleColor}
@ -91,6 +92,10 @@ export default function TransactionDetailItem({
}
TransactionDetailItem.propTypes = {
/**
* An identifier for use in end-to-end tests.
*/
'data-testid': PropTypes.string,
/**
* Detail title text wrapped in Typography component.
*/

View File

@ -388,6 +388,7 @@ exports[`ConfirmSendEther should render correct information for for confirm send
>
<div
class="transaction-detail-item"
data-testid="confirm-gas-display"
>
<div
class="transaction-detail-item__row"

View File

@ -335,6 +335,7 @@ exports[`Confirm Transaction Base should match snapshot 1`] = `
>
<div
class="transaction-detail-item"
data-testid="confirm-gas-display"
>
<div
class="transaction-detail-item__row"

View File

@ -181,6 +181,7 @@ exports[`SendContent Component render should match snapshot 1`] = `
>
<div
class="transaction-detail-item"
data-testid="confirm-gas-display"
>
<div
class="transaction-detail-item__row"

View File

@ -4007,18 +4007,20 @@ __metadata:
languageName: node
linkType: hard
"@metamask/controller-utils@npm:^4.0.0":
version: 4.0.0
resolution: "@metamask/controller-utils@npm:4.0.0"
"@metamask/controller-utils@npm:^4.0.0, @metamask/controller-utils@npm:^4.0.1":
version: 4.0.1
resolution: "@metamask/controller-utils@npm:4.0.1"
dependencies:
"@metamask/utils": ^5.0.2
"@spruceid/siwe-parser": 1.1.3
babel-runtime: ^6.26.0
eth-ens-namehash: ^2.0.8
eth-query: ^2.1.2
eth-rpc-errors: ^4.0.2
ethereumjs-util: ^7.0.10
ethjs-unit: ^0.1.6
fast-deep-equal: ^3.1.3
checksum: 3efdaf9b0c9f6d3bb633eeb926e78bedd637b0e042bb1e3974b9829e38456709a2f26d02405499934239351be8bff1854e267df8d0a522738d630bac0aadb732
checksum: c0c3b8b5a363aff6a7996e349f47fdc77d3b31846546dc937669a2ab2306c3ed3c2122cf682a1146c6ba3eeeabe491f8ba7aa0157d62843689242779bb506017
languageName: node
linkType: hard
@ -24433,7 +24435,7 @@ __metadata:
"@metamask/base-controller": ^3.0.0
"@metamask/browser-passworder": ^4.1.0
"@metamask/contract-metadata": ^2.3.1
"@metamask/controller-utils": ^4.0.0
"@metamask/controller-utils": ^4.0.1
"@metamask/design-tokens": ^1.9.0
"@metamask/desktop": ^0.3.0
"@metamask/eslint-config": ^9.0.0