Skip to content

Commit

Permalink
feat: add main network sync controller integration (#4701)
Browse files Browse the repository at this point in the history
## Explanation

This adds the main network sync method to the `UserStorageController`.
It also connects the `NetworkController` events and actions used for
network syncing.

## References

https://consensyssoftware.atlassian.net/browse/NOTIFY-1094

<!--
Are there any issues that this pull request is tied to? Are there other
links that reviewers should consult to understand these changes better?

For example:

* Fixes #12345
* Related to #67890
-->

## Changelog

<!--
If you're making any consumer-facing changes, list those changes here as
if you were updating a changelog, using the template below as a guide.

(CATEGORY is one of BREAKING, ADDED, CHANGED, DEPRECATED, REMOVED, or
FIXED. For security-related issues, follow the Security Advisory
process.)

Please take care to name the exact pieces of the API you've added or
changed (e.g. types, interfaces, functions, or methods).

If there are any breaking changes, make sure to offer a solution for
consumers to follow once they upgrade to the changes.

Finally, if you're only making changes to development scripts or tests,
you may replace the template below with "None".
-->

### `@metamask/profile-sync-controller`

- **ADDED**: network syncing controller config. Can pass in:
`onNetworkAdded`, `onNetworkUpdated`, `onNetworkRemoved` callbacks.
- **ADDED**: `NetworkController:networkRemoved`,
`NetworkController:addNetwork`, `NetworkController:updateNetwork` action
permissions
- **ADDED**: `NetworkController:removeNetwork` event permissions
- **ADDED**: add new method `syncNetworks` method used to initiate the
main network sync
- **ADDED**: add `performMainNetworkSync` method to call the
NetworkController actions to get and update NetworkConfigurations.
- **ADDED**: `getBoundedNetworksToAdd` logic to cap the amount of
networks to add.
- **ADDED**: `createUpdateNetworkProps` to correctly handle networks to
update.
- **ADDED**: add `getStorageOptions` private method to get storage
options for syncing.
- **CHANGED**: modified the batchNetworks call to use the user-storage
batch API instead of multiple calls.


## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've highlighted breaking changes using the "BREAKING" category
above as appropriate
  • Loading branch information
Prithpal-Sooriya authored Nov 29, 2024
1 parent 60e3ce7 commit 1d1b0e1
Show file tree
Hide file tree
Showing 20 changed files with 2,106 additions and 558 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ import {
type KeyringControllerAddNewAccountAction,
KeyringTypes,
} from '@metamask/keyring-controller';
import type { NetworkConfiguration } from '@metamask/network-controller';
import type {
NetworkControllerAddNetworkAction,
NetworkControllerGetStateAction,
NetworkControllerNetworkRemovedEvent,
NetworkControllerRemoveNetworkAction,
NetworkControllerUpdateNetworkAction,
} from '@metamask/network-controller';
import type { HandleSnapRequest } from '@metamask/snaps-controllers';

import { createSHA256Hash } from '../../shared/encryption';
Expand All @@ -43,7 +49,10 @@ import {
isNameDefaultAccountName,
mapInternalAccountToUserStorageAccount,
} from './accounts/user-storage';
import { startNetworkSyncing } from './network-syncing/controller-integration';
import {
performMainNetworkSync,
startNetworkSyncing,
} from './network-syncing/controller-integration';
import {
batchDeleteUserStorage,
batchUpsertUserStorage,
Expand All @@ -54,27 +63,6 @@ import {
upsertUserStorage,
} from './services';

// TODO: add external NetworkController event
// Need to listen for when a network gets added
type NetworkControllerNetworkAddedEvent = {
type: 'NetworkController:networkAdded';
payload: [networkConfiguration: NetworkConfiguration];
};

// TODO: add external NetworkController event
// Need to listen for when a network is updated, or the default rpc/block explorer changes
type NetworkControllerNetworkChangedEvent = {
type: 'NetworkController:networkChanged';
payload: [networkConfiguration: NetworkConfiguration];
};

// TODO: add external NetworkController event
// Need to listen for when a network gets deleted
type NetworkControllerNetworkDeletedEvent = {
type: 'NetworkController:networkDeleted';
payload: [networkConfiguration: NetworkConfiguration];
};

// TODO: fix external dependencies
export declare type NotificationServicesControllerDisableNotificationServices =
{
Expand Down Expand Up @@ -108,6 +96,10 @@ export type UserStorageControllerState = {
* Condition used by UI to determine if account syncing is ready to be dispatched.
*/
isAccountSyncingReadyToBeDispatched: boolean;
/**
* Condition used to ensure that we do not perform any network sync mutations until we have synced at least once
*/
hasNetworkSyncingSyncedAtLeastOnce?: boolean;
};

export const defaultState: UserStorageControllerState = {
Expand All @@ -128,12 +120,16 @@ const metadata: StateMetadata<UserStorageControllerState> = {
},
hasAccountSyncingSyncedAtLeastOnce: {
persist: true,
anonymous: true,
anonymous: false,
},
isAccountSyncingReadyToBeDispatched: {
persist: false,
anonymous: false,
},
hasNetworkSyncingSyncedAtLeastOnce: {
persist: true,
anonymous: false,
},
};

type ControllerConfig = {
Expand All @@ -160,6 +156,31 @@ type ControllerConfig = {
situationMessage: string,
) => void;
};

networkSyncing?: {
maxNumberOfNetworksToAdd?: number;
/**
* Callback that fires when network sync adds a network
* This is used for analytics.
* @param profileId - ID for a given User (shared cross devices once authenticated)
* @param chainId - Chain ID for the network added (in hex)
*/
onNetworkAdded?: (profileId: string, chainId: string) => void;
/**
* Callback that fires when network sync updates a network
* This is used for analytics.
* @param profileId - ID for a given User (shared cross devices once authenticated)
* @param chainId - Chain ID for the network added (in hex)
*/
onNetworkUpdated?: (profileId: string, chainId: string) => void;
/**
* Callback that fires when network sync deletes a network
* This is used for analytics.
* @param profileId - ID for a given User (shared cross devices once authenticated)
* @param chainId - Chain ID for the network added (in hex)
*/
onNetworkRemoved?: (profileId: string, chainId: string) => void;
};
};

// Messenger Actions
Expand Down Expand Up @@ -216,10 +237,15 @@ export type AllowedActions =
// Metamask Notifications
| NotificationServicesControllerDisableNotificationServices
| NotificationServicesControllerSelectIsNotificationServicesEnabled
// Account syncing
// Account Syncing
| AccountsControllerListAccountsAction
| AccountsControllerUpdateAccountMetadataAction
| KeyringControllerAddNewAccountAction;
| KeyringControllerAddNewAccountAction
// Network Syncing
| NetworkControllerGetStateAction
| NetworkControllerAddNetworkAction
| NetworkControllerRemoveNetworkAction
| NetworkControllerUpdateNetworkAction;

// Messenger events
export type UserStorageControllerStateChangeEvent = ControllerStateChangeEvent<
Expand Down Expand Up @@ -249,9 +275,7 @@ export type AllowedEvents =
| AccountsControllerAccountAddedEvent
| AccountsControllerAccountRenamedEvent
// Network Syncing Events
| NetworkControllerNetworkAddedEvent
| NetworkControllerNetworkChangedEvent
| NetworkControllerNetworkDeletedEvent;
| NetworkControllerNetworkRemovedEvent;

// Messenger
export type UserStorageControllerMessenger = RestrictedControllerMessenger<
Expand All @@ -275,6 +299,13 @@ export default class UserStorageController extends BaseController<
UserStorageControllerState,
UserStorageControllerMessenger
> {
// This is replaced with the actual value in the constructor
// We will remove this once the feature will be released
#env = {
isAccountSyncingEnabled: false,
isNetworkSyncingEnabled: false,
};

#auth = {
getBearerToken: async () => {
return await this.messagingSystem.call(
Expand Down Expand Up @@ -303,8 +334,6 @@ export default class UserStorageController extends BaseController<
};

#accounts = {
// This is replaced with the actual value in the constructor
isAccountSyncingEnabled: false,
isAccountSyncingInProgress: false,
maxNumberOfAccountsToAdd: 0,
canSync: () => {
Expand All @@ -315,7 +344,7 @@ export default class UserStorageController extends BaseController<
return false;
}

if (!this.#accounts.isAccountSyncingEnabled) {
if (!this.#env.isAccountSyncingEnabled) {
return false;
}

Expand Down Expand Up @@ -482,12 +511,10 @@ export default class UserStorageController extends BaseController<
state: { ...defaultState, ...state },
});

this.#env.isAccountSyncingEnabled = Boolean(env?.isAccountSyncingEnabled);
this.#env.isNetworkSyncingEnabled = Boolean(env?.isNetworkSyncingEnabled);
this.#config = config;

this.#accounts.isAccountSyncingEnabled = Boolean(
env?.isAccountSyncingEnabled,
);

this.#accounts.maxNumberOfAccountsToAdd =
config?.accountSyncing?.maxNumberOfAccountsToAdd ?? 100;

Expand All @@ -498,18 +525,12 @@ export default class UserStorageController extends BaseController<
this.#accounts.setupAccountSyncingSubscriptions();

// Network Syncing
if (env?.isNetworkSyncingEnabled) {
if (this.#env.isNetworkSyncingEnabled) {
startNetworkSyncing({
messenger,
getStorageConfig: async () => {
const { storageKey, bearerToken } =
await this.#getStorageKeyAndBearerToken();
return {
storageKey,
bearerToken,
nativeScryptCrypto: this.#nativeScryptCrypto,
};
},
getStorageConfig: () => this.#getStorageOptions(),
isMutationSyncBlocked: () =>
!this.state.hasNetworkSyncingSyncedAtLeastOnce,
});
}
}
Expand Down Expand Up @@ -560,6 +581,20 @@ export default class UserStorageController extends BaseController<
);
}

