Skip to content

Commit

Permalink
feat(keyring-api)!: add scopes field to KeyringAccount (#101)
Browse files Browse the repository at this point in the history
See:
- MetaMask/accounts-planning#627

---------

Co-authored-by: Gustavo Antunes <[email protected]>
Co-authored-by: Dario Anongba Varela <[email protected]>
  • Loading branch information
3 people authored Dec 18, 2024
1 parent 29e1524 commit a3e4dbd
Show file tree
Hide file tree
Showing 25 changed files with 416 additions and 72 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ linkStyle default opacity:0.5
eth_snap_keyring --> keyring_api;
eth_snap_keyring --> keyring_internal_api;
eth_snap_keyring --> keyring_internal_snap_client;
eth_snap_keyring --> keyring_utils;
keyring_snap_client --> keyring_api;
keyring_snap_client --> keyring_utils;
keyring_snap_sdk --> keyring_utils;
Expand Down
24 changes: 23 additions & 1 deletion packages/keyring-api/src/api/account.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assert } from '@metamask/superstruct';

import { KeyringAccountStruct } from './account';
import { EthAccountType, KeyringAccountStruct } from './account';

const supportedKeyringAccountTypes = Object.keys(
KeyringAccountStruct.schema.type.schema,
Expand All @@ -12,6 +12,7 @@ describe('api', () => {
const baseAccount = {
id: '606a7759-b0fb-48e4-9874-bab62ff8e7eb',
address: '0x000',
scopes: [],
options: {},
methods: [],
};
Expand All @@ -33,5 +34,26 @@ describe('api', () => {
);
},
);

it.each([
// Namespace too short (< 3):
'',
'a',
'ei',
'bi',
'bi:p122something',
// Namespace too long (> 8):
'eip11155111',
'eip11155111:11155111',
])('throws an error if account scopes is: %s', (scope: string) => {
const account = {
...baseAccount,
type: EthAccountType.Eoa,
scopes: [scope],
};
expect(() => assert(account, KeyringAccountStruct)).toThrow(
`At path: scopes.0 -- Expected the value to satisfy a union of \`string | string\`, but received: "${scope}"`,
);
});
});
});
20 changes: 18 additions & 2 deletions packages/keyring-api/src/api/account.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import { object, UuidStruct } from '@metamask/keyring-utils';
import type { Infer } from '@metamask/superstruct';
import { array, enums, record, string } from '@metamask/superstruct';
import { JsonStruct } from '@metamask/utils';
import {
nonempty,
array,
enums,
record,
string,
union,
} from '@metamask/superstruct';
import {
CaipChainIdStruct,
CaipNamespaceStruct,
JsonStruct,
} from '@metamask/utils';

/**
* Supported Ethereum account types.
Expand Down Expand Up @@ -62,6 +73,11 @@ export const KeyringAccountStruct = object({
*/
address: string(),

/**
* Account supported scopes (CAIP-2 chain IDs).
*/
scopes: nonempty(array(union([CaipNamespaceStruct, CaipChainIdStruct]))),

/**
* Account options.
*/
Expand Down
13 changes: 13 additions & 0 deletions packages/keyring-api/src/btc/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* istanbul ignore file */

/**
* Scopes for Bitcoin account type. See {@link KeyringAccount.scopes}.
*/
export enum BtcScopes {
Namespace = 'bip122',
Mainnet = 'bip122:000000000019d6689c085ae165831e93',
Testnet = 'bip122:000000000933ea01ad0ee984209779ba',
Testnet4 = 'bip122:00000000da84f2bafbbc53dee25a72ae',
Signet = 'bip122:00000008819873e925422c1ff0f99f7c',
Regtest = 'bip122:regtest',
}
1 change: 1 addition & 0 deletions packages/keyring-api/src/btc/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './constants';
export * from './types';
9 changes: 9 additions & 0 deletions packages/keyring-api/src/eth/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/* istanbul ignore file */

/**
* Scopes for EVM account type. See {@link KeyringAccount.scopes}.
*/
export enum EthScopes {
Namespace = 'eip155',
Mainnet = 'eip155:1',
}
1 change: 1 addition & 0 deletions packages/keyring-api/src/eth/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './constants';
export * from './erc4337';
export * from './types';
export * from './utils';
8 changes: 7 additions & 1 deletion packages/keyring-api/src/eth/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { object, definePattern } from '@metamask/keyring-utils';
import type { Infer } from '@metamask/superstruct';
import { array, enums, literal } from '@metamask/superstruct';
import { nonempty, array, enums, literal } from '@metamask/superstruct';

import { EthScopes } from '.';
import { EthAccountType, KeyringAccountStruct } from '../api';

