Skip to content
Draft
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
de3b7f8
feat: add NetworkController actions and events
Prithpal-Sooriya Sep 13, 2024
8844d55
test: add tests to cover calls through the controller messenger
Prithpal-Sooriya Nov 19, 2024
ef02bdf
feat: add lastUpdatedAt for NetworkConfiguration
Prithpal-Sooriya Sep 3, 2024
69acd6f
test: update test assertions that require lastUpdateAt property
Prithpal-Sooriya Nov 20, 2024
aa490e8
feat: use original networkClientId if provided when adding a network
Prithpal-Sooriya Nov 15, 2024
84301ae
feat: add dangerouslySetNetworkConfiguration method to override netwo…
Prithpal-Sooriya Nov 19, 2024
f5649fe
refactor: make dangerouslySetNetworkConfiguration a private method
Prithpal-Sooriya Nov 20, 2024
ccb17d2
feat: add main network sync controller integration
Prithpal-Sooriya Sep 13, 2024
a933a55
feat: add main network sync controller integration
Prithpal-Sooriya Sep 13, 2024
d291144
feat: add network sync callbacks
Prithpal-Sooriya Oct 24, 2024
7d48db3
test: add controller-integration - performMainSync() tests
Prithpal-Sooriya Oct 24, 2024
08c76ec
test: add test coverage
Prithpal-Sooriya Oct 24, 2024
dfa83b8
fix: fix syncing issues from manual integration testing
Prithpal-Sooriya Nov 19, 2024
c2531fb
test: fix controller integration tests
Prithpal-Sooriya Nov 19, 2024
d99306f
test: fix failing batch update network tests
Prithpal-Sooriya Nov 19, 2024
f8237c4
refactor: reuse user storage messengers and mocks
Prithpal-Sooriya Nov 19, 2024
b54490a
test: add test coverage for services and controller-integration
Prithpal-Sooriya Nov 19, 2024
8cf7bea
test: add controller syncNetwork tests
Prithpal-Sooriya Nov 19, 2024
49d7468
feat: add hasNetworkSyncingSyncedAtLeastOnce check
Prithpal-Sooriya Nov 21, 2024
75fce4a
refactor: use logging library instead of console
Prithpal-Sooriya Nov 21, 2024
f774423
feat: add max size bounds for networks to add
Prithpal-Sooriya Nov 21, 2024
0b2a267
fix: fix type issue
Prithpal-Sooriya Nov 21, 2024
86f5267
Merge branch 'main' into temp/network-syncing-all-changes
Prithpal-Sooriya Nov 25, 2024
9de1373
fix: fix type issue
Prithpal-Sooriya Nov 21, 2024
027ab3f
test: fix test
Prithpal-Sooriya Nov 25, 2024
50bb1e2
Merge branch 'main' into temp/network-syncing-all-changes
Prithpal-Sooriya Nov 26, 2024
d346ba8
feat: use existing network controller updateNetwork method
Prithpal-Sooriya Nov 26, 2024
d1104b6
refactor: remove dangerously set code
Prithpal-Sooriya Nov 26, 2024
1422588
refactor: clean up error logs
Prithpal-Sooriya Nov 27, 2024
5544fdb
feat: stuff
Prithpal-Sooriya Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 210 additions & 4 deletions packages/network-controller/src/NetworkController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ export type NetworkConfiguration = {
* interact with the chain.
*/
rpcEndpoints: RpcEndpoint[];
/**
* Profile Sync - Network Sync field.
* Allows comparison of local network state with state to sync.
*/
lastUpdatedAt?: number;
};

/**
Expand All @@ -212,8 +217,11 @@ export type NetworkConfiguration = {
* Custom RPC endpoints do not need a `networkClientId` property because it is
* assumed that they have not already been added and therefore network clients
* do not exist for them yet (and hence IDs need to be generated).
*
* However Custom RPC endpoints, that are synchronized between devices,
* can contain a `networkClientId` set on both devices.
*/
export type AddNetworkCustomRpcEndpointFields = Omit<
export type AddNetworkCustomRpcEndpointFields = Partialize<
CustomRpcEndpoint,
'networkClientId'
>;
Expand Down Expand Up @@ -409,13 +417,23 @@ export type NetworkControllerNetworkAddedEvent = {
payload: [networkConfiguration: NetworkConfiguration];
};

/**
* `networkRemoved` is published after a network configuration is removed from the
* network configuration registry and once the network clients have been removed.
*/
export type NetworkControllerNetworkRemovedEvent = {
type: 'NetworkController:networkRemoved';
payload: [networkConfiguration: NetworkConfiguration];
};

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

