Skip to content

Commit ca9c3d4

Browse files
authored
Merge branch 'main' into SWAPS-2839-update-snap-methods-in-bridge-controller
2 parents f35d3a2 + 32223e1 commit ca9c3d4

File tree

187 files changed

+5665
-1293
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

187 files changed

+5665
-1293
lines changed

eslint-warning-thresholds.json

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,10 @@
107107
"jsdoc/tag-lines": 2
108108
},
109109
"packages/base-controller/src/BaseController.test.ts": {
110-
"import-x/namespace": 16
111-
},
112-
"packages/base-controller/src/BaseController.ts": {
113-
"jsdoc/check-tag-names": 2
110+
"import-x/namespace": 18
114111
},
115112
"packages/base-controller/src/next/BaseController.test.ts": {
116-
"import-x/namespace": 16
117-
},
118-
"packages/base-controller/src/next/BaseController.ts": {
119-
"jsdoc/check-tag-names": 2
113+
"import-x/namespace": 18
120114
},
121115
"packages/build-utils/src/transforms/remove-fenced-code.test.ts": {
122116
"import-x/order": 1

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@metamask/core-monorepo",
3-
"version": "529.0.0",
3+
"version": "538.0.0",
44
"private": true,
55
"description": "Monorepo for packages shared between MetaMask clients",
66
"repository": {

packages/account-tree-controller/CHANGELOG.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.13.0]
11+
12+
### Added
13+
14+
- Add unique name validation for account groups to prevent duplicate group names ([#6492](https://github.com/MetaMask/core/pull/6492))
15+
- `setAccountGroupName` now validates that group names are unique across all groups.
16+
- Added `isAccountGroupNameUnique` utility function to check name uniqueness.
17+
- Names are trimmed of leading/trailing whitespace before comparison to prevent accidental duplicates.
18+
1019
### Changed
1120

1221
- **BREAKING:** Remove support for `AccountsController:accountRenamed` event handling ([#6438](https://github.com/MetaMask/core/pull/6438))
22+
- Bump `@metamask/base-controller` from `^8.2.0` to `^8.3.0` ([#6465](https://github.com/MetaMask/core/pull/6465))
1323

1424
## [0.12.1]
1525

@@ -164,7 +174,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
164174
- Initial release ([#5847](https://github.com/MetaMask/core/pull/5847))
165175
- Grouping accounts into 3 main categories: Entropy source, Snap ID, keyring types.
166176

167-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.12.1...HEAD
177+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.13.0...HEAD
178+
[0.13.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.12.1...@metamask/account-tree-controller@0.13.0
168179
[0.12.1]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.12.0...@metamask/account-tree-controller@0.12.1
169180
[0.12.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.11.0...@metamask/account-tree-controller@0.12.0
170181
[0.11.0]: https://github.com/MetaMask/core/compare/@metamask/account-tree-controller@0.10.0...@metamask/account-tree-controller@0.11.0

packages/account-tree-controller/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@metamask/account-tree-controller",
3-
"version": "0.12.1",
3+
"version": "0.13.0",
44
"description": "Controller to group account together based on some pre-defined rules",
55
"keywords": [
66
"MetaMask",
@@ -47,7 +47,7 @@
4747
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
4848
},
4949
"dependencies": {
50-
"@metamask/base-controller": "^8.2.0",
50+
"@metamask/base-controller": "^8.3.0",
5151
"@metamask/snaps-sdk": "^9.0.0",
5252
"@metamask/snaps-utils": "^11.0.0",
5353
"lodash": "^4.17.21"

packages/account-tree-controller/src/AccountTreeController.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1949,6 +1949,115 @@ describe('AccountTreeController', () => {
19491949
controller.state.accountWalletsMetadata[nonExistentWalletId],
19501950
).toBeUndefined();
19511951
});
1952+
1953+
it('allows setting the same name for the same group', () => {
1954+
const { controller } = setup({
1955+
accounts: [MOCK_HD_ACCOUNT_1],
1956+
keyrings: [MOCK_HD_KEYRING_1],
1957+
});
1958+
1959+
controller.init();
1960+
1961+
const wallets = controller.getAccountWalletObjects();
1962+
const groups = Object.values(wallets[0].groups);
1963+
const groupId = groups[0].id;
1964+
1965+
const customName = 'My Custom Group';
1966+
1967+
// Set the name first time - should succeed
1968+
controller.setAccountGroupName(groupId, customName);
1969+
1970+
// Set the same name again for the same group - should succeed
1971+
expect(() => {
1972+
controller.setAccountGroupName(groupId, customName);
1973+
}).not.toThrow();
1974+
});
1975+
1976+
it('prevents setting duplicate names across different groups', () => {
1977+
const { controller } = setup({
1978+
accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2],
1979+
keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2],
1980+
});
1981+
1982+
controller.init();
1983+
1984+
const wallets = controller.getAccountWalletObjects();
1985+
1986+
// We should have 2 wallets (one for each keyring)
1987+
expect(wallets).toHaveLength(2);
1988+
1989+
const wallet1 = wallets[0];
1990+
const wallet2 = wallets[1];
1991+
const groups1 = Object.values(wallet1.groups);
1992+
const groups2 = Object.values(wallet2.groups);
1993+
1994+
expect(groups1.length).toBeGreaterThanOrEqual(1);
1995+
expect(groups2.length).toBeGreaterThanOrEqual(1);
1996+
1997+
const groupId1 = groups1[0].id;
1998+
const groupId2 = groups2[0].id;
1999+
const duplicateName = 'Duplicate Group Name';
2000+
2001+
// Set name for first group - should succeed
2002+
controller.setAccountGroupName(groupId1, duplicateName);
2003+
2004+
// Try to set the same name for second group - should throw
2005+
expect(() => {
2006+
controller.setAccountGroupName(groupId2, duplicateName);
2007+
}).toThrow('Account group name already exists');
2008+
});
2009+
2010+
it('ensures unique names when generating default names', () => {
2011+
const { controller } = setup({
2012+
accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2],
2013+
keyrings: [MOCK_HD_KEYRING_1],
2014+
});
2015+
2016+
controller.init();
2017+
2018+
const wallets = controller.getAccountWalletObjects();
2019+
const groups = Object.values(wallets[0].groups);
2020+
2021+
// All groups should have unique names by default
2022+
const names = groups.map((group) => group.metadata.name);
2023+
const uniqueNames = new Set(names);
2024+
2025+
expect(uniqueNames.size).toBe(names.length);
2026+
expect(names.every((name) => name.length > 0)).toBe(true);
2027+
});
2028+
2029+
it('prevents duplicate names when comparing trimmed names', () => {
2030+
const { controller } = setup({
2031+
accounts: [MOCK_HD_ACCOUNT_1, MOCK_HD_ACCOUNT_2],
2032+
keyrings: [MOCK_HD_KEYRING_1, MOCK_HD_KEYRING_2],
2033+
});
2034+
2035+
controller.init();
2036+
2037+
const wallets = controller.getAccountWalletObjects();
2038+
expect(wallets).toHaveLength(2);
2039+
2040+
const wallet1 = wallets[0];
2041+
const wallet2 = wallets[1];
2042+
const groups1 = Object.values(wallet1.groups);
2043+
const groups2 = Object.values(wallet2.groups);
2044+
2045+
expect(groups1.length).toBeGreaterThanOrEqual(1);
2046+
expect(groups2.length).toBeGreaterThanOrEqual(1);
2047+
2048+
const groupId1 = groups1[0].id;
2049+
const groupId2 = groups2[0].id;
2050+
2051+
// Set name for first group with trailing spaces
2052+
const nameWithSpaces = ' My Group Name ';
2053+
controller.setAccountGroupName(groupId1, nameWithSpaces);
2054+
2055+
// Try to set the same name for second group with different spacing - should throw
2056+
const nameWithDifferentSpacing = ' My Group Name ';
2057+
expect(() => {
2058+
controller.setAccountGroupName(groupId2, nameWithDifferentSpacing);
2059+
}).toThrow('Account group name already exists');
2060+
});
19522061
});
19532062

19542063
describe('Fallback Naming', () => {

packages/account-tree-controller/src/AccountTreeController.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { isEvmAccountType } from '@metamask/keyring-api';
1212
import type { InternalAccount } from '@metamask/keyring-internal-api';
1313

1414
import type { AccountGroupObject } from './group';
15+
import { isAccountGroupNameUnique } from './group';
1516
import type { Rule } from './rule';
1617
import { EntropyRule } from './rules/entropy';
1718
import { KeyringRule } from './rules/keyring';
@@ -693,6 +694,19 @@ export class AccountTreeController extends BaseController<
693694
}
694695
}
695696

697+
/**
698+
* Asserts that an account group name is unique across all groups.
699+
*
700+
* @param groupId - The account group ID to exclude from the check.
701+
* @param name - The name to validate for uniqueness.
702+
* @throws Error if the name already exists in another group.
703+
*/
704+
#assertAccountGroupNameIsUnique(groupId: AccountGroupId, name: string): void {
705+
if (!isAccountGroupNameUnique(this.state, groupId, name)) {
706+
throw new Error('Account group name already exists');
707+
}
708+
}
709+
696710
/**
697711
* Gets the currently selected account group ID.
698712
*
@@ -891,11 +905,15 @@ export class AccountTreeController extends BaseController<
891905
* @param groupId - The account group ID.
892906
* @param name - The custom name to set.
893907
* @throws If the account group ID is not found in the current tree.
908+
* @throws If the account group name already exists.
894909
*/
895910
setAccountGroupName(groupId: AccountGroupId, name: string): void {
896911
// Validate that the group exists in the current tree
897912
this.#assertAccountGroupExists(groupId);
898913

914+
// Validate that the name is unique
915+
this.#assertAccountGroupNameIsUnique(groupId, name);
916+
899917
this.update((state) => {
900918
// Update persistent metadata
901919
state.accountGroupsMetadata[groupId] ??= {};

packages/account-tree-controller/src/group.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {
55
import type { AccountGroupId } from '@metamask/account-api';
66
import type { AccountId } from '@metamask/accounts-controller';
77

8-
import type { UpdatableField, ExtractFieldValues } from './type-utils.js';
8+
import type { UpdatableField, ExtractFieldValues } from './type-utils';
9+
import type { AccountTreeControllerState } from './types';
910

1011
/**
1112
* Persisted metadata for account groups (stored in controller state for persistence/sync).
@@ -84,3 +85,29 @@ export type AccountGroupObjectOf<GroupType extends AccountGroupType> = Extract<
8485
},
8586
{ type: GroupType }
8687
>['object'];
88+
89+
/**
90+
* Checks if an account group name is unique across all groups.
91+
*
92+
* @param state - The account tree controller state.
93+
* @param groupId - The account group ID to exclude from the check.
94+
* @param name - The name to validate for uniqueness.
95+
* @returns True if the name is unique, false otherwise.
96+
*/
97+
export function isAccountGroupNameUnique(
98+
state: AccountTreeControllerState,
99+
groupId: AccountGroupId,
100+
name: string,
101+
): boolean {
102+
const trimmedName = name.trim();
103+
104+
for (const wallet of Object.values(state.accountTree.wallets)) {
105+
for (const group of Object.values(wallet.groups)) {
106+
if (group.id !== groupId && group.metadata.name.trim() === trimmedName) {
107+
return false;
108+
}
109+
}
110+
}
111+
112+
return true;
113+
}

packages/account-tree-controller/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export type { AccountWalletObject } from './wallet';
22
export type { AccountGroupObject } from './group';
3+
export { isAccountGroupNameUnique } from './group';
34

45
export type {
56
AccountTreeControllerState,

packages/accounts-controller/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Changed
1111

12-
- Bump `@metamask/base-controller` from `^8.1.0` to `^8.2.0` ([#6355](https://github.com/MetaMask/core/pull/6355))
12+
- Bump `@metamask/base-controller` from `^8.1.0` to `^8.3.0` ([#6355](https://github.com/MetaMask/core/pull/6355), [#6465](https://github.com/MetaMask/core/pull/6465))
1313

1414
## [33.0.0]
1515

packages/accounts-controller/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
},
4949
"dependencies": {
5050
"@ethereumjs/util": "^9.1.0",
51-
"@metamask/base-controller": "^8.2.0",
51+
"@metamask/base-controller": "^8.3.0",
5252
"@metamask/eth-snap-keyring": "^16.1.0",
5353
"@metamask/keyring-api": "^20.1.0",
5454
"@metamask/keyring-internal-api": "^8.1.0",

0 commit comments

Comments
 (0)