Skip to content

Commit eef26fd

Browse files
feat: add main network sync controller integration
I need to add some integration tests to be a bit more confident in outcome
1 parent ccd95c8 commit eef26fd

File tree

4 files changed

+205
-188
lines changed

4 files changed

+205
-188
lines changed

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

Lines changed: 73 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ import type {
1818
KeyringControllerUnlockEvent,
1919
KeyringControllerAddNewAccountAction,
2020
} from '@metamask/keyring-controller';
21-
import type { NetworkConfiguration } from '@metamask/network-controller';
21+
import type {
22+
NetworkConfiguration,
23+
NetworkController,
24+
NetworkControllerGetStateAction,
25+
} from '@metamask/network-controller';
2226
import type { HandleSnapRequest } from '@metamask/snaps-controllers';
2327

2428
import { createSnapSignMessageRequest } from '../authentication/auth-snap-requests';
@@ -35,7 +39,10 @@ import {
3539
mapInternalAccountToUserStorageAccount,
3640
} from './accounts/user-storage';
3741
import { createSHA256Hash } from './encryption';
38-
import { startNetworkSyncing } from './network-syncing/controller-integration';
42+
import {
43+
performMainNetworkSync,
44+
startNetworkSyncing,
45+
} from './network-syncing/controller-integration';
3946
import type {
4047
UserStoragePathWithFeatureAndKey,
4148
UserStoragePathWithFeatureOnly,
@@ -46,26 +53,32 @@ import {
4653
upsertUserStorage,
4754
} from './services';
4855

49-
// TODO: add external NetworkController event
50-
// Need to listen for when a network gets added
56+
// TODO - replace shimmed interface with actual interfaces once merged
57+
// Waiting on #4698
5158
type NetworkControllerNetworkAddedEvent = {
5259
type: 'NetworkController:networkAdded';
5360
payload: [networkConfiguration: NetworkConfiguration];
5461
};
55-
56-
// TODO: add external NetworkController event
57-
// Need to listen for when a network is updated, or the default rpc/block explorer changes
58-
type NetworkControllerNetworkChangedEvent = {
59-
type: 'NetworkController:networkChanged';
62+
type NetworkControllerNetworkUpdatedEvent = {
63+
type: 'NetworkController:networkUpdated';
6064
payload: [networkConfiguration: NetworkConfiguration];
6165
};
62-
63-
// TODO: add external NetworkController event
64-
// Need to listen for when a network gets deleted
65-
type NetworkControllerNetworkDeletedEvent = {
66-
type: 'NetworkController:networkDeleted';
66+
type NetworkControllerNetworkRemovedEvent = {
67+
type: 'NetworkController:networkRemoved';
6768
payload: [networkConfiguration: NetworkConfiguration];
6869
};
70+
type NetworkControllerAddNetworkAction = {
71+
type: 'NetworkController:addNetwork';
72+
handler: NetworkController['addNetwork'];
73+
};
74+
type NetworkControllerUpdateNetworkAction = {
75+
type: 'NetworkController:updateNetwork';
76+
handler: NetworkController['updateNetwork'];
77+
};
78+
type NetworkControllerRemoveNetworkAction = {
79+
type: 'NetworkController:removeNetwork';
80+
handler: NetworkController['removeNetwork'];
81+
};
6982

7083
// TODO: fix external dependencies
7184
export declare type NotificationServicesControllerDisableNotificationServices =
@@ -173,10 +186,15 @@ export type AllowedActions =
173186
// Metamask Notifications
174187
| NotificationServicesControllerDisableNotificationServices
175188
| NotificationServicesControllerSelectIsNotificationServicesEnabled
176-
// Account syncing
189+
// Account Syncing
177190
| AccountsControllerListAccountsAction
178191
| AccountsControllerUpdateAccountMetadataAction
179-
| KeyringControllerAddNewAccountAction;
192+
| KeyringControllerAddNewAccountAction
193+
// Network Syncing
194+
| NetworkControllerGetStateAction
195+
| NetworkControllerAddNetworkAction
196+
| NetworkControllerUpdateNetworkAction
197+
| NetworkControllerRemoveNetworkAction;
180198

181199
// Messenger events
182200
export type UserStorageControllerStateChangeEvent = ControllerStateChangeEvent<
@@ -207,8 +225,8 @@ export type AllowedEvents =
207225
| AccountsControllerAccountRenamedEvent
208226
// Network Syncing Events
209227
| NetworkControllerNetworkAddedEvent
210-
| NetworkControllerNetworkChangedEvent
211-
| NetworkControllerNetworkDeletedEvent;
228+
| NetworkControllerNetworkUpdatedEvent
229+
| NetworkControllerNetworkRemovedEvent;
212230

213231
// Messenger
214232
export type UserStorageControllerMessenger = RestrictedControllerMessenger<
@@ -232,6 +250,13 @@ export default class UserStorageController extends BaseController<
232250
UserStorageControllerState,
233251
UserStorageControllerMessenger
234252
> {
253+
// This is replaced with the actual value in the constructor
254+
// We will remove this once the feature will be released
255+
#env = {
256+
isAccountSyncingEnabled: false,
257+
isNetworkSyncingEnabled: false,
258+
};
259+
235260
#auth = {
236261
getBearerToken: async () => {
237262
return await this.messagingSystem.call(
@@ -260,17 +285,12 @@ export default class UserStorageController extends BaseController<
260285
};
261286

262287
#accounts = {
263-
// This is replaced with the actual value in the constructor
264-
// We will remove this once the feature will be released
265-
isAccountSyncingEnabled: false,
266288
isAccountSyncingInProgress: false,
267289
canSync: () => {
268290
try {
269291
this.#assertProfileSyncingEnabled();
270292

271-
return (
272-
this.#accounts.isAccountSyncingEnabled && this.#auth.isAuthEnabled()
273-
);
293+
return this.#env.isAccountSyncingEnabled && this.#auth.isAuthEnabled();
274294
} catch {
275295
return false;
276296
}
@@ -406,9 +426,8 @@ export default class UserStorageController extends BaseController<
406426
state: { ...defaultState, ...state },
407427
});
408428

409-
this.#accounts.isAccountSyncingEnabled = Boolean(
410-
env?.isAccountSyncingEnabled,
411-
);
429+
this.#env.isAccountSyncingEnabled = Boolean(env?.isAccountSyncingEnabled);
430+
this.#env.isNetworkSyncingEnabled = Boolean(env?.isNetworkSyncingEnabled);
412431

413432
this.getMetaMetricsState = getMetaMetricsState;
414433
this.#keyringController.setupLockedStateSubscriptions();
@@ -417,18 +436,10 @@ export default class UserStorageController extends BaseController<
417436
this.#accounts.setupAccountSyncingSubscriptions();
418437

419438
// Network Syncing
420-
if (env?.isNetworkSyncingEnabled) {
439+
if (this.#env.isNetworkSyncingEnabled) {
421440
startNetworkSyncing({
422441
messenger,
423-
getStorageConfig: async () => {
424-
const { storageKey, bearerToken } =
425-
await this.#getStorageKeyAndBearerToken();
426-
return {
427-
storageKey,
428-
bearerToken,
429-
nativeScryptCrypto: this.#nativeScryptCrypto,
430-
};
431-
},
442+
getStorageConfig: this.#getStorageOptions,
432443
});
433444
}
434445
}
@@ -479,6 +490,20 @@ export default class UserStorageController extends BaseController<
479490
);
480491
}
481492

