Skip to content

Commit

Permalink
feat(NOTIFY-1046): attach account sync upwards sync to `AccountsContr…
Browse files Browse the repository at this point in the history
…oller` events (#4665)

## Explanation

This PR adds listeners for `AccountsController` events to
`UserStorageController`:
- `accountAdded`
- `accountRenamed`

`UserStorageController` will update the user storage whenever these
events are received.

## References


[NOTIFY-1046](https://consensyssoftware.atlassian.net/jira/software/projects/NOTIFY/boards/616?assignee=712020%3A5843b7e2-a7fe-4c45-9fbd-e1f2b2eb58c2&selectedIssue=NOTIFY-1046)

## Changelog

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

- **ADDED**: `accountAdded` and `accountRenamed` events will now trigger
upwards account syncing


## 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


[NOTIFY-1046]:
https://consensyssoftware.atlassian.net/browse/NOTIFY-1046?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
  • Loading branch information
mathieuartu authored Sep 9, 2024
1 parent 741f9b3 commit b9e499a
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,56 @@ describe('user-storage/user-storage-controller - saveInternalAccountToUserStorag
),
).rejects.toThrow(expect.any(Error));
});

it('saves an internal account to user storage when the AccountsController:accountRenamed event is fired', async () => {
const { baseMessenger, messenger } = mockUserStorageMessenger();

const controller = new UserStorageController({
messenger,
env: {
isAccountSyncingEnabled: true,
},
getMetaMetricsState: () => true,
});

const mockSaveInternalAccountToUserStorage = jest
.spyOn(controller, 'saveInternalAccountToUserStorage')
.mockImplementation();

baseMessenger.publish(
'AccountsController:accountRenamed',
MOCK_INTERNAL_ACCOUNTS.ONE[0] as InternalAccount,
);

expect(mockSaveInternalAccountToUserStorage).toHaveBeenCalledWith(
MOCK_INTERNAL_ACCOUNTS.ONE[0].address,
);
});

it('saves an internal account to user storage when the AccountsController:accountAdded event is fired', async () => {
const { baseMessenger, messenger } = mockUserStorageMessenger();

const controller = new UserStorageController({
messenger,
env: {
isAccountSyncingEnabled: true,
},
getMetaMetricsState: () => true,
});

const mockSaveInternalAccountToUserStorage = jest
.spyOn(controller, 'saveInternalAccountToUserStorage')
.mockImplementation();

baseMessenger.publish(
'AccountsController:accountAdded',
MOCK_INTERNAL_ACCOUNTS.ONE[0] as InternalAccount,
);

expect(mockSaveInternalAccountToUserStorage).toHaveBeenCalledWith(
MOCK_INTERNAL_ACCOUNTS.ONE[0].address,
);
});
});