export type NetworkControllerGetStateAction = ControllerGetStateAction<
typeof controllerName,
Expand Down Expand Up @@ -473,6 +491,26 @@ export type NetworkControllerGetNetworkConfigurationByNetworkClientId = {
handler: NetworkController['getNetworkConfigurationByNetworkClientId'];
};

export type NetworkControllerAddNetworkAction = {
type: 'NetworkController:addNetwork';
handler: NetworkController['addNetwork'];
};

export type NetworkControllerRemoveNetworkAction = {
type: 'NetworkController:removeNetwork';
handler: NetworkController['removeNetwork'];
};

export type NetworkControllerUpdateNetworkAction = {
type: 'NetworkController:updateNetwork';
handler: NetworkController['updateNetwork'];
};

export type NetworkControllerDangerouslySetNetworkConfigurationAction = {
type: 'NetworkController:dangerouslySetNetworkConfiguration';
handler: (networkConfiguration: NetworkConfiguration) => Promise<void>;
};

export type NetworkControllerActions =
| NetworkControllerGetStateAction
| NetworkControllerGetEthQueryAction
Expand All @@ -483,7 +521,11 @@ export type NetworkControllerActions =
| NetworkControllerSetActiveNetworkAction
| NetworkControllerSetProviderTypeAction
| NetworkControllerGetNetworkConfigurationByChainId
| NetworkControllerGetNetworkConfigurationByNetworkClientId;
| NetworkControllerGetNetworkConfigurationByNetworkClientId
| NetworkControllerAddNetworkAction
| NetworkControllerRemoveNetworkAction
| NetworkControllerUpdateNetworkAction
| NetworkControllerDangerouslySetNetworkConfigurationAction;

export type NetworkControllerMessenger = RestrictedControllerMessenger<
typeof controllerName,
Expand Down Expand Up @@ -954,6 +996,34 @@ export class NetworkController extends BaseController<
`${this.name}:getSelectedNetworkClient`,
this.getSelectedNetworkClient.bind(this),
);

this.messagingSystem.registerActionHandler(
// ESLint is mistaken here; `name` is a string.
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`${this.name}:addNetwork`,
this.addNetwork.bind(this),
);

this.messagingSystem.registerActionHandler(
// ESLint is mistaken here; `name` is a string.
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`${this.name}:removeNetwork`,
this.removeNetwork.bind(this),
);

this.messagingSystem.registerActionHandler(
// ESLint is mistaken here; `name` is a string.
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`${this.name}:updateNetwork`,
this.updateNetwork.bind(this),
);

this.messagingSystem.registerActionHandler(
// TODO: Either fix this lint violation or explain why it's necessary to ignore.
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
`${this.name}:dangerouslySetNetworkConfiguration`,
this.#dangerouslySetNetworkConfiguration.bind(this),
);
}

/**
Expand Down Expand Up @@ -1557,7 +1627,8 @@ export class NetworkController extends BaseController<
defaultOrCustomRpcEndpointFields.type === RpcEndpointType.Custom
? {
...defaultOrCustomRpcEndpointFields,
networkClientId: uuidV4(),
networkClientId:
defaultOrCustomRpcEndpointFields.networkClientId ?? uuidV4(),
}
: defaultOrCustomRpcEndpointFields;
return {
Expand Down Expand Up @@ -1939,6 +2010,134 @@ export class NetworkController extends BaseController<
buildNetworkConfigurationsByNetworkClientId(
this.state.networkConfigurationsByChainId,
);

this.messagingSystem.publish(
'NetworkController:networkRemoved',
existingNetworkConfiguration,
);
}

/**
* This is used to override an existing network configuration.
* This is only meant for internal use only and not to be exposed via the UI.
* It is used as part of "Network Syncing", to sync networks, RPCs and block explorers cross devices.
*
* This will subsequently update the network client registry; state.networksMetadata, and state.selectedNetworkClientId
* @param networkConfiguration - the network configuration to override
*/
async #dangerouslySetNetworkConfiguration(
networkConfiguration: NetworkConfiguration,
) {
const prevNetworkConfig: NetworkConfiguration | undefined =
networkConfiguration.chainId in this.state.networkConfigurationsByChainId
? this.state.networkConfigurationsByChainId[
networkConfiguration.chainId
]
: undefined;

if (!prevNetworkConfig) {
// We only want to perform overrides, not add new network configurations
return;
}

// Update Registry (remove old and add new)
const updateRegistry = () => {
// Unregister old networks we want to override
const autoManagedNetworkClientRegistry =
this.#ensureAutoManagedNetworkClientRegistryPopulated();
const networkClientRemoveOperations = prevNetworkConfig.rpcEndpoints.map(
(rpcEndpoint) => {
return {
type: 'remove' as const,
rpcEndpoint,
};
},
);
this.#unregisterNetworkClientsAsNeeded({
networkClientOperations: networkClientRemoveOperations,
autoManagedNetworkClientRegistry,
});

