Skip to content

Commit 96b80f0

Browse files
committed
chore(network-controller): Simplify network switch detection in lookupNetwork
`lookupNetwork` operates on an arbitrary network or, if given nothing, the globally selected network. The behavior between these two cases is largely the same, except that in the "selected network" case, it publishes one of two events, `NetworkController:infuraIsBlocked` or `NetworkController:infuraIsUnblocked`. This happens after making a request to the network, and while the request is running, it is possible for the network to change. This creates a potential problem because if that happens, the events that we want to fire would no longer represent the network they were intended to represent. `lookupNetwork` accounts for this by subscribing to `NetworkController:networkDidChange` before starting the request; if it's fired, then it exits early (with the presumption that the network switch called its own version of `lookupNetwork` and will publish `NetworkController:infuraIsBlocked` or `NetworkController:infuraIsUnblocked` at that point in time). However, this approach to detect a network switch is complicated and unnecessary. We can simply capture `selectedNetworkClientId` before we start the request, ask what it is afterward, and compare the two to know whether the network switched. This means that we can bring the implementation of the two branches within `lookupNetwork` closer together (and we can eliminate some tests).
1 parent 4c0c12e commit 96b80f0

File tree

2 files changed

+35
-216
lines changed

2 files changed

+35
-216
lines changed

packages/network-controller/src/NetworkController.ts

Lines changed: 35 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,8 +1558,8 @@ export class NetworkController extends BaseController<
15581558
}
15591559

15601560
/**
1561-
* Uses a request for the latest block to gather the following information on
1562-
* the given network:
1561+
* Uses a request for the latest block to gather and record the following
1562+
* information on the given network:
15631563
*
15641564
* - The connectivity status: whether it is available, geo-blocked (Infura
15651565
* only), unavailable, or unknown
@@ -1570,7 +1570,7 @@ export class NetworkController extends BaseController<
15701570
* If no ID is provided, uses the currently selected network.
15711571
* @returns The resulting metadata for the network.
15721572
*/
1573-
async #determineNetworkMetadata(networkClientId: NetworkClientId) {
1573+
async #captureNetworkMetadata(networkClientId: NetworkClientId) {
15741574
// Force TypeScript to use one of the two overloads explicitly
15751575
const networkClient = isInfuraNetworkType(networkClientId)
15761576
? this.getNetworkClientById(networkClientId)
@@ -1631,6 +1631,22 @@ export class NetworkController extends BaseController<
16311631
}
16321632
}
16331633

1634+
this.update((state) => {
1635+
if (state.networksMetadata[networkClientId] === undefined) {
1636+
state.networksMetadata[networkClientId] = {
1637+
status: NetworkStatus.Unknown,
1638+
EIPS: {},
1639+
};
1640+
}
1641+
const meta = state.networksMetadata[networkClientId];
1642+
meta.status = networkStatus;
1643+
if (isEIP1559Compatible === undefined) {
1644+
delete meta.EIPS[1559];
1645+
} else {
1646+
meta.EIPS[1559] = isEIP1559Compatible;
1647+
}
1648+
});
1649+
16341650
return { isInfura, networkStatus, isEIP1559Compatible };
16351651
}
16361652

@@ -1686,13 +1702,7 @@ export class NetworkController extends BaseController<
16861702
* @param networkClientId - The ID of the network client to inspect.
16871703
*/
16881704
async #lookupGivenNetwork(networkClientId: NetworkClientId) {
1689-
const { networkStatus, isEIP1559Compatible } =
1690-
await this.#determineNetworkMetadata(networkClientId);
1691-
this.#updateMetadataForNetwork(
1692-
networkClientId,
1693-
networkStatus,
1694-
isEIP1559Compatible,
1695-
);
1705+
await this.#captureNetworkMetadata(networkClientId);
16961706
}
16971707