493+
async #getStorageOptions() {
494+
if (!this.state.isProfileSyncingEnabled) {
495+
return null;
496+
}
497+
498+
const { storageKey, bearerToken } =
499+
await this.#getStorageKeyAndBearerToken();
500+
return {
501+
storageKey,
502+
bearerToken,
503+
nativeScryptCrypto: this.#nativeScryptCrypto,
504+
};
505+
}
506+
482507
public async enableProfileSyncing(): Promise<void> {
483508
try {
484509
this.#setIsProfileSyncingUpdateLoading(true);
@@ -868,4 +893,15 @@ export default class UserStorageController extends BaseController<
868893
);
869894
}
870895
}
896+
897+
async syncNetworks() {
898+
if (!this.#env.isNetworkSyncingEnabled) {
899+
return;
900+
}
901+
902+
await performMainNetworkSync({
903+
messenger: this.messagingSystem,
904+
getStorageConfig: this.#getStorageOptions,
905+
});
906+
}
871907
}

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

Lines changed: 90 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,50 @@ import log from 'loglevel';
22

33
import type { UserStorageBaseOptions } from '../services';
44
import type { UserStorageControllerMessenger } from '../UserStorageController';
5-
import { addNetwork, deleteNetwork, updateNetwork } from './sync-mutations';
5+
import { getAllRemoteNetworks } from './services';
6+
import { findNetworksToUpdate } from './sync-all';
7+
import { batchUpdateNetworks, deleteNetwork } from './sync-mutations';
68

