Skip to content

Commit c10c811

Browse files
feat: add new logic and fix syncing issues
- update network sync issue - remove network sync issue - switch networks updating issue
1 parent 6f71786 commit c10c811

File tree

7 files changed

+176
-35
lines changed

7 files changed

+176
-35
lines changed

packages/network-controller/src/NetworkController.ts

+117-3
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,11 @@ export type NetworkConfiguration = {
218218
* Custom RPC endpoints do not need a `networkClientId` property because it is
219219
* assumed that they have not already been added and therefore network clients
220220
* do not exist for them yet (and hence IDs need to be generated).
221+
*
222+
* However Custom RPC endpoints, that are synchronized between,
223+
* can use the same `networkClientId` set on both devices.
221224
*/
222-
export type AddNetworkCustomRpcEndpointFields = Omit<
225+
export type AddNetworkCustomRpcEndpointFields = Partialize<
223226
CustomRpcEndpoint,
224227
'networkClientId'
225228
>;
@@ -514,6 +517,11 @@ export type NetworkControllerRemoveNetworkAction = {
514517
handler: NetworkController['removeNetwork'];
515518
};
516519

520+
export type NetworkControllerDangerouslySetNetworkConfigurationAction = {
521+
type: 'NetworkController:dangerouslySetNetworkConfiguration';
522+
handler: NetworkController['dangerouslySetNetworkConfiguration'];
523+
};
524+
517525
export type NetworkControllerActions =
518526
| NetworkControllerGetStateAction
519527
| NetworkControllerGetEthQueryAction
@@ -527,7 +535,8 @@ export type NetworkControllerActions =
527535
| NetworkControllerGetNetworkConfigurationByNetworkClientId
528536
| NetworkControllerAddNetworkAction
529537
| NetworkControllerUpdateNetworkAction
530-
| NetworkControllerRemoveNetworkAction;
538+
| NetworkControllerRemoveNetworkAction
539+
| NetworkControllerDangerouslySetNetworkConfigurationAction;
531540

532541
export type NetworkControllerMessenger = RestrictedControllerMessenger<
533542
typeof controllerName,
@@ -1019,6 +1028,11 @@ export class NetworkController extends BaseController<
10191028
`${this.name}:removeNetwork`,
10201029
this.removeNetwork.bind(this),
10211030
);
1031+
1032+
this.messagingSystem.registerActionHandler(
1033+
`${this.name}:dangerouslySetNetworkConfiguration`,
1034+
this.dangerouslySetNetworkConfiguration.bind(this),
1035+
);
10221036
}
10231037