16981708
/**
@@ -1713,113 +1723,30 @@ export class NetworkController extends BaseController<
17131723
return;
17141724
}
17151725

1716-
let networkChanged = false;
1717-
const listener = () => {
1718-
networkChanged = true;
1719-
try {
1720-
this.messagingSystem.unsubscribe(
1721-
'NetworkController:networkDidChange',
1722-
listener,
1723-
);
1724-
} catch (error) {
1725-
// In theory, this `catch` should not be necessary given that this error
1726-
// would occur "inside" of the call to `#determineEIP1559Compatibility`
1727-
// below and so it should be caught by the `try`/`catch` below (it is
1728-
// impossible to reproduce in tests for that reason). However, somehow
1729-
// it occurs within Mobile and so we have to add our own `try`/`catch`
1730-
// here.
1731-
/* istanbul ignore next */
1732-
if (
1733-
!(error instanceof Error) ||
1734-
error.message !==
1735-
'Subscription not found for event: NetworkController:networkDidChange'
1736-
) {
1737-
// Again, this error should not happen and is impossible to reproduce
1738-
// in tests.
1739-
/* istanbul ignore next */
1740-
throw error;
1741-
}
1742-
}
1743-
};
1744-
this.messagingSystem.subscribe(
1745-
'NetworkController:networkDidChange',
1746-
listener,
1747-
);
1748-
1749-
const { isInfura, networkStatus, isEIP1559Compatible } =
1750-
await this.#determineNetworkMetadata(this.state.selectedNetworkClientId);
1751-
1752-
if (networkChanged) {
1753-
// If the network has changed, then `lookupNetwork` either has been or is
1754-
// in the process of being called, so we don't need to go further.
1755-
return;
1756-
}
1726+
// Capture up front in case the network is switched while awaiting the
1727+
// network request
1728+
const { selectedNetworkClientId } = this.state;
17571729

1758-
try {
1759-
this.messagingSystem.unsubscribe(
1760-
'NetworkController:networkDidChange',
1761-
listener,
1762-
);
1763-
} catch (error) {
1764-
if (
1765-
!(error instanceof Error) ||
1766-
error.message !==
1767-
'Subscription not found for event: NetworkController:networkDidChange'
1768-
) {
1769-
throw error;
1770-
}
1771-
}
1772-
1773-
this.#updateMetadataForNetwork(
1730+
const { isInfura, networkStatus } = await this.#captureNetworkMetadata(
17741731
this.state.selectedNetworkClientId,
1775-
networkStatus,
1776-
isEIP1559Compatible,
17771732
);
17781733