7-
type SetupNetworkSyncingProps = {
9+
type NetworkSyncingProps = {
810
messenger: UserStorageControllerMessenger;
9-
getStorageConfig: () => Promise<UserStorageBaseOptions>;
11+
getStorageConfig: () => Promise<UserStorageBaseOptions | null>;
1012
};
1113

14+
/**
15+
* Global in-mem cache to signify that the network syncing is in progress
16+
* Ensures that listeners do not fire during main sync (prevent double requests)
17+
*/
18+
let isMainNetworkSyncInProgress = false;
19+
1220
/**
1321
* Initialize and setup events to listen to for network syncing
22+
* We will be listening to:
23+
* - Remove Event, to indicate that we need to remote network from remote
24+
*
25+
* We will not be listening to:
26+
* - Add/Update events are not required, as we can sync these during the main sync
27+
*
1428
* @param props - parameters used for initializing and enabling network syncing
1529
*/
16-
export function startNetworkSyncing(props: SetupNetworkSyncingProps) {
30+
export function startNetworkSyncing(props: NetworkSyncingProps) {
1731
const { messenger, getStorageConfig } = props;
18-
1932
try {
2033
messenger.subscribe(
21-
'NetworkController:networkAdded',
34+
'NetworkController:networkRemoved',
2235
// eslint-disable-next-line @typescript-eslint/no-misused-promises
2336
async (networkConfiguration) => {
2437
try {
38+
// As main sync is in progress, it will already local and remote networks
39+
// So no need to re-process again.
40+
if (isMainNetworkSyncInProgress) {
41+
return;
42+
}
43+
2544
const opts = await getStorageConfig();
26-
await addNetwork(networkConfiguration, opts);
45+
if (!opts) {
46+
return;
47+
}
48+
await deleteNetwork(networkConfiguration, opts);
2749
} catch {
2850
// Silently fail sync
2951
}
@@ -32,38 +54,75 @@ export function startNetworkSyncing(props: SetupNetworkSyncingProps) {
3254
} catch (e) {
3355
log.warn('NetworkSyncing, event subscription failed', e);
3456
}
57+
}
3558

59+
/**
60+
* Action to perform the main network sync.
61+
* It will fetch local networks and remote networks, then determines which networks (local and remote) to add/update
62+
* @param props - parameters used for this main sync
63+
*/
64+
export async function performMainNetworkSync(props: NetworkSyncingProps) {
65+
const { messenger, getStorageConfig } = props;
66+
isMainNetworkSyncInProgress = true;
3667
try {
37-
messenger.subscribe(
38-
'NetworkController:networkDeleted',
39-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
40-
async (networkConfiguration) => {
68+
const opts = await getStorageConfig();
69+
if (!opts) {
70+
return;
71+
}
72+
73+
const localNetworks = Object.values(
74+
messenger.call('NetworkController:getState')
75+
.networkConfigurationsByChainId ?? {},
76+
);
77+
78+
const remoteNetworks = await getAllRemoteNetworks(opts);
79+
80+
const networksToUpdate = findNetworksToUpdate({
81+
localNetworks,
82+
remoteNetworks,
83+
});
84+
85+
// Update Remote
86+
if (networksToUpdate?.remoteNetworksToUpdate) {
87+
await batchUpdateNetworks(networksToUpdate?.remoteNetworksToUpdate, opts);
88+
}
89+
90+
// Add missing local networks
91+
if (networksToUpdate?.missingLocalNetworks) {
92+
networksToUpdate.missingLocalNetworks.forEach((n) => {
4193
try {
42-
const opts = await getStorageConfig();
43-
await deleteNetwork(networkConfiguration, opts);
94+
messenger.call('NetworkController:addNetwork', n);
4495
} catch {
45-
// Silently fail sync
96+
// Silently fail, we can try this again on next main sync
4697
}
47-
},
48-
);
49-
} catch (e) {
50-
log.warn('NetworkSyncing, event subscription failed', e);
51-
}
98+
});
99+
}
52100

53-
try {
54-
messenger.subscribe(
55-
'NetworkController:networkChanged',
56-
// eslint-disable-next-line @typescript-eslint/no-misused-promises
57-
async (networkConfiguration) => {
101+
// Update local networks
102+
if (networksToUpdate?.localNetworksToUpdate) {
103+
const promises = networksToUpdate.localNetworksToUpdate.map(async (n) => {
58104
try {
59-
const opts = await getStorageConfig();
60-
await updateNetwork(networkConfiguration, opts);
105+
await messenger.call('NetworkController:updateNetwork', n.chainId, n);
61106
} catch {
62-
// Silently fail sync
107+
// Silently fail, we can try this again on next main sync
63108
}
64-
},
65-
);
66-
} catch (e) {
67-
log.warn('NetworkSyncing, event subscription failed', e);
109+
});
110+
await Promise.all(promises);
111+
}
112+
113+
// Remove local networks
114+
if (networksToUpdate?.localNetworksToRemove) {
115+
networksToUpdate.localNetworksToRemove.forEach((n) => {
116+
try {
117+
messenger.call('NetworkController:removeNetwork', n.chainId);
118+
} catch {
119+
// Silently fail, we can try this again on next main sync
120+
}
121+
});
122+
}
123+
} catch {
124+
// Silently fail sync
125+
} finally {
126+
isMainNetworkSyncInProgress = false;
68127
}
69128
}

0 commit comments

Comments
 (0)