export const EthBytesStruct = definePattern('EthBytes', /^0x[0-9a-f]*$/iu);
Expand Down Expand Up @@ -46,6 +47,11 @@ export const EthEoaAccountStruct = object({
*/
type: literal(`${EthAccountType.Eoa}`),

/**
* Account scopes (must be ['eip155']).
*/
scopes: nonempty(array(literal(EthScopes.Namespace))),

/**
* Account supported methods.
*/
Expand Down
6 changes: 6 additions & 0 deletions packages/keyring-api/src/events.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { is } from '@metamask/superstruct';

import { EthAccountType } from './api';
import { EthScopes } from './eth/constants';
import {
AccountCreatedEventStruct,
AccountDeletedEventStruct,
Expand All @@ -21,6 +22,7 @@ describe('events', () => {
address: '0x0123',
methods: [],
options: {},
scopes: [EthScopes.Namespace],
type: EthAccountType.Eoa,
},
},
Expand All @@ -38,6 +40,7 @@ describe('events', () => {
address: '0x0123',
methods: [],
options: {},
scopes: [EthScopes.Namespace],
type: EthAccountType.Eoa,
},
},
Expand All @@ -55,6 +58,7 @@ describe('events', () => {
address: '0x0123',
methods: [],
options: {},
scopes: [EthScopes.Namespace],
type: EthAccountType.Eoa,
},
displayConfirmation: true,
Expand All @@ -75,6 +79,7 @@ describe('events', () => {
address: '0x0123',
methods: [],
options: {},
scopes: [EthScopes.Namespace],
type: EthAccountType.Eoa,
},
},
Expand All @@ -92,6 +97,7 @@ describe('events', () => {
address: '0x0123',
methods: [],
options: {},
scopes: [EthScopes.Namespace],
type: EthAccountType.Eoa,
},
},
Expand Down
11 changes: 11 additions & 0 deletions packages/keyring-api/src/sol/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* istanbul ignore file */

/**
* Scopes for Solana account type. See {@link KeyringAccount.scopes}.
*/
export enum SolScopes {
Namespace = 'solana',
Devnet = 'solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1',
Mainnet = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
Testnet = 'solana:4uhcVJyU9pJkvQyS88uRDiswHXSCkY3z',
}
1 change: 1 addition & 0 deletions packages/keyring-api/src/sol/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './constants';
export * from './types';
36 changes: 30 additions & 6 deletions packages/keyring-internal-api/src/types.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { assert } from '@metamask/superstruct';

import type { InternalAccount } from '.';
import { InternalAccountStruct } from '.';

describe('InternalAccount', () => {
it.each([
{ type: 'eip155:eoa', address: '0x000' },
{ type: 'eip155:eoa', address: '0x000', scopes: ['eip155'] },
{
type: 'bip122:p2wpkh',
address: 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4',
scopes: ['bip122:000000000019d6689c085ae165831e93'],
},
])('should have the correct structure: %s', ({ type, address }) => {
])('should have the correct structure: %s', ({ type, address, scopes }) => {
const account = {
id: '606a7759-b0fb-48e4-9874-bab62ff8e7eb',
address,
options: {},
methods: [],
scopes,
type,
metadata: {
keyring: {
Expand All @@ -34,6 +37,7 @@ describe('InternalAccount', () => {
address: '0x000',
options: {},
methods: [],
scopes: ['eip155'],
type: 'eip155:eoa',
metadata: {
keyring: {},
Expand All @@ -53,6 +57,7 @@ describe('InternalAccount', () => {
address: '0x000',
options: {},
methods: [],
scopes: ['eip155'],
type: 'eip155:eoa',
metadata: {
name: 'Account 1',
Expand All @@ -71,6 +76,7 @@ describe('InternalAccount', () => {
address: '0x000',
options: {},
methods: [],
scopes: ['eip155'],
type: 'eip155:eoa',
};

Expand All @@ -79,12 +85,27 @@ describe('InternalAccount', () => {
);
});

it('should throw if scopes is not set', () => {
const account = {
id: '606a7759-b0fb-48e4-9874-bab62ff8e7eb',
address: '0x000',
options: {},
methods: [],
type: 'eip155:eoa',
};

expect(() => assert(account, InternalAccountStruct)).toThrow(
'At path: scopes -- Expected an array value, but received: undefined',
);
});

it('should throw if there are extra fields', () => {
const account = {
id: '606a7759-b0fb-48e4-9874-bab62ff8e7eb',
address: '0x000',
options: {},
methods: [],
scopes: ['eip155'],
type: 'eip155:eoa',
metadata: {
keyring: {
Expand All @@ -102,12 +123,13 @@ describe('InternalAccount', () => {
});

it('should contain snap name, id and enabled if the snap metadata exists', () => {
const account = {
const account: InternalAccount = {
id: '606a7759-b0fb-48e4-9874-bab62ff8e7eb',
address: '0x000',
options: {},
methods: [],
type: 'eip155:eoa',
scopes: ['eip155'],
metadata: {
keyring: {
type: 'Test Keyring',
Expand All @@ -126,13 +148,14 @@ describe('InternalAccount', () => {
});

it.each([['name', 'enabled', 'id']])(
'should throw if snap.%i is not set',
'should throw if snap.%s is not set',
(key: string) => {
const account = {
const account: InternalAccount = {
id: '606a7759-b0fb-48e4-9874-bab62ff8e7eb',
address: '0x000',
options: {},
methods: [],
scopes: ['eip155'],
type: 'eip155:eoa',
metadata: {
keyring: {
Expand All @@ -148,7 +171,8 @@ describe('InternalAccount', () => {
},
};

delete account.metadata.snap[key as keyof typeof account.metadata.snap];
// On `InternalAccount` the `metadata.snap` is optional, hence the `?.` here.
delete account.metadata.snap?.[key as keyof typeof account.metadata.snap];

const regex = new RegExp(`At path: metadata.snap.${key}`, 'u');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('KeyringSnapControllerClient', () => {
address: '0xE9A74AACd7df8112911ca93260fC5a046f8a64Ae',
options: {},
methods: [],
scopes: ['eip155'],
type: 'eip155:eoa',
},
];
Expand Down
1 change: 1 addition & 0 deletions packages/keyring-snap-bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@metamask/keyring-api": "workspace:^",
"@metamask/keyring-internal-api": "workspace:^",
"@metamask/keyring-internal-snap-client": "workspace:^",
"@metamask/keyring-utils": "workspace:^",
"@metamask/snaps-controllers": "^9.10.0",
"@metamask/snaps-sdk": "^6.7.0",
"@metamask/snaps-utils": "^8.3.0",
Expand Down
Loading

0 comments on commit a3e4dbd

Please sign in to comment.