Skip to content

Commit

Permalink
Add network change events
Browse files Browse the repository at this point in the history
The network controller now emits the events `networkWillChange` and
`networkDidChange` when the network is being refreshed. The event
`networkWillChange` is emitted before the switch begins (before the
network status is cleared), and the `networkDidChange` event is emitted
after the new provider is setup (but before it has finished
initializing).

Closes #1210
  • Loading branch information
Gudahtt committed May 8, 2023
1 parent b91b9e5 commit 5ed561a
Show file tree
Hide file tree
Showing 2 changed files with 231 additions and 0 deletions.
23 changes: 23 additions & 0 deletions packages/network-controller/src/NetworkController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,25 @@ export type NetworkControllerStateChangeEvent = {
payload: [NetworkState, Patch[]];
};

/**
* `networkWillChange` is published when the current network is about to be
* switched, but the new provider has not been created and no state changes have
* occurred yet.
*/
export type NetworkControllerNetworkWillChangeEvent = {
type: 'NetworkController:networkWillChange';
payload: [];
};

/**
* `networkDidChange` is published after a provider has been created for a newly
* switched network (but before the network has been confirmed to be available).
*/
export type NetworkControllerNetworkDidChangeEvent = {
type: 'NetworkController:networkDidChange';
payload: [];
};