10241038
/**
@@ -1622,7 +1636,8 @@ export class NetworkController extends BaseController<
16221636
defaultOrCustomRpcEndpointFields.type === RpcEndpointType.Custom
16231637
? {
16241638
...defaultOrCustomRpcEndpointFields,
1625-
networkClientId: uuidV4(),
1639+
networkClientId:
1640+
defaultOrCustomRpcEndpointFields.networkClientId ?? uuidV4(),
16261641
}
16271642
: defaultOrCustomRpcEndpointFields;
16281643
return {
@@ -2016,6 +2031,105 @@ export class NetworkController extends BaseController<
20162031
);
20172032
}
20182033

2034+
/**
2035+
* This is used to override an existing network configuration.
2036+
* This is only meant for internal use only and not to be exposed via the UI.
2037+
* It is used as part of "Network Syncing", to sync networks, RPCs and block explorers cross devices.
2038+
*
2039+
* This will subsequently update the network client registry; state.networksMetadata, and state.selectedNetworkClientId
2040+
* @param networkConfiguration - the network configuration to override
2041+
*/
2042+
async dangerouslySetNetworkConfiguration(
2043+
networkConfiguration: NetworkConfiguration,
2044+
) {
2045+
const prevNetworkConfig: NetworkConfiguration | undefined =
2046+
networkConfiguration.chainId in this.state.networkConfigurationsByChainId
2047+
? this.state.networkConfigurationsByChainId[
2048+
networkConfiguration.chainId
2049+
]
2050+
: undefined;
2051+
2052+
// Update Registry (remove old and add new)
2053+
const autoManagedNetworkClientRegistry =
2054+
this.#ensureAutoManagedNetworkClientRegistryPopulated();
2055+
if (prevNetworkConfig) {
2056+
const networkClientOperations = prevNetworkConfig.rpcEndpoints.map(
2057+
(rpcEndpoint) => {
2058+
return {
2059+
type: 'remove' as const,
2060+
rpcEndpoint,
2061+
};
2062+
},
2063+
);
2064+
this.#unregisterNetworkClientsAsNeeded({
2065+
networkClientOperations,
2066+
autoManagedNetworkClientRegistry,
2067+
});
2068+
}
2069+
const networkClientOperations = networkConfiguration.rpcEndpoints.map(
2070+
(rpcEndpoint) => {
2071+
return {
2072+
type: 'add' as const,
2073+
rpcEndpoint,
2074+
};
2075+
},
2076+
);
2077+
this.#registerNetworkClientsAsNeeded({
2078+
networkFields: networkConfiguration,
2079+
networkClientOperations,
2080+
autoManagedNetworkClientRegistry,
2081+
});
2082+
2083+
// update new `networkConfigurationsByChainId` (full replace)
2084+
this.update((state) => {
2085+
state.networkConfigurationsByChainId[networkConfiguration.chainId] =
2086+
networkConfiguration;
2087+
});
2088+
this.#networkConfigurationsByNetworkClientId =
2089+
buildNetworkConfigurationsByNetworkClientId(
2090+
this.state.networkConfigurationsByChainId,
2091+
);
2092+
2093+
// update `networksMetadata` remove old
2094+
if (prevNetworkConfig) {
2095+
this.update((state) => {
2096+
const newNetworksMetadata: NetworksMetadata = {
2097+
...state.networksMetadata,
2098+
};
2099+
prevNetworkConfig.rpcEndpoints.forEach((rpcEndpoint) => {
2100+
delete newNetworksMetadata[rpcEndpoint.networkClientId];
2101+
});
2102+
});
2103+
}
2104+
2105+
// update `networksMetadata` update new
2106+
for (const rpcEndpoint of networkConfiguration.rpcEndpoints) {
2107+
await this.lookupNetwork(rpcEndpoint.networkClientId);
2108+
}
2109+
2110+
// update selectedNetworkId
2111+
const selectedClientId = this.state.selectedNetworkClientId;
2112+
const wasReplacedClientId = prevNetworkConfig?.rpcEndpoints.some(
2113+
(r) => r.networkClientId === selectedClientId,
2114+
);
2115+
const isValidClientIdInNewNetworks = networkConfiguration.rpcEndpoints.some(
2116+
(r) => r.networkClientId === selectedClientId,
2117+
);
2118+
if (wasReplacedClientId) {
2119+
if (!isValidClientIdInNewNetworks) {
2120+
// Update clientId to something that exists
2121+
const newSelectedNetworkMetadata =
2122+
networkConfiguration.rpcEndpoints.find(
2123+
(r) => r.networkClientId in this.state.networksMetadata,
2124+
)?.networkClientId;
2125+
const anyClientId = Object.keys(this.state.networksMetadata)[0];
2126+
const newlySelectedNetwork =
2127+
newSelectedNetworkMetadata ?? anyClientId ?? selectedClientId;
2128+
await this.#refreshNetwork(newlySelectedNetwork);
2129+
}
2130+
}
2131+
}
2132+
20192133
/**
20202134
* Assuming that the network has been previously switched, switches to this
20212135
* new network.

packages/network-controller/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export type {
3232
NetworkControllerAddNetworkAction,
3333
NetworkControllerUpdateNetworkAction,
3434
NetworkControllerRemoveNetworkAction,
35+
NetworkControllerDangerouslySetNetworkConfigurationAction,
3536
NetworkControllerGetNetworkConfigurationByNetworkClientId,
3637
NetworkControllerActions,
3738
NetworkControllerMessenger,

packages/profile-sync-controller/src/controllers/user-storage/UserStorageController.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
} from '@metamask/keyring-controller';
2222
import type {
2323
NetworkControllerAddNetworkAction,
24-
NetworkControllerUpdateNetworkAction,
24+
NetworkControllerDangerouslySetNetworkConfigurationAction,
2525
NetworkControllerGetStateAction,
2626
NetworkControllerNetworkRemovedEvent,
2727
NetworkControllerRemoveNetworkAction,
@@ -206,7 +206,7 @@ export type AllowedActions =
206206
// Network Syncing
207207
| NetworkControllerGetStateAction
208208
| NetworkControllerAddNetworkAction
209-
| NetworkControllerUpdateNetworkAction
209+
| NetworkControllerDangerouslySetNetworkConfigurationAction
210210
| NetworkControllerRemoveNetworkAction;
211211

212212
// Messenger events

packages/profile-sync-controller/src/controllers/user-storage/network-syncing/controller-integration.test.ts

+5-7
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,7 @@ type ExternalEvents = NotNamespacedBy<
6363
'UserStorageController',
6464
AllowedEvents['type']
6565
>;
66-
const getEvents = (): ExternalEvents[] => [
67-
'NetworkController:networkAdded',
68-
'NetworkController:networkUpdated',
69-
'NetworkController:networkRemoved',
70-
];
66+
const getEvents = (): ExternalEvents[] => ['NetworkController:networkRemoved'];
7167

7268
describe('network-syncing/controller-integration - startNetworkSyncing()', () => {
7369
it(`should successfully sync when NetworkController:networkRemoved is emitted`, async () => {
@@ -355,7 +351,7 @@ describe('network-syncing/controller-integration - performMainSync()', () => {
355351
typedMockCallFn<'NetworkController:addNetwork'>();
356352

357353
const mockNetworkControllerUpdateNetwork =
358-
typedMockCallFn<'NetworkController:updateNetwork'>();
354+
typedMockCallFn<'NetworkController:dangerouslySetNetworkConfiguration'>();
359355

360356
const mockNetworkControllerRemoveNetwork =
361357
typedMockCallFn<'NetworkController:removeNetwork'>();
@@ -373,7 +369,9 @@ describe('network-syncing/controller-integration - performMainSync()', () => {
373369
return mockNetworkControllerAddNetwork(...params);
374370
}
375371

376-
if (actionType === 'NetworkController:updateNetwork') {
372+
if (
373+
actionType === 'NetworkController:dangerouslySetNetworkConfiguration'
374+
) {
377375
const [, ...params] = typedArgs;
378376
return mockNetworkControllerUpdateNetwork(...params);
379377
}

packages/profile-sync-controller/src/controllers/user-storage/network-syncing/controller-integration.ts

+39-18
Original file line numberDiff line numberDiff line change
@@ -94,65 +94,86 @@ export async function performMainNetworkSync(
9494
);
9595

9696
const remoteNetworks = await getAllRemoteNetworks(opts);
97+
const networkChanges = findNetworksToUpdate({
98+
localNetworks,
99+
remoteNetworks,
100+
});
97101

98-
const networksToUpdate = findNetworksToUpdate({
102+
log.debug('performMainNetworkSync() - Network Syncing Started', {
99103
localNetworks,
100104
remoteNetworks,
105+
networkChanges,
101106
});
102107

103108
// Update Remote
104109
if (
105-
networksToUpdate?.remoteNetworksToUpdate &&
106-
networksToUpdate.remoteNetworksToUpdate.length > 0
110+
networkChanges?.remoteNetworksToUpdate &&
111+
networkChanges.remoteNetworksToUpdate.length > 0
107112
) {
108-
await batchUpdateNetworks(networksToUpdate?.remoteNetworksToUpdate, opts);
113+
await batchUpdateNetworks(networkChanges?.remoteNetworksToUpdate, opts);
109114
}
110115

111116
// Add missing local networks
112117
if (
113-
networksToUpdate?.missingLocalNetworks &&
114-
networksToUpdate.missingLocalNetworks.length > 0
118+
networkChanges?.missingLocalNetworks &&
119+
networkChanges.missingLocalNetworks.length > 0
115120
) {
116-
networksToUpdate.missingLocalNetworks.forEach((n) => {
121+
networkChanges.missingLocalNetworks.forEach((n) => {
117122
try {
118123
messenger.call('NetworkController:addNetwork', n);
119124
props.onNetworkAdded?.(n.chainId);
120-
} catch {
125+
} catch (e) {
126+
console.error(
127+
'performMainNetworkSync() - NetworkController:addNetwork failed',
128+
e,
129+
);
121130
// Silently fail, we can try this again on next main sync
122131
}
123132
});
124133
}
125134

126135
// Update local networks
127136
if (
128-
networksToUpdate?.localNetworksToUpdate &&
129-
networksToUpdate.localNetworksToUpdate.length > 0
137+
networkChanges?.localNetworksToUpdate &&
138+
networkChanges.localNetworksToUpdate.length > 0
130139
) {
131-
for (const n of networksToUpdate.localNetworksToUpdate) {
140+
for (const n of networkChanges.localNetworksToUpdate) {
132141
try {
133-
await messenger.call('NetworkController:updateNetwork', n.chainId, n);
142+
await messenger.call(
143+
'NetworkController:dangerouslySetNetworkConfiguration',
144+
n,
145+
);
134146
props.onNetworkUpdated?.(n.chainId);
135-
} catch {
147+
} catch (e) {
148+
console.error(
149+
'performMainNetworkSync() - NetworkController:dangerouslySetNetworkConfiguration failed',
150+
e,
151+
);
136152
// Silently fail, we can try this again on next main sync
137153
}
138154
}
139155
}
140156

141157
// Remove local networks
142158
if (
143-
networksToUpdate?.localNetworksToRemove &&
144-
networksToUpdate.localNetworksToRemove.length > 0
159+
networkChanges?.localNetworksToRemove &&
160+
networkChanges.localNetworksToRemove.length > 0
145161
) {
146-
networksToUpdate.localNetworksToRemove.forEach((n) => {
162+
networkChanges.localNetworksToRemove.forEach((n) => {
147163
try {
148164
messenger.call('NetworkController:removeNetwork', n.chainId);
149165
props.onNetworkRemoved?.(n.chainId);
150-
} catch {
166+
} catch (e) {
167+
console.error(
168+
'performMainNetworkSync() - NetworkController:removeNetwork failed',
169+
e,
170+
);
151171
// Silently fail, we can try this again on next main sync
152172
}
153173
});
154174
}
155-
} catch {
175+
} catch (e) {
176+
console.error('performMainNetworkSync() failed', e);
156177
// Silently fail sync
157178
} finally {
158179
isMainNetworkSyncInProgress = false;

packages/profile-sync-controller/src/controllers/user-storage/network-syncing/services.ts

+11-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { UserStorageBaseOptions } from '../services';
22
import {
3+
batchUpsertUserStorage,
34
getUserStorageAllFeatureEntries,
45
upsertUserStorage,
56
} from '../services';
@@ -71,8 +72,14 @@ export async function batchUpsertRemoteNetworks(
7172
networks: RemoteNetworkConfiguration[],
7273
opts: UserStorageBaseOptions,
7374
): Promise<void> {
74-
// TODO - this has not yet been provided by the backend team
75-
// we will replace this with a batch endpoint in near future
76-
const promises = networks.map((n) => upsertRemoteNetwork(n, opts));
77-
await Promise.allSettled(promises);
75+
const networkPathAndValues = networks.map((n) => {
76+
const path = n.chainId;
77+
const data = JSON.stringify(n);
78+
return [path, data] as [string, string];
79+
});
80+
81+
await batchUpsertUserStorage(networkPathAndValues, {
82+
path: 'networks',
83+
...opts,
84+
});
7885
}

packages/profile-sync-controller/src/controllers/user-storage/network-syncing/sync-mutations.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const deleteNetwork = async (
2121
v: '1',
2222
...network,
2323
d: true,
24-
lastUpdatedAt: network.lastUpdatedAt ?? Date.now(), // Ensures that a deleted entry has a date field
24+
lastUpdatedAt: Date.now(), // Ensures that a deleted entry has a date field.
2525
},
2626
opts,
2727
);

0 commit comments

Comments
 (0)