// Register new networks we want to override
const networkClientAddOperations = networkConfiguration.rpcEndpoints.map(
(rpcEndpoint) => {
return {
type: 'add' as const,
rpcEndpoint,
};
},
);
this.#registerNetworkClientsAsNeeded({
networkFields: networkConfiguration,
networkClientOperations: networkClientAddOperations,
autoManagedNetworkClientRegistry,
});
};

// Replace the networkConfiguration with our new networkConfiguration
// This is a full replace (no merging)
const replaceNetworkConfiguration = () => {
// Update State
this.update((state) => {
state.networkConfigurationsByChainId[networkConfiguration.chainId] =
networkConfiguration;
});

// Update Cache
this.#networkConfigurationsByNetworkClientId =
buildNetworkConfigurationsByNetworkClientId(
this.state.networkConfigurationsByChainId,
);
};

// Updates the NetworksMetadata State
const updateNetworksMetadata = async () => {
// Remove old metadata state
this.update((state) => {
prevNetworkConfig.rpcEndpoints.forEach((r) => {
if (state.networksMetadata?.[r.networkClientId]) {
delete state.networksMetadata[r.networkClientId];
}
});
});

// Add new metadata state
for (const r of networkConfiguration.rpcEndpoints) {
await this.lookupNetwork(r.networkClientId);
}
};

// Update selectedNetworkId State
// Will try to keep the same OR will select a new RPC from new network OR any network (edge case)
const updateSelectedNetworkId = async () => {
const selectedClientId = this.state.selectedNetworkClientId;
const wasClientIdReplaced = prevNetworkConfig.rpcEndpoints.some(
(r) => r.networkClientId === selectedClientId,
);
const doesExistInNewNetwork = networkConfiguration.rpcEndpoints.some(
(r) => r.networkClientId === selectedClientId,
);

const shouldUpdateSelectedNetworkId =
wasClientIdReplaced && !doesExistInNewNetwork;
if (shouldUpdateSelectedNetworkId) {
// Update the clientId to "something" that exists
const newRPCClientId = networkConfiguration.rpcEndpoints.find(
(r) => r.networkClientId in this.state.networksMetadata,
)?.networkClientId;
const anyRPCClientId = Object.keys(this.state.networksMetadata)[0];
/* istanbul ignore next: anyRPCClientId and selectedClientId are fallbacks and should be impossible to reach */
const newlySelectedNetwork =
newRPCClientId ?? anyRPCClientId ?? selectedClientId;
await this.#refreshNetwork(newlySelectedNetwork);
}
};

// Execute Set Network Config
updateRegistry();
replaceNetworkConfiguration();
await updateNetworksMetadata();
await updateSelectedNetworkId();
}

/**
Expand Down Expand Up @@ -2438,6 +2637,13 @@ export class NetworkController extends BaseController<
}

if (mode === 'add' || mode === 'update') {
if (
JSON.stringify(
state.networkConfigurationsByChainId[args.networkFields.chainId],
) !== JSON.stringify(args.networkConfigurationToPersist)
) {
args.networkConfigurationToPersist.lastUpdatedAt = Date.now();
}
state.networkConfigurationsByChainId[args.networkFields.chainId] =
args.networkConfigurationToPersist;
}
Expand Down
6 changes: 6 additions & 0 deletions packages/network-controller/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export type {
NetworkControllerNetworkDidChangeEvent,
NetworkControllerInfuraIsBlockedEvent,
NetworkControllerInfuraIsUnblockedEvent,
NetworkControllerNetworkAddedEvent,
NetworkControllerNetworkRemovedEvent,
NetworkControllerEvents,
NetworkControllerGetStateAction,
NetworkControllerGetEthQueryAction,
Expand All @@ -26,6 +28,10 @@ export type {
NetworkControllerFindNetworkClientIdByChainIdAction,
NetworkControllerSetProviderTypeAction,
NetworkControllerSetActiveNetworkAction,
NetworkControllerAddNetworkAction,
NetworkControllerRemoveNetworkAction,
NetworkControllerUpdateNetworkAction,
NetworkControllerDangerouslySetNetworkConfigurationAction,
NetworkControllerGetNetworkConfigurationByNetworkClientId,
NetworkControllerActions,
NetworkControllerMessenger,
Expand Down
Loading
Loading