/**
* `infuraIsBlocked` is published after the network is switched to an Infura
* network, but when Infura returns an error blocking the user based on their
Expand All @@ -183,6 +202,8 @@ export type NetworkControllerInfuraIsUnblockedEvent = {

export type NetworkControllerEvents =
| NetworkControllerStateChangeEvent
| NetworkControllerNetworkWillChangeEvent
| NetworkControllerNetworkDidChangeEvent
| NetworkControllerInfuraIsBlockedEvent
| NetworkControllerInfuraIsUnblockedEvent;

Expand Down Expand Up @@ -365,6 +386,7 @@ export class NetworkController extends BaseControllerV2<
}

async #refreshNetwork() {
this.messagingSystem.publish('NetworkController:networkWillChange');
this.update((state) => {
state.networkId = null;
state.networkStatus = NetworkStatus.Unknown;
Expand All @@ -374,6 +396,7 @@ export class NetworkController extends BaseControllerV2<
});
const { rpcUrl, type, chainId } = this.state.providerConfig;
this.#configureProvider(type, rpcUrl, chainId);
this.messagingSystem.publish('NetworkController:networkDidChange');
await this.lookupNetwork();
}

Expand Down
208 changes: 208 additions & 0 deletions packages/network-controller/tests/NetworkController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2332,6 +2332,94 @@ describe('NetworkController', () => {
describe('rollbackToPreviousProvider', () => {
for (const { networkType } of INFURA_NETWORKS) {
describe(`if the previous provider configuration had a type of "${networkType}"`, () => {
it('emits networkWillChange', async () => {
await withController(
{
state: {
providerConfig: buildProviderConfig({
type: networkType,
...BUILT_IN_NETWORKS[networkType],
}),
networkConfigurations: {
testNetworkConfiguration: {
id: 'testNetworkConfiguration',
rpcUrl: 'https://mock-rpc-url',
chainId: '0x1337',
ticker: 'TEST',
nickname: 'test network',
rpcPrefs: {
blockExplorerUrl: 'https://test-block-explorer.com',
},
},
},
},
infuraProjectId: 'some-infura-project-id',
},
async ({ controller, messenger }) => {
const fakeProvider = buildFakeProvider();
const fakeNetworkClient = buildFakeClient(fakeProvider);
mockCreateNetworkClient().mockReturnValue(fakeNetworkClient);
await controller.setActiveNetwork('testNetworkConfiguration');

const networkWillChange = await waitForPublishedEvents({
messenger,
eventType: 'NetworkController:networkWillChange',
operation: () => {
// Intentionally not awaited because we're capturing an event
// emitted partway through the operation
controller.rollbackToPreviousProvider();
},
});

expect(networkWillChange).toStrictEqual([[]]);
},
);
});

it('emits networkDidChange', async () => {
await withController(
{
state: {
providerConfig: buildProviderConfig({
type: networkType,
...BUILT_IN_NETWORKS[networkType],
}),
networkConfigurations: {
testNetworkConfiguration: {
id: 'testNetworkConfiguration',
rpcUrl: 'https://mock-rpc-url',
chainId: '0x1337',
ticker: 'TEST',
nickname: 'test network',
rpcPrefs: {
blockExplorerUrl: 'https://test-block-explorer.com',
},
},
},
},
infuraProjectId: 'some-infura-project-id',
},
async ({ controller, messenger }) => {
const fakeProvider = buildFakeProvider();
const fakeNetworkClient = buildFakeClient(fakeProvider);
mockCreateNetworkClient().mockReturnValue(fakeNetworkClient);
await controller.setActiveNetwork('testNetworkConfiguration');

const networkDidChange = await waitForPublishedEvents({
messenger,
eventType: 'NetworkController:networkDidChange',
operation: () => {
// Intentionally not awaited because we're capturing an event
// emitted partway through the operation
controller.rollbackToPreviousProvider();
},
});

expect(networkDidChange).toStrictEqual([[]]);
},
);
});

it('overwrites the the current provider configuration with the previous provider configuration', async () => {
await withController(
{
Expand Down Expand Up @@ -2890,6 +2978,72 @@ describe('NetworkController', () => {
}

describe(`if the previous provider configuration had a type of "rpc"`, () => {
it('emits networkWillChange', async () => {
await withController(
{
state: {
providerConfig: buildProviderConfig({
type: NetworkType.rpc,
rpcUrl: 'https://mock-rpc-url',
chainId: '1337',
}),
},
infuraProjectId: 'some-infura-project-id',
},
async ({ controller, messenger }) => {
const fakeProvider = buildFakeProvider();
const fakeNetworkClient = buildFakeClient(fakeProvider);
mockCreateNetworkClient().mockReturnValue(fakeNetworkClient);
await controller.setProviderType(InfuraNetworkType.goerli);

const networkWillChange = await waitForPublishedEvents({
messenger,
eventType: 'NetworkController:networkWillChange',
operation: () => {
// Intentionally not awaited because we're capturing an event
// emitted partway through the operation
controller.rollbackToPreviousProvider();
},
});

expect(networkWillChange).toStrictEqual([[]]);
},
);
});

it('emits networkDidChange', async () => {
await withController(
{
state: {
providerConfig: buildProviderConfig({
type: NetworkType.rpc,
rpcUrl: 'https://mock-rpc-url',
chainId: '1337',
}),
},
infuraProjectId: 'some-infura-project-id',
},
async ({ controller, messenger }) => {
const fakeProvider = buildFakeProvider();
const fakeNetworkClient = buildFakeClient(fakeProvider);
mockCreateNetworkClient().mockReturnValue(fakeNetworkClient);
await controller.setProviderType(InfuraNetworkType.goerli);

const networkDidChange = await waitForPublishedEvents({
messenger,
eventType: 'NetworkController:networkDidChange',
operation: () => {
// Intentionally not awaited because we're capturing an event
// emitted partway through the operation
controller.rollbackToPreviousProvider();
},
});

expect(networkDidChange).toStrictEqual([[]]);
},
);
});

it('overwrites the the current provider configuration with the previous provider configuration', async () => {
await withController(
{
Expand Down Expand Up @@ -3458,6 +3612,58 @@ function refreshNetworkTests({
initialState?: Partial<NetworkState>;
operation: (controller: NetworkController) => Promise<void>;
}) {
it('emits networkWillChange', async () => {
await withController(
{
infuraProjectId: 'infura-project-id',
state: initialState,
},
async ({ controller, messenger }) => {
const fakeProvider = buildFakeProvider();
const fakeNetworkClient = buildFakeClient(fakeProvider);
mockCreateNetworkClient().mockReturnValue(fakeNetworkClient);

const networkWillChange = await waitForPublishedEvents({
messenger,
eventType: 'NetworkController:networkWillChange',
operation: () => {
// Intentionally not awaited because we're capturing an event
// emitted partway through the operation
operation(controller);
},
});

expect(networkWillChange).toStrictEqual([[]]);
},
);
});

it('emits networkDidChange', async () => {
await withController(
{
infuraProjectId: 'infura-project-id',
state: initialState,
},
async ({ controller, messenger }) => {
const fakeProvider = buildFakeProvider();
const fakeNetworkClient = buildFakeClient(fakeProvider);
mockCreateNetworkClient().mockReturnValue(fakeNetworkClient);

const networkDidChange = await waitForPublishedEvents({
messenger,
eventType: 'NetworkController:networkDidChange',
operation: () => {
// Intentionally not awaited because we're capturing an event
// emitted partway through the operation
operation(controller);
},
});

expect(networkDidChange).toStrictEqual([[]]);
},
);
});

it('clears network id from state', async () => {
await withController(
{
Expand Down Expand Up @@ -4979,6 +5185,8 @@ function buildNetworkControllerMessenger(messenger = buildMessenger()) {
'NetworkController:stateChange',
'NetworkController:infuraIsBlocked',
'NetworkController:infuraIsUnblocked',
'NetworkController:networkDidChange',
'NetworkController:networkWillChange',
],
});
}
Expand Down

0 comments on commit 5ed561a

Please sign in to comment.