1779-
if (isInfura) {
1780-
if (networkStatus === NetworkStatus.Available) {
1734+
if (selectedNetworkClientId === this.state.selectedNetworkClientId) {
1735+
if (isInfura) {
1736+
if (networkStatus === NetworkStatus.Available) {
1737+
this.messagingSystem.publish('NetworkController:infuraIsUnblocked');
1738+
} else if (networkStatus === NetworkStatus.Blocked) {
1739+
this.messagingSystem.publish('NetworkController:infuraIsBlocked');
1740+
}
1741+
} else {
1742+
// Always publish infuraIsUnblocked regardless of network status to
1743+
// prevent consumers from being stuck in a blocked state if they were
1744+
// previously connected to an Infura network that was blocked
17811745
this.messagingSystem.publish('NetworkController:infuraIsUnblocked');
1782-
} else if (networkStatus === NetworkStatus.Blocked) {
1783-
this.messagingSystem.publish('NetworkController:infuraIsBlocked');
17841746
}
1785-
} else {
1786-
// Always publish infuraIsUnblocked regardless of network status to
1787-
// prevent consumers from being stuck in a blocked state if they were
1788-
// previously connected to an Infura network that was blocked
1789-
this.messagingSystem.publish('NetworkController:infuraIsUnblocked');
17901747
}
17911748
}
17921749

1793-
/**
1794-
* Updates the metadata for the given network in state.
1795-
*
1796-
* @param networkClientId - The associated network client ID.
1797-
* @param networkStatus - The network status to store in state.
1798-
* @param isEIP1559Compatible - The EIP-1559 compatibility status to
1799-
* store in state.
1800-
*/
1801-
#updateMetadataForNetwork(
1802-
networkClientId: NetworkClientId,
1803-
networkStatus: NetworkStatus,
1804-
isEIP1559Compatible: boolean | undefined,
1805-
) {
1806-
this.update((state) => {
1807-
if (state.networksMetadata[networkClientId] === undefined) {
1808-
state.networksMetadata[networkClientId] = {
1809-
status: NetworkStatus.Unknown,
1810-
EIPS: {},
1811-
};
1812-
}
1813-
const meta = state.networksMetadata[networkClientId];
1814-
meta.status = networkStatus;
1815-
if (isEIP1559Compatible === undefined) {
1816-
delete meta.EIPS[1559];
1817-
} else {
1818-
meta.EIPS[1559] = isEIP1559Compatible;
1819-
}
1820-
});
1821-
}
1822-
18231750
/**
18241751
* Convenience method to update provider network type settings.
18251752
*

packages/network-controller/tests/NetworkController.test.ts

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -2225,55 +2225,6 @@ describe('NetworkController', () => {
22252225
});
22262226
});
22272227

2228-
describe('if removing the networkDidChange subscription fails for an unknown reason', () => {
2229-
it('re-throws the error', async () => {
2230-
const infuraProjectId = 'some-infura-project-id';
2231-
2232-
await withController(
2233-
{
2234-
state: {
2235-
selectedNetworkClientId: infuraNetworkType,
2236-
},
2237-
infuraProjectId,
2238-
},
2239-
async ({ controller, messenger }) => {
2240-
const fakeProvider = buildFakeProvider([
2241-
// Called during provider initialization
2242-
{
2243-
request: {
2244-
method: 'eth_getBlockByNumber',
2245-
},
2246-
response: SUCCESSFUL_ETH_GET_BLOCK_BY_NUMBER_RESPONSE,
2247-
},
2248-
// Called via `lookupNetwork` directly
2249-
{
2250-
request: {
2251-
method: 'eth_getBlockByNumber',
2252-
},
2253-
response: SUCCESSFUL_ETH_GET_BLOCK_BY_NUMBER_RESPONSE,
2254-
},
2255-
]);
2256-
const fakeNetworkClient = buildFakeClient(fakeProvider);
2257-
createNetworkClientMock.mockReturnValue(fakeNetworkClient);
2258-
await controller.initializeProvider();
2259-
2260-
const lookupNetworkPromise = controller.lookupNetwork();
2261-
const error = new Error('oops');
2262-
jest
2263-
.spyOn(messenger, 'unsubscribe')
2264-
.mockImplementation((eventType) => {
2265-
// This is okay.
2266-
// eslint-disable-next-line jest/no-conditional-in-test
2267-
if (eventType === 'NetworkController:networkDidChange') {
2268-
throw error;
2269-
}
2270-
});
2271-
await expect(lookupNetworkPromise).rejects.toThrow(error);
2272-
},
2273-
);
2274-
});
2275-
});
2276-
22772228
lookupNetworkTests({
22782229
expectedNetworkClientType: NetworkClientType.Infura,
22792230
expectedNetworkClientId: infuraNetworkType,
@@ -2726,65 +2677,6 @@ describe('NetworkController', () => {
27262677
});
27272678
});
27282679

2729-
describe('if removing the networkDidChange subscription fails for an unknown reason', () => {
2730-
it('re-throws the error', async () => {
2731-
const infuraProjectId = 'some-infura-project-id';
2732-
2733-
await withController(
2734-
{
2735-
state: {
2736-
selectedNetworkClientId: 'AAAA-AAAA-AAAA-AAAA',
2737-
networkConfigurationsByChainId: {
2738-
'0x1337': buildCustomNetworkConfiguration({
2739-
chainId: '0x1337',
2740-
rpcEndpoints: [
2741-
buildCustomRpcEndpoint({
2742-
networkClientId: 'AAAA-AAAA-AAAA-AAAA',
2743-
}),
2744-
],
2745-
}),
2746-
},
2747-
},
2748-
infuraProjectId,
2749-
},
2750-
async ({ controller, messenger }) => {
2751-
const fakeProvider = buildFakeProvider([
2752-
// Called during provider initialization
2753-
{
2754-
request: {
2755-
method: 'eth_getBlockByNumber',
2756-
},
2757-
response: SUCCESSFUL_ETH_GET_BLOCK_BY_NUMBER_RESPONSE,
2758-
},
2759-
// Called via `lookupNetwork` directly
2760-
{
2761-
request: {
2762-
method: 'eth_getBlockByNumber',
2763-
},
2764-
response: SUCCESSFUL_ETH_GET_BLOCK_BY_NUMBER_RESPONSE,
2765-
},
2766-
]);
2767-
const fakeNetworkClient = buildFakeClient(fakeProvider);
2768-
createNetworkClientMock.mockReturnValue(fakeNetworkClient);
2769-
await controller.initializeProvider();
2770-
2771-
const lookupNetworkPromise = controller.lookupNetwork();
2772-
const error = new Error('oops');
2773-
jest
2774-
.spyOn(messenger, 'unsubscribe')
2775-
.mockImplementation((eventType) => {
2776-
// This is okay.
2777-
// eslint-disable-next-line jest/no-conditional-in-test
2778-
if (eventType === 'NetworkController:networkDidChange') {
2779-
throw error;
2780-
}
2781-
});
2782-
await expect(lookupNetworkPromise).rejects.toThrow(error);
2783-
},
2784-
);
2785-
});
2786-
});
2787-
27882680
lookupNetworkTests({
27892681
expectedNetworkClientType: NetworkClientType.Custom,
27902682
expectedNetworkClientId: 'AAAA-AAAA-AAAA-AAAA',

0 commit comments

Comments
 (0)