Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Multichain API to @metamask/multichain #4813

Draft
wants to merge 118 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
118 commits
Select commit Hold shift + click to select a range
bc56104
Initial package
jiexi Oct 10, 2024
a4b52ad
Fix caip25Permission
jiexi Oct 10, 2024
86ab58a
scopes
jiexi Oct 10, 2024
186d4b3
Fix middlewares
jiexi Oct 10, 2024
6a93d1d
Fix adapters except permission middleware
jiexi Oct 10, 2024
231bcaa
Rename handlers js to ts
jiexi Oct 10, 2024
4e52fc0
permission middleware test js ts rename
jiexi Oct 10, 2024
151eaac
fix: typescript + linting (#4788)
shanejonas Oct 11, 2024
85a723e
Added exports for multichain package (#4789)
shanejonas Oct 11, 2024
a0bb278
Add requires
jiexi Oct 11, 2024
1cd8ef2
add permittedChains adapter to exports
jiexi Oct 11, 2024
c636def
kill scope index barrel
jiexi Oct 11, 2024
a6aa7c1
barrel
jiexi Oct 14, 2024
42134f7
Merge branch 'main' into caip-multichain
jiexi Oct 14, 2024
a13b9c7
remove subjectTypes from CAIP-25 permission
jiexi Oct 14, 2024
81db877
update eth accounts adapter with empty wallet and wallet:eip155 Scope…
jiexi Oct 14, 2024
a3fc263
fix caip25permission spec
jiexi Oct 14, 2024
afe5cb9
upsert empty wallet:eip155 when setting permittedChains
jiexi Oct 14, 2024
9ea78be
lint
jiexi Oct 14, 2024
5650d31
Rename scope.test.ts to types.test.ts
jiexi Oct 14, 2024
df5167e
add networkClientId type to permission-adapter-middleware
jiexi Oct 15, 2024
9446b0b
fix snapshot
jiexi Oct 15, 2024
f65ca24
Merge branch 'main' into caip-multichain
jiexi Oct 15, 2024
941849d
add KnownWalletScopeString enum
jiexi Oct 15, 2024
a250de2
stop upserting wallet scope in setEthAccounts()
jiexi Oct 15, 2024
aa7ba39
lint
jiexi Oct 15, 2024
8415be6
update removeScope mutator to not revoke permission if requiredScope …
jiexi Oct 15, 2024
c7f510e
Fix specificationBuilder jsdoc and typing
jiexi Oct 15, 2024
4afa8f8
Fix caip25permission type
jiexi Oct 15, 2024
597e837
Fix caip25permission type
jiexi Oct 15, 2024
1872188
small dry todo fix
adonesky1 Oct 15, 2024
c5d8005
another small dry
adonesky1 Oct 15, 2024
54f7c49
Added handler wrappers to better integrate with existing middleware (…
shanejonas Oct 15, 2024
ada451d
change subscriptionManager require to import
jiexi Oct 15, 2024
2322e39
lint
jiexi Oct 15, 2024
9b21677
Merge branch 'main' into caip-multichain
jiexi Oct 16, 2024
00a41cb
Jl/caip multichain/update scope object account types (#4803)
jiexi Oct 16, 2024
f30dd8b
update @metamask/rpc-errors version
adonesky1 Oct 17, 2024
71085a0
fix merge
adonesky1 Oct 17, 2024
f2be8f0
Merge branch 'main' into caip-multichain
jiexi Oct 17, 2024
644f4d0
Add isEqualCaseInsensitive to controller-utils
jiexi Oct 17, 2024
1b78bbc
create initial multichain package
jiexi Oct 17, 2024
fc28c89
remove API related logic
jiexi Oct 17, 2024
ba25ce5
Merge branch 'initialize-caip-multichain' into caip-multichain
jiexi Oct 17, 2024
4ceb118
remove api related deps
jiexi Oct 17, 2024
a36ec4d
Revert "remove API related logic"
jiexi Oct 17, 2024
95dfbc9
Revert "remove api related deps"
jiexi Oct 17, 2024
9bcf869
Fix package.json
jiexi Oct 17, 2024
633485c
Merge branch 'initialize-caip-multichain' into caip-multichain
jiexi Oct 17, 2024
609518a
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 17, 2024
afb2e2c
fix bad merge
jiexi Oct 17, 2024
7c97afe
fix bad merge
jiexi Oct 17, 2024
ba210ac
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 17, 2024
109a7bf
Update packages/controller-utils/src/util.test.ts
jiexi Oct 17, 2024
1e67641
Merge branch 'controller-utils-isEqualCaseInsensitive' into initializ…
jiexi Oct 17, 2024
32b213a
Merge branch 'initialize-caip-multichain' into caip-multichain
jiexi Oct 17, 2024
dae4f73
add account support check in validator (#4816)
jiexi Oct 17, 2024
b673f62
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 17, 2024
d7ed999
Merge branch 'main' into initialize-caip-multichain
jiexi Oct 21, 2024
b4b65e2
Merge branch 'initialize-caip-multichain' into caip-multichain
jiexi Oct 21, 2024
4d752b7
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 21, 2024
fad338d
remove caip-permission-adapter-middleware
jiexi Oct 21, 2024
5bf15a2
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 21, 2024
8208931
Revert "remove caip-permission-adapter-middleware"
jiexi Oct 21, 2024
c5980c6
remove caip-permission-adapter-middleware from exports
jiexi Oct 21, 2024
106e986
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 21, 2024
5c6bb8f
Revert "remove caip-permission-adapter-middleware from exports"
jiexi Oct 21, 2024
a7a8e6e
remove types/@metamask/eth-json-rpc-filters.d.ts
jiexi Oct 21, 2024
0962fb2
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 21, 2024
54829e0
Revert "remove types/@metamask/eth-json-rpc-filters.d.ts"
jiexi Oct 21, 2024
6b367f0
Merge branch 'main' into caip-multichain
adonesky1 Oct 21, 2024
42297f9
add back readme content
adonesky1 Oct 21, 2024
dc31704
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 21, 2024
56176d1
yarn lock
jiexi Oct 21, 2024
26acfcd
Merge branch 'main' into initialize-caip-multichain
jiexi Oct 21, 2024
2e3bfb4
Merge branch 'initialize-caip-multichain' into caip-multichain
jiexi Oct 21, 2024
89d6101
Merge remote-tracking branch 'origin/caip-multichain-api' into caip-m…
jiexi Oct 21, 2024
b7ea5d5
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 21, 2024
7203358
bump network-controller dep
jiexi Oct 22, 2024
046133a
add Scoped Properties to Caip25Authorization type
jiexi Oct 22, 2024
eea9f46
Merge branch 'main' into caip-multichain
jiexi Oct 22, 2024
257618a
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 22, 2024
67b222e
Fix ScopedProperties type
jiexi Oct 22, 2024
ead2f43
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 22, 2024
009fb4a
Loosen get adapter param types (#4835)
jiexi Oct 22, 2024
e34032b
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 22, 2024
0631c48
Seperate types and constants
jiexi Oct 23, 2024
4685547
Merge branch 'main' into caip-multichain
jiexi Oct 23, 2024
18d1112
remove jest.resetAllMocks
jiexi Oct 23, 2024
1ba77c7
yarn lock
jiexi Oct 23, 2024
bb50a19
yarn
jiexi Oct 23, 2024
7d32a0b
100% coverage
jiexi Oct 23, 2024
f095370
Remove accountsChanged and chainChanged from KnownNotifications (sinc…
jiexi Oct 23, 2024
62929a4
Merge branch 'main' into caip-multichain
jiexi Oct 23, 2024
491552f
Update packages/multichain/src/scope/transform.test.ts
jiexi Oct 24, 2024
bd0f50f
Merge branch 'main' into caip-multichain
jiexi Oct 24, 2024
41dcc83
Merge branch 'caip-multichain' into caip-multichain-api
jiexi Oct 24, 2024
d80c9ce
Merge branch 'main' into caip-multichain-api
jiexi Nov 21, 2024
6eb3e87
Merge branch 'main' into caip-multichain-api
jiexi Nov 21, 2024
8374f37
Merge branch 'main' into caip-multichain-api
jiexi Nov 21, 2024
d6dfc71
Merge branch 'main' into caip-multichain-api
jiexi Nov 21, 2024
0d167a3
CAIP Multichain API with permission refactor changes (#4961)
jiexi Nov 21, 2024
17af395
export caipPermissionAdapterMiddleware
jiexi Nov 21, 2024
62975b6
Caip multichain api normalized to internal (#4964)
jiexi Nov 21, 2024
19a5983
Filter out eip1193 only methods from Eip155Methods
jiexi Nov 22, 2024
439ef9b
Merge branch 'main' into caip-multichain-api
jiexi Nov 22, 2024
7d61af6
export Eip1193OnlyMethods
jiexi Nov 22, 2024
b5d0cd7
Merge branch 'main' into caip-multichain-api
jiexi Nov 22, 2024
97b4a09
Revert "export Eip1193OnlyMethods"
jiexi Nov 22, 2024
6732cd9
use Eip1193OnlyMethods in adapter middleware
jiexi Nov 22, 2024
e03033c
expand Eip1193OnlyMethods to include meta methods
jiexi Nov 22, 2024
e1793ff
cleanup + add tests for MultichainSubscriptionManager
adonesky1 Nov 22, 2024
4c9be0e
adding tests for multichainMethodCallValidator
adonesky1 Nov 22, 2024
a25c05d
cleanup
adonesky1 Nov 22, 2024
b35bdc3
lint
adonesky1 Nov 22, 2024
2ad05a3
question
adonesky1 Nov 22, 2024
79acd26
small cleanup
adonesky1 Nov 22, 2024
32aa807
more cleanup
adonesky1 Nov 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/multichain/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,18 @@
"@metamask/controller-utils": "^11.4.3",
"@metamask/eth-json-rpc-filters": "^7.0.0",
"@metamask/rpc-errors": "^7.0.1",
"@metamask/safe-event-emitter": "^3.0.0",
"@metamask/utils": "^10.0.0",
"@open-rpc/schema-utils-js": "^2.0.5",
"jsonschema": "^1.2.4",
"lodash": "^4.17.21"
},
"devDependencies": {
"@metamask/auto-changelog": "^3.4.4",
"@metamask/json-rpc-engine": "^10.0.1",
"@metamask/network-controller": "^22.0.2",
"@metamask/permission-controller": "^11.0.3",
"@open-rpc/meta-schema": "^1.14.6",
"@types/jest": "^27.4.1",
"deepmerge": "^4.2.2",
"jest": "^27.5.1",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { providerErrors } from '@metamask/rpc-errors';
import type { JsonRpcRequest } from '@metamask/utils';

import {
Caip25CaveatType,
Caip25EndowmentPermissionName,
} from '../caip25Permission';
import { caipPermissionAdapterMiddleware } from './caip-permission-adapter-middleware';

const baseRequest = {
id: 1,
jsonrpc: '2.0' as const,
origin: 'http://test.com',
networkClientId: 'mainnet',
method: 'eth_call',
params: {
foo: 'bar',
},
};

const createMockedHandler = () => {
const next = jest.fn();
const end = jest.fn();
const getCaveat = jest.fn().mockReturnValue({
value: {
requiredScopes: {
'eip155:1': {
methods: ['eth_call'],
notifications: [],
accounts: [],
},
'eip155:5': {
methods: ['eth_chainId'],
notifications: [],
accounts: [],
},
},
optionalScopes: {
'eip155:1': {
methods: ['net_version'],
notifications: [],
accounts: [],
},
wallet: {
methods: ['wallet_watchAsset'],
notifications: [],
accounts: [],
},
unhandled: {
methods: ['foobar'],
notifications: [],
accounts: [],
},
},
isMultichainOrigin: true,
},
});
const getNetworkConfigurationByNetworkClientId = jest
.fn()
.mockImplementation((networkClientId: string) => {
const chainId =
{
mainnet: '0x1',
goerli: '0x5',
}[networkClientId] || '0x999';
return {
chainId,
};
});
const handler = (
request: JsonRpcRequest & {
networkClientId: string;
origin: string;
},
) =>
caipPermissionAdapterMiddleware(request, {}, next, end, {
getCaveat,
getNetworkConfigurationByNetworkClientId,
});

return {
next,
end,
getCaveat,
getNetworkConfigurationByNetworkClientId,
handler,
};
};

describe('CaipPermissionAdapterMiddleware', () => {
it('gets the authorized scopes from the CAIP-25 endowment permission', async () => {
const { handler, getCaveat } = createMockedHandler();
await handler(baseRequest);
expect(getCaveat).toHaveBeenCalledWith(
'http://test.com',
Caip25EndowmentPermissionName,
Caip25CaveatType,
);
});

it('allows the request when there is no CAIP-25 endowment permission', async () => {
const { handler, getCaveat, next } = createMockedHandler();
getCaveat.mockImplementation(() => {
throw new Error('permission not found');
});
await handler(baseRequest);
expect(next).toHaveBeenCalled();
});

it('allows the request when the CAIP-25 endowment permission was not granted from the multichain API', async () => {
const { handler, getCaveat, next } = createMockedHandler();
getCaveat.mockReturnValue({
value: {
isMultichainOrigin: false,
},
});
await handler(baseRequest);
expect(next).toHaveBeenCalled();
});

it('gets the chainId for the request networkClientId', async () => {
const { handler, getNetworkConfigurationByNetworkClientId } =
createMockedHandler();
await handler(baseRequest);
expect(getNetworkConfigurationByNetworkClientId).toHaveBeenCalledWith(
'mainnet',
);
});

describe('when the CAIP-25 endowment permission was granted over the multichain API', () => {
it('throws an error if the requested method is not authorized for the scope specified in the request', async () => {
const { handler, end } = createMockedHandler();

await handler({
...baseRequest,
method: 'unauthorized_method',
});
expect(end).toHaveBeenCalledWith(providerErrors.unauthorized());
});

it('allows the request if the requested scope method is authorized in the current scope', async () => {
const { handler, next } = createMockedHandler();

await handler(baseRequest);
expect(next).toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type {
NetworkConfiguration,
NetworkClientId,
} from '@metamask/network-controller';
import type { Caveat } from '@metamask/permission-controller';
import { providerErrors } from '@metamask/rpc-errors';
import type { JsonRpcRequest } from '@metamask/utils';

import type { Caip25CaveatValue } from '../caip25Permission';
import {
Caip25CaveatType,
Caip25EndowmentPermissionName,
} from '../caip25Permission';
import { Eip1193OnlyMethods, KnownWalletScopeString } from '../scope/constants';
import type { InternalScopeString } from '../scope/types';
import { getSessionScopes } from './caip-permission-adapter-session-scopes';

/**
* Middleware to handle CAIP-25 permission requests.
*
* @param request - The request object.
* @param _response - The response object.
* @param next - The next middleware function.
* @param end - The end function.
* @param hooks - The hooks object.
* @param hooks.getCaveat - Function to retrieve a caveat.
* @param hooks.getNetworkConfigurationByNetworkClientId - Function to retrieve a network configuration.
*/
export async function caipPermissionAdapterMiddleware(
request: JsonRpcRequest & {
networkClientId: NetworkClientId;
origin: string;
},
_response: unknown,
next: () => Promise<void>,
end: (error?: Error) => void,
hooks: {
getCaveat: (
...args: unknown[]
) => Caveat<typeof Caip25CaveatType, Caip25CaveatValue>;
getNetworkConfigurationByNetworkClientId: (
networkClientId: NetworkClientId,
) => NetworkConfiguration;
},
) {
const { networkClientId, method } = request;

let caveat;
try {
caveat = hooks.getCaveat(
request.origin,
Caip25EndowmentPermissionName,
Caip25CaveatType,
);
} catch (err) {
// noop
}
if (!caveat?.value?.isMultichainOrigin) {
return next();
}

const { chainId } =
hooks.getNetworkConfigurationByNetworkClientId(networkClientId);

const scope: InternalScopeString = `eip155:${parseInt(chainId, 16)}`;

const sesionScopes = getSessionScopes(caveat.value);

if (
!sesionScopes[scope]?.methods?.includes(method) &&
!sesionScopes[KnownWalletScopeString.Eip155]?.methods?.includes(method) &&
!sesionScopes.wallet?.methods?.includes(method) &&
!Eip1193OnlyMethods.includes(method)
) {
return end(providerErrors.unauthorized());
}

return next();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import {
KnownNotifications,
KnownRpcMethods,
KnownWalletNamespaceRpcMethods,
KnownWalletRpcMethods,
} from '../scope/constants';
import {
getInternalScopesObject,
getSessionScopes,
} from './caip-permission-adapter-session-scopes';

describe('CAIP-25 session scopes adapters', () => {
describe('getInternalScopesObject', () => {
it('returns an InternalScopesObject with only the accounts from each NormalizedScopeObject', () => {
const result = getInternalScopesObject({
'wallet:eip155': {
methods: ['foo', 'bar'],
notifications: ['baz'],
accounts: ['wallet:eip155:0xdead'],
},
'eip155:1': {
methods: ['eth_call'],
notifications: ['eth_subscription'],
accounts: ['eip155:1:0xdead', 'eip155:1:0xbeef'],
},
});

expect(result).toStrictEqual({
'wallet:eip155': {
accounts: ['wallet:eip155:0xdead'],
},
'eip155:1': {
accounts: ['eip155:1:0xdead', 'eip155:1:0xbeef'],
},
});
});
});

describe('getSessionScopes', () => {
it('returns a NormalizedScopesObject for the wallet scope', () => {
const result = getSessionScopes({
requiredScopes: {},
optionalScopes: {
wallet: {
accounts: [],
},
},
});

expect(result).toStrictEqual({
wallet: {
methods: KnownWalletRpcMethods,
notifications: [],
accounts: [],
},
});
});

it('returns a NormalizedScopesObject for the wallet:eip155 scope', () => {
const result = getSessionScopes({
requiredScopes: {},
optionalScopes: {
'wallet:eip155': {
accounts: ['wallet:eip155:0xdeadbeef'],
},
},
});

expect(result).toStrictEqual({
'wallet:eip155': {
methods: KnownWalletNamespaceRpcMethods.eip155,
notifications: [],
accounts: ['wallet:eip155:0xdeadbeef'],
},
});
});

it('returns a NormalizedScopesObject with empty methods and notifications for scope with wallet namespace and unknown reference', () => {
const result = getSessionScopes({
requiredScopes: {},
optionalScopes: {
'wallet:foobar': {
accounts: ['wallet:foobar:0xdeadbeef'],
},
},
});

expect(result).toStrictEqual({
'wallet:foobar': {
methods: [],
notifications: [],
accounts: ['wallet:foobar:0xdeadbeef'],
},
});
});

it('returns a NormalizedScopesObject for a eip155 namespaced scope', () => {
const result = getSessionScopes({
requiredScopes: {},
optionalScopes: {
'eip155:1': {
accounts: ['eip155:1:0xdeadbeef'],
},
},
});

expect(result).toStrictEqual({
'eip155:1': {
methods: KnownRpcMethods.eip155,
notifications: KnownNotifications.eip155,
accounts: ['eip155:1:0xdeadbeef'],
},
});
});
});
});
Loading
Loading