From bfcde75cb0c1533f411cfa6d26ff8007adab56a5 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Fri, 26 Jun 2026 15:35:57 -0700 Subject: [PATCH 1/3] add getSessionProperties --- .../chain-agnostic-permission/CHANGELOG.md | 4 + .../src/index.test.ts | 1 + .../chain-agnostic-permission/src/index.ts | 1 + ...permission-operator-session-scopes.test.ts | 117 ++++++++++++++++++ ...caip-permission-operator-session-scopes.ts | 40 +++++- .../multichain-api-middleware/CHANGELOG.md | 7 ++ .../src/handlers/index.test.ts | 1 + .../src/handlers/types.ts | 4 + .../src/handlers/wallet-createSession.test.ts | 8 ++ .../src/handlers/wallet-createSession.ts | 15 ++- .../src/handlers/wallet-getSession.test.ts | 53 +++++++- .../src/handlers/wallet-getSession.ts | 18 ++- 12 files changed, 261 insertions(+), 8 deletions(-) diff --git a/packages/chain-agnostic-permission/CHANGELOG.md b/packages/chain-agnostic-permission/CHANGELOG.md index 76f1cc114a..500c0caddb 100644 --- a/packages/chain-agnostic-permission/CHANGELOG.md +++ b/packages/chain-agnostic-permission/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `getSessionProperties` function that hydrates the persisted session properties of a CAIP-25 caveat value with a `capabilities` record built by invoking the provided `getCapabilities` hook for each permitted EVM account ([#0000](https://github.com/MetaMask/core/pull/0000)) + ## [1.6.2] ### Changed diff --git a/packages/chain-agnostic-permission/src/index.test.ts b/packages/chain-agnostic-permission/src/index.test.ts index fcc3f16a4a..33c73ae376 100644 --- a/packages/chain-agnostic-permission/src/index.test.ts +++ b/packages/chain-agnostic-permission/src/index.test.ts @@ -22,6 +22,7 @@ describe('@metamask/chain-agnostic-permission', () => { "getAllScopesFromScopesObjects", "getInternalScopesObject", "getSessionScopes", + "getSessionProperties", "getPermittedAccountsForScopes", "validateAndNormalizeScopes", "bucketScopes", diff --git a/packages/chain-agnostic-permission/src/index.ts b/packages/chain-agnostic-permission/src/index.ts index 1a9d7f2714..734114f6c4 100644 --- a/packages/chain-agnostic-permission/src/index.ts +++ b/packages/chain-agnostic-permission/src/index.ts @@ -21,6 +21,7 @@ export { export { getInternalScopesObject, getSessionScopes, + getSessionProperties, getPermittedAccountsForScopes, } from './operators/caip-permission-operator-session-scopes'; export type { Caip25Authorization } from './scope/authorization'; diff --git a/packages/chain-agnostic-permission/src/operators/caip-permission-operator-session-scopes.test.ts b/packages/chain-agnostic-permission/src/operators/caip-permission-operator-session-scopes.test.ts index 8dc0d75648..cf67efacd1 100644 --- a/packages/chain-agnostic-permission/src/operators/caip-permission-operator-session-scopes.test.ts +++ b/packages/chain-agnostic-permission/src/operators/caip-permission-operator-session-scopes.test.ts @@ -9,6 +9,7 @@ import { import { getInternalScopesObject, getPermittedAccountsForScopes, + getSessionProperties, getSessionScopes, } from './caip-permission-operator-session-scopes'; @@ -52,6 +53,7 @@ describe('CAIP-25 session scopes adapters', () => { accounts: [], }, }, + sessionProperties: {}, }, { getNonEvmSupportedMethods, @@ -76,6 +78,7 @@ describe('CAIP-25 session scopes adapters', () => { accounts: ['wallet:eip155:0xdeadbeef'], }, }, + sessionProperties: {}, }, { getNonEvmSupportedMethods, @@ -102,6 +105,7 @@ describe('CAIP-25 session scopes adapters', () => { accounts: ['wallet:foobar:0xdeadbeef'], }, }, + sessionProperties: {}, }, { getNonEvmSupportedMethods, @@ -122,6 +126,7 @@ describe('CAIP-25 session scopes adapters', () => { accounts: ['wallet:foobar:0xdeadbeef'], }, }, + sessionProperties: {}, }, { getNonEvmSupportedMethods, @@ -148,6 +153,7 @@ describe('CAIP-25 session scopes adapters', () => { accounts: ['foo:1:0xdeadbeef'], }, }, + sessionProperties: {}, }, { getNonEvmSupportedMethods, @@ -168,6 +174,7 @@ describe('CAIP-25 session scopes adapters', () => { accounts: ['foo:1:0xdeadbeef'], }, }, + sessionProperties: {}, }, { getNonEvmSupportedMethods, @@ -192,6 +199,7 @@ describe('CAIP-25 session scopes adapters', () => { accounts: ['eip155:1:0xdeadbeef'], }, }, + sessionProperties: {}, }, { getNonEvmSupportedMethods, @@ -227,6 +235,7 @@ describe('CAIP-25 session scopes adapters', () => { }, }, optionalScopes: {}, + sessionProperties: {}, }, { getNonEvmSupportedMethods, @@ -257,6 +266,7 @@ describe('CAIP-25 session scopes adapters', () => { }, }, optionalScopes: {}, + sessionProperties: {}, }, { getNonEvmSupportedMethods, @@ -307,6 +317,7 @@ describe('CAIP-25 session scopes adapters', () => { accounts: unsortedAccounts2, }, }, + sessionProperties: {}, }, { getNonEvmSupportedMethods, @@ -338,6 +349,112 @@ describe('CAIP-25 session scopes adapters', () => { }); }); + describe('getSessionProperties', () => { + it('returns the persisted session properties merged with an empty capabilities record when there are no permitted accounts', () => { + const getCapabilities = jest.fn(); + + const result = getSessionProperties( + { + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { 'eip1193-compatible': true }, + }, + { + getCapabilities, + }, + ); + + expect(getCapabilities).not.toHaveBeenCalled(); + expect(result).toStrictEqual({ + 'eip1193-compatible': true, + capabilities: {}, + }); + }); + + it('calls getCapabilities with each unique permitted EVM address', () => { + const getCapabilities = jest.fn().mockReturnValue({ atomic: 'supported' }); + + getSessionProperties( + { + requiredScopes: { + 'eip155:1': { + accounts: ['eip155:1:0xdead'], + }, + }, + optionalScopes: { + 'eip155:137': { + accounts: ['eip155:137:0xdead', 'eip155:137:0xbeef'], + }, + }, + sessionProperties: {}, + }, + { + getCapabilities, + }, + ); + + expect(getCapabilities).toHaveBeenCalledTimes(2); + expect(getCapabilities).toHaveBeenCalledWith({ address: '0xdead' }); + expect(getCapabilities).toHaveBeenCalledWith({ address: '0xbeef' }); + }); + + it('returns the session properties with a capabilities record keyed by address', () => { + const getCapabilities = jest.fn((params: { address: string }) => ({ + address: params.address, + atomic: { status: 'supported' }, + })); + + const result = getSessionProperties( + { + requiredScopes: { + 'eip155:1': { + accounts: ['eip155:1:0xdead'], + }, + }, + optionalScopes: {}, + sessionProperties: { expiry: '2025-01-01T00:00:00.000Z' }, + }, + { + getCapabilities, + }, + ); + + expect(result).toStrictEqual({ + expiry: '2025-01-01T00:00:00.000Z', + capabilities: { + '0xdead': { + address: '0xdead', + atomic: { status: 'supported' }, + }, + }, + }); + }); + + it('does not call getCapabilities for non-EVM accounts', () => { + const getCapabilities = jest.fn(); + + const result = getSessionProperties( + { + requiredScopes: {}, + optionalScopes: { + 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp': { + accounts: [ + 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp:DdpL8XNK9hSn8m6ycGAQvBwHJVgVz9eL5tWGtZ8L', + ], + }, + }, + sessionProperties: {}, + }, + { + getCapabilities, + }, + ); + + expect(getCapabilities).not.toHaveBeenCalled(); + expect(result).toStrictEqual({ capabilities: {} }); + }); + }); + describe('getPermittedAccountsForScopes', () => { it('returns an array of permitted accounts for a given scope', () => { const result = getPermittedAccountsForScopes( diff --git a/packages/chain-agnostic-permission/src/operators/caip-permission-operator-session-scopes.ts b/packages/chain-agnostic-permission/src/operators/caip-permission-operator-session-scopes.ts index fee3af3fbf..aeb8fb06bd 100644 --- a/packages/chain-agnostic-permission/src/operators/caip-permission-operator-session-scopes.ts +++ b/packages/chain-agnostic-permission/src/operators/caip-permission-operator-session-scopes.ts @@ -1,6 +1,7 @@ import { isCaipChainId, KnownCaipNamespace } from '@metamask/utils'; import type { CaipAccountId, CaipChainId } from '@metamask/utils'; +import { getEthAccounts } from './caip-permission-operator-accounts'; import type { Caip25CaveatValue } from '../caip25Permission'; import { KnownNotifications, @@ -107,7 +108,7 @@ const getNormalizedScopesObject = ( export const getSessionScopes = ( caip25CaveatValue: Pick< Caip25CaveatValue, - 'requiredScopes' | 'optionalScopes' + 'requiredScopes' | 'optionalScopes' | 'sessionProperties' >, { getNonEvmSupportedMethods, @@ -143,6 +144,43 @@ export const getSessionScopes = ( return mergedScopes; }; +/** + * Builds the session properties for an endowment:caip25 permission caveat value, + * hydrating the persisted session properties with the capabilities for each + * permitted EVM account. + * + * @param caip25CaveatValue - The CAIP-25 CaveatValue to get the session properties from. + * @param hooks - An object containing the following properties: + * @param hooks.getCapabilities - A function that returns the capabilities for a given address. + * @returns The session properties merged with a `capabilities` record keyed by account address. + */ +export const getSessionProperties = ( + caip25CaveatValue: Pick< + Caip25CaveatValue, + 'requiredScopes' | 'optionalScopes' | 'sessionProperties' + >, + { + getCapabilities, + }: { + getCapabilities: (params: { address: string }) => unknown; + }, +): Record => { + const addresses = getEthAccounts(caip25CaveatValue); + + const capabilities = addresses.reduce>( + (acc, address) => { + acc[address] = getCapabilities({ address }); + return acc; + }, + {}, + ); + + return { + ...caip25CaveatValue.sessionProperties, + capabilities, + }; +}; + /** * Get the permitted accounts for the given scopes. * diff --git a/packages/multichain-api-middleware/CHANGELOG.md b/packages/multichain-api-middleware/CHANGELOG.md index 8e0ae352be..73da1399ea 100644 --- a/packages/multichain-api-middleware/CHANGELOG.md +++ b/packages/multichain-api-middleware/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- **BREAKING:** The `wallet_getSession` and `wallet_createSession` handlers now require a `getCapabilities` hook (`(params: { address: string }) => unknown`) ([#0000](https://github.com/MetaMask/core/pull/0000)) + - `WalletGetSessionHooks` and `WalletCreateSessionHooks` now include this hook, which must be provided when wiring up the handlers. +- The `wallet_getSession` and `wallet_createSession` handlers now derive the returned `sessionProperties` via `getSessionProperties`, hydrating the persisted session properties with a `capabilities` record built from the `getCapabilities` hook for each permitted EVM account ([#0000](https://github.com/MetaMask/core/pull/0000)) + - `wallet_getSession` now always includes a `sessionProperties` field in its result (an empty object when there is no active session). + ## [3.1.4] ### Changed diff --git a/packages/multichain-api-middleware/src/handlers/index.test.ts b/packages/multichain-api-middleware/src/handlers/index.test.ts index e51ed60698..f3c0f2593b 100644 --- a/packages/multichain-api-middleware/src/handlers/index.test.ts +++ b/packages/multichain-api-middleware/src/handlers/index.test.ts @@ -37,6 +37,7 @@ const makeMockHooks = () => isNonEvmScopeSupported: () => false, getNonEvmAccountAddresses: () => [], sortAccountIdsByLastSelected: () => [], + getCapabilities: () => ({}), getCaveatForOrigin: (() => ({}) as unknown) as Hooks['getCaveatForOrigin'], getSelectedNetworkClientId: () => 'mainnet', handleNonEvmRequestForOrigin: () => Promise.resolve(null), diff --git a/packages/multichain-api-middleware/src/handlers/types.ts b/packages/multichain-api-middleware/src/handlers/types.ts index 1e7114a197..d80135088b 100644 --- a/packages/multichain-api-middleware/src/handlers/types.ts +++ b/packages/multichain-api-middleware/src/handlers/types.ts @@ -33,3 +33,7 @@ export type GetNonEvmSupportedMethodsHook = { export type SortAccountIdsByLastSelectedHook = { sortAccountIdsByLastSelected: (accounts: CaipAccountId[]) => CaipAccountId[]; }; + +export type GetCapabilitiesHook = { + getCapabilities: (params: { address: string }) => unknown; +}; diff --git a/packages/multichain-api-middleware/src/handlers/wallet-createSession.test.ts b/packages/multichain-api-middleware/src/handlers/wallet-createSession.test.ts index 5ca1de503f..dc1d7533e1 100644 --- a/packages/multichain-api-middleware/src/handlers/wallet-createSession.test.ts +++ b/packages/multichain-api-middleware/src/handlers/wallet-createSession.test.ts @@ -33,6 +33,7 @@ jest.mock('@metamask/chain-agnostic-permission', () => ({ validateAndNormalizeScopes: jest.fn(), bucketScopes: jest.fn(), getSessionScopes: jest.fn(), + getSessionProperties: jest.fn(), getSupportedScopeObjects: jest.fn(), })); const MockChainAgnosticPermission = jest.mocked(ChainAgnosticPermission); @@ -107,6 +108,7 @@ const createMockedHandler = (trackSessionEvents: boolean = true) => { }>; const getNonEvmAccountAddresses = jest.fn().mockReturnValue([]); const sortAccountIdsByLastSelected = jest.fn((accounts) => accounts); + const getCapabilities = jest.fn(); const handler = ( request: JsonRpcRequest & { origin: string }, ) => @@ -118,6 +120,7 @@ const createMockedHandler = (trackSessionEvents: boolean = true) => { isNonEvmScopeSupported, getNonEvmAccountAddresses, sortAccountIdsByLastSelected, + getCapabilities, trackSessionCreatedEvent, }); @@ -133,6 +136,7 @@ const createMockedHandler = (trackSessionEvents: boolean = true) => { isNonEvmScopeSupported, getNonEvmAccountAddresses, sortAccountIdsByLastSelected, + getCapabilities, handler, }; }; @@ -155,6 +159,7 @@ describe('wallet_createSession', () => { unsupportableScopes: {}, }); MockChainAgnosticPermission.getSessionScopes.mockReturnValue({}); + MockChainAgnosticPermission.getSessionProperties.mockReturnValue({}); MockChainAgnosticPermission.getSupportedScopeObjects.mockImplementation( (scopesObject) => scopesObject, ); @@ -878,6 +883,9 @@ describe('wallet_createSession', () => { accounts: ['eip155:5:0x1', 'eip155:5:0x2'], }, }); + MockChainAgnosticPermission.getSessionProperties.mockReturnValue({ + [KnownSessionProperties.SolanaAccountChangedNotifications]: true, + }); await handler({ ...baseRequest, params: { diff --git a/packages/multichain-api-middleware/src/handlers/wallet-createSession.ts b/packages/multichain-api-middleware/src/handlers/wallet-createSession.ts index 2041367bee..d92629cc34 100644 --- a/packages/multichain-api-middleware/src/handlers/wallet-createSession.ts +++ b/packages/multichain-api-middleware/src/handlers/wallet-createSession.ts @@ -6,6 +6,7 @@ import { validateAndNormalizeScopes, getInternalScopesObject, getSessionScopes, + getSessionProperties, getSupportedScopeObjects, isKnownSessionPropertyValue, getCaipAccountIdsFromScopesObjects, @@ -46,6 +47,7 @@ import type { } from '@metamask/utils'; import type { + GetCapabilitiesHook, GetNonEvmSupportedMethodsHook, SortAccountIdsByLastSelectedHook, } from './types'; @@ -53,7 +55,8 @@ import type { const SOLANA_CAIP_CHAIN_ID = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'; export type WalletCreateSessionHooks = GetNonEvmSupportedMethodsHook & - SortAccountIdsByLastSelectedHook & { + SortAccountIdsByLastSelectedHook & + GetCapabilitiesHook & { listAccounts: AccountsController['listAccounts']; findNetworkClientIdByChainId: NetworkController['findNetworkClientIdByChainId']; requestPermissionsForOrigin: ( @@ -97,6 +100,7 @@ type Result = { * @param hooks.isNonEvmScopeSupported - The hook that returns true if a non EVM scope is supported. * @param hooks.getNonEvmAccountAddresses - The hook that returns a list of CaipAccountIds that are supported for a CaipChainId. * @param hooks.sortAccountIdsByLastSelected - A function that accepts an array of CaipAccountId and returns an array of CaipAccountId sorted by last selected. + * @param hooks.getCapabilities - A function that returns the capabilities for a given address. * @param hooks.trackSessionCreatedEvent - An optional hook for platform specific logic to run. * @returns A promise with wallet_createSession handler */ @@ -281,8 +285,12 @@ async function handleWalletCreateSession( sortAccountIdsByLastSelected: hooks.sortAccountIdsByLastSelected, }); - const { sessionProperties: approvedSessionProperties = {} } = - approvedCaip25CaveatValue; + const approvedSessionProperties = getSessionProperties( + approvedCaip25CaveatValue, + { + getCapabilities: hooks.getCapabilities, + }, + ) as Record; hooks.trackSessionCreatedEvent?.(approvedCaip25CaveatValue); @@ -314,6 +322,7 @@ export const walletCreateSessionHandler = { isNonEvmScopeSupported: true, getNonEvmAccountAddresses: true, sortAccountIdsByLastSelected: true, + getCapabilities: true, trackSessionCreatedEvent: true, }, } satisfies WalletCreateSessionHandler; diff --git a/packages/multichain-api-middleware/src/handlers/wallet-getSession.test.ts b/packages/multichain-api-middleware/src/handlers/wallet-getSession.test.ts index d3805893d5..bab64509ac 100644 --- a/packages/multichain-api-middleware/src/handlers/wallet-getSession.test.ts +++ b/packages/multichain-api-middleware/src/handlers/wallet-getSession.test.ts @@ -24,6 +24,7 @@ const createMockedHandler = () => { const end = jest.fn(); const getNonEvmSupportedMethods = jest.fn(); const sortAccountIdsByLastSelected = jest.fn((accounts) => accounts); + const getCapabilities = jest.fn(); const getCaveatForOrigin = jest.fn().mockReturnValue({ value: { requiredScopes: { @@ -47,6 +48,7 @@ const createMockedHandler = () => { const response = { result: { sessionScopes: {}, + sessionProperties: {}, }, id: 1, jsonrpc: '2.0' as const, @@ -56,6 +58,7 @@ const createMockedHandler = () => { getCaveatForOrigin, getNonEvmSupportedMethods, sortAccountIdsByLastSelected, + getCapabilities, }); return { @@ -65,6 +68,7 @@ const createMockedHandler = () => { getCaveatForOrigin, getNonEvmSupportedMethods, sortAccountIdsByLastSelected, + getCapabilities, handler, }; }; @@ -74,6 +78,9 @@ describe('wallet_getSession', () => { jest .spyOn(chainAgnosticPermissionModule, 'getSessionScopes') .mockReturnValue({}); + jest + .spyOn(chainAgnosticPermissionModule, 'getSessionProperties') + .mockReturnValue({}); }); it('gets the authorized scopes from the CAIP-25 endowment permission', async () => { @@ -95,6 +102,7 @@ describe('wallet_getSession', () => { await handler(baseRequest); expect(response.result).toStrictEqual({ sessionScopes: {}, + sessionProperties: {}, }); }); @@ -129,7 +137,38 @@ describe('wallet_getSession', () => { ); }); - it('returns the session scopes', async () => { + it('gets the session properties from the CAIP-25 caveat value', async () => { + const { handler, getCapabilities } = createMockedHandler(); + + await handler(baseRequest); + expect( + chainAgnosticPermissionModule.getSessionProperties, + ).toHaveBeenCalledWith( + { + requiredScopes: { + 'eip155:1': { + accounts: [], + }, + 'eip155:5': { + accounts: [], + }, + }, + optionalScopes: { + 'eip155:1': { + accounts: [], + }, + wallet: { + accounts: [], + }, + }, + }, + { + getCapabilities, + }, + ); + }); + + it('returns the session scopes and session properties', async () => { const { handler, response } = createMockedHandler(); jest @@ -151,6 +190,13 @@ describe('wallet_getSession', () => { accounts: [], }, }); + jest + .spyOn(chainAgnosticPermissionModule, 'getSessionProperties') + .mockReturnValue({ + capabilities: { + '0x1': { atomic: { status: 'supported' } }, + }, + }); await handler(baseRequest); expect(response.result).toStrictEqual({ @@ -171,6 +217,11 @@ describe('wallet_getSession', () => { accounts: [], }, }, + sessionProperties: { + capabilities: { + '0x1': { atomic: { status: 'supported' } }, + }, + }, }); }); }); diff --git a/packages/multichain-api-middleware/src/handlers/wallet-getSession.ts b/packages/multichain-api-middleware/src/handlers/wallet-getSession.ts index f093e83293..44554a17ad 100644 --- a/packages/multichain-api-middleware/src/handlers/wallet-getSession.ts +++ b/packages/multichain-api-middleware/src/handlers/wallet-getSession.ts @@ -2,6 +2,7 @@ import type { NormalizedScopesObject } from '@metamask/chain-agnostic-permission import { Caip25CaveatType, Caip25EndowmentPermissionName, + getSessionProperties, getSessionScopes, } from '@metamask/chain-agnostic-permission'; import type { @@ -10,6 +11,7 @@ import type { MethodHandler, } from '@metamask/json-rpc-engine'; import type { + Json, JsonRpcParams, JsonRpcRequest, PendingJsonRpcResponse, @@ -17,16 +19,21 @@ import type { import type { Caip25Caveat, + GetCapabilitiesHook, GetCaveatForOriginHook, GetNonEvmSupportedMethodsHook, SortAccountIdsByLastSelectedHook, } from './types'; -type WalletGetSessionResult = { sessionScopes: NormalizedScopesObject }; +type WalletGetSessionResult = { + sessionScopes: NormalizedScopesObject; + sessionProperties: Record; +}; export type WalletGetSessionHooks = GetCaveatForOriginHook & GetNonEvmSupportedMethodsHook & - SortAccountIdsByLastSelectedHook; + SortAccountIdsByLastSelectedHook & + GetCapabilitiesHook; /** * Handler for the `wallet_getSession` RPC method as specified by [CAIP-312](https://chainagnostic.org/CAIPs/caip-312). @@ -42,6 +49,7 @@ export type WalletGetSessionHooks = GetCaveatForOriginHook & * @param hooks.getCaveatForOrigin - Function to retrieve a caveat for the origin. * @param hooks.getNonEvmSupportedMethods - A function that returns the supported methods for a non EVM scope. * @param hooks.sortAccountIdsByLastSelected - A function that accepts an array of CaipAccountId and returns an array of CaipAccountId sorted by corresponding last selected account in the wallet. + * @param hooks.getCapabilities - A function that returns the capabilities for a given address. * @returns Nothing. */ async function handleWalletGetSession( @@ -62,7 +70,7 @@ async function handleWalletGetSession( } if (!caveat) { - response.result = { sessionScopes: {} }; + response.result = { sessionScopes: {}, sessionProperties: {} }; return end(); } @@ -71,6 +79,9 @@ async function handleWalletGetSession( getNonEvmSupportedMethods: hooks.getNonEvmSupportedMethods, sortAccountIdsByLastSelected: hooks.sortAccountIdsByLastSelected, }), + sessionProperties: getSessionProperties(caveat.value, { + getCapabilities: hooks.getCapabilities, + }) as Record, }; return end(); } @@ -89,5 +100,6 @@ export const walletGetSessionHandler = { getCaveatForOrigin: true, getNonEvmSupportedMethods: true, sortAccountIdsByLastSelected: true, + getCapabilities: true, }, } satisfies WalletGetSessionHandler; From 9d0181c7c458bd487c4a0f24babddc1837075e04 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Fri, 26 Jun 2026 15:37:36 -0700 Subject: [PATCH 2/3] changelog --- packages/chain-agnostic-permission/CHANGELOG.md | 2 +- packages/multichain-api-middleware/CHANGELOG.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/chain-agnostic-permission/CHANGELOG.md b/packages/chain-agnostic-permission/CHANGELOG.md index 500c0caddb..bad5b435fe 100644 --- a/packages/chain-agnostic-permission/CHANGELOG.md +++ b/packages/chain-agnostic-permission/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add `getSessionProperties` function that hydrates the persisted session properties of a CAIP-25 caveat value with a `capabilities` record built by invoking the provided `getCapabilities` hook for each permitted EVM account ([#0000](https://github.com/MetaMask/core/pull/0000)) +- Add `getSessionProperties` function that hydrates the persisted session properties of a CAIP-25 caveat value with a `capabilities` record built by invoking the provided `getCapabilities` hook for each permitted EVM account ([#9294](https://github.com/MetaMask/core/pull/9294)) ## [1.6.2] diff --git a/packages/multichain-api-middleware/CHANGELOG.md b/packages/multichain-api-middleware/CHANGELOG.md index 73da1399ea..0e82d532a9 100644 --- a/packages/multichain-api-middleware/CHANGELOG.md +++ b/packages/multichain-api-middleware/CHANGELOG.md @@ -9,9 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- **BREAKING:** The `wallet_getSession` and `wallet_createSession` handlers now require a `getCapabilities` hook (`(params: { address: string }) => unknown`) ([#0000](https://github.com/MetaMask/core/pull/0000)) +- **BREAKING:** The `wallet_getSession` and `wallet_createSession` handlers now require a `getCapabilities` hook (`(params: { address: string }) => unknown`) ([#9294](https://github.com/MetaMask/core/pull/9294)) - `WalletGetSessionHooks` and `WalletCreateSessionHooks` now include this hook, which must be provided when wiring up the handlers. -- The `wallet_getSession` and `wallet_createSession` handlers now derive the returned `sessionProperties` via `getSessionProperties`, hydrating the persisted session properties with a `capabilities` record built from the `getCapabilities` hook for each permitted EVM account ([#0000](https://github.com/MetaMask/core/pull/0000)) +- The `wallet_getSession` and `wallet_createSession` handlers now derive the returned `sessionProperties` via `getSessionProperties`, hydrating the persisted session properties with a `capabilities` record built from the `getCapabilities` hook for each permitted EVM account ([#9294](https://github.com/MetaMask/core/pull/9294)) - `wallet_getSession` now always includes a `sessionProperties` field in its result (an empty object when there is no active session). ## [3.1.4] From e1c7d2838f7a61c26abd727ec984dd9fb086010e Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Fri, 26 Jun 2026 15:47:41 -0700 Subject: [PATCH 3/3] lint --- .../caip-permission-operator-session-scopes.test.ts | 4 +++- .../src/handlers/wallet-createSession.test.ts | 12 ++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/chain-agnostic-permission/src/operators/caip-permission-operator-session-scopes.test.ts b/packages/chain-agnostic-permission/src/operators/caip-permission-operator-session-scopes.test.ts index cf67efacd1..647ed4beb5 100644 --- a/packages/chain-agnostic-permission/src/operators/caip-permission-operator-session-scopes.test.ts +++ b/packages/chain-agnostic-permission/src/operators/caip-permission-operator-session-scopes.test.ts @@ -372,7 +372,9 @@ describe('CAIP-25 session scopes adapters', () => { }); it('calls getCapabilities with each unique permitted EVM address', () => { - const getCapabilities = jest.fn().mockReturnValue({ atomic: 'supported' }); + const getCapabilities = jest + .fn() + .mockReturnValue({ atomic: 'supported' }); getSessionProperties( { diff --git a/packages/multichain-api-middleware/src/handlers/wallet-createSession.test.ts b/packages/multichain-api-middleware/src/handlers/wallet-createSession.test.ts index dc1d7533e1..af20a95a01 100644 --- a/packages/multichain-api-middleware/src/handlers/wallet-createSession.test.ts +++ b/packages/multichain-api-middleware/src/handlers/wallet-createSession.test.ts @@ -868,7 +868,8 @@ describe('wallet_createSession', () => { }, }, sessionProperties: { - [KnownSessionProperties.SolanaAccountChangedNotifications]: true, + [KnownSessionProperties.SolanaAccountChangedNotifications]: + true, }, }, }, @@ -1126,7 +1127,8 @@ describe('wallet_createSession', () => { }, isMultichainOrigin: true, sessionProperties: { - [KnownSessionProperties.SolanaAccountChangedNotifications]: true, + [KnownSessionProperties.SolanaAccountChangedNotifications]: + true, }, }, }, @@ -1193,7 +1195,8 @@ describe('wallet_createSession', () => { }, isMultichainOrigin: true, sessionProperties: { - [KnownSessionProperties.SolanaAccountChangedNotifications]: true, + [KnownSessionProperties.SolanaAccountChangedNotifications]: + true, }, }, }, @@ -1253,7 +1256,8 @@ describe('wallet_createSession', () => { }, isMultichainOrigin: true, sessionProperties: { - [KnownSessionProperties.SolanaAccountChangedNotifications]: true, + [KnownSessionProperties.SolanaAccountChangedNotifications]: + true, }, }, },