/**
Expand All @@ -1290,14 +1340,16 @@ describe('user-storage/user-storage-controller - saveInternalAccountToUserStorag
* @returns Mock User Storage Messenger
*/
function mockUserStorageMessenger(options?: {
accounts: {
accounts?: {
accountsList?: InternalAccount[];
};
}) {
const messenger = new ControllerMessenger<
const baseMessenger = new ControllerMessenger<
AllowedActions,
AllowedEvents
>().getRestricted({
>();

const messenger = baseMessenger.getRestricted({
name: 'UserStorageController',
allowedActions: [
'KeyringController:getState',
Expand All @@ -1314,7 +1366,12 @@ function mockUserStorageMessenger(options?: {
'AccountsController:getAccountByAddress',
'KeyringController:addNewAccount',
],
allowedEvents: ['KeyringController:lock', 'KeyringController:unlock'],
allowedEvents: [
'KeyringController:lock',
'KeyringController:unlock',
'AccountsController:accountAdded',
'AccountsController:accountRenamed',
],
});

const mockSnapGetPublicKey = jest.fn().mockResolvedValue('MOCK_PUBLIC_KEY');
Expand Down Expand Up @@ -1369,14 +1426,9 @@ function mockUserStorageMessenger(options?: {

const mockAccountsUpdateAccountMetadata = jest.fn().mockResolvedValue(true);

const mockAccountsGetAccountByAddress = jest.fn().mockResolvedValue({
address: '0x123',
id: '1',
metadata: {
name: 'test',
nameLastUpdatedAt: 1,
},
});
const mockAccountsGetAccountByAddress = jest
.fn()
.mockResolvedValue(MOCK_INTERNAL_ACCOUNTS.ONE[0]);

jest.spyOn(messenger, 'call').mockImplementation((...args) => {
// Creates the correct typed call params for mocks
Expand Down Expand Up @@ -1469,6 +1521,7 @@ function mockUserStorageMessenger(options?: {
});

return {
baseMessenger,
messenger,
mockSnapGetPublicKey,
mockSnapSignMessage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import type {
AccountsControllerListAccountsAction,
AccountsControllerUpdateAccountMetadataAction,
AccountsControllerGetAccountByAddressAction,
AccountsControllerAccountRenamedEvent,
AccountsControllerAccountAddedEvent,
} from '@metamask/accounts-controller';
import type {
ControllerGetStateAction,
Expand Down Expand Up @@ -181,7 +183,9 @@ export type AllowedEvents =
| UserStorageControllerAccountSyncingInProgress
| UserStorageControllerAccountSyncingComplete
| KeyringControllerLockEvent
| KeyringControllerUnlockEvent;
| KeyringControllerUnlockEvent
| AccountsControllerAccountAddedEvent
| AccountsControllerAccountRenamedEvent;

// Messenger
export type UserStorageControllerMessenger = RestrictedControllerMessenger<
Expand Down Expand Up @@ -245,6 +249,23 @@ export default class UserStorageController extends BaseController<
this.#accounts.maxSyncInterval
);
},
setupAccountSyncingSubscriptions: () => {
this.messagingSystem.subscribe(
'AccountsController:accountAdded',
// eslint-disable-next-line @typescript-eslint/no-misused-promises
async (account) => {
await this.saveInternalAccountToUserStorage(account.address);
},
);

this.messagingSystem.subscribe(
'AccountsController:accountRenamed',
// eslint-disable-next-line @typescript-eslint/no-misused-promises
async (account) => {
await this.saveInternalAccountToUserStorage(account.address);
},
);
},
getInternalAccountByAddress: async (address: string) => {
return this.messagingSystem.call(
'AccountsController:getAccountByAddress',
Expand Down Expand Up @@ -328,15 +349,9 @@ export default class UserStorageController extends BaseController<
);
this.#isUnlocked = isUnlocked;

this.messagingSystem.subscribe(
'KeyringController:unlock',
// eslint-disable-next-line @typescript-eslint/no-misused-promises
async () => {
this.#isUnlocked = true;

await this.syncInternalAccountsWithUserStorage();
},
);
this.messagingSystem.subscribe('KeyringController:unlock', () => {
this.#isUnlocked = true;
});

this.messagingSystem.subscribe('KeyringController:lock', () => {
this.#isUnlocked = false;
Expand Down Expand Up @@ -381,6 +396,7 @@ export default class UserStorageController extends BaseController<
this.#keyringController.setupLockedStateSubscriptions();
this.#registerMessageHandlers();
this.#nativeScryptCrypto = nativeScryptCrypto;
this.#accounts.setupAccountSyncingSubscriptions();
}

/**
Expand Down Expand Up @@ -812,6 +828,8 @@ export default class UserStorageController extends BaseController<
}

try {
this.#assertProfileSyncingEnabled();

await this.#accounts.saveInternalAccountToUserStorage(address);
} catch (e) {
const errorMessage = e instanceof Error ? e.message : JSON.stringify(e);
Expand Down

0 comments on commit b9e499a

Please sign in to comment.