Skip to content

Commit

Permalink
Add addNewAccountForKeyring method (#1591)
Browse files Browse the repository at this point in the history
feat: add new account for keyring

Co-authored-by: Mark Stacey <[email protected]>
  • Loading branch information
2 people authored and MajorLift committed Oct 11, 2023
1 parent 88c4e74 commit 0a152c6
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 0 deletions.
100 changes: 100 additions & 0 deletions packages/keyring-controller/src/KeyringController.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,106 @@ describe('KeyringController', () => {
});
});

describe('addNewAccountForKeyring', () => {
describe('when accountCount is not provided', () => {
it('should add new account', async () => {
await withController(
async ({ controller, initialState, preferences }) => {
const [primaryKeyring] = controller.getKeyringsByType(
KeyringTypes.hd,
) as Keyring<Json>[];
const addedAccountAddress =
await controller.addNewAccountForKeyring(primaryKeyring);
expect(initialState.keyrings).toHaveLength(1);
expect(initialState.keyrings[0].accounts).not.toStrictEqual(
controller.state.keyrings[0].accounts,
);
expect(controller.state.keyrings[0].accounts).toHaveLength(2);
expect(initialState.keyrings[0].accounts).not.toContain(
addedAccountAddress,
);
expect(addedAccountAddress).toBe(
controller.state.keyrings[0].accounts[1],
);
expect(
preferences.updateIdentities.calledWith(
controller.state.keyrings[0].accounts,
),
).toBe(true);
expect(preferences.setSelectedAddress.called).toBe(false);
},
);
});
});

describe('when accountCount is provided', () => {
it('should add new account if accountCount is in sequence', async () => {
await withController(
async ({ controller, initialState, preferences }) => {
const [primaryKeyring] = controller.getKeyringsByType(
KeyringTypes.hd,
) as Keyring<Json>[];
const addedAccountAddress =
await controller.addNewAccountForKeyring(primaryKeyring);
expect(initialState.keyrings).toHaveLength(1);
expect(initialState.keyrings[0].accounts).not.toStrictEqual(
controller.state.keyrings[0].accounts,
);
expect(controller.state.keyrings[0].accounts).toHaveLength(2);
expect(initialState.keyrings[0].accounts).not.toContain(
addedAccountAddress,
);
expect(addedAccountAddress).toBe(
controller.state.keyrings[0].accounts[1],
);
expect(
preferences.updateIdentities.calledWith(
controller.state.keyrings[0].accounts,
),
).toBe(true);
expect(preferences.setSelectedAddress.called).toBe(false);
},
);
});

it('should throw an error if passed accountCount param is out of sequence', async () => {
await withController(async ({ controller, initialState }) => {
const [primaryKeyring] = controller.getKeyringsByType(
KeyringTypes.hd,
) as Keyring<Json>[];
const accountCount = initialState.keyrings[0].accounts.length;
await expect(
controller.addNewAccountForKeyring(
primaryKeyring,
accountCount + 1,
),
).rejects.toThrow('Account out of sequence');
});
});

it('should not add a new account if called twice with the same accountCount param', async () => {
await withController(async ({ controller, initialState }) => {
const accountCount = initialState.keyrings[0].accounts.length;
const [primaryKeyring] = controller.getKeyringsByType(
KeyringTypes.hd,
) as Keyring<Json>[];
const firstAccountAdded = await controller.addNewAccountForKeyring(
primaryKeyring,
accountCount,
);
const secondAccountAdded = await controller.addNewAccountForKeyring(
primaryKeyring,
accountCount,
);
expect(firstAccountAdded).toBe(secondAccountAdded);
expect(controller.state.keyrings[0].accounts).toHaveLength(
accountCount + 1,
);
});
});
});
});

describe('addNewAccountWithoutUpdate', () => {
it('should add new account without updating', async () => {
await withController(
Expand Down
31 changes: 31 additions & 0 deletions packages/keyring-controller/src/KeyringController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,37 @@ export class KeyringController extends BaseControllerV2<
};
}

/**
* Adds a new account to the specified keyring.
*
* @param keyring - Keyring to add the account to.
* @param accountCount - Number of accounts before adding a new one, used to make the method idempotent.
* @returns Promise resolving to keyring current state and added account
*/
async addNewAccountForKeyring(
keyring: Keyring<Json>,
accountCount?: number,
): Promise<Hex> {
const oldAccounts = await keyring.getAccounts();

if (accountCount && oldAccounts.length !== accountCount) {
if (accountCount > oldAccounts.length) {
throw new Error('Account out of sequence');
}
return oldAccounts[accountCount];
}

await this.#keyring.addNewAccount(keyring);
const addedAccountAddress = (await keyring.getAccounts()).find(
(selectedAddress) => !oldAccounts.includes(selectedAddress),
);
assertIsStrictHexString(addedAccountAddress);

this.updateIdentities(await this.#keyring.getAccounts());

return addedAccountAddress;
}

/**
* Adds a new account to the default (first) HD seed phrase keyring without updating identities in preferences.
*
Expand Down

0 comments on commit 0a152c6

Please sign in to comment.