Skip to content

Commit e8d9fcf

Browse files
jieximcmireadonesky1
authored
feat: Integrate non-evm SIP-26 support into @metamask/multichain (#5191)
Updates the `@metamask/multichain` package to use new hooks provided by the Snaps `MultichainRouter` to enable non-evm support on the Multichain API. --------- Co-authored-by: Elliot Winkler <elliot.winkler@gmail.com> Co-authored-by: Alex Donesky <adonesky@gmail.com>
1 parent ee1e79d commit e8d9fcf

17 files changed

+1335
-462
lines changed

eslint-warning-thresholds.json

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -300,29 +300,20 @@
300300
"jsdoc/tag-lines": 5
301301
},
302302
"packages/multichain/src/adapters/caip-permission-adapter-session-scopes.test.ts": {
303-
"import-x/order": 1
304-
},
305-
"packages/multichain/src/adapters/caip-permission-adapter-session-scopes.ts": {
306-
"jsdoc/tag-lines": 3
303+
"@typescript-eslint/no-unused-vars": 2
307304
},
308305
"packages/multichain/src/caip25Permission.test.ts": {
309-
"@typescript-eslint/no-unused-vars": 3
306+
"@typescript-eslint/no-unused-vars": 5
310307
},
311308
"packages/multichain/src/caip25Permission.ts": {
312-
"@typescript-eslint/no-unused-vars": 1,
313-
"jsdoc/tag-lines": 1
314-
},
315-
"packages/multichain/src/handlers/wallet-getSession.test.ts": {
316-
"import-x/order": 1
309+
"@typescript-eslint/no-unused-vars": 1
317310
},
318311
"packages/multichain/src/handlers/wallet-getSession.ts": {
319-
"@typescript-eslint/no-unused-vars": 2,
312+
"@typescript-eslint/no-unused-vars": 1,
320313
"jsdoc/require-returns": 1
321314
},
322-
"packages/multichain/src/handlers/wallet-invokeMethod.test.ts": {
323-
"import-x/order": 2
324-
},
325315
"packages/multichain/src/handlers/wallet-invokeMethod.ts": {
316+
"@typescript-eslint/no-unsafe-enum-comparison": 1,
326317
"@typescript-eslint/no-unused-vars": 1,
327318
"jsdoc/require-returns": 1
328319
},
@@ -348,26 +339,23 @@
348339
"@typescript-eslint/no-unused-vars": 3
349340
},
350341
"packages/multichain/src/scope/assert.ts": {
351-
"@typescript-eslint/no-unsafe-enum-comparison": 1,
352-
"jsdoc/tag-lines": 8
342+
"@typescript-eslint/no-unsafe-enum-comparison": 1
353343
},
354-
"packages/multichain/src/scope/authorization.ts": {
355-
"jsdoc/tag-lines": 2
344+
"packages/multichain/src/scope/authorization.test.ts": {
345+
"@typescript-eslint/no-unused-vars": 2
356346
},
357347
"packages/multichain/src/scope/errors.ts": {
358348
"jsdoc/tag-lines": 5
359349
},
360350
"packages/multichain/src/scope/filter.test.ts": {
361-
"jest/no-conditional-in-test": 9,
362-
"prettier/prettier": 1
351+
"jest/no-conditional-in-test": 9
363352
},
364353
"packages/multichain/src/scope/filter.ts": {
365354
"@typescript-eslint/no-unused-vars": 1,
366-
"jsdoc/tag-lines": 3
355+
"jsdoc/require-returns": 1
367356
},
368357
"packages/multichain/src/scope/supported.ts": {
369-
"@typescript-eslint/no-unsafe-enum-comparison": 4,
370-
"jsdoc/tag-lines": 4
358+
"@typescript-eslint/no-unsafe-enum-comparison": 6
371359
},
372360
"packages/multichain/src/scope/validation.ts": {
373361
"jsdoc/tag-lines": 2

packages/multichain/src/adapters/caip-permission-adapter-session-scopes.test.ts

Lines changed: 109 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1+
import {
2+
getInternalScopesObject,
3+
getSessionScopes,
4+
} from './caip-permission-adapter-session-scopes';
15
import {
26
KnownNotifications,
37
KnownRpcMethods,
48
KnownWalletNamespaceRpcMethods,
59
KnownWalletRpcMethods,
610
} from '../scope/constants';
7-
import {
8-
getInternalScopesObject,
9-
getSessionScopes,
10-
} from './caip-permission-adapter-session-scopes';
1111

1212
describe('CAIP-25 session scopes adapters', () => {
1313
describe('getInternalScopesObject', () => {
@@ -37,15 +37,22 @@ describe('CAIP-25 session scopes adapters', () => {
3737
});
3838

3939
describe('getSessionScopes', () => {
40+
const getNonEvmSupportedMethods = jest.fn();
41+
4042
it('returns a NormalizedScopesObject for the wallet scope', () => {
41-
const result = getSessionScopes({
42-
requiredScopes: {},
43-
optionalScopes: {
44-
wallet: {
45-
accounts: [],
43+
const result = getSessionScopes(
44+
{
45+
requiredScopes: {},
46+
optionalScopes: {
47+
wallet: {
48+
accounts: [],
49+
},
4650
},
4751
},
48-
});
52+
{
53+
getNonEvmSupportedMethods,
54+
},
55+
);
4956

5057
expect(result).toStrictEqual({
5158
wallet: {
@@ -57,14 +64,19 @@ describe('CAIP-25 session scopes adapters', () => {
5764
});
5865

5966
it('returns a NormalizedScopesObject for the wallet:eip155 scope', () => {
60-
const result = getSessionScopes({
61-
requiredScopes: {},
62-
optionalScopes: {
63-
'wallet:eip155': {
64-
accounts: ['wallet:eip155:0xdeadbeef'],
67+
const result = getSessionScopes(
68+
{
69+
requiredScopes: {},
70+
optionalScopes: {
71+
'wallet:eip155': {
72+
accounts: ['wallet:eip155:0xdeadbeef'],
73+
},
6574
},
6675
},
67-
});
76+
{
77+
getNonEvmSupportedMethods,
78+
},
79+
);
6880

6981
expect(result).toStrictEqual({
7082
'wallet:eip155': {
@@ -75,53 +87,112 @@ describe('CAIP-25 session scopes adapters', () => {
7587
});
7688
});
7789

78-
it('returns a NormalizedScopesObject with empty methods and notifications for scope with wallet namespace and unknown reference', () => {
79-
const result = getSessionScopes({
80-
requiredScopes: {},
81-
optionalScopes: {
82-
'wallet:foobar': {
83-
accounts: ['wallet:foobar:0xdeadbeef'],
90+
it('gets methods from getNonEvmSupportedMethods for scope with wallet namespace and non-evm reference', () => {
91+
getNonEvmSupportedMethods.mockReturnValue(['nonEvmMethod']);
92+
93+
const result = getSessionScopes(
94+
{
95+
requiredScopes: {},
96+
optionalScopes: {
97+
'wallet:foobar': {
98+
accounts: ['wallet:foobar:0xdeadbeef'],
99+
},
84100
},
85101
},
86-
});
102+
{
103+
getNonEvmSupportedMethods,
104+
},
105+
);
106+
107+
expect(getNonEvmSupportedMethods).toHaveBeenCalledWith('wallet:foobar');
108+
});
109+
110+
it('returns a NormalizedScopesObject with methods from getNonEvmSupportedMethods and empty notifications for scope with wallet namespace and non-evm reference', () => {
111+
getNonEvmSupportedMethods.mockReturnValue(['nonEvmMethod']);
112+
113+
const result = getSessionScopes(
114+
{
115+
requiredScopes: {},
116+
optionalScopes: {
117+
'wallet:foobar': {
118+
accounts: ['wallet:foobar:0xdeadbeef'],
119+
},
120+
},
121+
},
122+
{
123+
getNonEvmSupportedMethods,
124+
},
125+
);
87126

88127
expect(result).toStrictEqual({
89128
'wallet:foobar': {
90-
methods: [],
129+
methods: ['nonEvmMethod'],
91130
notifications: [],
92131
accounts: ['wallet:foobar:0xdeadbeef'],
93132
},
94133
});
95134
});
96135

97-
it('returns a NormalizedScopesObject with empty methods and notifications for scope not wallet namespace and unknown reference', () => {
98-
const result = getSessionScopes({
99-
requiredScopes: {},
100-
optionalScopes: {
101-
'foo:1': {
102-
accounts: ['foo:1:0xdeadbeef'],
136+
it('gets methods from getNonEvmSupportedMethods for non-evm (not `eip155`, `wallet` or `wallet:eip155`) scopes', () => {
137+
getNonEvmSupportedMethods.mockReturnValue(['nonEvmMethod']);
138+
139+
const result = getSessionScopes(
140+
{
141+
requiredScopes: {},
142+
optionalScopes: {
143+
'foo:1': {
144+
accounts: ['foo:1:0xdeadbeef'],
145+
},
103146
},
104147
},
105-
});
148+
{
149+
getNonEvmSupportedMethods,
150+
},
151+
);
152+
153+
expect(getNonEvmSupportedMethods).toHaveBeenCalledWith('foo:1');
154+
});
155+
156+
it('returns a NormalizedScopesObject with methods from getNonEvmSupportedMethods and empty notifications for scope non-evm namespace', () => {
157+
getNonEvmSupportedMethods.mockReturnValue(['nonEvmMethod']);
158+
159+
const result = getSessionScopes(
160+
{
161+
requiredScopes: {},
162+
optionalScopes: {
163+
'foo:1': {
164+
accounts: ['foo:1:0xdeadbeef'],
165+
},
166+
},
167+
},
168+
{
169+
getNonEvmSupportedMethods,
170+
},
171+
);
106172

107173
expect(result).toStrictEqual({
108174
'foo:1': {
109-
methods: [],
175+
methods: ['nonEvmMethod'],
110176
notifications: [],
111177
accounts: ['foo:1:0xdeadbeef'],
112178
},
113179
});
114180
});
115181

116182
it('returns a NormalizedScopesObject for a eip155 namespaced scope', () => {
117-
const result = getSessionScopes({
118-
requiredScopes: {},
119-
optionalScopes: {
120-
'eip155:1': {
121-
accounts: ['eip155:1:0xdeadbeef'],
183+
const result = getSessionScopes(
184+
{
185+
requiredScopes: {},
186+
optionalScopes: {
187+
'eip155:1': {
188+
accounts: ['eip155:1:0xdeadbeef'],
189+
},
122190
},
123191
},
124-
});
192+
{
193+
getNonEvmSupportedMethods,
194+
},
195+
);
125196

126197
expect(result).toStrictEqual({
127198
'eip155:1': {

packages/multichain/src/adapters/caip-permission-adapter-session-scopes.ts

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { KnownCaipNamespace } from '@metamask/utils';
1+
import {
2+
type CaipChainId,
3+
isCaipChainId,
4+
KnownCaipNamespace,
5+
} from '@metamask/utils';
26

37
import type { Caip25CaveatValue } from '../caip25Permission';
48
import {
@@ -10,13 +14,13 @@ import {
1014
import { mergeNormalizedScopes } from '../scope/transform';
1115
import type {
1216
InternalScopesObject,
13-
NonWalletKnownCaipNamespace,
1417
NormalizedScopesObject,
1518
} from '../scope/types';
1619
import { parseScopeString } from '../scope/types';
1720

1821
/**
1922
* Converts an NormalizedScopesObject to a InternalScopesObject.
23+
*
2024
* @param normalizedScopesObject - The NormalizedScopesObject to convert.
2125
* @returns An InternalScopesObject.
2226
*/
@@ -40,11 +44,19 @@ export const getInternalScopesObject = (
4044

4145
/**
4246
* Converts an InternalScopesObject to a NormalizedScopesObject.
47+
*
4348
* @param internalScopesObject - The InternalScopesObject to convert.
49+
* @param hooks - An object containing the following properties:
50+
* @param hooks.getNonEvmSupportedMethods - A function that returns the supported methods for a non EVM scope.
4451
* @returns A NormalizedScopesObject.
4552
*/
4653
const getNormalizedScopesObject = (
4754
internalScopesObject: InternalScopesObject,
55+
{
56+
getNonEvmSupportedMethods,
57+
}: {
58+
getNonEvmSupportedMethods: (scope: CaipChainId) => string[];
59+
},
4860
) => {
4961
const normalizedScopes: NormalizedScopesObject = {};
5062

@@ -55,20 +67,23 @@ const getNormalizedScopesObject = (
5567
let methods: string[] = [];
5668
let notifications: string[] = [];
5769

58-
if (namespace === KnownCaipNamespace.Wallet) {
59-
if (reference) {
60-
methods =
61-
KnownWalletNamespaceRpcMethods[
62-
reference as NonWalletKnownCaipNamespace
63-
] ?? [];
70+
if (
71+
scopeString === KnownCaipNamespace.Wallet ||
72+
namespace === KnownCaipNamespace.Wallet
73+
) {
74+
if (reference === KnownCaipNamespace.Eip155) {
75+
methods = KnownWalletNamespaceRpcMethods[reference];
76+
} else if (isCaipChainId(scopeString)) {
77+
methods = getNonEvmSupportedMethods(scopeString);
6478
} else {
6579
methods = KnownWalletRpcMethods;
6680
}
81+
} else if (namespace === KnownCaipNamespace.Eip155) {
82+
methods = KnownRpcMethods[namespace];
83+
notifications = KnownNotifications[namespace];
6784
} else {
68-
methods =
69-
KnownRpcMethods[namespace as NonWalletKnownCaipNamespace] ?? [];
70-
notifications =
71-
KnownNotifications[namespace as NonWalletKnownCaipNamespace] ?? [];
85+
methods = getNonEvmSupportedMethods(scopeString);
86+
notifications = [];
7287
}
7388

7489
normalizedScopes[scopeString] = {
@@ -85,17 +100,29 @@ const getNormalizedScopesObject = (
85100
/**
86101
* Takes the scopes from an endowment:caip25 permission caveat value,
87102
* hydrates them with supported methods and notifications, and returns a NormalizedScopesObject.
103+
*
88104
* @param caip25CaveatValue - The CAIP-25 CaveatValue to convert.
105+
* @param hooks - An object containing the following properties:
106+
* @param hooks.getNonEvmSupportedMethods - A function that returns the supported methods for a non EVM scope.
89107
* @returns A NormalizedScopesObject.
90108
*/
91109
export const getSessionScopes = (
92110
caip25CaveatValue: Pick<
93111
Caip25CaveatValue,
94112
'requiredScopes' | 'optionalScopes'
95113
>,
114+
{
115+
getNonEvmSupportedMethods,
116+
}: {
117+
getNonEvmSupportedMethods: (scope: CaipChainId) => string[];
118+
},
96119
) => {
97120
return mergeNormalizedScopes(
98-
getNormalizedScopesObject(caip25CaveatValue.requiredScopes),
99-
getNormalizedScopesObject(caip25CaveatValue.optionalScopes),
121+
getNormalizedScopesObject(caip25CaveatValue.requiredScopes, {
122+
getNonEvmSupportedMethods,
123+
}),
124+
getNormalizedScopesObject(caip25CaveatValue.optionalScopes, {
125+
getNonEvmSupportedMethods,
126+
}),
100127
);
101128
};

0 commit comments

Comments
 (0)