async #getStorageOptions() {
if (!this.state.isProfileSyncingEnabled) {
return null;
}

const { storageKey, bearerToken } =
await this.#getStorageKeyAndBearerToken();
return {
storageKey,
bearerToken,
nativeScryptCrypto: this.#nativeScryptCrypto,
};
}

public async enableProfileSyncing(): Promise<void> {
try {
this.#setIsProfileSyncingUpdateLoading(true);
Expand Down Expand Up @@ -1128,4 +1163,28 @@ export default class UserStorageController extends BaseController<
);
}
}

async syncNetworks() {
if (!this.#env.isNetworkSyncingEnabled) {
return;
}

const profileId = await this.#auth.getProfileId();

await performMainNetworkSync({
messenger: this.messagingSystem,
getStorageConfig: () => this.#getStorageOptions(),
maxNetworksToAdd: this.#config?.networkSyncing?.maxNumberOfNetworksToAdd,
onNetworkAdded: (cId) =>
this.#config?.networkSyncing?.onNetworkAdded?.(profileId, cId),
onNetworkUpdated: (cId) =>
this.#config?.networkSyncing?.onNetworkUpdated?.(profileId, cId),
onNetworkRemoved: (cId) =>
this.#config?.networkSyncing?.onNetworkRemoved?.(profileId, cId),
});

this.update((s) => {
s.hasNetworkSyncingSyncedAtLeastOnce = true;
});
}
}
Loading

0 comments on commit 1d1b0e1

Please sign in to comment.