Skip to content

Commit 441df67

Browse files
committed
Gracefully handle requesting gas fee estimates for nonexistent network
Currently, when GasFeeController tries to fetch gas fee estimates for a network that has been removed, it consults NetworkController for the corresponding network client, and NetworkController throws an error. This commit gracefully handles this situation by returning an empty set of estimates.
1 parent 9e83960 commit 441df67

File tree

8 files changed

+106
-41
lines changed

8 files changed

+106
-41
lines changed

packages/gas-fee-controller/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- Bump `@metamask/base-controller` from `^7.0.0` to `^7.1.0` ([#5079](https://github.com/MetaMask/core/pull/5079))
1313

14+
### Fixed
15+
16+
- Fix `fetchGasFeeEstimates` so that it no longer throws an error if given a reference to a network that does not exist ([#5084](https://github.com/MetaMask/core/pull/5084))
17+
1418
## [22.0.2]
1519

1620
### Changed

packages/gas-fee-controller/src/GasFeeController.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,7 @@ describe('GasFeeController', () => {
999999
);
10001000
});
10011001
});
1002+
10021003
describe('when passed a networkClientId in options object', () => {
10031004
const getDefaultOptions = () => ({
10041005
getIsEIP1559Compatible: jest.fn().mockResolvedValue(true),
@@ -1033,6 +1034,35 @@ describe('GasFeeController', () => {
10331034
);
10341035
});
10351036

1037+
it("should not update state if the network client doesn't exist", async () => {
1038+
await setupGasFeeController({
1039+
...getDefaultOptions(),
1040+
});
1041+
const initialState = gasFeeController.state;
1042+
1043+
await gasFeeController.fetchGasFeeEstimates({
1044+
networkClientId: 'nonexistent',
1045+
});
1046+
1047+
expect(gasFeeController.state).toBe(initialState);
1048+
});
1049+
1050+
it("should return an empty set of estimates if the network client doesn't exist", async () => {
1051+
await setupGasFeeController({
1052+
...getDefaultOptions(),
1053+
});
1054+
1055+
const estimates = await gasFeeController.fetchGasFeeEstimates({
1056+
networkClientId: 'nonexistent',
1057+
});
1058+
1059+
expect(estimates).toStrictEqual({
1060+
gasFeeEstimates: {},
1061+
estimatedGasFeeTimeBounds: {},
1062+
gasEstimateType: 'none',
1063+
});
1064+
});
1065+
10361066
it('should call determineGasFeeCalculations correctly', async () => {
10371067
await setupGasFeeController({
10381068
...getDefaultOptions(),

packages/gas-fee-controller/src/GasFeeController.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import {
99
toHex,
1010
} from '@metamask/controller-utils';
1111
import EthQuery from '@metamask/eth-query';
12-
import type {
13-
NetworkClientId,
14-
NetworkControllerGetEIP1559CompatibilityAction,
15-
NetworkControllerGetNetworkClientByIdAction,
16-
NetworkControllerGetStateAction,
17-
NetworkControllerNetworkDidChangeEvent,
18-
NetworkState,
19-
ProviderProxy,
12+
import {
13+
NoNetworkClientFoundError,
14+
type NetworkClientId,
15+
type NetworkControllerGetEIP1559CompatibilityAction,
16+
type NetworkControllerGetNetworkClientByIdAction,
17+
type NetworkControllerGetStateAction,
18+
type NetworkControllerNetworkDidChangeEvent,
19+
type NetworkState,
20+
type ProviderProxy,
2021
} from '@metamask/network-controller';
2122
import { StaticIntervalPollingController } from '@metamask/polling-controller';
2223
import type { Hex } from '@metamask/utils';
@@ -29,6 +30,9 @@ import {
2930
fetchEthGasPriceEstimate,
3031
calculateTimeEstimate,
3132
} from './gas-util';
33+
import { createModuleLogger, projectLogger } from './logger';
34+
35+
const log = createModuleLogger(projectLogger, 'GasFeeController');
3236

3337
export const LEGACY_GAS_PRICES_API_URL = `https://api.metaswap.codefi.network/gasPrices`;
3438

@@ -443,10 +447,23 @@ export class GasFeeController extends StaticIntervalPollingController<GasFeePoll
443447
decimalChainId: number;
444448

445449
if (networkClientId !== undefined) {
446-
const networkClient = this.messagingSystem.call(
447-
'NetworkController:getNetworkClientById',
448-
networkClientId,
449-
);
450+
let networkClient;
451+
try {
452+
networkClient = this.messagingSystem.call(
453+
'NetworkController:getNetworkClientById',
454+
networkClientId,
455+
);
456+
} catch (error) {
457+
if (error instanceof NoNetworkClientFoundError) {
458+
log(error.message);
459+
return {
460+
gasFeeEstimates: {},
461+
estimatedGasFeeTimeBounds: {},
462+
gasEstimateType: GAS_ESTIMATE_TYPES.NONE,
463+
};
464+
}
465+
throw error;
466+
}
450467
isLegacyGasAPICompatible = networkClient.configuration.chainId === '0x38';
451468

452469
decimalChainId = convertHexToDecimal(networkClient.configuration.chainId);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* istanbul ignore file */
2+
3+
import { createProjectLogger, createModuleLogger } from '@metamask/utils';
4+
5+
export const projectLogger = createProjectLogger('gas-fee-controller');
6+
7+
export { createModuleLogger };

packages/network-controller/src/NetworkController.ts

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import type {
3333
ProxyWithAccessibleTarget,
3434
} from './create-auto-managed-network-client';
3535
import { createAutoManagedNetworkClient } from './create-auto-managed-network-client';
36+
import { NoNetworkClientFoundError } from './errors';
3637
import { projectLogger, createModuleLogger } from './logger';
3738
import { NetworkClientType } from './types';
3839
import type {
@@ -41,6 +42,9 @@ import type {
4142
CustomNetworkClientConfiguration,
4243
InfuraNetworkClientConfiguration,
4344
NetworkClientConfiguration,
45+
NetworkClientId,
46+
BuiltInNetworkClientId,
47+
CustomNetworkClientId,
4448
} from './types';
4549

4650
const debugLog = createModuleLogger(projectLogger, 'NetworkController');
@@ -294,21 +298,6 @@ function isErrorWithCode(error: unknown): error is { code: string | number } {
294298
return typeof error === 'object' && error !== null && 'code' in error;
295299
}
296300

297-
/**
298-
* The string that uniquely identifies an Infura network client.
299-
*/
300-
export type BuiltInNetworkClientId = InfuraNetworkType;
301-
302-
/**
303-
* The string that uniquely identifies a custom network client.
304-
*/
305-
export type CustomNetworkClientId = string;
306-
307-
/**
308-
* The string that uniquely identifies a network client.
309-
*/
310-
export type NetworkClientId = BuiltInNetworkClientId | CustomNetworkClientId;
311-
312301
/**
313302
* Extra information about each network, such as whether it is accessible or
314303
* blocked and whether it supports EIP-1559, keyed by network client ID.
@@ -1107,11 +1096,7 @@ export class NetworkController extends BaseController<
11071096
// This is impossible to reach
11081097
/* istanbul ignore if */
11091098
if (!infuraNetworkClient) {
1110-
throw new Error(
1111-
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
1112-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
1113-
`No Infura network client was found with the ID "${networkClientId}".`,
1114-
);
1099+
throw NoNetworkClientFoundError.create(networkClientId);
11151100
}
11161101
return infuraNetworkClient;
11171102
}
@@ -1121,11 +1106,7 @@ export class NetworkController extends BaseController<
11211106
networkClientId
11221107
];
11231108
if (!customNetworkClient) {
1124-
throw new Error(
1125-
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
1126-
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
1127-
`No custom network client was found with the ID "${networkClientId}".`,
1128-
);
1109+
throw NoNetworkClientFoundError.create(networkClientId);
11291110
}
11301111
return customNetworkClient;
11311112
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { NetworkClientId } from './types';
2+
3+
export class NoNetworkClientFoundError extends Error {
4+
static create(networkClientId: NetworkClientId) {
5+
// ESLint is confused here; this is guaranteed to be a string.
6+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
7+
return new this(`No network client found with the ID "${networkClientId}"`);
8+
}
9+
}

packages/network-controller/src/index.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,6 @@ export type {
33
Block,
44
NetworkMetadata,
55
NetworkConfiguration,
6-
BuiltInNetworkClientId,
7-
CustomNetworkClientId,
8-
NetworkClientId,
96
NetworksMetadata,
107
NetworkState,
118
BlockTrackerProxy,
@@ -44,11 +41,16 @@ export {
4441
RpcEndpointType,
4542
} from './NetworkController';
4643
export * from './constants';
47-
export type { BlockTracker, Provider } from './types';
4844
export type {
45+
BlockTracker,
46+
Provider,
4947
NetworkClientConfiguration,
5048
InfuraNetworkClientConfiguration,
5149
CustomNetworkClientConfiguration,
50+
BuiltInNetworkClientId,
51+
CustomNetworkClientId,
52+
NetworkClientId,
5253
} from './types';
5354
export { NetworkClientType } from './types';
5455
export type { NetworkClient } from './create-network-client';
56+
export { NoNetworkClientFoundError } from './errors';

packages/network-controller/src/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,18 @@ export type InfuraNetworkClientConfiguration = {
4646
export type NetworkClientConfiguration =
4747
| CustomNetworkClientConfiguration
4848
| InfuraNetworkClientConfiguration;
49+
50+
/**
51+
* The string that uniquely identifies an Infura network client.
52+
*/
53+
export type BuiltInNetworkClientId = InfuraNetworkType;
54+
55+
/**
56+
* The string that uniquely identifies a custom network client.
57+
*/
58+
export type CustomNetworkClientId = string;
59+
60+
/**
61+
* The string that uniquely identifies a network client.
62+
*/
63+
export type NetworkClientId = BuiltInNetworkClientId | CustomNetworkClientId;

0 commit comments

Comments
 (0)