From df2ef3393a5eb938368c53f06b58419c7b620514 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 14 Oct 2024 12:15:57 -0700 Subject: [PATCH 001/148] initial copy over --- .storybook/test-data.js | 26 +- app/scripts/background.js | 9 +- .../controllers/permissions/background-api.js | 269 +++- .../permissions/background-api.test.js | 1150 +++++++++++++---- app/scripts/controllers/permissions/enums.ts | 1 + .../controllers/permissions/selectors.js | 140 +- .../controllers/permissions/selectors.test.js | 169 ++- .../controllers/permissions/specifications.js | 266 +--- .../permissions/specifications.test.js | 549 +------- .../multichain-api/wallet-getPermissions.js | 85 ++ .../wallet-getPermissions.test.js | 295 +++++ .../wallet-requestPermissions.js | 205 +++ .../wallet-requestPermissions.test.js | 715 ++++++++++ .../wallet-revokePermissions.js | 88 ++ .../wallet-revokePermissions.test.js | 204 +++ .../createMethodMiddleware.js | 34 +- .../createMethodMiddleware.test.js | 88 +- .../handlers/add-ethereum-chain.js | 37 +- .../handlers/add-ethereum-chain.test.js | 788 +++-------- .../handlers/ethereum-chain-utils.js | 115 +- .../rpc-method-middleware/handlers/index.ts | 10 +- .../handlers/request-accounts.js | 126 +- .../handlers/switch-ethereum-chain.js | 33 +- .../handlers/switch-ethereum-chain.test.js | 335 ++--- app/scripts/metamask-controller.js | 779 ++++++++--- app/scripts/metamask-controller.test.js | 11 +- package.json | 1 + test/e2e/fixture-builder.js | 258 ++-- test/e2e/tests/request-queuing/ui.spec.js | 2 +- .../unconnected-account-alert.test.js | 20 +- .../permission-page-container.component.js | 2 +- .../account-list-menu.test.tsx | 92 +- .../connected-accounts-menu.test.tsx | 24 +- .../edit-accounts-modal.tsx | 9 +- .../pages/connections/connections.test.tsx | 40 +- .../pages/connections/connections.tsx | 2 +- .../permissions-page/permissions-page.test.js | 20 +- .../site-cell/site-cell.tsx | 5 +- .../send/components/account-picker.test.tsx | 20 +- .../permission-details-modal.test.tsx | 24 +- .../connect-page/connect-page.tsx | 33 +- ui/pages/routes/routes.component.test.js | 18 +- ui/selectors/permissions.js | 65 +- ui/selectors/permissions.test.js | 222 +++- ui/selectors/selectors.test.js | 20 +- yarn.lock | 90 +- 46 files changed, 5000 insertions(+), 2494 deletions(-) create mode 100644 app/scripts/lib/multichain-api/wallet-getPermissions.js create mode 100644 app/scripts/lib/multichain-api/wallet-getPermissions.test.js create mode 100644 app/scripts/lib/multichain-api/wallet-requestPermissions.js create mode 100644 app/scripts/lib/multichain-api/wallet-requestPermissions.test.js create mode 100644 app/scripts/lib/multichain-api/wallet-revokePermissions.js create mode 100644 app/scripts/lib/multichain-api/wallet-revokePermissions.test.js diff --git a/.storybook/test-data.js b/.storybook/test-data.js index cbcebb6347ed..c9e79ca510a3 100644 --- a/.storybook/test-data.js +++ b/.storybook/test-data.js @@ -1398,17 +1398,29 @@ const state = { subjects: { 'https://app.uniswap.org': { permissions: { - eth_accounts: { - invoker: 'https://app.uniswap.org', - parentCapability: 'eth_accounts', - id: 'a7342e4b-beae-4525-a36c-c0635fd03359', - date: 1620710693178, + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x64a845a5b02460acf8a3d84503b0d68d028b4bb4'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], + invoker: 'https://app.uniswap.org', + id: 'a7342e4b-beae-4525-a36c-c0635fd03359', + date: 1620710693178, + parentCapability: 'endowment:caip25', }, }, }, diff --git a/app/scripts/background.js b/app/scripts/background.js index 7d9d0f5684a6..d741fe245fbb 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -637,13 +637,8 @@ function emitDappViewedMetricEvent(origin) { return; } - const permissions = controller.controllerMessenger.call( - 'PermissionController:getPermissions', - origin, - ); - const numberOfConnectedAccounts = - permissions?.eth_accounts?.caveats[0]?.value.length; - if (!numberOfConnectedAccounts) { + const numberOfConnectedAccounts = controller.getPermittedAccounts(origin); + if (numberOfConnectedAccounts.length === 0) { return; } diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index b778ff42385d..83302ba4b7d2 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -1,45 +1,178 @@ import nanoid from 'nanoid'; +import { MethodNames } from '@metamask/permission-controller'; import { - CaveatTypes, - RestrictedMethods, -} from '../../../../shared/constants/permissions'; -import { CaveatFactories, PermissionNames } from './specifications'; + Caip25CaveatType, + Caip25EndowmentPermissionName, + getEthAccounts, + setEthAccounts, + getPermittedEthChainIds, + setPermittedEthChainIds, +} from '@metamask/multichain'; +import { RestrictedMethods } from '../../../../shared/constants/permissions'; +import { PermissionNames } from './specifications'; -export function getPermissionBackgroundApiMethods(permissionController) { +export function getPermissionBackgroundApiMethods({ + permissionController, + approvalController, +}) { + // To add more than one account when already connected to the dapp const addMoreAccounts = (origin, accounts) => { - const caveat = CaveatFactories.restrictReturnedAccounts(accounts); - - permissionController.grantPermissionsIncremental({ - subject: { origin }, - approvedPermissions: { - [RestrictedMethods.eth_accounts]: { caveats: [caveat] }, - }, - }); + let caip25Caveat; + try { + caip25Caveat = permissionController.getCaveat( + origin, + Caip25EndowmentPermissionName, + Caip25CaveatType, + ); + } catch (err) { + // noop + } + + if (!caip25Caveat) { + throw new Error('tried to add accounts when none have been permissioned'); // TODO: better error + } + + const ethAccounts = getEthAccounts(caip25Caveat.value); + + const updatedEthAccounts = Array.from( + new Set([...ethAccounts, ...accounts]), + ); + + const updatedCaveatValue = setEthAccounts( + caip25Caveat.value, + updatedEthAccounts, + ); + + permissionController.updateCaveat( + origin, + Caip25EndowmentPermissionName, + Caip25CaveatType, + updatedCaveatValue, + ); }; const addMoreChains = (origin, chainIds) => { - const caveat = CaveatFactories.restrictNetworkSwitching(chainIds); - - permissionController.grantPermissionsIncremental({ - subject: { origin }, - approvedPermissions: { - [PermissionNames.permittedChains]: { caveats: [caveat] }, - }, - }); + let caip25Caveat; + try { + caip25Caveat = permissionController.getCaveat( + origin, + Caip25EndowmentPermissionName, + Caip25CaveatType, + ); + } catch (err) { + // noop + } + + if (!caip25Caveat) { + throw new Error('tried to add chains when none have been permissioned'); // TODO: better error + } + + // get the list of permitted eth accounts before we modify the permitted chains and potentially lose some + const ethAccounts = getEthAccounts(caip25Caveat.value); + + const ethChainIds = getPermittedEthChainIds(caip25Caveat.value); + + const updatedEthChainIds = Array.from( + new Set([...ethChainIds, ...chainIds]), + ); + + let updatedCaveatValue = setPermittedEthChainIds( + caip25Caveat.value, + updatedEthChainIds, + ); + + // ensure that the list of permitted eth accounts is intact after permitted chain updates + updatedCaveatValue = setEthAccounts(updatedCaveatValue, ethAccounts); + + permissionController.updateCaveat( + origin, + Caip25EndowmentPermissionName, + Caip25CaveatType, + updatedCaveatValue, + ); + }; + + const requestAccountsAndChainPermissionsWithId = (origin) => { + const id = nanoid(); + // NOTE: the eth_accounts/permittedChains approvals will be combined in the future. + // Until they are actually combined, when testing, you must request both + // eth_accounts and permittedChains together. + approvalController + .addAndShowApprovalRequest({ + id, + origin, + requestData: { + metadata: { + id, + origin, + }, + permissions: { + [RestrictedMethods.eth_accounts]: {}, + [PermissionNames.permittedChains]: {}, + }, + }, + type: MethodNames.requestPermissions, + }) + .then((legacyApproval) => { + let caveatValue = { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }; + caveatValue = setPermittedEthChainIds( + caveatValue, + legacyApproval.approvedChainIds, + ); + + caveatValue = setEthAccounts( + caveatValue, + legacyApproval.approvedAccounts, + ); + + permissionController.grantPermissions({ + subject: { origin }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: caveatValue, + }, + ], + }, + }, + }); + }); + + return id; }; return { addPermittedAccount: (origin, account) => addMoreAccounts(origin, [account]), + addPermittedAccounts: (origin, accounts) => addMoreAccounts(origin, accounts), removePermittedAccount: (origin, account) => { - const { value: existingAccounts } = permissionController.getCaveat( - origin, - RestrictedMethods.eth_accounts, - CaveatTypes.restrictReturnedAccounts, - ); + let caip25Caveat; + try { + caip25Caveat = permissionController.getCaveat( + origin, + Caip25EndowmentPermissionName, + Caip25CaveatType, + ); + } catch (err) { + // noop + } + + if (!caip25Caveat) { + throw new Error( + 'tried to remove accounts when none have been permissioned', + ); // TODO: better error + } + + const existingAccounts = getEthAccounts(caip25Caveat.value); const remainingAccounts = existingAccounts.filter( (existingAccount) => existingAccount !== account, @@ -52,74 +185,78 @@ export function getPermissionBackgroundApiMethods(permissionController) { if (remainingAccounts.length === 0) { permissionController.revokePermission( origin, - RestrictedMethods.eth_accounts, + Caip25EndowmentPermissionName, ); } else { + const updatedCaveatValue = setEthAccounts( + caip25Caveat.value, + remainingAccounts, + ); permissionController.updateCaveat( origin, - RestrictedMethods.eth_accounts, - CaveatTypes.restrictReturnedAccounts, - remainingAccounts, + Caip25EndowmentPermissionName, + Caip25CaveatType, + updatedCaveatValue, ); } }, addPermittedChain: (origin, chainId) => addMoreChains(origin, [chainId]), + addPermittedChains: (origin, chainIds) => addMoreChains(origin, chainIds), removePermittedChain: (origin, chainId) => { - const { value: existingChains } = permissionController.getCaveat( - origin, - PermissionNames.permittedChains, - CaveatTypes.restrictNetworkSwitching, - ); + let caip25Caveat; + try { + caip25Caveat = permissionController.getCaveat( + origin, + Caip25EndowmentPermissionName, + Caip25CaveatType, + ); + } catch (err) { + // noop + } - const remainingChains = existingChains.filter( - (existingChain) => existingChain !== chainId, + if (!caip25Caveat) { + throw new Error( + 'tried to remove chains when none have been permissioned', + ); // TODO: better error + } + + const existingEthChainIds = getPermittedEthChainIds(caip25Caveat.value); + + const remainingChainIds = existingEthChainIds.filter( + (existingChainId) => existingChainId !== chainId, ); - if (remainingChains.length === existingChains.length) { + if (remainingChainIds.length === existingEthChainIds.length) { return; } - if (remainingChains.length === 0) { + // TODO: Is this right? Do we want to revoke the entire + // CAIP-25 permission if no eip-155 chains are left? + if (remainingChainIds.length === 0) { permissionController.revokePermission( origin, - PermissionNames.permittedChains, + Caip25EndowmentPermissionName, ); } else { + const updatedCaveatValue = setPermittedEthChainIds( + caip25Caveat.value, + remainingChainIds, + ); permissionController.updateCaveat( origin, - PermissionNames.permittedChains, - CaveatTypes.restrictNetworkSwitching, - remainingChains, + Caip25EndowmentPermissionName, + Caip25CaveatType, + updatedCaveatValue, ); } }, - requestAccountsAndChainPermissionsWithId: async (origin) => { - const id = nanoid(); - permissionController.requestPermissions( - { origin }, - { - [PermissionNames.eth_accounts]: {}, - [PermissionNames.permittedChains]: {}, - }, - { id }, - ); - return id; - }, + requestAccountsAndChainPermissionsWithId, - requestAccountsPermissionWithId: async (origin) => { - const id = nanoid(); - permissionController.requestPermissions( - { origin }, - { - eth_accounts: {}, - }, - { id }, - ); - return id; - }, + // TODO: Remove this / DRY with requestAccountsAndChainPermissionsWithId + requestAccountsPermissionWithId: requestAccountsAndChainPermissionsWithId, }; } diff --git a/app/scripts/controllers/permissions/background-api.test.js b/app/scripts/controllers/permissions/background-api.test.js index 2a050b29a00e..5622f02f0b5c 100644 --- a/app/scripts/controllers/permissions/background-api.test.js +++ b/app/scripts/controllers/permissions/background-api.test.js @@ -1,390 +1,1050 @@ +import { MethodNames } from '@metamask/permission-controller'; import { - CaveatTypes, - RestrictedMethods, -} from '../../../../shared/constants/permissions'; + Caip25CaveatType, + Caip25EndowmentPermissionName, + KnownNotifications, + KnownRpcMethods, +} from '@metamask/multichain'; +import { RestrictedMethods } from '../../../../shared/constants/permissions'; +import { flushPromises } from '../../../../test/lib/timer-helpers'; import { getPermissionBackgroundApiMethods } from './background-api'; -import { CaveatFactories, PermissionNames } from './specifications'; +import { PermissionNames } from './specifications'; describe('permission background API methods', () => { - const getEthAccountsPermissions = (accounts) => ({ - [RestrictedMethods.eth_accounts]: { - caveats: [CaveatFactories.restrictReturnedAccounts(accounts)], - }, - }); - - const getPermittedChainsPermissions = (chainIds) => ({ - [PermissionNames.permittedChains]: { - caveats: [CaveatFactories.restrictNetworkSwitching(chainIds)], - }, + afterEach(() => { + jest.resetAllMocks(); }); describe('addPermittedAccount', () => { - it('calls grantPermissionsIncremental with expected parameters', () => { + it('gets the CAIP-25 caveat', () => { const permissionController = { - grantPermissionsIncremental: jest.fn(), + getCaveat: jest.fn(), }; - getPermissionBackgroundApiMethods( - permissionController, - ).addPermittedAccount('foo.com', '0x1'); - - expect( - permissionController.grantPermissionsIncremental, - ).toHaveBeenCalledTimes(1); - expect( - permissionController.grantPermissionsIncremental, - ).toHaveBeenCalledWith({ - subject: { origin: 'foo.com' }, - approvedPermissions: getEthAccountsPermissions(['0x1']), - }); + try { + getPermissionBackgroundApiMethods({ + permissionController, + }).addPermittedAccount('foo.com', '0x1'); + } catch (err) { + // noop + } + + expect(permissionController.getCaveat).toHaveBeenCalledWith( + 'foo.com', + Caip25EndowmentPermissionName, + Caip25CaveatType, + ); }); - }); - describe('addPermittedAccounts', () => { - it('calls grantPermissionsIncremental with expected parameters for single account', () => { + it('throws an error if there is no existing CAIP-25 caveat', () => { const permissionController = { - grantPermissionsIncremental: jest.fn(), + getCaveat: jest.fn(), }; - getPermissionBackgroundApiMethods( - permissionController, - ).addPermittedAccounts('foo.com', ['0x1']); - - expect( - permissionController.grantPermissionsIncremental, - ).toHaveBeenCalledTimes(1); - expect( - permissionController.grantPermissionsIncremental, - ).toHaveBeenCalledWith({ - subject: { origin: 'foo.com' }, - approvedPermissions: getEthAccountsPermissions(['0x1']), - }); + expect(() => + getPermissionBackgroundApiMethods({ + permissionController, + }).addPermittedAccount('foo.com', '0x1'), + ).toThrow( + new Error('tried to add accounts when none have been permissioned'), + ); }); - it('calls grantPermissionsIncremental with expected parameters with multiple accounts', () => { + it('calls updateCaveat with the account added', () => { const permissionController = { - grantPermissionsIncremental: jest.fn(), + getCaveat: jest.fn().mockReturnValue({ + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: ['eip155:10:0x1', 'eip155:10:0x2'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x2', 'eip155:1:0x3'], + }, + }, + isMultichainOrigin: true, + }, + }), + updateCaveat: jest.fn(), }; - getPermissionBackgroundApiMethods( + getPermissionBackgroundApiMethods({ permissionController, - ).addPermittedAccounts('foo.com', ['0x1', '0x2']); - - expect( - permissionController.grantPermissionsIncremental, - ).toHaveBeenCalledTimes(1); - expect( - permissionController.grantPermissionsIncremental, - ).toHaveBeenCalledWith({ - subject: { origin: 'foo.com' }, - approvedPermissions: getEthAccountsPermissions(['0x1', '0x2']), - }); + }).addPermittedAccount('foo.com', '0x4'); + + expect(permissionController.updateCaveat).toHaveBeenCalledTimes(1); + expect(permissionController.updateCaveat).toHaveBeenCalledWith( + 'foo.com', + Caip25EndowmentPermissionName, + Caip25CaveatType, + { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x2', + 'eip155:1:0x3', + 'eip155:1:0x1', + 'eip155:1:0x4', + ], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: [ + 'eip155:10:0x2', + 'eip155:10:0x3', + 'eip155:10:0x1', + 'eip155:10:0x4', + ], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x2', + 'eip155:1:0x3', + 'eip155:1:0x1', + 'eip155:1:0x4', + ], + }, + 'wallet:eip155': { + methods: [], + notifications: [], + accounts: [ + 'wallet:eip155:0x2', + 'wallet:eip155:0x3', + 'wallet:eip155:0x1', + 'wallet:eip155:0x4', + ], + }, + wallet: { + methods: [], + notifications: [], + accounts: [ + 'wallet:eip155:0x2', + 'wallet:eip155:0x3', + 'wallet:eip155:0x1', + 'wallet:eip155:0x4', + ], + }, + }, + isMultichainOrigin: true, + }, + ); }); }); - describe('removePermittedAccount', () => { - it('removes a permitted account', () => { + describe('addPermittedAccounts', () => { + it('gets the CAIP-25 caveat', () => { const permissionController = { - getCaveat: jest.fn().mockImplementationOnce(() => { - return { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0x1', '0x2'], - }; - }), - revokePermission: jest.fn(), - updateCaveat: jest.fn(), + getCaveat: jest.fn(), }; - getPermissionBackgroundApiMethods( - permissionController, - ).removePermittedAccount('foo.com', '0x2'); + try { + getPermissionBackgroundApiMethods({ + permissionController, + }).addPermittedAccounts('foo.com', ['0x1']); + } catch (err) { + // noop + } - expect(permissionController.getCaveat).toHaveBeenCalledTimes(1); expect(permissionController.getCaveat).toHaveBeenCalledWith( 'foo.com', - RestrictedMethods.eth_accounts, - CaveatTypes.restrictReturnedAccounts, + Caip25EndowmentPermissionName, + Caip25CaveatType, ); + }); - expect(permissionController.revokePermission).not.toHaveBeenCalled(); + it('throws an error if there is no existing CAIP-25 caveat', () => { + const permissionController = { + getCaveat: jest.fn(), + }; - expect(permissionController.updateCaveat).toHaveBeenCalledTimes(1); - expect(permissionController.updateCaveat).toHaveBeenCalledWith( - 'foo.com', - RestrictedMethods.eth_accounts, - CaveatTypes.restrictReturnedAccounts, - ['0x1'], + expect(() => + getPermissionBackgroundApiMethods({ + permissionController, + }).addPermittedAccounts('foo.com', ['0x1']), + ).toThrow( + new Error('tried to add accounts when none have been permissioned'), ); }); - it('revokes the accounts permission if the removed account is the only permitted account', () => { + it('calls updateCaveat with the accounts added to only eip155 scopes and all accounts for eip155 scopes synced', () => { const permissionController = { - getCaveat: jest.fn().mockImplementationOnce(() => { - return { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0x1'], - }; + getCaveat: jest.fn().mockReturnValue({ + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: ['eip155:10:0x1', 'eip155:10:0x2'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x2', 'eip155:1:0x3'], + }, + }, + isMultichainOrigin: true, + }, }), - revokePermission: jest.fn(), updateCaveat: jest.fn(), }; - getPermissionBackgroundApiMethods( + getPermissionBackgroundApiMethods({ permissionController, - ).removePermittedAccount('foo.com', '0x1'); + }).addPermittedAccounts('foo.com', ['0x4', '0x5']); - expect(permissionController.getCaveat).toHaveBeenCalledTimes(1); - expect(permissionController.getCaveat).toHaveBeenCalledWith( + expect(permissionController.updateCaveat).toHaveBeenCalledTimes(1); + expect(permissionController.updateCaveat).toHaveBeenCalledWith( 'foo.com', - RestrictedMethods.eth_accounts, - CaveatTypes.restrictReturnedAccounts, + Caip25EndowmentPermissionName, + Caip25CaveatType, + { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x2', + 'eip155:1:0x3', + 'eip155:1:0x1', + 'eip155:1:0x4', + 'eip155:1:0x5', + ], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: [ + 'eip155:10:0x2', + 'eip155:10:0x3', + 'eip155:10:0x1', + 'eip155:10:0x4', + 'eip155:10:0x5', + ], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x2', + 'eip155:1:0x3', + 'eip155:1:0x1', + 'eip155:1:0x4', + 'eip155:1:0x5', + ], + }, + 'wallet:eip155': { + methods: [], + notifications: [], + accounts: [ + 'wallet:eip155:0x2', + 'wallet:eip155:0x3', + 'wallet:eip155:0x1', + 'wallet:eip155:0x4', + 'wallet:eip155:0x5', + ], + }, + wallet: { + methods: [], + notifications: [], + accounts: [ + 'wallet:eip155:0x2', + 'wallet:eip155:0x3', + 'wallet:eip155:0x1', + 'wallet:eip155:0x4', + 'wallet:eip155:0x5', + ], + }, + }, + isMultichainOrigin: true, + }, ); + }); + }); - expect(permissionController.revokePermission).toHaveBeenCalledTimes(1); - expect(permissionController.revokePermission).toHaveBeenCalledWith( + describe('removePermittedAccount', () => { + it('gets the CAIP-25 caveat', () => { + const permissionController = { + getCaveat: jest.fn(), + }; + + try { + getPermissionBackgroundApiMethods({ + permissionController, + }).removePermittedAccount('foo.com', '0x1'); + } catch (err) { + // noop + } + + expect(permissionController.getCaveat).toHaveBeenCalledWith( 'foo.com', - RestrictedMethods.eth_accounts, + Caip25EndowmentPermissionName, + Caip25CaveatType, ); + }); - expect(permissionController.updateCaveat).not.toHaveBeenCalled(); + it('throws an error if there is no existing CAIP-25 caveat', () => { + const permissionController = { + getCaveat: jest.fn(), + }; + + expect(() => + getPermissionBackgroundApiMethods({ + permissionController, + }).removePermittedAccount('foo.com', '0x1'), + ).toThrow( + new Error('tried to remove accounts when none have been permissioned'), + ); }); - it('does not call permissionController.updateCaveat if the specified account is not permitted', () => { + it('does nothing if the account being removed does not exist', () => { const permissionController = { - getCaveat: jest.fn().mockImplementationOnce(() => { - return { type: CaveatTypes.restrictReturnedAccounts, value: ['0x1'] }; + getCaveat: jest.fn().mockReturnValue({ + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: ['eip155:10:0x1', 'eip155:10:0x2'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x2', 'eip155:1:0x3'], + }, + }, + isMultichainOrigin: true, + }, }), - revokePermission: jest.fn(), updateCaveat: jest.fn(), + revokePermission: jest.fn(), }; - getPermissionBackgroundApiMethods( + getPermissionBackgroundApiMethods({ permissionController, - ).removePermittedAccount('foo.com', '0x2'); - expect(permissionController.getCaveat).toHaveBeenCalledTimes(1); - expect(permissionController.getCaveat).toHaveBeenCalledWith( - 'foo.com', - RestrictedMethods.eth_accounts, - CaveatTypes.restrictReturnedAccounts, - ); + }).removePermittedAccount('foo.com', '0xdeadbeef'); - expect(permissionController.revokePermission).not.toHaveBeenCalled(); expect(permissionController.updateCaveat).not.toHaveBeenCalled(); + expect(permissionController.revokePermission).not.toHaveBeenCalled(); }); - }); - describe('requestAccountsPermissionWithId', () => { - it('request an accounts permission and returns the request id', async () => { + it('revokes the entire permission if the removed account is the only eip:155 scoped account', () => { const permissionController = { - requestPermissions: jest - .fn() - .mockImplementationOnce(async (_, __, { id }) => { - return [null, { id }]; - }), + getCaveat: jest.fn().mockReturnValue({ + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: ['eip155:10:0x1'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + }, + isMultichainOrigin: true, + }, + }), + revokePermission: jest.fn(), }; - const id = await getPermissionBackgroundApiMethods( + getPermissionBackgroundApiMethods({ permissionController, - ).requestAccountsPermissionWithId('foo.com'); + }).removePermittedAccount('foo.com', '0x1'); - expect(permissionController.requestPermissions).toHaveBeenCalledTimes(1); - expect(permissionController.requestPermissions).toHaveBeenCalledWith( - { origin: 'foo.com' }, - { eth_accounts: {} }, - { id: expect.any(String) }, + expect(permissionController.revokePermission).toHaveBeenCalledWith( + 'foo.com', + Caip25EndowmentPermissionName, ); + }); - expect(id.length > 0).toBe(true); - expect(id).toStrictEqual( - permissionController.requestPermissions.mock.calls[0][2].id, + it('updates the caveat with the account removed and all eip155 accounts synced', () => { + const permissionController = { + getCaveat: jest.fn().mockReturnValue({ + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: ['eip155:10:0x1', 'eip155:10:0x2'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x2', 'eip155:1:0x3'], + }, + }, + isMultichainOrigin: true, + }, + }), + updateCaveat: jest.fn(), + }; + + getPermissionBackgroundApiMethods({ + permissionController, + }).removePermittedAccount('foo.com', '0x2'); + + expect(permissionController.updateCaveat).toHaveBeenCalledWith( + 'foo.com', + Caip25EndowmentPermissionName, + Caip25CaveatType, + { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x3', 'eip155:1:0x1'], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: ['eip155:10:0x3', 'eip155:10:0x1'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x3', 'eip155:1:0x1'], + }, + 'wallet:eip155': { + methods: [], + notifications: [], + accounts: ['wallet:eip155:0x3', 'wallet:eip155:0x1'], + }, + wallet: { + methods: [], + notifications: [], + accounts: ['wallet:eip155:0x3', 'wallet:eip155:0x1'], + }, + }, + isMultichainOrigin: true, + }, ); }); }); describe('requestAccountsAndChainPermissionsWithId', () => { - it('request eth_accounts and permittedChains permissions and returns the request id', async () => { + it('requests eth_accounts and permittedChains approval and returns the request id', async () => { + const approvalController = { + addAndShowApprovalRequest: jest.fn().mockResolvedValue({ + approvedChainIds: ['0x1', '0x5'], + approvedAccounts: ['0xdeadbeef'], + }), + }; const permissionController = { - requestPermissions: jest - .fn() - .mockImplementationOnce(async (_, __, { id }) => { - return [null, { id }]; - }), + grantPermissions: jest.fn(), }; - const id = await getPermissionBackgroundApiMethods( + const result = getPermissionBackgroundApiMethods({ + approvalController, permissionController, - ).requestAccountsAndChainPermissionsWithId('foo.com'); + }).requestAccountsAndChainPermissionsWithId('foo.com'); + + const { id } = + approvalController.addAndShowApprovalRequest.mock.calls[0][0]; - expect(permissionController.requestPermissions).toHaveBeenCalledTimes(1); - expect(permissionController.requestPermissions).toHaveBeenCalledWith( - { origin: 'foo.com' }, + expect(result).toStrictEqual(id); + expect(approvalController.addAndShowApprovalRequest).toHaveBeenCalledWith( { - [PermissionNames.eth_accounts]: {}, - [PermissionNames.permittedChains]: {}, + id, + origin: 'foo.com', + requestData: { + metadata: { + id, + origin: 'foo.com', + }, + permissions: { + [RestrictedMethods.eth_accounts]: {}, + [PermissionNames.permittedChains]: {}, + }, + }, + type: MethodNames.requestPermissions, }, - { id: expect.any(String) }, ); + }); - expect(id.length > 0).toBe(true); - expect(id).toStrictEqual( - permissionController.requestPermissions.mock.calls[0][2].id, - ); + it('grants a legacy CAIP-25 permission (isMultichainOrigin: false) with the approved eip155 chainIds and accounts', async () => { + const approvalController = { + addAndShowApprovalRequest: jest.fn().mockResolvedValue({ + approvedChainIds: ['0x1', '0x5'], + approvedAccounts: ['0xdeadbeef'], + }), + }; + const permissionController = { + grantPermissions: jest.fn(), + }; + + getPermissionBackgroundApiMethods({ + approvalController, + permissionController, + }).requestAccountsAndChainPermissionsWithId('foo.com'); + + await flushPromises(); + + expect(permissionController.grantPermissions).toHaveBeenCalledWith({ + subject: { + origin: 'foo.com', + }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: KnownRpcMethods.eip155, + notifications: KnownNotifications.eip155, + accounts: ['eip155:1:0xdeadbeef'], + }, + 'eip155:5': { + methods: KnownRpcMethods.eip155, + notifications: KnownNotifications.eip155, + accounts: ['eip155:5:0xdeadbeef'], + }, + 'wallet:eip155': { + methods: [], + notifications: [], + accounts: ['wallet:eip155:0xdeadbeef'], + }, + wallet: { + methods: [], + notifications: [], + accounts: ['wallet:eip155:0xdeadbeef'], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + }, + }, + }); }); }); describe('addPermittedChain', () => { - it('calls grantPermissionsIncremental with expected parameters', () => { + it('gets the CAIP-25 caveat', () => { const permissionController = { - grantPermissionsIncremental: jest.fn(), + getCaveat: jest.fn(), }; - getPermissionBackgroundApiMethods(permissionController).addPermittedChain( + try { + getPermissionBackgroundApiMethods({ + permissionController, + }).addPermittedChain('foo.com', '0x1'); + } catch (err) { + // noop + } + + expect(permissionController.getCaveat).toHaveBeenCalledWith( 'foo.com', - '0x1', + Caip25EndowmentPermissionName, + Caip25CaveatType, ); - - expect( - permissionController.grantPermissionsIncremental, - ).toHaveBeenCalledTimes(1); - expect( - permissionController.grantPermissionsIncremental, - ).toHaveBeenCalledWith({ - subject: { origin: 'foo.com' }, - approvedPermissions: getPermittedChainsPermissions(['0x1']), - }); }); - }); - describe('addPermittedChains', () => { - it('calls grantPermissionsIncremental with expected parameters for single chain', () => { + it('throws an error if there is no existing CAIP-25 caveat', () => { const permissionController = { - grantPermissionsIncremental: jest.fn(), + getCaveat: jest.fn(), }; - getPermissionBackgroundApiMethods( - permissionController, - ).addPermittedChains('foo.com', ['0x1']); - - expect( - permissionController.grantPermissionsIncremental, - ).toHaveBeenCalledTimes(1); - expect( - permissionController.grantPermissionsIncremental, - ).toHaveBeenCalledWith({ - subject: { origin: 'foo.com' }, - approvedPermissions: getPermittedChainsPermissions(['0x1']), - }); + expect(() => + getPermissionBackgroundApiMethods({ + permissionController, + }).addPermittedChain('foo.com', '0x1'), + ).toThrow( + new Error('tried to add chains when none have been permissioned'), + ); }); - it('calls grantPermissionsIncremental with expected parameters with multiple chains', () => { + it('calls updateCaveat with the chain added and all eip155 accounts synced', () => { const permissionController = { - grantPermissionsIncremental: jest.fn(), + getCaveat: jest.fn().mockReturnValue({ + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: ['eip155:10:0x2'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x1'], + }, + }, + isMultichainOrigin: true, + }, + }), + updateCaveat: jest.fn(), }; - getPermissionBackgroundApiMethods( + getPermissionBackgroundApiMethods({ permissionController, - ).addPermittedChains('foo.com', ['0x1', '0x2']); - - expect( - permissionController.grantPermissionsIncremental, - ).toHaveBeenCalledTimes(1); - expect( - permissionController.grantPermissionsIncremental, - ).toHaveBeenCalledWith({ - subject: { origin: 'foo.com' }, - approvedPermissions: getPermittedChainsPermissions(['0x1', '0x2']), - }); + }).addPermittedChain('foo.com', '0x539'); // 1337 + + expect(permissionController.updateCaveat).toHaveBeenCalledTimes(1); + expect(permissionController.updateCaveat).toHaveBeenCalledWith( + 'foo.com', + Caip25EndowmentPermissionName, + Caip25CaveatType, + { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x1', 'eip155:1:0x2'], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: ['eip155:10:0x1', 'eip155:10:0x2'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x1', 'eip155:1:0x2'], + }, + 'eip155:1337': { + methods: KnownRpcMethods.eip155, + notifications: KnownNotifications.eip155, + accounts: ['eip155:1337:0x1', 'eip155:1337:0x2'], + }, + 'wallet:eip155': { + methods: [], + notifications: [], + accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x2'], + }, + wallet: { + methods: [], + notifications: [], + accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x2'], + }, + }, + isMultichainOrigin: true, + }, + ); }); }); - describe('removePermittedChain', () => { - it('removes a permitted chain', () => { + describe('addPermittedChains', () => { + it('gets the CAIP-25 caveat', () => { const permissionController = { - getCaveat: jest.fn().mockImplementationOnce(() => { - return { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x1', '0x2'], - }; - }), - revokePermission: jest.fn(), - updateCaveat: jest.fn(), + getCaveat: jest.fn(), }; - getPermissionBackgroundApiMethods( - permissionController, - ).removePermittedChain('foo.com', '0x2'); + try { + getPermissionBackgroundApiMethods({ + permissionController, + }).addPermittedChains('foo.com', ['0x1']); + } catch (err) { + // noop + } - expect(permissionController.getCaveat).toHaveBeenCalledTimes(1); expect(permissionController.getCaveat).toHaveBeenCalledWith( 'foo.com', - PermissionNames.permittedChains, - CaveatTypes.restrictNetworkSwitching, + Caip25EndowmentPermissionName, + Caip25CaveatType, ); + }); - expect(permissionController.revokePermission).not.toHaveBeenCalled(); + it('throws an error if there is no existing CAIP-25 caveat', () => { + const permissionController = { + getCaveat: jest.fn(), + }; - expect(permissionController.updateCaveat).toHaveBeenCalledTimes(1); - expect(permissionController.updateCaveat).toHaveBeenCalledWith( - 'foo.com', - PermissionNames.permittedChains, - CaveatTypes.restrictNetworkSwitching, - ['0x1'], + expect(() => + getPermissionBackgroundApiMethods({ + permissionController, + }).addPermittedChains('foo.com', ['0x1']), + ).toThrow( + new Error('tried to add chains when none have been permissioned'), ); }); - it('revokes the permittedChains permission if the removed chain is the only permitted chain', () => { + it('calls updateCaveat with the chains added and all eip155 accounts synced', () => { const permissionController = { - getCaveat: jest.fn().mockImplementationOnce(() => { - return { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x1'], - }; + getCaveat: jest.fn().mockReturnValue({ + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: ['eip155:10:0x2'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x1'], + }, + }, + isMultichainOrigin: true, + }, }), - revokePermission: jest.fn(), updateCaveat: jest.fn(), }; - getPermissionBackgroundApiMethods( + getPermissionBackgroundApiMethods({ permissionController, - ).removePermittedChain('foo.com', '0x1'); + }).addPermittedChains('foo.com', ['0x4', '0x5']); - expect(permissionController.getCaveat).toHaveBeenCalledTimes(1); - expect(permissionController.getCaveat).toHaveBeenCalledWith( + expect(permissionController.updateCaveat).toHaveBeenCalledTimes(1); + expect(permissionController.updateCaveat).toHaveBeenCalledWith( 'foo.com', - PermissionNames.permittedChains, - CaveatTypes.restrictNetworkSwitching, + Caip25EndowmentPermissionName, + Caip25CaveatType, + { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x1', 'eip155:1:0x2'], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: ['eip155:10:0x1', 'eip155:10:0x2'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x1', 'eip155:1:0x2'], + }, + 'eip155:4': { + methods: KnownRpcMethods.eip155, + notifications: KnownNotifications.eip155, + accounts: ['eip155:4:0x1', 'eip155:4:0x2'], + }, + 'eip155:5': { + methods: KnownRpcMethods.eip155, + notifications: KnownNotifications.eip155, + accounts: ['eip155:5:0x1', 'eip155:5:0x2'], + }, + 'wallet:eip155': { + methods: [], + notifications: [], + accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x2'], + }, + wallet: { + methods: [], + notifications: [], + accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x2'], + }, + }, + isMultichainOrigin: true, + }, ); + }); + }); - expect(permissionController.revokePermission).toHaveBeenCalledTimes(1); - expect(permissionController.revokePermission).toHaveBeenCalledWith( + describe('removePermittedChain', () => { + it('gets the CAIP-25 caveat', () => { + const permissionController = { + getCaveat: jest.fn(), + }; + + try { + getPermissionBackgroundApiMethods({ + permissionController, + }).removePermittedChain('foo.com', '0x1'); + } catch (err) { + // noop + } + + expect(permissionController.getCaveat).toHaveBeenCalledWith( 'foo.com', - PermissionNames.permittedChains, + Caip25EndowmentPermissionName, + Caip25CaveatType, + ); + }); + + it('throws an error if there is no existing CAIP-25 caveat', () => { + const permissionController = { + getCaveat: jest.fn(), + }; + + expect(() => + getPermissionBackgroundApiMethods({ + permissionController, + }).removePermittedChain('foo.com', '0x1'), + ).toThrow( + new Error('tried to remove chains when none have been permissioned'), ); + }); + + it('does nothing if the chain being removed does not exist', () => { + const permissionController = { + getCaveat: jest.fn().mockReturnValue({ + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: ['eip155:10:0x1', 'eip155:10:0x2'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + }, + isMultichainOrigin: true, + }, + }), + updateCaveat: jest.fn(), + revokePermission: jest.fn(), + }; + + getPermissionBackgroundApiMethods({ + permissionController, + }).removePermittedChain('foo.com', '0xdeadbeef'); expect(permissionController.updateCaveat).not.toHaveBeenCalled(); + expect(permissionController.revokePermission).not.toHaveBeenCalled(); }); - it('does not call permissionController.updateCaveat if the specified chain is not permitted', () => { + it('revokes the entire permission if the removed chain is the only eip:155 scope', () => { const permissionController = { - getCaveat: jest.fn().mockImplementationOnce(() => { - return { type: CaveatTypes.restrictNetworkSwitching, value: ['0x1'] }; + getCaveat: jest.fn().mockReturnValue({ + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + }, + isMultichainOrigin: true, + }, }), revokePermission: jest.fn(), - updateCaveat: jest.fn(), }; - getPermissionBackgroundApiMethods( + getPermissionBackgroundApiMethods({ permissionController, - ).removePermittedChain('foo.com', '0x2'); - expect(permissionController.getCaveat).toHaveBeenCalledTimes(1); - expect(permissionController.getCaveat).toHaveBeenCalledWith( + }).removePermittedChain('foo.com', '0x1'); + + expect(permissionController.revokePermission).toHaveBeenCalledWith( 'foo.com', - PermissionNames.permittedChains, - CaveatTypes.restrictNetworkSwitching, + Caip25EndowmentPermissionName, ); + }); - expect(permissionController.revokePermission).not.toHaveBeenCalled(); - expect(permissionController.updateCaveat).not.toHaveBeenCalled(); + it('updates the caveat with the chain removed', () => { + const permissionController = { + getCaveat: jest.fn().mockReturnValue({ + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + 'eip155:10': { + methods: [], + notifications: [], + accounts: ['eip155:10:0x1', 'eip155:10:0x2'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + }, + isMultichainOrigin: true, + }, + }), + updateCaveat: jest.fn(), + }; + + getPermissionBackgroundApiMethods({ + permissionController, + }).removePermittedChain('foo.com', '0xa'); // 10 + + expect(permissionController.updateCaveat).toHaveBeenCalledWith( + 'foo.com', + Caip25EndowmentPermissionName, + Caip25CaveatType, + { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + }, + isMultichainOrigin: true, + }, + ); }); }); }); diff --git a/app/scripts/controllers/permissions/enums.ts b/app/scripts/controllers/permissions/enums.ts index c170bd78aa67..9210d6751bdc 100644 --- a/app/scripts/controllers/permissions/enums.ts +++ b/app/scripts/controllers/permissions/enums.ts @@ -2,4 +2,5 @@ export enum NOTIFICATION_NAMES { accountsChanged = 'metamask_accountsChanged', unlockStateChanged = 'metamask_unlockStateChanged', chainChanged = 'metamask_chainChanged', + sessionChanged = 'wallet_sessionChanged', } diff --git a/app/scripts/controllers/permissions/selectors.js b/app/scripts/controllers/permissions/selectors.js index 76e638d25b54..df244681ad8c 100644 --- a/app/scripts/controllers/permissions/selectors.js +++ b/app/scripts/controllers/permissions/selectors.js @@ -1,6 +1,10 @@ import { createSelector } from 'reselect'; -import { CaveatTypes } from '../../../../shared/constants/permissions'; -import { PermissionNames } from './specifications'; +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, + getEthAccounts, + getPermittedEthChainIds, +} from '@metamask/multichain'; /** * This file contains selectors for PermissionController selector event @@ -26,20 +30,47 @@ export const getPermittedAccountsByOrigin = createSelector( getSubjects, (subjects) => { return Object.values(subjects).reduce((originToAccountsMap, subject) => { - const caveats = subject.permissions?.eth_accounts?.caveats || []; + const caveats = + subject.permissions?.[Caip25EndowmentPermissionName]?.caveats || []; - const caveat = caveats.find( - ({ type }) => type === CaveatTypes.restrictReturnedAccounts, - ); + const caveat = caveats.find(({ type }) => type === Caip25CaveatType); if (caveat) { - originToAccountsMap.set(subject.origin, caveat.value); + const ethAccounts = getEthAccounts(caveat.value); + originToAccountsMap.set(subject.origin, ethAccounts); } return originToAccountsMap; }, new Map()); }, ); +/** + * Get the authorized CAIP-25 scopes for each subject, keyed by origin. + * The values of the returned map are immutable values from the + * PermissionController state. + * + * @returns {Map} The current origin:authorization map. + */ +export const getAuthorizedScopesByOrigin = createSelector( + getSubjects, + (subjects) => { + return Object.values(subjects).reduce( + (originToAuthorizationsMap, subject) => { + const caveats = + subject.permissions?.[Caip25EndowmentPermissionName]?.caveats || []; + + const caveat = caveats.find(({ type }) => type === Caip25CaveatType); + + if (caveat) { + originToAuthorizationsMap.set(subject.origin, caveat.value); + } + return originToAuthorizationsMap; + }, + new Map(), + ); + }, +); + /** * Get the permitted chains for each subject, keyed by origin. * The values of the returned map are immutable values from the @@ -52,14 +83,13 @@ export const getPermittedChainsByOrigin = createSelector( (subjects) => { return Object.values(subjects).reduce((originToChainsMap, subject) => { const caveats = - subject.permissions?.[PermissionNames.permittedChains]?.caveats || []; + subject.permissions?.[Caip25EndowmentPermissionName]?.caveats || []; - const caveat = caveats.find( - ({ type }) => type === CaveatTypes.restrictNetworkSwitching, - ); + const caveat = caveats.find(({ type }) => type === Caip25CaveatType); if (caveat) { - originToChainsMap.set(subject.origin, caveat.value); + const ethChainIds = getPermittedEthChainIds(caveat.value); + originToChainsMap.set(subject.origin, ethChainIds); } return originToChainsMap; }, new Map()); @@ -109,3 +139,89 @@ export const diffMap = (currentMap, previousMap) => { } return changedMap; }; + +/** + * Given the current and previous exposed CAIP-25 authorization for each PermissionController + * subject, returns a new map containing all authorizations that have changed. + * The values of each map must be immutable values directly from the + * PermissionController state, or an empty object instantiated in this + * function. + * + * @param {Map} newAuthorizationsMap - The new origin:authorization map. + * @param {Map} [previousAuthorizationsMap] - The previous origin:authorization map. + * @returns {Map} The origin:authorization map of changed authorizations. + */ +export const getChangedAuthorizations = ( + newAuthorizationsMap, + previousAuthorizationsMap, +) => { + if (previousAuthorizationsMap === undefined) { + return newAuthorizationsMap; + } + + const changedAuthorizations = new Map(); + if (newAuthorizationsMap === previousAuthorizationsMap) { + return changedAuthorizations; + } + + const newOrigins = new Set([...newAuthorizationsMap.keys()]); + + for (const origin of previousAuthorizationsMap.keys()) { + const newAuthorizations = newAuthorizationsMap.get(origin) ?? { + requiredScopes: {}, + optionalScopes: {}, + }; + + // The values of these maps are references to immutable values, which is why + // a strict equality check is enough for diffing. The values are either from + // PermissionController state, or an empty object initialized in the previous + // call to this function. `newAuthorizationsMap` will never contain any empty + // objects. + if (previousAuthorizationsMap.get(origin) !== newAuthorizations) { + changedAuthorizations.set(origin, newAuthorizations); + } + + newOrigins.delete(origin); + } + + // By now, newOrigins is either empty or contains some number of previously + // unencountered origins, and all of their authorizations have "changed". + for (const origin of newOrigins.keys()) { + changedAuthorizations.set(origin, newAuthorizationsMap.get(origin)); + } + return changedAuthorizations; +}; + +/** + * + * @param {Map} newAuthorizationsMap - The new origin:authorization map. + * @param {Map} [previousAuthorizationsMap] - The previous origin:authorization map. + * @returns {Map} The origin:authorization map of changed authorizations. + */ +export const getRemovedAuthorizations = ( + newAuthorizationsMap, + previousAuthorizationsMap, +) => { + const removedAuthorizations = new Map(); + + // If there are no previous authorizations, there are no removed authorizations. + // OR If the new authorizations map is the same as the previous authorizations map, + // there are no removed authorizations + if ( + previousAuthorizationsMap === undefined || + newAuthorizationsMap === previousAuthorizationsMap + ) { + return removedAuthorizations; + } + + const previousOrigins = new Set([...previousAuthorizationsMap.keys()]); + for (const origin of newAuthorizationsMap.keys()) { + previousOrigins.delete(origin); + } + + for (const origin of previousOrigins.keys()) { + removedAuthorizations.set(origin, previousAuthorizationsMap.get(origin)); + } + + return removedAuthorizations; +}; diff --git a/app/scripts/controllers/permissions/selectors.test.js b/app/scripts/controllers/permissions/selectors.test.js index 41264d405ab2..5b667d1ac36b 100644 --- a/app/scripts/controllers/permissions/selectors.test.js +++ b/app/scripts/controllers/permissions/selectors.test.js @@ -1,11 +1,14 @@ import { cloneDeep } from 'lodash'; -import { CaveatTypes } from '../../../../shared/constants/permissions'; +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, +} from '@metamask/multichain'; import { diffMap, getPermittedAccountsByOrigin, getPermittedChainsByOrigin, + getRemovedAuthorizations, } from './selectors'; -import { PermissionNames } from './specifications'; describe('PermissionController selectors', () => { describe('diffMap', () => { @@ -53,25 +56,82 @@ describe('PermissionController selectors', () => { 'foo.bar': { origin: 'foo.bar', permissions: { - eth_accounts: { - caveats: [{ type: 'restrictReturnedAccounts', value: ['0x1'] }], + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x1'], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [ + 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', + ], + }, + }, + isMultichainOrigin: true, + }, + }, + ], }, }, }, 'bar.baz': { origin: 'bar.baz', permissions: { - eth_accounts: { - caveats: [{ type: 'restrictReturnedAccounts', value: ['0x2'] }], + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x2'], + }, + }, + isMultichainOrigin: false, + }, + }, + ], }, }, }, 'baz.bizz': { origin: 'baz.fizz', permissions: { - eth_accounts: { + [Caip25EndowmentPermissionName]: { caveats: [ - { type: 'restrictReturnedAccounts', value: ['0x1', '0x2'] }, + { + type: Caip25CaveatType, + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x1'], + }, + }, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x2'], + }, + }, + isMultichainOrigin: false, + }, + }, ], }, }, @@ -118,6 +178,37 @@ describe('PermissionController selectors', () => { }); }); + describe('getRemovedAuthorizations', () => { + it('returns an empty map if the new and previous values are the same', () => { + const newAuthorizations = new Map(); + expect( + getRemovedAuthorizations(newAuthorizations, newAuthorizations), + ).toStrictEqual(new Map()); + }); + + it('returns a new map of the removed authorizations if the new and previous values differ', () => { + const mockAuthorization = { + requiredScopes: { + 'eip155:1': { + methods: ['eth_sendTransaction'], + notifications: [], + }, + }, + optionalScopes: {}, + }; + const previousAuthorizations = new Map([ + ['foo.bar', mockAuthorization], + ['bar.baz', mockAuthorization], + ]); + + const newAuthorizations = new Map([['foo.bar', mockAuthorization]]); + + expect( + getRemovedAuthorizations(newAuthorizations, previousAuthorizations), + ).toStrictEqual(new Map([['bar.baz', mockAuthorization]])); + }); + }); + describe('getPermittedChainsByOrigin', () => { it('memoizes and gets permitted chains by origin', () => { const state1 = { @@ -125,11 +216,27 @@ describe('PermissionController selectors', () => { 'foo.bar': { origin: 'foo.bar', permissions: { - [PermissionNames.permittedChains]: { + [Caip25EndowmentPermissionName]: { caveats: [ { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x1'], + type: Caip25CaveatType, + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [], + }, + }, + optionalScopes: { + 'bip122:000000000019d6689c085ae165831e93': { + methods: [], + notifications: [], + accounts: [], + }, + }, + isMultichainOrigin: true, + }, }, ], }, @@ -138,11 +245,21 @@ describe('PermissionController selectors', () => { 'bar.baz': { origin: 'bar.baz', permissions: { - [PermissionNames.permittedChains]: { + [Caip25EndowmentPermissionName]: { caveats: [ { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x2'], + type: Caip25CaveatType, + value: { + requiredScopes: { + 'eip155:2': { + methods: [], + notifications: [], + accounts: [], + }, + }, + optionalScopes: {}, + isMultichainOrigin: true, + }, }, ], }, @@ -151,17 +268,33 @@ describe('PermissionController selectors', () => { 'baz.bizz': { origin: 'baz.fizz', permissions: { - [PermissionNames.permittedChains]: { + [Caip25EndowmentPermissionName]: { caveats: [ { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x1', '0x2'], + type: Caip25CaveatType, + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [], + }, + }, + optionalScopes: { + 'eip155:2': { + methods: [], + notifications: [], + accounts: [], + }, + }, + isMultichainOrigin: true, + }, }, ], }, }, }, - 'no.accounts': { + 'no.chains': { // we shouldn't see this in the result permissions: { foobar: {}, diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index 8a40082d4d80..43d8779b8f23 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -1,14 +1,13 @@ -import { - constructPermission, - PermissionType, -} from '@metamask/permission-controller'; import { caveatSpecifications as snapsCaveatsSpecifications, endowmentCaveatSpecifications as snapsEndowmentCaveatSpecifications, } from '@metamask/snaps-rpc-methods'; -import { isValidHexAddress } from '@metamask/utils'; import { - CaveatTypes, + Caip25CaveatFactoryFn, + Caip25CaveatType, + caip25EndowmentBuilder, +} from '@metamask/multichain'; +import { EndowmentTypes, RestrictedMethods, } from '../../../../shared/constants/permissions'; @@ -33,58 +32,18 @@ export const PermissionNames = Object.freeze({ * PermissionController. */ export const CaveatFactories = Object.freeze({ - [CaveatTypes.restrictReturnedAccounts]: (accounts) => { - return { type: CaveatTypes.restrictReturnedAccounts, value: accounts }; - }, - - [CaveatTypes.restrictNetworkSwitching]: (chainIds) => { - return { type: CaveatTypes.restrictNetworkSwitching, value: chainIds }; - }, + [Caip25CaveatType]: Caip25CaveatFactoryFn, }); /** * Gets the specifications for all caveats that will be recognized by the * PermissionController. - * - * @param {{ - * getInternalAccounts: () => Record, - * }} options - Options bag. */ -export const getCaveatSpecifications = ({ - getInternalAccounts, - findNetworkClientIdByChainId, -}) => { +export const getCaveatSpecifications = () => { return { - [CaveatTypes.restrictReturnedAccounts]: { - type: CaveatTypes.restrictReturnedAccounts, - - decorator: (method, caveat) => { - return async (args) => { - const result = await method(args); - return result.filter((account) => caveat.value.includes(account)); - }; - }, - - validator: (caveat, _origin, _target) => - validateCaveatAccounts(caveat.value, getInternalAccounts), - - merger: (leftValue, rightValue) => { - const newValue = Array.from(new Set([...leftValue, ...rightValue])); - const diff = newValue.filter((value) => !leftValue.includes(value)); - return [newValue, diff]; - }, + [Caip25CaveatType]: { + type: Caip25CaveatType, }, - [CaveatTypes.restrictNetworkSwitching]: { - type: CaveatTypes.restrictNetworkSwitching, - validator: (caveat, _origin, _target) => - validateCaveatNetworks(caveat.value, findNetworkClientIdByChainId), - merger: (leftValue, rightValue) => { - const newValue = Array.from(new Set([...leftValue, ...rightValue])); - const diff = newValue.filter((value) => !leftValue.includes(value)); - return [newValue, diff]; - }, - }, - ...snapsCaveatsSpecifications, ...snapsEndowmentCaveatSpecifications, }; @@ -108,213 +67,18 @@ export const getCaveatSpecifications = ({ * current MetaMask instance. */ export const getPermissionSpecifications = ({ - getAllAccounts, getInternalAccounts, - captureKeyringTypesWithMissingIdentities, + findNetworkClientIdByChainId, }) => { return { - [PermissionNames.eth_accounts]: { - permissionType: PermissionType.RestrictedMethod, - targetName: PermissionNames.eth_accounts, - allowedCaveats: [CaveatTypes.restrictReturnedAccounts], - - factory: (permissionOptions, requestData) => { - // This occurs when we use PermissionController.grantPermissions(). - if (requestData === undefined) { - return constructPermission({ - ...permissionOptions, - }); - } - - // The approved accounts will be further validated as part of the caveat. - if (!requestData.approvedAccounts) { - throw new Error( - `${PermissionNames.eth_accounts} error: No approved accounts specified.`, - ); - } - - return constructPermission({ - ...permissionOptions, - caveats: [ - CaveatFactories[CaveatTypes.restrictReturnedAccounts]( - requestData.approvedAccounts, - ), - ], - }); - }, - methodImplementation: async (_args) => { - // We only consider EVM addresses here, hence the filtering: - const accounts = (await getAllAccounts()).filter(isValidHexAddress); - const internalAccounts = getInternalAccounts(); - - return accounts.sort((firstAddress, secondAddress) => { - const firstAccount = internalAccounts.find( - (internalAccount) => - internalAccount.address.toLowerCase() === - firstAddress.toLowerCase(), - ); - - const secondAccount = internalAccounts.find( - (internalAccount) => - internalAccount.address.toLowerCase() === - secondAddress.toLowerCase(), - ); - - if (!firstAccount) { - captureKeyringTypesWithMissingIdentities( - internalAccounts, - accounts, - ); - throw new Error(`Missing identity for address: "${firstAddress}".`); - } else if (!secondAccount) { - captureKeyringTypesWithMissingIdentities( - internalAccounts, - accounts, - ); - throw new Error( - `Missing identity for address: "${secondAddress}".`, - ); - } else if ( - firstAccount.metadata.lastSelected === - secondAccount.metadata.lastSelected - ) { - return 0; - } else if (firstAccount.metadata.lastSelected === undefined) { - return 1; - } else if (secondAccount.metadata.lastSelected === undefined) { - return -1; - } - - return ( - secondAccount.metadata.lastSelected - - firstAccount.metadata.lastSelected - ); - }); - }, - validator: (permission, _origin, _target) => { - const { caveats } = permission; - if ( - !caveats || - caveats.length !== 1 || - caveats[0].type !== CaveatTypes.restrictReturnedAccounts - ) { - throw new Error( - `${PermissionNames.eth_accounts} error: Invalid caveats. There must be a single caveat of type "${CaveatTypes.restrictReturnedAccounts}".`, - ); - } - }, - }, - - [PermissionNames.permittedChains]: { - permissionType: PermissionType.Endowment, - targetName: PermissionNames.permittedChains, - allowedCaveats: [CaveatTypes.restrictNetworkSwitching], - - factory: (permissionOptions, requestData) => { - if (requestData === undefined) { - return constructPermission({ - ...permissionOptions, - }); - } - if (!requestData.approvedChainIds) { - throw new Error( - `${PermissionNames.permittedChains}: No approved networks specified.`, - ); - } - - return constructPermission({ - ...permissionOptions, - caveats: [ - CaveatFactories[CaveatTypes.restrictNetworkSwitching]( - requestData.approvedChainIds, - ), - ], - }); - }, - endowmentGetter: async (_getterOptions) => undefined, - validator: (permission, _origin, _target) => { - const { caveats } = permission; - if ( - !caveats || - caveats.length !== 1 || - caveats[0].type !== CaveatTypes.restrictNetworkSwitching - ) { - throw new Error( - `${PermissionNames.permittedChains} error: Invalid caveats. There must be a single caveat of type "${CaveatTypes.restrictNetworkSwitching}".`, - ); - } - }, - }, + [caip25EndowmentBuilder.targetName]: + caip25EndowmentBuilder.specificationBuilder({ + findNetworkClientIdByChainId, + getInternalAccounts, + }), }; }; -/** - * Validates the accounts associated with a caveat. In essence, ensures that - * the accounts value is an array of non-empty strings, and that each string - * corresponds to a PreferencesController identity. - * - * @param {string[]} accounts - The accounts associated with the caveat. - * @param {() => Record} getInternalAccounts - - * Gets all AccountsController InternalAccounts. - */ -function validateCaveatAccounts(accounts, getInternalAccounts) { - if (!Array.isArray(accounts) || accounts.length === 0) { - throw new Error( - `${PermissionNames.eth_accounts} error: Expected non-empty array of Ethereum addresses.`, - ); - } - - const internalAccounts = getInternalAccounts(); - accounts.forEach((address) => { - if (!address || typeof address !== 'string') { - throw new Error( - `${PermissionNames.eth_accounts} error: Expected an array of Ethereum addresses. Received: "${address}".`, - ); - } - - if ( - !internalAccounts.some( - (internalAccount) => - internalAccount.address.toLowerCase() === address.toLowerCase(), - ) - ) { - throw new Error( - `${PermissionNames.eth_accounts} error: Received unrecognized address: "${address}".`, - ); - } - }); -} - -/** - * Validates the networks associated with a caveat. Ensures that - * the networks value is an array of valid chain IDs. - * - * @param {string[]} chainIdsForCaveat - The list of chain IDs to validate. - * @param {function(string): string} findNetworkClientIdByChainId - Function to find network client ID by chain ID. - * @throws {Error} If the chainIdsForCaveat is not a non-empty array of valid chain IDs. - */ -function validateCaveatNetworks( - chainIdsForCaveat, - findNetworkClientIdByChainId, -) { - if (!Array.isArray(chainIdsForCaveat) || chainIdsForCaveat.length === 0) { - throw new Error( - `${PermissionNames.permittedChains} error: Expected non-empty array of chainIds.`, - ); - } - - chainIdsForCaveat.forEach((chainId) => { - try { - findNetworkClientIdByChainId(chainId); - } catch (e) { - console.error(e); - throw new Error( - `${PermissionNames.permittedChains} error: Received unrecognized chainId: "${chainId}". Please try adding the network first via wallet_addEthereumChain.`, - ); - } - }); -} - /** * Unrestricted methods for Ethereum, see {@link unrestrictedMethods} for more details. */ diff --git a/app/scripts/controllers/permissions/specifications.test.js b/app/scripts/controllers/permissions/specifications.test.js index b27ec07a45b1..e0b3f1623ccd 100644 --- a/app/scripts/controllers/permissions/specifications.test.js +++ b/app/scripts/controllers/permissions/specifications.test.js @@ -1,15 +1,11 @@ -import { EthAccountType } from '@metamask/keyring-api'; import { SnapCaveatType } from '@metamask/snaps-rpc-methods'; import { - CaveatTypes, - RestrictedMethods, -} from '../../../../shared/constants/permissions'; -import { ETH_EOA_METHODS } from '../../../../shared/constants/eth-methods'; + Caip25CaveatType, + Caip25EndowmentPermissionName, +} from '@metamask/multichain'; import { - CaveatFactories, getCaveatSpecifications, getPermissionSpecifications, - PermissionNames, unrestrictedMethods, } from './specifications'; @@ -20,13 +16,10 @@ describe('PermissionController specifications', () => { describe('caveat specifications', () => { it('getCaveatSpecifications returns the expected specifications object', () => { const caveatSpecifications = getCaveatSpecifications({}); - expect(Object.keys(caveatSpecifications)).toHaveLength(13); - expect( - caveatSpecifications[CaveatTypes.restrictReturnedAccounts].type, - ).toStrictEqual(CaveatTypes.restrictReturnedAccounts); - expect( - caveatSpecifications[CaveatTypes.restrictNetworkSwitching].type, - ).toStrictEqual(CaveatTypes.restrictNetworkSwitching); + expect(Object.keys(caveatSpecifications)).toHaveLength(12); + expect(caveatSpecifications[Caip25CaveatType].type).toStrictEqual( + Caip25CaveatType, + ); expect(caveatSpecifications.permittedDerivationPaths.type).toStrictEqual( SnapCaveatType.PermittedDerivationPaths, @@ -62,537 +55,15 @@ describe('PermissionController specifications', () => { SnapCaveatType.LookupMatchers, ); }); - - describe('restrictReturnedAccounts', () => { - describe('decorator', () => { - it('only returns array members included in the caveat value', async () => { - const getInternalAccounts = jest.fn(); - const { decorator } = getCaveatSpecifications({ - getInternalAccounts, - })[CaveatTypes.restrictReturnedAccounts]; - - const method = async () => ['0x1', '0x2', '0x3']; - const caveat = { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0x1', '0x3'], - }; - const decorated = decorator(method, caveat); - expect(await decorated()).toStrictEqual(['0x1', '0x3']); - }); - - it('returns an empty array if no array members are included in the caveat value', async () => { - const getInternalAccounts = jest.fn(); - const { decorator } = getCaveatSpecifications({ - getInternalAccounts, - })[CaveatTypes.restrictReturnedAccounts]; - - const method = async () => ['0x1', '0x2', '0x3']; - const caveat = { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0x5'], - }; - const decorated = decorator(method, caveat); - expect(await decorated()).toStrictEqual([]); - }); - - it('returns an empty array if the method result is an empty array', async () => { - const getInternalAccounts = jest.fn(); - const { decorator } = getCaveatSpecifications({ - getInternalAccounts, - })[CaveatTypes.restrictReturnedAccounts]; - - const method = async () => []; - const caveat = { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0x1', '0x2'], - }; - const decorated = decorator(method, caveat); - expect(await decorated()).toStrictEqual([]); - }); - }); - - describe('validator', () => { - it('rejects invalid array values', () => { - const getInternalAccounts = jest.fn(); - const { validator } = getCaveatSpecifications({ - getInternalAccounts, - })[CaveatTypes.restrictReturnedAccounts]; - - [null, 'foo', {}, []].forEach((invalidValue) => { - expect(() => validator({ value: invalidValue })).toThrow( - /Expected non-empty array of Ethereum addresses\.$/u, - ); - }); - }); - - it('rejects falsy or non-string addresses', () => { - const getInternalAccounts = jest.fn(); - const { validator } = getCaveatSpecifications({ - getInternalAccounts, - })[CaveatTypes.restrictReturnedAccounts]; - - [[{}], [[]], [null], ['']].forEach((invalidValue) => { - expect(() => validator({ value: invalidValue })).toThrow( - /Expected an array of Ethereum addresses. Received:/u, - ); - }); - }); - - it('rejects addresses that have no corresponding identity', () => { - const getInternalAccounts = jest.fn().mockImplementationOnce(() => { - return [ - { - address: '0x1', - id: '21066553-d8c8-4cdc-af33-efc921cd3ca9', - metadata: { - name: 'Test Account 1', - lastSelected: 1, - keyring: { - type: 'HD Key Tree', - }, - }, - options: {}, - methods: ETH_EOA_METHODS, - type: EthAccountType.Eoa, - }, - { - address: '0x3', - id: 'ff8fda69-d416-4d25-80a2-efb77bc7d4ad', - metadata: { - name: 'Test Account 3', - lastSelected: 3, - keyring: { - type: 'HD Key Tree', - }, - }, - options: {}, - methods: ETH_EOA_METHODS, - type: EthAccountType.Eoa, - }, - ]; - }); - - const { validator } = getCaveatSpecifications({ - getInternalAccounts, - })[CaveatTypes.restrictReturnedAccounts]; - - expect(() => validator({ value: ['0x1', '0x2', '0x3'] })).toThrow( - /Received unrecognized address:/u, - ); - }); - }); - - describe('merger', () => { - it.each([ - { - left: [], - right: [], - expected: [[], []], - }, - { - left: ['0x1'], - right: [], - expected: [['0x1'], []], - }, - { - left: [], - right: ['0x1'], - expected: [['0x1'], ['0x1']], - }, - { - left: ['0x1', '0x2'], - right: ['0x1', '0x2'], - expected: [['0x1', '0x2'], []], - }, - { - left: ['0x1', '0x2'], - right: ['0x2', '0x3'], - expected: [['0x1', '0x2', '0x3'], ['0x3']], - }, - { - left: ['0x1', '0x2'], - right: ['0x3', '0x4'], - expected: [ - ['0x1', '0x2', '0x3', '0x4'], - ['0x3', '0x4'], - ], - }, - { - left: [{ a: 1 }, { b: 2 }], - right: [{ a: 1 }], - expected: [[{ a: 1 }, { b: 2 }, { a: 1 }], [{ a: 1 }]], - }, - ])('merges arrays as expected', ({ left, right, expected }) => { - const { merger } = getCaveatSpecifications({})[ - CaveatTypes.restrictReturnedAccounts - ]; - - expect(merger(left, right)).toStrictEqual(expected); - }); - }); - }); }); describe('permission specifications', () => { it('getPermissionSpecifications returns the expected specifications object', () => { const permissionSpecifications = getPermissionSpecifications({}); - expect(Object.keys(permissionSpecifications)).toHaveLength(2); + expect(Object.keys(permissionSpecifications)).toHaveLength(1); expect( - permissionSpecifications[RestrictedMethods.eth_accounts].targetName, - ).toStrictEqual(RestrictedMethods.eth_accounts); - expect( - permissionSpecifications[PermissionNames.permittedChains].targetName, - ).toStrictEqual('endowment:permitted-chains'); - }); - - describe('eth_accounts', () => { - describe('factory', () => { - it('constructs a valid eth_accounts permission, using permissionOptions', () => { - const getInternalAccounts = jest.fn(); - const getAllAccounts = jest.fn(); - const { factory } = getPermissionSpecifications({ - getInternalAccounts, - getAllAccounts, - })[RestrictedMethods.eth_accounts]; - - expect( - factory({ - invoker: 'foo.bar', - target: 'eth_accounts', - caveats: [ - CaveatFactories[CaveatTypes.restrictReturnedAccounts](['0x1']), - ], - }), - ).toStrictEqual({ - caveats: [ - { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0x1'], - }, - ], - date: 1, - id: expect.any(String), - invoker: 'foo.bar', - parentCapability: 'eth_accounts', - }); - }); - - it('constructs a valid eth_accounts permission, using requestData.approvedAccounts', () => { - const getInternalAccounts = jest.fn(); - const getAllAccounts = jest.fn(); - const { factory } = getPermissionSpecifications({ - getInternalAccounts, - getAllAccounts, - })[RestrictedMethods.eth_accounts]; - - expect( - factory( - { invoker: 'foo.bar', target: 'eth_accounts' }, - { approvedAccounts: ['0x1'] }, - ), - ).toStrictEqual({ - caveats: [ - { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0x1'], - }, - ], - date: 1, - id: expect.any(String), - invoker: 'foo.bar', - parentCapability: 'eth_accounts', - }); - }); - - it('throws if requestData is defined but approvedAccounts is not specified', () => { - const getInternalAccounts = jest.fn(); - const getAllAccounts = jest.fn(); - const { factory } = getPermissionSpecifications({ - getInternalAccounts, - getAllAccounts, - })[RestrictedMethods.eth_accounts]; - - expect(() => - factory( - { invoker: 'foo.bar', target: 'eth_accounts' }, - {}, // no approvedAccounts - ), - ).toThrow(/No approved accounts specified\.$/u); - }); - - it('prefers requestData.approvedAccounts over a specified caveat', () => { - const getInternalAccounts = jest.fn(); - const getAllAccounts = jest.fn(); - const { factory } = getPermissionSpecifications({ - getInternalAccounts, - getAllAccounts, - })[RestrictedMethods.eth_accounts]; - - expect( - factory( - { - caveats: [ - CaveatFactories[CaveatTypes.restrictReturnedAccounts]([ - '0x1', - '0x2', - ]), - ], - invoker: 'foo.bar', - target: 'eth_accounts', - }, - { approvedAccounts: ['0x1', '0x3'] }, - ), - ).toStrictEqual({ - caveats: [ - { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0x1', '0x3'], - }, - ], - date: 1, - id: expect.any(String), - invoker: 'foo.bar', - parentCapability: 'eth_accounts', - }); - }); - }); - - describe('methodImplementation', () => { - it('returns the keyring accounts in lastSelected order', async () => { - const getInternalAccounts = jest.fn().mockImplementationOnce(() => { - return [ - { - address: '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', - id: '21066553-d8c8-4cdc-af33-efc921cd3ca9', - metadata: { - name: 'Test Account', - lastSelected: 1, - keyring: { - type: 'HD Key Tree', - }, - }, - options: {}, - methods: ETH_EOA_METHODS, - type: EthAccountType.Eoa, - }, - { - address: '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', - id: '0bd7348e-bdfe-4f67-875c-de831a583857', - metadata: { - name: 'Test Account', - keyring: { - type: 'HD Key Tree', - }, - }, - options: {}, - methods: ETH_EOA_METHODS, - type: EthAccountType.Eoa, - }, - { - address: '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', - id: 'ff8fda69-d416-4d25-80a2-efb77bc7d4ad', - metadata: { - name: 'Test Account', - keyring: { - type: 'HD Key Tree', - }, - lastSelected: 3, - }, - options: {}, - methods: ETH_EOA_METHODS, - type: EthAccountType.Eoa, - }, - { - address: '0x04eBa9B766477d8eCA77F5f0e67AE1863C95a7E3', - id: '0bd7348e-bdfe-4f67-875c-de831a583857', - metadata: { - name: 'Test Account', - lastSelected: 3, - keyring: { - type: 'HD Key Tree', - }, - }, - options: {}, - methods: ETH_EOA_METHODS, - type: EthAccountType.Eoa, - }, - ]; - }); - const getAllAccounts = jest - .fn() - .mockImplementationOnce(() => [ - '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', - '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', - '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', - '0x04eBa9B766477d8eCA77F5f0e67AE1863C95a7E3', - ]); - - const { methodImplementation } = getPermissionSpecifications({ - getInternalAccounts, - getAllAccounts, - })[RestrictedMethods.eth_accounts]; - - expect(await methodImplementation()).toStrictEqual([ - '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', - '0x04eBa9B766477d8eCA77F5f0e67AE1863C95a7E3', - '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', - '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', - ]); - }); - - it('throws if a keyring account is missing an address (case 1)', async () => { - const getInternalAccounts = jest.fn().mockImplementationOnce(() => { - return [ - { - address: '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', - id: '0bd7348e-bdfe-4f67-875c-de831a583857', - metadata: { - name: 'Test Account', - lastSelected: 2, - keyring: { - type: 'HD Key Tree', - }, - }, - options: {}, - methods: ETH_EOA_METHODS, - type: EthAccountType.Eoa, - }, - { - address: '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', - id: 'ff8fda69-d416-4d25-80a2-efb77bc7d4ad', - metadata: { - name: 'Test Account', - lastSelected: 3, - keyring: { - type: 'HD Key Tree', - }, - }, - options: {}, - methods: ETH_EOA_METHODS, - type: EthAccountType.Eoa, - }, - ]; - }); - const getAllAccounts = jest - .fn() - .mockImplementationOnce(() => [ - '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', - '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', - '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', - ]); - - const { methodImplementation } = getPermissionSpecifications({ - getInternalAccounts, - getAllAccounts, - captureKeyringTypesWithMissingIdentities: jest.fn(), - })[RestrictedMethods.eth_accounts]; - - await expect(() => methodImplementation()).rejects.toThrow( - 'Missing identity for address: "0x7A2Bd22810088523516737b4Dc238A4bC37c23F2".', - ); - }); - - it('throws if a keyring account is missing an address (case 2)', async () => { - const getInternalAccounts = jest.fn().mockImplementationOnce(() => { - return [ - { - address: '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', - id: 'cf8dace4-9439-4bd4-b3a8-88c821c8fcb3', - metadata: { - name: 'Test Account', - lastSelected: 1, - keyring: { - type: 'HD Key Tree', - }, - }, - options: {}, - methods: ETH_EOA_METHODS, - type: EthAccountType.Eoa, - }, - { - address: '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', - id: 'ff8fda69-d416-4d25-80a2-efb77bc7d4ad', - metadata: { - name: 'Test Account', - lastSelected: 3, - keyring: { - type: 'HD Key Tree', - }, - }, - options: {}, - methods: ETH_EOA_METHODS, - type: EthAccountType.Eoa, - }, - ]; - }); - const getAllAccounts = jest - .fn() - .mockImplementationOnce(() => [ - '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', - '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', - '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', - ]); - - const { methodImplementation } = getPermissionSpecifications({ - getInternalAccounts, - getAllAccounts, - captureKeyringTypesWithMissingIdentities: jest.fn(), - })[RestrictedMethods.eth_accounts]; - - await expect(() => methodImplementation()).rejects.toThrow( - 'Missing identity for address: "0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3".', - ); - }); - }); - - describe('validator', () => { - it('accepts valid permissions', () => { - const getInternalAccounts = jest.fn(); - const getAllAccounts = jest.fn(); - const { validator } = getPermissionSpecifications({ - getInternalAccounts, - getAllAccounts, - })[RestrictedMethods.eth_accounts]; - - expect(() => - validator({ - caveats: [ - { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0x1', '0x2'], - }, - ], - date: 1, - id: expect.any(String), - invoker: 'foo.bar', - parentCapability: 'eth_accounts', - }), - ).not.toThrow(); - }); - - it('rejects invalid caveats', () => { - const getInternalAccounts = jest.fn(); - const getAllAccounts = jest.fn(); - const { validator } = getPermissionSpecifications({ - getInternalAccounts, - getAllAccounts, - })[RestrictedMethods.eth_accounts]; - - [null, [], [1, 2], [{ type: 'foobar' }]].forEach( - (invalidCaveatsValue) => { - expect(() => - validator({ - caveats: invalidCaveatsValue, - date: 1, - id: expect.any(String), - invoker: 'foo.bar', - parentCapability: 'eth_accounts', - }), - ).toThrow(/Invalid caveats./u); - }, - ); - }); - }); + permissionSpecifications[Caip25EndowmentPermissionName].targetName, + ).toStrictEqual('endowment:caip25'); }); }); diff --git a/app/scripts/lib/multichain-api/wallet-getPermissions.js b/app/scripts/lib/multichain-api/wallet-getPermissions.js new file mode 100644 index 000000000000..6dd6d1f2ad0e --- /dev/null +++ b/app/scripts/lib/multichain-api/wallet-getPermissions.js @@ -0,0 +1,85 @@ +import { MethodNames } from '@metamask/permission-controller'; +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, + getPermittedEthChainIds, +} from '@metamask/multichain'; +import { + CaveatTypes, + RestrictedMethods, +} from '../../../../shared/constants/permissions'; +import { PermissionNames } from '../../controllers/permissions'; + +export const getPermissionsHandler = { + methodNames: [MethodNames.getPermissions], + implementation: getPermissionsImplementation, + hookNames: { + getPermissionsForOrigin: true, + getAccounts: true, + }, +}; + +/** + * Get Permissions implementation to be used in JsonRpcEngine middleware. + * + * @param _req - The JsonRpcEngine request - unused + * @param res - The JsonRpcEngine result object + * @param _next - JsonRpcEngine next() callback - unused + * @param end - JsonRpcEngine end() callback + * @param options - Method hooks passed to the method implementation + * @param options.getPermissionsForOrigin - The specific method hook needed for this method implementation + * @param options.getAccounts + * @returns A promise that resolves to nothing + */ +async function getPermissionsImplementation( + _req, + res, + _next, + end, + { getPermissionsForOrigin, getAccounts }, +) { + // permissions are frozen and must be cloned before modified + const permissions = { ...getPermissionsForOrigin() } || {}; + const caip25Endowment = permissions[Caip25EndowmentPermissionName]; + const caip25Caveat = caip25Endowment?.caveats?.find( + ({ type }) => type === Caip25CaveatType, + ); + delete permissions[Caip25EndowmentPermissionName]; + + if (caip25Caveat) { + // We cannot derive ethAccounts directly from the CAIP-25 permission + // because the accounts will not be in order of lastSelected + const ethAccounts = await getAccounts(); + + if (ethAccounts.length > 0) { + permissions[RestrictedMethods.eth_accounts] = { + ...caip25Endowment, + parentCapability: RestrictedMethods.eth_accounts, + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ethAccounts, + }, + ], + }; + } + + const ethChainIds = getPermittedEthChainIds(caip25Caveat.value); + + if (ethChainIds.length > 0) { + permissions[PermissionNames.permittedChains] = { + ...caip25Endowment, + parentCapability: PermissionNames.permittedChains, + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ethChainIds, + }, + ], + }; + } + } + + res.result = Object.values(permissions); + return end(); +} diff --git a/app/scripts/lib/multichain-api/wallet-getPermissions.test.js b/app/scripts/lib/multichain-api/wallet-getPermissions.test.js new file mode 100644 index 000000000000..b415a4ff7a47 --- /dev/null +++ b/app/scripts/lib/multichain-api/wallet-getPermissions.test.js @@ -0,0 +1,295 @@ +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, + getPermittedEthChainIds, +} from '@metamask/multichain'; +import { + CaveatTypes, + RestrictedMethods, +} from '../../../../shared/constants/permissions'; +import { PermissionNames } from '../../controllers/permissions'; +import { getPermissionsHandler } from './wallet-getPermissions'; + +jest.mock('@metamask/multichain', () => ({ + ...jest.requireActual('@metamask/multichain'), + getPermittedEthChainIds: jest.fn(), +})); + +const baseRequest = { + origin: 'http://test.com', +}; + +const createMockedHandler = () => { + const next = jest.fn(); + const end = jest.fn(); + const getPermissionsForOrigin = jest.fn().mockReturnValue( + Object.freeze({ + [Caip25EndowmentPermissionName]: { + id: '1', + parentCapability: Caip25EndowmentPermissionName, + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x1', 'eip155:1:0x2'], + }, + 'eip155:5': { + methods: [], + notifications: [], + accounts: ['eip155:5:0x1', 'eip155:5:0x3'], + }, + }, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0xdeadbeef'], + }, + }, + }, + }, + ], + }, + otherPermission: { + id: '2', + parentCapability: 'otherPermission', + caveats: [ + { + value: { + foo: 'bar', + }, + }, + ], + }, + }), + ); + const getAccounts = jest + .fn() + .mockResolvedValue(['0x1', '0x2', '0x3', '0xdeadbeef']); + const response = {}; + const handler = (request) => + getPermissionsHandler.implementation(request, response, next, end, { + getPermissionsForOrigin, + getAccounts, + }); + + return { + response, + next, + end, + getPermissionsForOrigin, + getAccounts, + handler, + }; +}; + +describe('getPermissionsHandler', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + beforeEach(() => { + getPermittedEthChainIds.mockReturnValue([]); + }); + + it('gets the permissions for the origin', async () => { + const { handler, getPermissionsForOrigin } = createMockedHandler(); + + await handler(baseRequest); + expect(getPermissionsForOrigin).toHaveBeenCalled(); + }); + + it('returns permissions unmodified if no CAIP-25 endowment permission has been granted', async () => { + const { handler, getPermissionsForOrigin, response } = + createMockedHandler(); + + getPermissionsForOrigin.mockReturnValue( + Object.freeze({ + otherPermission: { + id: '1', + parentCapability: 'otherPermission', + caveats: [ + { + value: { + foo: 'bar', + }, + }, + ], + }, + }), + ); + + await handler(baseRequest); + expect(response.result).toStrictEqual([ + { + id: '1', + parentCapability: 'otherPermission', + caveats: [ + { + value: { + foo: 'bar', + }, + }, + ], + }, + ]); + }); + + describe('CAIP-25 endowment permissions has been granted', () => { + it('returns the permissions with the CAIP-25 permission removed', async () => { + const { handler, getAccounts, response } = createMockedHandler(); + getAccounts.mockResolvedValue([]); + await handler(baseRequest); + expect(response.result).toStrictEqual([ + { + id: '2', + parentCapability: 'otherPermission', + caveats: [ + { + value: { + foo: 'bar', + }, + }, + ], + }, + ]); + }); + + it('gets the lastSelected sorted permissioned eth accounts for the origin', async () => { + const { handler, getAccounts } = createMockedHandler(); + await handler(baseRequest); + expect(getAccounts).toHaveBeenCalled(); + }); + + it('returns the permissions with an eth_accounts permission if some eth accounts are permissioned', async () => { + const { handler, response } = createMockedHandler(); + + await handler(baseRequest); + expect(response.result).toStrictEqual([ + { + id: '2', + parentCapability: 'otherPermission', + caveats: [ + { + value: { + foo: 'bar', + }, + }, + ], + }, + { + id: '1', + parentCapability: RestrictedMethods.eth_accounts, + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['0x1', '0x2', '0x3', '0xdeadbeef'], + }, + ], + }, + ]); + }); + + it('gets the permitted eip155 chainIds from the CAIP-25 caveat value', async () => { + const { handler } = createMockedHandler(); + await handler(baseRequest); + expect(getPermittedEthChainIds).toHaveBeenCalledWith({ + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x1', 'eip155:1:0x2'], + }, + 'eip155:5': { + methods: [], + notifications: [], + accounts: ['eip155:5:0x1', 'eip155:5:0x3'], + }, + }, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0xdeadbeef'], + }, + }, + }); + }); + + it('returns the permissions with a permittedChains permission if some eip155 chainIds are permissioned', async () => { + const { handler, getAccounts, response } = createMockedHandler(); + getAccounts.mockResolvedValue([]); + getPermittedEthChainIds.mockReturnValue(['0x1', '0x64']); + + await handler(baseRequest); + expect(response.result).toStrictEqual([ + { + id: '2', + parentCapability: 'otherPermission', + caveats: [ + { + value: { + foo: 'bar', + }, + }, + ], + }, + { + id: '1', + parentCapability: PermissionNames.permittedChains, + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1', '0x64'], + }, + ], + }, + ]); + }); + + it('returns the permissions with a eth_accounts and permittedChains permission if some eip155 accounts and chainIds are permissioned', async () => { + const { handler, getAccounts, response } = createMockedHandler(); + getAccounts.mockResolvedValue(['0x1', '0x2', '0xdeadbeef']); + getPermittedEthChainIds.mockReturnValue(['0x1', '0x64']); + + await handler(baseRequest); + expect(response.result).toStrictEqual([ + { + id: '2', + parentCapability: 'otherPermission', + caveats: [ + { + value: { + foo: 'bar', + }, + }, + ], + }, + { + id: '1', + parentCapability: RestrictedMethods.eth_accounts, + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['0x1', '0x2', '0xdeadbeef'], + }, + ], + }, + { + id: '1', + parentCapability: PermissionNames.permittedChains, + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1', '0x64'], + }, + ], + }, + ]); + }); + }); +}); diff --git a/app/scripts/lib/multichain-api/wallet-requestPermissions.js b/app/scripts/lib/multichain-api/wallet-requestPermissions.js new file mode 100644 index 000000000000..04f07456f81b --- /dev/null +++ b/app/scripts/lib/multichain-api/wallet-requestPermissions.js @@ -0,0 +1,205 @@ +import { pick } from 'lodash'; +import { isPlainObject } from '@metamask/controller-utils'; +import { invalidParams, MethodNames } from '@metamask/permission-controller'; +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, + setEthAccounts, + setPermittedEthChainIds, +} from '@metamask/multichain'; +import { + CaveatTypes, + RestrictedMethods, +} from '../../../../shared/constants/permissions'; +import { PermissionNames } from '../../controllers/permissions'; +// eslint-disable-next-line import/no-restricted-paths +import { isSnapId } from '../../../../ui/helpers/utils/snaps'; + +export const requestPermissionsHandler = { + methodNames: [MethodNames.requestPermissions], + implementation: requestPermissionsImplementation, + hookNames: { + requestPermissionsForOrigin: true, + getPermissionsForOrigin: true, + updateCaveat: true, + grantPermissions: true, + requestPermissionApprovalForOrigin: true, + getAccounts: true, + }, +}; + +/** + * Request Permissions implementation to be used in JsonRpcEngine middleware. + * + * @param req - The JsonRpcEngine request + * @param res - The JsonRpcEngine result object + * @param _next - JsonRpcEngine next() callback - unused + * @param end - JsonRpcEngine end() callback + * @param options - Method hooks passed to the method implementation + * @param options.requestPermissionsForOrigin - The specific method hook needed for this method implementation + * @param options.getPermissionsForOrigin + * @param options.updateCaveat + * @param options.grantPermissions + * @param options.requestPermissionApprovalForOrigin + * @param options.getAccounts + * @returns A promise that resolves to nothing + */ +async function requestPermissionsImplementation( + req, + res, + _next, + end, + { + requestPermissionsForOrigin, + getPermissionsForOrigin, + updateCaveat, + grantPermissions, + requestPermissionApprovalForOrigin, + getAccounts, + }, +) { + const { origin, params } = req; + + if (!Array.isArray(params) || !isPlainObject(params[0])) { + return end(invalidParams({ data: { request: req } })); + } + + const [requestedPermissions] = params; + delete requestedPermissions[Caip25EndowmentPermissionName]; + + const legacyRequestedPermissions = pick(requestedPermissions, [ + RestrictedMethods.eth_accounts, + PermissionNames.permittedChains, + ]); + delete requestedPermissions[RestrictedMethods.eth_accounts]; + delete requestedPermissions[PermissionNames.permittedChains]; + + // We manually handle eth_accounts and permittedChains permissions + // by calling the ApprovalController rather than the PermissionController + // because these two permissions do not actually exist in the Permssion + // Specifications. Calling the PermissionController with them will + // cause an error to be thrown. Instead, we will use the approval result + // from the ApprovalController to form a CAIP-25 permission later. + let legacyApproval; + const haveLegacyPermissions = + Object.keys(legacyRequestedPermissions).length > 0; + if (haveLegacyPermissions) { + if (!legacyRequestedPermissions[RestrictedMethods.eth_accounts]) { + legacyRequestedPermissions[RestrictedMethods.eth_accounts] = {}; + } + + if (!legacyRequestedPermissions[PermissionNames.permittedChains]) { + legacyRequestedPermissions[PermissionNames.permittedChains] = {}; + } + + if (isSnapId(origin)) { + delete legacyRequestedPermissions[PermissionNames.permittedChains]; + } + + legacyApproval = await requestPermissionApprovalForOrigin( + legacyRequestedPermissions, + ); + } + + let grantedPermissions = {}; + // Request permissions from the PermissionController for any permissions other + // than eth_accounts and permittedChains in the params. If no permissions + // are in the params, then request empty permissions from the PermissionController + // to get an appropriate error to be returned to the dapp. + if ( + (Object.keys(requestedPermissions).length === 0 && + !haveLegacyPermissions) || + Object.keys(requestedPermissions).length > 0 + ) { + const [_grantedPermissions] = await requestPermissionsForOrigin( + requestedPermissions, + ); + // permissions are frozen and must be cloned before modified + grantedPermissions = { ..._grantedPermissions }; + } + + if (legacyApproval) { + // NOTE: the eth_accounts/permittedChains approvals will be combined in the future. + // We assume that approvedAccounts and permittedChains are both defined here. + // Until they are actually combined, when testing, you must request both + // eth_accounts and permittedChains together. + let caveatValue = { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }; + if (!isSnapId(origin)) { + caveatValue = setPermittedEthChainIds( + caveatValue, + legacyApproval.approvedChainIds, + ); + } + + caveatValue = setEthAccounts(caveatValue, legacyApproval.approvedAccounts); + + const permissions = getPermissionsForOrigin(origin) || {}; + let caip25Endowment = permissions[Caip25EndowmentPermissionName]; + const existingCaveat = caip25Endowment?.caveats?.find( + ({ type }) => type === Caip25CaveatType, + ); + if (existingCaveat) { + if (existingCaveat.value.isMultichainOrigin) { + return end( + new Error('cannot modify permission granted from multichain flow'), + ); // TODO: better error + } + + updateCaveat( + origin, + Caip25EndowmentPermissionName, + Caip25CaveatType, + caveatValue, + ); + } else { + caip25Endowment = grantPermissions({ + subject: { origin }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: caveatValue, + }, + ], + }, + }, + })[Caip25EndowmentPermissionName]; + } + + // We cannot derive ethAccounts directly from the CAIP-25 permission + // because the accounts will not be in order of lastSelected + const ethAccounts = await getAccounts(); + + grantedPermissions[RestrictedMethods.eth_accounts] = { + ...caip25Endowment, + parentCapability: RestrictedMethods.eth_accounts, + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ethAccounts, + }, + ], + }; + + if (!isSnapId(origin)) { + grantedPermissions[PermissionNames.permittedChains] = { + ...caip25Endowment, + parentCapability: PermissionNames.permittedChains, + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: legacyApproval.approvedChainIds, + }, + ], + }; + } + } + + res.result = Object.values(grantedPermissions); + return end(); +} diff --git a/app/scripts/lib/multichain-api/wallet-requestPermissions.test.js b/app/scripts/lib/multichain-api/wallet-requestPermissions.test.js new file mode 100644 index 000000000000..312112cea39b --- /dev/null +++ b/app/scripts/lib/multichain-api/wallet-requestPermissions.test.js @@ -0,0 +1,715 @@ +import { invalidParams } from '@metamask/permission-controller'; +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, +} from '@metamask/multichain'; +import * as Multichain from '@metamask/multichain'; +import { + CaveatTypes, + RestrictedMethods, +} from '../../../../shared/constants/permissions'; +import { PermissionNames } from '../../controllers/permissions'; +import { requestPermissionsHandler } from './wallet-requestPermissions'; + +jest.mock('@metamask/multichain', () => ({ + ...jest.requireActual('@metamask/multichain'), + setEthAccounts: jest.fn(), + setPermittedEthChainIds: jest.fn(), +})); +const MockMultichain = jest.mocked(Multichain); + +const getBaseRequest = () => ({ + networkClientId: 'mainnet', + origin: 'http://test.com', + params: [ + { + eth_accounts: {}, + [Caip25EndowmentPermissionName]: {}, + otherPermission: {}, + }, + ], +}); + +const createMockedHandler = () => { + const next = jest.fn(); + const end = jest.fn(); + const requestPermissionsForOrigin = jest.fn().mockResolvedValue([ + Object.freeze({ + otherPermission: { + id: '2', + parentCapability: 'otherPermission', + caveats: [ + { + value: { + foo: 'bar', + }, + }, + ], + }, + }), + ]); + const getPermissionsForOrigin = jest.fn().mockReturnValue( + Object.freeze({ + [Caip25EndowmentPermissionName]: { + id: '1', + parentCapability: Caip25EndowmentPermissionName, + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x1', 'eip155:1:0x2'], + }, + 'eip155:5': { + methods: [], + notifications: [], + accounts: ['eip155:5:0x1', 'eip155:5:0x3'], + }, + }, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0x4'], + }, + 'other:1': { + methods: [], + notifications: [], + accounts: ['other:1:0x4'], + }, + }, + }, + isMultichainOrigin: false, + }, + ], + }, + }), + ); + const updateCaveat = jest.fn(); + const grantPermissions = jest.fn().mockReturnValue( + Object.freeze({ + [Caip25EndowmentPermissionName]: { + id: 'new', + parentCapability: Caip25EndowmentPermissionName, + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: {}, + }, + }, + ], + }, + }), + ); + const requestPermissionApprovalForOrigin = jest.fn().mockResolvedValue({ + approvedChainIds: ['0x1', '0x5'], + approvedAccounts: ['0xdeadbeef'], + }); + const getAccounts = jest.fn().mockResolvedValue([]); + const response = {}; + const handler = (request) => + requestPermissionsHandler.implementation(request, response, next, end, { + requestPermissionsForOrigin, + getPermissionsForOrigin, + updateCaveat, + grantPermissions, + requestPermissionApprovalForOrigin, + getAccounts, + request, + }); + + return { + response, + next, + end, + requestPermissionsForOrigin, + getPermissionsForOrigin, + updateCaveat, + grantPermissions, + requestPermissionApprovalForOrigin, + getAccounts, + handler, + }; +}; + +describe('requestPermissionsHandler', () => { + afterEach(() => { + jest.resetAllMocks(); + }); + + beforeEach(() => { + MockMultichain.setEthAccounts.mockImplementation( + (caveatValue) => caveatValue, + ); + MockMultichain.setPermittedEthChainIds.mockImplementation( + (caveatValue) => caveatValue, + ); + }); + + it('returns an error if params is malformed', async () => { + const { handler, end } = createMockedHandler(); + + const malformedRequest = { + ...getBaseRequest(), + params: [], + }; + await handler(malformedRequest); + expect(end).toHaveBeenCalledWith( + invalidParams({ data: { request: malformedRequest } }), + ); + }); + + it('requests approval from the ApprovalController for eth_accounts and permittedChains when only eth_accounts is specified in params and origin is not snapId', async () => { + const { handler, requestPermissionApprovalForOrigin } = + createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [ + { + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + }, + ], + }); + + expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + [PermissionNames.permittedChains]: {}, + }); + }); + + it('requests approval from the ApprovalController for eth_accounts and permittedChains when only permittedChains is specified in params and origin is not snapId', async () => { + const { handler, requestPermissionApprovalForOrigin } = + createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [ + { + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }, + ], + }); + + expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ + [RestrictedMethods.eth_accounts]: {}, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }); + }); + + it('requests approval from the ApprovalController for eth_accounts and permittedChains when both are specified in params and origin is not snapId', async () => { + const { handler, requestPermissionApprovalForOrigin } = + createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [ + { + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }, + ], + }); + + expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }); + }); + + it('requests approval from the ApprovalController for only eth_accounts when only eth_accounts is specified in params and origin is snapId', async () => { + const { handler, requestPermissionApprovalForOrigin } = + createMockedHandler(); + + await handler({ + ...getBaseRequest(), + origin: 'npm:snap', + params: [ + { + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + }, + ], + }); + + expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + }); + }); + + it('requests approval from the ApprovalController for only eth_accounts when only permittedChains is specified in params and origin is snapId', async () => { + const { handler, requestPermissionApprovalForOrigin } = + createMockedHandler(); + + await handler({ + ...getBaseRequest(), + origin: 'npm:snap', + params: [ + { + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }, + ], + }); + + expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ + [RestrictedMethods.eth_accounts]: {}, + }); + }); + + it('requests approval from the ApprovalController for only eth_accounts when both eth_accounts and permittedChains are specified in params and origin is snapId', async () => { + const { handler, requestPermissionApprovalForOrigin } = + createMockedHandler(); + + await handler({ + ...getBaseRequest(), + origin: 'npm:snap', + params: [ + { + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }, + ], + }); + + expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + }); + }); + + it('requests other permissions in params from the PermissionController, but ignores CAIP-25 if specified', async () => { + const { handler, requestPermissionsForOrigin } = createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [ + { + [Caip25EndowmentPermissionName]: {}, + otherPermission: {}, + }, + ], + }); + expect(requestPermissionsForOrigin).toHaveBeenCalledWith({ + otherPermission: {}, + }); + }); + + it('requests other permissions in params from the PermissionController, but ignores eth_accounts if specified', async () => { + const { handler, requestPermissionsForOrigin } = createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [ + { + [RestrictedMethods.eth_accounts]: {}, + otherPermission: {}, + }, + ], + }); + expect(requestPermissionsForOrigin).toHaveBeenCalledWith({ + otherPermission: {}, + }); + }); + + it('requests other permissions in params from the PermissionController, but ignores permittedChains if specified', async () => { + const { handler, requestPermissionsForOrigin } = createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [ + { + [PermissionNames.permittedChains]: {}, + otherPermission: {}, + }, + ], + }); + expect(requestPermissionsForOrigin).toHaveBeenCalledWith({ + otherPermission: {}, + }); + }); + + it('does not request permissions from the PermissionController when only eth_accounts is provided in params', async () => { + const { handler, requestPermissionsForOrigin } = createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [ + { + [RestrictedMethods.eth_accounts]: {}, + }, + ], + }); + expect(requestPermissionsForOrigin).not.toHaveBeenCalled(); + }); + + it('does not request permissions from the PermissionController when only permittedChains is provided in params', async () => { + const { handler, requestPermissionsForOrigin } = createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [ + { + [PermissionNames.permittedChains]: {}, + }, + ], + }); + expect(requestPermissionsForOrigin).not.toHaveBeenCalled(); + }); + + it('does not request permissions from the PermissionController when both eth_accounts and permittedChains are provided in params', async () => { + const { handler, requestPermissionsForOrigin } = createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [ + { + [RestrictedMethods.eth_accounts]: {}, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }, + ], + }); + expect(requestPermissionsForOrigin).not.toHaveBeenCalled(); + }); + + it('requests empty permissions from the PermissionController when only CAIP-25 permission is provided in params', async () => { + const { handler, requestPermissionsForOrigin } = createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [ + { + [Caip25EndowmentPermissionName]: {}, + }, + ], + }); + expect(requestPermissionsForOrigin).toHaveBeenCalledWith({}); + }); + + it('requests empty permissions from the PermissionController when no permissions are provided in params', async () => { + const { handler, requestPermissionsForOrigin } = createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [{}], + }); + expect(requestPermissionsForOrigin).toHaveBeenCalledWith({}); + }); + + it('does not update or grant a CAIP-25 endowment permission if eth_accounts and permittedChains approvals were not requested', async () => { + const { handler, updateCaveat, grantPermissions, getPermissionsForOrigin } = + createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [ + { + otherPermission: {}, + }, + ], + }); + expect(getPermissionsForOrigin).not.toHaveBeenCalled(); + expect(updateCaveat).not.toHaveBeenCalled(); + expect(grantPermissions).not.toHaveBeenCalled(); + }); + + it('returns the granted permissions if eth_accounts and permittedChains approvals were not requested', async () => { + const { handler, response } = createMockedHandler(); + + await handler({ + ...getBaseRequest(), + params: [ + { + otherPermission: {}, + }, + ], + }); + expect(response.result).toStrictEqual([ + { + caveats: [{ value: { foo: 'bar' } }], + id: '2', + parentCapability: 'otherPermission', + }, + ]); + }); + + it('does not update or grant a CAIP-25 endowment type permission if eth_accounts and permittedChains approvals were denied', async () => { + const { + handler, + updateCaveat, + grantPermissions, + getPermissionsForOrigin, + requestPermissionApprovalForOrigin, + } = createMockedHandler(); + requestPermissionApprovalForOrigin.mockRejectedValue( + new Error('user denied approval'), + ); + + try { + await handler({ + ...getBaseRequest(), + params: [ + { + [RestrictedMethods.eth_accounts]: {}, + }, + ], + }); + } catch (err) { + // noop + } + expect(getPermissionsForOrigin).not.toHaveBeenCalled(); + expect(updateCaveat).not.toHaveBeenCalled(); + expect(grantPermissions).not.toHaveBeenCalled(); + }); + + describe('eth_accounts and permittedChains approvals were accepted', () => { + it('sets the approved chainIds on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is not snapId', async () => { + const { handler } = createMockedHandler(); + + await handler(getBaseRequest()); + expect(MockMultichain.setPermittedEthChainIds).toHaveBeenCalledWith( + { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + ['0x1', '0x5'], + ); + }); + + it('sets the approved accounts on the CAIP-25 caveat after the approved chainIds if origin is not snapId', async () => { + const { handler } = createMockedHandler(); + MockMultichain.setPermittedEthChainIds.mockReturnValue( + 'caveatValueWithEthChainIdsSet', + ); + + await handler(getBaseRequest()); + expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( + 'caveatValueWithEthChainIdsSet', + ['0xdeadbeef'], + ); + }); + + it('does not set the approved chainIds on an empty CAIP-25 caveat if origin is snapId', async () => { + const { handler } = createMockedHandler(); + + await handler({ ...getBaseRequest(), origin: 'npm:snapm' }); + expect(MockMultichain.setPermittedEthChainIds).not.toHaveBeenCalled(); + }); + + it('sets the approved accounts on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is snapId', async () => { + const { handler } = createMockedHandler(); + + await handler({ ...getBaseRequest(), origin: 'npm:snapm' }); + expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( + { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + ['0xdeadbeef'], + ); + }); + + it('gets permission for the origin', async () => { + const { handler, getPermissionsForOrigin } = createMockedHandler(); + + await handler(getBaseRequest()); + expect(getPermissionsForOrigin).toHaveBeenCalledWith('http://test.com'); + }); + + it('throws an error when a CAIP-25 already exists that was granted from the multichain flow (isMultichainOrigin: true)', async () => { + const { handler, getPermissionsForOrigin, end } = createMockedHandler(); + getPermissionsForOrigin.mockReturnValue({ + [Caip25EndowmentPermissionName]: { + id: '1', + parentCapability: Caip25EndowmentPermissionName, + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: true, + }, + }, + ], + }, + }); + + await handler(getBaseRequest()); + expect(end).toHaveBeenCalledWith( + new Error('cannot modify permission granted from multichain flow'), + ); + }); + + it('updates the caveat when a CAIP-25 already exists that was granted from the legacy flow (isMultichainOrigin: false)', async () => { + const { handler, updateCaveat } = createMockedHandler(); + MockMultichain.setEthAccounts.mockReturnValue('updatedCaveatValue'); + + await handler(getBaseRequest()); + expect(updateCaveat).toHaveBeenCalledWith( + 'http://test.com', + Caip25EndowmentPermissionName, + Caip25CaveatType, + 'updatedCaveatValue', + ); + }); + + it('grants a CAIP-25 permission if one does not already exist', async () => { + const { handler, getPermissionsForOrigin, grantPermissions } = + createMockedHandler(); + getPermissionsForOrigin.mockReturnValue({}); + MockMultichain.setEthAccounts.mockReturnValue('updatedCaveatValue'); + + await handler(getBaseRequest()); + expect(grantPermissions).toHaveBeenCalledWith({ + subject: { + origin: 'http://test.com', + }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: 'updatedCaveatValue', + }, + ], + }, + }, + }); + }); + + it('gets the ordered eth accounts', async () => { + const { handler, getAccounts } = createMockedHandler(); + + await handler(getBaseRequest()); + expect(getAccounts).toHaveBeenCalled(); + }); + + it('returns both eth_accounts and permittedChains permissions in addition to other permissions that were granted if origin is not snapId', async () => { + const { handler, getAccounts, response } = createMockedHandler(); + getAccounts.mockResolvedValue(['0xdeadbeef']); + + await handler(getBaseRequest()); + expect(response.result).toStrictEqual([ + { + caveats: [{ value: { foo: 'bar' } }], + id: '2', + parentCapability: 'otherPermission', + }, + { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['0xdeadbeef'], + }, + ], + id: '1', + parentCapability: RestrictedMethods.eth_accounts, + }, + { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1', '0x5'], + }, + ], + id: '1', + parentCapability: PermissionNames.permittedChains, + }, + ]); + }); + + it('returns only eth_accounts permissions in addition to other permissions that were granted if origin is snapId', async () => { + const { handler, getAccounts, response } = createMockedHandler(); + getAccounts.mockResolvedValue(['0xdeadbeef']); + + await handler({ ...getBaseRequest(), origin: 'npm:snap' }); + expect(response.result).toStrictEqual([ + { + caveats: [{ value: { foo: 'bar' } }], + id: '2', + parentCapability: 'otherPermission', + }, + { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['0xdeadbeef'], + }, + ], + id: '1', + parentCapability: RestrictedMethods.eth_accounts, + }, + ]); + }); + }); +}); diff --git a/app/scripts/lib/multichain-api/wallet-revokePermissions.js b/app/scripts/lib/multichain-api/wallet-revokePermissions.js new file mode 100644 index 000000000000..bccee84c4e41 --- /dev/null +++ b/app/scripts/lib/multichain-api/wallet-revokePermissions.js @@ -0,0 +1,88 @@ +import { invalidParams, MethodNames } from '@metamask/permission-controller'; +import { isNonEmptyArray } from '@metamask/utils'; +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, +} from '@metamask/multichain'; +import { RestrictedMethods } from '../../../../shared/constants/permissions'; +import { PermissionNames } from '../../controllers/permissions'; + +export const revokePermissionsHandler = { + methodNames: [MethodNames.revokePermissions], + implementation: revokePermissionsImplementation, + hookNames: { + revokePermissionsForOrigin: true, + getPermissionsForOrigin: true, + updateCaveat: true, + }, +}; + +/** + * Revoke Permissions implementation to be used in JsonRpcEngine middleware. + * + * @param req - The JsonRpcEngine request + * @param res - The JsonRpcEngine result object + * @param _next - JsonRpcEngine next() callback - unused + * @param end - JsonRpcEngine end() callback + * @param options - Method hooks passed to the method implementation + * @param options.revokePermissionsForOrigin - A hook that revokes given permission keys for an origin + * @param options.getPermissionsForOrigin + * @returns A promise that resolves to nothing + */ +function revokePermissionsImplementation( + req, + res, + _next, + end, + { revokePermissionsForOrigin, getPermissionsForOrigin }, +) { + const { params, origin } = req; + + const param = params?.[0]; + + if (!param) { + return end(invalidParams({ data: { request: req } })); + } + + // For now, this API revokes the entire permission key + // even if caveats are specified. + const permissionKeys = Object.keys(param).filter( + (name) => name !== Caip25EndowmentPermissionName, + ); + + if (!isNonEmptyArray(permissionKeys)) { + return end(invalidParams({ data: { request: req } })); + } + + const relevantPermissionKeys = permissionKeys.filter( + (name) => + ![ + RestrictedMethods.eth_accounts, + PermissionNames.permittedChains, + ].includes(name), + ); + + const shouldRevokeLegacyPermission = + relevantPermissionKeys.length !== permissionKeys.length; + + if (shouldRevokeLegacyPermission) { + const permissions = getPermissionsForOrigin(origin) || {}; + const caip25Endowment = permissions?.[Caip25EndowmentPermissionName]; + const caip25Caveat = caip25Endowment?.caveats?.find( + ({ type }) => type === Caip25CaveatType, + ); + + if (caip25Caveat && caip25Caveat.value.isMultichainOrigin) { + return end( + new Error('cannot modify permission granted from multichain flow'), + ); // TODO: better error + } + relevantPermissionKeys.push(Caip25EndowmentPermissionName); + } + + revokePermissionsForOrigin(relevantPermissionKeys); + + res.result = null; + + return end(); +} diff --git a/app/scripts/lib/multichain-api/wallet-revokePermissions.test.js b/app/scripts/lib/multichain-api/wallet-revokePermissions.test.js new file mode 100644 index 000000000000..5d340527d857 --- /dev/null +++ b/app/scripts/lib/multichain-api/wallet-revokePermissions.test.js @@ -0,0 +1,204 @@ +import { invalidParams } from '@metamask/permission-controller'; +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, +} from '@metamask/multichain'; +import { PermissionNames } from '../../controllers/permissions'; +import { RestrictedMethods } from '../../../../shared/constants/permissions'; +import { revokePermissionsHandler } from './wallet-revokePermissions'; + +const baseRequest = { + origin: 'http://test.com', + params: [ + { + [Caip25EndowmentPermissionName]: {}, + otherPermission: {}, + }, + ], +}; + +const createMockedHandler = () => { + const next = jest.fn(); + const end = jest.fn(); + const revokePermissionsForOrigin = jest.fn(); + const getPermissionsForOrigin = jest.fn().mockReturnValue( + Object.freeze({ + [Caip25EndowmentPermissionName]: { + id: '1', + parentCapability: Caip25EndowmentPermissionName, + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + }, + ], + }, + }), + ); + const response = {}; + const handler = (request) => + revokePermissionsHandler.implementation(request, response, next, end, { + revokePermissionsForOrigin, + getPermissionsForOrigin, + }); + + return { + response, + next, + end, + revokePermissionsForOrigin, + getPermissionsForOrigin, + handler, + }; +}; + +describe('revokePermissionsHandler', () => { + it('returns an error if params is malformed', () => { + const { handler, end } = createMockedHandler(); + + const malformedRequest = { + ...baseRequest, + params: [], + }; + handler(malformedRequest); + expect(end).toHaveBeenCalledWith( + invalidParams({ data: { request: malformedRequest } }), + ); + }); + + it('returns an error if params are empty', () => { + const { handler, end } = createMockedHandler(); + + const emptyRequest = { + ...baseRequest, + params: [{}], + }; + handler(emptyRequest); + expect(end).toHaveBeenCalledWith( + invalidParams({ data: { request: emptyRequest } }), + ); + }); + + it('returns an error if params only the CAIP-25 permission is specified', () => { + const { handler, end } = createMockedHandler(); + + const emptyRequest = { + ...baseRequest, + params: [ + { + [Caip25EndowmentPermissionName]: {}, + }, + ], + }; + handler(emptyRequest); + expect(end).toHaveBeenCalledWith( + invalidParams({ data: { request: emptyRequest } }), + ); + }); + + describe.each([ + [RestrictedMethods.eth_accounts], + [PermissionNames.permittedChains], + ])('%s permission is specified', (permission) => { + it('gets permissions for the origin', () => { + const { handler, getPermissionsForOrigin } = createMockedHandler(); + + handler({ + ...baseRequest, + params: [ + { + [permission]: {}, + }, + ], + }); + expect(getPermissionsForOrigin).toHaveBeenCalled(); + }); + + it('revokes the CAIP-25 endowment permission', () => { + const { handler, revokePermissionsForOrigin } = createMockedHandler(); + + handler({ + ...baseRequest, + params: [ + { + [permission]: {}, + }, + ], + }); + expect(revokePermissionsForOrigin).toHaveBeenCalledWith([ + Caip25EndowmentPermissionName, + ]); + }); + + it('revokes other permissions specified', () => { + const { handler, revokePermissionsForOrigin } = createMockedHandler(); + + handler({ + ...baseRequest, + params: [ + { + [permission]: {}, + otherPermission: {}, + }, + ], + }); + expect(revokePermissionsForOrigin).toHaveBeenCalledWith([ + 'otherPermission', + Caip25EndowmentPermissionName, + ]); + }); + + it('throws an error when a CAIP-25 permission exists from the multichain flow (isMultichainOrigin: true)', () => { + const { handler, getPermissionsForOrigin, end } = createMockedHandler(); + getPermissionsForOrigin.mockReturnValue({ + [Caip25EndowmentPermissionName]: { + id: '1', + parentCapability: Caip25EndowmentPermissionName, + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: true, + }, + }, + ], + }, + }); + + handler({ + ...baseRequest, + params: [ + { + [permission]: {}, + otherPermission: {}, + }, + ], + }); + expect(end).toHaveBeenCalledWith( + new Error('cannot modify permission granted from multichain flow'), + ); + }); + }); + + it('revokes permissions other than eth_accounts, permittedChains, CAIP-25 if specified', () => { + const { handler, revokePermissionsForOrigin } = createMockedHandler(); + + handler(baseRequest); + expect(revokePermissionsForOrigin).toHaveBeenCalledWith([ + 'otherPermission', + ]); + }); + + it('returns null', () => { + const { handler, response } = createMockedHandler(); + + handler(baseRequest); + expect(response.result).toStrictEqual(null); + }); +}); diff --git a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js index e4b436163fc6..ed8abbb5d4c5 100644 --- a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js +++ b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js @@ -1,19 +1,35 @@ -import { permissionRpcMethods } from '@metamask/permission-controller'; import { selectHooks } from '@metamask/snaps-rpc-methods'; import { hasProperty } from '@metamask/utils'; import { ethErrors } from 'eth-rpc-errors'; -import { handlers as localHandlers, legacyHandlers } from './handlers'; -const allHandlers = [...localHandlers, ...permissionRpcMethods.handlers]; +import { getPermissionsHandler } from '../multichain-api/wallet-getPermissions'; +import { requestPermissionsHandler } from '../multichain-api/wallet-requestPermissions'; +import { revokePermissionsHandler } from '../multichain-api/wallet-revokePermissions'; +import { + handlers as localHandlers, + eip1193OnlyHandlers, + ethAccountsHandler, +} from './handlers'; -// The primary home of RPC method implementations in MetaMask. MUST be subsequent -// to our permissioning logic in the JSON-RPC middleware pipeline. -export const createMethodMiddleware = makeMethodMiddlewareMaker(allHandlers); +// The primary home of RPC method implementations for the injected 1193 provider API. MUST be subsequent +// to our permissioning logic in the EIP-1193 JSON-RPC middleware pipeline. +export const createEip1193MethodMiddleware = makeMethodMiddlewareMaker([ + ...localHandlers, + ...eip1193OnlyHandlers, + getPermissionsHandler, + requestPermissionsHandler, + revokePermissionsHandler, +]); // A collection of RPC method implementations that, for legacy reasons, MAY precede -// our permissioning logic in the JSON-RPC middleware pipeline. -export const createLegacyMethodMiddleware = - makeMethodMiddlewareMaker(legacyHandlers); +// our permissioning logic on the in the EIP-1193 JSON-RPC middleware pipeline. +export const createEthAccountsMethodMiddleware = makeMethodMiddlewareMaker([ + ethAccountsHandler, +]); + +// The primary home of RPC method implementations for the MultiChain API. +export const createMultichainMethodMiddleware = + makeMethodMiddlewareMaker(localHandlers); /** * Creates a method middleware factory function given a set of method handlers. diff --git a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.test.js b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.test.js index 46aba9abe746..fdcff0b459a6 100644 --- a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.test.js +++ b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.test.js @@ -3,49 +3,65 @@ import { assertIsJsonRpcFailure, assertIsJsonRpcSuccess, } from '@metamask/utils'; -import { createMethodMiddleware, createLegacyMethodMiddleware } from '.'; +import { + createEip1193MethodMiddleware, + createEthAccountsMethodMiddleware, + createMultichainMethodMiddleware, +} from '.'; + +const getHandler = () => ({ + implementation: (req, res, _next, end, hooks) => { + if (Array.isArray(req.params)) { + switch (req.params[0]) { + case 1: + res.result = hooks.hook1(); + break; + case 2: + res.result = hooks.hook2(); + break; + case 3: + return end(new Error('test error')); + case 4: + throw new Error('test error'); + case 5: + // eslint-disable-next-line no-throw-literal + throw 'foo'; + default: + throw new Error(`unexpected param "${req.params[0]}"`); + } + } + return end(); + }, + hookNames: { hook1: true, hook2: true }, + methodNames: ['method1', 'method2'], +}); jest.mock('@metamask/permission-controller', () => ({ - permissionRpcMethods: { handlers: [] }, + ...jest.requireActual('@metamask/permission-controller'), })); -jest.mock('./handlers', () => { - const getHandler = () => ({ - implementation: (req, res, _next, end, hooks) => { - if (Array.isArray(req.params)) { - switch (req.params[0]) { - case 1: - res.result = hooks.hook1(); - break; - case 2: - res.result = hooks.hook2(); - break; - case 3: - return end(new Error('test error')); - case 4: - throw new Error('test error'); - case 5: - // eslint-disable-next-line no-throw-literal - throw 'foo'; - default: - throw new Error(`unexpected param "${req.params[0]}"`); - } - } - return end(); - }, - hookNames: { hook1: true, hook2: true }, - methodNames: ['method1', 'method2'], - }); +jest.mock('../multichain-api/wallet-getPermissions', () => ({ + getPermissionsHandler: getHandler(), +})); - return { - handlers: [getHandler()], - legacyHandlers: [getHandler()], - }; -}); +jest.mock('../multichain-api/wallet-requestPermissions', () => ({ + requestPermissionsHandler: getHandler(), +})); + +jest.mock('../multichain-api/wallet-revokePermissions', () => ({ + revokePermissionsHandler: getHandler(), +})); + +jest.mock('./handlers', () => ({ + handlers: [getHandler()], + eip1193OnlyHandlers: [getHandler()], + ethAccountsHandler: getHandler(), +})); describe.each([ - ['createMethodMiddleware', createMethodMiddleware], - ['createLegacyMethodMiddleware', createLegacyMethodMiddleware], + ['createEip1193MethodMiddleware', createEip1193MethodMiddleware], + ['createEthAccountsMethodMiddleware', createEthAccountsMethodMiddleware], + ['createMultichainMethodMiddleware', createMultichainMethodMiddleware], ])('%s', (_name, createMiddleware) => { const method1 = 'method1'; diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js index 2f4727fdab36..38dad2e66592 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js @@ -22,8 +22,9 @@ const addEthereumChain = { endApprovalFlow: true, getCurrentChainIdForDomain: true, getCaveat: true, - requestPermittedChainsPermission: true, - grantPermittedChainsPermissionIncremental: true, + requestPermissionApprovalForOrigin: true, + updateCaveat: true, + grantPermissions: true, }, }; @@ -44,13 +45,14 @@ async function addEthereumChainHandler( endApprovalFlow, getCurrentChainIdForDomain, getCaveat, - requestPermittedChainsPermission, - grantPermittedChainsPermissionIncremental, + requestPermissionApprovalForOrigin, + updateCaveat, + grantPermissions, }, ) { let validParams; try { - validParams = validateAddEthereumChainParams(req.params[0], end); + validParams = validateAddEthereumChainParams(req.params[0]); } catch (error) { return end(error); } @@ -193,14 +195,23 @@ async function addEthereumChainHandler( const { networkClientId } = updatedNetwork.rpcEndpoints[updatedNetwork.defaultRpcEndpointIndex]; - return switchChain(res, end, chainId, networkClientId, approvalFlowId, { - isAddFlow: true, - setActiveNetwork, - endApprovalFlow, - getCaveat, - requestPermittedChainsPermission, - grantPermittedChainsPermissionIncremental, - }); + return switchChain( + res, + end, + origin, + chainId, + networkClientId, + approvalFlowId, + { + isAddFlow: true, + setActiveNetwork, + getCaveat, + requestPermissionApprovalForOrigin, + updateCaveat, + endApprovalFlow, + grantPermissions, + }, + ); } else if (approvalFlowId) { endApprovalFlow({ id: approvalFlowId }); } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js index 945953cff562..9a0249953ec4 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js @@ -1,6 +1,12 @@ import { ethErrors } from 'eth-rpc-errors'; import { CHAIN_IDS } from '../../../../../shared/constants/network'; import addEthereumChain from './add-ethereum-chain'; +import EthChainUtils from './ethereum-chain-utils'; + +jest.mock('./ethereum-chain-utils', () => ({ + ...jest.requireActual('./ethereum-chain-utils'), + switchChain: jest.fn(), +})); const NON_INFURA_CHAIN_ID = '0x123456789'; @@ -52,611 +58,236 @@ const createMockNonInfuraConfiguration = () => ({ defaultBlockExplorerUrlIndex: 0, }); -describe('addEthereumChainHandler', () => { - const addEthereumChainHandler = addEthereumChain.implementation; - const makeMocks = ({ permissionedChainIds = [], overrides = {} } = {}) => { - return { - getCurrentChainIdForDomain: jest - .fn() - .mockReturnValue(NON_INFURA_CHAIN_ID), - setNetworkClientIdForDomain: jest.fn(), - getNetworkConfigurationByChainId: jest.fn(), - setActiveNetwork: jest.fn(), - requestUserApproval: jest.fn().mockResolvedValue(123), - requestPermittedChainsPermission: jest.fn(), - grantPermittedChainsPermissionIncremental: jest.fn(), - getCaveat: jest.fn().mockReturnValue({ value: permissionedChainIds }), - startApprovalFlow: () => ({ id: 'approvalFlowId' }), - endApprovalFlow: jest.fn(), - addNetwork: jest.fn().mockResolvedValue({ - defaultRpcEndpointIndex: 0, - rpcEndpoints: [{ networkClientId: 123 }], - }), - updateNetwork: jest.fn().mockResolvedValue({ - defaultRpcEndpointIndex: 0, - rpcEndpoints: [{ networkClientId: 123 }], - }), - ...overrides, - }; +const createMockedHandler = () => { + const next = jest.fn(); + const end = jest.fn(); + const mocks = { + getCurrentChainIdForDomain: jest.fn().mockReturnValue(NON_INFURA_CHAIN_ID), + setNetworkClientIdForDomain: jest.fn(), + getNetworkConfigurationByChainId: jest.fn(), + setActiveNetwork: jest.fn(), + requestUserApproval: jest.fn().mockResolvedValue(123), + requestPermissionApprovalForOrigin: jest.fn(), + getCaveat: jest.fn(), + startApprovalFlow: () => ({ id: 'approvalFlowId' }), + endApprovalFlow: jest.fn(), + addNetwork: jest.fn().mockResolvedValue({ + defaultRpcEndpointIndex: 0, + rpcEndpoints: [{ networkClientId: 123 }], + }), + updateNetwork: jest.fn().mockResolvedValue({ + defaultRpcEndpointIndex: 0, + rpcEndpoints: [{ networkClientId: 123 }], + }), + updateCaveat: jest.fn(), + grantPermissions: jest.fn(), }; + const response = {}; + const handler = (request) => + addEthereumChain.implementation(request, response, next, end, mocks); + + return { + mocks, + response, + next, + end, + handler, + }; +}; +describe('addEthereumChainHandler', () => { afterEach(() => { jest.clearAllMocks(); }); - describe('with `endowment:permitted-chains` permissioning inactive', () => { - it('creates a new network configuration for the given chainid and switches to it if none exists', async () => { - const mocks = makeMocks(); - await addEthereumChainHandler( - { - origin: 'example.com', - params: [ - { - chainId: CHAIN_IDS.OPTIMISM, - chainName: 'Optimism Mainnet', - rpcUrls: ['https://optimism.llamarpc.com'], - nativeCurrency: { - symbol: 'ETH', - decimals: 18, - }, - blockExplorerUrls: ['https://optimistic.etherscan.io'], - iconUrls: ['https://optimism.icon.com'], - }, - ], - }, - {}, - jest.fn(), - jest.fn(), - mocks, - ); + it('creates a new network configuration for the given chainid and switches to it if no networkConfigurations with the same chainId exist', async () => { + const nonInfuraConfiguration = createMockNonInfuraConfiguration(); - expect(mocks.requestUserApproval).toHaveBeenCalledTimes(1); - expect(mocks.addNetwork).toHaveBeenCalledTimes(1); - expect(mocks.addNetwork).toHaveBeenCalledWith({ - blockExplorerUrls: ['https://optimistic.etherscan.io'], - defaultBlockExplorerUrlIndex: 0, - chainId: '0xa', - defaultRpcEndpointIndex: 0, - name: 'Optimism Mainnet', - nativeCurrency: 'ETH', - rpcEndpoints: [ - { - name: 'Optimism Mainnet', - url: 'https://optimism.llamarpc.com', - type: 'custom', + const { mocks, end, handler } = createMockedHandler(); + mocks.getCurrentChainIdForDomain.mockReturnValue(CHAIN_IDS.MAINNET); + + await handler({ + origin: 'example.com', + params: [ + { + chainId: nonInfuraConfiguration.chainId, + chainName: nonInfuraConfiguration.name, + rpcUrls: nonInfuraConfiguration.rpcEndpoints.map((rpc) => rpc.url), + nativeCurrency: { + symbol: nonInfuraConfiguration.nativeCurrency, + decimals: 18, }, - ], - }); - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith(123); + blockExplorerUrls: nonInfuraConfiguration.blockExplorerUrls, + }, + ], }); - it('creates a new networkConfiguration when called without "blockExplorerUrls" property', async () => { - const mocks = makeMocks(); - await addEthereumChainHandler( - { + expect(mocks.addNetwork).toHaveBeenCalledWith(nonInfuraConfiguration); + expect(EthChainUtils.switchChain).toHaveBeenCalledTimes(1); + expect(EthChainUtils.switchChain).toHaveBeenCalledWith( + {}, + end, + 'example.com', + NON_INFURA_CHAIN_ID, + 123, + 'approvalFlowId', + { + isAddFlow: true, + endApprovalFlow: mocks.endApprovalFlow, + getCaveat: mocks.getCaveat, + requestPermissionApprovalForOrigin: + mocks.requestPermissionApprovalForOrigin, + setActiveNetwork: mocks.setActiveNetwork, + updateCaveat: mocks.updateCaveat, + grantPermissions: mocks.grantPermissions, + }, + ); + }); + + describe('if a networkConfiguration for the given chainId already exists', () => { + describe('if the proposed networkConfiguration has a different rpcUrl from the one already in state', () => { + it('create a new networkConfiguration and switches to it', async () => { + const { mocks, end, handler } = createMockedHandler(); + mocks.getCurrentChainIdForDomain.mockReturnValue(CHAIN_IDS.SEPOLIA); + + await handler({ origin: 'example.com', params: [ { - chainId: CHAIN_IDS.OPTIMISM, - chainName: 'Optimism Mainnet', - rpcUrls: ['https://optimism.llamarpc.com'], + chainId: CHAIN_IDS.MAINNET, + chainName: 'Ethereum Mainnet', + rpcUrls: ['https://eth.llamarpc.com'], nativeCurrency: { symbol: 'ETH', decimals: 18, }, - iconUrls: ['https://optimism.icon.com'], - }, - ], - }, - {}, - jest.fn(), - jest.fn(), - mocks, - ); - expect(mocks.addNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - }); - - describe('if a networkConfiguration for the given chainId already exists', () => { - it('updates the existing networkConfiguration with the new rpc url if it doesnt already exist', async () => { - const mocks = makeMocks({ - overrides: { - getNetworkConfigurationByChainId: jest - .fn() - // Start with just infura endpoint - .mockReturnValue(createMockMainnetConfiguration()), - }, - }); - - // Add a custom endpoint - await addEthereumChainHandler( - { - origin: 'example.com', - params: [ - { - chainId: CHAIN_IDS.MAINNET, - chainName: 'Ethereum Mainnet', - rpcUrls: ['https://eth.llamarpc.com'], - nativeCurrency: { - symbol: 'ETH', - decimals: 18, - }, - blockExplorerUrls: ['https://etherscan.io'], - }, - ], - }, - {}, - jest.fn(), - jest.fn(), - mocks, - ); - - expect(mocks.updateNetwork).toHaveBeenCalledTimes(1); - expect(mocks.updateNetwork).toHaveBeenCalledWith( - '0x1', - { - chainId: '0x1', - name: 'Ethereum Mainnet', - // Expect both endpoints - rpcEndpoints: [ - { - networkClientId: 'mainnet', - url: 'https://mainnet.infura.io/v3/', - type: 'infura', - }, - { - name: 'Ethereum Mainnet', - url: 'https://eth.llamarpc.com', - type: 'custom', - }, - ], - // and the new one is the default - defaultRpcEndpointIndex: 1, - nativeCurrency: 'ETH', - blockExplorerUrls: ['https://etherscan.io'], - defaultBlockExplorerUrlIndex: 0, - }, - undefined, - ); - }); - - it('makes the rpc url the default if it already exists', async () => { - const existingNetwork = { - chainId: '0x1', - name: 'Ethereum Mainnet', - // Start with infura + custom endpoint - rpcEndpoints: [ - { - networkClientId: 'mainnet', - url: 'https://mainnet.infura.io/v3/', - type: 'infura', - }, - { - name: 'Ethereum Mainnet', - url: 'https://eth.llamarpc.com', - type: 'custom', + blockExplorerUrls: ['https://etherscan.io'], }, ], - // Infura is the default - defaultRpcEndpointIndex: 0, - nativeCurrency: 'ETH', - blockExplorerUrls: ['https://etherscan.io'], - defaultBlockExplorerUrlIndex: 0, - }; - - const mocks = makeMocks({ - overrides: { - getNetworkConfigurationByChainId: jest - .fn() - .mockReturnValue(existingNetwork), - }, }); - // Add the same custom endpoint - await addEthereumChainHandler( - { - origin: 'example.com', - params: [ - { - chainId: CHAIN_IDS.MAINNET, - chainName: 'Ethereum Mainnet', - rpcUrls: ['https://eth.llamarpc.com'], - nativeCurrency: { - symbol: 'ETH', - decimals: 18, - }, - blockExplorerUrls: ['https://etherscan.io'], - }, - ], - }, + expect(EthChainUtils.switchChain).toHaveBeenCalledTimes(1); + expect(EthChainUtils.switchChain).toHaveBeenCalledWith( {}, - jest.fn(), - jest.fn(), - mocks, - ); - - expect(mocks.updateNetwork).toHaveBeenCalledTimes(1); - expect(mocks.updateNetwork).toHaveBeenCalledWith( + end, + 'example.com', '0x1', + 123, + 'approvalFlowId', { - ...existingNetwork, - // Verify the custom endpoint becomes the default - defaultRpcEndpointIndex: 1, - }, - undefined, - ); - }); - - it('switches to the network if its not already the currently selected chain id', async () => { - const existingNetwork = createMockMainnetConfiguration(); - - const mocks = makeMocks({ - overrides: { - // Start on sepolia - getCurrentChainIdForDomain: jest - .fn() - .mockReturnValue(CHAIN_IDS.SEPOLIA), - getNetworkConfigurationByChainId: jest - .fn() - .mockReturnValue(existingNetwork), - }, - }); - - // Add with rpc + block explorers that already exist - await addEthereumChainHandler( - { - origin: 'example.com', - params: [ - { - chainId: CHAIN_IDS.MAINNET, - chainName: 'Ethereum Mainnet', - rpcUrls: [existingNetwork.rpcEndpoints[0].url], - nativeCurrency: { - symbol: 'ETH', - decimals: 18, - }, - blockExplorerUrls: ['https://etherscan.io'], - }, - ], + isAddFlow: true, + endApprovalFlow: mocks.endApprovalFlow, + getCaveat: mocks.getCaveat, + requestPermissionApprovalForOrigin: + mocks.requestPermissionApprovalForOrigin, + setActiveNetwork: mocks.setActiveNetwork, + updateCaveat: mocks.updateCaveat, + grantPermissions: mocks.grantPermissions, }, - {}, - jest.fn(), - jest.fn(), - mocks, ); - - // No updates, network already had all the info - expect(mocks.updateNetwork).toHaveBeenCalledTimes(0); - - // User should be prompted to switch chains - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); }); + }); - it('should return error for invalid chainId', async () => { - const mocks = makeMocks(); - const mockEnd = jest.fn(); - - await addEthereumChainHandler( + it('should switch to the existing networkConfiguration if one already exists for the given chain id', async () => { + const { mocks, end, handler } = createMockedHandler(); + mocks.getCurrentChainIdForDomain.mockReturnValue(CHAIN_IDS.MAINNET); + mocks.getNetworkConfigurationByChainId.mockReturnValue( + createMockOptimismConfiguration(), + ); + await handler({ + origin: 'example.com', + params: [ { - origin: 'example.com', - params: [{ chainId: 'invalid_chain_id' }], + chainId: createMockOptimismConfiguration().chainId, + chainName: createMockOptimismConfiguration().name, + rpcUrls: createMockOptimismConfiguration().rpcEndpoints.map( + (rpc) => rpc.url, + ), + nativeCurrency: { + symbol: createMockOptimismConfiguration().nativeCurrency, + decimals: 18, + }, + blockExplorerUrls: + createMockOptimismConfiguration().blockExplorerUrls, }, - {}, - jest.fn(), - mockEnd, - mocks, - ); - - expect(mockEnd).toHaveBeenCalledWith( - ethErrors.rpc.invalidParams({ - message: `Expected 0x-prefixed, unpadded, non-zero hexadecimal string 'chainId'. Received:\ninvalid_chain_id`, - }), - ); + ], }); - }); - }); - - describe('with `endowment:permitted-chains` permissioning active', () => { - it('creates a new network configuration for the given chainid, requests `endowment:permitted-chains` permission and switches to it if no networkConfigurations with the same chainId exist', async () => { - const nonInfuraConfiguration = createMockNonInfuraConfiguration(); - const mocks = makeMocks({ - permissionedChainIds: [], - overrides: { - getCurrentChainIdForDomain: jest - .fn() - .mockReturnValue(CHAIN_IDS.MAINNET), - }, - }); - await addEthereumChainHandler( + expect(EthChainUtils.switchChain).toHaveBeenCalledTimes(1); + expect(EthChainUtils.switchChain).toHaveBeenCalledWith( + {}, + end, + 'example.com', + '0xa', + createMockOptimismConfiguration().rpcEndpoints[0].networkClientId, + undefined, { - origin: 'example.com', - params: [ - { - chainId: nonInfuraConfiguration.chainId, - chainName: nonInfuraConfiguration.name, - rpcUrls: nonInfuraConfiguration.rpcEndpoints.map( - (rpc) => rpc.url, - ), - nativeCurrency: { - symbol: nonInfuraConfiguration.nativeCurrency, - decimals: 18, - }, - blockExplorerUrls: nonInfuraConfiguration.blockExplorerUrls, - }, - ], + isAddFlow: true, + endApprovalFlow: mocks.endApprovalFlow, + getCaveat: mocks.getCaveat, + requestPermissionApprovalForOrigin: + mocks.requestPermissionApprovalForOrigin, + setActiveNetwork: mocks.setActiveNetwork, + updateCaveat: mocks.updateCaveat, + grantPermissions: mocks.grantPermissions, }, - {}, - jest.fn(), - jest.fn(), - mocks, ); - - expect(mocks.addNetwork).toHaveBeenCalledWith(nonInfuraConfiguration); - expect( - mocks.grantPermittedChainsPermissionIncremental, - ).toHaveBeenCalledTimes(1); - expect( - mocks.grantPermittedChainsPermissionIncremental, - ).toHaveBeenCalledWith([createMockNonInfuraConfiguration().chainId]); - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith(123); - }); - - describe('if a networkConfiguration for the given chainId already exists', () => { - describe('if the proposed networkConfiguration has a different rpcUrl from the one already in state', () => { - it('create a new networkConfiguration and switches to it without requesting permissions, if the requested chainId has `endowment:permitted-chains` permission granted for requesting origin', async () => { - const mocks = makeMocks({ - permissionedChainIds: [CHAIN_IDS.MAINNET], - overrides: { - getCurrentChainIdForDomain: jest - .fn() - .mockReturnValue(CHAIN_IDS.SEPOLIA), - }, - }); - - await addEthereumChainHandler( - { - origin: 'example.com', - params: [ - { - chainId: CHAIN_IDS.MAINNET, - chainName: 'Ethereum Mainnet', - rpcUrls: ['https://eth.llamarpc.com'], - nativeCurrency: { - symbol: 'ETH', - decimals: 18, - }, - blockExplorerUrls: ['https://etherscan.io'], - }, - ], - }, - {}, - jest.fn(), - jest.fn(), - mocks, - ); - - expect(mocks.requestUserApproval).toHaveBeenCalledTimes(1); - expect(mocks.requestPermittedChainsPermission).not.toHaveBeenCalled(); - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith(123); - }); - - it('create a new networkConfiguration, requests permissions and switches to it, if the requested chainId does not have permittedChains permission granted for requesting origin', async () => { - const mocks = makeMocks({ - permissionedChainIds: [], - overrides: { - getNetworkConfigurationByChainId: jest - .fn() - .mockReturnValue(createMockNonInfuraConfiguration()), - getCurrentChainIdForDomain: jest - .fn() - .mockReturnValue(CHAIN_IDS.MAINNET), - }, - }); - - await addEthereumChainHandler( - { - origin: 'example.com', - params: [ - { - chainId: NON_INFURA_CHAIN_ID, - chainName: 'Custom Network', - rpcUrls: ['https://new-custom.network'], - nativeCurrency: { - symbol: 'CUST', - decimals: 18, - }, - blockExplorerUrls: ['https://custom.blockexplorer'], - }, - ], - }, - {}, - jest.fn(), - jest.fn(), - mocks, - ); - - expect(mocks.updateNetwork).toHaveBeenCalledTimes(1); - expect( - mocks.grantPermittedChainsPermissionIncremental, - ).toHaveBeenCalledTimes(1); - expect( - mocks.grantPermittedChainsPermissionIncremental, - ).toHaveBeenCalledWith([NON_INFURA_CHAIN_ID]); - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - }); - }); - - it('should switch to the existing networkConfiguration if one already exsits for the given chain id', async () => { - const mocks = makeMocks({ - permissionedChainIds: [ - createMockOptimismConfiguration().chainId, - CHAIN_IDS.MAINNET, - ], - overrides: { - getCurrentChainIdForDomain: jest - .fn() - .mockReturnValue(CHAIN_IDS.MAINNET), - getNetworkConfigurationByChainId: jest - .fn() - .mockReturnValue(createMockOptimismConfiguration()), - }, - }); - - await addEthereumChainHandler( - { - origin: 'example.com', - params: [ - { - chainId: createMockOptimismConfiguration().chainId, - chainName: createMockOptimismConfiguration().name, - rpcUrls: createMockOptimismConfiguration().rpcEndpoints.map( - (rpc) => rpc.url, - ), - nativeCurrency: { - symbol: createMockOptimismConfiguration().nativeCurrency, - decimals: 18, - }, - blockExplorerUrls: - createMockOptimismConfiguration().blockExplorerUrls, - }, - ], - }, - {}, - jest.fn(), - jest.fn(), - mocks, - ); - - expect(mocks.requestPermittedChainsPermission).not.toHaveBeenCalled(); - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockOptimismConfiguration().rpcEndpoints[0].networkClientId, - ); - }); }); }); it('should return an error if an unexpected parameter is provided', async () => { - const mocks = makeMocks(); - const mockEnd = jest.fn(); + const { end, handler } = createMockedHandler(); const unexpectedParam = 'unexpected'; - await addEthereumChainHandler( - { - origin: 'example.com', - params: [ - { - chainId: createMockNonInfuraConfiguration().chainId, - chainName: createMockNonInfuraConfiguration().nickname, - rpcUrls: [createMockNonInfuraConfiguration().rpcUrl], - nativeCurrency: { - symbol: createMockNonInfuraConfiguration().ticker, - decimals: 18, - }, - blockExplorerUrls: [ - createMockNonInfuraConfiguration().blockExplorerUrls[0], - ], - [unexpectedParam]: 'parameter', + await handler({ + origin: 'example.com', + params: [ + { + chainId: createMockNonInfuraConfiguration().chainId, + chainName: createMockNonInfuraConfiguration().nickname, + rpcUrls: [createMockNonInfuraConfiguration().rpcUrl], + nativeCurrency: { + symbol: createMockNonInfuraConfiguration().ticker, + decimals: 18, }, - ], - }, - {}, - jest.fn(), - mockEnd, - mocks, - ); + blockExplorerUrls: [ + createMockNonInfuraConfiguration().blockExplorerUrls[0], + ], + [unexpectedParam]: 'parameter', + }, + ], + }); - expect(mockEnd).toHaveBeenCalledWith( + expect(end).toHaveBeenCalledWith( ethErrors.rpc.invalidParams({ message: `Received unexpected keys on object parameter. Unsupported keys:\n${unexpectedParam}`, }), ); }); - it('should handle errors during the switch network permission request', async () => { - const mockError = new Error('Permission request failed'); - const mocks = makeMocks({ - permissionedChainIds: [], - overrides: { - getCurrentChainIdForDomain: jest - .fn() - .mockReturnValue(CHAIN_IDS.SEPOLIA), - grantPermittedChainsPermissionIncremental: jest - .fn() - .mockRejectedValue(mockError), - }, - }); - const mockEnd = jest.fn(); - - await addEthereumChainHandler( - { - origin: 'example.com', - params: [ - { - chainId: CHAIN_IDS.MAINNET, - chainName: 'Ethereum Mainnet', - rpcUrls: ['https://mainnet.infura.io/v3/'], - nativeCurrency: { - symbol: 'ETH', - decimals: 18, - }, - blockExplorerUrls: ['https://etherscan.io'], - }, - ], - }, - {}, - jest.fn(), - mockEnd, - mocks, - ); - - expect( - mocks.grantPermittedChainsPermissionIncremental, - ).toHaveBeenCalledTimes(1); - expect(mockEnd).toHaveBeenCalledWith(mockError); - expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); - }); - it('should return an error if nativeCurrency.symbol does not match an existing network with the same chainId', async () => { - const mocks = makeMocks({ - permissionedChainIds: [CHAIN_IDS.MAINNET], - overrides: { - getNetworkConfigurationByChainId: jest - .fn() - .mockReturnValue(createMockMainnetConfiguration()), - }, - }); - const mockEnd = jest.fn(); - - await addEthereumChainHandler( - { - origin: 'example.com', - params: [ - { - chainId: CHAIN_IDS.MAINNET, - chainName: 'Ethereum Mainnet', - rpcUrls: ['https://mainnet.infura.io/v3/'], - nativeCurrency: { - symbol: 'WRONG', - decimals: 18, - }, - blockExplorerUrls: ['https://etherscan.io'], - }, - ], - }, - {}, - jest.fn(), - mockEnd, - mocks, + const { mocks, end, handler } = createMockedHandler(); + mocks.getNetworkConfigurationByChainId.mockReturnValue( + createMockMainnetConfiguration(), ); + await handler({ + origin: 'example.com', + params: [ + { + chainId: CHAIN_IDS.MAINNET, + chainName: 'Ethereum Mainnet', + rpcUrls: ['https://mainnet.infura.io/v3/'], + nativeCurrency: { + symbol: 'WRONG', + decimals: 18, + }, + blockExplorerUrls: ['https://etherscan.io'], + }, + ], + }); - expect(mockEnd).toHaveBeenCalledWith( + expect(end).toHaveBeenCalledWith( ethErrors.rpc.invalidParams({ message: `nativeCurrency.symbol does not match currency symbol for a network the user already has added with the same chainId. Received:\nWRONG`, }), @@ -666,39 +297,26 @@ describe('addEthereumChainHandler', () => { it('should add result set to null to response object if the requested rpcUrl (and chainId) is currently selected', async () => { const CURRENT_RPC_CONFIG = createMockNonInfuraConfiguration(); - const mocks = makeMocks({ - overrides: { - getCurrentChainIdForDomain: jest - .fn() - .mockReturnValue(CURRENT_RPC_CONFIG.chainId), - getNetworkConfigurationByChainId: jest - .fn() - .mockReturnValue(CURRENT_RPC_CONFIG), - }, - }); - const res = {}; - - await addEthereumChainHandler( - { - origin: 'example.com', - params: [ - { - chainId: CURRENT_RPC_CONFIG.chainId, - chainName: 'Custom Network', - rpcUrls: [CURRENT_RPC_CONFIG.rpcEndpoints[0].url], - nativeCurrency: { - symbol: CURRENT_RPC_CONFIG.nativeCurrency, - decimals: 18, - }, - blockExplorerUrls: ['https://custom.blockexplorer'], - }, - ], - }, - res, - jest.fn(), - jest.fn(), - mocks, + const { mocks, response, handler } = createMockedHandler(); + mocks.getCurrentChainIdForDomain.mockReturnValue( + CURRENT_RPC_CONFIG.chainId, ); - expect(res.result).toBeNull(); + mocks.getNetworkConfigurationByChainId.mockReturnValue(CURRENT_RPC_CONFIG); + await handler({ + origin: 'example.com', + params: [ + { + chainId: CURRENT_RPC_CONFIG.chainId, + chainName: 'Custom Network', + rpcUrls: [CURRENT_RPC_CONFIG.rpcEndpoints[0].url], + nativeCurrency: { + symbol: CURRENT_RPC_CONFIG.nativeCurrency, + decimals: 18, + }, + blockExplorerUrls: ['https://custom.blockexplorer'], + }, + ], + }); + expect(response.result).toBeNull(); }); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index 080fef549564..eeaf246ee0df 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -1,15 +1,21 @@ import { errorCodes, ethErrors } from 'eth-rpc-errors'; +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, + getPermittedEthChainIds, + addPermittedEthChainId, +} from '@metamask/multichain'; import { isPrefixedFormattedHexString, isSafeChainId, } from '../../../../../shared/modules/network.utils'; -import { CaveatTypes } from '../../../../../shared/constants/permissions'; import { UNKNOWN_TICKER_SYMBOL } from '../../../../../shared/constants/app'; -import { PermissionNames } from '../../../controllers/permissions'; import { getValidUrl } from '../../util'; +import { CaveatTypes } from '../../../../../shared/constants/permissions'; +import { PermissionNames } from '../../../controllers/permissions'; export function validateChainId(chainId) { - const _chainId = typeof chainId === 'string' && chainId.toLowerCase(); + const _chainId = typeof chainId === 'string' ? chainId.toLowerCase() : ''; if (!isPrefixedFormattedHexString(_chainId)) { throw ethErrors.rpc.invalidParams({ message: `Expected 0x-prefixed, unpadded, non-zero hexadecimal string 'chainId'. Received:\n${chainId}`, @@ -25,7 +31,7 @@ export function validateChainId(chainId) { return _chainId; } -export function validateSwitchEthereumChainParams(req, end) { +export function validateSwitchEthereumChainParams(req) { if (!req.params?.[0] || typeof req.params[0] !== 'object') { throw ethErrors.rpc.invalidParams({ message: `Expected single, object parameter. Received:\n${JSON.stringify( @@ -43,10 +49,10 @@ export function validateSwitchEthereumChainParams(req, end) { }); } - return validateChainId(chainId, end); + return validateChainId(chainId); } -export function validateAddEthereumChainParams(params, end) { +export function validateAddEthereumChainParams(params) { if (!params || typeof params !== 'object') { throw ethErrors.rpc.invalidParams({ message: `Expected single, object parameter. Received:\n${JSON.stringify( @@ -75,7 +81,7 @@ export function validateAddEthereumChainParams(params, end) { }); } - const _chainId = validateChainId(chainId, end); + const _chainId = validateChainId(chainId); if (!rpcUrls || !Array.isArray(rpcUrls) || rpcUrls.length === 0) { throw ethErrors.rpc.invalidParams({ message: `Expected an array with at least one valid string HTTPS url 'rpcUrls', Received:\n${rpcUrls}`, @@ -155,6 +161,7 @@ export function validateAddEthereumChainParams(params, end) { export async function switchChain( res, end, + origin, chainId, networkClientId, approvalFlowId, @@ -163,26 +170,90 @@ export async function switchChain( setActiveNetwork, endApprovalFlow, getCaveat, - requestPermittedChainsPermission, - grantPermittedChainsPermissionIncremental, + requestPermissionApprovalForOrigin, + updateCaveat, + grantPermissions, }, ) { try { - const { value: permissionedChainIds } = - getCaveat({ - target: PermissionNames.permittedChains, - caveatType: CaveatTypes.restrictNetworkSwitching, - }) ?? {}; + const caip25Caveat = getCaveat({ + target: Caip25EndowmentPermissionName, + caveatType: Caip25CaveatType, + }); - if ( - permissionedChainIds === undefined || - !permissionedChainIds.includes(chainId) - ) { - if (isAddFlow) { - await grantPermittedChainsPermissionIncremental([chainId]); - } else { - await requestPermittedChainsPermission([chainId]); + if (caip25Caveat) { + const ethChainIds = getPermittedEthChainIds(caip25Caveat.value); + + if (!ethChainIds.includes(chainId)) { + if (caip25Caveat.value.isMultichainOrigin) { + return end( + new Error( + 'cannot switch to chain that was not permissioned in the multichain flow', + ), + ); // TODO: better error + } + + // TODO: This behavior may have deviated from the original permittedChains add chain behavior + // Verify that this helper behaves as expected + if (!isAddFlow) { + await requestPermissionApprovalForOrigin({ + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: [chainId], + }, + ], + }, + }); + } + + const updatedCaveatValue = addPermittedEthChainId( + caip25Caveat.value, + chainId, + ); + + updateCaveat( + origin, + Caip25EndowmentPermissionName, + Caip25CaveatType, + updatedCaveatValue, + ); } + } else { + if (!isAddFlow) { + await requestPermissionApprovalForOrigin({ + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: [chainId], + }, + ], + }, + }); + } + + let caveatValue = { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }; + caveatValue = addPermittedEthChainId(caveatValue, chainId); + + grantPermissions({ + subject: { origin }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: caveatValue, + }, + ], + }, + }, + }); } await setActiveNetwork(networkClientId); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/index.ts b/app/scripts/lib/rpc-method-middleware/handlers/index.ts index 09bca12b5b67..521cb32bec64 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/index.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/index.ts @@ -20,9 +20,7 @@ export const handlers = [ addEthereumChain, getProviderState, logWeb3ShimUsage, - requestAccounts, sendMetadata, - switchEthereumChain, watchAsset, ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) mmiAuthenticate, @@ -34,4 +32,10 @@ export const handlers = [ ///: END:ONLY_INCLUDE_IF ]; -export const legacyHandlers = [ethAccounts]; +export const eip1193OnlyHandlers = [ + switchEthereumChain, + ethAccounts, + requestAccounts, +]; + +export const ethAccountsHandler = ethAccounts; diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.js b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.js index f90fb5bd0d42..e8f8a70c0633 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.js @@ -1,10 +1,20 @@ import { ethErrors } from 'eth-rpc-errors'; +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, + setEthAccounts, + setPermittedEthChainIds, +} from '@metamask/multichain'; import { MESSAGE_TYPE } from '../../../../../shared/constants/app'; import { MetaMetricsEventName, MetaMetricsEventCategory, } from '../../../../../shared/constants/metametrics'; import { shouldEmitDappViewedEvent } from '../../util'; +import { RestrictedMethods } from '../../../../../shared/constants/permissions'; +import { PermissionNames } from '../../../controllers/permissions'; +// eslint-disable-next-line import/no-restricted-paths +import { isSnapId } from '../../../../../ui/helpers/utils/snaps'; /** * This method attempts to retrieve the Ethereum accounts available to the @@ -18,14 +28,12 @@ const requestEthereumAccounts = { methodNames: [MESSAGE_TYPE.ETH_REQUEST_ACCOUNTS], implementation: requestEthereumAccountsHandler, hookNames: { - origin: true, getAccounts: true, getUnlockPromise: true, - hasPermission: true, - requestAccountsPermission: true, + requestPermissionApprovalForOrigin: true, sendMetrics: true, - getPermissionsForOrigin: true, metamaskState: true, + grantPermissions: true, }, }; export default requestEthereumAccounts; @@ -35,7 +43,6 @@ const locks = new Set(); /** * @typedef {Record} RequestEthereumAccountsOptions - * @property {string} origin - The requesting origin. * @property {Function} getAccounts - Gets the accounts for the requesting * origin. * @property {Function} getUnlockPromise - Gets a promise that resolves when @@ -48,28 +55,27 @@ const locks = new Set(); /** * - * @param {import('json-rpc-engine').JsonRpcRequest} _req - The JSON-RPC request object. + * @param {import('json-rpc-engine').JsonRpcRequest} req - The JSON-RPC request object. * @param {import('json-rpc-engine').JsonRpcResponse} res - The JSON-RPC response object. * @param {Function} _next - The json-rpc-engine 'next' callback. * @param {Function} end - The json-rpc-engine 'end' callback. * @param {RequestEthereumAccountsOptions} options - The RPC method hooks. */ async function requestEthereumAccountsHandler( - _req, + req, res, _next, end, { - origin, getAccounts, getUnlockPromise, - hasPermission, - requestAccountsPermission, + requestPermissionApprovalForOrigin, sendMetrics, - getPermissionsForOrigin, metamaskState, + grantPermissions, }, ) { + const { origin } = req; if (locks.has(origin)) { res.error = ethErrors.rpc.resourceUnavailable( `Already processing ${MESSAGE_TYPE.ETH_REQUEST_ACCOUNTS}. Please wait.`, @@ -77,14 +83,15 @@ async function requestEthereumAccountsHandler( return end(); } - if (hasPermission(MESSAGE_TYPE.ETH_ACCOUNTS)) { + let ethAccounts = await getAccounts(); + if (ethAccounts.length > 0) { // We wait for the extension to unlock in this case only, because permission // requests are handled when the extension is unlocked, regardless of the // lock state when they were received. try { locks.add(origin); await getUnlockPromise(true); - res.result = await getAccounts(); + res.result = ethAccounts; end(); } catch (error) { end(error); @@ -94,48 +101,77 @@ async function requestEthereumAccountsHandler( return undefined; } - // If no accounts, request the accounts permission + let legacyApproval; try { - await requestAccountsPermission(); + legacyApproval = await requestPermissionApprovalForOrigin({ + [RestrictedMethods.eth_accounts]: {}, + ...(!isSnapId(origin) && { + [PermissionNames.permittedChains]: {}, + }), + }); } catch (err) { res.error = err; return end(); } - // Get the approved accounts - const accounts = await getAccounts(); - /* istanbul ignore else: too hard to induce, see below comment */ - if (accounts.length > 0) { - res.result = accounts; - const numberOfConnectedAccounts = - getPermissionsForOrigin(origin).eth_accounts.caveats[0].value.length; - // first time connection to dapp will lead to no log in the permissionHistory - // and if user has connected to dapp before, the dapp origin will be included in the permissionHistory state - // we will leverage that to identify `is_first_visit` for metrics + // NOTE: the eth_accounts/permittedChains approvals will be combined in the future. + // We assume that approvedAccounts and permittedChains are both defined here. + // Until they are actually combined, when testing, you must request both + // eth_accounts and permittedChains together. + let caveatValue = { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }; + + if (!isSnapId(origin)) { + caveatValue = setPermittedEthChainIds( + caveatValue, + legacyApproval.approvedChainIds, + ); + } + + caveatValue = setEthAccounts(caveatValue, legacyApproval.approvedAccounts); + + grantPermissions({ + subject: { origin }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: caveatValue, + }, + ], + }, + }, + }); + + ethAccounts = await getAccounts(); + // first time connection to dapp will lead to no log in the permissionHistory + // and if user has connected to dapp before, the dapp origin will be included in the permissionHistory state + // we will leverage that to identify `is_first_visit` for metrics + if (shouldEmitDappViewedEvent(metamaskState.metaMetricsId)) { const isFirstVisit = !Object.keys(metamaskState.permissionHistory).includes( origin, ); - if (shouldEmitDappViewedEvent(metamaskState.metaMetricsId)) { - sendMetrics({ - event: MetaMetricsEventName.DappViewed, - category: MetaMetricsEventCategory.InpageProvider, - referrer: { - url: origin, - }, - properties: { - is_first_visit: isFirstVisit, - number_of_accounts: Object.keys(metamaskState.accounts).length, - number_of_accounts_connected: numberOfConnectedAccounts, - }, - }); - } - } else { - // This should never happen, because it should be caught in the - // above catch clause - res.error = ethErrors.rpc.internal( - 'Accounts unexpectedly unavailable. Please report this bug.', - ); + sendMetrics({ + event: MetaMetricsEventName.DappViewed, + category: MetaMetricsEventCategory.InpageProvider, + referrer: { + url: origin, + }, + properties: { + is_first_visit: isFirstVisit, + number_of_accounts: Object.keys(metamaskState.accounts).length, + number_of_accounts_connected: ethAccounts.length, + }, + }); } + // We cannot derive ethAccounts directly from the CAIP-25 permission + // because the accounts will not be in order of lastSelected + res.result = ethAccounts; + return end(); } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js index f43973e4ba57..57c4378989aa 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js @@ -12,9 +12,10 @@ const switchEthereumChain = { getNetworkConfigurationByChainId: true, setActiveNetwork: true, getCaveat: true, - requestPermittedChainsPermission: true, getCurrentChainIdForDomain: true, - grantPermittedChainsPermissionIncremental: true, + requestPermissionApprovalForOrigin: true, + updateCaveat: true, + grantPermissions: true, }, }; @@ -28,15 +29,16 @@ async function switchEthereumChainHandler( { getNetworkConfigurationByChainId, setActiveNetwork, - requestPermittedChainsPermission, getCaveat, getCurrentChainIdForDomain, - grantPermittedChainsPermissionIncremental, + requestPermissionApprovalForOrigin, + updateCaveat, + grantPermissions, }, ) { let chainId; try { - chainId = validateSwitchEthereumChainParams(req, end); + chainId = validateSwitchEthereumChainParams(req); } catch (error) { return end(error); } @@ -64,10 +66,19 @@ async function switchEthereumChainHandler( ); } - return switchChain(res, end, chainId, networkClientIdToSwitchTo, null, { - setActiveNetwork, - getCaveat, - requestPermittedChainsPermission, - grantPermittedChainsPermissionIncremental, - }); + return switchChain( + res, + end, + origin, + chainId, + networkClientIdToSwitchTo, + null, + { + setActiveNetwork, + getCaveat, + updateCaveat, + requestPermissionApprovalForOrigin, + grantPermissions, + }, + ); } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js index be612fbc7d8e..c17d8dc6cc94 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js @@ -1,8 +1,15 @@ +import { ethErrors } from 'eth-rpc-errors'; import { CHAIN_IDS, NETWORK_TYPES, } from '../../../../../shared/constants/network'; import switchEthereumChain from './switch-ethereum-chain'; +import EthChainUtils from './ethereum-chain-utils'; + +jest.mock('./ethereum-chain-utils', () => ({ + ...jest.requireActual('./ethereum-chain-utils'), + switchChain: jest.fn(), +})); const NON_INFURA_CHAIN_ID = '0x123456789'; @@ -26,257 +33,143 @@ const createMockLineaMainnetConfiguration = () => ({ ], }); -describe('switchEthereumChainHandler', () => { - const makeMocks = ({ - permissionedChainIds = [], - overrides = {}, - mockedGetNetworkConfigurationByChainIdReturnValue = createMockMainnetConfiguration(), - mockedGetCurrentChainIdForDomainReturnValue = NON_INFURA_CHAIN_ID, - } = {}) => { - const mockGetCaveat = jest.fn(); - mockGetCaveat.mockReturnValue({ value: permissionedChainIds }); - - return { - getCurrentChainIdForDomain: jest - .fn() - .mockReturnValue(mockedGetCurrentChainIdForDomainReturnValue), - setNetworkClientIdForDomain: jest.fn(), - setActiveNetwork: jest.fn(), - requestPermittedChainsPermission: jest.fn(), - getCaveat: mockGetCaveat, - getNetworkConfigurationByChainId: jest - .fn() - .mockReturnValue(mockedGetNetworkConfigurationByChainIdReturnValue), - ...overrides, - }; +const createMockedHandler = () => { + const next = jest.fn(); + const end = jest.fn(); + const mocks = { + getNetworkConfigurationByChainId: jest + .fn() + .mockReturnValue(createMockMainnetConfiguration()), + setActiveNetwork: jest.fn(), + getCaveat: jest.fn(), + getCurrentChainIdForDomain: jest.fn().mockReturnValue(NON_INFURA_CHAIN_ID), + requestPermissionApprovalForOrigin: jest.fn(), + updateCaveat: jest.fn(), + grantPermissions: jest.fn(), + }; + const response = {}; + const handler = (request) => + switchEthereumChain.implementation(request, response, next, end, mocks); + + return { + mocks, + response, + next, + end, + handler, }; +}; +describe('switchEthereumChainHandler', () => { afterEach(() => { jest.clearAllMocks(); }); - describe('with permittedChains permissioning inactive', () => { - it('should call setActiveNetwork when switching to a built-in infura network', async () => { - const mocks = makeMocks({ - overrides: { - getNetworkConfigurationByChainId: jest - .fn() - .mockReturnValue(createMockMainnetConfiguration()), - }, - }); - const switchEthereumChainHandler = switchEthereumChain.implementation; - await switchEthereumChainHandler( + it('returns null if the current chain id for the domain matches the chainId in the params', async () => { + const { end, response, handler } = createMockedHandler(); + await handler({ + origin: 'example.com', + params: [ { - origin: 'example.com', - params: [{ chainId: CHAIN_IDS.MAINNET }], + chainId: NON_INFURA_CHAIN_ID, }, - {}, - jest.fn(), - jest.fn(), - mocks, - ); - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockMainnetConfiguration().rpcEndpoints[0].networkClientId, - ); + ], }); - it('should call setActiveNetwork when switching to a built-in infura network, when chainId from request is lower case', async () => { - const mocks = makeMocks({ - overrides: { - getNetworkConfigurationByChainId: jest - .fn() - .mockReturnValue(createMockLineaMainnetConfiguration()), - }, - }); - const switchEthereumChainHandler = switchEthereumChain.implementation; - await switchEthereumChainHandler( - { - origin: 'example.com', - params: [{ chainId: CHAIN_IDS.LINEA_MAINNET.toLowerCase() }], - }, - {}, - jest.fn(), - jest.fn(), - mocks, - ); - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockLineaMainnetConfiguration().rpcEndpoints[0].networkClientId, - ); - }); + expect(response.result).toStrictEqual(null); + expect(end).toHaveBeenCalled(); + expect(EthChainUtils.switchChain).not.toHaveBeenCalled(); + }); - it('should call setActiveNetwork when switching to a built-in infura network, when chainId from request is upper case', async () => { - const mocks = makeMocks({ - overrides: { - getNetworkConfigurationByChainId: jest - .fn() - .mockReturnValue(createMockLineaMainnetConfiguration()), - }, - }); - const switchEthereumChainHandler = switchEthereumChain.implementation; - await switchEthereumChainHandler( - { - origin: 'example.com', - params: [{ chainId: CHAIN_IDS.LINEA_MAINNET.toUpperCase() }], - }, - {}, - jest.fn(), - jest.fn(), - mocks, - ); - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockLineaMainnetConfiguration().rpcEndpoints[0].networkClientId, - ); - }); + it('throws an error if unable to find a network matching the chainId in the params', async () => { + const { mocks, end, handler } = createMockedHandler(); + mocks.getCurrentChainIdForDomain.mockReturnValue('0x1'); + mocks.getNetworkConfigurationByChainId.mockReturnValue(undefined); - it('should call setActiveNetwork when switching to a custom network', async () => { - const mocks = makeMocks({ - overrides: { - getCurrentChainIdForDomain: jest - .fn() - .mockReturnValue(CHAIN_IDS.MAINNET), - }, - }); - const switchEthereumChainHandler = switchEthereumChain.implementation; - await switchEthereumChainHandler( + await handler({ + origin: 'example.com', + params: [ { - origin: 'example.com', - params: [{ chainId: NON_INFURA_CHAIN_ID }], + chainId: NON_INFURA_CHAIN_ID, }, - {}, - jest.fn(), - jest.fn(), - mocks, - ); - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockMainnetConfiguration().rpcEndpoints[0].networkClientId, - ); + ], }); - it('should handle missing networkConfiguration', async () => { - // Mock a network configuration that has an undefined or missing rpcEndpoints - const mockNetworkConfiguration = undefined; - - const mocks = makeMocks({ - overrides: { - getNetworkConfigurationByChainId: jest - .fn() - .mockReturnValue(mockNetworkConfiguration), - }, - }); - - const switchEthereumChainHandler = switchEthereumChain.implementation; + expect(end).toHaveBeenCalledWith( + ethErrors.provider.custom({ + code: 4902, + message: `Unrecognized chain ID "${NON_INFURA_CHAIN_ID}". Try adding the chain using wallet_addEthereumChain first.`, + }), + ); + expect(EthChainUtils.switchChain).not.toHaveBeenCalled(); + }); - const mockEnd = jest.fn(); - await switchEthereumChainHandler( + it('tries to switch the network', async () => { + const { mocks, end, handler } = createMockedHandler(); + mocks.getNetworkConfigurationByChainId + .mockReturnValueOnce(createMockMainnetConfiguration()) + .mockReturnValueOnce(createMockLineaMainnetConfiguration()); + await handler({ + origin: 'example.com', + params: [ { - origin: 'example.com', - params: [{ chainId: CHAIN_IDS.MAINNET }], + chainId: '0xdeadbeef', }, - {}, - jest.fn(), - mockEnd, - mocks, - ); - - // Check that the function handled the missing rpcEndpoints and did not attempt to call setActiveNetwork - expect(mockEnd).toHaveBeenCalledWith( - expect.objectContaining({ - code: 4902, - message: expect.stringContaining('Unrecognized chain ID'), - }), - ); - expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); + ], }); + + expect(EthChainUtils.switchChain).toHaveBeenCalledWith( + {}, + end, + 'example.com', + '0xdeadbeef', + 'mainnet', + null, + { + setActiveNetwork: mocks.setActiveNetwork, + getCaveat: mocks.getCaveat, + updateCaveat: mocks.updateCaveat, + requestPermissionApprovalForOrigin: + mocks.requestPermissionApprovalForOrigin, + grantPermissions: mocks.grantPermissions, + }, + ); }); - describe('with permittedChains permissioning active', () => { - it('should call requestPermittedChainsPermission and setActiveNetwork when chainId is not in `endowment:permitted-chains`', async () => { - const mockrequestPermittedChainsPermission = jest - .fn() - .mockResolvedValue(); - const mocks = makeMocks({ - overrides: { - requestPermittedChainsPermission: - mockrequestPermittedChainsPermission, - }, - }); - const switchEthereumChainHandler = switchEthereumChain.implementation; - await switchEthereumChainHandler( - { - origin: 'example.com', - params: [{ chainId: CHAIN_IDS.MAINNET }], - }, - {}, - jest.fn(), - jest.fn(), - mocks, - ); + it('should return an error if an unexpected parameter is provided', async () => { + const { end, handler } = createMockedHandler(); - expect(mocks.requestPermittedChainsPermission).toHaveBeenCalledTimes(1); - expect(mocks.requestPermittedChainsPermission).toHaveBeenCalledWith([ - CHAIN_IDS.MAINNET, - ]); - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockMainnetConfiguration().rpcEndpoints[0].networkClientId, - ); - }); + const unexpectedParam = 'unexpected'; - it('should call setActiveNetwork without calling requestPermittedChainsPermission when requested chainId is in `endowment:permitted-chains`', async () => { - const mocks = makeMocks({ - permissionedChainIds: [CHAIN_IDS.MAINNET], - }); - const switchEthereumChainHandler = switchEthereumChain.implementation; - await switchEthereumChainHandler( + await handler({ + origin: 'example.com', + params: [ { - origin: 'example.com', - params: [{ chainId: CHAIN_IDS.MAINNET }], + chainId: createMockMainnetConfiguration().chainId, + [unexpectedParam]: 'parameter', }, - {}, - jest.fn(), - jest.fn(), - mocks, - ); - - expect(mocks.requestPermittedChainsPermission).not.toHaveBeenCalled(); - expect(mocks.setActiveNetwork).toHaveBeenCalledTimes(1); - expect(mocks.setActiveNetwork).toHaveBeenCalledWith( - createMockMainnetConfiguration().rpcEndpoints[0].networkClientId, - ); + ], }); - it('should handle errors during the switch network permission request', async () => { - const mockError = new Error('Permission request failed'); - const mockrequestPermittedChainsPermission = jest - .fn() - .mockRejectedValue(mockError); - const mocks = makeMocks({ - overrides: { - requestPermittedChainsPermission: - mockrequestPermittedChainsPermission, - }, - }); - const mockEnd = jest.fn(); - const switchEthereumChainHandler = switchEthereumChain.implementation; + expect(end).toHaveBeenCalledWith( + ethErrors.rpc.invalidParams({ + message: `Received unexpected keys on object parameter. Unsupported keys:\n${unexpectedParam}`, + }), + ); + }); - await switchEthereumChainHandler( - { - origin: 'example.com', - params: [{ chainId: CHAIN_IDS.MAINNET }], - }, - {}, - jest.fn(), - mockEnd, - mocks, - ); + it('should return error for invalid chainId', async () => { + const { handler, end } = createMockedHandler(); - expect(mocks.requestPermittedChainsPermission).toHaveBeenCalledTimes(1); - expect(mockEnd).toHaveBeenCalledWith(mockError); - expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); + await handler({ + origin: 'example.com', + params: [{ chainId: 'invalid_chain_id' }], }); + + expect(end).toHaveBeenCalledWith( + ethErrors.rpc.invalidParams({ + message: `Expected 0x-prefixed, unpadded, non-zero hexadecimal string 'chainId'. Received:\ninvalid_chain_id`, + }), + ); }); }); diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b19c91a232ab..86f8b68ea05c 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -15,7 +15,7 @@ import { } from '@metamask/assets-controllers'; import { ObservableStore } from '@metamask/obs-store'; import { storeAsStream } from '@metamask/obs-store/dist/asStream'; -import { JsonRpcEngine } from 'json-rpc-engine'; +import { JsonRpcEngine, createScaffoldMiddleware } from 'json-rpc-engine'; import { createEngineStream } from 'json-rpc-middleware-stream'; import { providerAsMiddleware } from '@metamask/eth-json-rpc-middleware'; import { debounce, throttle, memoize, wrap } from 'lodash'; @@ -25,11 +25,7 @@ import { } from '@metamask/keyring-controller'; import createFilterMiddleware from '@metamask/eth-json-rpc-filters'; import createSubscriptionManager from '@metamask/eth-json-rpc-filters/subscriptionManager'; -import { - errorCodes as rpcErrorCodes, - EthereumRpcError, - ethErrors, -} from 'eth-rpc-errors'; +import { EthereumRpcError, ethErrors } from 'eth-rpc-errors'; import { Mutex } from 'await-semaphore'; import log from 'loglevel'; @@ -62,6 +58,7 @@ import { } from '@metamask/network-controller'; import { GasFeeController } from '@metamask/gas-fee-controller'; import { + MethodNames, PermissionController, PermissionDoesNotExistError, PermissionsRequestNotFoundError, @@ -147,6 +144,7 @@ import { import { Interface } from '@ethersproject/abi'; import { abiERC1155, abiERC721 } from '@metamask/metamask-eth-abis'; import { isEvmAccountType } from '@metamask/keyring-api'; +import { isValidHexAddress, toCaipChainId } from '@metamask/utils'; import { AuthenticationController, UserStorageController, @@ -155,6 +153,20 @@ import { NotificationServicesPushController, NotificationServicesController, } from '@metamask/notification-services-controller'; +import { + walletInvokeMethodHandler, + Caip25CaveatMutatorFactories, + Caip25CaveatType, + Caip25EndowmentPermissionName, + multichainMethodCallValidatorMiddleware, + MultichainSubscriptionManager, + MultichainMiddlewareManager, + walletRevokeSessionHandler, + walletGetSessionHandler, + mergeScopes, + getEthAccounts, + caipPermissionAdapterMiddleware, +} from '@metamask/multichain'; import { methodsRequiringNetworkSwitch } from '../../shared/constants/methods-tags'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) @@ -171,6 +183,7 @@ import { CHAIN_IDS, NETWORK_TYPES, NetworkStatus, + UNSUPPORTED_RPC_METHODS, MAINNET_DISPLAY_NAME, } from '../../shared/constants/network'; import { getAllowedSmartTransactionsChainIds } from '../../shared/constants/smartTransactions'; @@ -192,6 +205,7 @@ import { MILLISECOND, SECOND } from '../../shared/constants/time'; import { ORIGIN_METAMASK, POLLING_TOKEN_ENVIRONMENT_TYPES, + MESSAGE_TYPE, } from '../../shared/constants/app'; import { MetaMetricsEventCategory, @@ -280,8 +294,9 @@ import AccountTrackerController from './controllers/account-tracker-controller'; import createDupeReqFilterStream from './lib/createDupeReqFilterStream'; import createLoggerMiddleware from './lib/createLoggerMiddleware'; import { - createLegacyMethodMiddleware, - createMethodMiddleware, + createEthAccountsMethodMiddleware, + createEip1193MethodMiddleware, + createMultichainMethodMiddleware, createUnsupportedMethodMiddleware, } from './lib/rpc-method-middleware'; import createOriginMiddleware from './lib/createOriginMiddleware'; @@ -311,17 +326,19 @@ import EncryptionPublicKeyController from './controllers/encryption-public-key'; import AppMetadataController from './controllers/app-metadata'; import { - CaveatFactories, CaveatMutatorFactories, + getAuthorizedScopesByOrigin, getCaveatSpecifications, + getChangedAuthorizations, diffMap, getPermissionBackgroundApiMethods, getPermissionSpecifications, getPermittedAccountsByOrigin, + getRemovedAuthorizations, getPermittedChainsByOrigin, NOTIFICATION_NAMES, - PermissionNames, unrestrictedMethods, + PermissionNames, } from './controllers/permissions'; import { MetaMetricsDataDeletionController } from './controllers/metametrics-data-deletion/metametrics-data-deletion'; import { DataDeletionService } from './services/data-deletion-service'; @@ -348,6 +365,7 @@ import { createTxVerificationMiddleware } from './lib/tx-verification/tx-verific import { updateSecurityAlertResponse } from './lib/ppom/ppom-util'; import createEvmMethodsToNonEvmAccountReqFilterMiddleware from './lib/createEvmMethodsToNonEvmAccountReqFilterMiddleware'; import { isEthAddress } from './lib/multichain/address'; + import { decodeTransactionData } from './lib/transaction/decode/util'; import { BridgeUserAction, @@ -362,6 +380,7 @@ import { import createTracingMiddleware from './lib/createTracingMiddleware'; import { PatchStore } from './lib/PatchStore'; import { sanitizeUIState } from './lib/state-utils'; +import { walletCreateSessionHandler } from './lib/multichain-api/wallet-createSession'; export const METAMASK_CONTROLLER_EVENTS = { // Fired after state changes that impact the extension badge (unapproved msg count) @@ -570,7 +589,19 @@ export default class MetamaskController extends EventEmitter { state: initialNetworkControllerState, infuraProjectId: opts.infuraProjectId, }); + this.networkController.initializeProvider(); + this.multichainSubscriptionManager = new MultichainSubscriptionManager({ + getNetworkClientById: this.networkController.getNetworkClientById.bind( + this.networkController, + ), + findNetworkClientIdByChainId: + this.networkController.findNetworkClientIdByChainId.bind( + this.networkController, + ), + }); + + this.multichainMiddlewareManager = new MultichainMiddlewareManager(); this.provider = this.networkController.getProviderAndBlockTracker().provider; this.blockTracker = @@ -1200,51 +1231,16 @@ export default class MetamaskController extends EventEmitter { ], }), state: initState.PermissionController, - caveatSpecifications: getCaveatSpecifications({ - getInternalAccounts: this.accountsController.listAccounts.bind( - this.accountsController, - ), - findNetworkClientIdByChainId: - this.networkController.findNetworkClientIdByChainId.bind( - this.networkController, - ), - }), + caveatSpecifications: getCaveatSpecifications(), permissionSpecifications: { ...getPermissionSpecifications({ getInternalAccounts: this.accountsController.listAccounts.bind( this.accountsController, ), - getAllAccounts: this.keyringController.getAccounts.bind( - this.keyringController, - ), - captureKeyringTypesWithMissingIdentities: ( - internalAccounts = [], - accounts = [], - ) => { - const accountsMissingIdentities = accounts.filter( - (address) => - !internalAccounts.some( - (account) => - account.address.toLowerCase() === address.toLowerCase(), - ), - ); - const keyringTypesWithMissingIdentities = - accountsMissingIdentities.map((address) => - this.keyringController.getAccountKeyringType(address), - ); - - const internalAccountCount = internalAccounts.length; - - const accountTrackerCount = Object.keys( - this.accountTrackerController.state.accounts || {}, - ).length; - - captureException( - new Error( - `Attempt to get permission specifications failed because their were ${accounts.length} accounts, but ${internalAccountCount} identities, and the ${keyringTypesWithMissingIdentities} keyrings included accounts with missing identities. Meanwhile, there are ${accountTrackerCount} accounts in the account tracker.`, - ), - ); - }, + findNetworkClientIdByChainId: + this.networkController.findNetworkClientIdByChainId.bind( + this.networkController, + ), }), ...this.getSnapPermissionSpecifications(), }, @@ -1552,7 +1548,7 @@ export default class MetamaskController extends EventEmitter { }, }, env: { - isAccountSyncingEnabled: isManifestV3, + isAccountSyncingEnabled: false, // TODO: undo this once fixed }, messenger: this.controllerMessenger.getRestricted({ name: 'UserStorageController', @@ -1861,7 +1857,7 @@ export default class MetamaskController extends EventEmitter { this.networkController, ), getNetworkState: () => this.networkController.state, - getPermittedAccounts: this.getPermittedAccounts.bind(this), + getPermittedAccounts: this.getPermittedAccountsSorted.bind(this), getSavedGasFees: () => this.preferencesController.state.advancedGasFee[ getCurrentChainId({ metamask: this.networkController.state }) @@ -2249,18 +2245,13 @@ export default class MetamaskController extends EventEmitter { }, version, // account mgmt - getAccounts: async ( - { origin: innerOrigin }, - { suppressUnauthorizedError = true } = {}, - ) => { + getAccounts: async ({ origin: innerOrigin }) => { if (innerOrigin === ORIGIN_METAMASK) { const selectedAddress = this.accountsController.getSelectedAccount().address; return selectedAddress ? [selectedAddress] : []; } else if (this.isUnlocked()) { - return await this.getPermittedAccounts(innerOrigin, { - suppressUnauthorizedError, - }); + return await this.getPermittedAccounts(innerOrigin); } return []; // changing this is a breaking change }, @@ -2891,6 +2882,86 @@ export default class MetamaskController extends EventEmitter { getPermittedAccountsByOrigin, ); + // This handles CAIP-25 authorization changes every time relevant permission state + // changes, for any reason. + this.controllerMessenger.subscribe( + `${this.permissionController.name}:stateChange`, + async (currentValue, previousValue) => { + const changedAuthorizations = getChangedAuthorizations( + currentValue, + previousValue, + ); + + const removedAuthorizations = getRemovedAuthorizations( + currentValue, + previousValue, + ); + + // remove any existing notification subscriptions for removed authorizations + for (const [origin, authorization] of removedAuthorizations.entries()) { + const mergedScopes = mergeScopes( + authorization.requiredScopes, + authorization.optionalScopes, + ); + // if the eth_subscription notification is in the scope and eth_subscribe is in the methods + // then remove middleware and unsubscribe + Object.entries(mergedScopes).forEach(([scope, scopeObject]) => { + if ( + scopeObject.notifications.includes('eth_subscription') && + scopeObject.methods.includes('eth_subscribe') + ) { + this.multichainMiddlewareManager.removeMiddlewareByScopeAndOrigin( + scope, + origin, + ); + this.multichainSubscriptionManager.unsubscribeByScopeAndOrigin( + scope, + origin, + ); + } + }); + } + + // add new notification subscriptions for changed authorizations + for (const [origin, authorization] of changedAuthorizations.entries()) { + const mergedScopes = mergeScopes( + authorization.requiredScopes, + authorization.optionalScopes, + ); + + // if the eth_subscription notification is in the scope and eth_subscribe is in the methods + // then get the subscriptionManager going for that scope + Object.entries(mergedScopes).forEach(([scope, scopeObject]) => { + if ( + scopeObject.notifications.includes('eth_subscription') && + scopeObject.methods.includes('eth_subscribe') + ) { + // for each tabId + Object.entries(this.connections[origin]).forEach( + ([_, { tabId }]) => { + const subscriptionManager = + this.multichainSubscriptionManager.subscribe({ + scope, + origin, + tabId, + }); + this.multichainMiddlewareManager.addMiddleware({ + scope, + origin, + tabId, + middleware: subscriptionManager.middleware, + }); + }, + ); + } + }); + + this._notifyAuthorizationChange(origin, authorization); + } + }, + getAuthorizedScopesByOrigin, + ); + this.controllerMessenger.subscribe( `${this.permissionController.name}:stateChange`, async (currentValue, previousValue) => { @@ -3433,9 +3504,7 @@ export default class MetamaskController extends EventEmitter { updateNetwork: this.networkController.updateNetwork.bind( this.networkController, ), - removeNetwork: this.networkController.removeNetwork.bind( - this.networkController, - ), + removeNetwork: this.removeNetwork.bind(this), getCurrentNetworkEIP1559Compatibility: this.networkController.getEIP1559Compatibility.bind( this.networkController, @@ -3712,7 +3781,10 @@ export default class MetamaskController extends EventEmitter { removePermissionsFor: this.removePermissionsFor, approvePermissionsRequest: this.acceptPermissionsRequest, rejectPermissionsRequest: this.rejectPermissionsRequest, - ...getPermissionBackgroundApiMethods(permissionController), + ...getPermissionBackgroundApiMethods({ + permissionController, + approvalController, + }), ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) connectCustodyAddresses: this.mmiController.connectCustodyAddresses.bind( @@ -4855,56 +4927,142 @@ export default class MetamaskController extends EventEmitter { return selectedAddress; } + captureKeyringTypesWithMissingIdentities( + internalAccounts = [], + accounts = [], + ) { + const accountsMissingIdentities = accounts.filter( + (address) => + !internalAccounts.some( + (account) => account.address.toLowerCase() === address.toLowerCase(), + ), + ); + const keyringTypesWithMissingIdentities = accountsMissingIdentities.map( + (address) => this.keyringController.getAccountKeyringType(address), + ); + + const internalAccountCount = internalAccounts.length; + + const accountTrackerCount = Object.keys( + this.accountTrackerController.state.accounts || {}, + ).length; + + captureException( + new Error( + `Attempt to get permission specifications failed because their were ${accounts.length} accounts, but ${internalAccountCount} identities, and the ${keyringTypesWithMissingIdentities} keyrings included accounts with missing identities. Meanwhile, there are ${accountTrackerCount} accounts in the account tracker.`, + ), + ); + } + + async getAllEvmAccountsSorted() { + // We only consider EVM addresses here, hence the filtering: + const accounts = (await this.keyringController.getAccounts()).filter( + isValidHexAddress, + ); + const internalAccounts = this.accountsController.listAccounts(); + + return accounts.sort((firstAddress, secondAddress) => { + const firstAccount = internalAccounts.find( + (internalAccount) => + internalAccount.address.toLowerCase() === firstAddress.toLowerCase(), + ); + + const secondAccount = internalAccounts.find( + (internalAccount) => + internalAccount.address.toLowerCase() === secondAddress.toLowerCase(), + ); + + if (!firstAccount) { + this.captureKeyringTypesWithMissingIdentities( + internalAccounts, + accounts, + ); + throw new Error(`Missing identity for address: "${firstAddress}".`); + } else if (!secondAccount) { + this.captureKeyringTypesWithMissingIdentities( + internalAccounts, + accounts, + ); + throw new Error(`Missing identity for address: "${secondAddress}".`); + } else if ( + firstAccount.metadata.lastSelected === + secondAccount.metadata.lastSelected + ) { + return 0; + } else if (firstAccount.metadata.lastSelected === undefined) { + return 1; + } else if (secondAccount.metadata.lastSelected === undefined) { + return -1; + } + + return ( + secondAccount.metadata.lastSelected - firstAccount.metadata.lastSelected + ); + }); + } + /** * Gets the permitted accounts for the specified origin. Returns an empty * array if no accounts are permitted. * * @param {string} origin - The origin whose exposed accounts to retrieve. - * @param {boolean} [suppressUnauthorizedError] - Suppresses the unauthorized error. * @returns {Promise} The origin's permitted accounts, or an empty * array. */ - async getPermittedAccounts( - origin, - { suppressUnauthorizedError = true } = {}, - ) { + getPermittedAccounts(origin) { + let caveat; try { - return await this.permissionController.executeRestrictedMethod( + caveat = this.permissionController.getCaveat( origin, - RestrictedMethods.eth_accounts, + Caip25EndowmentPermissionName, + Caip25CaveatType, ); - } catch (error) { - if ( - suppressUnauthorizedError && - error.code === rpcErrorCodes.provider.unauthorized - ) { - return []; - } - throw error; + } catch (err) { + // noop + } + if (!caveat) { + return []; } + + return getEthAccounts(caveat.value); + } + + async getPermittedAccountsSorted(origin) { + const permittedAccounts = this.getPermittedAccounts(origin); + const allEvmAccounts = await this.getAllEvmAccountsSorted(); + return allEvmAccounts.filter((account) => + permittedAccounts.includes(account), + ); } /** * Stops exposing the specified chain ID to all third parties. - * Exposed chain IDs are stored in caveats of the `endowment:permitted-chains` - * permission. This method uses `PermissionController.updatePermissionsByCaveat` - * to remove the specified chain ID from every `endowment:permitted-chains` - * permission. If a permission only included this chain ID, the permission is - * revoked entirely. * * @param {string} targetChainId - The chain ID to stop exposing * to third parties. */ removeAllChainIdPermissions(targetChainId) { this.permissionController.updatePermissionsByCaveat( - CaveatTypes.restrictNetworkSwitching, - (existingChainIds) => - CaveatMutatorFactories[ - CaveatTypes.restrictNetworkSwitching - ].removeChainId(targetChainId, existingChainIds), + Caip25CaveatType, + (existingScopes) => + Caip25CaveatMutatorFactories[Caip25CaveatType].removeScope( + toCaipChainId('eip155', parseInt(targetChainId, 16)), + existingScopes, + ), ); } + // Figure out what needs to be done with the middleware/subscription logic + removeNetwork(chainId) { + const scope = `eip155:${parseInt(chainId, 16)}`; + this.multichainSubscriptionManager.unsubscribeByScope(scope); + this.multichainMiddlewareManager.removeMiddlewareByScope(scope); + + this.removeAllChainIdPermissions(chainId); + + this.networkController.removeNetwork(chainId); + } + /** * Stops exposing the account with the specified address to all third parties. * Exposed accounts are stored in caveats of the eth_accounts permission. This @@ -4923,6 +5081,14 @@ export default class MetamaskController extends EventEmitter { CaveatTypes.restrictReturnedAccounts ].removeAccount(targetAccount, existingAccounts), ); + this.permissionController.updatePermissionsByCaveat( + Caip25CaveatType, + (existingScopes) => + Caip25CaveatMutatorFactories[Caip25CaveatType].removeAccount( + targetAccount, + existingScopes, + ), + ); } /** @@ -4964,6 +5130,28 @@ export default class MetamaskController extends EventEmitter { this.preferencesController.setSelectedAddress(importedAccountAddress); } + /** + * Requests approval for permissions for the specified origin + * + * @param origin - The origin to request approval for. + * @param permissions - The permissions to request approval for. + */ + async requestPermissionApprovalForOrigin(origin, permissions) { + const id = nanoid(); + return this.approvalController.addAndShowApprovalRequest({ + id, + origin, + requestData: { + metadata: { + id, + origin, + }, + permissions, + }, + type: MethodNames.requestPermissions, + }); + } + // --------------------------------------------------------------------------- // Identity Management (signature operations) @@ -5471,7 +5659,7 @@ export default class MetamaskController extends EventEmitter { // setup connection const providerStream = createEngineStream({ engine }); - const connectionId = this.addConnection(origin, { engine }); + const connectionId = this.addConnection(origin, { tabId, engine }); pipeline( outStream, @@ -5539,7 +5727,7 @@ export default class MetamaskController extends EventEmitter { // setup connection const providerStream = createEngineStream({ engine }); - const connectionId = this.addConnection(origin, { engine }); + const connectionId = this.addConnection(origin, { tabId, engine }); pipeline( outStream, @@ -5547,6 +5735,15 @@ export default class MetamaskController extends EventEmitter { providerStream, outStream, (err) => { + this.multichainMiddlewareManager.removeMiddlewareByOriginAndTabId( + origin, + tabId, + ); + this.multichainSubscriptionManager.unsubscribeByOriginAndTabId( + origin, + tabId, + ); + // handle any middleware cleanup engine._middleware.forEach((mid) => { if (mid.destroy && typeof mid.destroy === 'function') { @@ -5606,6 +5803,8 @@ export default class MetamaskController extends EventEmitter { useRequestQueue: this.preferencesController.getUseRequestQueue.bind( this.preferencesController, ), + // TODO: Should this be made async in queued-request-controller package? + // Doing so allows us to DRY up getPermittedAcounts and getPermittedAccountsSorted shouldEnqueueRequest: (request) => { return methodsRequiringNetworkSwitch.includes(request.method); }, @@ -5679,13 +5878,25 @@ export default class MetamaskController extends EventEmitter { }), ); - engine.push(createUnsupportedMethodMiddleware()); + engine.push(createUnsupportedMethodMiddleware(UNSUPPORTED_RPC_METHODS)); + + engine.push((req, res, next, end) => + caipPermissionAdapterMiddleware(req, res, next, end, { + getCaveat: this.permissionController.getCaveat.bind( + this.permissionController, + ), + getNetworkConfigurationByNetworkClientId: + this.networkController.getNetworkConfigurationByNetworkClientId.bind( + this.networkController, + ), + }), + ); - // Legacy RPC methods that need to be implemented _ahead of_ the permission + // Legacy RPC method that needs to be implemented _ahead of_ the permission // middleware. engine.push( - createLegacyMethodMiddleware({ - getAccounts: this.getPermittedAccounts.bind(this, origin), + createEthAccountsMethodMiddleware({ + getAccounts: this.getPermittedAccountsSorted.bind(this, origin), }), ); @@ -5720,9 +5931,7 @@ export default class MetamaskController extends EventEmitter { // Unrestricted/permissionless RPC method implementations. // They must nevertheless be placed _behind_ the permission middleware. engine.push( - createMethodMiddleware({ - origin, - + createEip1193MethodMiddleware({ subjectType, // Miscellaneous @@ -5750,59 +5959,21 @@ export default class MetamaskController extends EventEmitter { this.metaMetricsController, ), // Permission-related - getAccounts: this.getPermittedAccounts.bind(this, origin), + getAccounts: this.getPermittedAccountsSorted.bind(this, origin), getPermissionsForOrigin: this.permissionController.getPermissions.bind( this.permissionController, origin, ), - hasPermission: this.permissionController.hasPermission.bind( - this.permissionController, - origin, - ), - requestAccountsPermission: - this.permissionController.requestPermissions.bind( - this.permissionController, - { origin }, - { - eth_accounts: {}, - ...(!isSnapId(origin) && { - [PermissionNames.permittedChains]: {}, - }), - }, - ), - requestPermittedChainsPermission: (chainIds) => - this.permissionController.requestPermissionsIncremental( - { origin }, - { - [PermissionNames.permittedChains]: { - caveats: [ - CaveatFactories[CaveatTypes.restrictNetworkSwitching]( - chainIds, - ), - ], - }, - }, - ), - grantPermittedChainsPermissionIncremental: (chainIds) => - this.permissionController.grantPermissionsIncremental({ - subject: { origin }, - approvedPermissions: { - [PermissionNames.permittedChains]: { - caveats: [ - CaveatFactories[CaveatTypes.restrictNetworkSwitching]( - chainIds, - ), - ], - }, - }, - }), + requestPermissionApprovalForOrigin: + this.requestPermissionApprovalForOrigin.bind(this, origin), requestPermissionsForOrigin: (requestedPermissions) => this.permissionController.requestPermissions( { origin }, { - ...(requestedPermissions[PermissionNames.eth_accounts] && { - [PermissionNames.permittedChains]: {}, - }), + ...(requestedPermissions[PermissionNames.eth_accounts] && + !isSnapId(origin) && { + [PermissionNames.permittedChains]: {}, + }), ...(requestedPermissions[PermissionNames.permittedChains] && { [PermissionNames.eth_accounts]: {}, }), @@ -5844,12 +6015,12 @@ export default class MetamaskController extends EventEmitter { // network configuration-related setActiveNetwork: async (networkClientId) => { await this.networkController.setActiveNetwork(networkClientId); - // if the origin has the eth_accounts permission + // if the origin has the CAIP-25 permission // we set per dapp network selection state if ( this.permissionController.hasPermission( origin, - PermissionNames.eth_accounts, + Caip25EndowmentPermissionName, ) ) { this.selectedNetworkController.setNetworkClientIdForDomain( @@ -5887,6 +6058,13 @@ export default class MetamaskController extends EventEmitter { this.alertController, ), + grantPermissions: this.permissionController.grantPermissions.bind( + this.permissionController, + ), + updateCaveat: this.permissionController.updateCaveat.bind( + this.permissionController, + ), + ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) handleMmiAuthenticate: this.institutionalFeaturesController.handleMmiAuthenticate.bind( @@ -6001,7 +6179,7 @@ export default class MetamaskController extends EventEmitter { } /** - * A method for creating a CAIP provider that is safely restricted for the requesting subject. + * A method for creating a provider that is safely restricted for the requesting subject. * * @param {object} options - Provider engine options * @param {string} options.origin - The origin of the sender @@ -6010,9 +6188,290 @@ export default class MetamaskController extends EventEmitter { setupProviderEngineCaip({ origin, tabId }) { const engine = new JsonRpcEngine(); - engine.push((request, _res, _next, end) => { - console.log('CAIP request received', { origin, tabId, request }); - return end(new Error('CAIP RPC Pipeline not yet implemented.')); + // Append origin to each request + engine.push(createOriginMiddleware({ origin })); + + // Append tabId to each request if it exists + if (tabId) { + engine.push(createTabIdMiddleware({ tabId })); + } + + engine.push(createLoggerMiddleware({ origin })); + + engine.push((req, _res, next, end) => { + if ( + ![ + MESSAGE_TYPE.WALLET_CREATE_SESSION, + MESSAGE_TYPE.WALLET_INVOKE_METHOD, + MESSAGE_TYPE.WALLET_GET_SESSION, + MESSAGE_TYPE.WALLET_REVOKE_SESSION, + ].includes(req.method) + ) { + return end(new Error('Invalid method')); // TODO: Use a proper error + } + return next(); + }); + + // TODO: Uncomment this when wallet lifecycle methods are added to api-specs + engine.push(multichainMethodCallValidatorMiddleware); + + engine.push( + createScaffoldMiddleware({ + [MESSAGE_TYPE.WALLET_CREATE_SESSION]: ( + request, + response, + next, + end, + ) => { + return walletCreateSessionHandler(request, response, next, end, { + grantPermissions: this.permissionController.grantPermissions.bind( + this.permissionController, + ), + findNetworkClientIdByChainId: + this.networkController.findNetworkClientIdByChainId.bind( + this.networkController, + ), + listAccounts: this.accountsController.listAccounts.bind( + this.accountsController, + ), + addNetwork: this.networkController.addNetwork.bind( + this.networkController, + ), + removeNetwork: this.removeNetwork.bind(this), + requestPermissionApprovalForOrigin: + this.requestPermissionApprovalForOrigin.bind(this, origin), + sendMetrics: this.metaMetricsController.trackEvent.bind( + this.metaMetricsController, + ), + metamaskState: this.getState(), + }); + }, + [MESSAGE_TYPE.WALLET_INVOKE_METHOD]: (request, response, next, end) => { + return walletInvokeMethodHandler(request, response, next, end, { + findNetworkClientIdByChainId: + this.networkController.findNetworkClientIdByChainId.bind( + this.networkController, + ), + getCaveat: this.permissionController.getCaveat.bind( + this.permissionController, + ), + getSelectedNetworkClientId: () => + this.networkController.state.selectedNetworkClientId, + }); + }, + [MESSAGE_TYPE.WALLET_REVOKE_SESSION]: ( + request, + response, + next, + end, + ) => { + return walletRevokeSessionHandler(request, response, next, end, { + revokePermission: this.permissionController.revokePermission.bind( + this.permissionController, + ), + }); + }, + [MESSAGE_TYPE.WALLET_GET_SESSION]: (request, response, next, end) => { + return walletGetSessionHandler(request, response, next, end, { + getCaveat: this.permissionController.getCaveat.bind( + this.permissionController, + ), + }); + }, + }), + ); + + // TODO: Does this need to go before the wallet_createSession middleware? + // Add a middleware that will switch chain on each request (as needed) + const requestQueueMiddleware = createQueuedRequestMiddleware({ + enqueueRequest: this.queuedRequestController.enqueueRequest.bind( + this.queuedRequestController, + ), + useRequestQueue: this.preferencesController.getUseRequestQueue.bind( + this.preferencesController, + ), + shouldEnqueueRequest: (request) => { + return methodsRequiringNetworkSwitch.includes(request.method); + }, + }); + engine.push(requestQueueMiddleware); + + engine.push( + createUnsupportedMethodMiddleware([ + ...UNSUPPORTED_RPC_METHODS, + 'eth_requestAccounts', + 'eth_accounts', + ]), + ); + + engine.push( + createMultichainMethodMiddleware({ + subjectType: SubjectType.Website, // TODO: this should probably be passed in + + // Miscellaneous + addSubjectMetadata: + this.subjectMetadataController.addSubjectMetadata.bind( + this.subjectMetadataController, + ), + getProviderState: this.getProviderState.bind(this), + handleWatchAssetRequest: this.handleWatchAssetRequest.bind(this), + requestUserApproval: + this.approvalController.addAndShowApprovalRequest.bind( + this.approvalController, + ), + startApprovalFlow: this.approvalController.startFlow.bind( + this.approvalController, + ), + endApprovalFlow: this.approvalController.endFlow.bind( + this.approvalController, + ), + getCaveat: ({ target, caveatType }) => { + try { + return this.permissionController.getCaveat( + origin, + target, + caveatType, + ); + } catch (e) { + if (e instanceof PermissionDoesNotExistError) { + // suppress expected error in case that the origin + // does not have the target permission yet + } else { + throw e; + } + } + + return undefined; + }, + addNetwork: this.networkController.addNetwork.bind( + this.networkController, + ), + updateNetwork: this.networkController.updateNetwork.bind( + this.networkController, + ), + setActiveNetwork: async (networkClientId) => { + await this.networkController.setActiveNetwork(networkClientId); + // if the origin has the CAIP-25 permission + // we set per dapp network selection state + if ( + this.permissionController.hasPermission( + origin, + Caip25EndowmentPermissionName, + ) + ) { + this.selectedNetworkController.setNetworkClientIdForDomain( + origin, + networkClientId, + ); + } + }, + getNetworkConfigurationByChainId: + this.networkController.getNetworkConfigurationByChainId.bind( + this.networkController, + ), + // TODO refactor `add-ethereum-chain` handler so that this hook can be removed from multichain middleware + getCurrentChainIdForDomain: (domain) => { + const networkClientId = + this.selectedNetworkController.getNetworkClientIdForDomain(domain); + const { chainId } = + this.networkController.getNetworkConfigurationByNetworkClientId( + networkClientId, + ); + return chainId; + }, + + // Web3 shim-related + getWeb3ShimUsageState: this.alertController.getWeb3ShimUsageState.bind( + this.alertController, + ), + setWeb3ShimUsageRecorded: + this.alertController.setWeb3ShimUsageRecorded.bind( + this.alertController, + ), + + requestPermissionApprovalForOrigin: + this.requestPermissionApprovalForOrigin.bind(this, origin), + updateCaveat: this.permissionController.updateCaveat.bind( + this.permissionController, + ), + grantPermissions: this.permissionController.grantPermissions.bind( + this.permissionController, + ), + }), + ); + + engine.push(this.metamaskMiddleware); + + // TODO: Might be able to DRY this with the stateChange event + try { + const caip25Caveat = this.permissionController.getCaveat( + origin, + Caip25EndowmentPermissionName, + Caip25CaveatType, + ); + + // add new notification subscriptions for changed authorizations + const mergedScopes = mergeScopes( + caip25Caveat.value.requiredScopes, + caip25Caveat.value.optionalScopes, + ); + + // if the eth_subscription notification is in the scope and eth_subscribe is in the methods + // then get the subscriptionManager going for that scope + Object.entries(mergedScopes).forEach(([scope, scopeObject]) => { + if ( + scopeObject.notifications.includes('eth_subscription') && + scopeObject.methods.includes('eth_subscribe') + ) { + const subscriptionManager = + this.multichainSubscriptionManager.subscribe({ + scope, + origin, + tabId, + }); + this.multichainMiddlewareManager.addMiddleware({ + scope, + origin, + tabId, + middleware: subscriptionManager.middleware, + }); + } + }); + } catch (err) { + // noop + } + + this.multichainSubscriptionManager.on( + 'notification', + (targetOrigin, targetTabId, message) => { + if (origin === targetOrigin && tabId === targetTabId) { + engine.emit('notification', message); + } + }, + ); + + engine.push( + this.multichainMiddlewareManager.generateMultichainMiddlewareForOriginAndTabId( + origin, + tabId, + ), + ); + + engine.push((req, res, _next, end) => { + const { provider } = this.networkController.getNetworkClientById( + req.networkClientId, + ); + + // send request to provider + provider.sendAsync(req, (err, providerRes) => { + // forward any error + if (err instanceof Error) { + return end(err); + } + // copy provider response onto original response + Object.assign(res, providerRes); + return end(); + }); }); return engine; @@ -6049,9 +6508,10 @@ export default class MetamaskController extends EventEmitter { * @param {string} origin - The connection's origin string. * @param {object} options - Data associated with the connection * @param {object} options.engine - The connection's JSON Rpc Engine + * @param options.tabId * @returns {string} The connection's id (so that it can be deleted later) */ - addConnection(origin, { engine }) { + addConnection(origin, { tabId, engine }) { if (origin === ORIGIN_METAMASK) { return null; } @@ -6062,6 +6522,7 @@ export default class MetamaskController extends EventEmitter { const id = nanoid(); this.connections[origin][id] = { + tabId, engine, }; @@ -6217,7 +6678,7 @@ export default class MetamaskController extends EventEmitter { method: NOTIFICATION_NAMES.unlockStateChanged, params: { isUnlocked: true, - accounts: await this.getPermittedAccounts(origin), + accounts: await this.getPermittedAccountsSorted(origin), }, }; }); @@ -6258,7 +6719,7 @@ export default class MetamaskController extends EventEmitter { */ _onStateUpdate(newState) { this.isClientOpenAndUnlocked = newState.isUnlocked && this._isClientOpen; - this._notifyChainChange(); + // this._notifyChainChange(); } // misc @@ -6748,13 +7209,27 @@ export default class MetamaskController extends EventEmitter { newAccounts : // If the length is 2 or greater, we have to execute // `eth_accounts` vi this method. - await this.getPermittedAccounts(origin), + await this.getPermittedAccountsSorted(origin), }); } this.permissionLogController.updateAccountsHistory(origin, newAccounts); } + async _notifyAuthorizationChange(origin, newAuthorization) { + if (this.isUnlocked()) { + this.notifyConnections(origin, { + method: NOTIFICATION_NAMES.sessionChanged, + params: { + sessionScopes: mergeScopes( + newAuthorization.requiredScopes ?? {}, + newAuthorization.optionalScopes ?? {}, + ), + }, + }); + } + } + async _notifyChainChange() { if (this.preferencesController.getUseRequestQueue()) { this.notifyAllConnections(async (origin) => ({ diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 77b062bcfdc7..09022da677c0 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -102,10 +102,13 @@ const createLoggerMiddlewareMock = () => (req, res, next) => { jest.mock('./lib/createLoggerMiddleware', () => createLoggerMiddlewareMock); const rpcMethodMiddlewareMock = { - createMethodMiddleware: () => (_req, _res, next, _end) => { + createEip1193MethodMiddleware: () => (_req, _res, next, _end) => { next(); }, - createLegacyMethodMiddleware: () => (_req, _res, next, _end) => { + createEthAccountsMethodMiddleware: () => (_req, _res, next, _end) => { + next(); + }, + createMultichainMethodMiddleware: () => (_req, _res, next, _end) => { next(); }, createUnsupportedMethodMiddleware: () => (_req, _res, next, _end) => { @@ -1397,6 +1400,10 @@ describe('MetaMaskController', () => { }); describe('#setupUntrustedCommunicationCaip', () => { + it.todo('adds a tabId, origin and networkClient to requests'); + + it.todo('should add only origin to request if tabId not provided'); + it.todo('should only process `caip-x` CAIP formatted messages'); }); diff --git a/package.json b/package.json index 4973fa0da559..aea0ffb859f1 100644 --- a/package.json +++ b/package.json @@ -331,6 +331,7 @@ "@metamask/message-manager": "^10.1.0", "@metamask/message-signing-snap": "^0.3.3", "@metamask/metamask-eth-abis": "^3.1.1", + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-a13b9c75", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 4c802e13bfa0..4fb4a2de4996 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -447,106 +447,156 @@ class FixtureBuilder { account = '', } = {}) { const selectedAccount = account || DEFAULT_FIXTURE_ACCOUNT; - return this.withPermissionController({ - subjects: { + let subjects = {}; + if (restrictReturnedAccounts) { + subjects = { [DAPP_URL]: { origin: DAPP_URL, permissions: { - eth_accounts: { - id: 'ZaqPEWxyhNCJYACFw93jE', - parentCapability: 'eth_accounts', - invoker: DAPP_URL, - caveats: restrictReturnedAccounts && [ + 'endowment:caip25': { + caveats: [ { - type: 'restrictReturnedAccounts', - value: [ - selectedAccount.toLowerCase(), - '0x09781764c08de8ca82e156bbf156a3ca217c7950', - ERC_4337_ACCOUNT.toLowerCase(), - ], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + `eip155:1:${selectedAccount.toLowerCase()}`, + 'eip155:1:0x09781764c08de8ca82e156bbf156a3ca217c7950', + `eip155:1:${ERC_4337_ACCOUNT.toLowerCase()}`, + ], + }, + }, + isMultichainOrigin: false, + }, }, ], + id: 'ZaqPEWxyhNCJYACFw93jE', date: 1664388714636, + invoker: DAPP_URL, + parentCapability: 'endowment:caip25', }, }, }, - }, + }; + } + return this.withPermissionController({ + subjects, }); } withPermissionControllerSnapAccountConnectedToTestDapp( restrictReturnedAccounts = true, ) { - return this.withPermissionController({ - subjects: { + let subjects = {}; + if (restrictReturnedAccounts) { + subjects = { [DAPP_URL]: { origin: DAPP_URL, permissions: { - eth_accounts: { - id: 'ZaqPEWxyhNCJYACFw93jE', - parentCapability: 'eth_accounts', - invoker: DAPP_URL, - caveats: restrictReturnedAccounts && [ + 'endowment:caip25': { + caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x09781764c08de8ca82e156bbf156a3ca217c7950'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x09781764c08de8ca82e156bbf156a3ca217c7950', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], + id: 'ZaqPEWxyhNCJYACFw93jE', date: 1664388714636, + invoker: DAPP_URL, + parentCapability: 'endowment:caip25', }, }, }, - }, - }); + }; + } + return this.withPermissionController({ subjects }); } withPermissionControllerConnectedToTwoTestDapps( restrictReturnedAccounts = true, ) { - return this.withPermissionController({ - subjects: { + let subjects = {}; + if (restrictReturnedAccounts) { + subjects = { [DAPP_URL]: { origin: DAPP_URL, permissions: { - eth_accounts: { - id: 'ZaqPEWxyhNCJYACFw93jE', - parentCapability: 'eth_accounts', - invoker: DAPP_URL, - caveats: restrictReturnedAccounts && [ + 'endowment:caip25': { + caveats: [ { - type: 'restrictReturnedAccounts', - value: [ - '0x5cfe73b6021e818b776b421b1c4db2474086a7e1', - '0x09781764c08de8ca82e156bbf156a3ca217c7950', - ], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', + 'eip155:1:0x09781764c08de8ca82e156bbf156a3ca217c7950', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], + id: 'ZaqPEWxyhNCJYACFw93jE', date: 1664388714636, + invoker: DAPP_URL, + parentCapability: 'endowment:caip25', }, }, }, [DAPP_ONE_URL]: { origin: DAPP_ONE_URL, permissions: { - eth_accounts: { - id: 'AqPEWxyhNCJYACFw93jE4', - parentCapability: 'eth_accounts', - invoker: DAPP_ONE_URL, - caveats: restrictReturnedAccounts && [ + 'endowment:caip25': { + caveats: [ { - type: 'restrictReturnedAccounts', - value: [ - '0x5cfe73b6021e818b776b421b1c4db2474086a7e1', - '0x09781764c08de8ca82e156bbf156a3ca217c7950', - ], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', + 'eip155:1:0x09781764c08de8ca82e156bbf156a3ca217c7950', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], + id: 'ZaqPEWxyhNCJYACFw93jE', date: 1664388714636, + invoker: DAPP_ONE_URL, + parentCapability: 'endowment:caip25', }, }, }, - }, - }); + }; + } + return this.withPermissionController({ subjects }); } withPermissionControllerConnectedToSnapDapp() { @@ -1183,78 +1233,120 @@ class FixtureBuilder { 'https://app.ens.domains': { origin: 'https://app.ens.domains', permissions: { - eth_accounts: { - id: 'oKXoF_MNlffiR2u1Y3mDE', - parentCapability: 'eth_accounts', - invoker: 'https://app.ens.domains', + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: [ - '0xbee150bdc171c7d4190891e78234f791a3ac7b24', - '0xb9504634e5788208933b51ae7440b478bfadf865', - ], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0xbee150bdc171c7d4190891e78234f791a3ac7b24', + 'eip155:1:0xb9504634e5788208933b51ae7440b478bfadf865', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1708029792962, + id: 'oKXoF_MNlffiR2u1Y3mDE', + invoker: 'https://app.ens.domains', + parentCapability: 'endowment:caip25', }, }, }, 'https://app.uniswap.org': { origin: 'https://app.uniswap.org', permissions: { - eth_accounts: { - id: 'vaa88u5Iv3VmsJwG3bDKW', - parentCapability: 'eth_accounts', - invoker: 'https://app.uniswap.org', + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: [ - '0xbee150bdc171c7d4190891e78234f791a3ac7b24', - '0xd1ca923697a701cba1364d803d72b4740fc39bc9', - ], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0xbee150bdc171c7d4190891e78234f791a3ac7b24', + 'eip155:1:0xd1ca923697a701cba1364d803d72b4740fc39bc9', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1708029870079, + id: 'vaa88u5Iv3VmsJwG3bDKW', + invoker: 'https://app.uniswap.org', + parentCapability: 'endowment:caip25', }, }, }, 'https://www.dextools.io': { origin: 'https://www.dextools.io', permissions: { - eth_accounts: { - id: 'bvvPcFtIhkFyHyW0Tmwi4', - parentCapability: 'eth_accounts', - invoker: 'https://www.dextools.io', + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: [ - '0xbee150bdc171c7d4190891e78234f791a3ac7b24', - '0xa5c5293e124d04e2f85e8553851001fd2f192647', - '0xb9504634e5788208933b51ae7440b478bfadf865', - ], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0xbee150bdc171c7d4190891e78234f791a3ac7b24', + 'eip155:1:0xa5c5293e124d04e2f85e8553851001fd2f192647', + 'eip155:1:0xb9504634e5788208933b51ae7440b478bfadf865', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1708029948170, + id: 'bvvPcFtIhkFyHyW0Tmwi4', + invoker: 'https://www.dextools.io', + parentCapability: 'endowment:caip25', }, }, }, 'https://coinmarketcap.com': { origin: 'https://coinmarketcap.com', permissions: { - eth_accounts: { - id: 'AiblK84K1Cic-Y0FDSzMD', - parentCapability: 'eth_accounts', - invoker: 'https://coinmarketcap.com', + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0xbee150bdc171c7d4190891e78234f791a3ac7b24'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0xbee150bdc171c7d4190891e78234f791a3ac7b24', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1708030049641, + id: 'AiblK84K1Cic-Y0FDSzMD', + invoker: 'https://coinmarketcap.com', + parentCapability: 'endowment:caip25', }, }, }, diff --git a/test/e2e/tests/request-queuing/ui.spec.js b/test/e2e/tests/request-queuing/ui.spec.js index b857d4307d5b..f940c35d0e69 100644 --- a/test/e2e/tests/request-queuing/ui.spec.js +++ b/test/e2e/tests/request-queuing/ui.spec.js @@ -60,7 +60,7 @@ async function openDappAndSwitchChain(driver, dappUrl, chainId) { (permission) => permission.parentCapability === PermissionNames.permittedChains, ) - ?.caveats.find( + ?.caveats?.find( (caveat) => caveat.type === CaveatTypes.restrictNetworkSwitching, )?.value || []; diff --git a/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.test.js b/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.test.js index dff1cf47d4be..7b6e2595c63d 100644 --- a/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.test.js +++ b/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.test.js @@ -123,15 +123,27 @@ describe('Unconnected Account Alert', () => { subjects: { 'https://test.dapp': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], invoker: 'https://test.dapp', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, diff --git a/ui/components/app/permission-page-container/permission-page-container.component.js b/ui/components/app/permission-page-container/permission-page-container.component.js index f5f69da8f947..aac1d6731464 100644 --- a/ui/components/app/permission-page-container/permission-page-container.component.js +++ b/ui/components/app/permission-page-container/permission-page-container.component.js @@ -148,7 +148,7 @@ export default class PermissionPageContainer extends Component { const permittedChainsPermission = _request.permissions?.[PermissionNames.permittedChains]; - const approvedChainIds = permittedChainsPermission?.caveats.find( + const approvedChainIds = permittedChainsPermission?.caveats?.find( (caveat) => caveat.type === CaveatTypes.restrictNetworkSwitching, )?.value; diff --git a/ui/components/multichain/account-list-menu/account-list-menu.test.tsx b/ui/components/multichain/account-list-menu/account-list-menu.test.tsx index 7a849577dfa4..36d7c9a363f1 100644 --- a/ui/components/multichain/account-list-menu/account-list-menu.test.tsx +++ b/ui/components/multichain/account-list-menu/account-list-menu.test.tsx @@ -69,15 +69,27 @@ const render = ( subjects: { 'https://test.dapp': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], invoker: 'https://test.dapp', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, @@ -200,15 +212,31 @@ describe('AccountListMenu', () => { subjects: { 'https://test.dapp': { permissions: { - eth_accounts: { - caveats: [ - { - type: 'restrictReturnedAccounts', - value: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'], + 'https://test.dapp': { + permissions: { + 'endowment:caip25': { + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', + ], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + invoker: 'https://test.dapp', + parentCapability: 'endowment:caip25', }, - ], - invoker: 'https://test.dapp', - parentCapability: 'eth_accounts', + }, }, }, }, @@ -327,15 +355,27 @@ describe('AccountListMenu', () => { subjects: { 'https://test.dapp': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], invoker: 'https://test.dapp', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, @@ -438,15 +478,27 @@ describe('AccountListMenu', () => { subjects: { 'https://test.dapp': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], invoker: 'https://test.dapp', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, diff --git a/ui/components/multichain/connected-accounts-menu/connected-accounts-menu.test.tsx b/ui/components/multichain/connected-accounts-menu/connected-accounts-menu.test.tsx index 91ffeb5e21a7..c7d0e91644db 100644 --- a/ui/components/multichain/connected-accounts-menu/connected-accounts-menu.test.tsx +++ b/ui/components/multichain/connected-accounts-menu/connected-accounts-menu.test.tsx @@ -81,20 +81,30 @@ const renderComponent = (props = {}, stateChanges = {}) => { subjects: { 'https://remix.ethereum.org': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: [ - '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', - '0x7250739de134d33ec7ab1ee592711e15098c9d2d', - ], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + 'eip155:1:0x7250739de134d33ec7ab1ee592711e15098c9d2d', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1586359844177, id: '3aa65a8b-3bcb-4944-941b-1baa5fe0ed8b', invoker: 'https://remix.ethereum.org', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index ba842efc6a11..47a9b43071f9 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -30,6 +30,7 @@ import { } from '../../../helpers/constants/design-system'; import { getURLHost } from '../../../helpers/utils/util'; import { MergedInternalAccount } from '../../../selectors/selectors.types'; +import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; import { MetaMetricsEventCategory, MetaMetricsEventName, @@ -141,8 +142,12 @@ export const EditAccountsModal: React.FC = ({ isPinned={Boolean(account.pinned)} startAccessory={ + isEqualCaseInsensitive( + selectedAccountAddress, + account.address, + ), )} /> } diff --git a/ui/components/multichain/pages/connections/connections.test.tsx b/ui/components/multichain/pages/connections/connections.test.tsx index 0840e3a2f9ed..ae351be699a7 100644 --- a/ui/components/multichain/pages/connections/connections.test.tsx +++ b/ui/components/multichain/pages/connections/connections.test.tsx @@ -40,17 +40,29 @@ describe('Connections Content', () => { 'https://metamask.github.io': { origin: 'https://metamask.github.io', permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1616006369498, id: '3d0bdc27-e8e4-4fb0-a24b-340d61f6a3fa', invoker: 'https://metamask.github.io', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, @@ -67,15 +79,27 @@ describe('Connections Content', () => { subjects: { 'https://metamask.github.io': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], invoker: 'https://metamask.github.io', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, diff --git a/ui/components/multichain/pages/connections/connections.tsx b/ui/components/multichain/pages/connections/connections.tsx index 4a8b188b86b6..3dbb77f3a2e3 100644 --- a/ui/components/multichain/pages/connections/connections.tsx +++ b/ui/components/multichain/pages/connections/connections.tsx @@ -396,7 +396,7 @@ export const Connections = () => { size={ButtonPrimarySize.Lg} block data-test-id="no-connections-button" - onClick={() => dispatch(requestAccountsPermission())} + onClick={() => requestAccountsPermission()} > {t('connectAccounts')} diff --git a/ui/components/multichain/pages/permissions-page/permissions-page.test.js b/ui/components/multichain/pages/permissions-page/permissions-page.test.js index 026cddeff34d..1adbdd5febe5 100644 --- a/ui/components/multichain/pages/permissions-page/permissions-page.test.js +++ b/ui/components/multichain/pages/permissions-page/permissions-page.test.js @@ -35,17 +35,29 @@ mockState.metamask.subjects = { 'https://metamask.github.io': { origin: 'https://metamask.github.io', permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1698071087770, id: 'BIko27gpEajmo_CcNYPxD', invoker: 'https://metamask.github.io', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index bb3a14a8f5e8..43d6efdd598e 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -14,6 +14,7 @@ import { } from '../../../../component-library'; import { EditAccountsModal, EditNetworksModal } from '../../..'; import { MergedInternalAccount } from '../../../../../selectors/selectors.types'; +import { isEqualCaseInsensitive } from '../../../../../../shared/modules/string-utils'; import { MetaMetricsContext } from '../../../../../contexts/metametrics'; import { MetaMetricsEventCategory, @@ -59,7 +60,9 @@ export const SiteCell: React.FC = ({ const [showEditNetworksModal, setShowEditNetworksModal] = useState(false); const selectedAccounts = accounts.filter(({ address }) => - selectedAccountAddresses.includes(address), + selectedAccountAddresses.some((selectedAccountAddress) => + isEqualCaseInsensitive(selectedAccountAddress, address), + ), ); const selectedNetworks = allNetworks.filter(({ chainId }) => selectedChainIds.includes(chainId), diff --git a/ui/components/multichain/pages/send/components/account-picker.test.tsx b/ui/components/multichain/pages/send/components/account-picker.test.tsx index 136a2986a63e..0905db72ecce 100644 --- a/ui/components/multichain/pages/send/components/account-picker.test.tsx +++ b/ui/components/multichain/pages/send/components/account-picker.test.tsx @@ -46,15 +46,27 @@ const render = ( subjects: { 'https://test.dapp': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], invoker: 'https://test.dapp', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, diff --git a/ui/components/multichain/permission-details-modal/permission-details-modal.test.tsx b/ui/components/multichain/permission-details-modal/permission-details-modal.test.tsx index 3c3d5ff18bfa..1aff89ae8015 100644 --- a/ui/components/multichain/permission-details-modal/permission-details-modal.test.tsx +++ b/ui/components/multichain/permission-details-modal/permission-details-modal.test.tsx @@ -69,20 +69,30 @@ describe('PermissionDetailsModal', () => { subjects: { 'https://remix.ethereum.org': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: [ - '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', - '0x7250739de134d33ec7ab1ee592711e15098c9d2d', - ], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + 'eip155:1:0x7250739de134d33ec7ab1ee592711e15098c9d2d', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1586359844177, id: '3aa65a8b-3bcb-4944-941b-1baa5fe0ed8b', invoker: 'https://remix.ethereum.org', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, diff --git a/ui/pages/permissions-connect/connect-page/connect-page.tsx b/ui/pages/permissions-connect/connect-page/connect-page.tsx index a30047fbd38a..1bac3ceafbc2 100644 --- a/ui/pages/permissions-connect/connect-page/connect-page.tsx +++ b/ui/pages/permissions-connect/connect-page/connect-page.tsx @@ -33,11 +33,20 @@ import { import { MergedInternalAccount } from '../../../selectors/selectors.types'; import { mergeAccounts } from '../../../components/multichain/account-list-menu/account-list-menu'; import { TEST_CHAINS } from '../../../../shared/constants/network'; +import { + CaveatTypes, + EndowmentTypes, + RestrictedMethods, +} from '../../../../shared/constants/permissions'; import PermissionsConnectFooter from '../../../components/app/permissions-connect-footer'; export type ConnectPageRequest = { id: string; origin: string; + permissions?: Record< + string, + { caveats: { type: string; value: string[] }[] } + >; }; type ConnectPageProps = { @@ -57,6 +66,20 @@ export const ConnectPage: React.FC = ({ }) => { const t = useI18nContext(); + const ethAccountsPermission = + request?.permissions?.[RestrictedMethods.eth_accounts]; + const requestedAccounts = + ethAccountsPermission?.caveats?.find( + (caveat) => caveat.type === CaveatTypes.restrictReturnedAccounts, + )?.value || []; + + const permittedChainsPermission = + request?.permissions?.[EndowmentTypes.permittedChains]; + const requestedChainIds = + permittedChainsPermission?.caveats?.find( + (caveat) => caveat.type === CaveatTypes.restrictNetworkSwitching, + )?.value || []; + const networkConfigurations = useSelector(getNetworkConfigurationsByChainId); const [nonTestNetworks, testNetworks] = useMemo( () => @@ -70,7 +93,10 @@ export const ConnectPage: React.FC = ({ ), [networkConfigurations], ); - const defaultSelectedChainIds = nonTestNetworks.map(({ chainId }) => chainId); + const defaultSelectedChainIds = + requestedChainIds.length > 0 + ? requestedChainIds + : nonTestNetworks.map(({ chainId }) => chainId); const [selectedChainIds, setSelectedChainIds] = useState( defaultSelectedChainIds, ); @@ -84,7 +110,10 @@ export const ConnectPage: React.FC = ({ }, [accounts, internalAccounts]); const currentAccount = useSelector(getSelectedInternalAccount); - const defaultAccountsAddresses = [currentAccount?.address]; + const defaultAccountsAddresses = + requestedAccounts.length > 0 + ? requestedAccounts + : [currentAccount?.address]; const [selectedAccountAddresses, setSelectedAccountAddresses] = useState( defaultAccountsAddresses, ); diff --git a/ui/pages/routes/routes.component.test.js b/ui/pages/routes/routes.component.test.js index 6151fedc687b..23cedb96cbd4 100644 --- a/ui/pages/routes/routes.component.test.js +++ b/ui/pages/routes/routes.component.test.js @@ -200,16 +200,26 @@ describe('toast display', () => { subjects: { [mockOrigin]: { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: [mockAccount.address], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [`eip155:1:${mockAccount.address}`], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1719910288437, invoker: 'https://metamask.github.io', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, diff --git a/ui/selectors/permissions.js b/ui/selectors/permissions.js index fb32d41c9b17..180b8dfcbd36 100644 --- a/ui/selectors/permissions.js +++ b/ui/selectors/permissions.js @@ -1,9 +1,12 @@ import { ApprovalType } from '@metamask/controller-utils'; import { WALLET_SNAP_PERMISSION_KEY } from '@metamask/snaps-rpc-methods'; import { isEvmAccountType } from '@metamask/keyring-api'; -import { CaveatTypes } from '../../shared/constants/permissions'; -// eslint-disable-next-line import/no-restricted-paths -import { PermissionNames } from '../../app/scripts/controllers/permissions'; +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, + getEthAccounts, + getPermittedEthChainIds, +} from '@metamask/multichain'; import { getApprovalRequestsByType } from './approvals'; import { createDeepEqualSelector } from './util'; import { @@ -58,13 +61,13 @@ export function getPermissionSubjects(state) { */ export function getPermittedAccounts(state, origin) { return getAccountsFromPermission( - getAccountsPermissionFromSubject(subjectSelector(state, origin)), + getCaip25PermissionFromSubject(subjectSelector(state, origin)), ); } export function getPermittedChains(state, origin) { return getChainsFromPermission( - getChainsPermissionFromSubject(subjectSelector(state, origin)), + getCaip25PermissionFromSubject(subjectSelector(state, origin)), ); } @@ -274,53 +277,33 @@ export const isAccountConnectedToCurrentTab = createDeepEqualSelector( ); // selector helpers - -function getAccountsFromSubject(subject) { - return getAccountsFromPermission(getAccountsPermissionFromSubject(subject)); +function getCaip25PermissionFromSubject(subject = {}) { + return subject.permissions?.[Caip25EndowmentPermissionName] || {}; } -function getAccountsPermissionFromSubject(subject = {}) { - return subject.permissions?.eth_accounts || {}; +function getAccountsFromSubject(subject) { + return getAccountsFromPermission(getCaip25PermissionFromSubject(subject)); } function getChainsFromSubject(subject) { - return getChainsFromPermission(getChainsPermissionFromSubject(subject)); -} - -function getChainsPermissionFromSubject(subject = {}) { - return subject.permissions?.[PermissionNames.permittedChains] || {}; -} - -function getAccountsFromPermission(accountsPermission) { - const accountsCaveat = getAccountsCaveatFromPermission(accountsPermission); - return accountsCaveat && Array.isArray(accountsCaveat.value) - ? accountsCaveat.value - : []; + return getChainsFromPermission(getCaip25PermissionFromSubject(subject)); } -function getChainsFromPermission(chainsPermission) { - const chainsCaveat = getChainsCaveatFromPermission(chainsPermission); - return chainsCaveat && Array.isArray(chainsCaveat.value) - ? chainsCaveat.value - : []; -} - -function getChainsCaveatFromPermission(chainsPermission = {}) { +function getCaveatFromPermission(caip25Permission = {}) { return ( - Array.isArray(chainsPermission.caveats) && - chainsPermission.caveats.find( - (caveat) => caveat.type === CaveatTypes.restrictNetworkSwitching, - ) + Array.isArray(caip25Permission.caveats) && + caip25Permission.caveats.find((caveat) => caveat.type === Caip25CaveatType) ); } -function getAccountsCaveatFromPermission(accountsPermission = {}) { - return ( - Array.isArray(accountsPermission.caveats) && - accountsPermission.caveats.find( - (caveat) => caveat.type === CaveatTypes.restrictReturnedAccounts, - ) - ); +function getAccountsFromPermission(caip25Permission) { + const caip25Caveat = getCaveatFromPermission(caip25Permission); + return caip25Caveat ? getEthAccounts(caip25Caveat.value) : []; +} + +function getChainsFromPermission(caip25Permission) { + const caip25Caveat = getCaveatFromPermission(caip25Permission); + return caip25Caveat ? getPermittedEthChainIds(caip25Caveat.value) : []; } function subjectSelector(state, origin) { diff --git a/ui/selectors/permissions.test.js b/ui/selectors/permissions.test.js index 3c55179d4a0e..f07b5422bf1e 100644 --- a/ui/selectors/permissions.test.js +++ b/ui/selectors/permissions.test.js @@ -46,33 +46,57 @@ describe('selectors', () => { subjects: { 'peepeth.com': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1585676177970, id: '840d72a0-925f-449f-830a-1aa1dd5ce151', invoker: 'peepeth.com', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, 'https://remix.ethereum.org': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1585685128948, id: '6b9615cc-64e4-4317-afab-3c4f8ee0244a', invoker: 'https://remix.ethereum.org', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, @@ -147,36 +171,58 @@ describe('selectors', () => { subjects: { 'peepeth.com': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1585676177970, id: '840d72a0-925f-449f-830a-1aa1dd5ce151', invoker: 'peepeth.com', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, 'https://remix.ethereum.org': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: [ - '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', - '0x7250739de134d33ec7ab1ee592711e15098c9d2d', - ], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + 'eip155:1:0x7250739de134d33ec7ab1ee592711e15098c9d2d', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1585685128948, id: '6b9615cc-64e4-4317-afab-3c4f8ee0244a', invoker: 'https://remix.ethereum.org', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, @@ -302,39 +348,61 @@ describe('selectors', () => { subjects: { 'https://remix.ethereum.org': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: [ - '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', - '0x7250739de134d33ec7ab1ee592711e15098c9d2d', - '0x617b3f8050a0bd94b6b1da02b4384ee5b4df13f4', - '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', - '0xb3958fb96c8201486ae20be1d5c9f58083df343a', - ], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + 'eip155:1:0x7250739de134d33ec7ab1ee592711e15098c9d2d', + 'eip155:1:0x617b3f8050a0bd94b6b1da02b4384ee5b4df13f4', + 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', + 'eip155:1:0xb3958fb96c8201486ae20be1d5c9f58083df343a', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1586359844177, id: '3aa65a8b-3bcb-4944-941b-1baa5fe0ed8b', invoker: 'https://remix.ethereum.org', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, 'peepeth.com': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1585676177970, id: '840d72a0-925f-449f-830a-1aa1dd5ce151', invoker: 'peepeth.com', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, @@ -553,52 +621,86 @@ describe('selectors', () => { subjects: { 'https://remix.ethereum.org': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: [ - '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', - '0x7250739de134d33ec7ab1ee592711e15098c9d2d', - ], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + 'eip155:1:0x7250739de134d33ec7ab1ee592711e15098c9d2d', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1586359844177, id: '3aa65a8b-3bcb-4944-941b-1baa5fe0ed8b', invoker: 'https://remix.ethereum.org', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, 'peepeth.com': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1585676177970, id: '840d72a0-925f-449f-830a-1aa1dd5ce151', invoker: 'peepeth.com', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, 'uniswap.exchange': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1585616816623, id: 'ce625215-f2e9-48e7-93ca-21ba193244ff', invoker: 'uniswap.exchange', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, @@ -626,21 +728,31 @@ describe('selectors', () => { it('should return a list of permissions keys and values', () => { expect(getPermissionsForActiveTab(mockState)).toStrictEqual([ { - key: 'eth_accounts', + key: 'endowment:caip25', value: { caveats: [ { - type: 'restrictReturnedAccounts', - value: [ - '0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', - '0x7250739de134d33ec7ab1ee592711e15098c9d2d', - ], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', + 'eip155:1:0x7250739de134d33ec7ab1ee592711e15098c9d2d', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], date: 1586359844177, id: '3aa65a8b-3bcb-4944-941b-1baa5fe0ed8b', invoker: 'https://remix.ethereum.org', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, ]); diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js index 24b2a2afe125..81a4b2532743 100644 --- a/ui/selectors/selectors.test.js +++ b/ui/selectors/selectors.test.js @@ -1618,15 +1618,27 @@ describe('Selectors', () => { subjects: { 'https://test.dapp': { permissions: { - eth_accounts: { + 'endowment:caip25': { caveats: [ { - type: 'restrictReturnedAccounts', - value: ['0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc'], + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', + ], + }, + }, + isMultichainOrigin: false, + }, }, ], invoker: 'https://test.dapp', - parentCapability: 'eth_accounts', + parentCapability: 'endowment:caip25', }, }, }, diff --git a/yarn.lock b/yarn.lock index 733c94112452..1edd6313870f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4125,6 +4125,17 @@ __metadata: languageName: node linkType: hard +"@json-schema-tools/dereferencer@npm:^1.6.3": + version: 1.6.3 + resolution: "@json-schema-tools/dereferencer@npm:1.6.3" + dependencies: + "@json-schema-tools/reference-resolver": "npm:^1.2.6" + "@json-schema-tools/traverse": "npm:^1.10.4" + fast-safe-stringify: "npm:^2.1.1" + checksum: 10/da6ef5b82a8a9c3a7e62ffcab5c04c581f1e0f8165c0debdb272bb1e08ccd726107ee194487b8fa736cac00fb390b8df74bc1ad1b200eddbe25c98ee0d3d000b + languageName: node + linkType: hard + "@json-schema-tools/meta-schema@npm:1.6.19": version: 1.6.19 resolution: "@json-schema-tools/meta-schema@npm:1.6.19" @@ -4139,6 +4150,13 @@ __metadata: languageName: node linkType: hard +"@json-schema-tools/meta-schema@npm:^1.7.5": + version: 1.7.5 + resolution: "@json-schema-tools/meta-schema@npm:1.7.5" + checksum: 10/707dc3a285c26c37d00f418e9d0ef8a2ad1c23d4936ad5aab0ce94c9ae36a7a6125c4ca5048513af64b7e6e527b5472a1701d1f709c379acdd7ad12f6409d2cd + languageName: node + linkType: hard + "@json-schema-tools/reference-resolver@npm:1.2.4": version: 1.2.4 resolution: "@json-schema-tools/reference-resolver@npm:1.2.4" @@ -4159,6 +4177,23 @@ __metadata: languageName: node linkType: hard +"@json-schema-tools/reference-resolver@npm:^1.2.6": + version: 1.2.6 + resolution: "@json-schema-tools/reference-resolver@npm:1.2.6" + dependencies: + "@json-schema-spec/json-pointer": "npm:^0.1.2" + isomorphic-fetch: "npm:^3.0.0" + checksum: 10/91d6b4b2ac43f8163fd27bde6d826f29f339e9c7ce3b7e2b73b85e891fa78e3702fd487deda143a0701879cbc2fe28c53a4efce4cd2d2dd2fe6e82b64bbd9c9c + languageName: node + linkType: hard + +"@json-schema-tools/traverse@npm:^1.10.4": + version: 1.10.4 + resolution: "@json-schema-tools/traverse@npm:1.10.4" + checksum: 10/0027bc90df01c5eeee0833e722b7320b53be8b5ce3f4e0e4a6e45713a38e6f88f21aba31e3dd973093ef75cd21a40c07fe8f112da8f49a7919b1c0e44c904d20 + languageName: node + linkType: hard + "@json-schema-tools/traverse@npm:^1.7.5, @json-schema-tools/traverse@npm:^1.7.8": version: 1.10.3 resolution: "@json-schema-tools/traverse@npm:1.10.3" @@ -4842,6 +4877,13 @@ __metadata: languageName: node linkType: hard +"@metamask/api-specs@npm:^0.10.12": + version: 0.10.12 + resolution: "@metamask/api-specs@npm:0.10.12" + checksum: 10/e592f27f350994688d3d54a8a8db16de033011ef665efe3283a77431914d8d69d1c3312fad33e4245b4984e1223b04c98da3d0a68c7f9577cf8290ba441c52ee + languageName: node + linkType: hard + "@metamask/api-specs@npm:^0.9.3": version: 0.9.3 resolution: "@metamask/api-specs@npm:0.9.3" @@ -5749,6 +5791,26 @@ __metadata: languageName: node linkType: hard +"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-a13b9c75": + version: 0.0.0-preview-a13b9c75 + resolution: "@metamask-previews/multichain@npm:0.0.0-preview-a13b9c75" + dependencies: + "@metamask/api-specs": "npm:^0.10.12" + "@metamask/controller-utils": "npm:^11.3.0" + "@metamask/eth-json-rpc-filters": "npm:^7.0.0" + "@metamask/rpc-errors": "npm:^6.3.1" + "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/utils": "npm:^9.1.0" + "@open-rpc/schema-utils-js": "npm:^2.0.5" + jsonschema: "npm:^1.2.4" + lodash: "npm:^4.17.21" + peerDependencies: + "@metamask/network-controller": ^21.0.0 + "@metamask/permission-controller": ^11.0.0 + checksum: 10/9ffe476ea4ab42d3b4df167df539c722cd0ea398f100328cc74ad635d1e70839769f9cd959d3a8ea6b9125184013ddf3b1ac6295e29818038891110e6eb00843 + languageName: node + linkType: hard + "@metamask/name-controller@npm:^8.0.0": version: 8.0.0 resolution: "@metamask/name-controller@npm:8.0.0" @@ -7018,6 +7080,13 @@ __metadata: languageName: node linkType: hard +"@open-rpc/meta-schema@npm:^1.14.9": + version: 1.14.9 + resolution: "@open-rpc/meta-schema@npm:1.14.9" + checksum: 10/51505dcf7aa1a2285c78953c9b33711cede5f2765aa37dcb9ee7756d689e2ff2a89cfc6039504f0569c52a805fb9aa18f30a7c02ad7a06e793c801e43b419104 + languageName: node + linkType: hard + "@open-rpc/mock-server@npm:^1.7.5": version: 1.7.5 resolution: "@open-rpc/mock-server@npm:1.7.5" @@ -7087,6 +7156,24 @@ __metadata: languageName: node linkType: hard +"@open-rpc/schema-utils-js@npm:^2.0.5": + version: 2.0.5 + resolution: "@open-rpc/schema-utils-js@npm:2.0.5" + dependencies: + "@json-schema-tools/dereferencer": "npm:^1.6.3" + "@json-schema-tools/meta-schema": "npm:^1.7.5" + "@json-schema-tools/reference-resolver": "npm:^1.2.6" + "@open-rpc/meta-schema": "npm:^1.14.9" + ajv: "npm:^6.10.0" + detect-node: "npm:^2.0.4" + fast-safe-stringify: "npm:^2.0.7" + fs-extra: "npm:^10.1.0" + is-url: "npm:^1.2.4" + isomorphic-fetch: "npm:^3.0.0" + checksum: 10/9e10215606e9a00a47b082c9cfd70d05bf0d38de6cf1c147246c545c6997375d94cd3caafe919b71178df58b5facadfd0dcc8b6857bf5e79c40e5e33683dd3d5 + languageName: node + linkType: hard + "@open-rpc/server-js@npm:1.9.3": version: 1.9.3 resolution: "@open-rpc/server-js@npm:1.9.3" @@ -19202,7 +19289,7 @@ __metadata: languageName: node linkType: hard -"fast-safe-stringify@npm:^2.0.6, fast-safe-stringify@npm:^2.0.7": +"fast-safe-stringify@npm:^2.0.6, fast-safe-stringify@npm:^2.0.7, fast-safe-stringify@npm:^2.1.1": version: 2.1.1 resolution: "fast-safe-stringify@npm:2.1.1" checksum: 10/dc1f063c2c6ac9533aee14d406441f86783a8984b2ca09b19c2fe281f9ff59d315298bc7bc22fd1f83d26fe19ef2f20e2ddb68e96b15040292e555c5ced0c1e4 @@ -26165,6 +26252,7 @@ __metadata: "@metamask/message-manager": "npm:^10.1.0" "@metamask/message-signing-snap": "npm:^0.3.3" "@metamask/metamask-eth-abis": "npm:^3.1.1" + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-a13b9c75" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch" "@metamask/notification-controller": "npm:^6.0.0" From 61b2077756bde0625faef262f6514779122099d0 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 14 Oct 2024 12:16:15 -0700 Subject: [PATCH 002/148] move wallet_ into rpc-method-middleware/handlers --- .../handlers}/wallet-getPermissions.js | 0 .../handlers}/wallet-getPermissions.test.js | 0 .../handlers}/wallet-requestPermissions.js | 0 .../handlers}/wallet-requestPermissions.test.js | 0 .../handlers}/wallet-revokePermissions.js | 0 .../handlers}/wallet-revokePermissions.test.js | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename app/scripts/lib/{multichain-api => rpc-method-middleware/handlers}/wallet-getPermissions.js (100%) rename app/scripts/lib/{multichain-api => rpc-method-middleware/handlers}/wallet-getPermissions.test.js (100%) rename app/scripts/lib/{multichain-api => rpc-method-middleware/handlers}/wallet-requestPermissions.js (100%) rename app/scripts/lib/{multichain-api => rpc-method-middleware/handlers}/wallet-requestPermissions.test.js (100%) rename app/scripts/lib/{multichain-api => rpc-method-middleware/handlers}/wallet-revokePermissions.js (100%) rename app/scripts/lib/{multichain-api => rpc-method-middleware/handlers}/wallet-revokePermissions.test.js (100%) diff --git a/app/scripts/lib/multichain-api/wallet-getPermissions.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js similarity index 100% rename from app/scripts/lib/multichain-api/wallet-getPermissions.js rename to app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js diff --git a/app/scripts/lib/multichain-api/wallet-getPermissions.test.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.js similarity index 100% rename from app/scripts/lib/multichain-api/wallet-getPermissions.test.js rename to app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.js diff --git a/app/scripts/lib/multichain-api/wallet-requestPermissions.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js similarity index 100% rename from app/scripts/lib/multichain-api/wallet-requestPermissions.js rename to app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js diff --git a/app/scripts/lib/multichain-api/wallet-requestPermissions.test.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.js similarity index 100% rename from app/scripts/lib/multichain-api/wallet-requestPermissions.test.js rename to app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.js diff --git a/app/scripts/lib/multichain-api/wallet-revokePermissions.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.js similarity index 100% rename from app/scripts/lib/multichain-api/wallet-revokePermissions.js rename to app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.js diff --git a/app/scripts/lib/multichain-api/wallet-revokePermissions.test.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.js similarity index 100% rename from app/scripts/lib/multichain-api/wallet-revokePermissions.test.js rename to app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.js From 661008777e8a257d27f172a95ec7a335a77a1fa3 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 14 Oct 2024 12:21:03 -0700 Subject: [PATCH 003/148] Fix handler imports --- .../lib/rpc-method-middleware/createMethodMiddleware.js | 6 +++--- .../handlers/wallet-getPermissions.js | 7 ++----- .../handlers/wallet-getPermissions.test.js | 4 ++-- .../handlers/wallet-requestPermissions.js | 6 +++--- .../handlers/wallet-requestPermissions.test.js | 4 ++-- .../handlers/wallet-revokePermissions.js | 4 ++-- .../handlers/wallet-revokePermissions.test.js | 4 ++-- 7 files changed, 16 insertions(+), 19 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js index ed8abbb5d4c5..b44ba5397c2f 100644 --- a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js +++ b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js @@ -2,14 +2,14 @@ import { selectHooks } from '@metamask/snaps-rpc-methods'; import { hasProperty } from '@metamask/utils'; import { ethErrors } from 'eth-rpc-errors'; -import { getPermissionsHandler } from '../multichain-api/wallet-getPermissions'; -import { requestPermissionsHandler } from '../multichain-api/wallet-requestPermissions'; -import { revokePermissionsHandler } from '../multichain-api/wallet-revokePermissions'; import { handlers as localHandlers, eip1193OnlyHandlers, ethAccountsHandler, } from './handlers'; +import { getPermissionsHandler } from './handlers/wallet-getPermissions'; +import { requestPermissionsHandler } from './handlers/wallet-requestPermissions'; +import { revokePermissionsHandler } from './handlers/wallet-revokePermissions'; // The primary home of RPC method implementations for the injected 1193 provider API. MUST be subsequent // to our permissioning logic in the EIP-1193 JSON-RPC middleware pipeline. diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js index 6dd6d1f2ad0e..2068a02597c6 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js @@ -4,11 +4,8 @@ import { Caip25EndowmentPermissionName, getPermittedEthChainIds, } from '@metamask/multichain'; -import { - CaveatTypes, - RestrictedMethods, -} from '../../../../shared/constants/permissions'; -import { PermissionNames } from '../../controllers/permissions'; +import { PermissionNames } from '../../../controllers/permissions'; +import { CaveatTypes, RestrictedMethods } from '../../../../../shared/constants/permissions'; export const getPermissionsHandler = { methodNames: [MethodNames.getPermissions], diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.js index b415a4ff7a47..a20f6bb7f4b9 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.js @@ -6,8 +6,8 @@ import { import { CaveatTypes, RestrictedMethods, -} from '../../../../shared/constants/permissions'; -import { PermissionNames } from '../../controllers/permissions'; +} from '../../../../../shared/constants/permissions'; +import { PermissionNames } from '../../../controllers/permissions'; import { getPermissionsHandler } from './wallet-getPermissions'; jest.mock('@metamask/multichain', () => ({ diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js index 04f07456f81b..866d71d47230 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js @@ -10,10 +10,10 @@ import { import { CaveatTypes, RestrictedMethods, -} from '../../../../shared/constants/permissions'; -import { PermissionNames } from '../../controllers/permissions'; +} from '../../../../../shared/constants/permissions'; +import { PermissionNames } from '../../../controllers/permissions'; // eslint-disable-next-line import/no-restricted-paths -import { isSnapId } from '../../../../ui/helpers/utils/snaps'; +import { isSnapId } from '../../../../../ui/helpers/utils/snaps'; export const requestPermissionsHandler = { methodNames: [MethodNames.requestPermissions], diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.js index 312112cea39b..0e038225a744 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.js @@ -7,8 +7,8 @@ import * as Multichain from '@metamask/multichain'; import { CaveatTypes, RestrictedMethods, -} from '../../../../shared/constants/permissions'; -import { PermissionNames } from '../../controllers/permissions'; +} from '../../../../../shared/constants/permissions'; +import { PermissionNames } from '../../../controllers/permissions'; import { requestPermissionsHandler } from './wallet-requestPermissions'; jest.mock('@metamask/multichain', () => ({ diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.js index bccee84c4e41..30e06d672068 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.js @@ -4,8 +4,8 @@ import { Caip25CaveatType, Caip25EndowmentPermissionName, } from '@metamask/multichain'; -import { RestrictedMethods } from '../../../../shared/constants/permissions'; -import { PermissionNames } from '../../controllers/permissions'; +import { RestrictedMethods } from '../../../../../shared/constants/permissions'; +import { PermissionNames } from '../../../controllers/permissions'; export const revokePermissionsHandler = { methodNames: [MethodNames.revokePermissions], diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.js index 5d340527d857..4b04314ffd47 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.js @@ -3,8 +3,8 @@ import { Caip25CaveatType, Caip25EndowmentPermissionName, } from '@metamask/multichain'; -import { PermissionNames } from '../../controllers/permissions'; -import { RestrictedMethods } from '../../../../shared/constants/permissions'; +import { PermissionNames } from '../../../controllers/permissions'; +import { RestrictedMethods } from '../../../../../shared/constants/permissions'; import { revokePermissionsHandler } from './wallet-revokePermissions'; const baseRequest = { From ec8a9c9b865ae7694107d738d7a2189d8b78ccd3 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 14 Oct 2024 12:23:02 -0700 Subject: [PATCH 004/148] reset mmc --- app/scripts/metamask-controller.js | 779 +++++------------------- app/scripts/metamask-controller.test.js | 11 +- 2 files changed, 154 insertions(+), 636 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 86f8b68ea05c..b19c91a232ab 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -15,7 +15,7 @@ import { } from '@metamask/assets-controllers'; import { ObservableStore } from '@metamask/obs-store'; import { storeAsStream } from '@metamask/obs-store/dist/asStream'; -import { JsonRpcEngine, createScaffoldMiddleware } from 'json-rpc-engine'; +import { JsonRpcEngine } from 'json-rpc-engine'; import { createEngineStream } from 'json-rpc-middleware-stream'; import { providerAsMiddleware } from '@metamask/eth-json-rpc-middleware'; import { debounce, throttle, memoize, wrap } from 'lodash'; @@ -25,7 +25,11 @@ import { } from '@metamask/keyring-controller'; import createFilterMiddleware from '@metamask/eth-json-rpc-filters'; import createSubscriptionManager from '@metamask/eth-json-rpc-filters/subscriptionManager'; -import { EthereumRpcError, ethErrors } from 'eth-rpc-errors'; +import { + errorCodes as rpcErrorCodes, + EthereumRpcError, + ethErrors, +} from 'eth-rpc-errors'; import { Mutex } from 'await-semaphore'; import log from 'loglevel'; @@ -58,7 +62,6 @@ import { } from '@metamask/network-controller'; import { GasFeeController } from '@metamask/gas-fee-controller'; import { - MethodNames, PermissionController, PermissionDoesNotExistError, PermissionsRequestNotFoundError, @@ -144,7 +147,6 @@ import { import { Interface } from '@ethersproject/abi'; import { abiERC1155, abiERC721 } from '@metamask/metamask-eth-abis'; import { isEvmAccountType } from '@metamask/keyring-api'; -import { isValidHexAddress, toCaipChainId } from '@metamask/utils'; import { AuthenticationController, UserStorageController, @@ -153,20 +155,6 @@ import { NotificationServicesPushController, NotificationServicesController, } from '@metamask/notification-services-controller'; -import { - walletInvokeMethodHandler, - Caip25CaveatMutatorFactories, - Caip25CaveatType, - Caip25EndowmentPermissionName, - multichainMethodCallValidatorMiddleware, - MultichainSubscriptionManager, - MultichainMiddlewareManager, - walletRevokeSessionHandler, - walletGetSessionHandler, - mergeScopes, - getEthAccounts, - caipPermissionAdapterMiddleware, -} from '@metamask/multichain'; import { methodsRequiringNetworkSwitch } from '../../shared/constants/methods-tags'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) @@ -183,7 +171,6 @@ import { CHAIN_IDS, NETWORK_TYPES, NetworkStatus, - UNSUPPORTED_RPC_METHODS, MAINNET_DISPLAY_NAME, } from '../../shared/constants/network'; import { getAllowedSmartTransactionsChainIds } from '../../shared/constants/smartTransactions'; @@ -205,7 +192,6 @@ import { MILLISECOND, SECOND } from '../../shared/constants/time'; import { ORIGIN_METAMASK, POLLING_TOKEN_ENVIRONMENT_TYPES, - MESSAGE_TYPE, } from '../../shared/constants/app'; import { MetaMetricsEventCategory, @@ -294,9 +280,8 @@ import AccountTrackerController from './controllers/account-tracker-controller'; import createDupeReqFilterStream from './lib/createDupeReqFilterStream'; import createLoggerMiddleware from './lib/createLoggerMiddleware'; import { - createEthAccountsMethodMiddleware, - createEip1193MethodMiddleware, - createMultichainMethodMiddleware, + createLegacyMethodMiddleware, + createMethodMiddleware, createUnsupportedMethodMiddleware, } from './lib/rpc-method-middleware'; import createOriginMiddleware from './lib/createOriginMiddleware'; @@ -326,19 +311,17 @@ import EncryptionPublicKeyController from './controllers/encryption-public-key'; import AppMetadataController from './controllers/app-metadata'; import { + CaveatFactories, CaveatMutatorFactories, - getAuthorizedScopesByOrigin, getCaveatSpecifications, - getChangedAuthorizations, diffMap, getPermissionBackgroundApiMethods, getPermissionSpecifications, getPermittedAccountsByOrigin, - getRemovedAuthorizations, getPermittedChainsByOrigin, NOTIFICATION_NAMES, - unrestrictedMethods, PermissionNames, + unrestrictedMethods, } from './controllers/permissions'; import { MetaMetricsDataDeletionController } from './controllers/metametrics-data-deletion/metametrics-data-deletion'; import { DataDeletionService } from './services/data-deletion-service'; @@ -365,7 +348,6 @@ import { createTxVerificationMiddleware } from './lib/tx-verification/tx-verific import { updateSecurityAlertResponse } from './lib/ppom/ppom-util'; import createEvmMethodsToNonEvmAccountReqFilterMiddleware from './lib/createEvmMethodsToNonEvmAccountReqFilterMiddleware'; import { isEthAddress } from './lib/multichain/address'; - import { decodeTransactionData } from './lib/transaction/decode/util'; import { BridgeUserAction, @@ -380,7 +362,6 @@ import { import createTracingMiddleware from './lib/createTracingMiddleware'; import { PatchStore } from './lib/PatchStore'; import { sanitizeUIState } from './lib/state-utils'; -import { walletCreateSessionHandler } from './lib/multichain-api/wallet-createSession'; export const METAMASK_CONTROLLER_EVENTS = { // Fired after state changes that impact the extension badge (unapproved msg count) @@ -589,19 +570,7 @@ export default class MetamaskController extends EventEmitter { state: initialNetworkControllerState, infuraProjectId: opts.infuraProjectId, }); - this.networkController.initializeProvider(); - this.multichainSubscriptionManager = new MultichainSubscriptionManager({ - getNetworkClientById: this.networkController.getNetworkClientById.bind( - this.networkController, - ), - findNetworkClientIdByChainId: - this.networkController.findNetworkClientIdByChainId.bind( - this.networkController, - ), - }); - - this.multichainMiddlewareManager = new MultichainMiddlewareManager(); this.provider = this.networkController.getProviderAndBlockTracker().provider; this.blockTracker = @@ -1231,16 +1200,51 @@ export default class MetamaskController extends EventEmitter { ], }), state: initState.PermissionController, - caveatSpecifications: getCaveatSpecifications(), + caveatSpecifications: getCaveatSpecifications({ + getInternalAccounts: this.accountsController.listAccounts.bind( + this.accountsController, + ), + findNetworkClientIdByChainId: + this.networkController.findNetworkClientIdByChainId.bind( + this.networkController, + ), + }), permissionSpecifications: { ...getPermissionSpecifications({ getInternalAccounts: this.accountsController.listAccounts.bind( this.accountsController, ), - findNetworkClientIdByChainId: - this.networkController.findNetworkClientIdByChainId.bind( - this.networkController, - ), + getAllAccounts: this.keyringController.getAccounts.bind( + this.keyringController, + ), + captureKeyringTypesWithMissingIdentities: ( + internalAccounts = [], + accounts = [], + ) => { + const accountsMissingIdentities = accounts.filter( + (address) => + !internalAccounts.some( + (account) => + account.address.toLowerCase() === address.toLowerCase(), + ), + ); + const keyringTypesWithMissingIdentities = + accountsMissingIdentities.map((address) => + this.keyringController.getAccountKeyringType(address), + ); + + const internalAccountCount = internalAccounts.length; + + const accountTrackerCount = Object.keys( + this.accountTrackerController.state.accounts || {}, + ).length; + + captureException( + new Error( + `Attempt to get permission specifications failed because their were ${accounts.length} accounts, but ${internalAccountCount} identities, and the ${keyringTypesWithMissingIdentities} keyrings included accounts with missing identities. Meanwhile, there are ${accountTrackerCount} accounts in the account tracker.`, + ), + ); + }, }), ...this.getSnapPermissionSpecifications(), }, @@ -1548,7 +1552,7 @@ export default class MetamaskController extends EventEmitter { }, }, env: { - isAccountSyncingEnabled: false, // TODO: undo this once fixed + isAccountSyncingEnabled: isManifestV3, }, messenger: this.controllerMessenger.getRestricted({ name: 'UserStorageController', @@ -1857,7 +1861,7 @@ export default class MetamaskController extends EventEmitter { this.networkController, ), getNetworkState: () => this.networkController.state, - getPermittedAccounts: this.getPermittedAccountsSorted.bind(this), + getPermittedAccounts: this.getPermittedAccounts.bind(this), getSavedGasFees: () => this.preferencesController.state.advancedGasFee[ getCurrentChainId({ metamask: this.networkController.state }) @@ -2245,13 +2249,18 @@ export default class MetamaskController extends EventEmitter { }, version, // account mgmt - getAccounts: async ({ origin: innerOrigin }) => { + getAccounts: async ( + { origin: innerOrigin }, + { suppressUnauthorizedError = true } = {}, + ) => { if (innerOrigin === ORIGIN_METAMASK) { const selectedAddress = this.accountsController.getSelectedAccount().address; return selectedAddress ? [selectedAddress] : []; } else if (this.isUnlocked()) { - return await this.getPermittedAccounts(innerOrigin); + return await this.getPermittedAccounts(innerOrigin, { + suppressUnauthorizedError, + }); } return []; // changing this is a breaking change }, @@ -2882,86 +2891,6 @@ export default class MetamaskController extends EventEmitter { getPermittedAccountsByOrigin, ); - // This handles CAIP-25 authorization changes every time relevant permission state - // changes, for any reason. - this.controllerMessenger.subscribe( - `${this.permissionController.name}:stateChange`, - async (currentValue, previousValue) => { - const changedAuthorizations = getChangedAuthorizations( - currentValue, - previousValue, - ); - - const removedAuthorizations = getRemovedAuthorizations( - currentValue, - previousValue, - ); - - // remove any existing notification subscriptions for removed authorizations - for (const [origin, authorization] of removedAuthorizations.entries()) { - const mergedScopes = mergeScopes( - authorization.requiredScopes, - authorization.optionalScopes, - ); - // if the eth_subscription notification is in the scope and eth_subscribe is in the methods - // then remove middleware and unsubscribe - Object.entries(mergedScopes).forEach(([scope, scopeObject]) => { - if ( - scopeObject.notifications.includes('eth_subscription') && - scopeObject.methods.includes('eth_subscribe') - ) { - this.multichainMiddlewareManager.removeMiddlewareByScopeAndOrigin( - scope, - origin, - ); - this.multichainSubscriptionManager.unsubscribeByScopeAndOrigin( - scope, - origin, - ); - } - }); - } - - // add new notification subscriptions for changed authorizations - for (const [origin, authorization] of changedAuthorizations.entries()) { - const mergedScopes = mergeScopes( - authorization.requiredScopes, - authorization.optionalScopes, - ); - - // if the eth_subscription notification is in the scope and eth_subscribe is in the methods - // then get the subscriptionManager going for that scope - Object.entries(mergedScopes).forEach(([scope, scopeObject]) => { - if ( - scopeObject.notifications.includes('eth_subscription') && - scopeObject.methods.includes('eth_subscribe') - ) { - // for each tabId - Object.entries(this.connections[origin]).forEach( - ([_, { tabId }]) => { - const subscriptionManager = - this.multichainSubscriptionManager.subscribe({ - scope, - origin, - tabId, - }); - this.multichainMiddlewareManager.addMiddleware({ - scope, - origin, - tabId, - middleware: subscriptionManager.middleware, - }); - }, - ); - } - }); - - this._notifyAuthorizationChange(origin, authorization); - } - }, - getAuthorizedScopesByOrigin, - ); - this.controllerMessenger.subscribe( `${this.permissionController.name}:stateChange`, async (currentValue, previousValue) => { @@ -3504,7 +3433,9 @@ export default class MetamaskController extends EventEmitter { updateNetwork: this.networkController.updateNetwork.bind( this.networkController, ), - removeNetwork: this.removeNetwork.bind(this), + removeNetwork: this.networkController.removeNetwork.bind( + this.networkController, + ), getCurrentNetworkEIP1559Compatibility: this.networkController.getEIP1559Compatibility.bind( this.networkController, @@ -3781,10 +3712,7 @@ export default class MetamaskController extends EventEmitter { removePermissionsFor: this.removePermissionsFor, approvePermissionsRequest: this.acceptPermissionsRequest, rejectPermissionsRequest: this.rejectPermissionsRequest, - ...getPermissionBackgroundApiMethods({ - permissionController, - approvalController, - }), + ...getPermissionBackgroundApiMethods(permissionController), ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) connectCustodyAddresses: this.mmiController.connectCustodyAddresses.bind( @@ -4927,142 +4855,56 @@ export default class MetamaskController extends EventEmitter { return selectedAddress; } - captureKeyringTypesWithMissingIdentities( - internalAccounts = [], - accounts = [], - ) { - const accountsMissingIdentities = accounts.filter( - (address) => - !internalAccounts.some( - (account) => account.address.toLowerCase() === address.toLowerCase(), - ), - ); - const keyringTypesWithMissingIdentities = accountsMissingIdentities.map( - (address) => this.keyringController.getAccountKeyringType(address), - ); - - const internalAccountCount = internalAccounts.length; - - const accountTrackerCount = Object.keys( - this.accountTrackerController.state.accounts || {}, - ).length; - - captureException( - new Error( - `Attempt to get permission specifications failed because their were ${accounts.length} accounts, but ${internalAccountCount} identities, and the ${keyringTypesWithMissingIdentities} keyrings included accounts with missing identities. Meanwhile, there are ${accountTrackerCount} accounts in the account tracker.`, - ), - ); - } - - async getAllEvmAccountsSorted() { - // We only consider EVM addresses here, hence the filtering: - const accounts = (await this.keyringController.getAccounts()).filter( - isValidHexAddress, - ); - const internalAccounts = this.accountsController.listAccounts(); - - return accounts.sort((firstAddress, secondAddress) => { - const firstAccount = internalAccounts.find( - (internalAccount) => - internalAccount.address.toLowerCase() === firstAddress.toLowerCase(), - ); - - const secondAccount = internalAccounts.find( - (internalAccount) => - internalAccount.address.toLowerCase() === secondAddress.toLowerCase(), - ); - - if (!firstAccount) { - this.captureKeyringTypesWithMissingIdentities( - internalAccounts, - accounts, - ); - throw new Error(`Missing identity for address: "${firstAddress}".`); - } else if (!secondAccount) { - this.captureKeyringTypesWithMissingIdentities( - internalAccounts, - accounts, - ); - throw new Error(`Missing identity for address: "${secondAddress}".`); - } else if ( - firstAccount.metadata.lastSelected === - secondAccount.metadata.lastSelected - ) { - return 0; - } else if (firstAccount.metadata.lastSelected === undefined) { - return 1; - } else if (secondAccount.metadata.lastSelected === undefined) { - return -1; - } - - return ( - secondAccount.metadata.lastSelected - firstAccount.metadata.lastSelected - ); - }); - } - /** * Gets the permitted accounts for the specified origin. Returns an empty * array if no accounts are permitted. * * @param {string} origin - The origin whose exposed accounts to retrieve. + * @param {boolean} [suppressUnauthorizedError] - Suppresses the unauthorized error. * @returns {Promise} The origin's permitted accounts, or an empty * array. */ - getPermittedAccounts(origin) { - let caveat; + async getPermittedAccounts( + origin, + { suppressUnauthorizedError = true } = {}, + ) { try { - caveat = this.permissionController.getCaveat( + return await this.permissionController.executeRestrictedMethod( origin, - Caip25EndowmentPermissionName, - Caip25CaveatType, + RestrictedMethods.eth_accounts, ); - } catch (err) { - // noop - } - if (!caveat) { - return []; + } catch (error) { + if ( + suppressUnauthorizedError && + error.code === rpcErrorCodes.provider.unauthorized + ) { + return []; + } + throw error; } - - return getEthAccounts(caveat.value); - } - - async getPermittedAccountsSorted(origin) { - const permittedAccounts = this.getPermittedAccounts(origin); - const allEvmAccounts = await this.getAllEvmAccountsSorted(); - return allEvmAccounts.filter((account) => - permittedAccounts.includes(account), - ); } /** * Stops exposing the specified chain ID to all third parties. + * Exposed chain IDs are stored in caveats of the `endowment:permitted-chains` + * permission. This method uses `PermissionController.updatePermissionsByCaveat` + * to remove the specified chain ID from every `endowment:permitted-chains` + * permission. If a permission only included this chain ID, the permission is + * revoked entirely. * * @param {string} targetChainId - The chain ID to stop exposing * to third parties. */ removeAllChainIdPermissions(targetChainId) { this.permissionController.updatePermissionsByCaveat( - Caip25CaveatType, - (existingScopes) => - Caip25CaveatMutatorFactories[Caip25CaveatType].removeScope( - toCaipChainId('eip155', parseInt(targetChainId, 16)), - existingScopes, - ), + CaveatTypes.restrictNetworkSwitching, + (existingChainIds) => + CaveatMutatorFactories[ + CaveatTypes.restrictNetworkSwitching + ].removeChainId(targetChainId, existingChainIds), ); } - // Figure out what needs to be done with the middleware/subscription logic - removeNetwork(chainId) { - const scope = `eip155:${parseInt(chainId, 16)}`; - this.multichainSubscriptionManager.unsubscribeByScope(scope); - this.multichainMiddlewareManager.removeMiddlewareByScope(scope); - - this.removeAllChainIdPermissions(chainId); - - this.networkController.removeNetwork(chainId); - } - /** * Stops exposing the account with the specified address to all third parties. * Exposed accounts are stored in caveats of the eth_accounts permission. This @@ -5081,14 +4923,6 @@ export default class MetamaskController extends EventEmitter { CaveatTypes.restrictReturnedAccounts ].removeAccount(targetAccount, existingAccounts), ); - this.permissionController.updatePermissionsByCaveat( - Caip25CaveatType, - (existingScopes) => - Caip25CaveatMutatorFactories[Caip25CaveatType].removeAccount( - targetAccount, - existingScopes, - ), - ); } /** @@ -5130,28 +4964,6 @@ export default class MetamaskController extends EventEmitter { this.preferencesController.setSelectedAddress(importedAccountAddress); } - /** - * Requests approval for permissions for the specified origin - * - * @param origin - The origin to request approval for. - * @param permissions - The permissions to request approval for. - */ - async requestPermissionApprovalForOrigin(origin, permissions) { - const id = nanoid(); - return this.approvalController.addAndShowApprovalRequest({ - id, - origin, - requestData: { - metadata: { - id, - origin, - }, - permissions, - }, - type: MethodNames.requestPermissions, - }); - } - // --------------------------------------------------------------------------- // Identity Management (signature operations) @@ -5659,7 +5471,7 @@ export default class MetamaskController extends EventEmitter { // setup connection const providerStream = createEngineStream({ engine }); - const connectionId = this.addConnection(origin, { tabId, engine }); + const connectionId = this.addConnection(origin, { engine }); pipeline( outStream, @@ -5727,7 +5539,7 @@ export default class MetamaskController extends EventEmitter { // setup connection const providerStream = createEngineStream({ engine }); - const connectionId = this.addConnection(origin, { tabId, engine }); + const connectionId = this.addConnection(origin, { engine }); pipeline( outStream, @@ -5735,15 +5547,6 @@ export default class MetamaskController extends EventEmitter { providerStream, outStream, (err) => { - this.multichainMiddlewareManager.removeMiddlewareByOriginAndTabId( - origin, - tabId, - ); - this.multichainSubscriptionManager.unsubscribeByOriginAndTabId( - origin, - tabId, - ); - // handle any middleware cleanup engine._middleware.forEach((mid) => { if (mid.destroy && typeof mid.destroy === 'function') { @@ -5803,8 +5606,6 @@ export default class MetamaskController extends EventEmitter { useRequestQueue: this.preferencesController.getUseRequestQueue.bind( this.preferencesController, ), - // TODO: Should this be made async in queued-request-controller package? - // Doing so allows us to DRY up getPermittedAcounts and getPermittedAccountsSorted shouldEnqueueRequest: (request) => { return methodsRequiringNetworkSwitch.includes(request.method); }, @@ -5878,25 +5679,13 @@ export default class MetamaskController extends EventEmitter { }), ); - engine.push(createUnsupportedMethodMiddleware(UNSUPPORTED_RPC_METHODS)); - - engine.push((req, res, next, end) => - caipPermissionAdapterMiddleware(req, res, next, end, { - getCaveat: this.permissionController.getCaveat.bind( - this.permissionController, - ), - getNetworkConfigurationByNetworkClientId: - this.networkController.getNetworkConfigurationByNetworkClientId.bind( - this.networkController, - ), - }), - ); + engine.push(createUnsupportedMethodMiddleware()); - // Legacy RPC method that needs to be implemented _ahead of_ the permission + // Legacy RPC methods that need to be implemented _ahead of_ the permission // middleware. engine.push( - createEthAccountsMethodMiddleware({ - getAccounts: this.getPermittedAccountsSorted.bind(this, origin), + createLegacyMethodMiddleware({ + getAccounts: this.getPermittedAccounts.bind(this, origin), }), ); @@ -5931,7 +5720,9 @@ export default class MetamaskController extends EventEmitter { // Unrestricted/permissionless RPC method implementations. // They must nevertheless be placed _behind_ the permission middleware. engine.push( - createEip1193MethodMiddleware({ + createMethodMiddleware({ + origin, + subjectType, // Miscellaneous @@ -5959,21 +5750,59 @@ export default class MetamaskController extends EventEmitter { this.metaMetricsController, ), // Permission-related - getAccounts: this.getPermittedAccountsSorted.bind(this, origin), + getAccounts: this.getPermittedAccounts.bind(this, origin), getPermissionsForOrigin: this.permissionController.getPermissions.bind( this.permissionController, origin, ), - requestPermissionApprovalForOrigin: - this.requestPermissionApprovalForOrigin.bind(this, origin), + hasPermission: this.permissionController.hasPermission.bind( + this.permissionController, + origin, + ), + requestAccountsPermission: + this.permissionController.requestPermissions.bind( + this.permissionController, + { origin }, + { + eth_accounts: {}, + ...(!isSnapId(origin) && { + [PermissionNames.permittedChains]: {}, + }), + }, + ), + requestPermittedChainsPermission: (chainIds) => + this.permissionController.requestPermissionsIncremental( + { origin }, + { + [PermissionNames.permittedChains]: { + caveats: [ + CaveatFactories[CaveatTypes.restrictNetworkSwitching]( + chainIds, + ), + ], + }, + }, + ), + grantPermittedChainsPermissionIncremental: (chainIds) => + this.permissionController.grantPermissionsIncremental({ + subject: { origin }, + approvedPermissions: { + [PermissionNames.permittedChains]: { + caveats: [ + CaveatFactories[CaveatTypes.restrictNetworkSwitching]( + chainIds, + ), + ], + }, + }, + }), requestPermissionsForOrigin: (requestedPermissions) => this.permissionController.requestPermissions( { origin }, { - ...(requestedPermissions[PermissionNames.eth_accounts] && - !isSnapId(origin) && { - [PermissionNames.permittedChains]: {}, - }), + ...(requestedPermissions[PermissionNames.eth_accounts] && { + [PermissionNames.permittedChains]: {}, + }), ...(requestedPermissions[PermissionNames.permittedChains] && { [PermissionNames.eth_accounts]: {}, }), @@ -6015,12 +5844,12 @@ export default class MetamaskController extends EventEmitter { // network configuration-related setActiveNetwork: async (networkClientId) => { await this.networkController.setActiveNetwork(networkClientId); - // if the origin has the CAIP-25 permission + // if the origin has the eth_accounts permission // we set per dapp network selection state if ( this.permissionController.hasPermission( origin, - Caip25EndowmentPermissionName, + PermissionNames.eth_accounts, ) ) { this.selectedNetworkController.setNetworkClientIdForDomain( @@ -6058,13 +5887,6 @@ export default class MetamaskController extends EventEmitter { this.alertController, ), - grantPermissions: this.permissionController.grantPermissions.bind( - this.permissionController, - ), - updateCaveat: this.permissionController.updateCaveat.bind( - this.permissionController, - ), - ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) handleMmiAuthenticate: this.institutionalFeaturesController.handleMmiAuthenticate.bind( @@ -6179,7 +6001,7 @@ export default class MetamaskController extends EventEmitter { } /** - * A method for creating a provider that is safely restricted for the requesting subject. + * A method for creating a CAIP provider that is safely restricted for the requesting subject. * * @param {object} options - Provider engine options * @param {string} options.origin - The origin of the sender @@ -6188,290 +6010,9 @@ export default class MetamaskController extends EventEmitter { setupProviderEngineCaip({ origin, tabId }) { const engine = new JsonRpcEngine(); - // Append origin to each request - engine.push(createOriginMiddleware({ origin })); - - // Append tabId to each request if it exists - if (tabId) { - engine.push(createTabIdMiddleware({ tabId })); - } - - engine.push(createLoggerMiddleware({ origin })); - - engine.push((req, _res, next, end) => { - if ( - ![ - MESSAGE_TYPE.WALLET_CREATE_SESSION, - MESSAGE_TYPE.WALLET_INVOKE_METHOD, - MESSAGE_TYPE.WALLET_GET_SESSION, - MESSAGE_TYPE.WALLET_REVOKE_SESSION, - ].includes(req.method) - ) { - return end(new Error('Invalid method')); // TODO: Use a proper error - } - return next(); - }); - - // TODO: Uncomment this when wallet lifecycle methods are added to api-specs - engine.push(multichainMethodCallValidatorMiddleware); - - engine.push( - createScaffoldMiddleware({ - [MESSAGE_TYPE.WALLET_CREATE_SESSION]: ( - request, - response, - next, - end, - ) => { - return walletCreateSessionHandler(request, response, next, end, { - grantPermissions: this.permissionController.grantPermissions.bind( - this.permissionController, - ), - findNetworkClientIdByChainId: - this.networkController.findNetworkClientIdByChainId.bind( - this.networkController, - ), - listAccounts: this.accountsController.listAccounts.bind( - this.accountsController, - ), - addNetwork: this.networkController.addNetwork.bind( - this.networkController, - ), - removeNetwork: this.removeNetwork.bind(this), - requestPermissionApprovalForOrigin: - this.requestPermissionApprovalForOrigin.bind(this, origin), - sendMetrics: this.metaMetricsController.trackEvent.bind( - this.metaMetricsController, - ), - metamaskState: this.getState(), - }); - }, - [MESSAGE_TYPE.WALLET_INVOKE_METHOD]: (request, response, next, end) => { - return walletInvokeMethodHandler(request, response, next, end, { - findNetworkClientIdByChainId: - this.networkController.findNetworkClientIdByChainId.bind( - this.networkController, - ), - getCaveat: this.permissionController.getCaveat.bind( - this.permissionController, - ), - getSelectedNetworkClientId: () => - this.networkController.state.selectedNetworkClientId, - }); - }, - [MESSAGE_TYPE.WALLET_REVOKE_SESSION]: ( - request, - response, - next, - end, - ) => { - return walletRevokeSessionHandler(request, response, next, end, { - revokePermission: this.permissionController.revokePermission.bind( - this.permissionController, - ), - }); - }, - [MESSAGE_TYPE.WALLET_GET_SESSION]: (request, response, next, end) => { - return walletGetSessionHandler(request, response, next, end, { - getCaveat: this.permissionController.getCaveat.bind( - this.permissionController, - ), - }); - }, - }), - ); - - // TODO: Does this need to go before the wallet_createSession middleware? - // Add a middleware that will switch chain on each request (as needed) - const requestQueueMiddleware = createQueuedRequestMiddleware({ - enqueueRequest: this.queuedRequestController.enqueueRequest.bind( - this.queuedRequestController, - ), - useRequestQueue: this.preferencesController.getUseRequestQueue.bind( - this.preferencesController, - ), - shouldEnqueueRequest: (request) => { - return methodsRequiringNetworkSwitch.includes(request.method); - }, - }); - engine.push(requestQueueMiddleware); - - engine.push( - createUnsupportedMethodMiddleware([ - ...UNSUPPORTED_RPC_METHODS, - 'eth_requestAccounts', - 'eth_accounts', - ]), - ); - - engine.push( - createMultichainMethodMiddleware({ - subjectType: SubjectType.Website, // TODO: this should probably be passed in - - // Miscellaneous - addSubjectMetadata: - this.subjectMetadataController.addSubjectMetadata.bind( - this.subjectMetadataController, - ), - getProviderState: this.getProviderState.bind(this), - handleWatchAssetRequest: this.handleWatchAssetRequest.bind(this), - requestUserApproval: - this.approvalController.addAndShowApprovalRequest.bind( - this.approvalController, - ), - startApprovalFlow: this.approvalController.startFlow.bind( - this.approvalController, - ), - endApprovalFlow: this.approvalController.endFlow.bind( - this.approvalController, - ), - getCaveat: ({ target, caveatType }) => { - try { - return this.permissionController.getCaveat( - origin, - target, - caveatType, - ); - } catch (e) { - if (e instanceof PermissionDoesNotExistError) { - // suppress expected error in case that the origin - // does not have the target permission yet - } else { - throw e; - } - } - - return undefined; - }, - addNetwork: this.networkController.addNetwork.bind( - this.networkController, - ), - updateNetwork: this.networkController.updateNetwork.bind( - this.networkController, - ), - setActiveNetwork: async (networkClientId) => { - await this.networkController.setActiveNetwork(networkClientId); - // if the origin has the CAIP-25 permission - // we set per dapp network selection state - if ( - this.permissionController.hasPermission( - origin, - Caip25EndowmentPermissionName, - ) - ) { - this.selectedNetworkController.setNetworkClientIdForDomain( - origin, - networkClientId, - ); - } - }, - getNetworkConfigurationByChainId: - this.networkController.getNetworkConfigurationByChainId.bind( - this.networkController, - ), - // TODO refactor `add-ethereum-chain` handler so that this hook can be removed from multichain middleware - getCurrentChainIdForDomain: (domain) => { - const networkClientId = - this.selectedNetworkController.getNetworkClientIdForDomain(domain); - const { chainId } = - this.networkController.getNetworkConfigurationByNetworkClientId( - networkClientId, - ); - return chainId; - }, - - // Web3 shim-related - getWeb3ShimUsageState: this.alertController.getWeb3ShimUsageState.bind( - this.alertController, - ), - setWeb3ShimUsageRecorded: - this.alertController.setWeb3ShimUsageRecorded.bind( - this.alertController, - ), - - requestPermissionApprovalForOrigin: - this.requestPermissionApprovalForOrigin.bind(this, origin), - updateCaveat: this.permissionController.updateCaveat.bind( - this.permissionController, - ), - grantPermissions: this.permissionController.grantPermissions.bind( - this.permissionController, - ), - }), - ); - - engine.push(this.metamaskMiddleware); - - // TODO: Might be able to DRY this with the stateChange event - try { - const caip25Caveat = this.permissionController.getCaveat( - origin, - Caip25EndowmentPermissionName, - Caip25CaveatType, - ); - - // add new notification subscriptions for changed authorizations - const mergedScopes = mergeScopes( - caip25Caveat.value.requiredScopes, - caip25Caveat.value.optionalScopes, - ); - - // if the eth_subscription notification is in the scope and eth_subscribe is in the methods - // then get the subscriptionManager going for that scope - Object.entries(mergedScopes).forEach(([scope, scopeObject]) => { - if ( - scopeObject.notifications.includes('eth_subscription') && - scopeObject.methods.includes('eth_subscribe') - ) { - const subscriptionManager = - this.multichainSubscriptionManager.subscribe({ - scope, - origin, - tabId, - }); - this.multichainMiddlewareManager.addMiddleware({ - scope, - origin, - tabId, - middleware: subscriptionManager.middleware, - }); - } - }); - } catch (err) { - // noop - } - - this.multichainSubscriptionManager.on( - 'notification', - (targetOrigin, targetTabId, message) => { - if (origin === targetOrigin && tabId === targetTabId) { - engine.emit('notification', message); - } - }, - ); - - engine.push( - this.multichainMiddlewareManager.generateMultichainMiddlewareForOriginAndTabId( - origin, - tabId, - ), - ); - - engine.push((req, res, _next, end) => { - const { provider } = this.networkController.getNetworkClientById( - req.networkClientId, - ); - - // send request to provider - provider.sendAsync(req, (err, providerRes) => { - // forward any error - if (err instanceof Error) { - return end(err); - } - // copy provider response onto original response - Object.assign(res, providerRes); - return end(); - }); + engine.push((request, _res, _next, end) => { + console.log('CAIP request received', { origin, tabId, request }); + return end(new Error('CAIP RPC Pipeline not yet implemented.')); }); return engine; @@ -6508,10 +6049,9 @@ export default class MetamaskController extends EventEmitter { * @param {string} origin - The connection's origin string. * @param {object} options - Data associated with the connection * @param {object} options.engine - The connection's JSON Rpc Engine - * @param options.tabId * @returns {string} The connection's id (so that it can be deleted later) */ - addConnection(origin, { tabId, engine }) { + addConnection(origin, { engine }) { if (origin === ORIGIN_METAMASK) { return null; } @@ -6522,7 +6062,6 @@ export default class MetamaskController extends EventEmitter { const id = nanoid(); this.connections[origin][id] = { - tabId, engine, }; @@ -6678,7 +6217,7 @@ export default class MetamaskController extends EventEmitter { method: NOTIFICATION_NAMES.unlockStateChanged, params: { isUnlocked: true, - accounts: await this.getPermittedAccountsSorted(origin), + accounts: await this.getPermittedAccounts(origin), }, }; }); @@ -6719,7 +6258,7 @@ export default class MetamaskController extends EventEmitter { */ _onStateUpdate(newState) { this.isClientOpenAndUnlocked = newState.isUnlocked && this._isClientOpen; - // this._notifyChainChange(); + this._notifyChainChange(); } // misc @@ -7209,27 +6748,13 @@ export default class MetamaskController extends EventEmitter { newAccounts : // If the length is 2 or greater, we have to execute // `eth_accounts` vi this method. - await this.getPermittedAccountsSorted(origin), + await this.getPermittedAccounts(origin), }); } this.permissionLogController.updateAccountsHistory(origin, newAccounts); } - async _notifyAuthorizationChange(origin, newAuthorization) { - if (this.isUnlocked()) { - this.notifyConnections(origin, { - method: NOTIFICATION_NAMES.sessionChanged, - params: { - sessionScopes: mergeScopes( - newAuthorization.requiredScopes ?? {}, - newAuthorization.optionalScopes ?? {}, - ), - }, - }); - } - } - async _notifyChainChange() { if (this.preferencesController.getUseRequestQueue()) { this.notifyAllConnections(async (origin) => ({ diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 09022da677c0..77b062bcfdc7 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -102,13 +102,10 @@ const createLoggerMiddlewareMock = () => (req, res, next) => { jest.mock('./lib/createLoggerMiddleware', () => createLoggerMiddlewareMock); const rpcMethodMiddlewareMock = { - createEip1193MethodMiddleware: () => (_req, _res, next, _end) => { + createMethodMiddleware: () => (_req, _res, next, _end) => { next(); }, - createEthAccountsMethodMiddleware: () => (_req, _res, next, _end) => { - next(); - }, - createMultichainMethodMiddleware: () => (_req, _res, next, _end) => { + createLegacyMethodMiddleware: () => (_req, _res, next, _end) => { next(); }, createUnsupportedMethodMiddleware: () => (_req, _res, next, _end) => { @@ -1400,10 +1397,6 @@ describe('MetaMaskController', () => { }); describe('#setupUntrustedCommunicationCaip', () => { - it.todo('adds a tabId, origin and networkClient to requests'); - - it.todo('should add only origin to request if tabId not provided'); - it.todo('should only process `caip-x` CAIP formatted messages'); }); From c7ad0b412f7fee34fa6c25058d0530dc8dc806e5 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 14 Oct 2024 12:29:13 -0700 Subject: [PATCH 005/148] Bring back MMC changes. Remove multichain from createMethodMiddleware --- .../createMethodMiddleware.js | 4 - .../createMethodMiddleware.test.js | 8 +- app/scripts/metamask-controller.js | 340 ++++++++++-------- app/scripts/metamask-controller.test.js | 7 +- 4 files changed, 206 insertions(+), 153 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js index b44ba5397c2f..af34952c29df 100644 --- a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js +++ b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js @@ -27,10 +27,6 @@ export const createEthAccountsMethodMiddleware = makeMethodMiddlewareMaker([ ethAccountsHandler, ]); -// The primary home of RPC method implementations for the MultiChain API. -export const createMultichainMethodMiddleware = - makeMethodMiddlewareMaker(localHandlers); - /** * Creates a method middleware factory function given a set of method handlers. * diff --git a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.test.js b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.test.js index fdcff0b459a6..0bf416e8bd75 100644 --- a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.test.js +++ b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.test.js @@ -6,7 +6,6 @@ import { import { createEip1193MethodMiddleware, createEthAccountsMethodMiddleware, - createMultichainMethodMiddleware, } from '.'; const getHandler = () => ({ @@ -40,15 +39,15 @@ jest.mock('@metamask/permission-controller', () => ({ ...jest.requireActual('@metamask/permission-controller'), })); -jest.mock('../multichain-api/wallet-getPermissions', () => ({ +jest.mock('./handlers/wallet-getPermissions', () => ({ getPermissionsHandler: getHandler(), })); -jest.mock('../multichain-api/wallet-requestPermissions', () => ({ +jest.mock('./handlers/wallet-requestPermissions', () => ({ requestPermissionsHandler: getHandler(), })); -jest.mock('../multichain-api/wallet-revokePermissions', () => ({ +jest.mock('./handlers/wallet-revokePermissions', () => ({ revokePermissionsHandler: getHandler(), })); @@ -61,7 +60,6 @@ jest.mock('./handlers', () => ({ describe.each([ ['createEip1193MethodMiddleware', createEip1193MethodMiddleware], ['createEthAccountsMethodMiddleware', createEthAccountsMethodMiddleware], - ['createMultichainMethodMiddleware', createMultichainMethodMiddleware], ])('%s', (_name, createMiddleware) => { const method1 = 'method1'; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index b19c91a232ab..357e302d151a 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -15,7 +15,7 @@ import { } from '@metamask/assets-controllers'; import { ObservableStore } from '@metamask/obs-store'; import { storeAsStream } from '@metamask/obs-store/dist/asStream'; -import { JsonRpcEngine } from 'json-rpc-engine'; +import { JsonRpcEngine, createScaffoldMiddleware } from 'json-rpc-engine'; import { createEngineStream } from 'json-rpc-middleware-stream'; import { providerAsMiddleware } from '@metamask/eth-json-rpc-middleware'; import { debounce, throttle, memoize, wrap } from 'lodash'; @@ -25,11 +25,7 @@ import { } from '@metamask/keyring-controller'; import createFilterMiddleware from '@metamask/eth-json-rpc-filters'; import createSubscriptionManager from '@metamask/eth-json-rpc-filters/subscriptionManager'; -import { - errorCodes as rpcErrorCodes, - EthereumRpcError, - ethErrors, -} from 'eth-rpc-errors'; +import { EthereumRpcError, ethErrors } from 'eth-rpc-errors'; import { Mutex } from 'await-semaphore'; import log from 'loglevel'; @@ -62,6 +58,7 @@ import { } from '@metamask/network-controller'; import { GasFeeController } from '@metamask/gas-fee-controller'; import { + MethodNames, PermissionController, PermissionDoesNotExistError, PermissionsRequestNotFoundError, @@ -147,6 +144,7 @@ import { import { Interface } from '@ethersproject/abi'; import { abiERC1155, abiERC721 } from '@metamask/metamask-eth-abis'; import { isEvmAccountType } from '@metamask/keyring-api'; +import { isValidHexAddress, toCaipChainId } from '@metamask/utils'; import { AuthenticationController, UserStorageController, @@ -155,6 +153,20 @@ import { NotificationServicesPushController, NotificationServicesController, } from '@metamask/notification-services-controller'; +import { + walletInvokeMethodHandler, + Caip25CaveatMutatorFactories, + Caip25CaveatType, + Caip25EndowmentPermissionName, + multichainMethodCallValidatorMiddleware, + MultichainSubscriptionManager, + MultichainMiddlewareManager, + walletRevokeSessionHandler, + walletGetSessionHandler, + mergeScopes, + getEthAccounts, + caipPermissionAdapterMiddleware, +} from '@metamask/multichain'; import { methodsRequiringNetworkSwitch } from '../../shared/constants/methods-tags'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) @@ -192,6 +204,7 @@ import { MILLISECOND, SECOND } from '../../shared/constants/time'; import { ORIGIN_METAMASK, POLLING_TOKEN_ENVIRONMENT_TYPES, + MESSAGE_TYPE, } from '../../shared/constants/app'; import { MetaMetricsEventCategory, @@ -280,8 +293,8 @@ import AccountTrackerController from './controllers/account-tracker-controller'; import createDupeReqFilterStream from './lib/createDupeReqFilterStream'; import createLoggerMiddleware from './lib/createLoggerMiddleware'; import { - createLegacyMethodMiddleware, - createMethodMiddleware, + createEthAccountsMethodMiddleware, + createEip1193MethodMiddleware, createUnsupportedMethodMiddleware, } from './lib/rpc-method-middleware'; import createOriginMiddleware from './lib/createOriginMiddleware'; @@ -311,17 +324,19 @@ import EncryptionPublicKeyController from './controllers/encryption-public-key'; import AppMetadataController from './controllers/app-metadata'; import { - CaveatFactories, CaveatMutatorFactories, + getAuthorizedScopesByOrigin, getCaveatSpecifications, + getChangedAuthorizations, diffMap, getPermissionBackgroundApiMethods, getPermissionSpecifications, getPermittedAccountsByOrigin, + getRemovedAuthorizations, getPermittedChainsByOrigin, NOTIFICATION_NAMES, - PermissionNames, unrestrictedMethods, + PermissionNames, } from './controllers/permissions'; import { MetaMetricsDataDeletionController } from './controllers/metametrics-data-deletion/metametrics-data-deletion'; import { DataDeletionService } from './services/data-deletion-service'; @@ -1200,51 +1215,16 @@ export default class MetamaskController extends EventEmitter { ], }), state: initState.PermissionController, - caveatSpecifications: getCaveatSpecifications({ - getInternalAccounts: this.accountsController.listAccounts.bind( - this.accountsController, - ), - findNetworkClientIdByChainId: - this.networkController.findNetworkClientIdByChainId.bind( - this.networkController, - ), - }), + caveatSpecifications: getCaveatSpecifications(), permissionSpecifications: { ...getPermissionSpecifications({ getInternalAccounts: this.accountsController.listAccounts.bind( this.accountsController, ), - getAllAccounts: this.keyringController.getAccounts.bind( - this.keyringController, - ), - captureKeyringTypesWithMissingIdentities: ( - internalAccounts = [], - accounts = [], - ) => { - const accountsMissingIdentities = accounts.filter( - (address) => - !internalAccounts.some( - (account) => - account.address.toLowerCase() === address.toLowerCase(), - ), - ); - const keyringTypesWithMissingIdentities = - accountsMissingIdentities.map((address) => - this.keyringController.getAccountKeyringType(address), - ); - - const internalAccountCount = internalAccounts.length; - - const accountTrackerCount = Object.keys( - this.accountTrackerController.state.accounts || {}, - ).length; - - captureException( - new Error( - `Attempt to get permission specifications failed because their were ${accounts.length} accounts, but ${internalAccountCount} identities, and the ${keyringTypesWithMissingIdentities} keyrings included accounts with missing identities. Meanwhile, there are ${accountTrackerCount} accounts in the account tracker.`, - ), - ); - }, + findNetworkClientIdByChainId: + this.networkController.findNetworkClientIdByChainId.bind( + this.networkController, + ), }), ...this.getSnapPermissionSpecifications(), }, @@ -1552,7 +1532,7 @@ export default class MetamaskController extends EventEmitter { }, }, env: { - isAccountSyncingEnabled: isManifestV3, + isAccountSyncingEnabled: false, // TODO: undo this once fixed }, messenger: this.controllerMessenger.getRestricted({ name: 'UserStorageController', @@ -1861,7 +1841,7 @@ export default class MetamaskController extends EventEmitter { this.networkController, ), getNetworkState: () => this.networkController.state, - getPermittedAccounts: this.getPermittedAccounts.bind(this), + getPermittedAccounts: this.getPermittedAccountsSorted.bind(this), getSavedGasFees: () => this.preferencesController.state.advancedGasFee[ getCurrentChainId({ metamask: this.networkController.state }) @@ -2249,18 +2229,13 @@ export default class MetamaskController extends EventEmitter { }, version, // account mgmt - getAccounts: async ( - { origin: innerOrigin }, - { suppressUnauthorizedError = true } = {}, - ) => { + getAccounts: async ({ origin: innerOrigin }) => { if (innerOrigin === ORIGIN_METAMASK) { const selectedAddress = this.accountsController.getSelectedAccount().address; return selectedAddress ? [selectedAddress] : []; } else if (this.isUnlocked()) { - return await this.getPermittedAccounts(innerOrigin, { - suppressUnauthorizedError, - }); + return await this.getPermittedAccounts(innerOrigin); } return []; // changing this is a breaking change }, @@ -3433,9 +3408,7 @@ export default class MetamaskController extends EventEmitter { updateNetwork: this.networkController.updateNetwork.bind( this.networkController, ), - removeNetwork: this.networkController.removeNetwork.bind( - this.networkController, - ), + removeNetwork: this.removeNetwork.bind(this), getCurrentNetworkEIP1559Compatibility: this.networkController.getEIP1559Compatibility.bind( this.networkController, @@ -3712,7 +3685,10 @@ export default class MetamaskController extends EventEmitter { removePermissionsFor: this.removePermissionsFor, approvePermissionsRequest: this.acceptPermissionsRequest, rejectPermissionsRequest: this.rejectPermissionsRequest, - ...getPermissionBackgroundApiMethods(permissionController), + ...getPermissionBackgroundApiMethods({ + permissionController, + approvalController, + }), ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) connectCustodyAddresses: this.mmiController.connectCustodyAddresses.bind( @@ -4855,56 +4831,139 @@ export default class MetamaskController extends EventEmitter { return selectedAddress; } + captureKeyringTypesWithMissingIdentities( + internalAccounts = [], + accounts = [], + ) { + const accountsMissingIdentities = accounts.filter( + (address) => + !internalAccounts.some( + (account) => account.address.toLowerCase() === address.toLowerCase(), + ), + ); + const keyringTypesWithMissingIdentities = accountsMissingIdentities.map( + (address) => this.keyringController.getAccountKeyringType(address), + ); + + const internalAccountCount = internalAccounts.length; + + const accountTrackerCount = Object.keys( + this.accountTrackerController.state.accounts || {}, + ).length; + + captureException( + new Error( + `Attempt to get permission specifications failed because their were ${accounts.length} accounts, but ${internalAccountCount} identities, and the ${keyringTypesWithMissingIdentities} keyrings included accounts with missing identities. Meanwhile, there are ${accountTrackerCount} accounts in the account tracker.`, + ), + ); + } + + async getAllEvmAccountsSorted() { + // We only consider EVM addresses here, hence the filtering: + const accounts = (await this.keyringController.getAccounts()).filter( + isValidHexAddress, + ); + const internalAccounts = this.accountsController.listAccounts(); + + return accounts.sort((firstAddress, secondAddress) => { + const firstAccount = internalAccounts.find( + (internalAccount) => + internalAccount.address.toLowerCase() === firstAddress.toLowerCase(), + ); + + const secondAccount = internalAccounts.find( + (internalAccount) => + internalAccount.address.toLowerCase() === secondAddress.toLowerCase(), + ); + + if (!firstAccount) { + this.captureKeyringTypesWithMissingIdentities( + internalAccounts, + accounts, + ); + throw new Error(`Missing identity for address: "${firstAddress}".`); + } else if (!secondAccount) { + this.captureKeyringTypesWithMissingIdentities( + internalAccounts, + accounts, + ); + throw new Error(`Missing identity for address: "${secondAddress}".`); + } else if ( + firstAccount.metadata.lastSelected === + secondAccount.metadata.lastSelected + ) { + return 0; + } else if (firstAccount.metadata.lastSelected === undefined) { + return 1; + } else if (secondAccount.metadata.lastSelected === undefined) { + return -1; + } + + return ( + secondAccount.metadata.lastSelected - firstAccount.metadata.lastSelected + ); + }); + } + /** * Gets the permitted accounts for the specified origin. Returns an empty * array if no accounts are permitted. * * @param {string} origin - The origin whose exposed accounts to retrieve. - * @param {boolean} [suppressUnauthorizedError] - Suppresses the unauthorized error. * @returns {Promise} The origin's permitted accounts, or an empty * array. */ - async getPermittedAccounts( - origin, - { suppressUnauthorizedError = true } = {}, - ) { + getPermittedAccounts(origin) { + let caveat; try { - return await this.permissionController.executeRestrictedMethod( + caveat = this.permissionController.getCaveat( origin, - RestrictedMethods.eth_accounts, + Caip25EndowmentPermissionName, + Caip25CaveatType, ); - } catch (error) { - if ( - suppressUnauthorizedError && - error.code === rpcErrorCodes.provider.unauthorized - ) { - return []; - } - throw error; + } catch (err) { + // noop } + if (!caveat) { + return []; + } + + return getEthAccounts(caveat.value); + } + + async getPermittedAccountsSorted(origin) { + const permittedAccounts = this.getPermittedAccounts(origin); + const allEvmAccounts = await this.getAllEvmAccountsSorted(); + return allEvmAccounts.filter((account) => + permittedAccounts.includes(account), + ); } /** * Stops exposing the specified chain ID to all third parties. - * Exposed chain IDs are stored in caveats of the `endowment:permitted-chains` - * permission. This method uses `PermissionController.updatePermissionsByCaveat` - * to remove the specified chain ID from every `endowment:permitted-chains` - * permission. If a permission only included this chain ID, the permission is - * revoked entirely. * * @param {string} targetChainId - The chain ID to stop exposing * to third parties. */ removeAllChainIdPermissions(targetChainId) { this.permissionController.updatePermissionsByCaveat( - CaveatTypes.restrictNetworkSwitching, - (existingChainIds) => - CaveatMutatorFactories[ - CaveatTypes.restrictNetworkSwitching - ].removeChainId(targetChainId, existingChainIds), + Caip25CaveatType, + (existingScopes) => + Caip25CaveatMutatorFactories[Caip25CaveatType].removeScope( + toCaipChainId('eip155', parseInt(targetChainId, 16)), + existingScopes, + ), ); } + removeNetwork(chainId) { + const scope = `eip155:${parseInt(chainId, 16)}`; + + this.removeAllChainIdPermissions(chainId); + + this.networkController.removeNetwork(chainId); + } + /** * Stops exposing the account with the specified address to all third parties. * Exposed accounts are stored in caveats of the eth_accounts permission. This @@ -4923,6 +4982,14 @@ export default class MetamaskController extends EventEmitter { CaveatTypes.restrictReturnedAccounts ].removeAccount(targetAccount, existingAccounts), ); + this.permissionController.updatePermissionsByCaveat( + Caip25CaveatType, + (existingScopes) => + Caip25CaveatMutatorFactories[Caip25CaveatType].removeAccount( + targetAccount, + existingScopes, + ), + ); } /** @@ -4964,6 +5031,28 @@ export default class MetamaskController extends EventEmitter { this.preferencesController.setSelectedAddress(importedAccountAddress); } + /** + * Requests approval for permissions for the specified origin + * + * @param origin - The origin to request approval for. + * @param permissions - The permissions to request approval for. + */ + async requestPermissionApprovalForOrigin(origin, permissions) { + const id = nanoid(); + return this.approvalController.addAndShowApprovalRequest({ + id, + origin, + requestData: { + metadata: { + id, + origin, + }, + permissions, + }, + type: MethodNames.requestPermissions, + }); + } + // --------------------------------------------------------------------------- // Identity Management (signature operations) @@ -5684,8 +5773,8 @@ export default class MetamaskController extends EventEmitter { // Legacy RPC methods that need to be implemented _ahead of_ the permission // middleware. engine.push( - createLegacyMethodMiddleware({ - getAccounts: this.getPermittedAccounts.bind(this, origin), + createEthAccountsMethodMiddleware({ + getAccounts: this.getPermittedAccountsSorted.bind(this, origin), }), ); @@ -5720,9 +5809,7 @@ export default class MetamaskController extends EventEmitter { // Unrestricted/permissionless RPC method implementations. // They must nevertheless be placed _behind_ the permission middleware. engine.push( - createMethodMiddleware({ - origin, - + createEip1193MethodMiddleware({ subjectType, // Miscellaneous @@ -5750,59 +5837,21 @@ export default class MetamaskController extends EventEmitter { this.metaMetricsController, ), // Permission-related - getAccounts: this.getPermittedAccounts.bind(this, origin), + getAccounts: this.getPermittedAccountsSorted.bind(this, origin), getPermissionsForOrigin: this.permissionController.getPermissions.bind( this.permissionController, origin, ), - hasPermission: this.permissionController.hasPermission.bind( - this.permissionController, - origin, - ), - requestAccountsPermission: - this.permissionController.requestPermissions.bind( - this.permissionController, - { origin }, - { - eth_accounts: {}, - ...(!isSnapId(origin) && { - [PermissionNames.permittedChains]: {}, - }), - }, - ), - requestPermittedChainsPermission: (chainIds) => - this.permissionController.requestPermissionsIncremental( - { origin }, - { - [PermissionNames.permittedChains]: { - caveats: [ - CaveatFactories[CaveatTypes.restrictNetworkSwitching]( - chainIds, - ), - ], - }, - }, - ), - grantPermittedChainsPermissionIncremental: (chainIds) => - this.permissionController.grantPermissionsIncremental({ - subject: { origin }, - approvedPermissions: { - [PermissionNames.permittedChains]: { - caveats: [ - CaveatFactories[CaveatTypes.restrictNetworkSwitching]( - chainIds, - ), - ], - }, - }, - }), + requestPermissionApprovalForOrigin: + this.requestPermissionApprovalForOrigin.bind(this, origin), requestPermissionsForOrigin: (requestedPermissions) => this.permissionController.requestPermissions( { origin }, { - ...(requestedPermissions[PermissionNames.eth_accounts] && { - [PermissionNames.permittedChains]: {}, - }), + ...(requestedPermissions[PermissionNames.eth_accounts] && + !isSnapId(origin) && { + [PermissionNames.permittedChains]: {}, + }), ...(requestedPermissions[PermissionNames.permittedChains] && { [PermissionNames.eth_accounts]: {}, }), @@ -5844,12 +5893,12 @@ export default class MetamaskController extends EventEmitter { // network configuration-related setActiveNetwork: async (networkClientId) => { await this.networkController.setActiveNetwork(networkClientId); - // if the origin has the eth_accounts permission + // if the origin has the CAIP-25 permission // we set per dapp network selection state if ( this.permissionController.hasPermission( origin, - PermissionNames.eth_accounts, + Caip25EndowmentPermissionName, ) ) { this.selectedNetworkController.setNetworkClientIdForDomain( @@ -5887,6 +5936,13 @@ export default class MetamaskController extends EventEmitter { this.alertController, ), + grantPermissions: this.permissionController.grantPermissions.bind( + this.permissionController, + ), + updateCaveat: this.permissionController.updateCaveat.bind( + this.permissionController, + ), + ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) handleMmiAuthenticate: this.institutionalFeaturesController.handleMmiAuthenticate.bind( @@ -6217,7 +6273,7 @@ export default class MetamaskController extends EventEmitter { method: NOTIFICATION_NAMES.unlockStateChanged, params: { isUnlocked: true, - accounts: await this.getPermittedAccounts(origin), + accounts: await this.getPermittedAccountsSorted(origin), }, }; }); @@ -6748,7 +6804,7 @@ export default class MetamaskController extends EventEmitter { newAccounts : // If the length is 2 or greater, we have to execute // `eth_accounts` vi this method. - await this.getPermittedAccounts(origin), + await this.getPermittedAccountsSorted(origin), }); } diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 77b062bcfdc7..7ed9abcb39f0 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -102,10 +102,13 @@ const createLoggerMiddlewareMock = () => (req, res, next) => { jest.mock('./lib/createLoggerMiddleware', () => createLoggerMiddlewareMock); const rpcMethodMiddlewareMock = { - createMethodMiddleware: () => (_req, _res, next, _end) => { + createEip1193MethodMiddleware: () => (_req, _res, next, _end) => { next(); }, - createLegacyMethodMiddleware: () => (_req, _res, next, _end) => { + createEthAccountsMethodMiddleware: () => (_req, _res, next, _end) => { + next(); + }, + createMultichainMethodMiddleware: () => (_req, _res, next, _end) => { next(); }, createUnsupportedMethodMiddleware: () => (_req, _res, next, _end) => { From 7e61aa7f24155ec0472ad489062c3ccf8ec22dd6 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 14 Oct 2024 12:36:26 -0700 Subject: [PATCH 006/148] lint --- .../handlers/wallet-getPermissions.js | 5 ++++- app/scripts/metamask-controller.js | 16 +--------------- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js index 2068a02597c6..e4ad911f0a11 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js @@ -5,7 +5,10 @@ import { getPermittedEthChainIds, } from '@metamask/multichain'; import { PermissionNames } from '../../../controllers/permissions'; -import { CaveatTypes, RestrictedMethods } from '../../../../../shared/constants/permissions'; +import { + CaveatTypes, + RestrictedMethods, +} from '../../../../../shared/constants/permissions'; export const getPermissionsHandler = { methodNames: [MethodNames.getPermissions], diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 357e302d151a..1ea53b3e0d63 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -15,7 +15,7 @@ import { } from '@metamask/assets-controllers'; import { ObservableStore } from '@metamask/obs-store'; import { storeAsStream } from '@metamask/obs-store/dist/asStream'; -import { JsonRpcEngine, createScaffoldMiddleware } from 'json-rpc-engine'; +import { JsonRpcEngine } from 'json-rpc-engine'; import { createEngineStream } from 'json-rpc-middleware-stream'; import { providerAsMiddleware } from '@metamask/eth-json-rpc-middleware'; import { debounce, throttle, memoize, wrap } from 'lodash'; @@ -154,18 +154,10 @@ import { NotificationServicesController, } from '@metamask/notification-services-controller'; import { - walletInvokeMethodHandler, Caip25CaveatMutatorFactories, Caip25CaveatType, Caip25EndowmentPermissionName, - multichainMethodCallValidatorMiddleware, - MultichainSubscriptionManager, - MultichainMiddlewareManager, - walletRevokeSessionHandler, - walletGetSessionHandler, - mergeScopes, getEthAccounts, - caipPermissionAdapterMiddleware, } from '@metamask/multichain'; import { methodsRequiringNetworkSwitch } from '../../shared/constants/methods-tags'; @@ -204,7 +196,6 @@ import { MILLISECOND, SECOND } from '../../shared/constants/time'; import { ORIGIN_METAMASK, POLLING_TOKEN_ENVIRONMENT_TYPES, - MESSAGE_TYPE, } from '../../shared/constants/app'; import { MetaMetricsEventCategory, @@ -325,14 +316,11 @@ import AppMetadataController from './controllers/app-metadata'; import { CaveatMutatorFactories, - getAuthorizedScopesByOrigin, getCaveatSpecifications, - getChangedAuthorizations, diffMap, getPermissionBackgroundApiMethods, getPermissionSpecifications, getPermittedAccountsByOrigin, - getRemovedAuthorizations, getPermittedChainsByOrigin, NOTIFICATION_NAMES, unrestrictedMethods, @@ -4957,8 +4945,6 @@ export default class MetamaskController extends EventEmitter { } removeNetwork(chainId) { - const scope = `eip155:${parseInt(chainId, 16)}`; - this.removeAllChainIdPermissions(chainId); this.networkController.removeNetwork(chainId); From 590a206f942a64d146ede7033307b454b539381b Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 14 Oct 2024 12:37:09 -0700 Subject: [PATCH 007/148] bring migration --- app/scripts/migrations/131.test.ts | 952 +++++++++++++++++++++++++++++ app/scripts/migrations/131.ts | 269 ++++++++ app/scripts/migrations/index.js | 1 + 3 files changed, 1222 insertions(+) create mode 100644 app/scripts/migrations/131.test.ts create mode 100644 app/scripts/migrations/131.ts diff --git a/app/scripts/migrations/131.test.ts b/app/scripts/migrations/131.test.ts new file mode 100644 index 000000000000..9b805bde3a48 --- /dev/null +++ b/app/scripts/migrations/131.test.ts @@ -0,0 +1,952 @@ +import { migrate, version } from './131'; + +const PermissionNames = { + eth_accounts: 'eth_accounts', + permittedChains: 'endowment:permitted-chains', +}; + +const sentryCaptureExceptionMock = jest.fn(); + +global.sentry = { + captureException: sentryCaptureExceptionMock, +}; + +const oldVersion = 130; + +describe('migration #131', () => { + afterEach(() => jest.resetAllMocks()); + + it('updates the version metadata', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: {}, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.meta).toStrictEqual({ version }); + }); + + it('does nothing if PermissionController state is missing', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + NetworkController: {}, + SelectedNetworkController: {}, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.PermissionController is undefined`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if PermissionController state is not an object', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: 'foo', + NetworkController: {}, + SelectedNetworkController: {}, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.PermissionController is string`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if NetworkController state is missing', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: {}, + SelectedNetworkController: {}, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.NetworkController is undefined`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if NetworkController state is not an object', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: {}, + NetworkController: 'foo', + SelectedNetworkController: {}, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.NetworkController is string`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if SelectedNetworkController state is missing', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: {}, + NetworkController: {}, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.SelectedNetworkController is undefined`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if SelectedNetworkController state is not an object', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: {}, + NetworkController: {}, + SelectedNetworkController: 'foo', + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.SelectedNetworkController is string`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if PermissionController.subjects is not an object', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: { + subjects: 'foo', + }, + NetworkController: {}, + SelectedNetworkController: {}, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.PermissionController.subjects is string`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if NetworkController.selectedNetworkClientId is not a non-empty string', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: { + subjects: {}, + }, + NetworkController: { + selectedNetworkClientId: {}, + }, + SelectedNetworkController: {}, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.NetworkController.selectedNetworkClientId is object`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if NetworkController.networkConfigurations is not an object', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: { + subjects: {}, + }, + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: 'foo', + }, + SelectedNetworkController: {}, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.NetworkController.networkConfigurations is string`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if SelectedNetworkController.domains is not an object', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: { + subjects: {}, + }, + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: {}, + }, + SelectedNetworkController: { + domains: 'foo', + }, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.SelectedNetworkController.domains is string`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if the currently selected network client is neither built in nor exists in NetworkController.networkConfigurations', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: { + subjects: {}, + }, + NetworkController: { + selectedNetworkClientId: 'nonExistentNetworkClientId', + networkConfigurations: {}, + }, + SelectedNetworkController: { + domains: {}, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: Invalid chainId for selectedNetworkClientId "nonExistentNetworkClientId" of type undefined`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if a subject is not an object', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: {}, + }, + SelectedNetworkController: { + domains: {}, + }, + PermissionController: { + subjects: { + 'test.com': 'foo', + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: Invalid subject for origin "test.com" of type string`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it("does nothing if a subject's permissions is not an object", async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: {}, + }, + SelectedNetworkController: { + domains: {}, + }, + PermissionController: { + subjects: { + 'test.com': { + permissions: 'foo', + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: Invalid permissions for origin "test.com" of type string`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if neither eth_accounts nor permittedChains permissions have been granted', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: {}, + }, + SelectedNetworkController: { + domains: {}, + }, + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + }, + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({ + NetworkController: { + selectedNetworkClientId: 'mainnet', + networkConfigurations: {}, + }, + SelectedNetworkController: { + domains: {}, + }, + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + }, + }, + }, + }, + }); + }); + + // @ts-expect-error This function is missing from the Mocha type definitions + describe.each([ + [ + 'built-in', + { + selectedNetworkClientId: 'mainnet', + networkConfigurations: {}, + }, + '1', + ], + [ + 'custom', + { + selectedNetworkClientId: 'customId', + networkConfigurations: { + customId: { + chainId: '0xf', + }, + }, + }, + '15', + ], + ])( + 'the currently selected network client is %s', + ( + _type: string, + NetworkController: { + networkConfigurations: Record; + } & Record, + chainId: string, + ) => { + const baseData = () => ({ + PermissionController: { + subjects: {}, + }, + NetworkController, + SelectedNetworkController: { + domains: {}, + }, + }); + const currentScope = `eip155:${chainId}`; + + it('replaces the eth_accounts permission with a CAIP-25 permission using the eth_accounts value for the currently selected chain id when the origin does not have its own network client', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + ...baseData(), + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: 'restrictReturnedAccounts', + value: ['0xdeadbeef', '0x999'], + }, + ], + }, + }, + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({ + ...baseData(), + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + 'endowment:caip25': { + parentCapability: 'endowment:caip25', + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + [currentScope]: { + accounts: [ + `${currentScope}:0xdeadbeef`, + `${currentScope}:0x999`, + ], + methods: [], + notifications: [], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + }, + }, + }, + }, + }, + }); + }); + + it('replaces the eth_accounts permission with a CAIP-25 permission using the eth_accounts value for the currently selected chain id when the origin does have its own network client that cannot be resolved', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + ...baseData(), + SelectedNetworkController: { + domains: { + 'test.com': 'doesNotExist', + }, + }, + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: 'restrictReturnedAccounts', + value: ['0xdeadbeef', '0x999'], + }, + ], + }, + }, + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({ + ...baseData(), + SelectedNetworkController: { + domains: { + 'test.com': 'doesNotExist', + }, + }, + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + 'endowment:caip25': { + parentCapability: 'endowment:caip25', + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + [currentScope]: { + accounts: [ + `${currentScope}:0xdeadbeef`, + `${currentScope}:0x999`, + ], + methods: [], + notifications: [], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + }, + }, + }, + }, + }, + }); + }); + + it('replaces the eth_accounts permission with a CAIP-25 permission using the eth_accounts value for the origin chain id when the origin does have its own network client and it exists in the built-in networks', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + ...baseData(), + SelectedNetworkController: { + domains: { + 'test.com': 'sepolia', + }, + }, + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: 'restrictReturnedAccounts', + value: ['0xdeadbeef', '0x999'], + }, + ], + }, + }, + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({ + ...baseData(), + SelectedNetworkController: { + domains: { + 'test.com': 'sepolia', + }, + }, + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + 'endowment:caip25': { + parentCapability: 'endowment:caip25', + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:11155111': { + accounts: [ + 'eip155:11155111:0xdeadbeef', + 'eip155:11155111:0x999', + ], + methods: [], + notifications: [], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + }, + }, + }, + }, + }, + }); + }); + + it('replaces the eth_accounts permission with a CAIP-25 permission using the eth_accounts value for the origin chain id when the origin does have its own network client and it exists in the custom configurations', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + ...baseData(), + NetworkController: { + ...baseData().NetworkController, + networkConfigurations: { + ...baseData().NetworkController.networkConfigurations, + customNetworkClientId: { + chainId: '0xa', + }, + }, + }, + SelectedNetworkController: { + domains: { + 'test.com': 'customNetworkClientId', + }, + }, + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: 'restrictReturnedAccounts', + value: ['0xdeadbeef', '0x999'], + }, + ], + }, + }, + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({ + ...baseData(), + NetworkController: { + ...baseData().NetworkController, + networkConfigurations: { + ...baseData().NetworkController.networkConfigurations, + customNetworkClientId: { + chainId: '0xa', + }, + }, + }, + SelectedNetworkController: { + domains: { + 'test.com': 'customNetworkClientId', + }, + }, + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + 'endowment:caip25': { + parentCapability: 'endowment:caip25', + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:10': { + accounts: [ + 'eip155:10:0xdeadbeef', + 'eip155:10:0x999', + ], + methods: [], + notifications: [], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + }, + }, + }, + }, + }, + }); + }); + + it('does not create a CAIP-25 permission when eth_accounts permission is missing', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + ...baseData(), + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: 'restrictNetworkSwitching', + value: ['0xa', '0x64'], + }, + ], + }, + }, + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({ + ...baseData(), + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + }, + }, + }, + }, + }); + }); + + it('replaces both eth_accounts and permittedChains permission with a CAIP-25 permission using the values from both permissions', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + ...baseData(), + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: 'restrictReturnedAccounts', + value: ['0xdeadbeef', '0x999'], + }, + ], + }, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: 'restrictNetworkSwitching', + value: ['0xa', '0x64'], + }, + ], + }, + }, + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({ + ...baseData(), + PermissionController: { + subjects: { + 'test.com': { + permissions: { + unrelated: { + foo: 'bar', + }, + 'endowment:caip25': { + parentCapability: 'endowment:caip25', + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:10': { + accounts: [ + 'eip155:10:0xdeadbeef', + 'eip155:10:0x999', + ], + methods: [], + notifications: [], + }, + 'eip155:100': { + accounts: [ + 'eip155:100:0xdeadbeef', + 'eip155:100:0x999', + ], + methods: [], + notifications: [], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + }, + }, + }, + }, + }, + }); + }); + + it('replaces permissions for each subject', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + ...baseData(), + PermissionController: { + subjects: { + 'test.com': { + permissions: { + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: 'restrictReturnedAccounts', + value: ['0xdeadbeef'], + }, + ], + }, + }, + }, + 'test2.com': { + permissions: { + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: 'restrictReturnedAccounts', + value: ['0xdeadbeef'], + }, + ], + }, + }, + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({ + ...baseData(), + PermissionController: { + subjects: { + 'test.com': { + permissions: { + 'endowment:caip25': { + parentCapability: 'endowment:caip25', + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + [currentScope]: { + accounts: [`${currentScope}:0xdeadbeef`], + methods: [], + notifications: [], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + }, + }, + }, + 'test2.com': { + permissions: { + 'endowment:caip25': { + parentCapability: 'endowment:caip25', + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + [currentScope]: { + accounts: [`${currentScope}:0xdeadbeef`], + methods: [], + notifications: [], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + }, + }, + }, + }, + }, + }); + }); + }, + ); +}); diff --git a/app/scripts/migrations/131.ts b/app/scripts/migrations/131.ts new file mode 100644 index 000000000000..5d38816ed7f5 --- /dev/null +++ b/app/scripts/migrations/131.ts @@ -0,0 +1,269 @@ +import { + hasProperty, + Hex, + isObject, + NonEmptyArray, + Json, +} from '@metamask/utils'; +import { cloneDeep } from 'lodash'; + +type CaveatConstraint = { + type: string; + value: Json; +}; + +type PermissionConstraint = { + parentCapability: string; + caveats: null | NonEmptyArray; +}; + +const PermissionNames = { + eth_accounts: 'eth_accounts', + permittedChains: 'endowment:permitted-chains', +}; + +const BUILT_IN_NETWORKS = { + goerli: { + chainId: '0x5', + }, + sepolia: { + chainId: '0xaa36a7', + }, + mainnet: { + chainId: '0x1', + }, + 'linea-goerli': { + chainId: '0xe704', + }, + 'linea-sepolia': { + chainId: '0xe705', + }, + 'linea-mainnet': { + chainId: '0xe708', + }, +}; + +const Caip25CaveatType = 'authorizedScopes'; +const Caip25EndowmentPermissionName = 'endowment:caip25'; + +type VersionedData = { + meta: { version: number }; + data: Record; +}; + +export const version = 131; + +/** + * This migration transforms `eth_accounts` and `permittedChains` permissions into + * an equivalent CAIP-25 permission. + * + * @param originalVersionedData - Versioned MetaMask extension state, exactly + * what we persist to dist. + * @param originalVersionedData.meta - State metadata. + * @param originalVersionedData.meta.version - The current state version. + * @param originalVersionedData.data - The persisted MetaMask state, keyed by + * controller. + * @returns Updated versioned MetaMask extension state. + */ +export async function migrate( + originalVersionedData: VersionedData, +): Promise { + const versionedData = cloneDeep(originalVersionedData); + versionedData.meta.version = version; + transformState(versionedData.data); + return versionedData; +} + +function transformState(state: Record) { + if ( + !hasProperty(state, 'PermissionController') || + !isObject(state.PermissionController) + ) { + global.sentry?.captureException?.( + new Error( + `Migration ${version}: typeof state.PermissionController is ${typeof state.PermissionController}`, + ), + ); + return state; + } + + if ( + !hasProperty(state, 'NetworkController') || + !isObject(state.NetworkController) + ) { + global.sentry?.captureException?.( + new Error( + `Migration ${version}: typeof state.NetworkController is ${typeof state.NetworkController}`, + ), + ); + return state; + } + + if ( + !hasProperty(state, 'SelectedNetworkController') || + !isObject(state.SelectedNetworkController) + ) { + global.sentry?.captureException?.( + new Error( + `Migration ${version}: typeof state.SelectedNetworkController is ${typeof state.SelectedNetworkController}`, + ), + ); + return state; + } + + const { + PermissionController: { subjects }, + NetworkController: { selectedNetworkClientId, networkConfigurations }, + SelectedNetworkController: { domains }, + } = state; + + if (!isObject(subjects)) { + global.sentry?.captureException( + new Error( + `Migration ${version}: typeof state.PermissionController.subjects is ${typeof subjects}`, + ), + ); + return state; + } + if (!selectedNetworkClientId || typeof selectedNetworkClientId !== 'string') { + global.sentry?.captureException( + new Error( + `Migration ${version}: typeof state.NetworkController.selectedNetworkClientId is ${typeof selectedNetworkClientId}`, + ), + ); + return state; + } + if (!isObject(networkConfigurations)) { + global.sentry?.captureException( + new Error( + `Migration ${version}: typeof state.NetworkController.networkConfigurations is ${typeof networkConfigurations}`, + ), + ); + return state; + } + if (!isObject(domains)) { + global.sentry?.captureException( + new Error( + `Migration ${version}: typeof state.SelectedNetworkController.domains is ${typeof domains}`, + ), + ); + return state; + } + + const getChainIdForNetworkClientId = (networkClientId: string) => { + const networkConfiguration = + (networkConfigurations[networkClientId] as { chainId: Hex }) ?? + BUILT_IN_NETWORKS[ + networkClientId as unknown as keyof typeof BUILT_IN_NETWORKS + ]; + return networkConfiguration?.chainId; + }; + + const currentChainId = getChainIdForNetworkClientId(selectedNetworkClientId); + if (!currentChainId || typeof currentChainId !== 'string') { + global.sentry?.captureException( + new Error( + `Migration ${version}: Invalid chainId for selectedNetworkClientId "${selectedNetworkClientId}" of type ${typeof currentChainId}`, + ), + ); + return state; + } + + for (const [origin, subject] of Object.entries(subjects)) { + if (!isObject(subject)) { + global.sentry?.captureException( + new Error( + `Migration ${version}: Invalid subject for origin "${origin}" of type ${typeof subject}`, + ), + ); + return state; + } + + const { permissions } = subject as { + permissions: Record; + }; + if (!isObject(permissions)) { + global.sentry?.captureException( + new Error( + `Migration ${version}: Invalid permissions for origin "${origin}" of type ${typeof permissions}`, + ), + ); + return state; + } + + let basePermission; + + let ethAccounts: string[] = []; + if ( + isObject(permissions[PermissionNames.eth_accounts]) && + Array.isArray(permissions[PermissionNames.eth_accounts].caveats) + ) { + ethAccounts = + (permissions[PermissionNames.eth_accounts].caveats?.[0] + ?.value as string[]) ?? []; + basePermission = permissions[PermissionNames.eth_accounts]; + } + delete permissions[PermissionNames.eth_accounts]; + + let chainIds: string[] = []; + if ( + isObject(permissions[PermissionNames.permittedChains]) && + Array.isArray(permissions[PermissionNames.permittedChains].caveats) + ) { + chainIds = + (permissions[PermissionNames.permittedChains].caveats?.[0] + ?.value as string[]) ?? []; + basePermission ??= permissions[PermissionNames.permittedChains]; + } + delete permissions[PermissionNames.permittedChains]; + + if (ethAccounts.length === 0) { + continue; + } + + if (chainIds.length === 0) { + chainIds = [currentChainId]; + + const networkClientIdForOrigin = domains[origin]; + if (networkClientIdForOrigin) { + const chainIdForOrigin = getChainIdForNetworkClientId( + networkClientIdForOrigin as string, + ); + if (chainIdForOrigin && typeof chainIdForOrigin === 'string') { + chainIds = [chainIdForOrigin]; + } + } + } + + const scopes: Record = {}; + + chainIds.forEach((chainId) => { + const scopeString = `eip155:${parseInt(chainId, 16)}`; + const caipAccounts = ethAccounts.map( + (account) => `${scopeString}:${account}`, + ); + scopes[scopeString] = { + methods: [], + notifications: [], + accounts: caipAccounts, + }; + }); + + permissions[Caip25EndowmentPermissionName] = { + ...basePermission, + parentCapability: Caip25EndowmentPermissionName, + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: scopes, + isMultichainOrigin: false, + }, + }, + ], + }; + } + + return state; +} diff --git a/app/scripts/migrations/index.js b/app/scripts/migrations/index.js index a72fd34c3c28..d2c63eb2e35c 100644 --- a/app/scripts/migrations/index.js +++ b/app/scripts/migrations/index.js @@ -151,6 +151,7 @@ const migrations = [ require('./128'), require('./129'), require('./130'), + require('./131'), ]; export default migrations; From b507802a822717cb9cad2c39f4e7b3abf31b1886 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 14 Oct 2024 12:49:56 -0700 Subject: [PATCH 008/148] patch json-schema modules --- ...ec-json-pointer-npm-0.1.2-3d06119887.patch | 13 ++++++++ ...erence-resolver-npm-1.2.6-4e1497c16d.patch | 13 ++++++++ package.json | 5 +++ yarn.lock | 33 +++++++++---------- 4 files changed, 46 insertions(+), 18 deletions(-) create mode 100644 .yarn/patches/@json-schema-spec-json-pointer-npm-0.1.2-3d06119887.patch create mode 100644 .yarn/patches/@json-schema-tools-reference-resolver-npm-1.2.6-4e1497c16d.patch diff --git a/.yarn/patches/@json-schema-spec-json-pointer-npm-0.1.2-3d06119887.patch b/.yarn/patches/@json-schema-spec-json-pointer-npm-0.1.2-3d06119887.patch new file mode 100644 index 000000000000..4eddae30359d --- /dev/null +++ b/.yarn/patches/@json-schema-spec-json-pointer-npm-0.1.2-3d06119887.patch @@ -0,0 +1,13 @@ +diff --git a/lib/index.js b/lib/index.js +index f5795884311124b221d91f488ed45750eb6e9c80..e030d6f8d8e85e6d1350c565d36ad48bc49af881 100644 +--- a/lib/index.js ++++ b/lib/index.js +@@ -25,7 +25,7 @@ class Ptr { + }); + return `/${tokens.join("/")}`; + } +- eval(instance) { ++ shmeval(instance) { + for (const token of this.tokens) { + if (instance.hasOwnProperty(token)) { + instance = instance[token]; diff --git a/.yarn/patches/@json-schema-tools-reference-resolver-npm-1.2.6-4e1497c16d.patch b/.yarn/patches/@json-schema-tools-reference-resolver-npm-1.2.6-4e1497c16d.patch new file mode 100644 index 000000000000..2ff663fa18e4 --- /dev/null +++ b/.yarn/patches/@json-schema-tools-reference-resolver-npm-1.2.6-4e1497c16d.patch @@ -0,0 +1,13 @@ +diff --git a/build/resolve-pointer.js b/build/resolve-pointer.js +index d5a8ec7486250cd17572eb0e0449725643fc9842..044e74bb51a46e9bf3547f6d7a84763b93260613 100644 +--- a/build/resolve-pointer.js ++++ b/build/resolve-pointer.js +@@ -27,7 +27,7 @@ exports.default = (function (ref, root) { + try { + var withoutHash = ref.replace("#", ""); + var pointer = json_pointer_1.default.parse(withoutHash); +- return pointer.eval(root); ++ return pointer.shmeval(root); + } + catch (e) { + throw new InvalidJsonPointerRefError(ref, e.message); diff --git a/package.json b/package.json index aea0ffb859f1..ff20c0f2fbb4 100644 --- a/package.json +++ b/package.json @@ -261,6 +261,11 @@ "@metamask/message-manager": "^10.1.0", "@metamask/gas-fee-controller@npm:^15.1.1": "patch:@metamask/gas-fee-controller@npm%3A15.1.2#~/.yarn/patches/@metamask-gas-fee-controller-npm-15.1.2-db4d2976aa.patch", "@metamask/nonce-tracker@npm:^5.0.0": "patch:@metamask/nonce-tracker@npm%3A5.0.0#~/.yarn/patches/@metamask-nonce-tracker-npm-5.0.0-d81478218e.patch", + "@json-schema-spec/json-pointer@npm:^0.1.2": "patch:@json-schema-spec/json-pointer@npm%3A0.1.2#~/.yarn/patches/@json-schema-spec-json-pointer-npm-0.1.2-3d06119887.patch", + "@json-schema-tools/reference-resolver@npm:^1.2.6": "patch:@json-schema-tools/reference-resolver@npm%3A1.2.6#~/.yarn/patches/@json-schema-tools-reference-resolver-npm-1.2.6-4e1497c16d.patch", + "@json-schema-tools/reference-resolver@npm:1.2.4": "patch:@json-schema-tools/reference-resolver@npm%3A1.2.6#~/.yarn/patches/@json-schema-tools-reference-resolver-npm-1.2.6-4e1497c16d.patch", + "@json-schema-tools/reference-resolver@npm:^1.2.4": "patch:@json-schema-tools/reference-resolver@npm%3A1.2.6#~/.yarn/patches/@json-schema-tools-reference-resolver-npm-1.2.6-4e1497c16d.patch", + "@json-schema-tools/reference-resolver@npm:^1.2.1": "patch:@json-schema-tools/reference-resolver@npm%3A1.2.6#~/.yarn/patches/@json-schema-tools-reference-resolver-npm-1.2.6-4e1497c16d.patch", "@metamask/network-controller@npm:^17.0.0": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/network-controller@npm:^19.0.0": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/network-controller@npm:^20.0.0": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", diff --git a/yarn.lock b/yarn.lock index 1edd6313870f..cea5c05dfb8c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4085,13 +4085,20 @@ __metadata: languageName: node linkType: hard -"@json-schema-spec/json-pointer@npm:^0.1.2": +"@json-schema-spec/json-pointer@npm:0.1.2": version: 0.1.2 resolution: "@json-schema-spec/json-pointer@npm:0.1.2" checksum: 10/2a691ffc11f1a266ca4d0c9e2c99791679d580f343ef69746fad623d1abcf4953adde987890e41f906767d7729604c0182341e9012388b73a44d5b21fb296453 languageName: node linkType: hard +"@json-schema-spec/json-pointer@patch:@json-schema-spec/json-pointer@npm%3A0.1.2#~/.yarn/patches/@json-schema-spec-json-pointer-npm-0.1.2-3d06119887.patch": + version: 0.1.2 + resolution: "@json-schema-spec/json-pointer@patch:@json-schema-spec/json-pointer@npm%3A0.1.2#~/.yarn/patches/@json-schema-spec-json-pointer-npm-0.1.2-3d06119887.patch::version=0.1.2&hash=8ff707" + checksum: 10/b957be819e3f744e8546014064f9fd8e45aa133985384bf91ae5f20688a087e12da3cb9046cd163e57ed1bea90c95ff7ed9a3e817f4c5e47377a8b330177915e + languageName: node + linkType: hard + "@json-schema-tools/dereferencer@npm:1.5.1": version: 1.5.1 resolution: "@json-schema-tools/dereferencer@npm:1.5.1" @@ -4157,33 +4164,23 @@ __metadata: languageName: node linkType: hard -"@json-schema-tools/reference-resolver@npm:1.2.4": - version: 1.2.4 - resolution: "@json-schema-tools/reference-resolver@npm:1.2.4" - dependencies: - "@json-schema-spec/json-pointer": "npm:^0.1.2" - isomorphic-fetch: "npm:^3.0.0" - checksum: 10/1ad98d011e5aad72000112215615715593a0a244ca82dbf6008cc93bfcd14ef99a0796ab4e808faee083dc13182dc9ab2d01ca5db4f44ca880f45de2f5ea2437 - languageName: node - linkType: hard - -"@json-schema-tools/reference-resolver@npm:^1.2.1, @json-schema-tools/reference-resolver@npm:^1.2.4": - version: 1.2.5 - resolution: "@json-schema-tools/reference-resolver@npm:1.2.5" +"@json-schema-tools/reference-resolver@npm:1.2.6": + version: 1.2.6 + resolution: "@json-schema-tools/reference-resolver@npm:1.2.6" dependencies: "@json-schema-spec/json-pointer": "npm:^0.1.2" isomorphic-fetch: "npm:^3.0.0" - checksum: 10/0f48098ea6df853a56fc7c758974eee4c5b7e3979123f49f52929c82a1eb263c7d0154efc6671325920d670494b05cae4d4625c6204023b4b1fed6e5f93ccb96 + checksum: 10/91d6b4b2ac43f8163fd27bde6d826f29f339e9c7ce3b7e2b73b85e891fa78e3702fd487deda143a0701879cbc2fe28c53a4efce4cd2d2dd2fe6e82b64bbd9c9c languageName: node linkType: hard -"@json-schema-tools/reference-resolver@npm:^1.2.6": +"@json-schema-tools/reference-resolver@patch:@json-schema-tools/reference-resolver@npm%3A1.2.6#~/.yarn/patches/@json-schema-tools-reference-resolver-npm-1.2.6-4e1497c16d.patch": version: 1.2.6 - resolution: "@json-schema-tools/reference-resolver@npm:1.2.6" + resolution: "@json-schema-tools/reference-resolver@patch:@json-schema-tools/reference-resolver@npm%3A1.2.6#~/.yarn/patches/@json-schema-tools-reference-resolver-npm-1.2.6-4e1497c16d.patch::version=1.2.6&hash=6fefb6" dependencies: "@json-schema-spec/json-pointer": "npm:^0.1.2" isomorphic-fetch: "npm:^3.0.0" - checksum: 10/91d6b4b2ac43f8163fd27bde6d826f29f339e9c7ce3b7e2b73b85e891fa78e3702fd487deda143a0701879cbc2fe28c53a4efce4cd2d2dd2fe6e82b64bbd9c9c + checksum: 10/91534095e488dc091a6d9bf807a065697cdc2c070bbda70ebd0817569c46daa8cfb56b4e625a9d9ddaa0d08c5fdc40db3ef39cae97a16b682e8b593f1febf062 languageName: node linkType: hard From bb96bc3d4a53989eaef2c58d4d755871107d3c55 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 09:01:33 -0700 Subject: [PATCH 009/148] Bring in migration fixes from #27849 --- app/scripts/migrations/131.test.ts | 270 ++++++++++++++++++++++++++--- app/scripts/migrations/131.ts | 94 ++++++---- 2 files changed, 305 insertions(+), 59 deletions(-) diff --git a/app/scripts/migrations/131.test.ts b/app/scripts/migrations/131.test.ts index 9b805bde3a48..f65dfe5996b2 100644 --- a/app/scripts/migrations/131.test.ts +++ b/app/scripts/migrations/131.test.ts @@ -189,7 +189,7 @@ describe('migration #131', () => { expect(newStorage.data).toStrictEqual(oldStorage.data); }); - it('does nothing if NetworkController.networkConfigurations is not an object', async () => { + it('does nothing if NetworkController.networkConfigurationsByChainId is not an object', async () => { const oldStorage = { meta: { version: oldVersion }, data: { @@ -198,7 +198,7 @@ describe('migration #131', () => { }, NetworkController: { selectedNetworkClientId: 'mainnet', - networkConfigurations: 'foo', + networkConfigurationsByChainId: 'foo', }, SelectedNetworkController: {}, }, @@ -208,7 +208,7 @@ describe('migration #131', () => { expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( new Error( - `Migration ${version}: typeof state.NetworkController.networkConfigurations is string`, + `Migration ${version}: typeof state.NetworkController.networkConfigurationsByChainId is string`, ), ); expect(newStorage.data).toStrictEqual(oldStorage.data); @@ -223,7 +223,7 @@ describe('migration #131', () => { }, NetworkController: { selectedNetworkClientId: 'mainnet', - networkConfigurations: {}, + networkConfigurationsByChainId: {}, }, SelectedNetworkController: { domains: 'foo', @@ -241,7 +241,7 @@ describe('migration #131', () => { expect(newStorage.data).toStrictEqual(oldStorage.data); }); - it('does nothing if the currently selected network client is neither built in nor exists in NetworkController.networkConfigurations', async () => { + it('does nothing if NetworkController.networkConfigurationsByChainId[] is not an object', async () => { const oldStorage = { meta: { version: oldVersion }, data: { @@ -250,7 +250,98 @@ describe('migration #131', () => { }, NetworkController: { selectedNetworkClientId: 'nonExistentNetworkClientId', - networkConfigurations: {}, + networkConfigurationsByChainId: { + '0x1': 'foo', + }, + }, + SelectedNetworkController: { + domains: {}, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.NetworkController.networkConfigurationsByChainId["0x1"] is string`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if NetworkController.networkConfigurationsByChainId[].rpcEndpoints is not an array', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: { + subjects: {}, + }, + NetworkController: { + selectedNetworkClientId: 'nonExistentNetworkClientId', + networkConfigurationsByChainId: { + '0x1': { + rpcEndpoints: 'foo', + }, + }, + }, + SelectedNetworkController: { + domains: {}, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.NetworkController.networkConfigurationsByChainId["0x1"].rpcEndpoints is string`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if NetworkController.networkConfigurationsByChainId[].rpcEndpoints[] is not an object', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: { + subjects: {}, + }, + NetworkController: { + selectedNetworkClientId: 'nonExistentNetworkClientId', + networkConfigurationsByChainId: { + '0x1': { + rpcEndpoints: ['foo'], + }, + }, + }, + SelectedNetworkController: { + domains: {}, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: typeof state.NetworkController.networkConfigurationsByChainId["0x1"].rpcEndpoints[] is string`, + ), + ); + expect(newStorage.data).toStrictEqual(oldStorage.data); + }); + + it('does nothing if the currently selected network client is neither built in nor exists in NetworkController.networkConfigurationsByChainId', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + PermissionController: { + subjects: {}, + }, + NetworkController: { + selectedNetworkClientId: 'nonExistentNetworkClientId', + networkConfigurationsByChainId: {}, }, SelectedNetworkController: { domains: {}, @@ -274,7 +365,7 @@ describe('migration #131', () => { data: { NetworkController: { selectedNetworkClientId: 'mainnet', - networkConfigurations: {}, + networkConfigurationsByChainId: {}, }, SelectedNetworkController: { domains: {}, @@ -303,7 +394,7 @@ describe('migration #131', () => { data: { NetworkController: { selectedNetworkClientId: 'mainnet', - networkConfigurations: {}, + networkConfigurationsByChainId: {}, }, SelectedNetworkController: { domains: {}, @@ -334,7 +425,7 @@ describe('migration #131', () => { data: { NetworkController: { selectedNetworkClientId: 'mainnet', - networkConfigurations: {}, + networkConfigurationsByChainId: {}, }, SelectedNetworkController: { domains: {}, @@ -357,7 +448,7 @@ describe('migration #131', () => { expect(newStorage.data).toStrictEqual({ NetworkController: { selectedNetworkClientId: 'mainnet', - networkConfigurations: {}, + networkConfigurationsByChainId: {}, }, SelectedNetworkController: { domains: {}, @@ -382,7 +473,7 @@ describe('migration #131', () => { 'built-in', { selectedNetworkClientId: 'mainnet', - networkConfigurations: {}, + networkConfigurationsByChainId: {}, }, '1', ], @@ -390,9 +481,13 @@ describe('migration #131', () => { 'custom', { selectedNetworkClientId: 'customId', - networkConfigurations: { - customId: { - chainId: '0xf', + networkConfigurationsByChainId: { + '0xf': { + rpcEndpoints: [ + { + networkClientId: 'customId', + }, + ], }, }, }, @@ -403,7 +498,12 @@ describe('migration #131', () => { ( _type: string, NetworkController: { - networkConfigurations: Record; + networkConfigurationsByChainId: Record< + string, + { + rpcEndpoints: { networkClientId: string }[]; + } + >; } & Record, chainId: string, ) => { @@ -471,6 +571,14 @@ describe('migration #131', () => { methods: [], notifications: [], }, + 'wallet:eip155': { + accounts: [ + 'wallet:eip155:0xdeadbeef', + 'wallet:eip155:0x999', + ], + methods: [], + notifications: [], + }, }, isMultichainOrigin: false, }, @@ -547,6 +655,14 @@ describe('migration #131', () => { methods: [], notifications: [], }, + 'wallet:eip155': { + accounts: [ + 'wallet:eip155:0xdeadbeef', + 'wallet:eip155:0x999', + ], + methods: [], + notifications: [], + }, }, isMultichainOrigin: false, }, @@ -623,6 +739,80 @@ describe('migration #131', () => { methods: [], notifications: [], }, + 'wallet:eip155': { + accounts: [ + 'wallet:eip155:0xdeadbeef', + 'wallet:eip155:0x999', + ], + methods: [], + notifications: [], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + }, + }, + }, + }, + }, + }); + }); + + it('replaces the eth_accounts permission with a CAIP-25 permission using the eth_accounts value without permitted chains when the origin is snapId', async () => { + const oldStorage = { + meta: { version: oldVersion }, + data: { + ...baseData(), + PermissionController: { + subjects: { + 'npm:snap': { + permissions: { + unrelated: { + foo: 'bar', + }, + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: 'restrictReturnedAccounts', + value: ['0xdeadbeef', '0x999'], + }, + ], + }, + }, + }, + }, + }, + }, + }; + + const newStorage = await migrate(oldStorage); + expect(newStorage.data).toStrictEqual({ + ...baseData(), + PermissionController: { + subjects: { + 'npm:snap': { + permissions: { + unrelated: { + foo: 'bar', + }, + 'endowment:caip25': { + parentCapability: 'endowment:caip25', + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'wallet:eip155': { + accounts: [ + 'wallet:eip155:0xdeadbeef', + 'wallet:eip155:0x999', + ], + methods: [], + notifications: [], + }, }, isMultichainOrigin: false, }, @@ -643,10 +833,14 @@ describe('migration #131', () => { ...baseData(), NetworkController: { ...baseData().NetworkController, - networkConfigurations: { - ...baseData().NetworkController.networkConfigurations, - customNetworkClientId: { - chainId: '0xa', + networkConfigurationsByChainId: { + ...baseData().NetworkController.networkConfigurationsByChainId, + '0xa': { + rpcEndpoints: [ + { + networkClientId: 'customNetworkClientId', + }, + ], }, }, }, @@ -682,10 +876,14 @@ describe('migration #131', () => { ...baseData(), NetworkController: { ...baseData().NetworkController, - networkConfigurations: { - ...baseData().NetworkController.networkConfigurations, - customNetworkClientId: { - chainId: '0xa', + networkConfigurationsByChainId: { + ...baseData().NetworkController.networkConfigurationsByChainId, + '0xa': { + rpcEndpoints: [ + { + networkClientId: 'customNetworkClientId', + }, + ], }, }, }, @@ -717,6 +915,14 @@ describe('migration #131', () => { methods: [], notifications: [], }, + 'wallet:eip155': { + accounts: [ + 'wallet:eip155:0xdeadbeef', + 'wallet:eip155:0x999', + ], + methods: [], + notifications: [], + }, }, isMultichainOrigin: false, }, @@ -843,6 +1049,14 @@ describe('migration #131', () => { methods: [], notifications: [], }, + 'wallet:eip155': { + accounts: [ + 'wallet:eip155:0xdeadbeef', + 'wallet:eip155:0x999', + ], + methods: [], + notifications: [], + }, }, isMultichainOrigin: false, }, @@ -912,6 +1126,11 @@ describe('migration #131', () => { methods: [], notifications: [], }, + 'wallet:eip155': { + accounts: ['wallet:eip155:0xdeadbeef'], + methods: [], + notifications: [], + }, }, isMultichainOrigin: false, }, @@ -935,6 +1154,11 @@ describe('migration #131', () => { methods: [], notifications: [], }, + 'wallet:eip155': { + accounts: ['wallet:eip155:0xdeadbeef'], + methods: [], + notifications: [], + }, }, isMultichainOrigin: false, }, diff --git a/app/scripts/migrations/131.ts b/app/scripts/migrations/131.ts index 5d38816ed7f5..fdb0f618f9cd 100644 --- a/app/scripts/migrations/131.ts +++ b/app/scripts/migrations/131.ts @@ -1,10 +1,4 @@ -import { - hasProperty, - Hex, - isObject, - NonEmptyArray, - Json, -} from '@metamask/utils'; +import { hasProperty, isObject, NonEmptyArray, Json } from '@metamask/utils'; import { cloneDeep } from 'lodash'; type CaveatConstraint = { @@ -23,29 +17,19 @@ const PermissionNames = { }; const BUILT_IN_NETWORKS = { - goerli: { - chainId: '0x5', - }, - sepolia: { - chainId: '0xaa36a7', - }, - mainnet: { - chainId: '0x1', - }, - 'linea-goerli': { - chainId: '0xe704', - }, - 'linea-sepolia': { - chainId: '0xe705', - }, - 'linea-mainnet': { - chainId: '0xe708', - }, + goerli: '0x5', + sepolia: '0xaa36a7', + mainnet: '0x1', + 'linea-goerli': '0xe704', + 'linea-sepolia': '0xe705', + 'linea-mainnet': '0xe708', }; const Caip25CaveatType = 'authorizedScopes'; const Caip25EndowmentPermissionName = 'endowment:caip25'; +const snapsPrefixes = ['npm:', 'local:'] as const; + type VersionedData = { meta: { version: number }; data: Record; @@ -113,7 +97,10 @@ function transformState(state: Record) { const { PermissionController: { subjects }, - NetworkController: { selectedNetworkClientId, networkConfigurations }, + NetworkController: { + selectedNetworkClientId, + networkConfigurationsByChainId, + }, SelectedNetworkController: { domains }, } = state; @@ -133,10 +120,10 @@ function transformState(state: Record) { ); return state; } - if (!isObject(networkConfigurations)) { + if (!isObject(networkConfigurationsByChainId)) { global.sentry?.captureException( new Error( - `Migration ${version}: typeof state.NetworkController.networkConfigurations is ${typeof networkConfigurations}`, + `Migration ${version}: typeof state.NetworkController.networkConfigurationsByChainId is ${typeof networkConfigurationsByChainId}`, ), ); return state; @@ -151,12 +138,43 @@ function transformState(state: Record) { } const getChainIdForNetworkClientId = (networkClientId: string) => { - const networkConfiguration = - (networkConfigurations[networkClientId] as { chainId: Hex }) ?? - BUILT_IN_NETWORKS[ - networkClientId as unknown as keyof typeof BUILT_IN_NETWORKS - ]; - return networkConfiguration?.chainId; + for (const [chainId, networkConfiguration] of Object.entries( + networkConfigurationsByChainId, + )) { + if (!isObject(networkConfiguration)) { + global.sentry?.captureException( + new Error( + `Migration ${version}: typeof state.NetworkController.networkConfigurationsByChainId["${chainId}"] is ${typeof networkConfiguration}`, + ), + ); + return null; + } + if (!Array.isArray(networkConfiguration.rpcEndpoints)) { + global.sentry?.captureException( + new Error( + `Migration ${version}: typeof state.NetworkController.networkConfigurationsByChainId["${chainId}"].rpcEndpoints is ${typeof networkConfiguration.rpcEndpoints}`, + ), + ); + return null; + } + for (const rpcEndpoint of networkConfiguration.rpcEndpoints) { + if (!isObject(rpcEndpoint)) { + global.sentry?.captureException( + new Error( + `Migration ${version}: typeof state.NetworkController.networkConfigurationsByChainId["${chainId}"].rpcEndpoints[] is ${typeof rpcEndpoint}`, + ), + ); + return null; + } + if (rpcEndpoint.networkClientId === networkClientId) { + return chainId; + } + } + } + + return BUILT_IN_NETWORKS[ + networkClientId as unknown as keyof typeof BUILT_IN_NETWORKS + ]; }; const currentChainId = getChainIdForNetworkClientId(selectedNetworkClientId); @@ -235,10 +253,14 @@ function transformState(state: Record) { } } + const isSnap = snapsPrefixes.some((prefix) => origin.startsWith(prefix)); const scopes: Record = {}; + const scopeStrings = isSnap + ? [] + : chainIds.map((chainId) => `eip155:${parseInt(chainId, 16)}`); + scopeStrings.push('wallet:eip155'); - chainIds.forEach((chainId) => { - const scopeString = `eip155:${parseInt(chainId, 16)}`; + scopeStrings.forEach((scopeString) => { const caipAccounts = ethAccounts.map( (account) => `${scopeString}:${account}`, ); From d2dd1a329325ebbaea9bde15deea820ff0305158 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 10:28:01 -0700 Subject: [PATCH 010/148] bring in new preview build --- app/scripts/controllers/permissions/specifications.js | 6 ++++-- package.json | 2 +- yarn.lock | 10 +++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index 43d8779b8f23..cf86ed98712f 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -73,8 +73,10 @@ export const getPermissionSpecifications = ({ return { [caip25EndowmentBuilder.targetName]: caip25EndowmentBuilder.specificationBuilder({ - findNetworkClientIdByChainId, - getInternalAccounts, + methodHooks: { + findNetworkClientIdByChainId, + getInternalAccounts, + } }), }; }; diff --git a/package.json b/package.json index ff20c0f2fbb4..4b69667b9d64 100644 --- a/package.json +++ b/package.json @@ -336,7 +336,7 @@ "@metamask/message-manager": "^10.1.0", "@metamask/message-signing-snap": "^0.3.3", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-a13b9c75", + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-597e8377", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index cea5c05dfb8c..9d00a3aed0a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5788,9 +5788,9 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-a13b9c75": - version: 0.0.0-preview-a13b9c75 - resolution: "@metamask-previews/multichain@npm:0.0.0-preview-a13b9c75" +"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-597e8377": + version: 0.0.0-preview-597e8377 + resolution: "@metamask-previews/multichain@npm:0.0.0-preview-597e8377" dependencies: "@metamask/api-specs": "npm:^0.10.12" "@metamask/controller-utils": "npm:^11.3.0" @@ -5804,7 +5804,7 @@ __metadata: peerDependencies: "@metamask/network-controller": ^21.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/9ffe476ea4ab42d3b4df167df539c722cd0ea398f100328cc74ad635d1e70839769f9cd959d3a8ea6b9125184013ddf3b1ac6295e29818038891110e6eb00843 + checksum: 10/fd48967ceb2d8d4c589eb71a495b1507019bd9f87f323044d7045455388d992145eb93b65ba124eeb59d287838c3dc757e68d68d186568bbf0c2e2fc3e35fd94 languageName: node linkType: hard @@ -26249,7 +26249,7 @@ __metadata: "@metamask/message-manager": "npm:^10.1.0" "@metamask/message-signing-snap": "npm:^0.3.3" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-a13b9c75" + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-597e8377" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch" "@metamask/notification-controller": "npm:^6.0.0" From 859f60bba3a70327f602125c8f61922dcdeb80bf Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 10:57:09 -0700 Subject: [PATCH 011/148] convert wallet_revokePermissions to ts --- .../controllers/permissions/specifications.js | 2 +- .../handlers/wallet-requestPermissions.js | 2 +- ...st.js => wallet-revokePermissions.test.ts} | 15 +++-- ...issions.js => wallet-revokePermissions.ts} | 57 ++++++++++++++----- 4 files changed, 55 insertions(+), 21 deletions(-) rename app/scripts/lib/rpc-method-middleware/handlers/{wallet-revokePermissions.test.js => wallet-revokePermissions.test.ts} (92%) rename app/scripts/lib/rpc-method-middleware/handlers/{wallet-revokePermissions.js => wallet-revokePermissions.ts} (63%) diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index cf86ed98712f..ccf4c9cbdd5c 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -76,7 +76,7 @@ export const getPermissionSpecifications = ({ methodHooks: { findNetworkClientIdByChainId, getInternalAccounts, - } + }, }), }; }; diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js index 866d71d47230..4b39b7daa89e 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js @@ -137,7 +137,7 @@ async function requestPermissionsImplementation( caveatValue = setEthAccounts(caveatValue, legacyApproval.approvedAccounts); - const permissions = getPermissionsForOrigin(origin) || {}; + const permissions = getPermissionsForOrigin() || {}; let caip25Endowment = permissions[Caip25EndowmentPermissionName]; const existingCaveat = caip25Endowment?.caveats?.find( ({ type }) => type === Caip25CaveatType, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts similarity index 92% rename from app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.js rename to app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts index 4b04314ffd47..521a6cb9139d 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts @@ -3,12 +3,15 @@ import { Caip25CaveatType, Caip25EndowmentPermissionName, } from '@metamask/multichain'; +import { Json, JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils'; import { PermissionNames } from '../../../controllers/permissions'; import { RestrictedMethods } from '../../../../../shared/constants/permissions'; import { revokePermissionsHandler } from './wallet-revokePermissions'; const baseRequest = { - origin: 'http://test.com', + jsonrpc: '2.0' as const, + id: 0, + method: 'wallet_revokePermissions', params: [ { [Caip25EndowmentPermissionName]: {}, @@ -39,8 +42,11 @@ const createMockedHandler = () => { }, }), ); - const response = {}; - const handler = (request) => + const response: PendingJsonRpcResponse = { + jsonrpc: '2.0' as const, + id: 0, + }; + const handler = (request: JsonRpcRequest) => revokePermissionsHandler.implementation(request, response, next, end, { revokePermissionsForOrigin, getPermissionsForOrigin, @@ -100,10 +106,11 @@ describe('revokePermissionsHandler', () => { ); }); + // @ts-expect-error This is missing from the Mocha type definitions describe.each([ [RestrictedMethods.eth_accounts], [PermissionNames.permittedChains], - ])('%s permission is specified', (permission) => { + ])('%s permission is specified', (permission: string) => { it('gets permissions for the origin', () => { const { handler, getPermissionsForOrigin } = createMockedHandler(); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts similarity index 63% rename from app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.js rename to app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts index 30e06d672068..8b57cc8d1c66 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts @@ -1,9 +1,25 @@ -import { invalidParams, MethodNames } from '@metamask/permission-controller'; -import { isNonEmptyArray } from '@metamask/utils'; +import { + CaveatSpecificationConstraint, + invalidParams, + MethodNames, + PermissionController, + PermissionSpecificationConstraint, +} from '@metamask/permission-controller'; +import { + isNonEmptyArray, + Json, + JsonRpcRequest, + PendingJsonRpcResponse, +} from '@metamask/utils'; import { Caip25CaveatType, + Caip25CaveatValue, Caip25EndowmentPermissionName, } from '@metamask/multichain'; +import { + AsyncJsonRpcEngineNextCallback, + JsonRpcEngineEndCallback, +} from 'json-rpc-engine'; import { RestrictedMethods } from '../../../../../shared/constants/permissions'; import { PermissionNames } from '../../../controllers/permissions'; @@ -30,13 +46,24 @@ export const revokePermissionsHandler = { * @returns A promise that resolves to nothing */ function revokePermissionsImplementation( - req, - res, - _next, - end, - { revokePermissionsForOrigin, getPermissionsForOrigin }, + req: JsonRpcRequest, + res: PendingJsonRpcResponse, + _next: AsyncJsonRpcEngineNextCallback, + end: JsonRpcEngineEndCallback, + { + revokePermissionsForOrigin, + getPermissionsForOrigin, + }: { + revokePermissionsForOrigin: (permissionKeys: string[]) => void; + getPermissionsForOrigin: () => ReturnType< + PermissionController< + PermissionSpecificationConstraint, + CaveatSpecificationConstraint + >['getPermissions'] + >; + }, ) { - const { params, origin } = req; + const { params } = req; const param = params?.[0]; @@ -55,10 +82,10 @@ function revokePermissionsImplementation( } const relevantPermissionKeys = permissionKeys.filter( - (name) => + (name: string) => ![ - RestrictedMethods.eth_accounts, - PermissionNames.permittedChains, + RestrictedMethods.eth_accounts as string, + PermissionNames.permittedChains as string, ].includes(name), ); @@ -66,13 +93,13 @@ function revokePermissionsImplementation( relevantPermissionKeys.length !== permissionKeys.length; if (shouldRevokeLegacyPermission) { - const permissions = getPermissionsForOrigin(origin) || {}; + const permissions = getPermissionsForOrigin() || {}; const caip25Endowment = permissions?.[Caip25EndowmentPermissionName]; - const caip25Caveat = caip25Endowment?.caveats?.find( + const caip25CaveatValue = caip25Endowment?.caveats?.find( ({ type }) => type === Caip25CaveatType, - ); + )?.value as Caip25CaveatValue; - if (caip25Caveat && caip25Caveat.value.isMultichainOrigin) { + if (caip25CaveatValue?.isMultichainOrigin) { return end( new Error('cannot modify permission granted from multichain flow'), ); // TODO: better error From 419572faeaef92a6435eadb2f4e991116c1db288 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 11:11:39 -0700 Subject: [PATCH 012/148] convert wallet_getPermissions to ts --- ....test.js => wallet-getPermissions.test.ts} | 23 ++++++---- ...ermissions.js => wallet-getPermissions.ts} | 42 ++++++++++++++----- 2 files changed, 47 insertions(+), 18 deletions(-) rename app/scripts/lib/rpc-method-middleware/handlers/{wallet-getPermissions.test.js => wallet-getPermissions.test.ts} (91%) rename app/scripts/lib/rpc-method-middleware/handlers/{wallet-getPermissions.js => wallet-getPermissions.ts} (70%) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts similarity index 91% rename from app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.js rename to app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts index a20f6bb7f4b9..3180b05e8baa 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts @@ -1,8 +1,9 @@ import { Caip25CaveatType, Caip25EndowmentPermissionName, - getPermittedEthChainIds, } from '@metamask/multichain'; +import * as Multichain from '@metamask/multichain'; +import { Json, JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils'; import { CaveatTypes, RestrictedMethods, @@ -14,9 +15,12 @@ jest.mock('@metamask/multichain', () => ({ ...jest.requireActual('@metamask/multichain'), getPermittedEthChainIds: jest.fn(), })); +const MockMultichain = jest.mocked(Multichain); const baseRequest = { - origin: 'http://test.com', + jsonrpc: '2.0' as const, + id: 0, + method: 'wallet_getPermissions', }; const createMockedHandler = () => { @@ -70,8 +74,11 @@ const createMockedHandler = () => { const getAccounts = jest .fn() .mockResolvedValue(['0x1', '0x2', '0x3', '0xdeadbeef']); - const response = {}; - const handler = (request) => + const response: PendingJsonRpcResponse = { + jsonrpc: '2.0' as const, + id: 0, + }; + const handler = (request: JsonRpcRequest) => getPermissionsHandler.implementation(request, response, next, end, { getPermissionsForOrigin, getAccounts, @@ -93,7 +100,7 @@ describe('getPermissionsHandler', () => { }); beforeEach(() => { - getPermittedEthChainIds.mockReturnValue([]); + MockMultichain.getPermittedEthChainIds.mockReturnValue([]); }); it('gets the permissions for the origin', async () => { @@ -197,7 +204,7 @@ describe('getPermissionsHandler', () => { it('gets the permitted eip155 chainIds from the CAIP-25 caveat value', async () => { const { handler } = createMockedHandler(); await handler(baseRequest); - expect(getPermittedEthChainIds).toHaveBeenCalledWith({ + expect(MockMultichain.getPermittedEthChainIds).toHaveBeenCalledWith({ requiredScopes: { 'eip155:1': { methods: [], @@ -223,7 +230,7 @@ describe('getPermissionsHandler', () => { it('returns the permissions with a permittedChains permission if some eip155 chainIds are permissioned', async () => { const { handler, getAccounts, response } = createMockedHandler(); getAccounts.mockResolvedValue([]); - getPermittedEthChainIds.mockReturnValue(['0x1', '0x64']); + MockMultichain.getPermittedEthChainIds.mockReturnValue(['0x1', '0x64']); await handler(baseRequest); expect(response.result).toStrictEqual([ @@ -254,7 +261,7 @@ describe('getPermissionsHandler', () => { it('returns the permissions with a eth_accounts and permittedChains permission if some eip155 accounts and chainIds are permissioned', async () => { const { handler, getAccounts, response } = createMockedHandler(); getAccounts.mockResolvedValue(['0x1', '0x2', '0xdeadbeef']); - getPermittedEthChainIds.mockReturnValue(['0x1', '0x64']); + MockMultichain.getPermittedEthChainIds.mockReturnValue(['0x1', '0x64']); await handler(baseRequest); expect(response.result).toStrictEqual([ diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts similarity index 70% rename from app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js rename to app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts index e4ad911f0a11..09476121b33c 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts @@ -1,9 +1,20 @@ -import { MethodNames } from '@metamask/permission-controller'; +import { + CaveatSpecificationConstraint, + MethodNames, + PermissionController, + PermissionSpecificationConstraint, +} from '@metamask/permission-controller'; import { Caip25CaveatType, + Caip25CaveatValue, Caip25EndowmentPermissionName, getPermittedEthChainIds, } from '@metamask/multichain'; +import { + AsyncJsonRpcEngineNextCallback, + JsonRpcEngineEndCallback, +} from 'json-rpc-engine'; +import { Json, JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils'; import { PermissionNames } from '../../../controllers/permissions'; import { CaveatTypes, @@ -32,21 +43,32 @@ export const getPermissionsHandler = { * @returns A promise that resolves to nothing */ async function getPermissionsImplementation( - _req, - res, - _next, - end, - { getPermissionsForOrigin, getAccounts }, + _req: JsonRpcRequest, + res: PendingJsonRpcResponse, + _next: AsyncJsonRpcEngineNextCallback, + end: JsonRpcEngineEndCallback, + { + getPermissionsForOrigin, + getAccounts, + }: { + getPermissionsForOrigin: () => ReturnType< + PermissionController< + PermissionSpecificationConstraint, + CaveatSpecificationConstraint + >['getPermissions'] + >; + getAccounts: () => Promise; + }, ) { // permissions are frozen and must be cloned before modified const permissions = { ...getPermissionsForOrigin() } || {}; const caip25Endowment = permissions[Caip25EndowmentPermissionName]; - const caip25Caveat = caip25Endowment?.caveats?.find( + const caip25CaveatValue = caip25Endowment?.caveats?.find( ({ type }) => type === Caip25CaveatType, - ); + )?.value as Caip25CaveatValue; delete permissions[Caip25EndowmentPermissionName]; - if (caip25Caveat) { + if (caip25CaveatValue) { // We cannot derive ethAccounts directly from the CAIP-25 permission // because the accounts will not be in order of lastSelected const ethAccounts = await getAccounts(); @@ -64,7 +86,7 @@ async function getPermissionsImplementation( }; } - const ethChainIds = getPermittedEthChainIds(caip25Caveat.value); + const ethChainIds = getPermittedEthChainIds(caip25CaveatValue); if (ethChainIds.length > 0) { permissions[PermissionNames.permittedChains] = { From 81027d0d778207832725c4ab348ce5bf1642b9cb Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Tue, 15 Oct 2024 18:27:46 +0000 Subject: [PATCH 013/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 87 +++++++++++++++++++++++++-- lavamoat/browserify/flask/policy.json | 87 +++++++++++++++++++++++++-- lavamoat/browserify/main/policy.json | 87 +++++++++++++++++++++++++-- lavamoat/browserify/mmi/policy.json | 87 +++++++++++++++++++++++++-- 4 files changed, 324 insertions(+), 24 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index d7522783f9fc..5c94c91b060f 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1421,18 +1421,13 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/message-manager>jsonschema": true, + "@metamask/multichain>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, - "@metamask/message-manager>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1448,6 +1443,59 @@ "@noble/hashes": true } }, + "@metamask/multichain": { + "globals": { + "console.error": true, + "console.log": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/eth-json-rpc-filters": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/multichain>@open-rpc/schema-utils-js": true, + "@metamask/multichain>jsonschema": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/safe-event-emitter": true, + "@metamask/utils": true, + "browserify>assert": true, + "lodash": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js": { + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, + "@metamask/multichain>@open-rpc/schema-utils-js>@open-rpc/meta-schema": true, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, + "@open-rpc/schema-utils-js>is-url": true, + "eth-rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, + "eth-rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { + "globals": { + "console": true + }, + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "eslint>fast-deep-equal": true, + "uri-js": true + } + }, + "@metamask/multichain>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -2906,6 +2954,33 @@ "crypto": true } }, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { + "packages": { + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, + "@open-rpc/test-coverage>isomorphic-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch": { + "globals": { + "fetch.bind": true + }, + "packages": { + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { + "globals": { + "AbortController": true, + "Blob": true, + "FileReader": true, + "FormData": true, + "URLSearchParams.prototype.isPrototypeOf": true, + "XMLHttpRequest": true, + "console.warn": true, + "define": true, + "setTimeout": true + } + }, "@popperjs/core": { "globals": { "Element": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index d7522783f9fc..5c94c91b060f 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1421,18 +1421,13 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/message-manager>jsonschema": true, + "@metamask/multichain>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, - "@metamask/message-manager>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1448,6 +1443,59 @@ "@noble/hashes": true } }, + "@metamask/multichain": { + "globals": { + "console.error": true, + "console.log": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/eth-json-rpc-filters": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/multichain>@open-rpc/schema-utils-js": true, + "@metamask/multichain>jsonschema": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/safe-event-emitter": true, + "@metamask/utils": true, + "browserify>assert": true, + "lodash": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js": { + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, + "@metamask/multichain>@open-rpc/schema-utils-js>@open-rpc/meta-schema": true, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, + "@open-rpc/schema-utils-js>is-url": true, + "eth-rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, + "eth-rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { + "globals": { + "console": true + }, + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "eslint>fast-deep-equal": true, + "uri-js": true + } + }, + "@metamask/multichain>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -2906,6 +2954,33 @@ "crypto": true } }, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { + "packages": { + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, + "@open-rpc/test-coverage>isomorphic-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch": { + "globals": { + "fetch.bind": true + }, + "packages": { + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { + "globals": { + "AbortController": true, + "Blob": true, + "FileReader": true, + "FormData": true, + "URLSearchParams.prototype.isPrototypeOf": true, + "XMLHttpRequest": true, + "console.warn": true, + "define": true, + "setTimeout": true + } + }, "@popperjs/core": { "globals": { "Element": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index d7522783f9fc..5c94c91b060f 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1421,18 +1421,13 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/message-manager>jsonschema": true, + "@metamask/multichain>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, - "@metamask/message-manager>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1448,6 +1443,59 @@ "@noble/hashes": true } }, + "@metamask/multichain": { + "globals": { + "console.error": true, + "console.log": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/eth-json-rpc-filters": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/multichain>@open-rpc/schema-utils-js": true, + "@metamask/multichain>jsonschema": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/safe-event-emitter": true, + "@metamask/utils": true, + "browserify>assert": true, + "lodash": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js": { + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, + "@metamask/multichain>@open-rpc/schema-utils-js>@open-rpc/meta-schema": true, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, + "@open-rpc/schema-utils-js>is-url": true, + "eth-rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, + "eth-rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { + "globals": { + "console": true + }, + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "eslint>fast-deep-equal": true, + "uri-js": true + } + }, + "@metamask/multichain>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -2906,6 +2954,33 @@ "crypto": true } }, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { + "packages": { + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, + "@open-rpc/test-coverage>isomorphic-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch": { + "globals": { + "fetch.bind": true + }, + "packages": { + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { + "globals": { + "AbortController": true, + "Blob": true, + "FileReader": true, + "FormData": true, + "URLSearchParams.prototype.isPrototypeOf": true, + "XMLHttpRequest": true, + "console.warn": true, + "define": true, + "setTimeout": true + } + }, "@popperjs/core": { "globals": { "Element": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 3df824f29c78..43fc5792c40d 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -1513,18 +1513,13 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/message-manager>jsonschema": true, + "@metamask/multichain>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, - "@metamask/message-manager>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1540,6 +1535,59 @@ "@noble/hashes": true } }, + "@metamask/multichain": { + "globals": { + "console.error": true, + "console.log": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/eth-json-rpc-filters": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/multichain>@open-rpc/schema-utils-js": true, + "@metamask/multichain>jsonschema": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/safe-event-emitter": true, + "@metamask/utils": true, + "browserify>assert": true, + "lodash": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js": { + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, + "@metamask/multichain>@open-rpc/schema-utils-js>@open-rpc/meta-schema": true, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, + "@open-rpc/schema-utils-js>is-url": true, + "eth-rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, + "eth-rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { + "globals": { + "console": true + }, + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "eslint>fast-deep-equal": true, + "uri-js": true + } + }, + "@metamask/multichain>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -2998,6 +3046,33 @@ "crypto": true } }, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { + "packages": { + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, + "@open-rpc/test-coverage>isomorphic-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch": { + "globals": { + "fetch.bind": true + }, + "packages": { + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { + "globals": { + "AbortController": true, + "Blob": true, + "FileReader": true, + "FormData": true, + "URLSearchParams.prototype.isPrototypeOf": true, + "XMLHttpRequest": true, + "console.warn": true, + "define": true, + "setTimeout": true + } + }, "@popperjs/core": { "globals": { "Element": true, From 48388a29c08d7fa5f1f1e10626a06a74ee3c6cd8 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 12:05:15 -0700 Subject: [PATCH 014/148] convert wallet_requestPermissions to ts --- ...t.js => wallet-requestPermissions.test.ts} | 85 ++++++++++++----- ...ssions.js => wallet-requestPermissions.ts} | 91 +++++++++++++++---- 2 files changed, 137 insertions(+), 39 deletions(-) rename app/scripts/lib/rpc-method-middleware/handlers/{wallet-requestPermissions.test.js => wallet-requestPermissions.test.ts} (90%) rename app/scripts/lib/rpc-method-middleware/handlers/{wallet-requestPermissions.js => wallet-requestPermissions.ts} (72%) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts similarity index 90% rename from app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.js rename to app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts index 0e038225a744..ea08a867e8b4 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts @@ -1,9 +1,13 @@ -import { invalidParams } from '@metamask/permission-controller'; +import { + invalidParams, + RequestedPermissions, +} from '@metamask/permission-controller'; import { Caip25CaveatType, Caip25EndowmentPermissionName, } from '@metamask/multichain'; import * as Multichain from '@metamask/multichain'; +import { Json, JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils'; import { CaveatTypes, RestrictedMethods, @@ -19,6 +23,9 @@ jest.mock('@metamask/multichain', () => ({ const MockMultichain = jest.mocked(Multichain); const getBaseRequest = () => ({ + jsonrpc: '2.0' as const, + id: 0, + method: 'wallet_requestPermissions', networkClientId: 'mainnet', origin: 'http://test.com', params: [ @@ -111,17 +118,25 @@ const createMockedHandler = () => { approvedAccounts: ['0xdeadbeef'], }); const getAccounts = jest.fn().mockResolvedValue([]); - const response = {}; - const handler = (request) => - requestPermissionsHandler.implementation(request, response, next, end, { - requestPermissionsForOrigin, - getPermissionsForOrigin, - updateCaveat, - grantPermissions, - requestPermissionApprovalForOrigin, - getAccounts, - request, - }); + const response: PendingJsonRpcResponse = { + jsonrpc: '2.0' as const, + id: 0, + }; + const handler = (request: unknown) => + requestPermissionsHandler.implementation( + request as JsonRpcRequest<[RequestedPermissions]> & { origin: string }, + response, + next, + end, + { + requestPermissionsForOrigin, + getPermissionsForOrigin, + updateCaveat, + grantPermissions, + requestPermissionApprovalForOrigin, + getAccounts, + }, + ); return { response, @@ -546,13 +561,21 @@ describe('requestPermissionsHandler', () => { it('sets the approved accounts on the CAIP-25 caveat after the approved chainIds if origin is not snapId', async () => { const { handler } = createMockedHandler(); - MockMultichain.setPermittedEthChainIds.mockReturnValue( - 'caveatValueWithEthChainIdsSet', - ); + MockMultichain.setPermittedEthChainIds.mockReturnValue({ + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { caveatValueWithEthChainIdsSet: true }, + isMultichainOrigin: false, + }); await handler(getBaseRequest()); expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( - 'caveatValueWithEthChainIdsSet', + { + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { caveatValueWithEthChainIdsSet: true }, + isMultichainOrigin: false, + }, ['0xdeadbeef'], ); }); @@ -582,7 +605,7 @@ describe('requestPermissionsHandler', () => { const { handler, getPermissionsForOrigin } = createMockedHandler(); await handler(getBaseRequest()); - expect(getPermissionsForOrigin).toHaveBeenCalledWith('http://test.com'); + expect(getPermissionsForOrigin).toHaveBeenCalled(); }); it('throws an error when a CAIP-25 already exists that was granted from the multichain flow (isMultichainOrigin: true)', async () => { @@ -612,14 +635,24 @@ describe('requestPermissionsHandler', () => { it('updates the caveat when a CAIP-25 already exists that was granted from the legacy flow (isMultichainOrigin: false)', async () => { const { handler, updateCaveat } = createMockedHandler(); - MockMultichain.setEthAccounts.mockReturnValue('updatedCaveatValue'); + MockMultichain.setEthAccounts.mockReturnValue({ + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { caveatValueWithEthChainIdsSet: true }, + isMultichainOrigin: false, + }); await handler(getBaseRequest()); expect(updateCaveat).toHaveBeenCalledWith( 'http://test.com', Caip25EndowmentPermissionName, Caip25CaveatType, - 'updatedCaveatValue', + { + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { caveatValueWithEthChainIdsSet: true }, + isMultichainOrigin: false, + }, ); }); @@ -627,7 +660,12 @@ describe('requestPermissionsHandler', () => { const { handler, getPermissionsForOrigin, grantPermissions } = createMockedHandler(); getPermissionsForOrigin.mockReturnValue({}); - MockMultichain.setEthAccounts.mockReturnValue('updatedCaveatValue'); + MockMultichain.setEthAccounts.mockReturnValue({ + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { caveatValueWithEthChainIdsSet: true }, + isMultichainOrigin: false, + }); await handler(getBaseRequest()); expect(grantPermissions).toHaveBeenCalledWith({ @@ -639,7 +677,12 @@ describe('requestPermissionsHandler', () => { caveats: [ { type: Caip25CaveatType, - value: 'updatedCaveatValue', + value: { + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { caveatValueWithEthChainIdsSet: true }, + isMultichainOrigin: false, + }, }, ], }, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts similarity index 72% rename from app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js rename to app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index 4b39b7daa89e..1dca83223d07 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -1,12 +1,32 @@ import { pick } from 'lodash'; import { isPlainObject } from '@metamask/controller-utils'; -import { invalidParams, MethodNames } from '@metamask/permission-controller'; +import { + Caveat, + CaveatSpecificationConstraint, + invalidParams, + MethodNames, + PermissionController, + PermissionSpecificationConstraint, + RequestedPermissions, + ValidPermission, +} from '@metamask/permission-controller'; import { Caip25CaveatType, + Caip25CaveatValue, Caip25EndowmentPermissionName, setEthAccounts, setPermittedEthChainIds, } from '@metamask/multichain'; +import { + Hex, + Json, + JsonRpcRequest, + PendingJsonRpcResponse, +} from '@metamask/utils'; +import { + AsyncJsonRpcEngineNextCallback, + JsonRpcEngineEndCallback, +} from 'json-rpc-engine'; import { CaveatTypes, RestrictedMethods, @@ -28,6 +48,15 @@ export const requestPermissionsHandler = { }, }; +type AbstractPermissionController = PermissionController< + PermissionSpecificationConstraint, + CaveatSpecificationConstraint +>; + +type GrantedPermissions = Awaited< + ReturnType +>[0]; + /** * Request Permissions implementation to be used in JsonRpcEngine middleware. * @@ -45,10 +74,10 @@ export const requestPermissionsHandler = { * @returns A promise that resolves to nothing */ async function requestPermissionsImplementation( - req, - res, - _next, - end, + req: JsonRpcRequest<[RequestedPermissions]> & { origin: string }, + res: PendingJsonRpcResponse, + _next: AsyncJsonRpcEngineNextCallback, + end: JsonRpcEngineEndCallback, { requestPermissionsForOrigin, getPermissionsForOrigin, @@ -56,6 +85,27 @@ async function requestPermissionsImplementation( grantPermissions, requestPermissionApprovalForOrigin, getAccounts, + }: { + requestPermissionsForOrigin: ( + requestedPermissions: RequestedPermissions, + ) => Promise<[GrantedPermissions]>; + updateCaveat: ( + origin: string, + permissionName: string, + caveatName: string, + caveatValue: Caip25CaveatValue, + ) => void; + grantPermissions: ( + ...args: Parameters + ) => Record>>; + requestPermissionApprovalForOrigin: ( + requestedPermissions: RequestedPermissions, + ) => Promise<{ approvedAccounts: Hex[]; approvedChainIds: Hex[] }>; + + getPermissionsForOrigin: () => ReturnType< + AbstractPermissionController['getPermissions'] + >; + getAccounts: () => Promise; }, ) { const { origin, params } = req; @@ -67,7 +117,9 @@ async function requestPermissionsImplementation( const [requestedPermissions] = params; delete requestedPermissions[Caip25EndowmentPermissionName]; - const legacyRequestedPermissions = pick(requestedPermissions, [ + const legacyRequestedPermissions: Partial< + Pick + > = pick(requestedPermissions, [ RestrictedMethods.eth_accounts, PermissionNames.permittedChains, ]); @@ -101,7 +153,7 @@ async function requestPermissionsImplementation( ); } - let grantedPermissions = {}; + let grantedPermissions: GrantedPermissions = {}; // Request permissions from the PermissionController for any permissions other // than eth_accounts and permittedChains in the params. If no permissions // are in the params, then request empty permissions from the PermissionController @@ -123,27 +175,30 @@ async function requestPermissionsImplementation( // We assume that approvedAccounts and permittedChains are both defined here. // Until they are actually combined, when testing, you must request both // eth_accounts and permittedChains together. - let caveatValue = { + let newCaveatValue = { requiredScopes: {}, optionalScopes: {}, isMultichainOrigin: false, }; if (!isSnapId(origin)) { - caveatValue = setPermittedEthChainIds( - caveatValue, + newCaveatValue = setPermittedEthChainIds( + newCaveatValue, legacyApproval.approvedChainIds, ); } - caveatValue = setEthAccounts(caveatValue, legacyApproval.approvedAccounts); + newCaveatValue = setEthAccounts( + newCaveatValue, + legacyApproval.approvedAccounts, + ); const permissions = getPermissionsForOrigin() || {}; let caip25Endowment = permissions[Caip25EndowmentPermissionName]; - const existingCaveat = caip25Endowment?.caveats?.find( + const existingCaveatValue = caip25Endowment?.caveats?.find( ({ type }) => type === Caip25CaveatType, - ); - if (existingCaveat) { - if (existingCaveat.value.isMultichainOrigin) { + )?.value as Caip25CaveatValue; + if (existingCaveatValue) { + if (existingCaveatValue.isMultichainOrigin) { return end( new Error('cannot modify permission granted from multichain flow'), ); // TODO: better error @@ -153,7 +208,7 @@ async function requestPermissionsImplementation( origin, Caip25EndowmentPermissionName, Caip25CaveatType, - caveatValue, + newCaveatValue, ); } else { caip25Endowment = grantPermissions({ @@ -163,7 +218,7 @@ async function requestPermissionsImplementation( caveats: [ { type: Caip25CaveatType, - value: caveatValue, + value: newCaveatValue, }, ], }, @@ -200,6 +255,6 @@ async function requestPermissionsImplementation( } } - res.result = Object.values(grantedPermissions); + res.result = Object.values(grantedPermissions) as Json; return end(); } From 06663e9b4a1ce1fee91897fb2a4e0e24a6b50235 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 12:18:18 -0700 Subject: [PATCH 015/148] yarn dedupe --- yarn.lock | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9d00a3aed0a2..4851225c8b26 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4150,14 +4150,7 @@ __metadata: languageName: node linkType: hard -"@json-schema-tools/meta-schema@npm:^1.6.10": - version: 1.7.4 - resolution: "@json-schema-tools/meta-schema@npm:1.7.4" - checksum: 10/6a688260eaac550d372325a39e7d4f44db7904a3fcaa3d3e0bf318b259007326592b53e511025ff35010ba0e0314dba338fd169338c5ea090328663f3e7cbd46 - languageName: node - linkType: hard - -"@json-schema-tools/meta-schema@npm:^1.7.5": +"@json-schema-tools/meta-schema@npm:^1.6.10, @json-schema-tools/meta-schema@npm:^1.7.5": version: 1.7.5 resolution: "@json-schema-tools/meta-schema@npm:1.7.5" checksum: 10/707dc3a285c26c37d00f418e9d0ef8a2ad1c23d4936ad5aab0ce94c9ae36a7a6125c4ca5048513af64b7e6e527b5472a1701d1f709c379acdd7ad12f6409d2cd @@ -4184,20 +4177,13 @@ __metadata: languageName: node linkType: hard -"@json-schema-tools/traverse@npm:^1.10.4": +"@json-schema-tools/traverse@npm:^1.10.4, @json-schema-tools/traverse@npm:^1.7.5, @json-schema-tools/traverse@npm:^1.7.8": version: 1.10.4 resolution: "@json-schema-tools/traverse@npm:1.10.4" checksum: 10/0027bc90df01c5eeee0833e722b7320b53be8b5ce3f4e0e4a6e45713a38e6f88f21aba31e3dd973093ef75cd21a40c07fe8f112da8f49a7919b1c0e44c904d20 languageName: node linkType: hard -"@json-schema-tools/traverse@npm:^1.7.5, @json-schema-tools/traverse@npm:^1.7.8": - version: 1.10.3 - resolution: "@json-schema-tools/traverse@npm:1.10.3" - checksum: 10/690623740d223ea373d8e561dad5c70bf86461bcedc5fc45da01c87bcdf3284bbdbad3006d4a423f8d82e4b2d4580e45f92c0b272f006024fb597d7f01876215 - languageName: node - linkType: hard - "@juggle/resize-observer@npm:^3.3.1": version: 3.4.0 resolution: "@juggle/resize-observer@npm:3.4.0" @@ -7070,14 +7056,7 @@ __metadata: languageName: node linkType: hard -"@open-rpc/meta-schema@npm:^1.14.6": - version: 1.14.6 - resolution: "@open-rpc/meta-schema@npm:1.14.6" - checksum: 10/7cb672ea42c143c3fcb177ad04b935d56c38cb28fc7ede0a0bb50293e0e49dee81046c2d43bc57c8bbf9efbbb76356d60b4a8e408a03ecc8fa5952ef3e342316 - languageName: node - linkType: hard - -"@open-rpc/meta-schema@npm:^1.14.9": +"@open-rpc/meta-schema@npm:^1.14.6, @open-rpc/meta-schema@npm:^1.14.9": version: 1.14.9 resolution: "@open-rpc/meta-schema@npm:1.14.9" checksum: 10/51505dcf7aa1a2285c78953c9b33711cede5f2765aa37dcb9ee7756d689e2ff2a89cfc6039504f0569c52a805fb9aa18f30a7c02ad7a06e793c801e43b419104 From 273c5235462dd19a9dd3edd5e7be85655af35a8a Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 12:19:10 -0700 Subject: [PATCH 016/148] update ui/components/multichain/pages/permissions-page/permissions-page.test.js snapshot --- .../__snapshots__/permissions-page.test.js.snap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/multichain/pages/permissions-page/__snapshots__/permissions-page.test.js.snap b/ui/components/multichain/pages/permissions-page/__snapshots__/permissions-page.test.js.snap index c49cd1ba3236..e9f5fa78b9cd 100644 --- a/ui/components/multichain/pages/permissions-page/__snapshots__/permissions-page.test.js.snap +++ b/ui/components/multichain/pages/permissions-page/__snapshots__/permissions-page.test.js.snap @@ -84,7 +84,7 @@ exports[`All Connections render renders correctly 1`] = ` accounts •  - 0 + 1 networks From 3d83a85b9ed79bfab967e3080d5d0e302fd029e6 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 15 Oct 2024 14:32:42 -0500 Subject: [PATCH 017/148] improve errors + caveate mutator logic --- .../controllers/permissions/background-api.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index 83302ba4b7d2..c47c8caa15bf 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -11,6 +11,10 @@ import { import { RestrictedMethods } from '../../../../shared/constants/permissions'; import { PermissionNames } from './specifications'; +const snapsPrefixes = ['npm:', 'local:']; +const isSnap = (origin) => + snapsPrefixes.some((prefix) => origin.startsWith(prefix)); + export function getPermissionBackgroundApiMethods({ permissionController, approvalController, @@ -168,8 +172,8 @@ export function getPermissionBackgroundApiMethods({ if (!caip25Caveat) { throw new Error( - 'tried to remove accounts when none have been permissioned', - ); // TODO: better error + `Cannot remove account "${account}": No permissions exist for origin "${origin}".`, + ); } const existingAccounts = getEthAccounts(caip25Caveat.value); @@ -219,8 +223,8 @@ export function getPermissionBackgroundApiMethods({ if (!caip25Caveat) { throw new Error( - 'tried to remove chains when none have been permissioned', - ); // TODO: better error + `Cannot remove permission for chainId "${chainId}": No permissions exist for origin "${origin}".`, + ); } const existingEthChainIds = getPermittedEthChainIds(caip25Caveat.value); @@ -233,9 +237,7 @@ export function getPermissionBackgroundApiMethods({ return; } - // TODO: Is this right? Do we want to revoke the entire - // CAIP-25 permission if no eip-155 chains are left? - if (remainingChainIds.length === 0) { + if (remainingChainIds.length === 0 && !isSnap(origin)) { permissionController.revokePermission( origin, Caip25EndowmentPermissionName, From 92c2cf3e300177b4d922c45e4fa350e0fd7a0432 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 15 Oct 2024 14:49:15 -0500 Subject: [PATCH 018/148] more error improvements --- app/scripts/controllers/permissions/background-api.js | 8 ++++++-- .../handlers/ethereum-chain-utils.js | 6 +++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index c47c8caa15bf..6a839e1c31f6 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -33,7 +33,9 @@ export function getPermissionBackgroundApiMethods({ } if (!caip25Caveat) { - throw new Error('tried to add accounts when none have been permissioned'); // TODO: better error + throw new Error( + `Cannot add account permissions for origin "${origin}": no permission currently exists for this origin.`, + ); } const ethAccounts = getEthAccounts(caip25Caveat.value); @@ -68,7 +70,9 @@ export function getPermissionBackgroundApiMethods({ } if (!caip25Caveat) { - throw new Error('tried to add chains when none have been permissioned'); // TODO: better error + throw new Error( + `Cannot add chain permissions for origin "${origin}": no permission currently exists for this origin.`, + ); } // get the list of permitted eth accounts before we modify the permitted chains and potentially lose some diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index eeaf246ee0df..bfeaa2bae259 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -187,10 +187,10 @@ export async function switchChain( if (!ethChainIds.includes(chainId)) { if (caip25Caveat.value.isMultichainOrigin) { return end( - new Error( - 'cannot switch to chain that was not permissioned in the multichain flow', + ethErrors.provider.unauthorized( + `Cannot switch to or add permissions for chainId '${chainId}' because permissions were granted over the Multichain API.`, ), - ); // TODO: better error + ); } // TODO: This behavior may have deviated from the original permittedChains add chain behavior From d093b0cd7e60d26de423b008d895a3a552e0063e Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 13:30:18 -0700 Subject: [PATCH 019/148] bring in new preview build --- .../permissions/background-api.test.js | 41 ------------------- package.json | 2 +- yarn.lock | 10 ++--- 3 files changed, 6 insertions(+), 47 deletions(-) diff --git a/app/scripts/controllers/permissions/background-api.test.js b/app/scripts/controllers/permissions/background-api.test.js index 5622f02f0b5c..9226398cf159 100644 --- a/app/scripts/controllers/permissions/background-api.test.js +++ b/app/scripts/controllers/permissions/background-api.test.js @@ -145,16 +145,6 @@ describe('permission background API methods', () => { 'wallet:eip155:0x4', ], }, - wallet: { - methods: [], - notifications: [], - accounts: [ - 'wallet:eip155:0x2', - 'wallet:eip155:0x3', - 'wallet:eip155:0x1', - 'wallet:eip155:0x4', - ], - }, }, isMultichainOrigin: true, }, @@ -296,17 +286,6 @@ describe('permission background API methods', () => { 'wallet:eip155:0x5', ], }, - wallet: { - methods: [], - notifications: [], - accounts: [ - 'wallet:eip155:0x2', - 'wallet:eip155:0x3', - 'wallet:eip155:0x1', - 'wallet:eip155:0x4', - 'wallet:eip155:0x5', - ], - }, }, isMultichainOrigin: true, }, @@ -507,11 +486,6 @@ describe('permission background API methods', () => { notifications: [], accounts: ['wallet:eip155:0x3', 'wallet:eip155:0x1'], }, - wallet: { - methods: [], - notifications: [], - accounts: ['wallet:eip155:0x3', 'wallet:eip155:0x1'], - }, }, isMultichainOrigin: true, }, @@ -604,11 +578,6 @@ describe('permission background API methods', () => { notifications: [], accounts: ['wallet:eip155:0xdeadbeef'], }, - wallet: { - methods: [], - notifications: [], - accounts: ['wallet:eip155:0xdeadbeef'], - }, }, isMultichainOrigin: false, }, @@ -735,11 +704,6 @@ describe('permission background API methods', () => { notifications: [], accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x2'], }, - wallet: { - methods: [], - notifications: [], - accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x2'], - }, }, isMultichainOrigin: true, }, @@ -867,11 +831,6 @@ describe('permission background API methods', () => { notifications: [], accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x2'], }, - wallet: { - methods: [], - notifications: [], - accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x2'], - }, }, isMultichainOrigin: true, }, diff --git a/package.json b/package.json index 99b873b615b1..22257d70aa59 100644 --- a/package.json +++ b/package.json @@ -336,7 +336,7 @@ "@metamask/message-manager": "^10.1.0", "@metamask/message-signing-snap": "^0.3.3", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-597e8377", + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-54f7c497", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index a8648fe1bb2c..f7dc785944bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5774,9 +5774,9 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-597e8377": - version: 0.0.0-preview-597e8377 - resolution: "@metamask-previews/multichain@npm:0.0.0-preview-597e8377" +"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-54f7c497": + version: 0.0.0-preview-54f7c497 + resolution: "@metamask-previews/multichain@npm:0.0.0-preview-54f7c497" dependencies: "@metamask/api-specs": "npm:^0.10.12" "@metamask/controller-utils": "npm:^11.3.0" @@ -5790,7 +5790,7 @@ __metadata: peerDependencies: "@metamask/network-controller": ^21.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/fd48967ceb2d8d4c589eb71a495b1507019bd9f87f323044d7045455388d992145eb93b65ba124eeb59d287838c3dc757e68d68d186568bbf0c2e2fc3e35fd94 + checksum: 10/14697c5ddc554f0b45fdeb42bbc2f49d6c3903f63ab152889359dea08d99f77caa84e6074cae04d4012057ab6b141085ae181754bf896c6fa6840823c4384d5a languageName: node linkType: hard @@ -26228,7 +26228,7 @@ __metadata: "@metamask/message-manager": "npm:^10.1.0" "@metamask/message-signing-snap": "npm:^0.3.3" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-597e8377" + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-54f7c497" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch" "@metamask/notification-controller": "npm:^6.0.0" From 150221d3c83d7a96af94a6857743da9af0f7a78b Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Tue, 15 Oct 2024 20:42:05 +0000 Subject: [PATCH 020/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 4 ++-- lavamoat/browserify/flask/policy.json | 4 ++-- lavamoat/browserify/main/policy.json | 4 ++-- lavamoat/browserify/mmi/policy.json | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 5c94c91b060f..a6060bf6655a 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1466,8 +1466,8 @@ "packages": { "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@open-rpc/meta-schema": true, "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, + "@open-rpc/meta-schema": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, "@open-rpc/schema-utils-js>is-url": true, "eth-rpc-errors>fast-safe-stringify": true @@ -1475,7 +1475,7 @@ }, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, + "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, "eth-rpc-errors>fast-safe-stringify": true } diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 5c94c91b060f..a6060bf6655a 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1466,8 +1466,8 @@ "packages": { "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@open-rpc/meta-schema": true, "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, + "@open-rpc/meta-schema": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, "@open-rpc/schema-utils-js>is-url": true, "eth-rpc-errors>fast-safe-stringify": true @@ -1475,7 +1475,7 @@ }, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, + "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, "eth-rpc-errors>fast-safe-stringify": true } diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 5c94c91b060f..a6060bf6655a 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1466,8 +1466,8 @@ "packages": { "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@open-rpc/meta-schema": true, "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, + "@open-rpc/meta-schema": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, "@open-rpc/schema-utils-js>is-url": true, "eth-rpc-errors>fast-safe-stringify": true @@ -1475,7 +1475,7 @@ }, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, + "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, "eth-rpc-errors>fast-safe-stringify": true } diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 43fc5792c40d..333e756d4240 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -1558,8 +1558,8 @@ "packages": { "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@open-rpc/meta-schema": true, "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, + "@open-rpc/meta-schema": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, "@open-rpc/schema-utils-js>is-url": true, "eth-rpc-errors>fast-safe-stringify": true @@ -1567,7 +1567,7 @@ }, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, + "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, "eth-rpc-errors>fast-safe-stringify": true } From c7d40d31b2a3db37f166160a1049edcdd4586aaa Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 13:58:19 -0700 Subject: [PATCH 021/148] bring in new preview build --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 22257d70aa59..d0c04ea00608 100644 --- a/package.json +++ b/package.json @@ -336,7 +336,7 @@ "@metamask/message-manager": "^10.1.0", "@metamask/message-signing-snap": "^0.3.3", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-54f7c497", + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-ada451da", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index f7dc785944bd..554e883b5a59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5774,9 +5774,9 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-54f7c497": - version: 0.0.0-preview-54f7c497 - resolution: "@metamask-previews/multichain@npm:0.0.0-preview-54f7c497" +"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-ada451da": + version: 0.0.0-preview-ada451da + resolution: "@metamask-previews/multichain@npm:0.0.0-preview-ada451da" dependencies: "@metamask/api-specs": "npm:^0.10.12" "@metamask/controller-utils": "npm:^11.3.0" @@ -5790,7 +5790,7 @@ __metadata: peerDependencies: "@metamask/network-controller": ^21.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/14697c5ddc554f0b45fdeb42bbc2f49d6c3903f63ab152889359dea08d99f77caa84e6074cae04d4012057ab6b141085ae181754bf896c6fa6840823c4384d5a + checksum: 10/43b93228a9c5f4241d05be0984941ea6a511906849785707b3b500b72e0e3c8d99444c0f54ffa3c560324f9cb60fca8b09249e3ea7e35da1a42407ef2d5b14e3 languageName: node linkType: hard @@ -26228,7 +26228,7 @@ __metadata: "@metamask/message-manager": "npm:^10.1.0" "@metamask/message-signing-snap": "npm:^0.3.3" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-54f7c497" + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-ada451da" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch" "@metamask/notification-controller": "npm:^6.0.0" From a3f5c693023646dc33b450fef4acc92ab55fb76a Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 15 Oct 2024 16:01:05 -0500 Subject: [PATCH 022/148] more error improvements --- .../handlers/wallet-requestPermissions.test.ts | 2 +- .../handlers/wallet-requestPermissions.ts | 6 ++++-- .../handlers/wallet-revokePermissions.test.ts | 2 +- .../handlers/wallet-revokePermissions.ts | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts index ea08a867e8b4..983973bdf6e3 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts @@ -629,7 +629,7 @@ describe('requestPermissionsHandler', () => { await handler(getBaseRequest()); expect(end).toHaveBeenCalledWith( - new Error('cannot modify permission granted from multichain flow'), + new Error('Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.'), ); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index 1dca83223d07..fbe478b27d72 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -200,8 +200,10 @@ async function requestPermissionsImplementation( if (existingCaveatValue) { if (existingCaveatValue.isMultichainOrigin) { return end( - new Error('cannot modify permission granted from multichain flow'), - ); // TODO: better error + new Error( + 'Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.', + ), + ); } updateCaveat( diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts index 521a6cb9139d..e09a9f0a1c52 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts @@ -188,7 +188,7 @@ describe('revokePermissionsHandler', () => { ], }); expect(end).toHaveBeenCalledWith( - new Error('cannot modify permission granted from multichain flow'), + new Error('Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.'), ); }); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts index 8b57cc8d1c66..a94f010fa62a 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts @@ -101,7 +101,7 @@ function revokePermissionsImplementation( if (caip25CaveatValue?.isMultichainOrigin) { return end( - new Error('cannot modify permission granted from multichain flow'), + new Error('Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.'), ); // TODO: better error } relevantPermissionKeys.push(Caip25EndowmentPermissionName); From 522aeb7c88420195c8267593a581e762d7837af5 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 14:07:16 -0700 Subject: [PATCH 023/148] use controller-utils preview build too --- package.json | 2 +- yarn.lock | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d0c04ea00608..721db7e5f69b 100644 --- a/package.json +++ b/package.json @@ -311,7 +311,7 @@ "@metamask/bitcoin-wallet-snap": "^0.6.1", "@metamask/browser-passworder": "^4.3.0", "@metamask/contract-metadata": "^2.5.0", - "@metamask/controller-utils": "^11.2.0", + "@metamask/controller-utils": "npm:@metamask-previews/controller-utils@11.3.0-preview-ada451da", "@metamask/design-tokens": "^4.0.0", "@metamask/ens-controller": "^13.0.0", "@metamask/ens-resolver-snap": "^0.1.2", diff --git a/yarn.lock b/yarn.lock index 554e883b5a59..0857a83e1e5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5039,6 +5039,23 @@ __metadata: languageName: node linkType: hard +"@metamask/controller-utils@npm:@metamask-previews/controller-utils@11.3.0-preview-ada451da": + version: 11.3.0-preview-ada451da + resolution: "@metamask-previews/controller-utils@npm:11.3.0-preview-ada451da" + dependencies: + "@ethereumjs/util": "npm:^8.1.0" + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/ethjs-unit": "npm:^0.3.0" + "@metamask/utils": "npm:^9.1.0" + "@spruceid/siwe-parser": "npm:2.1.0" + "@types/bn.js": "npm:^5.1.5" + bn.js: "npm:^5.2.1" + eth-ens-namehash: "npm:^2.0.8" + fast-deep-equal: "npm:^3.1.3" + checksum: 10/48c4ff542a080bf64dd45a78b6717eac73512f95d86e127a197166e652af79700db7cc81a23ebba602f6fe921498bf382ea138723f97ce287b94597a7507c3d7 + languageName: node + linkType: hard + "@metamask/controller-utils@npm:^11.0.0, @metamask/controller-utils@npm:^11.0.2, @metamask/controller-utils@npm:^11.1.0, @metamask/controller-utils@npm:^11.2.0, @metamask/controller-utils@npm:^11.3.0": version: 11.3.0 resolution: "@metamask/controller-utils@npm:11.3.0" @@ -26196,7 +26213,7 @@ __metadata: "@metamask/browser-passworder": "npm:^4.3.0" "@metamask/build-utils": "npm:^3.0.0" "@metamask/contract-metadata": "npm:^2.5.0" - "@metamask/controller-utils": "npm:^11.2.0" + "@metamask/controller-utils": "npm:@metamask-previews/controller-utils@11.3.0-preview-ada451da" "@metamask/design-tokens": "npm:^4.0.0" "@metamask/ens-controller": "npm:^13.0.0" "@metamask/ens-resolver-snap": "npm:^0.1.2" From 0670559d42ea1fc28772ee9363660605dcc2cceb Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 14:19:27 -0700 Subject: [PATCH 024/148] use controller-utils preview build resolution --- package.json | 3 ++- yarn.lock | 34 +--------------------------------- 2 files changed, 3 insertions(+), 34 deletions(-) diff --git a/package.json b/package.json index 721db7e5f69b..5e9b44036212 100644 --- a/package.json +++ b/package.json @@ -131,6 +131,7 @@ "attributions:generate": "./development/generate-attributions.sh" }, "resolutions": { + "@metamask/controller-utils": "npm:@metamask-previews/controller-utils@11.3.0-preview-ada451da", "chokidar": "^3.6.0", "simple-update-notifier@^1.0.0": "^2.0.0", "@babel/core": "patch:@babel/core@npm%3A7.23.2#~/.yarn/patches/@babel-core-npm-7.23.2-b93f586907.patch", @@ -311,7 +312,7 @@ "@metamask/bitcoin-wallet-snap": "^0.6.1", "@metamask/browser-passworder": "^4.3.0", "@metamask/contract-metadata": "^2.5.0", - "@metamask/controller-utils": "npm:@metamask-previews/controller-utils@11.3.0-preview-ada451da", + "@metamask/controller-utils": "^11.2.0", "@metamask/design-tokens": "^4.0.0", "@metamask/ens-controller": "^13.0.0", "@metamask/ens-resolver-snap": "^0.1.2", diff --git a/yarn.lock b/yarn.lock index 0857a83e1e5a..18647bed7752 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5056,38 +5056,6 @@ __metadata: languageName: node linkType: hard -"@metamask/controller-utils@npm:^11.0.0, @metamask/controller-utils@npm:^11.0.2, @metamask/controller-utils@npm:^11.1.0, @metamask/controller-utils@npm:^11.2.0, @metamask/controller-utils@npm:^11.3.0": - version: 11.3.0 - resolution: "@metamask/controller-utils@npm:11.3.0" - dependencies: - "@ethereumjs/util": "npm:^8.1.0" - "@metamask/eth-query": "npm:^4.0.0" - "@metamask/ethjs-unit": "npm:^0.3.0" - "@metamask/utils": "npm:^9.1.0" - "@spruceid/siwe-parser": "npm:2.1.0" - "@types/bn.js": "npm:^5.1.5" - bn.js: "npm:^5.2.1" - eth-ens-namehash: "npm:^2.0.8" - fast-deep-equal: "npm:^3.1.3" - checksum: 10/3200228d1f4ea5fa095228db4e5050529caf0470e072382eb8f7571bb9b07515516ca9e846b7751388399d9ae967e4985dafd6120902ef6c998e98f4eb36d964 - languageName: node - linkType: hard - -"@metamask/controller-utils@npm:^8.0.1": - version: 8.0.4 - resolution: "@metamask/controller-utils@npm:8.0.4" - dependencies: - "@ethereumjs/util": "npm:^8.1.0" - "@metamask/eth-query": "npm:^4.0.0" - "@metamask/ethjs-unit": "npm:^0.3.0" - "@metamask/utils": "npm:^8.3.0" - "@spruceid/siwe-parser": "npm:1.1.3" - eth-ens-namehash: "npm:^2.0.8" - fast-deep-equal: "npm:^3.1.3" - checksum: 10/112a07614eec28cff270c99aa0695bec34cd29461d0c4cb83eb913a5bc37b3b72e4f33dad59a0ab23da5d1b091372ee5207657349bfdb814098c5a51d6570554 - languageName: node - linkType: hard - "@metamask/design-tokens@npm:^1.12.0": version: 1.13.0 resolution: "@metamask/design-tokens@npm:1.13.0" @@ -26213,7 +26181,7 @@ __metadata: "@metamask/browser-passworder": "npm:^4.3.0" "@metamask/build-utils": "npm:^3.0.0" "@metamask/contract-metadata": "npm:^2.5.0" - "@metamask/controller-utils": "npm:@metamask-previews/controller-utils@11.3.0-preview-ada451da" + "@metamask/controller-utils": "npm:^11.2.0" "@metamask/design-tokens": "npm:^4.0.0" "@metamask/ens-controller": "npm:^13.0.0" "@metamask/ens-resolver-snap": "npm:^0.1.2" From 1f24a413ea7648d5615e1de9e23f144dd2d43d00 Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Tue, 15 Oct 2024 21:40:12 +0000 Subject: [PATCH 025/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 35 +-------------------------- lavamoat/browserify/flask/policy.json | 35 +-------------------------- lavamoat/browserify/main/policy.json | 35 +-------------------------- lavamoat/browserify/mmi/policy.json | 35 +-------------------------- 4 files changed, 4 insertions(+), 136 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index a6060bf6655a..9121d65a272b 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1983,9 +1983,9 @@ "crypto": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query>json-rpc-random-id": true, "@metamask/ppom-validator>@metamask/base-controller": true, - "@metamask/ppom-validator>@metamask/controller-utils": true, "@metamask/ppom-validator>crypto-js": true, "@metamask/ppom-validator>elliptic": true, "@metamask/rpc-errors": true, @@ -2001,39 +2001,6 @@ "immer": true } }, - "@metamask/ppom-validator>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/ppom-validator>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/ppom-validator>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/ppom-validator>crypto-js": { "globals": { "crypto": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index a6060bf6655a..9121d65a272b 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1983,9 +1983,9 @@ "crypto": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query>json-rpc-random-id": true, "@metamask/ppom-validator>@metamask/base-controller": true, - "@metamask/ppom-validator>@metamask/controller-utils": true, "@metamask/ppom-validator>crypto-js": true, "@metamask/ppom-validator>elliptic": true, "@metamask/rpc-errors": true, @@ -2001,39 +2001,6 @@ "immer": true } }, - "@metamask/ppom-validator>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/ppom-validator>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/ppom-validator>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/ppom-validator>crypto-js": { "globals": { "crypto": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index a6060bf6655a..9121d65a272b 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1983,9 +1983,9 @@ "crypto": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query>json-rpc-random-id": true, "@metamask/ppom-validator>@metamask/base-controller": true, - "@metamask/ppom-validator>@metamask/controller-utils": true, "@metamask/ppom-validator>crypto-js": true, "@metamask/ppom-validator>elliptic": true, "@metamask/rpc-errors": true, @@ -2001,39 +2001,6 @@ "immer": true } }, - "@metamask/ppom-validator>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/ppom-validator>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/ppom-validator>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/ppom-validator>crypto-js": { "globals": { "crypto": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 333e756d4240..bcb3940df68b 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -2075,9 +2075,9 @@ "crypto": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query>json-rpc-random-id": true, "@metamask/ppom-validator>@metamask/base-controller": true, - "@metamask/ppom-validator>@metamask/controller-utils": true, "@metamask/ppom-validator>crypto-js": true, "@metamask/ppom-validator>elliptic": true, "@metamask/rpc-errors": true, @@ -2093,39 +2093,6 @@ "immer": true } }, - "@metamask/ppom-validator>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/ppom-validator>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, - "@metamask/ppom-validator>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/ppom-validator>crypto-js": { "globals": { "crypto": true, From 5d1d8c6082085b2456c443fdf6cbac8463d31840 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 14:40:22 -0700 Subject: [PATCH 026/148] Fix specs. Lint --- .../permissions/background-api.test.js | 24 ++++++++++++++----- .../wallet-requestPermissions.test.ts | 4 +++- .../handlers/wallet-revokePermissions.test.ts | 4 +++- .../handlers/wallet-revokePermissions.ts | 4 +++- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/app/scripts/controllers/permissions/background-api.test.js b/app/scripts/controllers/permissions/background-api.test.js index 9226398cf159..7de9b4c01ae1 100644 --- a/app/scripts/controllers/permissions/background-api.test.js +++ b/app/scripts/controllers/permissions/background-api.test.js @@ -46,7 +46,9 @@ describe('permission background API methods', () => { permissionController, }).addPermittedAccount('foo.com', '0x1'), ).toThrow( - new Error('tried to add accounts when none have been permissioned'), + new Error( + `Cannot add account permissions for origin "foo.com": no permission currently exists for this origin.`, + ), ); }); @@ -183,7 +185,9 @@ describe('permission background API methods', () => { permissionController, }).addPermittedAccounts('foo.com', ['0x1']), ).toThrow( - new Error('tried to add accounts when none have been permissioned'), + new Error( + `Cannot add account permissions for origin "foo.com": no permission currently exists for this origin.`, + ), ); }); @@ -324,7 +328,9 @@ describe('permission background API methods', () => { permissionController, }).removePermittedAccount('foo.com', '0x1'), ).toThrow( - new Error('tried to remove accounts when none have been permissioned'), + new Error( + `Cannot remove account "0x1": No permissions exist for origin "foo.com".`, + ), ); }); @@ -620,7 +626,9 @@ describe('permission background API methods', () => { permissionController, }).addPermittedChain('foo.com', '0x1'), ).toThrow( - new Error('tried to add chains when none have been permissioned'), + new Error( + `Cannot add chain permissions for origin "foo.com": no permission currently exists for this origin.`, + ), ); }); @@ -742,7 +750,9 @@ describe('permission background API methods', () => { permissionController, }).addPermittedChains('foo.com', ['0x1']), ).toThrow( - new Error('tried to add chains when none have been permissioned'), + new Error( + `Cannot add chain permissions for origin "foo.com": no permission currently exists for this origin.`, + ), ); }); @@ -869,7 +879,9 @@ describe('permission background API methods', () => { permissionController, }).removePermittedChain('foo.com', '0x1'), ).toThrow( - new Error('tried to remove chains when none have been permissioned'), + new Error( + `Cannot remove permission for chainId "0x1": No permissions exist for origin "foo.com".`, + ), ); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts index 983973bdf6e3..0804edcb082c 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts @@ -629,7 +629,9 @@ describe('requestPermissionsHandler', () => { await handler(getBaseRequest()); expect(end).toHaveBeenCalledWith( - new Error('Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.'), + new Error( + 'Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.', + ), ); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts index e09a9f0a1c52..b322e2a144ed 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts @@ -188,7 +188,9 @@ describe('revokePermissionsHandler', () => { ], }); expect(end).toHaveBeenCalledWith( - new Error('Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.'), + new Error( + 'Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.', + ), ); }); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts index a94f010fa62a..5dbe6789fb1f 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts @@ -101,7 +101,9 @@ function revokePermissionsImplementation( if (caip25CaveatValue?.isMultichainOrigin) { return end( - new Error('Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.'), + new Error( + 'Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.', + ), ); // TODO: better error } relevantPermissionKeys.push(Caip25EndowmentPermissionName); From c242aa5830d86be357390b2c8f0b577eeb8a4998 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 15:04:48 -0700 Subject: [PATCH 027/148] lint --- .../rpc-method-middleware/handlers/wallet-getPermissions.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts index 09476121b33c..6882a1c0fa7f 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts @@ -60,8 +60,7 @@ async function getPermissionsImplementation( getAccounts: () => Promise; }, ) { - // permissions are frozen and must be cloned before modified - const permissions = { ...getPermissionsForOrigin() } || {}; + const permissions = { ...getPermissionsForOrigin() }; const caip25Endowment = permissions[Caip25EndowmentPermissionName]; const caip25CaveatValue = caip25Endowment?.caveats?.find( ({ type }) => type === Caip25CaveatType, From bc2ef3e60583561a76b7d8a434929e23d7d0b4d3 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 15:10:29 -0700 Subject: [PATCH 028/148] Bring in ethereum-chain-utils.test.js. Whoops lol --- .../handlers/ethereum-chain-utils.test.js | 363 ++++++++++++++++++ 1 file changed, 363 insertions(+) create mode 100644 app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.js diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.js new file mode 100644 index 000000000000..bf552264f0df --- /dev/null +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.js @@ -0,0 +1,363 @@ +import { errorCodes } from 'eth-rpc-errors'; +import { + KnownNotifications, + KnownRpcMethods, + Caip25CaveatType, + Caip25EndowmentPermissionName, +} from '@metamask/multichain'; +import { CaveatTypes } from '../../../../../shared/constants/permissions'; +import { PermissionNames } from '../../../controllers/permissions'; +import * as EthChainUtils from './ethereum-chain-utils'; + +describe('Ethereum Chain Utils', () => { + const createMockedSwitchChain = () => { + const end = jest.fn(); + const mocks = { + isAddFlow: false, + setActiveNetwork: jest.fn(), + endApprovalFlow: jest.fn(), + getCaveat: jest.fn(), + requestPermissionApprovalForOrigin: jest.fn(), + updateCaveat: jest.fn(), + grantPermissions: jest.fn(), + }; + const response = {}; + const switchChain = (origin, chainId, networkClientId, approvalFlowId) => + EthChainUtils.switchChain( + response, + end, + origin, + chainId, + networkClientId, + approvalFlowId, + mocks, + ); + + return { + mocks, + response, + end, + switchChain, + }; + }; + + describe('switchChain', () => { + it('gets the CAIP-25 caveat', async () => { + const { mocks, switchChain } = createMockedSwitchChain(); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.getCaveat).toHaveBeenCalledWith({ + target: Caip25EndowmentPermissionName, + caveatType: Caip25CaveatType, + }); + }); + + it('passes through unexpected errors if approvalFlowId is not provided', async () => { + const { mocks, end, switchChain } = createMockedSwitchChain(); + mocks.requestPermissionApprovalForOrigin.mockRejectedValueOnce( + new Error('unexpected error'), + ); + + await switchChain('example.com', '0x1', 'mainnet', null); + + expect(end).toHaveBeenCalledWith(new Error('unexpected error')); + }); + + it('passes through unexpected errors if approvalFlowId is provided', async () => { + const { mocks, end, switchChain } = createMockedSwitchChain(); + mocks.requestPermissionApprovalForOrigin.mockRejectedValueOnce( + new Error('unexpected error'), + ); + + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(end).toHaveBeenCalledWith(new Error('unexpected error')); + }); + + it('ignores userRejectedRequest errors when approvalFlowId is provided', async () => { + const { mocks, end, response, switchChain } = createMockedSwitchChain(); + mocks.requestPermissionApprovalForOrigin.mockRejectedValueOnce({ + code: errorCodes.provider.userRejectedRequest, + }); + + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(response.result).toStrictEqual(null); + expect(end).toHaveBeenCalledWith(); + }); + + it('ends the approval flow when approvalFlowId is provided', async () => { + const { mocks, switchChain } = createMockedSwitchChain(); + + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.endApprovalFlow).toHaveBeenCalledWith({ + id: 'approvalFlowId', + }); + }); + + describe('with no existing CAIP-25 permission', () => { + it('requests a switch chain approval', async () => { + const { mocks, switchChain } = createMockedSwitchChain(); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1'], + }, + ], + }, + }); + }); + + it('grants a new CAIP-25 permission with the chain', async () => { + const { mocks, switchChain } = createMockedSwitchChain(); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.grantPermissions).toHaveBeenCalledWith({ + subject: { origin: 'example.com' }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: KnownRpcMethods.eip155, + notifications: KnownNotifications.eip155, + accounts: [], + }, + 'wallet:eip155': { + methods: [], + notifications: [], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + }, + }, + }); + }); + + it('switches to the chain', async () => { + const { mocks, switchChain } = createMockedSwitchChain(); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); + }); + + it('should handle errors if the switch chain approval is rejected', async () => { + const { mocks, end, switchChain } = createMockedSwitchChain(); + mocks.requestPermissionApprovalForOrigin.mockRejectedValueOnce({ + code: errorCodes.provider.userRejectedRequest, + }); + + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalled(); + expect(mocks.grantPermissions).not.toHaveBeenCalled(); + expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); + expect(end).toHaveBeenCalledWith(); + }); + }); + + describe('with an existing CAIP-25 permission granted from the legacy flow (isMultichainOrigin: false) and the chainId is not already permissioned', () => { + it('skips permittedChains approval and switches to it if isAddFlow: true', async () => { + const { mocks, switchChain } = createMockedSwitchChain(); + mocks.isAddFlow = true; + mocks.getCaveat.mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + }); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.requestPermissionApprovalForOrigin).not.toHaveBeenCalled(); + expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); + }); + + it('requests permittedChains approval then switches to it if isAddFlow: false', async () => { + const { mocks, switchChain } = createMockedSwitchChain(); + mocks.isAddFlow = false; + mocks.getCaveat.mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + }); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalled(); + expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1'], + }, + ], + }, + }); + expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); + }); + + it('updates the CAIP-25 caveat with the chain added', async () => { + const { mocks, switchChain } = createMockedSwitchChain(); + mocks.getCaveat.mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + }); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.updateCaveat).toHaveBeenCalledWith( + 'example.com', + Caip25EndowmentPermissionName, + Caip25CaveatType, + { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: KnownRpcMethods.eip155, + notifications: KnownNotifications.eip155, + accounts: [], + }, + 'wallet:eip155': { + methods: [], + notifications: [], + }, + }, + isMultichainOrigin: false, + }, + ); + }); + + it('should handle errors if the permittedChains approval is rejected', async () => { + const { mocks, end, switchChain } = createMockedSwitchChain(); + mocks.requestPermissionApprovalForOrigin.mockRejectedValueOnce({ + code: errorCodes.provider.userRejectedRequest, + }); + mocks.getCaveat.mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + }); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalled(); + expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); + expect(end).toHaveBeenCalledWith(); + }); + }); + + describe('with an existing CAIP-25 permission granted from the multichain flow (isMultichainOrigin: true) and the chainId is not already permissioned', () => { + it('does not request permittedChains approval', async () => { + const { mocks, switchChain } = createMockedSwitchChain(); + mocks.getCaveat.mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: true, + }, + }); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.requestPermissionApprovalForOrigin).not.toHaveBeenCalled(); + }); + + it('does not switch the active network', async () => { + const { mocks, switchChain } = createMockedSwitchChain(); + mocks.getCaveat.mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: true, + }, + }); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); + }); + + it('return error about not being able to switch chain', async () => { + const { mocks, end, switchChain } = createMockedSwitchChain(); + mocks.getCaveat.mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: true, + }, + }); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(end).toHaveBeenCalledWith( + new Error( + "Cannot switch to or add permissions for chainId '0x1' because permissions were granted over the Multichain API.", + ), + ); + }); + }); + + describe.each([ + ['legacy', false], + ['multichain', true], + ])( + 'with an existing CAIP-25 permission granted from the %s flow (isMultichainOrigin: %s) and the chainId is already permissioned', + (_, isMultichainOrigin) => { + it('does not request permittedChains approval', async () => { + const { mocks, switchChain } = createMockedSwitchChain(); + mocks.getCaveat.mockReturnValue({ + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + }, + optionalScopes: {}, + isMultichainOrigin, + }, + }); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect( + mocks.requestPermissionApprovalForOrigin, + ).not.toHaveBeenCalled(); + }); + + it('switches the active network', async () => { + const { mocks, switchChain } = createMockedSwitchChain(); + mocks.getCaveat.mockReturnValue({ + value: { + requiredScopes: { + 'eip155:1': { + methods: [], + notifications: [], + }, + }, + optionalScopes: {}, + isMultichainOrigin, + }, + }); + await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + + expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); + }); + }, + ); + }); +}); From b7b90b5eb7eb474ca041033a9f5453922b92b6a1 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 15:15:34 -0700 Subject: [PATCH 029/148] Bring in request-accounts.test.js. Whoops lol --- .../handlers/request-accounts.test.js | 286 ++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.js diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.js b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.js new file mode 100644 index 000000000000..42c215691af7 --- /dev/null +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.js @@ -0,0 +1,286 @@ +import { ethErrors } from 'eth-rpc-errors'; +import { deferredPromise, shouldEmitDappViewedEvent } from '../../util'; +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, +} from '@metamask/multichain'; +import { RestrictedMethods } from '../../../../../shared/constants/permissions'; +import { PermissionNames } from '../../../controllers/permissions'; +import * as Multichain from '@metamask/multichain'; +import { flushPromises } from '../../../../../test/lib/timer-helpers'; +import requestEthereumAccounts from './request-accounts'; + +jest.mock( + '@metamask/multichain', + () => ({ + ...jest.requireActual( + '@metamask/multichain', + ), + setPermittedEthChainIds: jest.fn(), + setEthAccounts: jest.fn(), + }), +); +const MockMultichain = jest.mocked(Multichain); + +jest.mock('../../util', () => ({ + ...jest.requireActual('../../util'), + shouldEmitDappViewedEvent: jest.fn(), +})); + +const baseRequest = { + networkClientId: 'mainnet', + origin: 'http://test.com', +}; + +const createMockedHandler = () => { + const next = jest.fn(); + const end = jest.fn(); + const getAccounts = jest.fn().mockResolvedValue([]); + const getUnlockPromise = jest.fn(); + const requestPermissionApprovalForOrigin = jest.fn().mockResolvedValue({ + approvedChainIds: ['0x1', '0x5'], + approvedAccounts: ['0xdeadbeef'], + }); + const sendMetrics = jest.fn(); + const metamaskState = { + permissionHistory: {}, + metaMetricsId: 'metaMetricsId', + accounts: { + '0x1': {}, + '0x2': {}, + '0x3': {}, + }, + }; + const grantPermissions = jest.fn(); + const response = {}; + const handler = (request) => + requestEthereumAccounts.implementation(request, response, next, end, { + getAccounts, + getUnlockPromise, + requestPermissionApprovalForOrigin, + sendMetrics, + metamaskState, + grantPermissions, + }); + + return { + response, + next, + end, + getAccounts, + getUnlockPromise, + requestPermissionApprovalForOrigin, + sendMetrics, + grantPermissions, + handler, + }; +}; + +describe('requestEthereumAccountsHandler', () => { + beforeEach(() => { + shouldEmitDappViewedEvent.mockReturnValue(true); + MockMultichain.setEthAccounts.mockImplementation( + (caveatValue) => caveatValue, + ); + MockMultichain.setPermittedEthChainIds.mockImplementation( + (caveatValue) => caveatValue, + ); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('checks if there are any eip155 accounts permissioned', async () => { + const { handler, getAccounts } = createMockedHandler(); + + await handler(baseRequest); + expect(getAccounts).toHaveBeenCalled(); + }); + + describe('eip155 account permissions exist', () => { + it('waits for the wallet to unlock', async () => { + const { handler, getUnlockPromise, getAccounts } = createMockedHandler(); + getAccounts.mockResolvedValue(['0xdead', '0xbeef']); + + await handler(baseRequest); + expect(getUnlockPromise).toHaveBeenCalledWith(true); + }); + + it('returns the accounts', async () => { + const { handler, response, getAccounts } = createMockedHandler(); + getAccounts.mockResolvedValue(['0xdead', '0xbeef']); + + await handler(baseRequest); + expect(response.result).toStrictEqual(['0xdead', '0xbeef']); + }); + + it('blocks subsequent requests if there is currently a request waiting for the wallet to be unlocked', async () => { + const { handler, getUnlockPromise, getAccounts, end, response } = + createMockedHandler(); + const { promise, resolve } = deferredPromise(); + getUnlockPromise.mockReturnValue(promise); + getAccounts.mockResolvedValue(['0xdead', '0xbeef']); + + handler(baseRequest); + expect(response).toStrictEqual({}); + expect(end).not.toHaveBeenCalled(); + + await flushPromises(); + + await handler(baseRequest); + expect(response.error).toStrictEqual( + ethErrors.rpc.resourceUnavailable( + `Already processing eth_requestAccounts. Please wait.`, + ), + ); + expect(end).toHaveBeenCalledTimes(1); + resolve(); + }); + }); + + describe('eip155 account permissions do not exist', () => { + it('requests eth_accounts and permittedChains approval if origin is not snapId', async () => { + const { handler, requestPermissionApprovalForOrigin } = + createMockedHandler(); + + await handler(baseRequest); + expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ + [RestrictedMethods.eth_accounts]: {}, + [PermissionNames.permittedChains]: {}, + }); + }); + + it('requests eth_accounts approval if origin is snapId', async () => { + const { handler, requestPermissionApprovalForOrigin } = + createMockedHandler(); + + await handler({ ...baseRequest, origin: 'npm:snap' }); + expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ + [RestrictedMethods.eth_accounts]: {}, + }); + }); + + it('throws an error if the eth_accounts and permittedChains approval is rejected', async () => { + const { handler, requestPermissionApprovalForOrigin, response, end } = + createMockedHandler(); + requestPermissionApprovalForOrigin.mockRejectedValue( + new Error('approval rejected'), + ); + + await handler(baseRequest); + expect(response.error).toStrictEqual(new Error('approval rejected')); + expect(end).toHaveBeenCalled(); + }); + + it('sets the approved chainIds on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is not snapId', async () => { + const { handler } = createMockedHandler(); + + await handler(baseRequest); + expect( + MockMultichain.setPermittedEthChainIds, + ).toHaveBeenCalledWith( + { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + ['0x1', '0x5'], + ); + }); + + it('sets the approved accounts on the CAIP-25 caveat after the approved chainIds if origin is not snapId', async () => { + const { handler } = createMockedHandler(); + + MockMultichain.setPermittedEthChainIds.mockReturnValue( + 'caveatValueWithEthChainIdsSet', + ); + + await handler(baseRequest); + expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( + 'caveatValueWithEthChainIdsSet', + ['0xdeadbeef'], + ); + }); + + it('does not set the approved chainIds on an empty CAIP-25 caveat if origin is snapId', async () => { + const { handler } = createMockedHandler(); + + await handler({ baseRequest, origin: 'npm:snap' }); + expect( + MockMultichain.setPermittedEthChainIds, + ).not.toHaveBeenCalled(); + }); + + it('sets the approved accounts on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is snapId', async () => { + const { handler } = createMockedHandler(); + + await handler({ baseRequest, origin: 'npm:snap' }); + expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( + { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + ['0xdeadbeef'], + ); + }); + + it('grants a CAIP-25 permission', async () => { + const { handler, grantPermissions } = createMockedHandler(); + + MockMultichain.setEthAccounts.mockReturnValue( + 'updatedCaveatValue', + ); + + await handler(baseRequest); + expect(grantPermissions).toHaveBeenCalledWith({ + subject: { + origin: 'http://test.com', + }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: 'updatedCaveatValue', + }, + ], + }, + }, + }); + }); + + it('returns the newly granted and properly ordered eth accounts', async () => { + const { handler, getAccounts, response } = createMockedHandler(); + getAccounts + .mockResolvedValueOnce([]) + .mockResolvedValueOnce(['0xdead', '0xbeef']); + + await handler(baseRequest); + expect(response.result).toStrictEqual(['0xdead', '0xbeef']); + expect(getAccounts).toHaveBeenCalledTimes(2); + }); + + it('emits the dapp viewed metrics event', async () => { + const { handler, getAccounts, sendMetrics } = createMockedHandler(); + getAccounts + .mockResolvedValueOnce([]) + .mockResolvedValueOnce(['0xdead', '0xbeef']); + + await handler(baseRequest); + expect(sendMetrics).toHaveBeenCalledWith({ + category: 'inpage_provider', + event: 'Dapp Viewed', + properties: { + is_first_visit: true, + number_of_accounts: 3, + number_of_accounts_connected: 2, + }, + referrer: { + url: 'http://test.com', + }, + }); + }); + }); +}); From 427ac9878194a32071f8558d690a84605376f914 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 15:16:39 -0700 Subject: [PATCH 030/148] Bring in eth-accounts.test.js . Whoops lol --- .../handlers/eth-accounts.test.js | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.js diff --git a/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.js b/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.js new file mode 100644 index 000000000000..bb8ba36142b0 --- /dev/null +++ b/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.js @@ -0,0 +1,40 @@ +import ethereumAccounts from './eth-accounts'; + +const baseRequest = { + origin: 'http://test.com', +}; + +const createMockedHandler = () => { + const next = jest.fn(); + const end = jest.fn(); + const getAccounts = jest.fn().mockResolvedValue(['0xdead', '0xbeef']); + const response = {}; + const handler = (request) => + ethereumAccounts.implementation(request, response, next, end, { + getAccounts, + }); + + return { + response, + next, + end, + getAccounts, + handler, + }; +}; + +describe('ethAccountsHandler', () => { + it('gets sorted eth accounts from the CAIP-25 permission via the getAccounts hook', async () => { + const { handler, getAccounts } = createMockedHandler(); + + await handler(baseRequest); + expect(getAccounts).toHaveBeenCalled(); + }); + + it('returns the accounts', async () => { + const { handler, response } = createMockedHandler(); + + await handler(baseRequest); + expect(response.result).toStrictEqual(['0xdead', '0xbeef']); + }); +}); From 6cd118d6e0732e8ba4ccf318671198e11abd776a Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 15:58:56 -0700 Subject: [PATCH 031/148] add request-accounts.test.ts. convert requestAccounts js to ts --- ...ounts.test.js => request-accounts.test.ts} | 95 ++++++++++++------- ...equest-accounts.js => request-accounts.ts} | 79 +++++++++------ 2 files changed, 112 insertions(+), 62 deletions(-) rename app/scripts/lib/rpc-method-middleware/handlers/{request-accounts.test.js => request-accounts.test.ts} (79%) rename app/scripts/lib/rpc-method-middleware/handlers/{request-accounts.js => request-accounts.ts} (74%) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.js b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts similarity index 79% rename from app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.js rename to app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts index 42c215691af7..8b76414e4c2b 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts @@ -1,35 +1,41 @@ import { ethErrors } from 'eth-rpc-errors'; -import { deferredPromise, shouldEmitDappViewedEvent } from '../../util'; import { Caip25CaveatType, Caip25EndowmentPermissionName, } from '@metamask/multichain'; +import * as Multichain from '@metamask/multichain'; +import { + JsonRpcParams, + JsonRpcRequest, + PendingJsonRpcResponse, +} from '@metamask/utils'; +import { deferredPromise } from '../../util'; +import * as Util from '../../util'; import { RestrictedMethods } from '../../../../../shared/constants/permissions'; import { PermissionNames } from '../../../controllers/permissions'; -import * as Multichain from '@metamask/multichain'; import { flushPromises } from '../../../../../test/lib/timer-helpers'; import requestEthereumAccounts from './request-accounts'; -jest.mock( - '@metamask/multichain', - () => ({ - ...jest.requireActual( - '@metamask/multichain', - ), - setPermittedEthChainIds: jest.fn(), - setEthAccounts: jest.fn(), - }), -); +jest.mock('@metamask/multichain', () => ({ + ...jest.requireActual('@metamask/multichain'), + setPermittedEthChainIds: jest.fn(), + setEthAccounts: jest.fn(), +})); const MockMultichain = jest.mocked(Multichain); jest.mock('../../util', () => ({ ...jest.requireActual('../../util'), shouldEmitDappViewedEvent: jest.fn(), })); +const MockUtil = jest.mocked(Util); const baseRequest = { + jsonrpc: '2.0' as const, + id: 0, + method: 'eth_requestAccounts', networkClientId: 'mainnet', origin: 'http://test.com', + params: [], }; const createMockedHandler = () => { @@ -52,8 +58,14 @@ const createMockedHandler = () => { }, }; const grantPermissions = jest.fn(); - const response = {}; - const handler = (request) => + const response: PendingJsonRpcResponse = { + jsonrpc: '2.0' as const, + id: 0, + result: undefined, + }; + const handler = ( + request: JsonRpcRequest & { origin: string }, + ) => requestEthereumAccounts.implementation(request, response, next, end, { getAccounts, getUnlockPromise, @@ -78,7 +90,7 @@ const createMockedHandler = () => { describe('requestEthereumAccountsHandler', () => { beforeEach(() => { - shouldEmitDappViewedEvent.mockReturnValue(true); + MockUtil.shouldEmitDappViewedEvent.mockReturnValue(true); MockMultichain.setEthAccounts.mockImplementation( (caveatValue) => caveatValue, ); @@ -123,7 +135,11 @@ describe('requestEthereumAccountsHandler', () => { getAccounts.mockResolvedValue(['0xdead', '0xbeef']); handler(baseRequest); - expect(response).toStrictEqual({}); + expect(response).toStrictEqual({ + id: 0, + jsonrpc: '2.0', + result: undefined, + }); expect(end).not.toHaveBeenCalled(); await flushPromises(); @@ -135,7 +151,7 @@ describe('requestEthereumAccountsHandler', () => { ), ); expect(end).toHaveBeenCalledTimes(1); - resolve(); + resolve?.(); }); }); @@ -162,24 +178,21 @@ describe('requestEthereumAccountsHandler', () => { }); it('throws an error if the eth_accounts and permittedChains approval is rejected', async () => { - const { handler, requestPermissionApprovalForOrigin, response, end } = + const { handler, requestPermissionApprovalForOrigin, end } = createMockedHandler(); requestPermissionApprovalForOrigin.mockRejectedValue( new Error('approval rejected'), ); await handler(baseRequest); - expect(response.error).toStrictEqual(new Error('approval rejected')); - expect(end).toHaveBeenCalled(); + expect(end).toHaveBeenCalledWith(new Error('approval rejected')); }); it('sets the approved chainIds on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is not snapId', async () => { const { handler } = createMockedHandler(); await handler(baseRequest); - expect( - MockMultichain.setPermittedEthChainIds, - ).toHaveBeenCalledWith( + expect(MockMultichain.setPermittedEthChainIds).toHaveBeenCalledWith( { requiredScopes: {}, optionalScopes: {}, @@ -192,13 +205,21 @@ describe('requestEthereumAccountsHandler', () => { it('sets the approved accounts on the CAIP-25 caveat after the approved chainIds if origin is not snapId', async () => { const { handler } = createMockedHandler(); - MockMultichain.setPermittedEthChainIds.mockReturnValue( - 'caveatValueWithEthChainIdsSet', - ); + MockMultichain.setPermittedEthChainIds.mockReturnValue({ + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { caveatValueWithEthChainIdsSet: true }, + isMultichainOrigin: false, + }); await handler(baseRequest); expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( - 'caveatValueWithEthChainIdsSet', + { + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { caveatValueWithEthChainIdsSet: true }, + isMultichainOrigin: false, + }, ['0xdeadbeef'], ); }); @@ -207,9 +228,7 @@ describe('requestEthereumAccountsHandler', () => { const { handler } = createMockedHandler(); await handler({ baseRequest, origin: 'npm:snap' }); - expect( - MockMultichain.setPermittedEthChainIds, - ).not.toHaveBeenCalled(); + expect(MockMultichain.setPermittedEthChainIds).not.toHaveBeenCalled(); }); it('sets the approved accounts on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is snapId', async () => { @@ -229,9 +248,12 @@ describe('requestEthereumAccountsHandler', () => { it('grants a CAIP-25 permission', async () => { const { handler, grantPermissions } = createMockedHandler(); - MockMultichain.setEthAccounts.mockReturnValue( - 'updatedCaveatValue', - ); + MockMultichain.setEthAccounts.mockReturnValue({ + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { caveatValueWithEthAccountsSet: true }, + isMultichainOrigin: false, + }); await handler(baseRequest); expect(grantPermissions).toHaveBeenCalledWith({ @@ -243,7 +265,12 @@ describe('requestEthereumAccountsHandler', () => { caveats: [ { type: Caip25CaveatType, - value: 'updatedCaveatValue', + value: { + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { caveatValueWithEthAccountsSet: true }, + isMultichainOrigin: false, + }, }, ], }, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.js b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts similarity index 74% rename from app/scripts/lib/rpc-method-middleware/handlers/request-accounts.js rename to app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts index e8f8a70c0633..7cb8873264cf 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts @@ -5,10 +5,31 @@ import { setEthAccounts, setPermittedEthChainIds, } from '@metamask/multichain'; +import { + Caveat, + CaveatSpecificationConstraint, + PermissionController, + PermissionSpecificationConstraint, + RequestedPermissions, + ValidPermission, +} from '@metamask/permission-controller'; +import { + Hex, + Json, + JsonRpcParams, + JsonRpcRequest, + PendingJsonRpcResponse, +} from '@metamask/utils'; +import { + JsonRpcEngineEndCallback, + JsonRpcEngineNextCallback, +} from 'json-rpc-engine'; import { MESSAGE_TYPE } from '../../../../../shared/constants/app'; import { MetaMetricsEventName, MetaMetricsEventCategory, + MetaMetricsEventPayload, + MetaMetricsEventOptions, } from '../../../../../shared/constants/metametrics'; import { shouldEmitDappViewedEvent } from '../../util'; import { RestrictedMethods } from '../../../../../shared/constants/permissions'; @@ -38,34 +59,19 @@ const requestEthereumAccounts = { }; export default requestEthereumAccounts; +type AbstractPermissionController = PermissionController< + PermissionSpecificationConstraint, + CaveatSpecificationConstraint +>; + // Used to rate-limit pending requests to one per origin const locks = new Set(); -/** - * @typedef {Record} RequestEthereumAccountsOptions - * @property {Function} getAccounts - Gets the accounts for the requesting - * origin. - * @property {Function} getUnlockPromise - Gets a promise that resolves when - * the extension unlocks. - * @property {Function} hasPermission - Returns whether the requesting origin - * has the specified permission. - * @property {Function} requestAccountsPermission - Requests the `eth_accounts` - * permission for the requesting origin. - */ - -/** - * - * @param {import('json-rpc-engine').JsonRpcRequest} req - The JSON-RPC request object. - * @param {import('json-rpc-engine').JsonRpcResponse} res - The JSON-RPC response object. - * @param {Function} _next - The json-rpc-engine 'next' callback. - * @param {Function} end - The json-rpc-engine 'end' callback. - * @param {RequestEthereumAccountsOptions} options - The RPC method hooks. - */ async function requestEthereumAccountsHandler( - req, - res, - _next, - end, + req: JsonRpcRequest & { origin: string }, + res: PendingJsonRpcResponse, + _next: JsonRpcEngineNextCallback, + end: JsonRpcEngineEndCallback, { getAccounts, getUnlockPromise, @@ -73,6 +79,24 @@ async function requestEthereumAccountsHandler( sendMetrics, metamaskState, grantPermissions, + }: { + getAccounts: () => Promise; + getUnlockPromise: (shouldShowUnlockRequest: true) => Promise; + requestPermissionApprovalForOrigin: ( + requestedPermissions: RequestedPermissions, + ) => Promise<{ approvedAccounts: Hex[]; approvedChainIds: Hex[] }>; + sendMetrics: ( + payload: MetaMetricsEventPayload, + options?: MetaMetricsEventOptions, + ) => void; + metamaskState: { + metaMetricsId: string; + permissionHistory: Record; + accounts: Record; + }; + grantPermissions: ( + ...args: Parameters + ) => Record>>; }, ) { const { origin } = req; @@ -94,7 +118,7 @@ async function requestEthereumAccountsHandler( res.result = ethAccounts; end(); } catch (error) { - end(error); + end(error as unknown as Error); } finally { locks.delete(origin); } @@ -109,9 +133,8 @@ async function requestEthereumAccountsHandler( [PermissionNames.permittedChains]: {}, }), }); - } catch (err) { - res.error = err; - return end(); + } catch (error) { + return end(error as unknown as Error); } // NOTE: the eth_accounts/permittedChains approvals will be combined in the future. From 655210149f0bb50d2a7e2e5983454af118f560e3 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 15:59:14 -0700 Subject: [PATCH 032/148] convert eth-accounts to ts --- ...{eth-accounts.test.js => eth-accounts.test.ts} | 15 +++++++++++++-- .../handlers/eth-accounts.ts | 7 +++---- 2 files changed, 16 insertions(+), 6 deletions(-) rename app/scripts/lib/rpc-method-middleware/handlers/{eth-accounts.test.js => eth-accounts.test.ts} (74%) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.js b/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.ts similarity index 74% rename from app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.js rename to app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.ts index bb8ba36142b0..0e0f98b17412 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.ts @@ -1,6 +1,14 @@ +import { + JsonRpcParams, + JsonRpcRequest, + PendingJsonRpcResponse, +} from '@metamask/utils'; import ethereumAccounts from './eth-accounts'; const baseRequest = { + jsonrpc: '2.0' as const, + id: 0, + method: 'eth_accounts', origin: 'http://test.com', }; @@ -8,8 +16,11 @@ const createMockedHandler = () => { const next = jest.fn(); const end = jest.fn(); const getAccounts = jest.fn().mockResolvedValue(['0xdead', '0xbeef']); - const response = {}; - const handler = (request) => + const response: PendingJsonRpcResponse = { + jsonrpc: '2.0' as const, + id: 0, + }; + const handler = (request: JsonRpcRequest) => ethereumAccounts.implementation(request, response, next, end, { getAccounts, }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.ts b/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.ts index 003cbd88281b..220cac0c7fa8 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.ts @@ -8,17 +8,16 @@ import type { PendingJsonRpcResponse, } from '@metamask/utils'; import { MESSAGE_TYPE } from '../../../../../shared/constants/app'; -import { AccountAddress } from '../../../controllers/account-order'; import { HandlerWrapper } from './types'; type EthAccountsHandlerOptions = { - getAccounts: () => Promise; + getAccounts: () => Promise; }; type EthAccountsConstraint = { implementation: ( _req: JsonRpcRequest, - res: PendingJsonRpcResponse, + res: PendingJsonRpcResponse, _next: JsonRpcEngineNextCallback, end: JsonRpcEngineEndCallback, { getAccounts }: EthAccountsHandlerOptions, @@ -49,7 +48,7 @@ export default ethAccounts; */ async function ethAccountsHandler( _req: JsonRpcRequest, - res: PendingJsonRpcResponse, + res: PendingJsonRpcResponse, _next: JsonRpcEngineNextCallback, end: JsonRpcEngineEndCallback, { getAccounts }: EthAccountsHandlerOptions, From 21293c35480ebc0a9e92feace82923f61f50a872 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 15 Oct 2024 15:59:32 -0700 Subject: [PATCH 033/148] Make requestPermissions test mock more accurate --- .../handlers/wallet-requestPermissions.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts index 0804edcb082c..b9dc0edd92b3 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts @@ -640,7 +640,7 @@ describe('requestPermissionsHandler', () => { MockMultichain.setEthAccounts.mockReturnValue({ requiredScopes: {}, optionalScopes: {}, - sessionProperties: { caveatValueWithEthChainIdsSet: true }, + sessionProperties: { caveatValueWithEthAccountsSet: true }, isMultichainOrigin: false, }); @@ -652,7 +652,7 @@ describe('requestPermissionsHandler', () => { { requiredScopes: {}, optionalScopes: {}, - sessionProperties: { caveatValueWithEthChainIdsSet: true }, + sessionProperties: { caveatValueWithEthAccountsSet: true }, isMultichainOrigin: false, }, ); @@ -665,7 +665,7 @@ describe('requestPermissionsHandler', () => { MockMultichain.setEthAccounts.mockReturnValue({ requiredScopes: {}, optionalScopes: {}, - sessionProperties: { caveatValueWithEthChainIdsSet: true }, + sessionProperties: { caveatValueWithEthAccountsSet: true }, isMultichainOrigin: false, }); @@ -682,7 +682,7 @@ describe('requestPermissionsHandler', () => { value: { requiredScopes: {}, optionalScopes: {}, - sessionProperties: { caveatValueWithEthChainIdsSet: true }, + sessionProperties: { caveatValueWithEthAccountsSet: true }, isMultichainOrigin: false, }, }, From 669fa713bcc56f29f868210929430c7fac8cdb54 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 08:30:35 -0700 Subject: [PATCH 034/148] convert ethereum-chain-utils.test.js to ts --- ...n-utils.test.js => ethereum-chain-utils.test.ts} | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) rename app/scripts/lib/rpc-method-middleware/handlers/{ethereum-chain-utils.test.js => ethereum-chain-utils.test.ts} (97%) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts similarity index 97% rename from app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.js rename to app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts index bf552264f0df..08aca8e387a9 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts @@ -5,6 +5,7 @@ import { Caip25CaveatType, Caip25EndowmentPermissionName, } from '@metamask/multichain'; +import { Hex } from '@metamask/utils'; import { CaveatTypes } from '../../../../../shared/constants/permissions'; import { PermissionNames } from '../../../controllers/permissions'; import * as EthChainUtils from './ethereum-chain-utils'; @@ -21,8 +22,13 @@ describe('Ethereum Chain Utils', () => { updateCaveat: jest.fn(), grantPermissions: jest.fn(), }; - const response = {}; - const switchChain = (origin, chainId, networkClientId, approvalFlowId) => + const response: { result?: true } = {}; + const switchChain = ( + origin: string, + chainId: Hex, + networkClientId: string, + approvalFlowId: string | null, + ) => EthChainUtils.switchChain( response, end, @@ -312,12 +318,13 @@ describe('Ethereum Chain Utils', () => { }); }); + // @ts-expect-error This function is missing from the Mocha type definitions describe.each([ ['legacy', false], ['multichain', true], ])( 'with an existing CAIP-25 permission granted from the %s flow (isMultichainOrigin: %s) and the chainId is already permissioned', - (_, isMultichainOrigin) => { + (_type: string, isMultichainOrigin: boolean) => { it('does not request permittedChains approval', async () => { const { mocks, switchChain } = createMockedSwitchChain(); mocks.getCaveat.mockReturnValue({ From bbffb07afaf4b6dc5311e6dba2a18bb0e94e992e Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 09:13:29 -0700 Subject: [PATCH 035/148] fix duplicated imports --- .../multichain/edit-accounts-modal/edit-accounts-modal.tsx | 1 - .../pages/review-permissions-page/site-cell/site-cell.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index cd288fc1aeff..cd78a2eeda83 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -32,7 +32,6 @@ import { getURLHost } from '../../../helpers/utils/util'; import { MergedInternalAccount } from '../../../selectors/selectors.types'; import { isEqualCaseInsensitive, - isEqualCaseInsensitive, } from '../../../../shared/modules/string-utils'; import { MetaMetricsEventCategory, diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index c1313d2ad8e2..fac3e3afd35a 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -16,7 +16,6 @@ import { EditAccountsModal, EditNetworksModal } from '../../..'; import { MergedInternalAccount } from '../../../../../selectors/selectors.types'; import { isEqualCaseInsensitive, - isEqualCaseInsensitive, } from '../../../../../../shared/modules/string-utils'; import { MetaMetricsContext } from '../../../../../contexts/metametrics'; import { From 2b80d2365acf0a561a6f6ad6d8f1e4aacb0e450e Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 09:27:54 -0700 Subject: [PATCH 036/148] revert isAccountSyncingEnabled --- app/scripts/metamask-controller.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 9d7f799e6688..3584f6eea934 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1520,7 +1520,7 @@ export default class MetamaskController extends EventEmitter { }, }, env: { - isAccountSyncingEnabled: false, // TODO: undo this once fixed + isAccountSyncingEnabled: isManifestV3, }, messenger: this.controllerMessenger.getRestricted({ name: 'UserStorageController', From db866f0c6ed6654e150cb37b3c809017ada6f7db Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 09:33:35 -0700 Subject: [PATCH 037/148] DRY requestAccountsPermissionWithId into requestAccountsAndChainPermissionsWithId. Remove NOTE comments --- .../controllers/permissions/background-api.js | 108 ++++++++---------- .../handlers/request-accounts.ts | 4 - .../handlers/wallet-requestPermissions.ts | 4 - .../edit-accounts-modal.tsx | 4 +- .../pages/connections/connections.tsx | 4 +- .../site-cell/site-cell.tsx | 4 +- .../connected-sites.container.js | 12 +- ui/store/actions.ts | 13 --- 8 files changed, 61 insertions(+), 92 deletions(-) diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index 6a839e1c31f6..0b64ca8118e3 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -100,61 +100,6 @@ export function getPermissionBackgroundApiMethods({ ); }; - const requestAccountsAndChainPermissionsWithId = (origin) => { - const id = nanoid(); - // NOTE: the eth_accounts/permittedChains approvals will be combined in the future. - // Until they are actually combined, when testing, you must request both - // eth_accounts and permittedChains together. - approvalController - .addAndShowApprovalRequest({ - id, - origin, - requestData: { - metadata: { - id, - origin, - }, - permissions: { - [RestrictedMethods.eth_accounts]: {}, - [PermissionNames.permittedChains]: {}, - }, - }, - type: MethodNames.requestPermissions, - }) - .then((legacyApproval) => { - let caveatValue = { - requiredScopes: {}, - optionalScopes: {}, - isMultichainOrigin: false, - }; - caveatValue = setPermittedEthChainIds( - caveatValue, - legacyApproval.approvedChainIds, - ); - - caveatValue = setEthAccounts( - caveatValue, - legacyApproval.approvedAccounts, - ); - - permissionController.grantPermissions({ - subject: { origin }, - approvedPermissions: { - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: caveatValue, - }, - ], - }, - }, - }); - }); - - return id; - }; - return { addPermittedAccount: (origin, account) => addMoreAccounts(origin, [account]), @@ -260,9 +205,56 @@ export function getPermissionBackgroundApiMethods({ } }, - requestAccountsAndChainPermissionsWithId, + requestAccountsAndChainPermissionsWithId: (origin) => { + const id = nanoid(); + approvalController + .addAndShowApprovalRequest({ + id, + origin, + requestData: { + metadata: { + id, + origin, + }, + permissions: { + [RestrictedMethods.eth_accounts]: {}, + [PermissionNames.permittedChains]: {}, + }, + }, + type: MethodNames.requestPermissions, + }) + .then((legacyApproval) => { + let caveatValue = { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }; + caveatValue = setPermittedEthChainIds( + caveatValue, + legacyApproval.approvedChainIds, + ); + + caveatValue = setEthAccounts( + caveatValue, + legacyApproval.approvedAccounts, + ); + + permissionController.grantPermissions({ + subject: { origin }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: caveatValue, + }, + ], + }, + }, + }); + }); - // TODO: Remove this / DRY with requestAccountsAndChainPermissionsWithId - requestAccountsPermissionWithId: requestAccountsAndChainPermissionsWithId, + return id; + }, }; } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts index 7cb8873264cf..bc51de5d946f 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts @@ -137,10 +137,6 @@ async function requestEthereumAccountsHandler( return end(error as unknown as Error); } - // NOTE: the eth_accounts/permittedChains approvals will be combined in the future. - // We assume that approvedAccounts and permittedChains are both defined here. - // Until they are actually combined, when testing, you must request both - // eth_accounts and permittedChains together. let caveatValue = { requiredScopes: {}, optionalScopes: {}, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index fbe478b27d72..13980ebd9976 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -171,10 +171,6 @@ async function requestPermissionsImplementation( } if (legacyApproval) { - // NOTE: the eth_accounts/permittedChains approvals will be combined in the future. - // We assume that approvedAccounts and permittedChains are both defined here. - // Until they are actually combined, when testing, you must request both - // eth_accounts and permittedChains together. let newCaveatValue = { requiredScopes: {}, optionalScopes: {}, diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index cd78a2eeda83..47a9b43071f9 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -30,9 +30,7 @@ import { } from '../../../helpers/constants/design-system'; import { getURLHost } from '../../../helpers/utils/util'; import { MergedInternalAccount } from '../../../selectors/selectors.types'; -import { - isEqualCaseInsensitive, -} from '../../../../shared/modules/string-utils'; +import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils'; import { MetaMetricsEventCategory, MetaMetricsEventName, diff --git a/ui/components/multichain/pages/connections/connections.tsx b/ui/components/multichain/pages/connections/connections.tsx index 3dbb77f3a2e3..8bf383d4f5fe 100644 --- a/ui/components/multichain/pages/connections/connections.tsx +++ b/ui/components/multichain/pages/connections/connections.tsx @@ -53,7 +53,7 @@ import { import { Content, Footer, Header, Page } from '../page'; import { ConnectAccountsModal } from '../../connect-accounts-modal/connect-accounts-modal'; import { - requestAccountsPermissionWithId, + requestAccountsAndChainPermissionsWithId, removePermissionsFor, } from '../../../../store/actions'; import { @@ -133,7 +133,7 @@ export const Connections = () => { } const requestAccountsPermission = async () => { const requestId = await dispatch( - requestAccountsPermissionWithId(tabToConnect.origin), + requestAccountsAndChainPermissionsWithId(tabToConnect.origin), ); history.push(`${CONNECT_ROUTE}/${requestId}`); }; diff --git a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx index fac3e3afd35a..43d6efdd598e 100644 --- a/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx +++ b/ui/components/multichain/pages/review-permissions-page/site-cell/site-cell.tsx @@ -14,9 +14,7 @@ import { } from '../../../../component-library'; import { EditAccountsModal, EditNetworksModal } from '../../..'; import { MergedInternalAccount } from '../../../../../selectors/selectors.types'; -import { - isEqualCaseInsensitive, -} from '../../../../../../shared/modules/string-utils'; +import { isEqualCaseInsensitive } from '../../../../../../shared/modules/string-utils'; import { MetaMetricsContext } from '../../../../../contexts/metametrics'; import { MetaMetricsEventCategory, diff --git a/ui/pages/connected-sites/connected-sites.container.js b/ui/pages/connected-sites/connected-sites.container.js index bdbeb423dc89..8c080335f70a 100644 --- a/ui/pages/connected-sites/connected-sites.container.js +++ b/ui/pages/connected-sites/connected-sites.container.js @@ -1,7 +1,7 @@ import { connect } from 'react-redux'; import { getOpenMetamaskTabsIds, - requestAccountsPermissionWithId, + requestAccountsAndChainPermissionsWithId, removePermissionsFor, removePermittedAccount, } from '../../store/actions'; @@ -61,8 +61,8 @@ const mapDispatchToProps = (dispatch) => { }), ); }, - requestAccountsPermissionWithId: (origin) => - dispatch(requestAccountsPermissionWithId(origin)), + requestAccountsAndChainPermissionsWithId: (origin) => + dispatch(requestAccountsAndChainPermissionsWithId(origin)), }; }; @@ -78,7 +78,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { disconnectAccount, disconnectAllAccounts, // eslint-disable-next-line no-shadow - requestAccountsPermissionWithId, + requestAccountsAndChainPermissionsWithId, } = dispatchProps; const { history } = ownProps; @@ -102,7 +102,9 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { } }, requestAccountsPermission: async () => { - const id = await requestAccountsPermissionWithId(tabToConnect.origin); + const id = await requestAccountsAndChainPermissionsWithId( + tabToConnect.origin, + ); history.push(`${CONNECT_ROUTE}/${id}`); }, }; diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 9c5ab7ebb45e..75b0d91cc6d0 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -3878,19 +3878,6 @@ export function setInitialGasEstimate( // Permissions -export function requestAccountsPermissionWithId( - origin: string, -): ThunkAction { - return async (dispatch: MetaMaskReduxDispatch) => { - const id = await submitRequestToBackground( - 'requestAccountsPermissionWithId', - [origin], - ); - await forceUpdateMetamaskState(dispatch); - return id; - }; -} - export function requestAccountsAndChainPermissionsWithId( origin: string, ): ThunkAction, MetaMaskReduxState, unknown, AnyAction> { From aa5067fb1b3262673dbab92a8161c4cdc2ccef63 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 10:16:58 -0700 Subject: [PATCH 038/148] Remove TODO about verifying switchChain permissioning flow --- .../lib/rpc-method-middleware/handlers/ethereum-chain-utils.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index bfeaa2bae259..e42753038d20 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -193,8 +193,6 @@ export async function switchChain( ); } - // TODO: This behavior may have deviated from the original permittedChains add chain behavior - // Verify that this helper behaves as expected if (!isAddFlow) { await requestPermissionApprovalForOrigin({ [PermissionNames.permittedChains]: { From 902bd5afeb91296993a5c927cec507eaf3a8750d Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 10:31:10 -0700 Subject: [PATCH 039/148] Remove selectors intended for multichain API --- .../controllers/permissions/selectors.js | 79 ----------- html-report-multichain/index.html | 132 ++++++++++++++++++ 2 files changed, 132 insertions(+), 79 deletions(-) create mode 100644 html-report-multichain/index.html diff --git a/app/scripts/controllers/permissions/selectors.js b/app/scripts/controllers/permissions/selectors.js index df244681ad8c..50134a7bb607 100644 --- a/app/scripts/controllers/permissions/selectors.js +++ b/app/scripts/controllers/permissions/selectors.js @@ -44,33 +44,6 @@ export const getPermittedAccountsByOrigin = createSelector( }, ); -/** - * Get the authorized CAIP-25 scopes for each subject, keyed by origin. - * The values of the returned map are immutable values from the - * PermissionController state. - * - * @returns {Map} The current origin:authorization map. - */ -export const getAuthorizedScopesByOrigin = createSelector( - getSubjects, - (subjects) => { - return Object.values(subjects).reduce( - (originToAuthorizationsMap, subject) => { - const caveats = - subject.permissions?.[Caip25EndowmentPermissionName]?.caveats || []; - - const caveat = caveats.find(({ type }) => type === Caip25CaveatType); - - if (caveat) { - originToAuthorizationsMap.set(subject.origin, caveat.value); - } - return originToAuthorizationsMap; - }, - new Map(), - ); - }, -); - /** * Get the permitted chains for each subject, keyed by origin. * The values of the returned map are immutable values from the @@ -140,58 +113,6 @@ export const diffMap = (currentMap, previousMap) => { return changedMap; }; -/** - * Given the current and previous exposed CAIP-25 authorization for each PermissionController - * subject, returns a new map containing all authorizations that have changed. - * The values of each map must be immutable values directly from the - * PermissionController state, or an empty object instantiated in this - * function. - * - * @param {Map} newAuthorizationsMap - The new origin:authorization map. - * @param {Map} [previousAuthorizationsMap] - The previous origin:authorization map. - * @returns {Map} The origin:authorization map of changed authorizations. - */ -export const getChangedAuthorizations = ( - newAuthorizationsMap, - previousAuthorizationsMap, -) => { - if (previousAuthorizationsMap === undefined) { - return newAuthorizationsMap; - } - - const changedAuthorizations = new Map(); - if (newAuthorizationsMap === previousAuthorizationsMap) { - return changedAuthorizations; - } - - const newOrigins = new Set([...newAuthorizationsMap.keys()]); - - for (const origin of previousAuthorizationsMap.keys()) { - const newAuthorizations = newAuthorizationsMap.get(origin) ?? { - requiredScopes: {}, - optionalScopes: {}, - }; - - // The values of these maps are references to immutable values, which is why - // a strict equality check is enough for diffing. The values are either from - // PermissionController state, or an empty object initialized in the previous - // call to this function. `newAuthorizationsMap` will never contain any empty - // objects. - if (previousAuthorizationsMap.get(origin) !== newAuthorizations) { - changedAuthorizations.set(origin, newAuthorizations); - } - - newOrigins.delete(origin); - } - - // By now, newOrigins is either empty or contains some number of previously - // unencountered origins, and all of their authorizations have "changed". - for (const origin of newOrigins.keys()) { - changedAuthorizations.set(origin, newAuthorizationsMap.get(origin)); - } - return changedAuthorizations; -}; - /** * * @param {Map} newAuthorizationsMap - The new origin:authorization map. diff --git a/html-report-multichain/index.html b/html-report-multichain/index.html new file mode 100644 index 000000000000..7d2eb184af27 --- /dev/null +++ b/html-report-multichain/index.html @@ -0,0 +1,132 @@ + + + + + + + + OpenRPC API Test HTML Reporter + + + + +
+ + From aa369570f65b7a8c96ff3e4063492aa327f85647 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 10:34:18 -0700 Subject: [PATCH 040/148] lint request-accounts.test.ts --- .../rpc-method-middleware/handlers/request-accounts.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts index 8b76414e4c2b..4ed3feeece7f 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts @@ -227,14 +227,14 @@ describe('requestEthereumAccountsHandler', () => { it('does not set the approved chainIds on an empty CAIP-25 caveat if origin is snapId', async () => { const { handler } = createMockedHandler(); - await handler({ baseRequest, origin: 'npm:snap' }); + await handler({ ...baseRequest, origin: 'npm:snap' }); expect(MockMultichain.setPermittedEthChainIds).not.toHaveBeenCalled(); }); it('sets the approved accounts on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is snapId', async () => { const { handler } = createMockedHandler(); - await handler({ baseRequest, origin: 'npm:snap' }); + await handler({ ...baseRequest, origin: 'npm:snap' }); expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( { requiredScopes: {}, From 865f09cc0b3be79d931fb61425c539acf35a005b Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Oct 2024 14:32:14 -0500 Subject: [PATCH 041/148] fix switch chain permission confirmation --- .../app/permission-cell/permission-cell.js | 6 +++--- .../permission-page-container-content.component.js | 11 +++++++++-- .../permission-page-container.component.js | 3 +++ .../permissions-connect-permission-list.js | 14 +++++++++++--- .../permissions-connect.component.js | 8 ++++++++ 5 files changed, 34 insertions(+), 8 deletions(-) diff --git a/ui/components/app/permission-cell/permission-cell.js b/ui/components/app/permission-cell/permission-cell.js index 4df1dc256765..7d85f48d2a54 100644 --- a/ui/components/app/permission-cell/permission-cell.js +++ b/ui/components/app/permission-cell/permission-cell.js @@ -42,7 +42,7 @@ const PermissionCell = ({ showOptions, hideStatus, accounts, - permissionValue, + chainIds, }) => { const infoIcon = IconName.Info; let infoIconColor = IconColor.iconMuted; @@ -71,7 +71,7 @@ const PermissionCell = ({ } const networksInfo = useSelector((state) => - getRequestingNetworkInfo(state, permissionValue), + getRequestingNetworkInfo(state, chainIds), ); return ( @@ -171,7 +171,7 @@ PermissionCell.propTypes = { showOptions: PropTypes.bool, hideStatus: PropTypes.bool, accounts: PropTypes.array, - permissionValue: PropTypes.array, + chainIds: PropTypes.array, }; export default PermissionCell; diff --git a/ui/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js b/ui/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js index e5e8503e6c73..646b341ff285 100644 --- a/ui/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js +++ b/ui/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js @@ -27,10 +27,12 @@ export default class PermissionPageContainerContent extends PureComponent { }), selectedPermissions: PropTypes.object.isRequired, selectedAccounts: PropTypes.array, + requestedChainIds: PropTypes.array, }; static defaultProps = { selectedAccounts: [], + requestedChainIds: [], }; static contextTypes = { @@ -40,8 +42,12 @@ export default class PermissionPageContainerContent extends PureComponent { render() { const { t } = this.context; - const { selectedPermissions, selectedAccounts, subjectMetadata } = - this.props; + const { + selectedPermissions, + selectedAccounts, + subjectMetadata, + requestedChainIds,s + } = this.props; const accounts = selectedAccounts.reduce((accumulator, account) => { accumulator.push({ @@ -98,6 +104,7 @@ export default class PermissionPageContainerContent extends PureComponent { permissions={selectedPermissions} subjectName={subjectMetadata.origin} accounts={accounts} + requestedChainIds={requestedChainIds} /> diff --git a/ui/components/app/permission-page-container/permission-page-container.component.js b/ui/components/app/permission-page-container/permission-page-container.component.js index aac1d6731464..6e0ff41cd461 100644 --- a/ui/components/app/permission-page-container/permission-page-container.component.js +++ b/ui/components/app/permission-page-container/permission-page-container.component.js @@ -31,6 +31,7 @@ export default class PermissionPageContainer extends Component { approvePermissionsRequest: PropTypes.func.isRequired, rejectPermissionsRequest: PropTypes.func.isRequired, selectedAccounts: PropTypes.array, + requestedChainIds: PropTypes.array, allAccountsSelected: PropTypes.bool, currentPermissions: PropTypes.object, snapsInstallPrivacyWarningShown: PropTypes.bool.isRequired, @@ -183,6 +184,7 @@ export default class PermissionPageContainer extends Component { targetSubjectMetadata, selectedAccounts, allAccountsSelected, + requestedChainIds, } = this.props; const requestedPermissions = this.getRequestedPermissions(); @@ -216,6 +218,7 @@ export default class PermissionPageContainer extends Component { requestMetadata={requestMetadata} subjectMetadata={targetSubjectMetadata} selectedPermissions={requestedPermissions} + requestedChainIds={requestedChainIds} selectedAccounts={selectedAccounts} allAccountsSelected={allAccountsSelected} /> diff --git a/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js b/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js index da15d384849c..39f9bb015b98 100644 --- a/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js +++ b/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js @@ -14,9 +14,10 @@ import { Box } from '../../component-library'; * @param permission - The permission to render. * @param index - The index of the permission. * @param accounts - An array representing list of accounts for which permission is used. + * @param requestedChainIds - An array representing list of chain ids for which permission is used. * @returns {JSX.Element} A permission description node. */ -function getDescriptionNode(permission, index, accounts) { +function getDescriptionNode(permission, index, accounts, requestedChainIds) { return ( ); } @@ -35,6 +36,7 @@ export default function PermissionsConnectPermissionList({ permissions, subjectName, accounts, + requestedChainIds, }) { const t = useI18nContext(); const snapsMetadata = useSelector(getSnapsMetadata); @@ -47,7 +49,12 @@ export default function PermissionsConnectPermissionList({ getSubjectName: getSnapName(snapsMetadata), subjectName, }).map((permission, index) => { - return getDescriptionNode(permission, index, accounts); + return getDescriptionNode( + permission, + index, + accounts, + requestedChainIds, + ); })} ); @@ -56,5 +63,6 @@ export default function PermissionsConnectPermissionList({ PermissionsConnectPermissionList.propTypes = { permissions: PropTypes.object.isRequired, subjectName: PropTypes.string.isRequired, + requestedChainIds: PropTypes.array, accounts: PropTypes.arrayOf(PropTypes.object), }; diff --git a/ui/pages/permissions-connect/permissions-connect.component.js b/ui/pages/permissions-connect/permissions-connect.component.js index 417a82777b36..66824daf03eb 100644 --- a/ui/pages/permissions-connect/permissions-connect.component.js +++ b/ui/pages/permissions-connect/permissions-connect.component.js @@ -48,6 +48,11 @@ function getDefaultSelectedAccounts(currentAddress, permissionsRequest) { return new Set(isEthAddress(currentAddress) ? [currentAddress] : []); } +function getRequestedChainIds(permissionsRequest) { + return permissionsRequest?.permissions?.[PermissionNames.permittedChains] + ?.caveats[0]?.value; +} + export default class PermissionConnect extends Component { static propTypes = { approvePermissionsRequest: PropTypes.func.isRequired, @@ -149,8 +154,10 @@ export default class PermissionConnect extends Component { history.replace(DEFAULT_ROUTE); return; } + // if this is an incremental permission request for permitted chains, skip the account selection if ( + // TODO pretty sure this is not needed anymore. permissionsRequest?.diff?.permissionDiffMap?.[ PermissionNames.permittedChains ] @@ -393,6 +400,7 @@ export default class PermissionConnect extends Component { selectedAccounts={accounts.filter((account) => selectedAccountAddresses.has(account.address), )} + requestedChainIds={getRequestedChainIds(permissionsRequest)} targetSubjectMetadata={targetSubjectMetadata} history={this.props.history} connectPath={connectPath} From a2c1c611ddd812fbca1319b4be3618bb0de8223c Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Oct 2024 14:46:53 -0500 Subject: [PATCH 042/148] update preview builds --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bdeb2bd4615c..ed8eb32f587b 100644 --- a/package.json +++ b/package.json @@ -337,7 +337,7 @@ "@metamask/message-manager": "^10.1.0", "@metamask/message-signing-snap": "^0.3.3", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-ada451da", + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-00a41cb8", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", From 86a6a41c4f2f629b2adfedb9a1ba8ff65a17759e Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 16 Oct 2024 15:01:41 -0500 Subject: [PATCH 043/148] fix yarn.lock --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7f5c0115b2bd..c28a50f94a7e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5759,9 +5759,9 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-ada451da": - version: 0.0.0-preview-ada451da - resolution: "@metamask-previews/multichain@npm:0.0.0-preview-ada451da" +"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-00a41cb8": + version: 0.0.0-preview-00a41cb8 + resolution: "@metamask-previews/multichain@npm:0.0.0-preview-00a41cb8" dependencies: "@metamask/api-specs": "npm:^0.10.12" "@metamask/controller-utils": "npm:^11.3.0" @@ -5775,7 +5775,7 @@ __metadata: peerDependencies: "@metamask/network-controller": ^21.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/43b93228a9c5f4241d05be0984941ea6a511906849785707b3b500b72e0e3c8d99444c0f54ffa3c560324f9cb60fca8b09249e3ea7e35da1a42407ef2d5b14e3 + checksum: 10/4fd73392e38201be03f3ff8dee10dc362b5474a8f9580ad6cea00c0d3249eceeeb21ec37c67e071124af329cf3d4baa42107115b43cacad758df7210057a86b9 languageName: node linkType: hard @@ -26213,7 +26213,7 @@ __metadata: "@metamask/message-manager": "npm:^10.1.0" "@metamask/message-signing-snap": "npm:^0.3.3" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-ada451da" + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-00a41cb8" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch" "@metamask/notification-controller": "npm:^6.0.0" From e7a351c5588f05077aa9f23957eed9bddca7e2ae Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 13:49:56 -0700 Subject: [PATCH 044/148] Add revoke permitted-chains e2e test --- .../revoke-permissions.spec.js | 45 ++++++++++++++++++- ...ission-page-container-content.component.js | 2 +- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/test/e2e/tests/dapp-interactions/revoke-permissions.spec.js b/test/e2e/tests/dapp-interactions/revoke-permissions.spec.js index 696095e3fd79..ddf4d9c51f43 100644 --- a/test/e2e/tests/dapp-interactions/revoke-permissions.spec.js +++ b/test/e2e/tests/dapp-interactions/revoke-permissions.spec.js @@ -7,7 +7,7 @@ const { const FixtureBuilder = require('../../fixture-builder'); describe('Wallet Revoke Permissions', function () { - it('should revoke eth_accounts permissions via test dapp', async function () { + it('should revoke "eth_accounts" permissions via test dapp', async function () { await withFixtures( { dapp: true, @@ -43,4 +43,47 @@ describe('Wallet Revoke Permissions', function () { }, ); }); + + it('should revoke "endowment:permitted-chains" permissions', async function () { + await withFixtures( + { + dapp: true, + fixtures: new FixtureBuilder() + .withPermissionControllerConnectedToTestDapp() + .build(), + ganacheOptions: defaultGanacheOptions, + title: this.test.fullTitle(), + }, + async ({ driver }) => { + await unlockWallet(driver); + await openDapp(driver); + + // Get initial accounts permissions + await driver.clickElement('#getPermissions'); + + const revokeChainsRequest = JSON.stringify({ + jsonrpc: '2.0', + method: 'wallet_revokePermissions', + params: [ + { + 'endowment:permitted-chains': {}, + }, + ], + }); + + await driver.executeScript( + `return window.ethereum.request(${revokeChainsRequest})`, + ); + + // Get new allowed permissions + await driver.clickElement('#getPermissions'); + + // Eth_accounts permissions removed + await driver.waitForSelector({ + css: '#permissionsResult', + text: 'No permissions found.', + }); + }, + ); + }); }); diff --git a/ui/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js b/ui/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js index 646b341ff285..26a69bd864e3 100644 --- a/ui/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js +++ b/ui/components/app/permission-page-container/permission-page-container-content/permission-page-container-content.component.js @@ -46,7 +46,7 @@ export default class PermissionPageContainerContent extends PureComponent { selectedPermissions, selectedAccounts, subjectMetadata, - requestedChainIds,s + requestedChainIds, } = this.props; const accounts = selectedAccounts.reduce((accumulator, account) => { From c96672d5fec1ced847ce95434f3fae5760b4b4c6 Mon Sep 17 00:00:00 2001 From: jiexi Date: Wed, 16 Oct 2024 13:50:29 -0700 Subject: [PATCH 045/148] Jl/caip25 permission migration/bump permissions controller fix UI spec (#27914) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27914?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../controllers/permissions/background-api.js | 2 +- .../permissions/background-api.test.js | 2 +- .../permissions/caveat-mutators.js | 12 +-- .../permissions/caveat-mutators.test.js | 14 +-- .../handlers/wallet-getPermissions.ts | 2 +- .../handlers/wallet-requestPermissions.ts | 2 +- .../handlers/wallet-revokePermissions.ts | 2 +- app/scripts/metamask-controller.js | 4 +- lavamoat/browserify/beta/policy.json | 97 ++----------------- lavamoat/browserify/flask/policy.json | 97 ++----------------- lavamoat/browserify/main/policy.json | 97 ++----------------- lavamoat/browserify/mmi/policy.json | 97 ++----------------- lavamoat/build-system/policy.json | 10 +- package.json | 2 +- yarn.lock | 21 +--- 15 files changed, 59 insertions(+), 402 deletions(-) diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index 0b64ca8118e3..486642b41ade 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -221,7 +221,7 @@ export function getPermissionBackgroundApiMethods({ [PermissionNames.permittedChains]: {}, }, }, - type: MethodNames.requestPermissions, + type: MethodNames.RequestPermissions, }) .then((legacyApproval) => { let caveatValue = { diff --git a/app/scripts/controllers/permissions/background-api.test.js b/app/scripts/controllers/permissions/background-api.test.js index 7de9b4c01ae1..9e8c67a7a074 100644 --- a/app/scripts/controllers/permissions/background-api.test.js +++ b/app/scripts/controllers/permissions/background-api.test.js @@ -534,7 +534,7 @@ describe('permission background API methods', () => { [PermissionNames.permittedChains]: {}, }, }, - type: MethodNames.requestPermissions, + type: MethodNames.RequestPermissions, }, ); }); diff --git a/app/scripts/controllers/permissions/caveat-mutators.js b/app/scripts/controllers/permissions/caveat-mutators.js index 551d1f7b37b2..047341e34770 100644 --- a/app/scripts/controllers/permissions/caveat-mutators.js +++ b/app/scripts/controllers/permissions/caveat-mutators.js @@ -33,14 +33,14 @@ function removeAccount(targetAccount, existingAccounts) { ); if (newAccounts.length === existingAccounts.length) { - return { operation: CaveatMutatorOperation.noop }; + return { operation: CaveatMutatorOperation.Noop }; } else if (newAccounts.length > 0) { return { - operation: CaveatMutatorOperation.updateValue, + operation: CaveatMutatorOperation.UpdateValue, value: newAccounts, }; } - return { operation: CaveatMutatorOperation.revokePermission }; + return { operation: CaveatMutatorOperation.RevokePermission }; } /** @@ -60,12 +60,12 @@ function removeChainId(targetChainId, existingChainIds) { ); if (newChainIds.length === existingChainIds.length) { - return { operation: CaveatMutatorOperation.noop }; + return { operation: CaveatMutatorOperation.Noop }; } else if (newChainIds.length > 0) { return { - operation: CaveatMutatorOperation.updateValue, + operation: CaveatMutatorOperation.UpdateValue, value: newChainIds, }; } - return { operation: CaveatMutatorOperation.revokePermission }; + return { operation: CaveatMutatorOperation.RevokePermission }; } diff --git a/app/scripts/controllers/permissions/caveat-mutators.test.js b/app/scripts/controllers/permissions/caveat-mutators.test.js index a87115dc744b..8c16924514f4 100644 --- a/app/scripts/controllers/permissions/caveat-mutators.test.js +++ b/app/scripts/controllers/permissions/caveat-mutators.test.js @@ -14,20 +14,20 @@ describe('caveat mutators', () => { describe('removeAccount', () => { it('returns the no-op operation if the target account is not permitted', () => { expect(removeAccount(address2, [address1])).toStrictEqual({ - operation: CaveatMutatorOperation.noop, + operation: CaveatMutatorOperation.Noop, }); }); it('returns the update operation and a new value if the target account is permitted', () => { expect(removeAccount(address2, [address1, address2])).toStrictEqual({ - operation: CaveatMutatorOperation.updateValue, + operation: CaveatMutatorOperation.UpdateValue, value: [address1], }); }); it('returns the revoke permission operation the target account is the only permitted account', () => { expect(removeAccount(address1, [address1])).toStrictEqual({ - operation: CaveatMutatorOperation.revokePermission, + operation: CaveatMutatorOperation.RevokePermission, }); }); @@ -36,20 +36,20 @@ describe('caveat mutators', () => { const checksummedAddress3 = '0x95222290dd7278AA3DDd389cc1E1d165Cc4BaeE5'; expect(removeAccount(checksummedAddress3, [address3])).toStrictEqual({ - operation: CaveatMutatorOperation.revokePermission, + operation: CaveatMutatorOperation.RevokePermission, }); }); describe('Multichain behaviour', () => { it('returns the no-op operation if the target account is not permitted', () => { expect(removeAccount(address2, [nonEvmAddress])).toStrictEqual({ - operation: CaveatMutatorOperation.noop, + operation: CaveatMutatorOperation.Noop, }); }); it('can revoke permission for non-EVM addresses', () => { expect(removeAccount(nonEvmAddress, [nonEvmAddress])).toStrictEqual({ - operation: CaveatMutatorOperation.revokePermission, + operation: CaveatMutatorOperation.RevokePermission, }); }); @@ -57,7 +57,7 @@ describe('caveat mutators', () => { expect( removeAccount(nonEvmAddress, [address1, nonEvmAddress]), ).toStrictEqual({ - operation: CaveatMutatorOperation.updateValue, + operation: CaveatMutatorOperation.UpdateValue, value: [address1], }); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts index 6882a1c0fa7f..c282a20b0e63 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts @@ -22,7 +22,7 @@ import { } from '../../../../../shared/constants/permissions'; export const getPermissionsHandler = { - methodNames: [MethodNames.getPermissions], + methodNames: [MethodNames.GetPermissions], implementation: getPermissionsImplementation, hookNames: { getPermissionsForOrigin: true, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index 13980ebd9976..e3f30d02ab5c 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -36,7 +36,7 @@ import { PermissionNames } from '../../../controllers/permissions'; import { isSnapId } from '../../../../../ui/helpers/utils/snaps'; export const requestPermissionsHandler = { - methodNames: [MethodNames.requestPermissions], + methodNames: [MethodNames.RequestPermissions], implementation: requestPermissionsImplementation, hookNames: { requestPermissionsForOrigin: true, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts index 5dbe6789fb1f..fa1fe7bf389a 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts @@ -24,7 +24,7 @@ import { RestrictedMethods } from '../../../../../shared/constants/permissions'; import { PermissionNames } from '../../../controllers/permissions'; export const revokePermissionsHandler = { - methodNames: [MethodNames.revokePermissions], + methodNames: [MethodNames.RevokePermissions], implementation: revokePermissionsImplementation, hookNames: { revokePermissionsForOrigin: true, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 49338010f008..b3a83ada6620 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -4939,7 +4939,7 @@ export default class MetamaskController extends EventEmitter { Caip25CaveatType, (existingScopes) => Caip25CaveatMutatorFactories[Caip25CaveatType].removeScope( - toCaipChainId('eip155', parseInt(targetChainId, 16)), + toCaipChainId('eip155', parseInt(targetChainId, 16).toString()), existingScopes, ), ); @@ -5036,7 +5036,7 @@ export default class MetamaskController extends EventEmitter { }, permissions, }, - type: MethodNames.requestPermissions, + type: MethodNames.RequestPermissions, }); } diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 9121d65a272b..03fc23ad9e0a 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1880,10 +1880,10 @@ "packages": { "@metamask/controller-utils": true, "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1896,21 +1896,6 @@ "immer": true } }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2463,12 +2448,12 @@ }, "packages": { "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2517,21 +2502,6 @@ "readable-stream": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/base-controller": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2589,8 +2559,8 @@ }, "@metamask/snaps-rpc-methods": { "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, "@metamask/snaps-utils": true, @@ -2599,34 +2569,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -2662,10 +2604,10 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, @@ -2681,34 +2623,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/snaps-registry": { "packages": { "@metamask/message-signing-snap>@noble/curves": true, @@ -4495,6 +4409,9 @@ } }, "extension-port-stream": { + "globals": { + "console.log": true + }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 9121d65a272b..03fc23ad9e0a 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1880,10 +1880,10 @@ "packages": { "@metamask/controller-utils": true, "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1896,21 +1896,6 @@ "immer": true } }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2463,12 +2448,12 @@ }, "packages": { "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2517,21 +2502,6 @@ "readable-stream": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/base-controller": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2589,8 +2559,8 @@ }, "@metamask/snaps-rpc-methods": { "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, "@metamask/snaps-utils": true, @@ -2599,34 +2569,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -2662,10 +2604,10 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, @@ -2681,34 +2623,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/snaps-registry": { "packages": { "@metamask/message-signing-snap>@noble/curves": true, @@ -4495,6 +4409,9 @@ } }, "extension-port-stream": { + "globals": { + "console.log": true + }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 9121d65a272b..03fc23ad9e0a 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1880,10 +1880,10 @@ "packages": { "@metamask/controller-utils": true, "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1896,21 +1896,6 @@ "immer": true } }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2463,12 +2448,12 @@ }, "packages": { "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2517,21 +2502,6 @@ "readable-stream": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/base-controller": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2589,8 +2559,8 @@ }, "@metamask/snaps-rpc-methods": { "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, "@metamask/snaps-utils": true, @@ -2599,34 +2569,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -2662,10 +2604,10 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, @@ -2681,34 +2623,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/snaps-registry": { "packages": { "@metamask/message-signing-snap>@noble/curves": true, @@ -4495,6 +4409,9 @@ } }, "extension-port-stream": { + "globals": { + "console.log": true + }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index bcb3940df68b..486dab456d29 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -1972,10 +1972,10 @@ "packages": { "@metamask/controller-utils": true, "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1988,21 +1988,6 @@ "immer": true } }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2555,12 +2540,12 @@ }, "packages": { "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/rpc-errors": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2609,21 +2594,6 @@ "readable-stream": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/base-controller": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2681,8 +2651,8 @@ }, "@metamask/snaps-rpc-methods": { "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, "@metamask/snaps-utils": true, @@ -2691,34 +2661,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -2754,10 +2696,10 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, @@ -2773,34 +2715,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/snaps-registry": { "packages": { "@metamask/message-signing-snap>@noble/curves": true, @@ -4587,6 +4501,9 @@ } }, "extension-port-stream": { + "globals": { + "console.log": true + }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index 6e3b319da1e8..3d31e465d90f 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -2131,7 +2131,8 @@ "chokidar>normalize-path": true, "chokidar>readdirp": true, "del>is-glob": true, - "eslint>glob-parent": true + "eslint>glob-parent": true, + "tsx>fsevents": true } }, "chokidar>anymatch": { @@ -8883,6 +8884,13 @@ "typescript": true } }, + "tsx>fsevents": { + "globals": { + "console.assert": true, + "process.platform": true + }, + "native": true + }, "typescript": { "builtin": { "buffer.Buffer": true, diff --git a/package.json b/package.json index 59f4371eeece..48d36ca5ea67 100644 --- a/package.json +++ b/package.json @@ -343,7 +343,7 @@ "@metamask/notification-services-controller": "^0.7.0", "@metamask/object-multiplex": "^2.0.0", "@metamask/obs-store": "^9.0.0", - "@metamask/permission-controller": "^10.0.0", + "@metamask/permission-controller": "^11.0.0", "@metamask/permission-log-controller": "^2.0.1", "@metamask/phishing-controller": "^12.0.1", "@metamask/post-message-stream": "^8.0.0", diff --git a/yarn.lock b/yarn.lock index 56e8baa077e0..12f50c75fd87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5946,25 +5946,6 @@ __metadata: languageName: node linkType: hard -"@metamask/permission-controller@npm:^10.0.0": - version: 10.0.0 - resolution: "@metamask/permission-controller@npm:10.0.0" - dependencies: - "@metamask/base-controller": "npm:^6.0.0" - "@metamask/controller-utils": "npm:^11.0.0" - "@metamask/json-rpc-engine": "npm:^9.0.0" - "@metamask/rpc-errors": "npm:^6.2.1" - "@metamask/utils": "npm:^8.3.0" - "@types/deep-freeze-strict": "npm:^1.1.0" - deep-freeze-strict: "npm:^1.1.1" - immer: "npm:^9.0.6" - nanoid: "npm:^3.1.31" - peerDependencies: - "@metamask/approval-controller": ^7.0.0 - checksum: 10/0c72e205be760fc471b2a6892a9ad52d5c6a40b4cf1757464e992a5ada2dec57efbb24b09351ce8c29990b59f1d731cd2b338caaef37ce7690ea2d1919afe061 - languageName: node - linkType: hard - "@metamask/permission-controller@npm:^11.0.0": version: 11.0.0 resolution: "@metamask/permission-controller@npm:11.0.0" @@ -26189,7 +26170,7 @@ __metadata: "@metamask/notification-services-controller": "npm:^0.7.0" "@metamask/object-multiplex": "npm:^2.0.0" "@metamask/obs-store": "npm:^9.0.0" - "@metamask/permission-controller": "npm:^10.0.0" + "@metamask/permission-controller": "npm:^11.0.0" "@metamask/permission-log-controller": "npm:^2.0.1" "@metamask/phishing-controller": "npm:^12.0.1" "@metamask/phishing-warning": "npm:^4.0.0" From c63d8d20d5808ff3fcd6db3c7767a34f656faace Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Wed, 16 Oct 2024 21:25:49 +0000 Subject: [PATCH 046/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 3 --- lavamoat/browserify/flask/policy.json | 3 --- lavamoat/browserify/main/policy.json | 3 --- lavamoat/browserify/mmi/policy.json | 3 --- lavamoat/build-system/policy.json | 10 +--------- 5 files changed, 1 insertion(+), 21 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 03fc23ad9e0a..d603a2f50aae 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -4409,9 +4409,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 03fc23ad9e0a..d603a2f50aae 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -4409,9 +4409,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 03fc23ad9e0a..d603a2f50aae 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -4409,9 +4409,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 486dab456d29..52386c7e8921 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -4501,9 +4501,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index 3d31e465d90f..6e3b319da1e8 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -2131,8 +2131,7 @@ "chokidar>normalize-path": true, "chokidar>readdirp": true, "del>is-glob": true, - "eslint>glob-parent": true, - "tsx>fsevents": true + "eslint>glob-parent": true } }, "chokidar>anymatch": { @@ -8884,13 +8883,6 @@ "typescript": true } }, - "tsx>fsevents": { - "globals": { - "console.assert": true, - "process.platform": true - }, - "native": true - }, "typescript": { "builtin": { "buffer.Buffer": true, From 5147e8be323b3e9a955ecc4a0066a1d0759a9e09 Mon Sep 17 00:00:00 2001 From: jiexi Date: Wed, 16 Oct 2024 14:30:39 -0700 Subject: [PATCH 047/148] Update app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts --- .../rpc-method-middleware/handlers/wallet-revokePermissions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts index fa1fe7bf389a..b51fbd3d718e 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts @@ -104,7 +104,7 @@ function revokePermissionsImplementation( new Error( 'Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.', ), - ); // TODO: better error + ); } relevantPermissionKeys.push(Caip25EndowmentPermissionName); } From b768c8cbd5e43b9e64f74749bd42bc8d0639cde6 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 14:45:35 -0700 Subject: [PATCH 048/148] fix background dapp viewed event --- app/scripts/background.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/scripts/background.js b/app/scripts/background.js index 9ef2ca921fbc..b2fb0af7975c 100644 --- a/app/scripts/background.js +++ b/app/scripts/background.js @@ -641,8 +641,9 @@ function emitDappViewedMetricEvent(origin) { return; } - const numberOfConnectedAccounts = controller.getPermittedAccounts(origin); - if (numberOfConnectedAccounts.length === 0) { + const numberOfConnectedAccounts = + controller.getPermittedAccounts(origin).length; + if (numberOfConnectedAccounts === 0) { return; } From 6828cd2bfff767339ca68cfd6477d16fdbaaa15e Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 15:07:12 -0700 Subject: [PATCH 049/148] Update fixture-builder to set correct default scopes --- test/e2e/fixture-builder.js | 48 ++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 3c71f2563efa..e72a32565917 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -460,13 +460,13 @@ class FixtureBuilder { value: { requiredScopes: {}, optionalScopes: { - 'eip155:1': { + 'eip155:1337': { methods: [], notifications: [], accounts: [ - `eip155:1:${selectedAccount.toLowerCase()}`, - 'eip155:1:0x09781764c08de8ca82e156bbf156a3ca217c7950', - `eip155:1:${ERC_4337_ACCOUNT.toLowerCase()}`, + `eip155:1337:${selectedAccount.toLowerCase()}`, + 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', + `eip155:1337:${ERC_4337_ACCOUNT.toLowerCase()}`, ], }, }, @@ -504,11 +504,11 @@ class FixtureBuilder { value: { requiredScopes: {}, optionalScopes: { - 'eip155:1': { + 'eip155:1337': { methods: [], notifications: [], accounts: [ - 'eip155:1:0x09781764c08de8ca82e156bbf156a3ca217c7950', + 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', ], }, }, @@ -544,12 +544,12 @@ class FixtureBuilder { value: { requiredScopes: {}, optionalScopes: { - 'eip155:1': { + 'eip155:1337': { methods: [], notifications: [], accounts: [ - 'eip155:1:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', - 'eip155:1:0x09781764c08de8ca82e156bbf156a3ca217c7950', + 'eip155:1337:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', + 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', ], }, }, @@ -574,12 +574,12 @@ class FixtureBuilder { value: { requiredScopes: {}, optionalScopes: { - 'eip155:1': { + 'eip155:1338': { methods: [], notifications: [], accounts: [ - 'eip155:1:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', - 'eip155:1:0x09781764c08de8ca82e156bbf156a3ca217c7950', + 'eip155:1338:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', + 'eip155:1338:0x09781764c08de8ca82e156bbf156a3ca217c7950', ], }, }, @@ -1248,12 +1248,12 @@ class FixtureBuilder { value: { requiredScopes: {}, optionalScopes: { - 'eip155:1': { + 'eip155:1337': { methods: [], notifications: [], accounts: [ - 'eip155:1:0xbee150bdc171c7d4190891e78234f791a3ac7b24', - 'eip155:1:0xb9504634e5788208933b51ae7440b478bfadf865', + 'eip155:1337:0xbee150bdc171c7d4190891e78234f791a3ac7b24', + 'eip155:1337:0xb9504634e5788208933b51ae7440b478bfadf865', ], }, }, @@ -1278,12 +1278,12 @@ class FixtureBuilder { value: { requiredScopes: {}, optionalScopes: { - 'eip155:1': { + 'eip155:1337': { methods: [], notifications: [], accounts: [ - 'eip155:1:0xbee150bdc171c7d4190891e78234f791a3ac7b24', - 'eip155:1:0xd1ca923697a701cba1364d803d72b4740fc39bc9', + 'eip155:1337:0xbee150bdc171c7d4190891e78234f791a3ac7b24', + 'eip155:1337:0xd1ca923697a701cba1364d803d72b4740fc39bc9', ], }, }, @@ -1308,13 +1308,13 @@ class FixtureBuilder { value: { requiredScopes: {}, optionalScopes: { - 'eip155:1': { + 'eip155:1337': { methods: [], notifications: [], accounts: [ - 'eip155:1:0xbee150bdc171c7d4190891e78234f791a3ac7b24', - 'eip155:1:0xa5c5293e124d04e2f85e8553851001fd2f192647', - 'eip155:1:0xb9504634e5788208933b51ae7440b478bfadf865', + 'eip155:1337:0xbee150bdc171c7d4190891e78234f791a3ac7b24', + 'eip155:1337:0xa5c5293e124d04e2f85e8553851001fd2f192647', + 'eip155:1337:0xb9504634e5788208933b51ae7440b478bfadf865', ], }, }, @@ -1339,11 +1339,11 @@ class FixtureBuilder { value: { requiredScopes: {}, optionalScopes: { - 'eip155:1': { + 'eip155:1337': { methods: [], notifications: [], accounts: [ - 'eip155:1:0xbee150bdc171c7d4190891e78234f791a3ac7b24', + 'eip155:1337:0xbee150bdc171c7d4190891e78234f791a3ac7b24', ], }, }, From d83629b743b50fb4278f28aceb57253c190966fe Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 15:17:02 -0700 Subject: [PATCH 050/148] fix background-api spec --- .../controllers/permissions/background-api.test.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/app/scripts/controllers/permissions/background-api.test.js b/app/scripts/controllers/permissions/background-api.test.js index 9e8c67a7a074..b9dc0cda14f4 100644 --- a/app/scripts/controllers/permissions/background-api.test.js +++ b/app/scripts/controllers/permissions/background-api.test.js @@ -60,6 +60,7 @@ describe('permission background API methods', () => { 'eip155:1': { methods: [], notifications: [], + accounts: [], }, 'eip155:10': { methods: [], @@ -199,6 +200,7 @@ describe('permission background API methods', () => { 'eip155:1': { methods: [], notifications: [], + accounts: [], }, 'eip155:10': { methods: [], @@ -342,6 +344,7 @@ describe('permission background API methods', () => { 'eip155:1': { methods: [], notifications: [], + accounts: [], }, 'eip155:10': { methods: [], @@ -386,6 +389,7 @@ describe('permission background API methods', () => { 'eip155:1': { methods: [], notifications: [], + accounts: [], }, 'eip155:10': { methods: [], @@ -426,6 +430,7 @@ describe('permission background API methods', () => { 'eip155:1': { methods: [], notifications: [], + accounts: [], }, 'eip155:10': { methods: [], @@ -640,6 +645,7 @@ describe('permission background API methods', () => { 'eip155:1': { methods: [], notifications: [], + accounts: [], }, 'eip155:10': { methods: [], @@ -764,6 +770,7 @@ describe('permission background API methods', () => { 'eip155:1': { methods: [], notifications: [], + accounts: [], }, 'eip155:10': { methods: [], @@ -893,6 +900,7 @@ describe('permission background API methods', () => { 'eip155:1': { methods: [], notifications: [], + accounts: [], }, 'eip155:10': { methods: [], @@ -967,6 +975,7 @@ describe('permission background API methods', () => { 'eip155:1': { methods: [], notifications: [], + accounts: [], }, 'eip155:10': { methods: [], @@ -1002,6 +1011,7 @@ describe('permission background API methods', () => { 'eip155:1': { methods: [], notifications: [], + accounts: [], }, }, optionalScopes: { From 46d7c0d45b8d2efec18da14db3708c8606f6dc95 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 15:28:37 -0700 Subject: [PATCH 051/148] fix ethereum-chain-utils.test.ts --- .../rpc-method-middleware/handlers/ethereum-chain-utils.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts index 08aca8e387a9..9b08beaec7aa 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts @@ -141,6 +141,7 @@ describe('Ethereum Chain Utils', () => { 'wallet:eip155': { methods: [], notifications: [], + accounts: [], }, }, isMultichainOrigin: false, From 8d59135016b6af3f5abe7a96342a036fc4d1b271 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 16 Oct 2024 15:51:25 -0700 Subject: [PATCH 052/148] fix ethereum-chain-utils.test.ts 2 --- .../handlers/ethereum-chain-utils.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts index 9b08beaec7aa..b635255da144 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts @@ -244,6 +244,7 @@ describe('Ethereum Chain Utils', () => { 'wallet:eip155': { methods: [], notifications: [], + accounts: [], }, }, isMultichainOrigin: false, @@ -334,6 +335,7 @@ describe('Ethereum Chain Utils', () => { 'eip155:1': { methods: [], notifications: [], + accounts: [], }, }, optionalScopes: {}, @@ -355,6 +357,7 @@ describe('Ethereum Chain Utils', () => { 'eip155:1': { methods: [], notifications: [], + accounts: [], }, }, optionalScopes: {}, From 192391057fef2d343dd6ae3fde20d5b84962d322 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 17 Oct 2024 08:55:14 -0700 Subject: [PATCH 053/148] reset lavamoat back to develop --- lavamoat/browserify/beta/policy.json | 244 ++++++++++++++++---------- lavamoat/browserify/flask/policy.json | 244 ++++++++++++++++---------- lavamoat/browserify/main/policy.json | 244 ++++++++++++++++---------- lavamoat/browserify/mmi/policy.json | 244 ++++++++++++++++---------- lavamoat/build-system/policy.json | 10 +- 5 files changed, 589 insertions(+), 397 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index af34ab78617f..880b542673ea 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1460,13 +1460,18 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/multichain>jsonschema": true, + "@metamask/message-manager>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, + "@metamask/message-manager>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1482,65 +1487,6 @@ "@noble/hashes": true } }, - "@metamask/multichain": { - "globals": { - "console.error": true, - "console.log": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/eth-json-rpc-filters": true, - "@metamask/multichain>@metamask/api-specs": true, - "@metamask/multichain>@metamask/rpc-errors": true, - "@metamask/multichain>@open-rpc/schema-utils-js": true, - "@metamask/multichain>jsonschema": true, - "@metamask/permission-controller": true, - "@metamask/safe-event-emitter": true, - "@metamask/utils": true, - "browserify>assert": true, - "lodash": true - } - }, - "@metamask/multichain>@metamask/rpc-errors": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/utils": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js": { - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/meta-schema": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, - "@open-rpc/schema-utils-js>is-url": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { - "globals": { - "console": true - }, - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "eslint>fast-deep-equal": true, - "uri-js": true - } - }, - "@metamask/multichain>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/name-controller": { "globals": { "fetch": true @@ -1959,9 +1905,9 @@ "@metamask/controller-utils": true, "@metamask/permission-controller>@metamask/base-controller": true, "@metamask/permission-controller>@metamask/rpc-errors": true, + "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1976,8 +1922,38 @@ }, "@metamask/permission-controller>@metamask/rpc-errors": { "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/utils": true + "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, + "@metamask/rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, + "@metamask/permission-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true } }, "@metamask/permission-controller>nanoid": { @@ -2052,9 +2028,9 @@ "crypto": true }, "packages": { - "@metamask/controller-utils": true, "@metamask/eth-query>json-rpc-random-id": true, "@metamask/ppom-validator>@metamask/base-controller": true, + "@metamask/ppom-validator>@metamask/controller-utils": true, "@metamask/ppom-validator>@metamask/rpc-errors": true, "@metamask/ppom-validator>crypto-js": true, "@metamask/ppom-validator>elliptic": true, @@ -2070,6 +2046,24 @@ "immer": true } }, + "@metamask/ppom-validator>@metamask/controller-utils": { + "globals": { + "URL": true, + "console.error": true, + "fetch": true, + "setTimeout": true + }, + "packages": { + "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@spruceid/siwe-parser": true, + "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/ppom-validator>@metamask/utils": true, + "bn.js": true, + "browserify>buffer": true, + "eslint>fast-deep-equal": true, + "eth-ens-namehash": true + } + }, "@metamask/ppom-validator>@metamask/rpc-errors": { "packages": { "@metamask/ppom-validator>@metamask/rpc-errors>@metamask/utils": true, @@ -2091,6 +2085,21 @@ "semver": true } }, + "@metamask/ppom-validator>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/ppom-validator>crypto-js": { "globals": { "crypto": true, @@ -2601,11 +2610,11 @@ }, "packages": { "@metamask/object-multiplex": true, - "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, + "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, @@ -2661,6 +2670,21 @@ "readable-stream": true } }, + "@metamask/snaps-controllers>@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/snaps-controllers>@metamask/base-controller": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-controllers>@metamask/rpc-errors": true, + "@metamask/snaps-controllers>nanoid": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, "@metamask/snaps-controllers>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2724,7 +2748,7 @@ }, "@metamask/snaps-rpc-methods": { "packages": { - "@metamask/permission-controller": true, + "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, @@ -2734,6 +2758,34 @@ "@noble/hashes": true } }, + "@metamask/snaps-rpc-methods>@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, + "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { + "globals": { + "crypto.getRandomValues": true + } + }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2781,9 +2833,9 @@ "fetch": true }, "packages": { - "@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/rpc-errors": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, @@ -2800,6 +2852,34 @@ "semver": true } }, + "@metamask/snaps-utils>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, + "@metamask/snaps-utils>@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-utils>@metamask/base-controller": true, + "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, + "@metamask/snaps-utils>@metamask/rpc-errors": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, + "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { + "globals": { + "crypto.getRandomValues": true + } + }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -3045,33 +3125,6 @@ "crypto": true } }, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { - "packages": { - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, - "@open-rpc/test-coverage>isomorphic-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch": { - "globals": { - "fetch.bind": true - }, - "packages": { - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { - "globals": { - "AbortController": true, - "Blob": true, - "FileReader": true, - "FormData": true, - "URLSearchParams.prototype.isPrototypeOf": true, - "XMLHttpRequest": true, - "console.warn": true, - "define": true, - "setTimeout": true - } - }, "@popperjs/core": { "globals": { "Element": true, @@ -4614,9 +4667,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index af34ab78617f..880b542673ea 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1460,13 +1460,18 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/multichain>jsonschema": true, + "@metamask/message-manager>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, + "@metamask/message-manager>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1482,65 +1487,6 @@ "@noble/hashes": true } }, - "@metamask/multichain": { - "globals": { - "console.error": true, - "console.log": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/eth-json-rpc-filters": true, - "@metamask/multichain>@metamask/api-specs": true, - "@metamask/multichain>@metamask/rpc-errors": true, - "@metamask/multichain>@open-rpc/schema-utils-js": true, - "@metamask/multichain>jsonschema": true, - "@metamask/permission-controller": true, - "@metamask/safe-event-emitter": true, - "@metamask/utils": true, - "browserify>assert": true, - "lodash": true - } - }, - "@metamask/multichain>@metamask/rpc-errors": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/utils": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js": { - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/meta-schema": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, - "@open-rpc/schema-utils-js>is-url": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { - "globals": { - "console": true - }, - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "eslint>fast-deep-equal": true, - "uri-js": true - } - }, - "@metamask/multichain>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/name-controller": { "globals": { "fetch": true @@ -1959,9 +1905,9 @@ "@metamask/controller-utils": true, "@metamask/permission-controller>@metamask/base-controller": true, "@metamask/permission-controller>@metamask/rpc-errors": true, + "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1976,8 +1922,38 @@ }, "@metamask/permission-controller>@metamask/rpc-errors": { "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/utils": true + "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, + "@metamask/rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, + "@metamask/permission-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true } }, "@metamask/permission-controller>nanoid": { @@ -2052,9 +2028,9 @@ "crypto": true }, "packages": { - "@metamask/controller-utils": true, "@metamask/eth-query>json-rpc-random-id": true, "@metamask/ppom-validator>@metamask/base-controller": true, + "@metamask/ppom-validator>@metamask/controller-utils": true, "@metamask/ppom-validator>@metamask/rpc-errors": true, "@metamask/ppom-validator>crypto-js": true, "@metamask/ppom-validator>elliptic": true, @@ -2070,6 +2046,24 @@ "immer": true } }, + "@metamask/ppom-validator>@metamask/controller-utils": { + "globals": { + "URL": true, + "console.error": true, + "fetch": true, + "setTimeout": true + }, + "packages": { + "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@spruceid/siwe-parser": true, + "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/ppom-validator>@metamask/utils": true, + "bn.js": true, + "browserify>buffer": true, + "eslint>fast-deep-equal": true, + "eth-ens-namehash": true + } + }, "@metamask/ppom-validator>@metamask/rpc-errors": { "packages": { "@metamask/ppom-validator>@metamask/rpc-errors>@metamask/utils": true, @@ -2091,6 +2085,21 @@ "semver": true } }, + "@metamask/ppom-validator>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/ppom-validator>crypto-js": { "globals": { "crypto": true, @@ -2601,11 +2610,11 @@ }, "packages": { "@metamask/object-multiplex": true, - "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, + "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, @@ -2661,6 +2670,21 @@ "readable-stream": true } }, + "@metamask/snaps-controllers>@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/snaps-controllers>@metamask/base-controller": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-controllers>@metamask/rpc-errors": true, + "@metamask/snaps-controllers>nanoid": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, "@metamask/snaps-controllers>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2724,7 +2748,7 @@ }, "@metamask/snaps-rpc-methods": { "packages": { - "@metamask/permission-controller": true, + "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, @@ -2734,6 +2758,34 @@ "@noble/hashes": true } }, + "@metamask/snaps-rpc-methods>@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, + "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { + "globals": { + "crypto.getRandomValues": true + } + }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2781,9 +2833,9 @@ "fetch": true }, "packages": { - "@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/rpc-errors": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, @@ -2800,6 +2852,34 @@ "semver": true } }, + "@metamask/snaps-utils>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, + "@metamask/snaps-utils>@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-utils>@metamask/base-controller": true, + "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, + "@metamask/snaps-utils>@metamask/rpc-errors": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, + "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { + "globals": { + "crypto.getRandomValues": true + } + }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -3045,33 +3125,6 @@ "crypto": true } }, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { - "packages": { - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, - "@open-rpc/test-coverage>isomorphic-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch": { - "globals": { - "fetch.bind": true - }, - "packages": { - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { - "globals": { - "AbortController": true, - "Blob": true, - "FileReader": true, - "FormData": true, - "URLSearchParams.prototype.isPrototypeOf": true, - "XMLHttpRequest": true, - "console.warn": true, - "define": true, - "setTimeout": true - } - }, "@popperjs/core": { "globals": { "Element": true, @@ -4614,9 +4667,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index af34ab78617f..880b542673ea 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1460,13 +1460,18 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/multichain>jsonschema": true, + "@metamask/message-manager>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, + "@metamask/message-manager>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1482,65 +1487,6 @@ "@noble/hashes": true } }, - "@metamask/multichain": { - "globals": { - "console.error": true, - "console.log": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/eth-json-rpc-filters": true, - "@metamask/multichain>@metamask/api-specs": true, - "@metamask/multichain>@metamask/rpc-errors": true, - "@metamask/multichain>@open-rpc/schema-utils-js": true, - "@metamask/multichain>jsonschema": true, - "@metamask/permission-controller": true, - "@metamask/safe-event-emitter": true, - "@metamask/utils": true, - "browserify>assert": true, - "lodash": true - } - }, - "@metamask/multichain>@metamask/rpc-errors": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/utils": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js": { - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/meta-schema": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, - "@open-rpc/schema-utils-js>is-url": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { - "globals": { - "console": true - }, - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "eslint>fast-deep-equal": true, - "uri-js": true - } - }, - "@metamask/multichain>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/name-controller": { "globals": { "fetch": true @@ -1959,9 +1905,9 @@ "@metamask/controller-utils": true, "@metamask/permission-controller>@metamask/base-controller": true, "@metamask/permission-controller>@metamask/rpc-errors": true, + "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1976,8 +1922,38 @@ }, "@metamask/permission-controller>@metamask/rpc-errors": { "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/utils": true + "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, + "@metamask/rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, + "@metamask/permission-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true } }, "@metamask/permission-controller>nanoid": { @@ -2052,9 +2028,9 @@ "crypto": true }, "packages": { - "@metamask/controller-utils": true, "@metamask/eth-query>json-rpc-random-id": true, "@metamask/ppom-validator>@metamask/base-controller": true, + "@metamask/ppom-validator>@metamask/controller-utils": true, "@metamask/ppom-validator>@metamask/rpc-errors": true, "@metamask/ppom-validator>crypto-js": true, "@metamask/ppom-validator>elliptic": true, @@ -2070,6 +2046,24 @@ "immer": true } }, + "@metamask/ppom-validator>@metamask/controller-utils": { + "globals": { + "URL": true, + "console.error": true, + "fetch": true, + "setTimeout": true + }, + "packages": { + "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@spruceid/siwe-parser": true, + "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/ppom-validator>@metamask/utils": true, + "bn.js": true, + "browserify>buffer": true, + "eslint>fast-deep-equal": true, + "eth-ens-namehash": true + } + }, "@metamask/ppom-validator>@metamask/rpc-errors": { "packages": { "@metamask/ppom-validator>@metamask/rpc-errors>@metamask/utils": true, @@ -2091,6 +2085,21 @@ "semver": true } }, + "@metamask/ppom-validator>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/ppom-validator>crypto-js": { "globals": { "crypto": true, @@ -2601,11 +2610,11 @@ }, "packages": { "@metamask/object-multiplex": true, - "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, + "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, @@ -2661,6 +2670,21 @@ "readable-stream": true } }, + "@metamask/snaps-controllers>@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/snaps-controllers>@metamask/base-controller": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-controllers>@metamask/rpc-errors": true, + "@metamask/snaps-controllers>nanoid": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, "@metamask/snaps-controllers>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2724,7 +2748,7 @@ }, "@metamask/snaps-rpc-methods": { "packages": { - "@metamask/permission-controller": true, + "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, @@ -2734,6 +2758,34 @@ "@noble/hashes": true } }, + "@metamask/snaps-rpc-methods>@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, + "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { + "globals": { + "crypto.getRandomValues": true + } + }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2781,9 +2833,9 @@ "fetch": true }, "packages": { - "@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/rpc-errors": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, @@ -2800,6 +2852,34 @@ "semver": true } }, + "@metamask/snaps-utils>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, + "@metamask/snaps-utils>@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-utils>@metamask/base-controller": true, + "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, + "@metamask/snaps-utils>@metamask/rpc-errors": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, + "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { + "globals": { + "crypto.getRandomValues": true + } + }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -3045,33 +3125,6 @@ "crypto": true } }, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { - "packages": { - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, - "@open-rpc/test-coverage>isomorphic-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch": { - "globals": { - "fetch.bind": true - }, - "packages": { - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { - "globals": { - "AbortController": true, - "Blob": true, - "FileReader": true, - "FormData": true, - "URLSearchParams.prototype.isPrototypeOf": true, - "XMLHttpRequest": true, - "console.warn": true, - "define": true, - "setTimeout": true - } - }, "@popperjs/core": { "globals": { "Element": true, @@ -4614,9 +4667,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 9d476f3f2372..25756f84ccc4 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -1552,13 +1552,18 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/multichain>jsonschema": true, + "@metamask/message-manager>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, + "@metamask/message-manager>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1574,65 +1579,6 @@ "@noble/hashes": true } }, - "@metamask/multichain": { - "globals": { - "console.error": true, - "console.log": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/eth-json-rpc-filters": true, - "@metamask/multichain>@metamask/api-specs": true, - "@metamask/multichain>@metamask/rpc-errors": true, - "@metamask/multichain>@open-rpc/schema-utils-js": true, - "@metamask/multichain>jsonschema": true, - "@metamask/permission-controller": true, - "@metamask/safe-event-emitter": true, - "@metamask/utils": true, - "browserify>assert": true, - "lodash": true - } - }, - "@metamask/multichain>@metamask/rpc-errors": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/utils": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js": { - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/meta-schema": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, - "@open-rpc/schema-utils-js>is-url": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { - "globals": { - "console": true - }, - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "eslint>fast-deep-equal": true, - "uri-js": true - } - }, - "@metamask/multichain>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/name-controller": { "globals": { "fetch": true @@ -2051,9 +1997,9 @@ "@metamask/controller-utils": true, "@metamask/permission-controller>@metamask/base-controller": true, "@metamask/permission-controller>@metamask/rpc-errors": true, + "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -2068,8 +2014,38 @@ }, "@metamask/permission-controller>@metamask/rpc-errors": { "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/utils": true + "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, + "@metamask/rpc-errors>fast-safe-stringify": true + } + }, + "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, + "@metamask/permission-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true } }, "@metamask/permission-controller>nanoid": { @@ -2144,9 +2120,9 @@ "crypto": true }, "packages": { - "@metamask/controller-utils": true, "@metamask/eth-query>json-rpc-random-id": true, "@metamask/ppom-validator>@metamask/base-controller": true, + "@metamask/ppom-validator>@metamask/controller-utils": true, "@metamask/ppom-validator>@metamask/rpc-errors": true, "@metamask/ppom-validator>crypto-js": true, "@metamask/ppom-validator>elliptic": true, @@ -2162,6 +2138,24 @@ "immer": true } }, + "@metamask/ppom-validator>@metamask/controller-utils": { + "globals": { + "URL": true, + "console.error": true, + "fetch": true, + "setTimeout": true + }, + "packages": { + "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@spruceid/siwe-parser": true, + "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/ppom-validator>@metamask/utils": true, + "bn.js": true, + "browserify>buffer": true, + "eslint>fast-deep-equal": true, + "eth-ens-namehash": true + } + }, "@metamask/ppom-validator>@metamask/rpc-errors": { "packages": { "@metamask/ppom-validator>@metamask/rpc-errors>@metamask/utils": true, @@ -2183,6 +2177,21 @@ "semver": true } }, + "@metamask/ppom-validator>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/ppom-validator>crypto-js": { "globals": { "crypto": true, @@ -2693,11 +2702,11 @@ }, "packages": { "@metamask/object-multiplex": true, - "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, + "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, @@ -2753,6 +2762,21 @@ "readable-stream": true } }, + "@metamask/snaps-controllers>@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/snaps-controllers>@metamask/base-controller": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-controllers>@metamask/rpc-errors": true, + "@metamask/snaps-controllers>nanoid": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, "@metamask/snaps-controllers>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2816,7 +2840,7 @@ }, "@metamask/snaps-rpc-methods": { "packages": { - "@metamask/permission-controller": true, + "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, @@ -2826,6 +2850,34 @@ "@noble/hashes": true } }, + "@metamask/snaps-rpc-methods>@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, + "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, + "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { + "globals": { + "crypto.getRandomValues": true + } + }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2873,9 +2925,9 @@ "fetch": true }, "packages": { - "@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, + "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/rpc-errors": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, @@ -2892,6 +2944,34 @@ "semver": true } }, + "@metamask/snaps-utils>@metamask/base-controller": { + "globals": { + "setTimeout": true + }, + "packages": { + "immer": true + } + }, + "@metamask/snaps-utils>@metamask/permission-controller": { + "globals": { + "console.error": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/snaps-utils>@metamask/base-controller": true, + "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, + "@metamask/snaps-utils>@metamask/rpc-errors": true, + "@metamask/utils": true, + "deep-freeze-strict": true, + "immer": true + } + }, + "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { + "globals": { + "crypto.getRandomValues": true + } + }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -3137,33 +3217,6 @@ "crypto": true } }, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { - "packages": { - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, - "@open-rpc/test-coverage>isomorphic-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch": { - "globals": { - "fetch.bind": true - }, - "packages": { - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { - "globals": { - "AbortController": true, - "Blob": true, - "FileReader": true, - "FormData": true, - "URLSearchParams.prototype.isPrototypeOf": true, - "XMLHttpRequest": true, - "console.warn": true, - "define": true, - "setTimeout": true - } - }, "@popperjs/core": { "globals": { "Element": true, @@ -4706,9 +4759,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index aa674f8b3cb2..e7ce64ceec23 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -2131,8 +2131,7 @@ "chokidar>normalize-path": true, "chokidar>readdirp": true, "del>is-glob": true, - "eslint>glob-parent": true, - "tsx>fsevents": true + "eslint>glob-parent": true } }, "chokidar>anymatch": { @@ -8884,13 +8883,6 @@ "typescript": true } }, - "tsx>fsevents": { - "globals": { - "console.assert": true, - "process.platform": true - }, - "native": true - }, "typescript": { "builtin": { "buffer.Buffer": true, From bf856b8c66814fd92ab998ade5c4815d4fa296ac Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Thu, 17 Oct 2024 16:06:47 +0000 Subject: [PATCH 054/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 241 ++++++++++---------------- lavamoat/browserify/flask/policy.json | 241 ++++++++++---------------- lavamoat/browserify/main/policy.json | 241 ++++++++++---------------- lavamoat/browserify/mmi/policy.json | 241 ++++++++++---------------- 4 files changed, 376 insertions(+), 588 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 880b542673ea..5d514ef8a4cb 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1460,18 +1460,13 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/message-manager>jsonschema": true, + "@metamask/multichain>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, - "@metamask/message-manager>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1487,6 +1482,65 @@ "@noble/hashes": true } }, + "@metamask/multichain": { + "globals": { + "console.error": true, + "console.log": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/eth-json-rpc-filters": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/multichain>@metamask/rpc-errors": true, + "@metamask/multichain>@open-rpc/schema-utils-js": true, + "@metamask/multichain>jsonschema": true, + "@metamask/permission-controller": true, + "@metamask/safe-event-emitter": true, + "@metamask/utils": true, + "browserify>assert": true, + "lodash": true + } + }, + "@metamask/multichain>@metamask/rpc-errors": { + "packages": { + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js": { + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, + "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/meta-schema": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, + "@open-rpc/schema-utils-js>is-url": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { + "packages": { + "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { + "globals": { + "console": true + }, + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "eslint>fast-deep-equal": true, + "uri-js": true + } + }, + "@metamask/multichain>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -1905,9 +1959,9 @@ "@metamask/controller-utils": true, "@metamask/permission-controller>@metamask/base-controller": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1922,38 +1976,8 @@ }, "@metamask/permission-controller>@metamask/rpc-errors": { "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/permission-controller>nanoid": { @@ -2028,9 +2052,9 @@ "crypto": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query>json-rpc-random-id": true, "@metamask/ppom-validator>@metamask/base-controller": true, - "@metamask/ppom-validator>@metamask/controller-utils": true, "@metamask/ppom-validator>@metamask/rpc-errors": true, "@metamask/ppom-validator>crypto-js": true, "@metamask/ppom-validator>elliptic": true, @@ -2046,24 +2070,6 @@ "immer": true } }, - "@metamask/ppom-validator>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/ppom-validator>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/ppom-validator>@metamask/rpc-errors": { "packages": { "@metamask/ppom-validator>@metamask/rpc-errors>@metamask/utils": true, @@ -2085,21 +2091,6 @@ "semver": true } }, - "@metamask/ppom-validator>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/ppom-validator>crypto-js": { "globals": { "crypto": true, @@ -2610,11 +2601,11 @@ }, "packages": { "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, @@ -2670,21 +2661,6 @@ "readable-stream": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/base-controller": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2748,7 +2724,7 @@ }, "@metamask/snaps-rpc-methods": { "packages": { - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, + "@metamask/permission-controller": true, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, @@ -2758,34 +2734,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2833,9 +2781,9 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/rpc-errors": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, @@ -2852,34 +2800,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -3125,6 +3045,33 @@ "crypto": true } }, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { + "packages": { + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, + "@open-rpc/test-coverage>isomorphic-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch": { + "globals": { + "fetch.bind": true + }, + "packages": { + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { + "globals": { + "AbortController": true, + "Blob": true, + "FileReader": true, + "FormData": true, + "URLSearchParams.prototype.isPrototypeOf": true, + "XMLHttpRequest": true, + "console.warn": true, + "define": true, + "setTimeout": true + } + }, "@popperjs/core": { "globals": { "Element": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 880b542673ea..5d514ef8a4cb 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1460,18 +1460,13 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/message-manager>jsonschema": true, + "@metamask/multichain>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, - "@metamask/message-manager>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1487,6 +1482,65 @@ "@noble/hashes": true } }, + "@metamask/multichain": { + "globals": { + "console.error": true, + "console.log": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/eth-json-rpc-filters": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/multichain>@metamask/rpc-errors": true, + "@metamask/multichain>@open-rpc/schema-utils-js": true, + "@metamask/multichain>jsonschema": true, + "@metamask/permission-controller": true, + "@metamask/safe-event-emitter": true, + "@metamask/utils": true, + "browserify>assert": true, + "lodash": true + } + }, + "@metamask/multichain>@metamask/rpc-errors": { + "packages": { + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js": { + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, + "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/meta-schema": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, + "@open-rpc/schema-utils-js>is-url": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { + "packages": { + "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { + "globals": { + "console": true + }, + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "eslint>fast-deep-equal": true, + "uri-js": true + } + }, + "@metamask/multichain>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -1905,9 +1959,9 @@ "@metamask/controller-utils": true, "@metamask/permission-controller>@metamask/base-controller": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1922,38 +1976,8 @@ }, "@metamask/permission-controller>@metamask/rpc-errors": { "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/permission-controller>nanoid": { @@ -2028,9 +2052,9 @@ "crypto": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query>json-rpc-random-id": true, "@metamask/ppom-validator>@metamask/base-controller": true, - "@metamask/ppom-validator>@metamask/controller-utils": true, "@metamask/ppom-validator>@metamask/rpc-errors": true, "@metamask/ppom-validator>crypto-js": true, "@metamask/ppom-validator>elliptic": true, @@ -2046,24 +2070,6 @@ "immer": true } }, - "@metamask/ppom-validator>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/ppom-validator>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/ppom-validator>@metamask/rpc-errors": { "packages": { "@metamask/ppom-validator>@metamask/rpc-errors>@metamask/utils": true, @@ -2085,21 +2091,6 @@ "semver": true } }, - "@metamask/ppom-validator>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/ppom-validator>crypto-js": { "globals": { "crypto": true, @@ -2610,11 +2601,11 @@ }, "packages": { "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, @@ -2670,21 +2661,6 @@ "readable-stream": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/base-controller": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2748,7 +2724,7 @@ }, "@metamask/snaps-rpc-methods": { "packages": { - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, + "@metamask/permission-controller": true, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, @@ -2758,34 +2734,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2833,9 +2781,9 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/rpc-errors": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, @@ -2852,34 +2800,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -3125,6 +3045,33 @@ "crypto": true } }, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { + "packages": { + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, + "@open-rpc/test-coverage>isomorphic-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch": { + "globals": { + "fetch.bind": true + }, + "packages": { + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { + "globals": { + "AbortController": true, + "Blob": true, + "FileReader": true, + "FormData": true, + "URLSearchParams.prototype.isPrototypeOf": true, + "XMLHttpRequest": true, + "console.warn": true, + "define": true, + "setTimeout": true + } + }, "@popperjs/core": { "globals": { "Element": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 880b542673ea..5d514ef8a4cb 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1460,18 +1460,13 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/message-manager>jsonschema": true, + "@metamask/multichain>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, - "@metamask/message-manager>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1487,6 +1482,65 @@ "@noble/hashes": true } }, + "@metamask/multichain": { + "globals": { + "console.error": true, + "console.log": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/eth-json-rpc-filters": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/multichain>@metamask/rpc-errors": true, + "@metamask/multichain>@open-rpc/schema-utils-js": true, + "@metamask/multichain>jsonschema": true, + "@metamask/permission-controller": true, + "@metamask/safe-event-emitter": true, + "@metamask/utils": true, + "browserify>assert": true, + "lodash": true + } + }, + "@metamask/multichain>@metamask/rpc-errors": { + "packages": { + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js": { + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, + "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/meta-schema": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, + "@open-rpc/schema-utils-js>is-url": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { + "packages": { + "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { + "globals": { + "console": true + }, + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "eslint>fast-deep-equal": true, + "uri-js": true + } + }, + "@metamask/multichain>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -1905,9 +1959,9 @@ "@metamask/controller-utils": true, "@metamask/permission-controller>@metamask/base-controller": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1922,38 +1976,8 @@ }, "@metamask/permission-controller>@metamask/rpc-errors": { "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/permission-controller>nanoid": { @@ -2028,9 +2052,9 @@ "crypto": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query>json-rpc-random-id": true, "@metamask/ppom-validator>@metamask/base-controller": true, - "@metamask/ppom-validator>@metamask/controller-utils": true, "@metamask/ppom-validator>@metamask/rpc-errors": true, "@metamask/ppom-validator>crypto-js": true, "@metamask/ppom-validator>elliptic": true, @@ -2046,24 +2070,6 @@ "immer": true } }, - "@metamask/ppom-validator>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/ppom-validator>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/ppom-validator>@metamask/rpc-errors": { "packages": { "@metamask/ppom-validator>@metamask/rpc-errors>@metamask/utils": true, @@ -2085,21 +2091,6 @@ "semver": true } }, - "@metamask/ppom-validator>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/ppom-validator>crypto-js": { "globals": { "crypto": true, @@ -2610,11 +2601,11 @@ }, "packages": { "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, @@ -2670,21 +2661,6 @@ "readable-stream": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/base-controller": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2748,7 +2724,7 @@ }, "@metamask/snaps-rpc-methods": { "packages": { - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, + "@metamask/permission-controller": true, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, @@ -2758,34 +2734,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2833,9 +2781,9 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/rpc-errors": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, @@ -2852,34 +2800,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -3125,6 +3045,33 @@ "crypto": true } }, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { + "packages": { + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, + "@open-rpc/test-coverage>isomorphic-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch": { + "globals": { + "fetch.bind": true + }, + "packages": { + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { + "globals": { + "AbortController": true, + "Blob": true, + "FileReader": true, + "FormData": true, + "URLSearchParams.prototype.isPrototypeOf": true, + "XMLHttpRequest": true, + "console.warn": true, + "define": true, + "setTimeout": true + } + }, "@popperjs/core": { "globals": { "Element": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 25756f84ccc4..f7867b55779b 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -1552,18 +1552,13 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/message-manager>jsonschema": true, + "@metamask/multichain>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, - "@metamask/message-manager>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1579,6 +1574,65 @@ "@noble/hashes": true } }, + "@metamask/multichain": { + "globals": { + "console.error": true, + "console.log": true + }, + "packages": { + "@metamask/controller-utils": true, + "@metamask/eth-json-rpc-filters": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/multichain>@metamask/rpc-errors": true, + "@metamask/multichain>@open-rpc/schema-utils-js": true, + "@metamask/multichain>jsonschema": true, + "@metamask/permission-controller": true, + "@metamask/safe-event-emitter": true, + "@metamask/utils": true, + "browserify>assert": true, + "lodash": true + } + }, + "@metamask/multichain>@metamask/rpc-errors": { + "packages": { + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js": { + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, + "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/meta-schema": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, + "@open-rpc/schema-utils-js>is-url": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { + "packages": { + "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true + } + }, + "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { + "globals": { + "console": true + }, + "packages": { + "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, + "@metamask/snaps-utils>fast-json-stable-stringify": true, + "eslint>fast-deep-equal": true, + "uri-js": true + } + }, + "@metamask/multichain>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -1997,9 +2051,9 @@ "@metamask/controller-utils": true, "@metamask/permission-controller>@metamask/base-controller": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -2014,38 +2068,8 @@ }, "@metamask/permission-controller>@metamask/rpc-errors": { "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/permission-controller>nanoid": { @@ -2120,9 +2144,9 @@ "crypto": true }, "packages": { + "@metamask/controller-utils": true, "@metamask/eth-query>json-rpc-random-id": true, "@metamask/ppom-validator>@metamask/base-controller": true, - "@metamask/ppom-validator>@metamask/controller-utils": true, "@metamask/ppom-validator>@metamask/rpc-errors": true, "@metamask/ppom-validator>crypto-js": true, "@metamask/ppom-validator>elliptic": true, @@ -2138,24 +2162,6 @@ "immer": true } }, - "@metamask/ppom-validator>@metamask/controller-utils": { - "globals": { - "URL": true, - "console.error": true, - "fetch": true, - "setTimeout": true - }, - "packages": { - "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@spruceid/siwe-parser": true, - "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/ppom-validator>@metamask/utils": true, - "bn.js": true, - "browserify>buffer": true, - "eslint>fast-deep-equal": true, - "eth-ens-namehash": true - } - }, "@metamask/ppom-validator>@metamask/rpc-errors": { "packages": { "@metamask/ppom-validator>@metamask/rpc-errors>@metamask/utils": true, @@ -2177,21 +2183,6 @@ "semver": true } }, - "@metamask/ppom-validator>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/ppom-validator>crypto-js": { "globals": { "crypto": true, @@ -2702,11 +2693,11 @@ }, "packages": { "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, "@metamask/snaps-controllers>@metamask/json-rpc-middleware-stream": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, @@ -2762,21 +2753,6 @@ "readable-stream": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/base-controller": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2840,7 +2816,7 @@ }, "@metamask/snaps-rpc-methods": { "packages": { - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, + "@metamask/permission-controller": true, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, @@ -2850,34 +2826,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2925,9 +2873,9 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/rpc-errors": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, @@ -2944,34 +2892,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -3217,6 +3137,33 @@ "crypto": true } }, + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { + "packages": { + "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, + "@open-rpc/test-coverage>isomorphic-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch": { + "globals": { + "fetch.bind": true + }, + "packages": { + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true + } + }, + "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { + "globals": { + "AbortController": true, + "Blob": true, + "FileReader": true, + "FormData": true, + "URLSearchParams.prototype.isPrototypeOf": true, + "XMLHttpRequest": true, + "console.warn": true, + "define": true, + "setTimeout": true + } + }, "@popperjs/core": { "globals": { "Element": true, From fe584ed43f48d013ecc705a7f6cb95637c413035 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 17 Oct 2024 09:28:41 -0700 Subject: [PATCH 055/148] fix fixture-builder restrictReturnedAccounts??? --- test/e2e/fixture-builder.js | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index e72a32565917..13e67657424f 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -447,9 +447,7 @@ class FixtureBuilder { account = '', } = {}) { const selectedAccount = account || DEFAULT_FIXTURE_ACCOUNT; - let subjects = {}; - if (restrictReturnedAccounts) { - subjects = { + const subjects = { [DAPP_URL]: { origin: DAPP_URL, permissions: { @@ -463,7 +461,7 @@ class FixtureBuilder { 'eip155:1337': { methods: [], notifications: [], - accounts: [ + accounts: restrictReturnedAccounts ? [] : [ `eip155:1337:${selectedAccount.toLowerCase()}`, 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', `eip155:1337:${ERC_4337_ACCOUNT.toLowerCase()}`, @@ -482,7 +480,6 @@ class FixtureBuilder { }, }, }; - } return this.withPermissionController({ subjects, }); @@ -491,9 +488,7 @@ class FixtureBuilder { withPermissionControllerSnapAccountConnectedToTestDapp( restrictReturnedAccounts = true, ) { - let subjects = {}; - if (restrictReturnedAccounts) { - subjects = { + const subjects = { [DAPP_URL]: { origin: DAPP_URL, permissions: { @@ -507,7 +502,7 @@ class FixtureBuilder { 'eip155:1337': { methods: [], notifications: [], - accounts: [ + accounts: restrictReturnedAccounts ? [] : [ 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', ], }, @@ -524,16 +519,13 @@ class FixtureBuilder { }, }, }; - } return this.withPermissionController({ subjects }); } withPermissionControllerConnectedToTwoTestDapps( restrictReturnedAccounts = true, ) { - let subjects = {}; - if (restrictReturnedAccounts) { - subjects = { + const subjects = { [DAPP_URL]: { origin: DAPP_URL, permissions: { @@ -547,7 +539,7 @@ class FixtureBuilder { 'eip155:1337': { methods: [], notifications: [], - accounts: [ + accounts: restrictReturnedAccounts ? [] : [ 'eip155:1337:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', ], @@ -577,7 +569,7 @@ class FixtureBuilder { 'eip155:1338': { methods: [], notifications: [], - accounts: [ + accounts: restrictReturnedAccounts ? [] : [ 'eip155:1338:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', 'eip155:1338:0x09781764c08de8ca82e156bbf156a3ca217c7950', ], @@ -595,7 +587,6 @@ class FixtureBuilder { }, }, }; - } return this.withPermissionController({ subjects }); } From 2497a19e154202becaa64136399681398275f135 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 17 Oct 2024 09:51:31 -0700 Subject: [PATCH 056/148] Remove restrictedReturnedAccounts in fixtures. Fix snap-account-signatures --- test/e2e/accounts/common.ts | 22 -- test/e2e/fixture-builder.js | 219 +++++++++--------- .../account/snap-account-signatures.spec.ts | 23 +- 3 files changed, 123 insertions(+), 141 deletions(-) diff --git a/test/e2e/accounts/common.ts b/test/e2e/accounts/common.ts index 62f3fc082b53..42d1e6d42f69 100644 --- a/test/e2e/accounts/common.ts +++ b/test/e2e/accounts/common.ts @@ -1,6 +1,5 @@ import { privateToAddress } from 'ethereumjs-util'; import messages from '../../../app/_locales/en/messages.json'; -import FixtureBuilder from '../fixture-builder'; import { PRIVATE_KEY, PRIVATE_KEY_TWO, @@ -9,32 +8,11 @@ import { switchToOrOpenDapp, unlockWallet, validateContractDetails, - multipleGanacheOptions, } from '../helpers'; import { Driver } from '../webdriver/driver'; import { DAPP_URL, TEST_SNAPS_SIMPLE_KEYRING_WEBSITE_URL } from '../constants'; import { retry } from '../../../development/lib/retry'; -/** - * These are fixtures specific to Account Snap E2E tests: - * -- connected to Test Dapp - * -- two private keys with 25 ETH each - * - * @param title - */ -export const accountSnapFixtures = (title: string | undefined) => { - return { - dapp: true, - fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDapp({ - restrictReturnedAccounts: false, - }) - .build(), - ganacheOptions: multipleGanacheOptions, - title, - }; -}; - // convert PRIVATE_KEY to public key export const PUBLIC_KEY = privateToAddress( Buffer.from(PRIVATE_KEY.slice(2), 'hex'), diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 13e67657424f..bc7fb0a6c4e2 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -442,151 +442,144 @@ class FixtureBuilder { return this; } - withPermissionControllerConnectedToTestDapp({ - restrictReturnedAccounts = true, - account = '', - } = {}) { + withPermissionControllerConnectedToTestDapp({ account = '' } = {}) { const selectedAccount = account || DEFAULT_FIXTURE_ACCOUNT; const subjects = { - [DAPP_URL]: { - origin: DAPP_URL, - permissions: { - 'endowment:caip25': { - caveats: [ - { - type: 'authorizedScopes', - value: { - requiredScopes: {}, - optionalScopes: { - 'eip155:1337': { - methods: [], - notifications: [], - accounts: restrictReturnedAccounts ? [] : [ - `eip155:1337:${selectedAccount.toLowerCase()}`, - 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', - `eip155:1337:${ERC_4337_ACCOUNT.toLowerCase()}`, - ], - }, + [DAPP_URL]: { + origin: DAPP_URL, + permissions: { + 'endowment:caip25': { + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1337': { + methods: [], + notifications: [], + accounts: [ + `eip155:1337:${selectedAccount.toLowerCase()}`, + 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', + `eip155:1337:${ERC_4337_ACCOUNT.toLowerCase()}`, + ], }, - isMultichainOrigin: false, }, + isMultichainOrigin: false, }, - ], - id: 'ZaqPEWxyhNCJYACFw93jE', - date: 1664388714636, - invoker: DAPP_URL, - parentCapability: 'endowment:caip25', - }, + }, + ], + id: 'ZaqPEWxyhNCJYACFw93jE', + date: 1664388714636, + invoker: DAPP_URL, + parentCapability: 'endowment:caip25', }, }, - }; + }, + }; return this.withPermissionController({ subjects, }); } - withPermissionControllerSnapAccountConnectedToTestDapp( - restrictReturnedAccounts = true, - ) { + withPermissionControllerSnapAccountConnectedToTestDapp() { const subjects = { - [DAPP_URL]: { - origin: DAPP_URL, - permissions: { - 'endowment:caip25': { - caveats: [ - { - type: 'authorizedScopes', - value: { - requiredScopes: {}, - optionalScopes: { - 'eip155:1337': { - methods: [], - notifications: [], - accounts: restrictReturnedAccounts ? [] : [ - 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', - ], - }, + [DAPP_URL]: { + origin: DAPP_URL, + permissions: { + 'endowment:caip25': { + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1337': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', + ], }, - isMultichainOrigin: false, }, + isMultichainOrigin: false, }, - ], - id: 'ZaqPEWxyhNCJYACFw93jE', - date: 1664388714636, - invoker: DAPP_URL, - parentCapability: 'endowment:caip25', - }, + }, + ], + id: 'ZaqPEWxyhNCJYACFw93jE', + date: 1664388714636, + invoker: DAPP_URL, + parentCapability: 'endowment:caip25', }, }, - }; + }, + }; return this.withPermissionController({ subjects }); } - withPermissionControllerConnectedToTwoTestDapps( - restrictReturnedAccounts = true, - ) { + withPermissionControllerConnectedToTwoTestDapps() { const subjects = { - [DAPP_URL]: { - origin: DAPP_URL, - permissions: { - 'endowment:caip25': { - caveats: [ - { - type: 'authorizedScopes', - value: { - requiredScopes: {}, - optionalScopes: { - 'eip155:1337': { - methods: [], - notifications: [], - accounts: restrictReturnedAccounts ? [] : [ - 'eip155:1337:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', - 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', - ], - }, + [DAPP_URL]: { + origin: DAPP_URL, + permissions: { + 'endowment:caip25': { + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1337': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1337:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', + 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', + ], }, - isMultichainOrigin: false, }, + isMultichainOrigin: false, }, - ], - id: 'ZaqPEWxyhNCJYACFw93jE', - date: 1664388714636, - invoker: DAPP_URL, - parentCapability: 'endowment:caip25', - }, + }, + ], + id: 'ZaqPEWxyhNCJYACFw93jE', + date: 1664388714636, + invoker: DAPP_URL, + parentCapability: 'endowment:caip25', }, }, - [DAPP_ONE_URL]: { - origin: DAPP_ONE_URL, - permissions: { - 'endowment:caip25': { - caveats: [ - { - type: 'authorizedScopes', - value: { - requiredScopes: {}, - optionalScopes: { - 'eip155:1338': { - methods: [], - notifications: [], - accounts: restrictReturnedAccounts ? [] : [ - 'eip155:1338:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', - 'eip155:1338:0x09781764c08de8ca82e156bbf156a3ca217c7950', - ], - }, + }, + [DAPP_ONE_URL]: { + origin: DAPP_ONE_URL, + permissions: { + 'endowment:caip25': { + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1338': { + methods: [], + notifications: [], + accounts: [ + 'eip155:1338:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', + 'eip155:1338:0x09781764c08de8ca82e156bbf156a3ca217c7950', + ], }, - isMultichainOrigin: false, }, + isMultichainOrigin: false, }, - ], - id: 'ZaqPEWxyhNCJYACFw93jE', - date: 1664388714636, - invoker: DAPP_ONE_URL, - parentCapability: 'endowment:caip25', - }, + }, + ], + id: 'ZaqPEWxyhNCJYACFw93jE', + date: 1664388714636, + invoker: DAPP_ONE_URL, + parentCapability: 'endowment:caip25', }, }, - }; + }, + }; return this.withPermissionController({ subjects }); } diff --git a/test/e2e/tests/account/snap-account-signatures.spec.ts b/test/e2e/tests/account/snap-account-signatures.spec.ts index f5010fb61269..a811f4d29717 100644 --- a/test/e2e/tests/account/snap-account-signatures.spec.ts +++ b/test/e2e/tests/account/snap-account-signatures.spec.ts @@ -16,6 +16,7 @@ import { import SettingsPage from '../../page-objects/pages/settings-page'; import SnapSimpleKeyringPage from '../../page-objects/pages/snap-simple-keyring-page'; import TestDapp from '../../page-objects/pages/test-dapp'; +import { DAPP_URL } from '../../constants'; describe('Snap Account Signatures @no-mmi', function (this: Suite) { // Run sync, async approve, and async reject flows @@ -29,11 +30,7 @@ describe('Snap Account Signatures @no-mmi', function (this: Suite) { await withFixtures( { dapp: true, - fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDapp({ - restrictReturnedAccounts: false, - }) - .build(), + fixtures: new FixtureBuilder().build(), title, }, async ({ driver }: { driver: Driver }) => { @@ -61,8 +58,22 @@ describe('Snap Account Signatures @no-mmi', function (this: Suite) { await experimentalSettings.check_pageIsLoaded(); await experimentalSettings.toggleRedesignedSignature(); - // Run all 5 signature types + // Connect the SSK account await new TestDapp(driver).openTestDappPage(); + + await driver.findClickableElement({ text: 'Connect', tag: 'button' }); + await driver.clickElement('#connectButton'); + + await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); + + await driver.clickElementAndWaitForWindowToClose({ + text: 'Connect', + tag: 'button', + }); + + await driver.switchToWindowWithUrl(DAPP_URL); + + // Run all 5 signature types await personalSignWithSnapAccount( driver, newPublicKey, From b780d8904b642804303b88a77733db83ea46fe45 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 17 Oct 2024 10:04:41 -0700 Subject: [PATCH 057/148] temporarily skip test-snap-revoke-perm.spec.js --- test/e2e/snaps/test-snap-revoke-perm.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/e2e/snaps/test-snap-revoke-perm.spec.js b/test/e2e/snaps/test-snap-revoke-perm.spec.js index b70cc3ab4cc1..c75294ed5ec6 100644 --- a/test/e2e/snaps/test-snap-revoke-perm.spec.js +++ b/test/e2e/snaps/test-snap-revoke-perm.spec.js @@ -8,7 +8,8 @@ const { const FixtureBuilder = require('../fixture-builder'); const { TEST_SNAPS_WEBSITE_URL } = require('./enums'); -describe('Test Snap revoke permission', function () { +// TODO: Resolve this before merging. I'm sure the linter will make sure of it though +describe.skip('Test Snap revoke permission', function () { it('can revoke a permission', async function () { await withFixtures( { From 609d50fa7f6a85e4924922f648281a5fe01aaba3 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 17 Oct 2024 15:09:19 -0500 Subject: [PATCH 058/148] dedupe --- yarn.lock | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/yarn.lock b/yarn.lock index 55fab34adb5f..dd8542a2d13d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4150,14 +4150,7 @@ __metadata: languageName: node linkType: hard -"@json-schema-tools/meta-schema@npm:^1.6.10": - version: 1.7.4 - resolution: "@json-schema-tools/meta-schema@npm:1.7.4" - checksum: 10/6a688260eaac550d372325a39e7d4f44db7904a3fcaa3d3e0bf318b259007326592b53e511025ff35010ba0e0314dba338fd169338c5ea090328663f3e7cbd46 - languageName: node - linkType: hard - -"@json-schema-tools/meta-schema@npm:^1.7.5": +"@json-schema-tools/meta-schema@npm:^1.6.10, @json-schema-tools/meta-schema@npm:^1.7.5": version: 1.7.5 resolution: "@json-schema-tools/meta-schema@npm:1.7.5" checksum: 10/707dc3a285c26c37d00f418e9d0ef8a2ad1c23d4936ad5aab0ce94c9ae36a7a6125c4ca5048513af64b7e6e527b5472a1701d1f709c379acdd7ad12f6409d2cd @@ -4184,20 +4177,13 @@ __metadata: languageName: node linkType: hard -"@json-schema-tools/traverse@npm:^1.10.4": +"@json-schema-tools/traverse@npm:^1.10.4, @json-schema-tools/traverse@npm:^1.7.5, @json-schema-tools/traverse@npm:^1.7.8": version: 1.10.4 resolution: "@json-schema-tools/traverse@npm:1.10.4" checksum: 10/0027bc90df01c5eeee0833e722b7320b53be8b5ce3f4e0e4a6e45713a38e6f88f21aba31e3dd973093ef75cd21a40c07fe8f112da8f49a7919b1c0e44c904d20 languageName: node linkType: hard -"@json-schema-tools/traverse@npm:^1.7.5, @json-schema-tools/traverse@npm:^1.7.8": - version: 1.10.3 - resolution: "@json-schema-tools/traverse@npm:1.10.3" - checksum: 10/690623740d223ea373d8e561dad5c70bf86461bcedc5fc45da01c87bcdf3284bbdbad3006d4a423f8d82e4b2d4580e45f92c0b272f006024fb597d7f01876215 - languageName: node - linkType: hard - "@juggle/resize-observer@npm:^3.3.1": version: 3.4.0 resolution: "@juggle/resize-observer@npm:3.4.0" @@ -7015,14 +7001,7 @@ __metadata: languageName: node linkType: hard -"@open-rpc/meta-schema@npm:^1.14.6": - version: 1.14.6 - resolution: "@open-rpc/meta-schema@npm:1.14.6" - checksum: 10/7cb672ea42c143c3fcb177ad04b935d56c38cb28fc7ede0a0bb50293e0e49dee81046c2d43bc57c8bbf9efbbb76356d60b4a8e408a03ecc8fa5952ef3e342316 - languageName: node - linkType: hard - -"@open-rpc/meta-schema@npm:^1.14.9": +"@open-rpc/meta-schema@npm:^1.14.6, @open-rpc/meta-schema@npm:^1.14.9": version: 1.14.9 resolution: "@open-rpc/meta-schema@npm:1.14.9" checksum: 10/51505dcf7aa1a2285c78953c9b33711cede5f2765aa37dcb9ee7756d689e2ff2a89cfc6039504f0569c52a805fb9aa18f30a7c02ad7a06e793c801e43b419104 From 80156440399516460a57938accef3a432e867734 Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Thu, 17 Oct 2024 20:21:54 +0000 Subject: [PATCH 059/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 7 ++----- lavamoat/browserify/flask/policy.json | 7 ++----- lavamoat/browserify/main/policy.json | 7 ++----- lavamoat/browserify/mmi/policy.json | 7 ++----- lavamoat/build-system/policy.json | 10 +--------- 5 files changed, 9 insertions(+), 29 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 19beabda154a..075d54fec68a 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1511,17 +1511,17 @@ "packages": { "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@open-rpc/meta-schema": true, "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/meta-schema": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, "@open-rpc/schema-utils-js>is-url": true } }, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true } }, @@ -4584,9 +4584,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 19beabda154a..075d54fec68a 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1511,17 +1511,17 @@ "packages": { "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@open-rpc/meta-schema": true, "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/meta-schema": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, "@open-rpc/schema-utils-js>is-url": true } }, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true } }, @@ -4584,9 +4584,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 19beabda154a..075d54fec68a 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1511,17 +1511,17 @@ "packages": { "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@open-rpc/meta-schema": true, "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/meta-schema": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, "@open-rpc/schema-utils-js>is-url": true } }, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true } }, @@ -4584,9 +4584,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 135a96602db8..e80cc8460a42 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -1603,17 +1603,17 @@ "packages": { "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@open-rpc/meta-schema": true, "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/meta-schema": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, "@open-rpc/schema-utils-js>is-url": true } }, "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, "@metamask/rpc-errors>fast-safe-stringify": true, + "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true } }, @@ -4676,9 +4676,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index aa674f8b3cb2..e7ce64ceec23 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -2131,8 +2131,7 @@ "chokidar>normalize-path": true, "chokidar>readdirp": true, "del>is-glob": true, - "eslint>glob-parent": true, - "tsx>fsevents": true + "eslint>glob-parent": true } }, "chokidar>anymatch": { @@ -8884,13 +8883,6 @@ "typescript": true } }, - "tsx>fsevents": { - "globals": { - "console.assert": true, - "process.platform": true - }, - "native": true - }, "typescript": { "builtin": { "buffer.Buffer": true, From 4ae73774ce81b32694bedd847800806207e4af26 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 17 Oct 2024 13:47:35 -0700 Subject: [PATCH 060/148] bring in preview build from new core pr --- package.json | 4 +-- yarn.lock | 75 ++++++++-------------------------------------------- 2 files changed, 13 insertions(+), 66 deletions(-) diff --git a/package.json b/package.json index b322cddbabc3..8ec368405bef 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ "attributions:generate": "./development/generate-attributions.sh" }, "resolutions": { - "@metamask/controller-utils": "npm:@metamask-previews/controller-utils@11.3.0-preview-ada451da", + "@metamask/controller-utils": "npm:@metamask-previews/controller-utils@11.3.0-preview-afb2e2c0", "chokidar": "^3.6.0", "simple-update-notifier@^1.0.0": "^2.0.0", "@babel/core": "patch:@babel/core@npm%3A7.23.2#~/.yarn/patches/@babel-core-npm-7.23.2-b93f586907.patch", @@ -336,7 +336,7 @@ "@metamask/message-manager": "^10.1.0", "@metamask/message-signing-snap": "^0.3.3", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-00a41cb8", + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-afb2e2c0", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index 55fab34adb5f..dae55e3aa0dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4132,17 +4132,6 @@ __metadata: languageName: node linkType: hard -"@json-schema-tools/dereferencer@npm:^1.6.3": - version: 1.6.3 - resolution: "@json-schema-tools/dereferencer@npm:1.6.3" - dependencies: - "@json-schema-tools/reference-resolver": "npm:^1.2.6" - "@json-schema-tools/traverse": "npm:^1.10.4" - fast-safe-stringify: "npm:^2.1.1" - checksum: 10/da6ef5b82a8a9c3a7e62ffcab5c04c581f1e0f8165c0debdb272bb1e08ccd726107ee194487b8fa736cac00fb390b8df74bc1ad1b200eddbe25c98ee0d3d000b - languageName: node - linkType: hard - "@json-schema-tools/meta-schema@npm:1.6.19": version: 1.6.19 resolution: "@json-schema-tools/meta-schema@npm:1.6.19" @@ -4157,13 +4146,6 @@ __metadata: languageName: node linkType: hard -"@json-schema-tools/meta-schema@npm:^1.7.5": - version: 1.7.5 - resolution: "@json-schema-tools/meta-schema@npm:1.7.5" - checksum: 10/707dc3a285c26c37d00f418e9d0ef8a2ad1c23d4936ad5aab0ce94c9ae36a7a6125c4ca5048513af64b7e6e527b5472a1701d1f709c379acdd7ad12f6409d2cd - languageName: node - linkType: hard - "@json-schema-tools/reference-resolver@npm:1.2.6": version: 1.2.6 resolution: "@json-schema-tools/reference-resolver@npm:1.2.6" @@ -4184,13 +4166,6 @@ __metadata: languageName: node linkType: hard -"@json-schema-tools/traverse@npm:^1.10.4": - version: 1.10.4 - resolution: "@json-schema-tools/traverse@npm:1.10.4" - checksum: 10/0027bc90df01c5eeee0833e722b7320b53be8b5ce3f4e0e4a6e45713a38e6f88f21aba31e3dd973093ef75cd21a40c07fe8f112da8f49a7919b1c0e44c904d20 - languageName: node - linkType: hard - "@json-schema-tools/traverse@npm:^1.7.5, @json-schema-tools/traverse@npm:^1.7.8": version: 1.10.3 resolution: "@json-schema-tools/traverse@npm:1.10.3" @@ -5053,9 +5028,9 @@ __metadata: languageName: node linkType: hard -"@metamask/controller-utils@npm:@metamask-previews/controller-utils@11.3.0-preview-ada451da": - version: 11.3.0-preview-ada451da - resolution: "@metamask-previews/controller-utils@npm:11.3.0-preview-ada451da" +"@metamask/controller-utils@npm:@metamask-previews/controller-utils@11.3.0-preview-afb2e2c0": + version: 11.3.0-preview-afb2e2c0 + resolution: "@metamask-previews/controller-utils@npm:11.3.0-preview-afb2e2c0" dependencies: "@ethereumjs/util": "npm:^8.1.0" "@metamask/eth-query": "npm:^4.0.0" @@ -5066,7 +5041,7 @@ __metadata: bn.js: "npm:^5.2.1" eth-ens-namehash: "npm:^2.0.8" fast-deep-equal: "npm:^3.1.3" - checksum: 10/48c4ff542a080bf64dd45a78b6717eac73512f95d86e127a197166e652af79700db7cc81a23ebba602f6fe921498bf382ea138723f97ce287b94597a7507c3d7 + checksum: 10/0ce0d41c9c53b1fd5965c53e16e606a6889f19eb4b7eec1204fb3a11d5835b14fb30ef7b0b9085b7a7f460d54d222dc54264b89a0cafff9e58d3508f5fbe8bcf languageName: node linkType: hard @@ -5773,23 +5748,20 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-00a41cb8": - version: 0.0.0-preview-00a41cb8 - resolution: "@metamask-previews/multichain@npm:0.0.0-preview-00a41cb8" +"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-afb2e2c0": + version: 0.0.0-preview-afb2e2c0 + resolution: "@metamask-previews/multichain@npm:0.0.0-preview-afb2e2c0" dependencies: "@metamask/api-specs": "npm:^0.10.12" "@metamask/controller-utils": "npm:^11.3.0" "@metamask/eth-json-rpc-filters": "npm:^7.0.0" - "@metamask/rpc-errors": "npm:^6.3.1" - "@metamask/safe-event-emitter": "npm:^3.0.0" + "@metamask/rpc-errors": "npm:^7.0.0" "@metamask/utils": "npm:^9.1.0" - "@open-rpc/schema-utils-js": "npm:^2.0.5" - jsonschema: "npm:^1.2.4" lodash: "npm:^4.17.21" peerDependencies: "@metamask/network-controller": ^21.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/4fd73392e38201be03f3ff8dee10dc362b5474a8f9580ad6cea00c0d3249eceeeb21ec37c67e071124af329cf3d4baa42107115b43cacad758df7210057a86b9 + checksum: 10/40e7b66aa6f320e7001fc00d30f5734456ff9232fdf3c255c5fd7e860635ffa24437b297767f3de73f5b9d76853c39bea2a0f26976db81d157f85e4d5f494d86 languageName: node linkType: hard @@ -7022,13 +6994,6 @@ __metadata: languageName: node linkType: hard -"@open-rpc/meta-schema@npm:^1.14.9": - version: 1.14.9 - resolution: "@open-rpc/meta-schema@npm:1.14.9" - checksum: 10/51505dcf7aa1a2285c78953c9b33711cede5f2765aa37dcb9ee7756d689e2ff2a89cfc6039504f0569c52a805fb9aa18f30a7c02ad7a06e793c801e43b419104 - languageName: node - linkType: hard - "@open-rpc/mock-server@npm:^1.7.5": version: 1.7.5 resolution: "@open-rpc/mock-server@npm:1.7.5" @@ -7098,24 +7063,6 @@ __metadata: languageName: node linkType: hard -"@open-rpc/schema-utils-js@npm:^2.0.5": - version: 2.0.5 - resolution: "@open-rpc/schema-utils-js@npm:2.0.5" - dependencies: - "@json-schema-tools/dereferencer": "npm:^1.6.3" - "@json-schema-tools/meta-schema": "npm:^1.7.5" - "@json-schema-tools/reference-resolver": "npm:^1.2.6" - "@open-rpc/meta-schema": "npm:^1.14.9" - ajv: "npm:^6.10.0" - detect-node: "npm:^2.0.4" - fast-safe-stringify: "npm:^2.0.7" - fs-extra: "npm:^10.1.0" - is-url: "npm:^1.2.4" - isomorphic-fetch: "npm:^3.0.0" - checksum: 10/9e10215606e9a00a47b082c9cfd70d05bf0d38de6cf1c147246c545c6997375d94cd3caafe919b71178df58b5facadfd0dcc8b6857bf5e79c40e5e33683dd3d5 - languageName: node - linkType: hard - "@open-rpc/server-js@npm:1.9.3": version: 1.9.3 resolution: "@open-rpc/server-js@npm:1.9.3" @@ -19238,7 +19185,7 @@ __metadata: languageName: node linkType: hard -"fast-safe-stringify@npm:^2.0.6, fast-safe-stringify@npm:^2.0.7, fast-safe-stringify@npm:^2.1.1": +"fast-safe-stringify@npm:^2.0.6, fast-safe-stringify@npm:^2.0.7": version: 2.1.1 resolution: "fast-safe-stringify@npm:2.1.1" checksum: 10/dc1f063c2c6ac9533aee14d406441f86783a8984b2ca09b19c2fe281f9ff59d315298bc7bc22fd1f83d26fe19ef2f20e2ddb68e96b15040292e555c5ced0c1e4 @@ -26202,7 +26149,7 @@ __metadata: "@metamask/message-manager": "npm:^10.1.0" "@metamask/message-signing-snap": "npm:^0.3.3" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-00a41cb8" + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-afb2e2c0" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch" "@metamask/notification-controller": "npm:^6.0.0" From 16d8be4c05354a18fb9126cb387a8a54cfcff9aa Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 17 Oct 2024 14:13:10 -0700 Subject: [PATCH 061/148] lavamoat --- lavamoat/browserify/beta/policy.json | 84 ++++----------------------- lavamoat/browserify/flask/policy.json | 84 ++++----------------------- lavamoat/browserify/main/policy.json | 84 ++++----------------------- lavamoat/browserify/mmi/policy.json | 84 ++++----------------------- lavamoat/build-system/policy.json | 10 +++- 5 files changed, 49 insertions(+), 297 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 075d54fec68a..1269bd63e4d1 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1460,13 +1460,18 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/multichain>jsonschema": true, + "@metamask/message-manager>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, + "@metamask/message-manager>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1484,63 +1489,18 @@ }, "@metamask/multichain": { "globals": { - "console.error": true, "console.log": true }, "packages": { "@metamask/controller-utils": true, - "@metamask/eth-json-rpc-filters": true, "@metamask/multichain>@metamask/api-specs": true, - "@metamask/multichain>@metamask/rpc-errors": true, - "@metamask/multichain>@open-rpc/schema-utils-js": true, - "@metamask/multichain>jsonschema": true, "@metamask/permission-controller": true, - "@metamask/safe-event-emitter": true, + "@metamask/rpc-errors": true, "@metamask/utils": true, "browserify>assert": true, "lodash": true } }, - "@metamask/multichain>@metamask/rpc-errors": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/utils": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js": { - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/meta-schema": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, - "@open-rpc/schema-utils-js>is-url": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { - "globals": { - "console": true - }, - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "eslint>fast-deep-equal": true, - "uri-js": true - } - }, - "@metamask/multichain>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/name-controller": { "globals": { "fetch": true @@ -3015,33 +2975,6 @@ "crypto": true } }, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { - "packages": { - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, - "@open-rpc/test-coverage>isomorphic-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch": { - "globals": { - "fetch.bind": true - }, - "packages": { - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { - "globals": { - "AbortController": true, - "Blob": true, - "FileReader": true, - "FormData": true, - "URLSearchParams.prototype.isPrototypeOf": true, - "XMLHttpRequest": true, - "console.warn": true, - "define": true, - "setTimeout": true - } - }, "@popperjs/core": { "globals": { "Element": true, @@ -4584,6 +4517,9 @@ } }, "extension-port-stream": { + "globals": { + "console.log": true + }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 075d54fec68a..1269bd63e4d1 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1460,13 +1460,18 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/multichain>jsonschema": true, + "@metamask/message-manager>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, + "@metamask/message-manager>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1484,63 +1489,18 @@ }, "@metamask/multichain": { "globals": { - "console.error": true, "console.log": true }, "packages": { "@metamask/controller-utils": true, - "@metamask/eth-json-rpc-filters": true, "@metamask/multichain>@metamask/api-specs": true, - "@metamask/multichain>@metamask/rpc-errors": true, - "@metamask/multichain>@open-rpc/schema-utils-js": true, - "@metamask/multichain>jsonschema": true, "@metamask/permission-controller": true, - "@metamask/safe-event-emitter": true, + "@metamask/rpc-errors": true, "@metamask/utils": true, "browserify>assert": true, "lodash": true } }, - "@metamask/multichain>@metamask/rpc-errors": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/utils": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js": { - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/meta-schema": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, - "@open-rpc/schema-utils-js>is-url": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { - "globals": { - "console": true - }, - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "eslint>fast-deep-equal": true, - "uri-js": true - } - }, - "@metamask/multichain>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/name-controller": { "globals": { "fetch": true @@ -3015,33 +2975,6 @@ "crypto": true } }, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { - "packages": { - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, - "@open-rpc/test-coverage>isomorphic-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch": { - "globals": { - "fetch.bind": true - }, - "packages": { - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { - "globals": { - "AbortController": true, - "Blob": true, - "FileReader": true, - "FormData": true, - "URLSearchParams.prototype.isPrototypeOf": true, - "XMLHttpRequest": true, - "console.warn": true, - "define": true, - "setTimeout": true - } - }, "@popperjs/core": { "globals": { "Element": true, @@ -4584,6 +4517,9 @@ } }, "extension-port-stream": { + "globals": { + "console.log": true + }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 075d54fec68a..1269bd63e4d1 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1460,13 +1460,18 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/multichain>jsonschema": true, + "@metamask/message-manager>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, + "@metamask/message-manager>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1484,63 +1489,18 @@ }, "@metamask/multichain": { "globals": { - "console.error": true, "console.log": true }, "packages": { "@metamask/controller-utils": true, - "@metamask/eth-json-rpc-filters": true, "@metamask/multichain>@metamask/api-specs": true, - "@metamask/multichain>@metamask/rpc-errors": true, - "@metamask/multichain>@open-rpc/schema-utils-js": true, - "@metamask/multichain>jsonschema": true, "@metamask/permission-controller": true, - "@metamask/safe-event-emitter": true, + "@metamask/rpc-errors": true, "@metamask/utils": true, "browserify>assert": true, "lodash": true } }, - "@metamask/multichain>@metamask/rpc-errors": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/utils": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js": { - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/meta-schema": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, - "@open-rpc/schema-utils-js>is-url": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { - "globals": { - "console": true - }, - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "eslint>fast-deep-equal": true, - "uri-js": true - } - }, - "@metamask/multichain>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/name-controller": { "globals": { "fetch": true @@ -3015,33 +2975,6 @@ "crypto": true } }, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { - "packages": { - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, - "@open-rpc/test-coverage>isomorphic-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch": { - "globals": { - "fetch.bind": true - }, - "packages": { - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { - "globals": { - "AbortController": true, - "Blob": true, - "FileReader": true, - "FormData": true, - "URLSearchParams.prototype.isPrototypeOf": true, - "XMLHttpRequest": true, - "console.warn": true, - "define": true, - "setTimeout": true - } - }, "@popperjs/core": { "globals": { "Element": true, @@ -4584,6 +4517,9 @@ } }, "extension-port-stream": { + "globals": { + "console.log": true + }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index e80cc8460a42..d81eddcc5182 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -1552,13 +1552,18 @@ "@metamask/base-controller": true, "@metamask/controller-utils": true, "@metamask/eth-sig-util": true, - "@metamask/multichain>jsonschema": true, + "@metamask/message-manager>jsonschema": true, "@metamask/utils": true, "browserify>buffer": true, "uuid": true, "webpack>events": true } }, + "@metamask/message-manager>jsonschema": { + "packages": { + "browserify>url": true + } + }, "@metamask/message-signing-snap>@noble/ciphers": { "globals": { "TextDecoder": true, @@ -1576,63 +1581,18 @@ }, "@metamask/multichain": { "globals": { - "console.error": true, "console.log": true }, "packages": { "@metamask/controller-utils": true, - "@metamask/eth-json-rpc-filters": true, "@metamask/multichain>@metamask/api-specs": true, - "@metamask/multichain>@metamask/rpc-errors": true, - "@metamask/multichain>@open-rpc/schema-utils-js": true, - "@metamask/multichain>jsonschema": true, "@metamask/permission-controller": true, - "@metamask/safe-event-emitter": true, + "@metamask/rpc-errors": true, "@metamask/utils": true, "browserify>assert": true, "lodash": true } }, - "@metamask/multichain>@metamask/rpc-errors": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/utils": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js": { - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": true, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/meta-schema": true, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": true, - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/meta-schema": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true, - "@open-rpc/schema-utils-js>is-url": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>@json-schema-tools/dereferencer": { - "packages": { - "@metamask/rpc-errors>fast-safe-stringify": true, - "@open-rpc/schema-utils-js>@json-schema-tools/dereferencer>@json-schema-tools/traverse": true, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": true - } - }, - "@metamask/multichain>@open-rpc/schema-utils-js>ajv": { - "globals": { - "console": true - }, - "packages": { - "@metamask/multichain>@open-rpc/schema-utils-js>ajv>json-schema-traverse": true, - "@metamask/snaps-utils>fast-json-stable-stringify": true, - "eslint>fast-deep-equal": true, - "uri-js": true - } - }, - "@metamask/multichain>jsonschema": { - "packages": { - "browserify>url": true - } - }, "@metamask/name-controller": { "globals": { "fetch": true @@ -3107,33 +3067,6 @@ "crypto": true } }, - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver": { - "packages": { - "@open-rpc/schema-utils-js>@json-schema-tools/reference-resolver>@json-schema-spec/json-pointer": true, - "@open-rpc/test-coverage>isomorphic-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch": { - "globals": { - "fetch.bind": true - }, - "packages": { - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": true - } - }, - "@open-rpc/test-coverage>isomorphic-fetch>whatwg-fetch": { - "globals": { - "AbortController": true, - "Blob": true, - "FileReader": true, - "FormData": true, - "URLSearchParams.prototype.isPrototypeOf": true, - "XMLHttpRequest": true, - "console.warn": true, - "define": true, - "setTimeout": true - } - }, "@popperjs/core": { "globals": { "Element": true, @@ -4676,6 +4609,9 @@ } }, "extension-port-stream": { + "globals": { + "console.log": true + }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index e7ce64ceec23..aa674f8b3cb2 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -2131,7 +2131,8 @@ "chokidar>normalize-path": true, "chokidar>readdirp": true, "del>is-glob": true, - "eslint>glob-parent": true + "eslint>glob-parent": true, + "tsx>fsevents": true } }, "chokidar>anymatch": { @@ -8883,6 +8884,13 @@ "typescript": true } }, + "tsx>fsevents": { + "globals": { + "console.assert": true, + "process.platform": true + }, + "native": true + }, "typescript": { "builtin": { "buffer.Buffer": true, From 2735fa56e61c7091a54b8cd76c1fbc7d2ddbd16d Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Thu, 17 Oct 2024 21:31:57 +0000 Subject: [PATCH 062/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 3 --- lavamoat/browserify/flask/policy.json | 3 --- lavamoat/browserify/main/policy.json | 3 --- lavamoat/browserify/mmi/policy.json | 3 --- lavamoat/build-system/policy.json | 10 +--------- 5 files changed, 1 insertion(+), 21 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 1269bd63e4d1..c8a8d91e9e70 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -4517,9 +4517,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 1269bd63e4d1..c8a8d91e9e70 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -4517,9 +4517,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 1269bd63e4d1..c8a8d91e9e70 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -4517,9 +4517,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index d81eddcc5182..24070b2d4fbe 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -4609,9 +4609,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index aa674f8b3cb2..e7ce64ceec23 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -2131,8 +2131,7 @@ "chokidar>normalize-path": true, "chokidar>readdirp": true, "del>is-glob": true, - "eslint>glob-parent": true, - "tsx>fsevents": true + "eslint>glob-parent": true } }, "chokidar>anymatch": { @@ -8884,13 +8883,6 @@ "typescript": true } }, - "tsx>fsevents": { - "globals": { - "console.assert": true, - "process.platform": true - }, - "native": true - }, "typescript": { "builtin": { "buffer.Buffer": true, From 91255c4f0363a4a5f4f382e886a733d3080948d4 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 17 Oct 2024 15:53:59 -0700 Subject: [PATCH 063/148] bring in new preview build for listAccounts() in permission validator --- .../controllers/permissions/specifications.js | 6 +++--- app/scripts/metamask-controller.js | 2 +- package.json | 4 ++-- yarn.lock | 18 +++++++++--------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index e3cb7fd2199c..21f2349598cc 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -59,7 +59,7 @@ export const getCaveatSpecifications = () => { * }} options - Options bag. * @param options.getAllAccounts - A function that returns all Ethereum accounts * in the current MetaMask instance. - * @param options.getInternalAccounts - A function that returns the + * @param options.listAccounts - A function that returns the * `AccountsController` internalAccount objects for all accounts in the * @param options.captureKeyringTypesWithMissingIdentities - A function that * captures extra error information about the "Missing identity for address" @@ -67,7 +67,7 @@ export const getCaveatSpecifications = () => { * current MetaMask instance. */ export const getPermissionSpecifications = ({ - getInternalAccounts, + listAccounts, findNetworkClientIdByChainId, }) => { return { @@ -75,7 +75,7 @@ export const getPermissionSpecifications = ({ caip25EndowmentBuilder.specificationBuilder({ methodHooks: { findNetworkClientIdByChainId, - getInternalAccounts, + listAccounts, }, }), }; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 723ae03b6ca6..68e8f303e8e4 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1214,7 +1214,7 @@ export default class MetamaskController extends EventEmitter { caveatSpecifications: getCaveatSpecifications(), permissionSpecifications: { ...getPermissionSpecifications({ - getInternalAccounts: this.accountsController.listAccounts.bind( + listAccounts: this.accountsController.listAccounts.bind( this.accountsController, ), findNetworkClientIdByChainId: diff --git a/package.json b/package.json index 8ec368405bef..a65292d952f9 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ "attributions:generate": "./development/generate-attributions.sh" }, "resolutions": { - "@metamask/controller-utils": "npm:@metamask-previews/controller-utils@11.3.0-preview-afb2e2c0", + "@metamask/controller-utils": "npm:@metamask-previews/controller-utils@11.3.0-preview-dae4f73d", "chokidar": "^3.6.0", "simple-update-notifier@^1.0.0": "^2.0.0", "@babel/core": "patch:@babel/core@npm%3A7.23.2#~/.yarn/patches/@babel-core-npm-7.23.2-b93f586907.patch", @@ -336,7 +336,7 @@ "@metamask/message-manager": "^10.1.0", "@metamask/message-signing-snap": "^0.3.3", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-afb2e2c0", + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-dae4f73d", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index fa9437a4bda3..cd6489729fd8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5028,9 +5028,9 @@ __metadata: languageName: node linkType: hard -"@metamask/controller-utils@npm:@metamask-previews/controller-utils@11.3.0-preview-afb2e2c0": - version: 11.3.0-preview-afb2e2c0 - resolution: "@metamask-previews/controller-utils@npm:11.3.0-preview-afb2e2c0" +"@metamask/controller-utils@npm:@metamask-previews/controller-utils@11.3.0-preview-dae4f73d": + version: 11.3.0-preview-dae4f73d + resolution: "@metamask-previews/controller-utils@npm:11.3.0-preview-dae4f73d" dependencies: "@ethereumjs/util": "npm:^8.1.0" "@metamask/eth-query": "npm:^4.0.0" @@ -5041,7 +5041,7 @@ __metadata: bn.js: "npm:^5.2.1" eth-ens-namehash: "npm:^2.0.8" fast-deep-equal: "npm:^3.1.3" - checksum: 10/0ce0d41c9c53b1fd5965c53e16e606a6889f19eb4b7eec1204fb3a11d5835b14fb30ef7b0b9085b7a7f460d54d222dc54264b89a0cafff9e58d3508f5fbe8bcf + checksum: 10/568559942f0c70307473b8078b37f4c82b4bc13d8b5ce7952eed5ac5bed5bc58f1a30e382b3d71f85fd704408c5058e88150c5815a9abd004cd8a811e9c6fd7a languageName: node linkType: hard @@ -5748,9 +5748,9 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-afb2e2c0": - version: 0.0.0-preview-afb2e2c0 - resolution: "@metamask-previews/multichain@npm:0.0.0-preview-afb2e2c0" +"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-dae4f73d": + version: 0.0.0-preview-dae4f73d + resolution: "@metamask-previews/multichain@npm:0.0.0-preview-dae4f73d" dependencies: "@metamask/api-specs": "npm:^0.10.12" "@metamask/controller-utils": "npm:^11.3.0" @@ -5761,7 +5761,7 @@ __metadata: peerDependencies: "@metamask/network-controller": ^21.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/40e7b66aa6f320e7001fc00d30f5734456ff9232fdf3c255c5fd7e860635ffa24437b297767f3de73f5b9d76853c39bea2a0f26976db81d157f85e4d5f494d86 + checksum: 10/3531a9802694b7581d132513e06cd569ccfb40c693b03f272941d007d1314a44283851855540a6c879c17e138313115ef9500f8d60de3a75540a20e1f3238549 languageName: node linkType: hard @@ -26149,7 +26149,7 @@ __metadata: "@metamask/message-manager": "npm:^10.1.0" "@metamask/message-signing-snap": "npm:^0.3.3" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-afb2e2c0" + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-dae4f73d" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch" "@metamask/notification-controller": "npm:^6.0.0" From 773a7445c0f3e95b947b53aa22f00094ade12398 Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Fri, 18 Oct 2024 14:16:01 +0000 Subject: [PATCH 064/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 55 +--------------- lavamoat/browserify/flask/policy.json | 90 +-------------------------- lavamoat/browserify/main/policy.json | 90 +-------------------------- lavamoat/browserify/mmi/policy.json | 90 +-------------------------- 4 files changed, 12 insertions(+), 313 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index d14793f552b8..e2be5963bffb 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1949,6 +1949,7 @@ "@metamask/permission-controller>@metamask/json-rpc-engine": true, "@metamask/permission-controller>@metamask/rpc-errors": true, "@metamask/permission-controller>nanoid": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1963,24 +1964,9 @@ }, "@metamask/permission-controller>@metamask/json-rpc-engine": { "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/safe-event-emitter": true, + "@metamask/utils": true } }, "@metamask/permission-controller>@metamask/rpc-errors": { @@ -2730,41 +2716,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index d8b5b2e6f785..e2be5963bffb 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1949,6 +1949,7 @@ "@metamask/permission-controller>@metamask/json-rpc-engine": true, "@metamask/permission-controller>@metamask/rpc-errors": true, "@metamask/permission-controller>nanoid": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1963,24 +1964,9 @@ }, "@metamask/permission-controller>@metamask/json-rpc-engine": { "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/safe-event-emitter": true, + "@metamask/utils": true } }, "@metamask/permission-controller>@metamask/rpc-errors": { @@ -2730,41 +2716,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2831,41 +2782,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index d8b5b2e6f785..e2be5963bffb 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1949,6 +1949,7 @@ "@metamask/permission-controller>@metamask/json-rpc-engine": true, "@metamask/permission-controller>@metamask/rpc-errors": true, "@metamask/permission-controller>nanoid": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1963,24 +1964,9 @@ }, "@metamask/permission-controller>@metamask/json-rpc-engine": { "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/safe-event-emitter": true, + "@metamask/utils": true } }, "@metamask/permission-controller>@metamask/rpc-errors": { @@ -2730,41 +2716,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2831,41 +2782,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 26ac2ee4ec1e..02176ebb0b22 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -2041,6 +2041,7 @@ "@metamask/permission-controller>@metamask/json-rpc-engine": true, "@metamask/permission-controller>@metamask/rpc-errors": true, "@metamask/permission-controller>nanoid": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -2055,24 +2056,9 @@ }, "@metamask/permission-controller>@metamask/json-rpc-engine": { "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/safe-event-emitter": true, + "@metamask/utils": true } }, "@metamask/permission-controller>@metamask/rpc-errors": { @@ -2822,41 +2808,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2923,41 +2874,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, From 5262186fdce58cfc4a50a27d34e517786ced9f37 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 18 Oct 2024 12:43:47 -0500 Subject: [PATCH 065/148] fix switch-custom-network test failure --- test/e2e/fixture-builder.js | 17 ++++++++++++++--- .../tests/network/switch-custom-network.spec.js | 3 +++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index bc7fb0a6c4e2..db04b5cd7b82 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -461,7 +461,6 @@ class FixtureBuilder { accounts: [ `eip155:1337:${selectedAccount.toLowerCase()}`, 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', - `eip155:1337:${ERC_4337_ACCOUNT.toLowerCase()}`, ], }, }, @@ -631,8 +630,8 @@ class FixtureBuilder { withPreferencesControllerAdditionalAccountIdentities() { return this.withPreferencesController({ identities: { - '0x5cfe73b6021e818b776b421b1c4db2474086a7e1': { - address: '0x5cfe73b6021e818b776b421b1c4db2474086a7e1', + [DEFAULT_FIXTURE_ACCOUNT]: { + address: DEFAULT_FIXTURE_ACCOUNT, lastSelected: 1665507600000, name: 'Account 1', }, @@ -641,6 +640,11 @@ class FixtureBuilder { lastSelected: 1665507800000, name: 'Account 2', }, + [ERC_4337_ACCOUNT]: { + address: ERC_4337_ACCOUNT, + lastSelected: 1665507600000, + name: 'Account 4', + }, }, }); } @@ -818,6 +822,13 @@ class FixtureBuilder { }, }, }, + '74c55111-be4f-48aa-a49c-55995c8a1b26': { + id: '74c55111-be4f-48aa-a49c-55995c8a1b26', + address: ERC_4337_ACCOUNT, + options: {}, + methods: [], + type: 'eip155:erc4337', + }, }, }, selectedAccount: 'd5e45e4a-3b04-4a09-a5e1-39762e5c6be4', diff --git a/test/e2e/tests/network/switch-custom-network.spec.js b/test/e2e/tests/network/switch-custom-network.spec.js index 09dedc3a62da..c5eb780a29db 100644 --- a/test/e2e/tests/network/switch-custom-network.spec.js +++ b/test/e2e/tests/network/switch-custom-network.spec.js @@ -13,6 +13,9 @@ describe('Switch ethereum chain', function () { { dapp: true, fixtures: new FixtureBuilder() + .withKeyringControllerAdditionalAccountVault() + .withPreferencesControllerAdditionalAccountIdentities() + .withAccountsControllerAdditionalAccountIdentities() .withPermissionControllerConnectedToTestDapp() .build(), ganacheOptions: generateGanacheOptions({ From 832e61e969f57996743b80fd16ef5293c0320ee4 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 18 Oct 2024 12:52:38 -0500 Subject: [PATCH 066/148] update json-rpc-engine type import --- .../lib/rpc-method-middleware/handlers/request-accounts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts index ed63ef1a3916..1ce6c60e7f21 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts @@ -23,7 +23,7 @@ import { import { JsonRpcEngineEndCallback, JsonRpcEngineNextCallback, -} from 'json-rpc-engine'; +} from '@metamask/json-rpc-engine'; import { MESSAGE_TYPE } from '../../../../../shared/constants/app'; import { MetaMetricsEventName, From 20ff891204a38cabd274e93ea7569d5ac03c41d8 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 18 Oct 2024 13:03:35 -0500 Subject: [PATCH 067/148] fix other json-rpc-engine imports --- .../lib/rpc-method-middleware/handlers/wallet-getPermissions.ts | 2 +- .../rpc-method-middleware/handlers/wallet-requestPermissions.ts | 2 +- .../rpc-method-middleware/handlers/wallet-revokePermissions.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts index c282a20b0e63..fa95d6b27773 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts @@ -13,7 +13,7 @@ import { import { AsyncJsonRpcEngineNextCallback, JsonRpcEngineEndCallback, -} from 'json-rpc-engine'; +} from '@metamask/json-rpc-engine'; import { Json, JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils'; import { PermissionNames } from '../../../controllers/permissions'; import { diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index e3f30d02ab5c..a059900a0622 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -26,7 +26,7 @@ import { import { AsyncJsonRpcEngineNextCallback, JsonRpcEngineEndCallback, -} from 'json-rpc-engine'; +} from '@metamask/json-rpc-engine'; import { CaveatTypes, RestrictedMethods, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts index b51fbd3d718e..dd59c0d7cce3 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts @@ -19,7 +19,7 @@ import { import { AsyncJsonRpcEngineNextCallback, JsonRpcEngineEndCallback, -} from 'json-rpc-engine'; +} from '@metamask/json-rpc-engine'; import { RestrictedMethods } from '../../../../../shared/constants/permissions'; import { PermissionNames } from '../../../controllers/permissions'; From f5df08d88152eec59767dd55ef0dceaf5d6d09ed Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 18 Oct 2024 13:27:00 -0500 Subject: [PATCH 068/148] add fixtures to fix test --- test/e2e/tests/network/deprecated-networks.spec.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/e2e/tests/network/deprecated-networks.spec.js b/test/e2e/tests/network/deprecated-networks.spec.js index 26c2388e4b51..80a11226a4df 100644 --- a/test/e2e/tests/network/deprecated-networks.spec.js +++ b/test/e2e/tests/network/deprecated-networks.spec.js @@ -51,6 +51,9 @@ describe('Deprecated networks', function () { { dapp: true, fixtures: new FixtureBuilder() + .withKeyringControllerAdditionalAccountVault() + .withPreferencesControllerAdditionalAccountIdentities() + .withAccountsControllerAdditionalAccountIdentities() .withPermissionControllerConnectedToTestDapp() .withPreferencesController({ useSafeChainsListValidation: false }) .build(), @@ -130,6 +133,9 @@ describe('Deprecated networks', function () { { dapp: true, fixtures: new FixtureBuilder() + .withKeyringControllerAdditionalAccountVault() + .withPreferencesControllerAdditionalAccountIdentities() + .withAccountsControllerAdditionalAccountIdentities() .withPermissionControllerConnectedToTestDapp() .withPreferencesController({ useSafeChainsListValidation: false }) .build(), @@ -209,6 +215,9 @@ describe('Deprecated networks', function () { { dapp: true, fixtures: new FixtureBuilder() + .withKeyringControllerAdditionalAccountVault() + .withPreferencesControllerAdditionalAccountIdentities() + .withAccountsControllerAdditionalAccountIdentities() .withPermissionControllerConnectedToTestDapp() .withPreferencesController({ useSafeChainsListValidation: false }) .build(), From e2d83050ec1eccbc7de19f913e179e11fef4c5e5 Mon Sep 17 00:00:00 2001 From: Shane Date: Fri, 18 Oct 2024 15:28:04 -0400 Subject: [PATCH 069/148] fix: add snap dummy descriptions copied from ethereum provider and get revoke e2e test passing and snaps permissions unit tests passing (#27965) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27965?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** image ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- app/scripts/metamask-controller.js | 1 + shared/constants/snaps/permissions.ts | 9 +++++++-- test/e2e/snaps/test-snap-revoke-perm.spec.js | 5 ++--- ui/helpers/utils/permission.js | 5 +++++ 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 33997ab115cf..c6151aa3acbc 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1336,6 +1336,7 @@ export default class MetamaskController extends EventEmitter { const requireAllowlist = process.env.REQUIRE_SNAPS_ALLOWLIST; this.snapController = new SnapController({ + dynamicPermissions: ['endowment:caip25'], environmentEndowmentPermissions: Object.values(EndowmentPermissions), excludedPermissions: { ...ExcludedSnapPermissions, diff --git a/shared/constants/snaps/permissions.ts b/shared/constants/snaps/permissions.ts index 837beea4d9ff..58f411ea4903 100644 --- a/shared/constants/snaps/permissions.ts +++ b/shared/constants/snaps/permissions.ts @@ -20,6 +20,11 @@ export const ExcludedSnapPermissions = Object.freeze({ 'eth_accounts is disabled. For more information please see https://github.com/MetaMask/snaps/issues/990.', }); -export const ExcludedSnapEndowments = Object.freeze({}); +export const ExcludedSnapEndowments = Object.freeze({ + 'endowment:caip25': 'endowment:caip25', +}); -export const DynamicSnapPermissions = Object.freeze(['eth_accounts']); +export const DynamicSnapPermissions = Object.freeze([ + 'eth_accounts', + 'endowment:caip25', +]); diff --git a/test/e2e/snaps/test-snap-revoke-perm.spec.js b/test/e2e/snaps/test-snap-revoke-perm.spec.js index c75294ed5ec6..e0fdcd9291cb 100644 --- a/test/e2e/snaps/test-snap-revoke-perm.spec.js +++ b/test/e2e/snaps/test-snap-revoke-perm.spec.js @@ -8,8 +8,7 @@ const { const FixtureBuilder = require('../fixture-builder'); const { TEST_SNAPS_WEBSITE_URL } = require('./enums'); -// TODO: Resolve this before merging. I'm sure the linter will make sure of it though -describe.skip('Test Snap revoke permission', function () { +describe('Test Snap revoke permission', function () { it('can revoke a permission', async function () { await withFixtures( { @@ -119,7 +118,7 @@ describe.skip('Test Snap revoke permission', function () { }); // try to click on options menu - await driver.clickElement('[data-testid="eth_accounts"]'); + await driver.clickElement('[data-testid="endowment:caip25"]'); // try to click on revoke permission await driver.clickElement({ diff --git a/ui/helpers/utils/permission.js b/ui/helpers/utils/permission.js index b1d1856c40df..2924474f795c 100644 --- a/ui/helpers/utils/permission.js +++ b/ui/helpers/utils/permission.js @@ -55,6 +55,11 @@ export const PERMISSION_DESCRIPTIONS = deepFreeze({ leftIcon: IconName.Eye, weight: PermissionWeight.eth_accounts, }), + [EndowmentPermissions['endowment:caip25']]: ({ t }) => ({ + label: t('permission_ethereumAccounts'), + leftIcon: IconName.Eye, + weight: PermissionWeight.eth_accounts, + }), [PermissionNames.permittedChains]: ({ t }) => ({ label: t('permission_walletSwitchEthereumChain'), leftIcon: IconName.Wifi, From ac192fcaeb3e48925630fbecbf414b418f9d044e Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 21 Oct 2024 09:56:01 -0700 Subject: [PATCH 070/148] lavamoat --- lavamoat/browserify/beta/policy.json | 3 +++ lavamoat/browserify/flask/policy.json | 3 +++ lavamoat/browserify/main/policy.json | 3 +++ lavamoat/browserify/mmi/policy.json | 3 +++ lavamoat/build-system/policy.json | 10 +++++++++- 5 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index e2be5963bffb..460aa15ad448 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -4569,6 +4569,9 @@ } }, "extension-port-stream": { + "globals": { + "console.log": true + }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index e2be5963bffb..460aa15ad448 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -4569,6 +4569,9 @@ } }, "extension-port-stream": { + "globals": { + "console.log": true + }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index e2be5963bffb..460aa15ad448 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -4569,6 +4569,9 @@ } }, "extension-port-stream": { + "globals": { + "console.log": true + }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 02176ebb0b22..7c554ae4894c 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -4661,6 +4661,9 @@ } }, "extension-port-stream": { + "globals": { + "console.log": true + }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index e7ce64ceec23..aa674f8b3cb2 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -2131,7 +2131,8 @@ "chokidar>normalize-path": true, "chokidar>readdirp": true, "del>is-glob": true, - "eslint>glob-parent": true + "eslint>glob-parent": true, + "tsx>fsevents": true } }, "chokidar>anymatch": { @@ -8883,6 +8884,13 @@ "typescript": true } }, + "tsx>fsevents": { + "globals": { + "console.assert": true, + "process.platform": true + }, + "native": true + }, "typescript": { "builtin": { "buffer.Buffer": true, From d151b63b435fb86e7495593813f5d64a01115b45 Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Mon, 21 Oct 2024 18:20:52 +0000 Subject: [PATCH 071/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 3 --- lavamoat/browserify/flask/policy.json | 3 --- lavamoat/browserify/main/policy.json | 3 --- lavamoat/browserify/mmi/policy.json | 3 --- lavamoat/build-system/policy.json | 10 +--------- 5 files changed, 1 insertion(+), 21 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 460aa15ad448..e2be5963bffb 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -4569,9 +4569,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 460aa15ad448..e2be5963bffb 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -4569,9 +4569,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 460aa15ad448..e2be5963bffb 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -4569,9 +4569,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 7c554ae4894c..02176ebb0b22 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -4661,9 +4661,6 @@ } }, "extension-port-stream": { - "globals": { - "console.log": true - }, "packages": { "browserify>buffer": true, "extension-port-stream>readable-stream": true diff --git a/lavamoat/build-system/policy.json b/lavamoat/build-system/policy.json index aa674f8b3cb2..e7ce64ceec23 100644 --- a/lavamoat/build-system/policy.json +++ b/lavamoat/build-system/policy.json @@ -2131,8 +2131,7 @@ "chokidar>normalize-path": true, "chokidar>readdirp": true, "del>is-glob": true, - "eslint>glob-parent": true, - "tsx>fsevents": true + "eslint>glob-parent": true } }, "chokidar>anymatch": { @@ -8884,13 +8883,6 @@ "typescript": true } }, - "tsx>fsevents": { - "globals": { - "console.assert": true, - "process.platform": true - }, - "native": true - }, "typescript": { "builtin": { "buffer.Buffer": true, From 9b023f0bcc8f8bc58f49dd332c373632e2048809 Mon Sep 17 00:00:00 2001 From: jiexi Date: Mon, 21 Oct 2024 12:37:29 -0700 Subject: [PATCH 072/148] Caip25/fix snaps signature disconnect (#27998) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/27998?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- test/e2e/page-objects/flows/sign.flow.ts | 7 ++++++- .../snap-account-signatures-and-disconnects.spec.ts | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/test/e2e/page-objects/flows/sign.flow.ts b/test/e2e/page-objects/flows/sign.flow.ts index c7d03bb4f96e..ec267ac23887 100644 --- a/test/e2e/page-objects/flows/sign.flow.ts +++ b/test/e2e/page-objects/flows/sign.flow.ts @@ -1,5 +1,5 @@ import { Driver } from '../../webdriver/driver'; -import { WINDOW_TITLES } from '../../helpers'; +import { veryLargeDelayMs, WINDOW_TITLES } from '../../helpers'; import SnapSimpleKeyringPage from '../pages/snap-simple-keyring-page'; import TestDapp from '../pages/test-dapp'; @@ -20,6 +20,7 @@ export const personalSignWithSnapAccount = async ( const testDapp = new TestDapp(driver); await testDapp.check_pageIsLoaded(); await testDapp.personalSign(); + await driver.delay(veryLargeDelayMs); if (!isSyncFlow) { await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await new SnapSimpleKeyringPage(driver).approveRejectSnapAccountTransaction( @@ -53,6 +54,7 @@ export const signTypedDataWithSnapAccount = async ( const testDapp = new TestDapp(driver); await testDapp.check_pageIsLoaded(); await testDapp.signTypedData(); + await driver.delay(veryLargeDelayMs); if (!isSyncFlow) { await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await new SnapSimpleKeyringPage(driver).approveRejectSnapAccountTransaction( @@ -86,6 +88,7 @@ export const signTypedDataV3WithSnapAccount = async ( const testDapp = new TestDapp(driver); await testDapp.check_pageIsLoaded(); await testDapp.signTypedDataV3(); + await driver.delay(veryLargeDelayMs); if (!isSyncFlow) { await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await new SnapSimpleKeyringPage(driver).approveRejectSnapAccountTransaction( @@ -119,6 +122,7 @@ export const signTypedDataV4WithSnapAccount = async ( const testDapp = new TestDapp(driver); await testDapp.check_pageIsLoaded(); await testDapp.signTypedDataV4(); + await driver.delay(veryLargeDelayMs); if (!isSyncFlow) { await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await new SnapSimpleKeyringPage(driver).approveRejectSnapAccountTransaction( @@ -152,6 +156,7 @@ export const signPermitWithSnapAccount = async ( const testDapp = new TestDapp(driver); await testDapp.check_pageIsLoaded(); await testDapp.signPermit(); + await driver.delay(veryLargeDelayMs); if (!isSyncFlow) { await driver.switchToWindowWithTitle(WINDOW_TITLES.Dialog); await new SnapSimpleKeyringPage(driver).approveRejectSnapAccountTransaction( diff --git a/test/e2e/tests/account/snap-account-signatures-and-disconnects.spec.ts b/test/e2e/tests/account/snap-account-signatures-and-disconnects.spec.ts index 7398747671c7..e77ace3b9f02 100644 --- a/test/e2e/tests/account/snap-account-signatures-and-disconnects.spec.ts +++ b/test/e2e/tests/account/snap-account-signatures-and-disconnects.spec.ts @@ -20,9 +20,6 @@ describe('Snap Account Signatures and Disconnects @no-mmi', function (this: Suit { dapp: true, fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDapp({ - restrictReturnedAccounts: false, - }) .build(), title: this.test?.fullTitle(), }, @@ -49,9 +46,12 @@ describe('Snap Account Signatures and Disconnects @no-mmi', function (this: Suit await experimentalSettings.check_pageIsLoaded(); await experimentalSettings.toggleRedesignedSignature(); - // Open the Test Dapp and signTypedDataV3 + // Open the Test Dapp and connect const testDapp = new TestDapp(driver); await testDapp.openTestDappPage(); + await testDapp.connectAccount(newPublicKey); + + // SignedTypedDataV3 with Test Dapp await signTypedDataV3WithSnapAccount(driver, newPublicKey, false, true); // Disconnect from Test Dapp and reconnect to Test Dapp From b1156e61df160bfa46431179767bac2e7de0d8d6 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 21 Oct 2024 12:40:29 -0700 Subject: [PATCH 073/148] lint --- .../account/snap-account-signatures-and-disconnects.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/e2e/tests/account/snap-account-signatures-and-disconnects.spec.ts b/test/e2e/tests/account/snap-account-signatures-and-disconnects.spec.ts index e77ace3b9f02..70e1ab7c5dca 100644 --- a/test/e2e/tests/account/snap-account-signatures-and-disconnects.spec.ts +++ b/test/e2e/tests/account/snap-account-signatures-and-disconnects.spec.ts @@ -19,8 +19,7 @@ describe('Snap Account Signatures and Disconnects @no-mmi', function (this: Suit await withFixtures( { dapp: true, - fixtures: new FixtureBuilder() - .build(), + fixtures: new FixtureBuilder().build(), title: this.test?.fullTitle(), }, async ({ driver }: { driver: Driver }) => { From 879b30e489af4d23ac311738d9c89e9b9e864381 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 21 Oct 2024 12:58:52 -0700 Subject: [PATCH 074/148] Fix flask/user-operations.spec.ts --- test/e2e/flask/user-operations.spec.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/e2e/flask/user-operations.spec.ts b/test/e2e/flask/user-operations.spec.ts index be7141444c97..25c7124c489b 100644 --- a/test/e2e/flask/user-operations.spec.ts +++ b/test/e2e/flask/user-operations.spec.ts @@ -24,6 +24,7 @@ import { Driver } from '../webdriver/driver'; import { Bundler } from '../bundler'; import { SWAP_TEST_ETH_USDC_TRADES_MOCK } from '../../data/mock-data'; import { Mockttp } from '../mock-e2e'; +import TestDapp from '../page-objects/pages/test-dapp'; enum TransactionDetailRowIndex { Nonce = 0, @@ -204,9 +205,7 @@ async function withAccountSnap( ) { await withFixtures( { - fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDapp() - .build(), + fixtures: new FixtureBuilder().build(), title, useBundler: true, usePaymaster: Boolean(paymaster), @@ -239,7 +238,10 @@ async function withAccountSnap( ERC_4337_ACCOUNT_SALT, ); - await driver.closeWindow(); + const testDapp = new TestDapp(driver); + await testDapp.openTestDappPage(); + await testDapp.connectAccount(ERC_4337_ACCOUNT); + await driver.switchToWindowWithTitle( WINDOW_TITLES.ExtensionInFullScreenView, ); From 25bfb4f2aaf7900c56548fe73020a49ca50c7ae0 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 21 Oct 2024 13:00:00 -0700 Subject: [PATCH 075/148] Remove html-report-multichain/ --- html-report-multichain/index.html | 132 ------------------------------ 1 file changed, 132 deletions(-) delete mode 100644 html-report-multichain/index.html diff --git a/html-report-multichain/index.html b/html-report-multichain/index.html deleted file mode 100644 index 7d2eb184af27..000000000000 --- a/html-report-multichain/index.html +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - - - OpenRPC API Test HTML Reporter - - - - -
- - From e10b65fb8cc8b3b8f6cd43a4a643dd66f087938c Mon Sep 17 00:00:00 2001 From: jiexi Date: Tue, 22 Oct 2024 15:25:04 -0700 Subject: [PATCH 076/148] Jl/caip25 permission migration/metamask controller test (#28012) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Improve test coverage of MetamaskController [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28012?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- app/scripts/metamask-controller.test.js | 364 ++++++++++++++++++++++++ 1 file changed, 364 insertions(+) diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 2b406ca26bf4..d903abe472d3 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -29,6 +29,10 @@ import { import ObjectMultiplex from '@metamask/object-multiplex'; import { TrezorKeyring } from '@metamask/eth-trezor-keyring'; import { LedgerKeyring } from '@metamask/eth-ledger-bridge-keyring'; +import { + Caip25CaveatType, + Caip25EndowmentPermissionName, +} from '@metamask/multichain'; import { createTestProviderTools } from '../../test/stub/provider'; import { HardwareDeviceNames } from '../../shared/constants/hardware-wallets'; import { KeyringType } from '../../shared/constants/keyring'; @@ -810,6 +814,366 @@ describe('MetaMaskController', () => { }); }); + describe('#getPermittedAccounts', () => { + it('gets the CAIP-25 caveat value for the origin', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue(); + + metamaskController.getPermittedAccounts('test.com'); + + expect( + metamaskController.permissionController.getCaveat, + ).toHaveBeenCalledWith( + 'test.com', + Caip25EndowmentPermissionName, + Caip25CaveatType, + ); + }); + + it('returns empty array if there is no CAIP-25 permission for the origin', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockImplementation(() => { + throw new Error('no caveat'); + }); + + expect( + metamaskController.getPermittedAccounts('test.com'), + ).toStrictEqual([]); + }); + + it('returns the eth accounts from the CAIP-25 permission', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + methods: [], + notifications: [], + accounts: ['eip155:1:0xdead', 'eip155:1:0xbeef'], + }, + }, + }, + }); + + expect( + metamaskController.getPermittedAccounts('test.com'), + ).toStrictEqual(['0xdead', '0xbeef']); + }); + }); + + describe('#getPermittedAccountsSorted', () => { + it('gets the permitted accounts for the origin', async () => { + jest + .spyOn(metamaskController, 'getPermittedAccounts') + .mockReturnValue([]); + + await metamaskController.getPermittedAccountsSorted('test.com'); + + expect(metamaskController.getPermittedAccounts).toHaveBeenCalledWith( + 'test.com', + ); + }); + + it('gets all evm accounts sorted by most recently used', async () => { + jest + .spyOn(metamaskController, 'getPermittedAccounts') + .mockReturnValue([]); + jest + .spyOn(metamaskController, 'getAllEvmAccountsSorted') + .mockResolvedValue([]); + + await metamaskController.getPermittedAccountsSorted('test.com'); + + expect(metamaskController.getAllEvmAccountsSorted).toHaveBeenCalled(); + }); + + it('returns the permitted accounts for the origin sorted by most recently used', async () => { + jest + .spyOn(metamaskController, 'getPermittedAccounts') + .mockReturnValue(['0x1', '0x3', '0x5']); + jest + .spyOn(metamaskController, 'getAllEvmAccountsSorted') + .mockResolvedValue([]); + + await metamaskController.getPermittedAccountsSorted('test.com'); + + expect(metamaskController.getAllEvmAccountsSorted).toHaveBeenCalled(); + }); + }); + + describe('#requestPermissionApprovalForOrigin', () => { + it('requests permissions for the origin from the ApprovalController', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockResolvedValue(); + + await metamaskController.requestPermissionApprovalForOrigin( + 'test.com', + { + eth_accounts: {}, + }, + ); + + expect( + metamaskController.approvalController.addAndShowApprovalRequest, + ).toHaveBeenCalledWith( + expect.objectContaining({ + id: expect.stringMatching(/.{21}/u), + origin: 'test.com', + requestData: { + metadata: { + id: expect.stringMatching(/.{21}/u), + origin: 'test.com', + }, + permissions: { + eth_accounts: {}, + }, + }, + type: 'wallet_requestPermissions', + }), + ); + + const [params] = + metamaskController.approvalController.addAndShowApprovalRequest.mock + .calls[0]; + expect(params.id).toStrictEqual(params.requestData.metadata.id); + }); + + it('returns the result from the ApprovalController', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockResolvedValue('approvalResult'); + + const result = + await metamaskController.requestPermissionApprovalForOrigin( + 'test.com', + { + eth_accounts: {}, + }, + ); + + expect(result).toStrictEqual('approvalResult'); + }); + }); + + describe('#getAllEvmAccountsSorted', () => { + it('returns the keyring accounts in lastSelected order', async () => { + jest + .spyOn(metamaskController.accountsController, 'listAccounts') + .mockReturnValueOnce([ + { + address: '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', + id: '21066553-d8c8-4cdc-af33-efc921cd3ca9', + metadata: { + name: 'Test Account', + lastSelected: 1, + keyring: { + type: 'HD Key Tree', + }, + }, + options: {}, + methods: ETH_EOA_METHODS, + type: EthAccountType.Eoa, + }, + { + address: '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', + id: '0bd7348e-bdfe-4f67-875c-de831a583857', + metadata: { + name: 'Test Account', + keyring: { + type: 'HD Key Tree', + }, + }, + options: {}, + methods: ETH_EOA_METHODS, + type: EthAccountType.Eoa, + }, + { + address: '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', + id: 'ff8fda69-d416-4d25-80a2-efb77bc7d4ad', + metadata: { + name: 'Test Account', + keyring: { + type: 'HD Key Tree', + }, + lastSelected: 3, + }, + options: {}, + methods: ETH_EOA_METHODS, + type: EthAccountType.Eoa, + }, + { + address: '0x04eBa9B766477d8eCA77F5f0e67AE1863C95a7E3', + id: '0bd7348e-bdfe-4f67-875c-de831a583857', + metadata: { + name: 'Test Account', + lastSelected: 3, + keyring: { + type: 'HD Key Tree', + }, + }, + options: {}, + methods: ETH_EOA_METHODS, + type: EthAccountType.Eoa, + }, + ]); + jest + .spyOn(metamaskController.keyringController, 'getAccounts') + .mockResolvedValueOnce([ + '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', + '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', + '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', + '0x04eBa9B766477d8eCA77F5f0e67AE1863C95a7E3', + ]); + jest + .spyOn(metamaskController, 'captureKeyringTypesWithMissingIdentities') + .mockImplementation(() => { + // noop + }); + + expect( + await metamaskController.getAllEvmAccountsSorted(), + ).toStrictEqual([ + '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', + '0x04eBa9B766477d8eCA77F5f0e67AE1863C95a7E3', + '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', + '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', + ]); + }); + + it('throws if a keyring account is missing an address (case 1)', async () => { + const internalAccounts = [ + { + address: '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', + id: '0bd7348e-bdfe-4f67-875c-de831a583857', + metadata: { + name: 'Test Account', + lastSelected: 2, + keyring: { + type: 'HD Key Tree', + }, + }, + options: {}, + methods: ETH_EOA_METHODS, + type: EthAccountType.Eoa, + }, + { + address: '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', + id: 'ff8fda69-d416-4d25-80a2-efb77bc7d4ad', + metadata: { + name: 'Test Account', + lastSelected: 3, + keyring: { + type: 'HD Key Tree', + }, + }, + options: {}, + methods: ETH_EOA_METHODS, + type: EthAccountType.Eoa, + }, + ]; + jest + .spyOn(metamaskController.accountsController, 'listAccounts') + .mockReturnValueOnce(internalAccounts); + jest + .spyOn(metamaskController.keyringController, 'getAccounts') + .mockResolvedValueOnce([ + '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', + '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', + '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', + ]); + jest + .spyOn(metamaskController, 'captureKeyringTypesWithMissingIdentities') + .mockImplementation(() => { + // noop + }); + + await expect(() => + metamaskController.getAllEvmAccountsSorted(), + ).rejects.toThrow( + 'Missing identity for address: "0x7A2Bd22810088523516737b4Dc238A4bC37c23F2".', + ); + expect( + metamaskController.captureKeyringTypesWithMissingIdentities, + ).toHaveBeenCalledWith(internalAccounts, [ + '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', + '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', + '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', + ]); + }); + + it('throws if a keyring account is missing an address (case 2)', async () => { + const internalAccounts = [ + { + address: '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', + id: 'cf8dace4-9439-4bd4-b3a8-88c821c8fcb3', + metadata: { + name: 'Test Account', + lastSelected: 1, + keyring: { + type: 'HD Key Tree', + }, + }, + options: {}, + methods: ETH_EOA_METHODS, + type: EthAccountType.Eoa, + }, + { + address: '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', + id: 'ff8fda69-d416-4d25-80a2-efb77bc7d4ad', + metadata: { + name: 'Test Account', + lastSelected: 3, + keyring: { + type: 'HD Key Tree', + }, + }, + options: {}, + methods: ETH_EOA_METHODS, + type: EthAccountType.Eoa, + }, + ]; + jest + .spyOn(metamaskController.accountsController, 'listAccounts') + .mockReturnValueOnce(internalAccounts); + jest + .spyOn(metamaskController.keyringController, 'getAccounts') + .mockResolvedValueOnce([ + '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', + '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', + '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', + ]); + jest + .spyOn(metamaskController, 'captureKeyringTypesWithMissingIdentities') + .mockImplementation(() => { + // noop + }); + + await expect(() => + metamaskController.getAllEvmAccountsSorted(), + ).rejects.toThrow( + 'Missing identity for address: "0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3".', + ); + expect( + metamaskController.captureKeyringTypesWithMissingIdentities, + ).toHaveBeenCalledWith(internalAccounts, [ + '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', + '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', + '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', + ]); + }); + }); + describe('#getApi', () => { it('getState', () => { const getApi = metamaskController.getApi(); From 7f70c0bb300e43a534cad9e9827614a77a9222cb Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 22 Oct 2024 15:39:41 -0700 Subject: [PATCH 077/148] yarn lock --- yarn.lock | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/yarn.lock b/yarn.lock index 403f1bb9b395..1b75b6426c19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5158,6 +5158,19 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-json-rpc-filters@npm:^7.0.0": + version: 7.0.1 + resolution: "@metamask/eth-json-rpc-filters@npm:7.0.1" + dependencies: + "@metamask/eth-query": "npm:^4.0.0" + "@metamask/json-rpc-engine": "npm:^8.0.2" + "@metamask/safe-event-emitter": "npm:^3.0.0" + async-mutex: "npm:^0.5.0" + pify: "npm:^5.0.0" + checksum: 10/5200f75cee48dfd79deba5e4f1b16ff6827e606da617891f5cb7b59c43ae4ac8420cb9a6a9ca31705c47d2c3d32a3754e052b30f61fd293cc37f009c4fe20c12 + languageName: node + linkType: hard + "@metamask/eth-json-rpc-filters@npm:^8.0.0": version: 8.0.0 resolution: "@metamask/eth-json-rpc-filters@npm:8.0.0" From a757cbf143fb34cc417bd4dc62a3947471797c06 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 23 Oct 2024 10:47:22 -0700 Subject: [PATCH 078/148] bring in new preview build with 100% coverage --- package.json | 4 ++-- yarn.lock | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 9e51b75e5f68..a7bbe1f00f62 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,7 @@ "attributions:generate": "./development/generate-attributions.sh" }, "resolutions": { - "@metamask/controller-utils": "npm:@metamask-previews/controller-utils@11.3.0-preview-dae4f73d", + "@metamask/controller-utils": "npm:@metamask-previews/controller-utils@11.4.0-preview-7d32a0b2", "chokidar": "^3.6.0", "gridplus-sdk": "~2.6.0", "gridplus-sdk/secp256k1": "^5.0.1", @@ -344,7 +344,7 @@ "@metamask/message-manager": "^10.1.0", "@metamask/message-signing-snap": "^0.4.0", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-dae4f73d", + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-7d32a0b2", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index 1b75b6426c19..475446238fd9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4983,9 +4983,9 @@ __metadata: languageName: node linkType: hard -"@metamask/controller-utils@npm:@metamask-previews/controller-utils@11.3.0-preview-dae4f73d": - version: 11.3.0-preview-dae4f73d - resolution: "@metamask-previews/controller-utils@npm:11.3.0-preview-dae4f73d" +"@metamask/controller-utils@npm:@metamask-previews/controller-utils@11.4.0-preview-7d32a0b2": + version: 11.4.0-preview-7d32a0b2 + resolution: "@metamask-previews/controller-utils@npm:11.4.0-preview-7d32a0b2" dependencies: "@ethereumjs/util": "npm:^8.1.0" "@metamask/eth-query": "npm:^4.0.0" @@ -4996,7 +4996,7 @@ __metadata: bn.js: "npm:^5.2.1" eth-ens-namehash: "npm:^2.0.8" fast-deep-equal: "npm:^3.1.3" - checksum: 10/568559942f0c70307473b8078b37f4c82b4bc13d8b5ce7952eed5ac5bed5bc58f1a30e382b3d71f85fd704408c5058e88150c5815a9abd004cd8a811e9c6fd7a + checksum: 10/c090ae3438e304650fdd7c23c68940c0064f260fd7daee559c27bd0b8d087f75276b016ff2f7d3f8eb0fefb09063880f588ff762290c63044a9a587616d47005 languageName: node linkType: hard @@ -5717,12 +5717,12 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-dae4f73d": - version: 0.0.0-preview-dae4f73d - resolution: "@metamask-previews/multichain@npm:0.0.0-preview-dae4f73d" +"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-7d32a0b2": + version: 0.0.0-preview-7d32a0b2 + resolution: "@metamask-previews/multichain@npm:0.0.0-preview-7d32a0b2" dependencies: "@metamask/api-specs": "npm:^0.10.12" - "@metamask/controller-utils": "npm:^11.3.0" + "@metamask/controller-utils": "npm:^11.4.0" "@metamask/eth-json-rpc-filters": "npm:^7.0.0" "@metamask/rpc-errors": "npm:^7.0.0" "@metamask/utils": "npm:^9.1.0" @@ -5730,7 +5730,7 @@ __metadata: peerDependencies: "@metamask/network-controller": ^21.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/3531a9802694b7581d132513e06cd569ccfb40c693b03f272941d007d1314a44283851855540a6c879c17e138313115ef9500f8d60de3a75540a20e1f3238549 + checksum: 10/90855e2d9e382008736facef3bf1e0f1e2cf02f20087bb81c0bc352730d89cb4b3606bcbc15b36a9d4a8ecec4b590b42aadcb4106cc640f1054294e02a8d0682 languageName: node linkType: hard @@ -26005,7 +26005,7 @@ __metadata: "@metamask/message-manager": "npm:^10.1.0" "@metamask/message-signing-snap": "npm:^0.4.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-dae4f73d" + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-7d32a0b2" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch" "@metamask/notification-controller": "npm:^6.0.0" From a1d4e40414a983ba27d508bd4e28db830b07659b Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Wed, 23 Oct 2024 18:08:10 +0000 Subject: [PATCH 079/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 3 --- lavamoat/browserify/flask/policy.json | 3 --- lavamoat/browserify/main/policy.json | 3 --- lavamoat/browserify/mmi/policy.json | 3 --- 4 files changed, 12 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 28d81266d3ef..12de4eda5204 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1435,9 +1435,6 @@ } }, "@metamask/multichain": { - "globals": { - "console.log": true - }, "packages": { "@metamask/controller-utils": true, "@metamask/multichain>@metamask/api-specs": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 28d81266d3ef..12de4eda5204 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1435,9 +1435,6 @@ } }, "@metamask/multichain": { - "globals": { - "console.log": true - }, "packages": { "@metamask/controller-utils": true, "@metamask/multichain>@metamask/api-specs": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 28d81266d3ef..12de4eda5204 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1435,9 +1435,6 @@ } }, "@metamask/multichain": { - "globals": { - "console.log": true - }, "packages": { "@metamask/controller-utils": true, "@metamask/multichain>@metamask/api-specs": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 15f82163fd19..32a1b0a38c71 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -1527,9 +1527,6 @@ } }, "@metamask/multichain": { - "globals": { - "console.log": true - }, "packages": { "@metamask/controller-utils": true, "@metamask/multichain>@metamask/api-specs": true, From 6b3dda97185df4b687dbb46062f2b13e72aa6738 Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Wed, 30 Oct 2024 21:28:54 +0000 Subject: [PATCH 080/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 174 +++----------------------- lavamoat/browserify/flask/policy.json | 174 +++----------------------- lavamoat/browserify/main/policy.json | 174 +++----------------------- lavamoat/browserify/mmi/policy.json | 174 +++----------------------- 4 files changed, 80 insertions(+), 616 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index d511ca2a5122..b56599992ced 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -750,30 +750,15 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, - "@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -1459,6 +1444,17 @@ "crypto": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/controller-utils": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "browserify>assert": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -1893,8 +1889,8 @@ "@metamask/permission-controller>@metamask/base-controller": true, "@metamask/permission-controller>@metamask/json-rpc-engine": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1909,60 +1905,15 @@ }, "@metamask/permission-controller>@metamask/json-rpc-engine": { "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/safe-event-emitter": true, + "@metamask/utils": true } }, "@metamask/permission-controller>@metamask/rpc-errors": { "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/permission-controller>nanoid": { @@ -2548,10 +2499,10 @@ "packages": { "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, @@ -2590,21 +2541,6 @@ "@metamask/utils": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/base-controller": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2668,7 +2604,7 @@ }, "@metamask/snaps-rpc-methods": { "packages": { - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, + "@metamask/permission-controller": true, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, @@ -2678,41 +2614,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2760,9 +2661,9 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/rpc-errors": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, @@ -2779,41 +2680,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index d511ca2a5122..b56599992ced 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -750,30 +750,15 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, - "@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -1459,6 +1444,17 @@ "crypto": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/controller-utils": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "browserify>assert": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -1893,8 +1889,8 @@ "@metamask/permission-controller>@metamask/base-controller": true, "@metamask/permission-controller>@metamask/json-rpc-engine": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1909,60 +1905,15 @@ }, "@metamask/permission-controller>@metamask/json-rpc-engine": { "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/safe-event-emitter": true, + "@metamask/utils": true } }, "@metamask/permission-controller>@metamask/rpc-errors": { "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/permission-controller>nanoid": { @@ -2548,10 +2499,10 @@ "packages": { "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, @@ -2590,21 +2541,6 @@ "@metamask/utils": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/base-controller": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2668,7 +2604,7 @@ }, "@metamask/snaps-rpc-methods": { "packages": { - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, + "@metamask/permission-controller": true, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, @@ -2678,41 +2614,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2760,9 +2661,9 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/rpc-errors": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, @@ -2779,41 +2680,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index d511ca2a5122..b56599992ced 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -750,30 +750,15 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, - "@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -1459,6 +1444,17 @@ "crypto": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/controller-utils": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "browserify>assert": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -1893,8 +1889,8 @@ "@metamask/permission-controller>@metamask/base-controller": true, "@metamask/permission-controller>@metamask/json-rpc-engine": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -1909,60 +1905,15 @@ }, "@metamask/permission-controller>@metamask/json-rpc-engine": { "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/safe-event-emitter": true, + "@metamask/utils": true } }, "@metamask/permission-controller>@metamask/rpc-errors": { "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/permission-controller>nanoid": { @@ -2548,10 +2499,10 @@ "packages": { "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, @@ -2590,21 +2541,6 @@ "@metamask/utils": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/base-controller": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2668,7 +2604,7 @@ }, "@metamask/snaps-rpc-methods": { "packages": { - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, + "@metamask/permission-controller": true, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, @@ -2678,41 +2614,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2760,9 +2661,9 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/rpc-errors": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, @@ -2779,41 +2680,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 2f8a3f7cec97..724857218a75 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -842,30 +842,15 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, - "@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -1551,6 +1536,17 @@ "crypto": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/controller-utils": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "browserify>assert": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -1985,8 +1981,8 @@ "@metamask/permission-controller>@metamask/base-controller": true, "@metamask/permission-controller>@metamask/json-rpc-engine": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, "@metamask/permission-controller>nanoid": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } @@ -2001,60 +1997,15 @@ }, "@metamask/permission-controller>@metamask/json-rpc-engine": { "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/safe-event-emitter": true, + "@metamask/utils": true } }, "@metamask/permission-controller>@metamask/rpc-errors": { "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/rpc-errors>fast-safe-stringify": true, + "@metamask/utils": true } }, "@metamask/permission-controller>nanoid": { @@ -2640,10 +2591,10 @@ "packages": { "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/snaps-controllers>@metamask/base-controller": true, "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@metamask/rpc-errors": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, @@ -2682,21 +2633,6 @@ "@metamask/utils": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-controllers>@metamask/base-controller": true, - "@metamask/snaps-controllers>@metamask/json-rpc-engine": true, - "@metamask/snaps-controllers>@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2760,7 +2696,7 @@ }, "@metamask/snaps-rpc-methods": { "packages": { - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, + "@metamask/permission-controller": true, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, @@ -2770,41 +2706,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-rpc-methods>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-rpc-methods>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, @@ -2852,9 +2753,9 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/rpc-errors": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, @@ -2871,41 +2772,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/controller-utils": true, - "@metamask/snaps-utils>@metamask/base-controller": true, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/safe-event-emitter": true, - "@metamask/snaps-utils>@metamask/rpc-errors": true, - "@metamask/utils": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, From 7f1ea3361ba8f82ba2d55bc91cbbb1f82dad6376 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 30 Oct 2024 14:29:16 -0700 Subject: [PATCH 081/148] use addPermittedChain instead of grant. delete grantPermittedChain(s) --- test/e2e/fixture-builder.js | 5 +- .../network-list-menu/network-list-menu.tsx | 4 +- ui/store/actions.test.js | 73 ------------------- ui/store/actions.ts | 46 ------------ 4 files changed, 6 insertions(+), 122 deletions(-) diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 49694e66419d..cf3793aa5d9c 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -446,7 +446,10 @@ class FixtureBuilder { return this; } - withPermissionControllerConnectedToTestDapp({ account = '', useLocalhostHostname = false } = {}) { + withPermissionControllerConnectedToTestDapp({ + account = '', + useLocalhostHostname = false, + } = {}) { const selectedAccount = account || DEFAULT_FIXTURE_ACCOUNT; const subjects = { [useLocalhostHostname ? DAPP_URL_LOCALHOST : DAPP_URL]: { diff --git a/ui/components/multichain/network-list-menu/network-list-menu.tsx b/ui/components/multichain/network-list-menu/network-list-menu.tsx index 518f9f19387a..116828cf2de1 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.tsx +++ b/ui/components/multichain/network-list-menu/network-list-menu.tsx @@ -24,10 +24,10 @@ import { updateNetworksList, setNetworkClientIdForDomain, setEditedNetwork, - grantPermittedChain, showPermittedNetworkToast, updateCustomNonce, setNextNonce, + addPermittedChain, } from '../../../store/actions'; import { CHAIN_ID_TO_NETWORK_IMAGE_URL_MAP, @@ -280,7 +280,7 @@ export const NetworkListMenu = ({ onClose }: { onClose: () => void }) => { dispatch(setNextNonce('')); if (permittedAccountAddresses.length > 0) { - grantPermittedChain(selectedTabOrigin, network.chainId); + addPermittedChain(selectedTabOrigin, network.chainId); if (!permittedChainIds.includes(network.chainId)) { dispatch(showPermittedNetworkToast()); } diff --git a/ui/store/actions.test.js b/ui/store/actions.test.js index 22e8db2fa281..02c0c8355a96 100644 --- a/ui/store/actions.test.js +++ b/ui/store/actions.test.js @@ -17,10 +17,6 @@ import { MetaMetricsNetworkEventSource } from '../../shared/constants/metametric import { ETH_EOA_METHODS } from '../../shared/constants/eth-methods'; import { mockNetworkState } from '../../test/stub/networks'; import { CHAIN_IDS } from '../../shared/constants/network'; -import { - CaveatTypes, - EndowmentTypes, -} from '../../shared/constants/permissions'; import * as actions from './actions'; import * as actionConstants from './actionConstants'; import { setBackgroundConnection } from './background-connection'; @@ -2660,73 +2656,4 @@ describe('Actions', () => { expect(store.getActions()).toStrictEqual([]); }); }); - - describe('grantPermittedChain', () => { - afterEach(() => { - sinon.restore(); - }); - - it('calls grantPermissionsIncremental in the background', async () => { - const store = mockStore(); - - background.grantPermissionsIncremental.callsFake((_, cb) => cb()); - setBackgroundConnection(background); - - await actions.grantPermittedChain('test.com', '0x1'); - expect( - background.grantPermissionsIncremental.calledWith( - { - subject: { origin: 'test.com' }, - approvedPermissions: { - [EndowmentTypes.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x1'], - }, - ], - }, - }, - }, - sinon.match.func, - ), - ).toBe(true); - expect(store.getActions()).toStrictEqual([]); - }); - }); - - describe('grantPermittedChains', () => { - afterEach(() => { - sinon.restore(); - }); - - it('calls grantPermissions in the background', async () => { - const store = mockStore(); - - background.grantPermissions.callsFake((_, cb) => cb()); - setBackgroundConnection(background); - - await actions.grantPermittedChains('test.com', ['0x1', '0x2']); - expect( - background.grantPermissions.calledWith( - { - subject: { origin: 'test.com' }, - approvedPermissions: { - [EndowmentTypes.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x1', '0x2'], - }, - ], - }, - }, - }, - sinon.match.func, - ), - ).toBe(true); - - expect(store.getActions()).toStrictEqual([]); - }); - }); }); diff --git a/ui/store/actions.ts b/ui/store/actions.ts index 5720f5c0a08b..a0d75bacfe29 100644 --- a/ui/store/actions.ts +++ b/ui/store/actions.ts @@ -124,10 +124,6 @@ import { DecodedTransactionDataResponse } from '../../shared/types/transaction-d import { LastInteractedConfirmationInfo } from '../pages/confirmations/types/confirm'; import { EndTraceRequest } from '../../shared/lib/trace'; import { SortCriteria } from '../components/app/assets/util/sort'; -import { - CaveatTypes, - EndowmentTypes, -} from '../../shared/constants/permissions'; import * as actionConstants from './actionConstants'; ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) import { updateCustodyState } from './institutional/institution-actions'; @@ -5691,48 +5687,6 @@ export async function getNextAvailableAccountName( ); } -export async function grantPermittedChain( - selectedTabOrigin: string, - chainId?: string, -): Promise { - return await submitRequestToBackground('grantPermissionsIncremental', [ - { - subject: { origin: selectedTabOrigin }, - approvedPermissions: { - [EndowmentTypes.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: [chainId], - }, - ], - }, - }, - }, - ]); -} - -export async function grantPermittedChains( - selectedTabOrigin: string, - chainIds: string[], -): Promise { - return await submitRequestToBackground('grantPermissions', [ - { - subject: { origin: selectedTabOrigin }, - approvedPermissions: { - [EndowmentTypes.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: chainIds, - }, - ], - }, - }, - }, - ]); -} - export async function decodeTransactionData({ transactionData, contractAddress, From 993e6fcb924d52844951f311b14b70102063699f Mon Sep 17 00:00:00 2001 From: jiexi Date: Thu, 21 Nov 2024 13:00:03 -0800 Subject: [PATCH 082/148] CAIP-25 Permission Migration Refactor (#28574) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28574?quickstart=1) ## **Related issues** Related: https://github.com/MetaMask/core/pull/4950/files#diff-1a495b7bd12828b73b1860b4cb3a85d75629017e75aa935d6a722e8745981954L25-R26 ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: Alex Co-authored-by: MetaMask Bot --- .storybook/test-data.js | 2 - .../permissions/background-api.test.js | 173 ++-------------- .../controllers/permissions/selectors.test.js | 23 +-- .../controllers/permissions/specifications.js | 4 +- .../handlers/ethereum-chain-utils.test.ts | 20 -- .../handlers/wallet-getPermissions.test.ts | 12 -- .../wallet-requestPermissions.test.ts | 8 - app/scripts/metamask-controller.js | 12 +- app/scripts/metamask-controller.test.js | 2 - app/scripts/migrations/132.test.ts | 32 --- app/scripts/migrations/132.ts | 2 - lavamoat/browserify/beta/policy.json | 194 +++++------------- lavamoat/browserify/flask/policy.json | 194 +++++------------- lavamoat/browserify/main/policy.json | 194 +++++------------- lavamoat/browserify/mmi/policy.json | 194 +++++------------- package.json | 2 +- test/e2e/fixture-builder.js | 16 -- .../unconnected-account-alert.test.js | 2 - .../account-list-menu.test.tsx | 8 - .../connected-accounts-menu.test.tsx | 2 - .../pages/connections/connections.test.tsx | 4 - .../permissions-page/permissions-page.test.js | 2 - .../send/components/account-picker.test.tsx | 2 - .../permission-details-modal.test.tsx | 2 - ui/pages/routes/routes.component.test.js | 2 - ui/selectors/permissions.test.js | 20 -- ui/selectors/selectors.test.js | 2 - yarn.lock | 64 ++---- 28 files changed, 236 insertions(+), 958 deletions(-) diff --git a/.storybook/test-data.js b/.storybook/test-data.js index fa844e8dbea9..838eaad9ed95 100644 --- a/.storybook/test-data.js +++ b/.storybook/test-data.js @@ -1406,8 +1406,6 @@ const state = { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x64a845a5b02460acf8a3d84503b0d68d028b4bb4', ], diff --git a/app/scripts/controllers/permissions/background-api.test.js b/app/scripts/controllers/permissions/background-api.test.js index b9dc0cda14f4..ff0ca9887ba7 100644 --- a/app/scripts/controllers/permissions/background-api.test.js +++ b/app/scripts/controllers/permissions/background-api.test.js @@ -2,8 +2,6 @@ import { MethodNames } from '@metamask/permission-controller'; import { Caip25CaveatType, Caip25EndowmentPermissionName, - KnownNotifications, - KnownRpcMethods, } from '@metamask/multichain'; import { RestrictedMethods } from '../../../../shared/constants/permissions'; import { flushPromises } from '../../../../test/lib/timer-helpers'; @@ -58,27 +56,19 @@ describe('permission background API methods', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, 'eip155:10': { - methods: [], - notifications: [], accounts: ['eip155:10:0x1', 'eip155:10:0x2'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], }, 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x2', 'eip155:1:0x3'], }, }, @@ -100,51 +90,41 @@ describe('permission background API methods', () => { { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ + 'eip155:1:0x1', 'eip155:1:0x2', 'eip155:1:0x3', - 'eip155:1:0x1', 'eip155:1:0x4', ], }, 'eip155:10': { - methods: [], - notifications: [], accounts: [ + 'eip155:10:0x1', 'eip155:10:0x2', 'eip155:10:0x3', - 'eip155:10:0x1', 'eip155:10:0x4', ], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], }, 'eip155:1': { - methods: [], - notifications: [], accounts: [ + 'eip155:1:0x1', 'eip155:1:0x2', 'eip155:1:0x3', - 'eip155:1:0x1', 'eip155:1:0x4', ], }, 'wallet:eip155': { - methods: [], - notifications: [], accounts: [ + 'wallet:eip155:0x1', 'wallet:eip155:0x2', 'wallet:eip155:0x3', - 'wallet:eip155:0x1', 'wallet:eip155:0x4', ], }, @@ -198,27 +178,19 @@ describe('permission background API methods', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, 'eip155:10': { - methods: [], - notifications: [], accounts: ['eip155:10:0x1', 'eip155:10:0x2'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], }, 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x2', 'eip155:1:0x3'], }, }, @@ -240,23 +212,19 @@ describe('permission background API methods', () => { { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ + 'eip155:1:0x1', 'eip155:1:0x2', 'eip155:1:0x3', - 'eip155:1:0x1', 'eip155:1:0x4', 'eip155:1:0x5', ], }, 'eip155:10': { - methods: [], - notifications: [], accounts: [ + 'eip155:10:0x1', 'eip155:10:0x2', 'eip155:10:0x3', - 'eip155:10:0x1', 'eip155:10:0x4', 'eip155:10:0x5', ], @@ -264,30 +232,24 @@ describe('permission background API methods', () => { }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], }, 'eip155:1': { - methods: [], - notifications: [], accounts: [ + 'eip155:1:0x1', 'eip155:1:0x2', 'eip155:1:0x3', - 'eip155:1:0x1', 'eip155:1:0x4', 'eip155:1:0x5', ], }, 'wallet:eip155': { - methods: [], - notifications: [], accounts: [ + 'wallet:eip155:0x1', 'wallet:eip155:0x2', 'wallet:eip155:0x3', - 'wallet:eip155:0x1', 'wallet:eip155:0x4', 'wallet:eip155:0x5', ], @@ -342,27 +304,19 @@ describe('permission background API methods', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, 'eip155:10': { - methods: [], - notifications: [], accounts: ['eip155:10:0x1', 'eip155:10:0x2'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], }, 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x2', 'eip155:1:0x3'], }, }, @@ -387,20 +341,14 @@ describe('permission background API methods', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, 'eip155:10': { - methods: [], - notifications: [], accounts: ['eip155:10:0x1'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], @@ -428,27 +376,19 @@ describe('permission background API methods', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, 'eip155:10': { - methods: [], - notifications: [], accounts: ['eip155:10:0x1', 'eip155:10:0x2'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], }, 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x2', 'eip155:1:0x3'], }, }, @@ -469,33 +409,23 @@ describe('permission background API methods', () => { { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], - accounts: ['eip155:1:0x3', 'eip155:1:0x1'], + accounts: ['eip155:1:0x1', 'eip155:1:0x3'], }, 'eip155:10': { - methods: [], - notifications: [], - accounts: ['eip155:10:0x3', 'eip155:10:0x1'], + accounts: ['eip155:10:0x1', 'eip155:10:0x3'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], }, 'eip155:1': { - methods: [], - notifications: [], - accounts: ['eip155:1:0x3', 'eip155:1:0x1'], + accounts: ['eip155:1:0x1', 'eip155:1:0x3'], }, 'wallet:eip155': { - methods: [], - notifications: [], - accounts: ['wallet:eip155:0x3', 'wallet:eip155:0x1'], + accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x3'], }, }, isMultichainOrigin: true, @@ -575,18 +505,12 @@ describe('permission background API methods', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: KnownRpcMethods.eip155, - notifications: KnownNotifications.eip155, accounts: ['eip155:1:0xdeadbeef'], }, 'eip155:5': { - methods: KnownRpcMethods.eip155, - notifications: KnownNotifications.eip155, accounts: ['eip155:5:0xdeadbeef'], }, 'wallet:eip155': { - methods: [], - notifications: [], accounts: ['wallet:eip155:0xdeadbeef'], }, }, @@ -643,28 +567,20 @@ describe('permission background API methods', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, 'eip155:10': { - methods: [], - notifications: [], - accounts: ['eip155:10:0x2'], + accounts: ['eip155:10:0x1'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], }, 'eip155:1': { - methods: [], - notifications: [], - accounts: ['eip155:1:0x1'], + accounts: ['eip155:1:0x2'], }, }, isMultichainOrigin: true, @@ -685,37 +601,25 @@ describe('permission background API methods', () => { { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x1', 'eip155:1:0x2'], }, 'eip155:10': { - methods: [], - notifications: [], accounts: ['eip155:10:0x1', 'eip155:10:0x2'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], }, 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x1', 'eip155:1:0x2'], }, 'eip155:1337': { - methods: KnownRpcMethods.eip155, - notifications: KnownNotifications.eip155, accounts: ['eip155:1337:0x1', 'eip155:1337:0x2'], }, 'wallet:eip155': { - methods: [], - notifications: [], accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x2'], }, }, @@ -768,28 +672,20 @@ describe('permission background API methods', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, 'eip155:10': { - methods: [], - notifications: [], - accounts: ['eip155:10:0x2'], + accounts: ['eip155:10:0x1'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], }, 'eip155:1': { - methods: [], - notifications: [], - accounts: ['eip155:1:0x1'], + accounts: ['eip155:1:0x2'], }, }, isMultichainOrigin: true, @@ -810,42 +706,28 @@ describe('permission background API methods', () => { { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x1', 'eip155:1:0x2'], }, 'eip155:10': { - methods: [], - notifications: [], accounts: ['eip155:10:0x1', 'eip155:10:0x2'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], }, 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x1', 'eip155:1:0x2'], }, 'eip155:4': { - methods: KnownRpcMethods.eip155, - notifications: KnownNotifications.eip155, accounts: ['eip155:4:0x1', 'eip155:4:0x2'], }, 'eip155:5': { - methods: KnownRpcMethods.eip155, - notifications: KnownNotifications.eip155, accounts: ['eip155:5:0x1', 'eip155:5:0x2'], }, 'wallet:eip155': { - methods: [], - notifications: [], accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x2'], }, }, @@ -898,20 +780,14 @@ describe('permission background API methods', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, 'eip155:10': { - methods: [], - notifications: [], accounts: ['eip155:10:0x1', 'eip155:10:0x2'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], @@ -937,15 +813,10 @@ describe('permission background API methods', () => { getCaveat: jest.fn().mockReturnValue({ value: { requiredScopes: { - 'eip155:1': { - methods: [], - notifications: [], - }, + 'eip155:1': {}, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], @@ -973,20 +844,14 @@ describe('permission background API methods', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, 'eip155:10': { - methods: [], - notifications: [], accounts: ['eip155:10:0x1', 'eip155:10:0x2'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], @@ -1009,15 +874,11 @@ describe('permission background API methods', () => { { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], diff --git a/app/scripts/controllers/permissions/selectors.test.js b/app/scripts/controllers/permissions/selectors.test.js index 5b667d1ac36b..11524e373d3d 100644 --- a/app/scripts/controllers/permissions/selectors.test.js +++ b/app/scripts/controllers/permissions/selectors.test.js @@ -63,15 +63,11 @@ describe('PermissionController selectors', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x1'], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [ 'bip122:000000000019d6689c085ae165831e93:128Lkh3S7CkDTBZ8W7BbpsN3YYizJMp8p6', ], @@ -95,8 +91,6 @@ describe('PermissionController selectors', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x2'], }, }, @@ -117,15 +111,11 @@ describe('PermissionController selectors', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x1'], }, }, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x2'], }, }, @@ -190,8 +180,7 @@ describe('PermissionController selectors', () => { const mockAuthorization = { requiredScopes: { 'eip155:1': { - methods: ['eth_sendTransaction'], - notifications: [], + accounts: [], }, }, optionalScopes: {}, @@ -223,15 +212,11 @@ describe('PermissionController selectors', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, }, optionalScopes: { 'bip122:000000000019d6689c085ae165831e93': { - methods: [], - notifications: [], accounts: [], }, }, @@ -252,8 +237,6 @@ describe('PermissionController selectors', () => { value: { requiredScopes: { 'eip155:2': { - methods: [], - notifications: [], accounts: [], }, }, @@ -275,15 +258,11 @@ describe('PermissionController selectors', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, }, optionalScopes: { 'eip155:2': { - methods: [], - notifications: [], accounts: [], }, }, diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index 21f2349598cc..f4c6fc8e329b 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -3,7 +3,7 @@ import { endowmentCaveatSpecifications as snapsEndowmentCaveatSpecifications, } from '@metamask/snaps-rpc-methods'; import { - Caip25CaveatFactoryFn, + createCaip25Caveat, Caip25CaveatType, caip25EndowmentBuilder, } from '@metamask/multichain'; @@ -32,7 +32,7 @@ export const PermissionNames = Object.freeze({ * PermissionController. */ export const CaveatFactories = Object.freeze({ - [Caip25CaveatType]: Caip25CaveatFactoryFn, + [Caip25CaveatType]: createCaip25Caveat, }); /** diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts index 303a81dfe615..fa8a90bdf8f4 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts @@ -1,7 +1,5 @@ import { errorCodes } from '@metamask/rpc-errors'; import { - KnownNotifications, - KnownRpcMethods, Caip25CaveatType, Caip25EndowmentPermissionName, } from '@metamask/multichain'; @@ -134,13 +132,6 @@ describe('Ethereum Chain Utils', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: KnownRpcMethods.eip155, - notifications: KnownNotifications.eip155, - accounts: [], - }, - 'wallet:eip155': { - methods: [], - notifications: [], accounts: [], }, }, @@ -237,13 +228,6 @@ describe('Ethereum Chain Utils', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: KnownRpcMethods.eip155, - notifications: KnownNotifications.eip155, - accounts: [], - }, - 'wallet:eip155': { - methods: [], - notifications: [], accounts: [], }, }, @@ -333,8 +317,6 @@ describe('Ethereum Chain Utils', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, }, @@ -355,8 +337,6 @@ describe('Ethereum Chain Utils', () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [], }, }, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts index 3180b05e8baa..9f1ca4a2f9ea 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts @@ -37,20 +37,14 @@ const createMockedHandler = () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x1', 'eip155:1:0x2'], }, 'eip155:5': { - methods: [], - notifications: [], accounts: ['eip155:5:0x1', 'eip155:5:0x3'], }, }, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0xdeadbeef'], }, }, @@ -207,20 +201,14 @@ describe('getPermissionsHandler', () => { expect(MockMultichain.getPermittedEthChainIds).toHaveBeenCalledWith({ requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x1', 'eip155:1:0x2'], }, 'eip155:5': { - methods: [], - notifications: [], accounts: ['eip155:5:0x1', 'eip155:5:0x3'], }, }, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0xdeadbeef'], }, }, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts index b9dc0edd92b3..c4418d9378c2 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts @@ -66,25 +66,17 @@ const createMockedHandler = () => { value: { requiredScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x1', 'eip155:1:0x2'], }, 'eip155:5': { - methods: [], - notifications: [], accounts: ['eip155:5:0x1', 'eip155:5:0x3'], }, }, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0x4'], }, 'other:1': { - methods: [], - notifications: [], accounts: ['other:1:0x4'], }, }, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 78435fdff0a3..c1cb0af64022 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -155,13 +155,13 @@ import { NotificationServicesPushController, NotificationServicesController, } from '@metamask/notification-services-controller'; -import { isProduction } from '../../shared/modules/environment'; import { - Caip25CaveatMutatorFactories, + Caip25CaveatMutators, Caip25CaveatType, Caip25EndowmentPermissionName, getEthAccounts, } from '@metamask/multichain'; +import { isProduction } from '../../shared/modules/environment'; import { methodsRequiringNetworkSwitch, methodsThatCanSwitchNetworkWithoutApproval, @@ -5078,9 +5078,9 @@ export default class MetamaskController extends EventEmitter { this.permissionController.updatePermissionsByCaveat( Caip25CaveatType, (existingScopes) => - Caip25CaveatMutatorFactories[Caip25CaveatType].removeScope( - toCaipChainId('eip155', parseInt(targetChainId, 16).toString()), + Caip25CaveatMutators[Caip25CaveatType].removeScope( existingScopes, + toCaipChainId('eip155', parseInt(targetChainId, 16).toString()), ), ); } @@ -5112,9 +5112,9 @@ export default class MetamaskController extends EventEmitter { this.permissionController.updatePermissionsByCaveat( Caip25CaveatType, (existingScopes) => - Caip25CaveatMutatorFactories[Caip25CaveatType].removeAccount( - targetAccount, + Caip25CaveatMutators[Caip25CaveatType].removeAccount( existingScopes, + targetAccount, ), ); } diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 3578de971942..a92f13419112 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -851,8 +851,6 @@ describe('MetaMaskController', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: ['eip155:1:0xdead', 'eip155:1:0xbeef'], }, }, diff --git a/app/scripts/migrations/132.test.ts b/app/scripts/migrations/132.test.ts index 0483a27bef4c..57fc79349844 100644 --- a/app/scripts/migrations/132.test.ts +++ b/app/scripts/migrations/132.test.ts @@ -568,16 +568,12 @@ describe('migration #132', () => { `${currentScope}:0xdeadbeef`, `${currentScope}:0x999`, ], - methods: [], - notifications: [], }, 'wallet:eip155': { accounts: [ 'wallet:eip155:0xdeadbeef', 'wallet:eip155:0x999', ], - methods: [], - notifications: [], }, }, isMultichainOrigin: false, @@ -652,16 +648,12 @@ describe('migration #132', () => { `${currentScope}:0xdeadbeef`, `${currentScope}:0x999`, ], - methods: [], - notifications: [], }, 'wallet:eip155': { accounts: [ 'wallet:eip155:0xdeadbeef', 'wallet:eip155:0x999', ], - methods: [], - notifications: [], }, }, isMultichainOrigin: false, @@ -736,16 +728,12 @@ describe('migration #132', () => { 'eip155:11155111:0xdeadbeef', 'eip155:11155111:0x999', ], - methods: [], - notifications: [], }, 'wallet:eip155': { accounts: [ 'wallet:eip155:0xdeadbeef', 'wallet:eip155:0x999', ], - methods: [], - notifications: [], }, }, isMultichainOrigin: false, @@ -810,8 +798,6 @@ describe('migration #132', () => { 'wallet:eip155:0xdeadbeef', 'wallet:eip155:0x999', ], - methods: [], - notifications: [], }, }, isMultichainOrigin: false, @@ -912,16 +898,12 @@ describe('migration #132', () => { 'eip155:10:0xdeadbeef', 'eip155:10:0x999', ], - methods: [], - notifications: [], }, 'wallet:eip155': { accounts: [ 'wallet:eip155:0xdeadbeef', 'wallet:eip155:0x999', ], - methods: [], - notifications: [], }, }, isMultichainOrigin: false, @@ -1038,24 +1020,18 @@ describe('migration #132', () => { 'eip155:10:0xdeadbeef', 'eip155:10:0x999', ], - methods: [], - notifications: [], }, 'eip155:100': { accounts: [ 'eip155:100:0xdeadbeef', 'eip155:100:0x999', ], - methods: [], - notifications: [], }, 'wallet:eip155': { accounts: [ 'wallet:eip155:0xdeadbeef', 'wallet:eip155:0x999', ], - methods: [], - notifications: [], }, }, isMultichainOrigin: false, @@ -1123,13 +1099,9 @@ describe('migration #132', () => { optionalScopes: { [currentScope]: { accounts: [`${currentScope}:0xdeadbeef`], - methods: [], - notifications: [], }, 'wallet:eip155': { accounts: ['wallet:eip155:0xdeadbeef'], - methods: [], - notifications: [], }, }, isMultichainOrigin: false, @@ -1151,13 +1123,9 @@ describe('migration #132', () => { optionalScopes: { [currentScope]: { accounts: [`${currentScope}:0xdeadbeef`], - methods: [], - notifications: [], }, 'wallet:eip155': { accounts: ['wallet:eip155:0xdeadbeef'], - methods: [], - notifications: [], }, }, isMultichainOrigin: false, diff --git a/app/scripts/migrations/132.ts b/app/scripts/migrations/132.ts index 1bff1bb42b84..c997f2f4839c 100644 --- a/app/scripts/migrations/132.ts +++ b/app/scripts/migrations/132.ts @@ -265,8 +265,6 @@ function transformState(state: Record) { (account) => `${scopeString}:${account}`, ); scopes[scopeString] = { - methods: [], - notifications: [], accounts: caipAccounts, }; }); diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index f87946a09c0f..9bfa52031565 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -767,15 +767,30 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, + "@metamask/controller-utils>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -1728,6 +1743,16 @@ "crypto": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/controller-utils": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -2175,82 +2200,16 @@ "console.error": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, - "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, + "@metamask/json-rpc-engine": true, "@metamask/permission-controller>nanoid": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors": { - "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2674,6 +2633,21 @@ "immer": true } }, + "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2771,22 +2745,7 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": true - } - }, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": true } }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": { @@ -2824,9 +2783,9 @@ "@metamask/json-rpc-engine": true, "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2850,21 +2809,6 @@ "crypto.getRandomValues": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2922,8 +2866,8 @@ }, "@metamask/snaps-rpc-methods": { "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, "@metamask/snaps-utils": true, @@ -2932,26 +2876,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -3002,10 +2926,10 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, @@ -3021,26 +2945,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/snaps-registry": { "packages": { "@metamask/message-signing-snap>@noble/curves": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index f87946a09c0f..9bfa52031565 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -767,15 +767,30 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, + "@metamask/controller-utils>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -1728,6 +1743,16 @@ "crypto": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/controller-utils": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -2175,82 +2200,16 @@ "console.error": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, - "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, + "@metamask/json-rpc-engine": true, "@metamask/permission-controller>nanoid": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors": { - "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2674,6 +2633,21 @@ "immer": true } }, + "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2771,22 +2745,7 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": true - } - }, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": true } }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": { @@ -2824,9 +2783,9 @@ "@metamask/json-rpc-engine": true, "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2850,21 +2809,6 @@ "crypto.getRandomValues": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2922,8 +2866,8 @@ }, "@metamask/snaps-rpc-methods": { "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, "@metamask/snaps-utils": true, @@ -2932,26 +2876,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -3002,10 +2926,10 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, @@ -3021,26 +2945,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/snaps-registry": { "packages": { "@metamask/message-signing-snap>@noble/curves": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index f87946a09c0f..9bfa52031565 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -767,15 +767,30 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, + "@metamask/controller-utils>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -1728,6 +1743,16 @@ "crypto": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/controller-utils": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -2175,82 +2200,16 @@ "console.error": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, - "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, + "@metamask/json-rpc-engine": true, "@metamask/permission-controller>nanoid": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors": { - "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2674,6 +2633,21 @@ "immer": true } }, + "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2771,22 +2745,7 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": true - } - }, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": true } }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": { @@ -2824,9 +2783,9 @@ "@metamask/json-rpc-engine": true, "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2850,21 +2809,6 @@ "crypto.getRandomValues": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -2922,8 +2866,8 @@ }, "@metamask/snaps-rpc-methods": { "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, "@metamask/snaps-utils": true, @@ -2932,26 +2876,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -3002,10 +2926,10 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, @@ -3021,26 +2945,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/snaps-registry": { "packages": { "@metamask/message-signing-snap>@noble/curves": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 1bad9f3288a2..518e28b6b656 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -859,15 +859,30 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, + "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, - "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, + "@metamask/controller-utils>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, @@ -1820,6 +1835,16 @@ "crypto": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/controller-utils": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true @@ -2267,82 +2292,16 @@ "console.error": true }, "packages": { + "@metamask/base-controller": true, "@metamask/controller-utils": true, - "@metamask/permission-controller>@metamask/base-controller": true, - "@metamask/permission-controller>@metamask/json-rpc-engine": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/permission-controller>@metamask/utils": true, + "@metamask/json-rpc-engine": true, "@metamask/permission-controller>nanoid": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, "deep-freeze-strict": true, "immer": true } }, - "@metamask/permission-controller>@metamask/base-controller": { - "globals": { - "setTimeout": true - }, - "packages": { - "immer": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine": { - "packages": { - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": true, - "@metamask/permission-controller>@metamask/rpc-errors": true, - "@metamask/safe-event-emitter": true - } - }, - "@metamask/permission-controller>@metamask/json-rpc-engine>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors": { - "packages": { - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": true, - "@metamask/rpc-errors>fast-safe-stringify": true - } - }, - "@metamask/permission-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, - "@metamask/permission-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/permission-controller>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2766,6 +2725,21 @@ "immer": true } }, + "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true + } + }, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2863,22 +2837,7 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": true - } - }, - "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true + "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": true } }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": { @@ -2916,9 +2875,9 @@ "@metamask/json-rpc-engine": true, "@metamask/json-rpc-middleware-stream": true, "@metamask/object-multiplex": true, + "@metamask/permission-controller": true, "@metamask/post-message-stream": true, "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>@metamask/permission-controller": true, "@metamask/snaps-controllers>@xstate/fsm": true, "@metamask/snaps-controllers>concat-stream": true, "@metamask/snaps-controllers>get-npm-tarball-url": true, @@ -2942,21 +2901,6 @@ "crypto.getRandomValues": true } }, - "@metamask/snaps-controllers>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-controllers>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, "@metamask/snaps-controllers>concat-stream": { "packages": { "browserify>buffer": true, @@ -3014,8 +2958,8 @@ }, "@metamask/snaps-rpc-methods": { "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, "@metamask/snaps-utils": true, @@ -3024,26 +2968,6 @@ "@noble/hashes": true } }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-rpc-methods>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-sdk": { "globals": { "fetch": true @@ -3094,10 +3018,10 @@ "fetch": true }, "packages": { + "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/snaps-sdk": true, "@metamask/snaps-sdk>@metamask/key-tree": true, - "@metamask/snaps-utils>@metamask/permission-controller": true, "@metamask/snaps-utils>@metamask/slip44": true, "@metamask/snaps-utils>cron-parser": true, "@metamask/snaps-utils>fast-json-stable-stringify": true, @@ -3113,26 +3037,6 @@ "semver": true } }, - "@metamask/snaps-utils>@metamask/permission-controller": { - "globals": { - "console.error": true - }, - "packages": { - "@metamask/base-controller": true, - "@metamask/controller-utils": true, - "@metamask/json-rpc-engine": true, - "@metamask/rpc-errors": true, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": true, - "@metamask/utils": true, - "deep-freeze-strict": true, - "immer": true - } - }, - "@metamask/snaps-utils>@metamask/permission-controller>nanoid": { - "globals": { - "crypto.getRandomValues": true - } - }, "@metamask/snaps-utils>@metamask/snaps-registry": { "packages": { "@metamask/message-signing-snap>@noble/curves": true, diff --git a/package.json b/package.json index d1887ad8ebff..3b8fcd16dc65 100644 --- a/package.json +++ b/package.json @@ -331,7 +331,7 @@ "@metamask/message-manager": "^10.1.0", "@metamask/message-signing-snap": "^0.4.0", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-7d32a0b2", + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-bc072ca4", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 7980b900eae4..ee48078eddc2 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -464,8 +464,6 @@ class FixtureBuilder { requiredScopes: {}, optionalScopes: { 'eip155:1337': { - methods: [], - notifications: [], accounts: [ `eip155:1337:${selectedAccount.toLowerCase()}`, 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', @@ -502,8 +500,6 @@ class FixtureBuilder { requiredScopes: {}, optionalScopes: { 'eip155:1337': { - methods: [], - notifications: [], accounts: [ 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', ], @@ -537,8 +533,6 @@ class FixtureBuilder { requiredScopes: {}, optionalScopes: { 'eip155:1337': { - methods: [], - notifications: [], accounts: [ 'eip155:1337:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', @@ -567,8 +561,6 @@ class FixtureBuilder { requiredScopes: {}, optionalScopes: { 'eip155:1338': { - methods: [], - notifications: [], accounts: [ 'eip155:1338:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', 'eip155:1338:0x09781764c08de8ca82e156bbf156a3ca217c7950', @@ -1252,8 +1244,6 @@ class FixtureBuilder { requiredScopes: {}, optionalScopes: { 'eip155:1337': { - methods: [], - notifications: [], accounts: [ 'eip155:1337:0xbee150bdc171c7d4190891e78234f791a3ac7b24', 'eip155:1337:0xb9504634e5788208933b51ae7440b478bfadf865', @@ -1282,8 +1272,6 @@ class FixtureBuilder { requiredScopes: {}, optionalScopes: { 'eip155:1337': { - methods: [], - notifications: [], accounts: [ 'eip155:1337:0xbee150bdc171c7d4190891e78234f791a3ac7b24', 'eip155:1337:0xd1ca923697a701cba1364d803d72b4740fc39bc9', @@ -1312,8 +1300,6 @@ class FixtureBuilder { requiredScopes: {}, optionalScopes: { 'eip155:1337': { - methods: [], - notifications: [], accounts: [ 'eip155:1337:0xbee150bdc171c7d4190891e78234f791a3ac7b24', 'eip155:1337:0xa5c5293e124d04e2f85e8553851001fd2f192647', @@ -1343,8 +1329,6 @@ class FixtureBuilder { requiredScopes: {}, optionalScopes: { 'eip155:1337': { - methods: [], - notifications: [], accounts: [ 'eip155:1337:0xbee150bdc171c7d4190891e78234f791a3ac7b24', ], diff --git a/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.test.js b/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.test.js index 7b6e2595c63d..3767d56de8b7 100644 --- a/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.test.js +++ b/ui/components/app/alerts/unconnected-account-alert/unconnected-account-alert.test.js @@ -131,8 +131,6 @@ describe('Unconnected Account Alert', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', ], diff --git a/ui/components/multichain/account-list-menu/account-list-menu.test.tsx b/ui/components/multichain/account-list-menu/account-list-menu.test.tsx index ac4146a8a7c1..8be034f8559c 100644 --- a/ui/components/multichain/account-list-menu/account-list-menu.test.tsx +++ b/ui/components/multichain/account-list-menu/account-list-menu.test.tsx @@ -92,8 +92,6 @@ const render = ( requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', ], @@ -238,8 +236,6 @@ describe('AccountListMenu', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', ], @@ -379,8 +375,6 @@ describe('AccountListMenu', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', ], @@ -502,8 +496,6 @@ describe('AccountListMenu', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', ], diff --git a/ui/components/multichain/connected-accounts-menu/connected-accounts-menu.test.tsx b/ui/components/multichain/connected-accounts-menu/connected-accounts-menu.test.tsx index c7d0e91644db..d6a16cd4c995 100644 --- a/ui/components/multichain/connected-accounts-menu/connected-accounts-menu.test.tsx +++ b/ui/components/multichain/connected-accounts-menu/connected-accounts-menu.test.tsx @@ -89,8 +89,6 @@ const renderComponent = (props = {}, stateChanges = {}) => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', 'eip155:1:0x7250739de134d33ec7ab1ee592711e15098c9d2d', diff --git a/ui/components/multichain/pages/connections/connections.test.tsx b/ui/components/multichain/pages/connections/connections.test.tsx index ae351be699a7..a27e8bb36ed2 100644 --- a/ui/components/multichain/pages/connections/connections.test.tsx +++ b/ui/components/multichain/pages/connections/connections.test.tsx @@ -48,8 +48,6 @@ describe('Connections Content', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', ], @@ -87,8 +85,6 @@ describe('Connections Content', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', ], diff --git a/ui/components/multichain/pages/permissions-page/permissions-page.test.js b/ui/components/multichain/pages/permissions-page/permissions-page.test.js index 1adbdd5febe5..b257d85ae29a 100644 --- a/ui/components/multichain/pages/permissions-page/permissions-page.test.js +++ b/ui/components/multichain/pages/permissions-page/permissions-page.test.js @@ -43,8 +43,6 @@ mockState.metamask.subjects = { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', ], diff --git a/ui/components/multichain/pages/send/components/account-picker.test.tsx b/ui/components/multichain/pages/send/components/account-picker.test.tsx index 0905db72ecce..ffa37e757f0f 100644 --- a/ui/components/multichain/pages/send/components/account-picker.test.tsx +++ b/ui/components/multichain/pages/send/components/account-picker.test.tsx @@ -54,8 +54,6 @@ const render = ( requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', ], diff --git a/ui/components/multichain/permission-details-modal/permission-details-modal.test.tsx b/ui/components/multichain/permission-details-modal/permission-details-modal.test.tsx index 1aff89ae8015..1c700ad25371 100644 --- a/ui/components/multichain/permission-details-modal/permission-details-modal.test.tsx +++ b/ui/components/multichain/permission-details-modal/permission-details-modal.test.tsx @@ -77,8 +77,6 @@ describe('PermissionDetailsModal', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', 'eip155:1:0x7250739de134d33ec7ab1ee592711e15098c9d2d', diff --git a/ui/pages/routes/routes.component.test.js b/ui/pages/routes/routes.component.test.js index 555a7099ed2c..9aabee89ce48 100644 --- a/ui/pages/routes/routes.component.test.js +++ b/ui/pages/routes/routes.component.test.js @@ -212,8 +212,6 @@ describe('toast display', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [`eip155:1:${mockAccount.address}`], }, }, diff --git a/ui/selectors/permissions.test.js b/ui/selectors/permissions.test.js index f07b5422bf1e..a48cda996bd9 100644 --- a/ui/selectors/permissions.test.js +++ b/ui/selectors/permissions.test.js @@ -54,8 +54,6 @@ describe('selectors', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', ], @@ -82,8 +80,6 @@ describe('selectors', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', ], @@ -179,8 +175,6 @@ describe('selectors', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', ], @@ -207,8 +201,6 @@ describe('selectors', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', 'eip155:1:0x7250739de134d33ec7ab1ee592711e15098c9d2d', @@ -356,8 +348,6 @@ describe('selectors', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', 'eip155:1:0x7250739de134d33ec7ab1ee592711e15098c9d2d', @@ -388,8 +378,6 @@ describe('selectors', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', ], @@ -629,8 +617,6 @@ describe('selectors', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', 'eip155:1:0x7250739de134d33ec7ab1ee592711e15098c9d2d', @@ -658,8 +644,6 @@ describe('selectors', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', ], @@ -686,8 +670,6 @@ describe('selectors', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', ], @@ -737,8 +719,6 @@ describe('selectors', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x8e5d75d60224ea0c33d0041e75de68b1c3cb6dd5', 'eip155:1:0x7250739de134d33ec7ab1ee592711e15098c9d2d', diff --git a/ui/selectors/selectors.test.js b/ui/selectors/selectors.test.js index d1d5ca2a2ae6..95e7d7447fb1 100644 --- a/ui/selectors/selectors.test.js +++ b/ui/selectors/selectors.test.js @@ -1499,8 +1499,6 @@ describe('Selectors', () => { requiredScopes: {}, optionalScopes: { 'eip155:1': { - methods: [], - notifications: [], accounts: [ 'eip155:1:0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc', ], diff --git a/yarn.lock b/yarn.lock index 1c9fe410e967..85bedc11c100 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1646,17 +1646,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.0, @babel/types@npm:^7.13.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.24.0, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.25.9 - resolution: "@babel/types@npm:7.25.9" - dependencies: - "@babel/helper-string-parser": "npm:^7.25.9" - "@babel/helper-validator-identifier": "npm:^7.25.9" - checksum: 10/dd0f2874b10048aa230a5633ab440bbee8c3905f254ef26223b5321ddb824b057b9404d24a87556c6a9f7430198fa6311473778d147ed8ed7845428aee2ebc34 - languageName: node - linkType: hard - -"@babel/types@npm:^7.20.0": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.0, @babel/types@npm:^7.13.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.24.0, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.26.0 resolution: "@babel/types@npm:7.26.0" dependencies: @@ -5963,20 +5953,20 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-7d32a0b2": - version: 0.0.0-preview-7d32a0b2 - resolution: "@metamask-previews/multichain@npm:0.0.0-preview-7d32a0b2" +"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-bc072ca4": + version: 0.0.0-preview-bc072ca4 + resolution: "@metamask-previews/multichain@npm:0.0.0-preview-bc072ca4" dependencies: "@metamask/api-specs": "npm:^0.10.12" - "@metamask/controller-utils": "npm:^11.4.0" + "@metamask/controller-utils": "npm:^11.4.3" "@metamask/eth-json-rpc-filters": "npm:^7.0.0" - "@metamask/rpc-errors": "npm:^7.0.0" - "@metamask/utils": "npm:^9.1.0" + "@metamask/rpc-errors": "npm:^7.0.1" + "@metamask/utils": "npm:^10.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@metamask/network-controller": ^21.0.0 + "@metamask/network-controller": ^22.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/90855e2d9e382008736facef3bf1e0f1e2cf02f20087bb81c0bc352730d89cb4b3606bcbc15b36a9d4a8ecec4b590b42aadcb4106cc640f1054294e02a8d0682 + checksum: 10/f58ac69a1552765d406188e99e0b6e00e8927572e3079df12014a8a3e19a39c18e2b4305021d9bb8a56589cc941c66add8f9de2d6cf4bd5acbc83d8e99e282f4 languageName: node linkType: hard @@ -12499,16 +12489,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.1.0, acorn@npm:^8.10.0, acorn@npm:^8.11.3, acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.8.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": - version: 8.11.3 - resolution: "acorn@npm:8.11.3" - bin: - acorn: bin/acorn - checksum: 10/b688e7e3c64d9bfb17b596e1b35e4da9d50553713b3b3630cf5690f2b023a84eac90c56851e6912b483fe60e8b4ea28b254c07e92f17ef83d72d78745a8352dd - languageName: node - linkType: hard - -"acorn@npm:^8.14.0": +"acorn@npm:^8.1.0, acorn@npm:^8.10.0, acorn@npm:^8.11.3, acorn@npm:^8.14.0, acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.8.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": version: 8.14.0 resolution: "acorn@npm:8.14.0" bin: @@ -20330,17 +20311,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.6": - version: 1.15.6 - resolution: "follow-redirects@npm:1.15.6" - peerDependenciesMeta: - debug: - optional: true - checksum: 10/70c7612c4cab18e546e36b991bbf8009a1a41cf85354afe04b113d1117569abf760269409cb3eb842d9f7b03d62826687086b081c566ea7b1e6613cf29030bf7 - languageName: node - linkType: hard - -"follow-redirects@npm:^1.15.0": +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.0, follow-redirects@npm:^1.15.6": version: 1.15.9 resolution: "follow-redirects@npm:1.15.9" peerDependenciesMeta: @@ -26913,7 +26884,7 @@ __metadata: "@metamask/message-manager": "npm:^10.1.0" "@metamask/message-signing-snap": "npm:^0.4.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-7d32a0b2" + "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-bc072ca4" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch" "@metamask/notification-controller": "npm:^6.0.0" @@ -38408,7 +38379,7 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^2.3.4": +"yaml@npm:^2.3.4, yaml@npm:^2.4.1": version: 2.6.0 resolution: "yaml@npm:2.6.0" bin: @@ -38417,15 +38388,6 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^2.4.1": - version: 2.4.1 - resolution: "yaml@npm:2.4.1" - bin: - yaml: bin.mjs - checksum: 10/2c54fd69ef59126758ae710f9756405a7d41abcbb61aca894250d0e81e76057c14dc9bb00a9528f72f99b8f24077f694a6f7fd09cdd6711fcec2eebfbb5df409 - languageName: node - linkType: hard - "yargs-parser@npm:20.2.4": version: 20.2.4 resolution: "yargs-parser@npm:20.2.4" From 87ce7e4ef1951e47bcb265b20c062afdd997fdf3 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 21 Nov 2024 13:27:00 -0800 Subject: [PATCH 083/148] use `@metamask/multichain@^1.0.0`!!! --- package.json | 3 +-- yarn.lock | 29 +++++++++++++++-------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index d1887ad8ebff..0b411772274e 100644 --- a/package.json +++ b/package.json @@ -131,7 +131,6 @@ "attributions:generate": "./development/generate-attributions.sh" }, "resolutions": { - "@metamask/controller-utils": "npm:@metamask-previews/controller-utils@11.4.0-preview-7d32a0b2", "chokidar": "^3.6.0", "gridplus-sdk/elliptic": "^6.5.7", "gridplus-sdk/secp256k1": "^5.0.1", @@ -331,7 +330,7 @@ "@metamask/message-manager": "^10.1.0", "@metamask/message-signing-snap": "^0.4.0", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-7d32a0b2", + "@metamask/multichain": "^1.0.0", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index 1c9fe410e967..d41631a53fe6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5152,20 +5152,21 @@ __metadata: languageName: node linkType: hard -"@metamask/controller-utils@npm:@metamask-previews/controller-utils@11.4.0-preview-7d32a0b2": - version: 11.4.0-preview-7d32a0b2 - resolution: "@metamask-previews/controller-utils@npm:11.4.0-preview-7d32a0b2" +"@metamask/controller-utils@npm:^11.0.0, @metamask/controller-utils@npm:^11.0.2, @metamask/controller-utils@npm:^11.1.0, @metamask/controller-utils@npm:^11.2.0, @metamask/controller-utils@npm:^11.3.0, @metamask/controller-utils@npm:^11.4.0, @metamask/controller-utils@npm:^11.4.1, @metamask/controller-utils@npm:^11.4.2, @metamask/controller-utils@npm:^11.4.3": + version: 11.4.3 + resolution: "@metamask/controller-utils@npm:11.4.3" dependencies: "@ethereumjs/util": "npm:^8.1.0" "@metamask/eth-query": "npm:^4.0.0" "@metamask/ethjs-unit": "npm:^0.3.0" - "@metamask/utils": "npm:^9.1.0" + "@metamask/utils": "npm:^10.0.0" "@spruceid/siwe-parser": "npm:2.1.0" "@types/bn.js": "npm:^5.1.5" + bignumber.js: "npm:^9.1.2" bn.js: "npm:^5.2.1" eth-ens-namehash: "npm:^2.0.8" fast-deep-equal: "npm:^3.1.3" - checksum: 10/c090ae3438e304650fdd7c23c68940c0064f260fd7daee559c27bd0b8d087f75276b016ff2f7d3f8eb0fefb09063880f588ff762290c63044a9a587616d47005 + checksum: 10/5703b0721daf679cf44affc690f2b313e40893b64b0aafaf203e69ee51438197cc3634ef7094145f580a8a8aaadcb79026b2fbd4065c1bb4a8c26627a2c4c69a languageName: node linkType: hard @@ -5963,20 +5964,20 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:@metamask-previews/multichain@0.0.0-preview-7d32a0b2": - version: 0.0.0-preview-7d32a0b2 - resolution: "@metamask-previews/multichain@npm:0.0.0-preview-7d32a0b2" +"@metamask/multichain@npm:^1.0.0": + version: 1.0.0 + resolution: "@metamask/multichain@npm:1.0.0" dependencies: "@metamask/api-specs": "npm:^0.10.12" - "@metamask/controller-utils": "npm:^11.4.0" + "@metamask/controller-utils": "npm:^11.4.3" "@metamask/eth-json-rpc-filters": "npm:^7.0.0" - "@metamask/rpc-errors": "npm:^7.0.0" - "@metamask/utils": "npm:^9.1.0" + "@metamask/rpc-errors": "npm:^7.0.1" + "@metamask/utils": "npm:^10.0.0" lodash: "npm:^4.17.21" peerDependencies: - "@metamask/network-controller": ^21.0.0 + "@metamask/network-controller": ^22.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/90855e2d9e382008736facef3bf1e0f1e2cf02f20087bb81c0bc352730d89cb4b3606bcbc15b36a9d4a8ecec4b590b42aadcb4106cc640f1054294e02a8d0682 + checksum: 10/ec4ae86bb91a002863bf2b50900488bcb3851981d9b5e91f4d14bba05acf89dd7fce23738b11b1c5079fd3e0edc3702849e28e5ecd59b99a1f9ddedf19d3fd14 languageName: node linkType: hard @@ -26913,7 +26914,7 @@ __metadata: "@metamask/message-manager": "npm:^10.1.0" "@metamask/message-signing-snap": "npm:^0.4.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:@metamask-previews/multichain@0.0.0-preview-7d32a0b2" + "@metamask/multichain": "npm:^1.0.0" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch" "@metamask/notification-controller": "npm:^6.0.0" From fcffac40adef4dae946f9d5acab90373fff88eeb Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Thu, 21 Nov 2024 21:52:43 +0000 Subject: [PATCH 084/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 17 +---------------- lavamoat/browserify/flask/policy.json | 17 +---------------- lavamoat/browserify/main/policy.json | 17 +---------------- lavamoat/browserify/mmi/policy.json | 17 +---------------- 4 files changed, 4 insertions(+), 64 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 07dcc316a2f5..76418ab268d1 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -767,30 +767,15 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, - "@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 07dcc316a2f5..76418ab268d1 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -767,30 +767,15 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, - "@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 07dcc316a2f5..76418ab268d1 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -767,30 +767,15 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, - "@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index d4adb8335781..db0e2418b9da 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -859,30 +859,15 @@ }, "packages": { "@ethereumjs/tx>@ethereumjs/util": true, - "@metamask/controller-utils>@metamask/utils": true, "@metamask/controller-utils>@spruceid/siwe-parser": true, "@metamask/ethjs>@metamask/ethjs-unit": true, + "@metamask/utils": true, "bn.js": true, "browserify>buffer": true, "eslint>fast-deep-equal": true, "eth-ens-namehash": true } }, - "@metamask/controller-utils>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/controller-utils>@spruceid/siwe-parser": { "globals": { "console.error": true, From 13d0390d71b16416ec5bfd78d40821a5ff2df6b1 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 21 Nov 2024 14:11:02 -0800 Subject: [PATCH 085/148] yarn dedupe --- yarn.lock | 46 ++++------------------------------------------ 1 file changed, 4 insertions(+), 42 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4f5b10393f77..96e0ad9e37be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1646,17 +1646,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.0, @babel/types@npm:^7.13.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.24.0, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.25.9 - resolution: "@babel/types@npm:7.25.9" - dependencies: - "@babel/helper-string-parser": "npm:^7.25.9" - "@babel/helper-validator-identifier": "npm:^7.25.9" - checksum: 10/dd0f2874b10048aa230a5633ab440bbee8c3905f254ef26223b5321ddb824b057b9404d24a87556c6a9f7430198fa6311473778d147ed8ed7845428aee2ebc34 - languageName: node - linkType: hard - -"@babel/types@npm:^7.20.0": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.12.0, @babel/types@npm:^7.13.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.24.0, @babel/types@npm:^7.24.7, @babel/types@npm:^7.25.9, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.26.0 resolution: "@babel/types@npm:7.26.0" dependencies: @@ -12461,16 +12451,7 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.1.0, acorn@npm:^8.10.0, acorn@npm:^8.11.3, acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.8.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": - version: 8.11.3 - resolution: "acorn@npm:8.11.3" - bin: - acorn: bin/acorn - checksum: 10/b688e7e3c64d9bfb17b596e1b35e4da9d50553713b3b3630cf5690f2b023a84eac90c56851e6912b483fe60e8b4ea28b254c07e92f17ef83d72d78745a8352dd - languageName: node - linkType: hard - -"acorn@npm:^8.14.0": +"acorn@npm:^8.1.0, acorn@npm:^8.10.0, acorn@npm:^8.11.3, acorn@npm:^8.14.0, acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.8.1, acorn@npm:^8.8.2, acorn@npm:^8.9.0": version: 8.14.0 resolution: "acorn@npm:8.14.0" bin: @@ -20292,17 +20273,7 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.6": - version: 1.15.6 - resolution: "follow-redirects@npm:1.15.6" - peerDependenciesMeta: - debug: - optional: true - checksum: 10/70c7612c4cab18e546e36b991bbf8009a1a41cf85354afe04b113d1117569abf760269409cb3eb842d9f7b03d62826687086b081c566ea7b1e6613cf29030bf7 - languageName: node - linkType: hard - -"follow-redirects@npm:^1.15.0": +"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.14.9, follow-redirects@npm:^1.15.0, follow-redirects@npm:^1.15.6": version: 1.15.9 resolution: "follow-redirects@npm:1.15.9" peerDependenciesMeta: @@ -38371,7 +38342,7 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^2.3.4": +"yaml@npm:^2.3.4, yaml@npm:^2.4.1": version: 2.6.0 resolution: "yaml@npm:2.6.0" bin: @@ -38380,15 +38351,6 @@ __metadata: languageName: node linkType: hard -"yaml@npm:^2.4.1": - version: 2.4.1 - resolution: "yaml@npm:2.4.1" - bin: - yaml: bin.mjs - checksum: 10/2c54fd69ef59126758ae710f9756405a7d41abcbb61aca894250d0e81e76057c14dc9bb00a9528f72f99b8f24077f694a6f7fd09cdd6711fcec2eebfbb5df409 - languageName: node - linkType: hard - "yargs-parser@npm:20.2.4": version: 20.2.4 resolution: "yargs-parser@npm:20.2.4" From 9651ad4d913d34de7c58b3ab7db2923090841423 Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Fri, 22 Nov 2024 19:51:37 +0000 Subject: [PATCH 086/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 32 +++++++++++++-------------- lavamoat/browserify/flask/policy.json | 32 +++++++++++++-------------- lavamoat/browserify/main/policy.json | 32 +++++++++++++-------------- lavamoat/browserify/mmi/policy.json | 32 +++++++++++++-------------- 4 files changed, 64 insertions(+), 64 deletions(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 039288a1de2b..dfc40de06f9d 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -2616,21 +2616,6 @@ "immer": true } }, - "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2728,7 +2713,22 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": true + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true } }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": { diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 039288a1de2b..dfc40de06f9d 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -2616,21 +2616,6 @@ "immer": true } }, - "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2728,7 +2713,22 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": true + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true } }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": { diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 039288a1de2b..dfc40de06f9d 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -2616,21 +2616,6 @@ "immer": true } }, - "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2728,7 +2713,22 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": true + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true } }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": { diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index e3604b56de1d..b1f39006ba66 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -2708,21 +2708,6 @@ "immer": true } }, - "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": { - "globals": { - "TextDecoder": true, - "TextEncoder": true - }, - "packages": { - "@metamask/utils>@metamask/superstruct": true, - "@metamask/utils>@scure/base": true, - "@metamask/utils>pony-cause": true, - "@noble/hashes": true, - "browserify>buffer": true, - "nock>debug": true, - "semver": true - } - }, "@metamask/smart-transactions-controller>@metamask/controllers>nanoid": { "globals": { "crypto.getRandomValues": true @@ -2820,7 +2805,22 @@ "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors": { "packages": { "@metamask/rpc-errors>fast-safe-stringify": true, - "@metamask/smart-transactions-controller>@metamask/base-controller>@metamask/utils": true + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": true + } + }, + "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/rpc-errors>@metamask/utils": { + "globals": { + "TextDecoder": true, + "TextEncoder": true + }, + "packages": { + "@metamask/utils>@metamask/superstruct": true, + "@metamask/utils>@scure/base": true, + "@metamask/utils>pony-cause": true, + "@noble/hashes": true, + "browserify>buffer": true, + "nock>debug": true, + "semver": true } }, "@metamask/smart-transactions-controller>@metamask/transaction-controller>@metamask/utils": { From b4346f44180145c73e958baa8e5f2f5a066d0c97 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 25 Nov 2024 10:17:29 -0800 Subject: [PATCH 087/148] Fix edit accounts and networks modal default rerendering --- .../multichain/edit-accounts-modal/edit-accounts-modal.tsx | 2 +- .../multichain/edit-networks-modal/edit-networks-modal.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx index 3bbbad14c739..54d5adadf0f3 100644 --- a/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx +++ b/ui/components/multichain/edit-accounts-modal/edit-accounts-modal.tsx @@ -60,7 +60,7 @@ export const EditAccountsModal: React.FC = ({ useEffect(() => { setSelectedAccountAddresses(defaultSelectedAccountAddresses); - }, [defaultSelectedAccountAddresses]); + }, [JSON.stringify(defaultSelectedAccountAddresses)]); const selectAll = () => { const allNetworksAccountAddresses = accounts.map(({ address }) => address); diff --git a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js index 0b86716af50a..704f3eb3ced4 100644 --- a/ui/components/multichain/edit-networks-modal/edit-networks-modal.js +++ b/ui/components/multichain/edit-networks-modal/edit-networks-modal.js @@ -52,7 +52,7 @@ export const EditNetworksModal = ({ useEffect(() => { setSelectedChainIds(defaultSelectedChainIds); - }, [defaultSelectedChainIds]); + }, [JSON.stringify(defaultSelectedChainIds)]); const selectAll = () => { const allNetworksChainIds = allNetworks.map(({ chainId }) => chainId); From 0fead5c0c5229f2387daf49a4e961336944cac99 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 26 Nov 2024 09:40:51 -0800 Subject: [PATCH 088/148] move sessionChanged notif constant to Multichain PR --- app/scripts/controllers/permissions/enums.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/app/scripts/controllers/permissions/enums.ts b/app/scripts/controllers/permissions/enums.ts index 9210d6751bdc..c170bd78aa67 100644 --- a/app/scripts/controllers/permissions/enums.ts +++ b/app/scripts/controllers/permissions/enums.ts @@ -2,5 +2,4 @@ export enum NOTIFICATION_NAMES { accountsChanged = 'metamask_accountsChanged', unlockStateChanged = 'metamask_unlockStateChanged', chainChanged = 'metamask_chainChanged', - sessionChanged = 'wallet_sessionChanged', } From bf999731ade4b0511fdafd602cf02810c1cbefcf Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 26 Nov 2024 11:46:53 -0800 Subject: [PATCH 089/148] dispatch addPermittedChain in network-list-menu --- .../multichain/network-list-menu/network-list-menu.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/components/multichain/network-list-menu/network-list-menu.tsx b/ui/components/multichain/network-list-menu/network-list-menu.tsx index 6ffb45adc59e..b99e355e89b1 100644 --- a/ui/components/multichain/network-list-menu/network-list-menu.tsx +++ b/ui/components/multichain/network-list-menu/network-list-menu.tsx @@ -297,7 +297,7 @@ export const NetworkListMenu = ({ onClose }: { onClose: () => void }) => { } if (permittedAccountAddresses.length > 0) { - addPermittedChain(selectedTabOrigin, network.chainId); + dispatch(addPermittedChain(selectedTabOrigin, network.chainId)); if (!permittedChainIds.includes(network.chainId)) { dispatch(showPermittedNetworkToast()); } From d2fd2824de0fa6beee89a1f5476d2aae7d83a341 Mon Sep 17 00:00:00 2001 From: jiexi Date: Tue, 26 Nov 2024 11:49:15 -0800 Subject: [PATCH 090/148] Jl/caip25 permission migration/update mutators (#28709) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Replaces existing caveat mutators. Handles ensuring `wallet:eip155` is only upserted for permissions granted to snaps. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28709?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/develop/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../permissions/background-api.test.js | 29 -------- .../permissions/caveat-mutators.js | 71 ------------------- .../permissions/caveat-mutators.test.js | 67 ----------------- app/scripts/controllers/permissions/index.js | 1 - .../handlers/request-accounts.test.ts | 8 ++- .../handlers/request-accounts.ts | 8 ++- .../wallet-requestPermissions.test.ts | 8 ++- .../handlers/wallet-requestPermissions.ts | 9 ++- app/scripts/metamask-controller.js | 9 --- package.json | 2 +- yarn.lock | 10 +-- 11 files changed, 33 insertions(+), 189 deletions(-) delete mode 100644 app/scripts/controllers/permissions/caveat-mutators.js delete mode 100644 app/scripts/controllers/permissions/caveat-mutators.test.js diff --git a/app/scripts/controllers/permissions/background-api.test.js b/app/scripts/controllers/permissions/background-api.test.js index ff0ca9887ba7..bed2129354cb 100644 --- a/app/scripts/controllers/permissions/background-api.test.js +++ b/app/scripts/controllers/permissions/background-api.test.js @@ -120,14 +120,6 @@ describe('permission background API methods', () => { 'eip155:1:0x4', ], }, - 'wallet:eip155': { - accounts: [ - 'wallet:eip155:0x1', - 'wallet:eip155:0x2', - 'wallet:eip155:0x3', - 'wallet:eip155:0x4', - ], - }, }, isMultichainOrigin: true, }, @@ -245,15 +237,6 @@ describe('permission background API methods', () => { 'eip155:1:0x5', ], }, - 'wallet:eip155': { - accounts: [ - 'wallet:eip155:0x1', - 'wallet:eip155:0x2', - 'wallet:eip155:0x3', - 'wallet:eip155:0x4', - 'wallet:eip155:0x5', - ], - }, }, isMultichainOrigin: true, }, @@ -424,9 +407,6 @@ describe('permission background API methods', () => { 'eip155:1': { accounts: ['eip155:1:0x1', 'eip155:1:0x3'], }, - 'wallet:eip155': { - accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x3'], - }, }, isMultichainOrigin: true, }, @@ -510,9 +490,6 @@ describe('permission background API methods', () => { 'eip155:5': { accounts: ['eip155:5:0xdeadbeef'], }, - 'wallet:eip155': { - accounts: ['wallet:eip155:0xdeadbeef'], - }, }, isMultichainOrigin: false, }, @@ -619,9 +596,6 @@ describe('permission background API methods', () => { 'eip155:1337': { accounts: ['eip155:1337:0x1', 'eip155:1337:0x2'], }, - 'wallet:eip155': { - accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x2'], - }, }, isMultichainOrigin: true, }, @@ -727,9 +701,6 @@ describe('permission background API methods', () => { 'eip155:5': { accounts: ['eip155:5:0x1', 'eip155:5:0x2'], }, - 'wallet:eip155': { - accounts: ['wallet:eip155:0x1', 'wallet:eip155:0x2'], - }, }, isMultichainOrigin: true, }, diff --git a/app/scripts/controllers/permissions/caveat-mutators.js b/app/scripts/controllers/permissions/caveat-mutators.js deleted file mode 100644 index 047341e34770..000000000000 --- a/app/scripts/controllers/permissions/caveat-mutators.js +++ /dev/null @@ -1,71 +0,0 @@ -import { CaveatMutatorOperation } from '@metamask/permission-controller'; -import { CaveatTypes } from '../../../../shared/constants/permissions'; -import { normalizeSafeAddress } from '../../lib/multichain/address'; - -/** - * Factories that construct caveat mutator functions that are passed to - * PermissionController.updatePermissionsByCaveat. - */ -export const CaveatMutatorFactories = { - [CaveatTypes.restrictReturnedAccounts]: { - removeAccount, - }, - [CaveatTypes.restrictNetworkSwitching]: { - removeChainId, - }, -}; - -/** - * Removes the target account from the value arrays of all - * `restrictReturnedAccounts` caveats. No-ops if the target account is not in - * the array, and revokes the parent permission if it's the only account in - * the array. - * - * @param {string} targetAccount - The address of the account to remove from - * all accounts permissions. - * @param {string[]} existingAccounts - The account address array from the - * account permissions. - */ -function removeAccount(targetAccount, existingAccounts) { - const checkSumTargetAccount = normalizeSafeAddress(targetAccount); - const newAccounts = existingAccounts.filter( - (address) => normalizeSafeAddress(address) !== checkSumTargetAccount, - ); - - if (newAccounts.length === existingAccounts.length) { - return { operation: CaveatMutatorOperation.Noop }; - } else if (newAccounts.length > 0) { - return { - operation: CaveatMutatorOperation.UpdateValue, - value: newAccounts, - }; - } - return { operation: CaveatMutatorOperation.RevokePermission }; -} - -/** - * Removes the target chain ID from the value arrays of all - * `restrictNetworkSwitching` caveats. No-ops if the target chain ID is not in - * the array, and revokes the parent permission if it's the only chain ID in - * the array. - * - * @param {string} targetChainId - The chain ID to remove from - * all network switching permissions. - * @param {string[]} existingChainIds - The chain ID array from the - * network switching permissions. - */ -function removeChainId(targetChainId, existingChainIds) { - const newChainIds = existingChainIds.filter( - (chainId) => chainId !== targetChainId, - ); - - if (newChainIds.length === existingChainIds.length) { - return { operation: CaveatMutatorOperation.Noop }; - } else if (newChainIds.length > 0) { - return { - operation: CaveatMutatorOperation.UpdateValue, - value: newChainIds, - }; - } - return { operation: CaveatMutatorOperation.RevokePermission }; -} diff --git a/app/scripts/controllers/permissions/caveat-mutators.test.js b/app/scripts/controllers/permissions/caveat-mutators.test.js deleted file mode 100644 index 8c16924514f4..000000000000 --- a/app/scripts/controllers/permissions/caveat-mutators.test.js +++ /dev/null @@ -1,67 +0,0 @@ -import { CaveatMutatorOperation } from '@metamask/permission-controller'; -import { CaveatTypes } from '../../../../shared/constants/permissions'; -import { CaveatMutatorFactories } from './caveat-mutators'; - -const address1 = '0xbf16f7f5db8ae6af2512399bace3101debbde7fc'; -const address2 = '0xb6d5abeca51bfc3d53d00afed06b17eeea32ecdf'; -const nonEvmAddress = 'bc1qdkwac3em6mvlur4fatn2g4q050f4kkqadrsmnp'; - -describe('caveat mutators', () => { - describe('restrictReturnedAccounts', () => { - const { removeAccount } = - CaveatMutatorFactories[CaveatTypes.restrictReturnedAccounts]; - - describe('removeAccount', () => { - it('returns the no-op operation if the target account is not permitted', () => { - expect(removeAccount(address2, [address1])).toStrictEqual({ - operation: CaveatMutatorOperation.Noop, - }); - }); - - it('returns the update operation and a new value if the target account is permitted', () => { - expect(removeAccount(address2, [address1, address2])).toStrictEqual({ - operation: CaveatMutatorOperation.UpdateValue, - value: [address1], - }); - }); - - it('returns the revoke permission operation the target account is the only permitted account', () => { - expect(removeAccount(address1, [address1])).toStrictEqual({ - operation: CaveatMutatorOperation.RevokePermission, - }); - }); - - it('returns the revoke permission operation even if the target account is a checksummed address', () => { - const address3 = '0x95222290dd7278aa3ddd389cc1e1d165cc4baee5'; - const checksummedAddress3 = - '0x95222290dd7278AA3DDd389cc1E1d165Cc4BaeE5'; - expect(removeAccount(checksummedAddress3, [address3])).toStrictEqual({ - operation: CaveatMutatorOperation.RevokePermission, - }); - }); - - describe('Multichain behaviour', () => { - it('returns the no-op operation if the target account is not permitted', () => { - expect(removeAccount(address2, [nonEvmAddress])).toStrictEqual({ - operation: CaveatMutatorOperation.Noop, - }); - }); - - it('can revoke permission for non-EVM addresses', () => { - expect(removeAccount(nonEvmAddress, [nonEvmAddress])).toStrictEqual({ - operation: CaveatMutatorOperation.RevokePermission, - }); - }); - - it('returns the update operation and a new value if the target non-EVM account is permitted', () => { - expect( - removeAccount(nonEvmAddress, [address1, nonEvmAddress]), - ).toStrictEqual({ - operation: CaveatMutatorOperation.UpdateValue, - value: [address1], - }); - }); - }); - }); - }); -}); diff --git a/app/scripts/controllers/permissions/index.js b/app/scripts/controllers/permissions/index.js index b0ec94b175f1..76a460487dfe 100644 --- a/app/scripts/controllers/permissions/index.js +++ b/app/scripts/controllers/permissions/index.js @@ -1,4 +1,3 @@ -export * from './caveat-mutators'; export * from './background-api'; export * from './enums'; export * from './specifications'; diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts index 569d3f8ba5e8..e354a1701494 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts @@ -231,14 +231,18 @@ describe('requestEthereumAccountsHandler', () => { expect(MockMultichain.setPermittedEthChainIds).not.toHaveBeenCalled(); }); - it('sets the approved accounts on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is snapId', async () => { + it('sets the approved accounts for the `wallet:eip155` scope with isMultichainOrigin: false if origin is snapId', async () => { const { handler } = createMockedHandler(); await handler({ ...baseRequest, origin: 'npm:snap' }); expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( { requiredScopes: {}, - optionalScopes: {}, + optionalScopes: { + 'wallet:eip155': { + accounts: [], + }, + }, isMultichainOrigin: false, }, ['0xdeadbeef'], diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts index 1ce6c60e7f21..22b5ee5b3695 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts @@ -143,7 +143,13 @@ async function requestEthereumAccountsHandler( isMultichainOrigin: false, }; - if (!isSnapId(origin)) { + if (isSnapId(origin)) { + caveatValue.optionalScopes = { + 'wallet:eip155': { + accounts: [], + }, + }; + } else { caveatValue = setPermittedEthChainIds( caveatValue, legacyApproval.approvedChainIds, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts index c4418d9378c2..2e8a4ad3eb24 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts @@ -579,14 +579,18 @@ describe('requestPermissionsHandler', () => { expect(MockMultichain.setPermittedEthChainIds).not.toHaveBeenCalled(); }); - it('sets the approved accounts on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is snapId', async () => { + it('sets the approved accounts for the `wallet:eip155` scope with isMultichainOrigin: false if origin is snapId', async () => { const { handler } = createMockedHandler(); await handler({ ...getBaseRequest(), origin: 'npm:snapm' }); expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( { requiredScopes: {}, - optionalScopes: {}, + optionalScopes: { + 'wallet:eip155': { + accounts: [], + }, + }, isMultichainOrigin: false, }, ['0xdeadbeef'], diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index a059900a0622..143d2e970dfd 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -176,7 +176,14 @@ async function requestPermissionsImplementation( optionalScopes: {}, isMultichainOrigin: false, }; - if (!isSnapId(origin)) { + + if (isSnapId(origin)) { + newCaveatValue.optionalScopes = { + 'wallet:eip155': { + accounts: [], + }, + }; + } else { newCaveatValue = setPermittedEthChainIds( newCaveatValue, legacyApproval.approvedChainIds, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index af1b43085084..896b652fe8aa 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -192,7 +192,6 @@ import { } from '../../shared/constants/hardware-wallets'; import { KeyringType } from '../../shared/constants/keyring'; import { - CaveatTypes, RestrictedMethods, EndowmentPermissions, ExcludedSnapPermissions, @@ -325,7 +324,6 @@ import EncryptionPublicKeyController from './controllers/encryption-public-key'; import AppMetadataController from './controllers/app-metadata'; import { - CaveatMutatorFactories, getCaveatSpecifications, diffMap, getPermissionBackgroundApiMethods, @@ -5148,13 +5146,6 @@ export default class MetamaskController extends EventEmitter { * to third parties. */ removeAllAccountPermissions(targetAccount) { - this.permissionController.updatePermissionsByCaveat( - CaveatTypes.restrictReturnedAccounts, - (existingAccounts) => - CaveatMutatorFactories[ - CaveatTypes.restrictReturnedAccounts - ].removeAccount(targetAccount, existingAccounts), - ); this.permissionController.updatePermissionsByCaveat( Caip25CaveatType, (existingScopes) => diff --git a/package.json b/package.json index 7afed937b5fe..997455a093e0 100644 --- a/package.json +++ b/package.json @@ -326,7 +326,7 @@ "@metamask/message-manager": "^10.1.0", "@metamask/message-signing-snap": "^0.4.0", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "^1.0.0", + "@metamask/multichain": "^1.1.0", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch", "@metamask/notification-controller": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index 34965d7d49d4..1080fbc5d744 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5951,9 +5951,9 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:^1.0.0": - version: 1.0.0 - resolution: "@metamask/multichain@npm:1.0.0" +"@metamask/multichain@npm:^1.1.0": + version: 1.1.0 + resolution: "@metamask/multichain@npm:1.1.0" dependencies: "@metamask/api-specs": "npm:^0.10.12" "@metamask/controller-utils": "npm:^11.4.3" @@ -5964,7 +5964,7 @@ __metadata: peerDependencies: "@metamask/network-controller": ^22.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/ec4ae86bb91a002863bf2b50900488bcb3851981d9b5e91f4d14bba05acf89dd7fce23738b11b1c5079fd3e0edc3702849e28e5ecd59b99a1f9ddedf19d3fd14 + checksum: 10/dab9a223c6cf1b11705f53cab3c1d6f2ecfb5f0c2b7b44d32847ea42b5cd2c3a9936b10239d3dbf0215b2656b3584a08a2ee979004cedd2edfb27813ec25665d languageName: node linkType: hard @@ -26883,7 +26883,7 @@ __metadata: "@metamask/message-manager": "npm:^10.1.0" "@metamask/message-signing-snap": "npm:^0.4.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:^1.0.0" + "@metamask/multichain": "npm:^1.1.0" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A21.0.0#~/.yarn/patches/@metamask-network-controller-npm-21.0.0-559aa8e395.patch" "@metamask/notification-controller": "npm:^6.0.0" From b08f4e6d6684e824be07a61deed8350b2b020efd Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 4 Dec 2024 09:28:32 -0800 Subject: [PATCH 091/148] revert removeNetwork half fix --- app/scripts/metamask-controller.js | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 2c3451d9cbc5..fb8257ff69c6 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -3560,7 +3560,9 @@ export default class MetamaskController extends EventEmitter { updateNetwork: this.networkController.updateNetwork.bind( this.networkController, ), - removeNetwork: this.removeNetwork.bind(this), + removeNetwork: this.networkController.removeNetwork.bind( + this.networkController, + ), getCurrentNetworkEIP1559Compatibility: this.networkController.getEIP1559Compatibility.bind( this.networkController, @@ -5214,12 +5216,6 @@ export default class MetamaskController extends EventEmitter { ); } - removeNetwork(chainId) { - this.removeAllChainIdPermissions(chainId); - - this.networkController.removeNetwork(chainId); - } - /** * Stops exposing the account with the specified address to all third parties. * Exposed accounts are stored in caveats of the eth_accounts permission. This From 337a6a069a46750f642b54e1d16a6546424d6810 Mon Sep 17 00:00:00 2001 From: jiexi Date: Wed, 4 Dec 2024 10:24:39 -0800 Subject: [PATCH 092/148] replace snaps eth_accounts references (#28937) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Replaces eth_account references in snaps permission constants [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28937?quickstart=1) ## **Related issues** Related: https://github.com/MetaMask/metamask-extension/pull/27847 ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ![image](https://github.com/user-attachments/assets/44eba063-b7a2-4bd4-a9fa-177933dc6835) ### **After** ![image](https://github.com/user-attachments/assets/493310fa-1854-4304-91b6-3ab32325f040) ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- shared/constants/snaps/permissions.ts | 11 +++-------- ui/helpers/utils/permission.js | 8 ++------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/shared/constants/snaps/permissions.ts b/shared/constants/snaps/permissions.ts index 58f411ea4903..3aa94d9dd4ac 100644 --- a/shared/constants/snaps/permissions.ts +++ b/shared/constants/snaps/permissions.ts @@ -16,15 +16,10 @@ export const EndowmentPermissions = Object.freeze({ // Methods / permissions in external packages that we are temporarily excluding. export const ExcludedSnapPermissions = Object.freeze({ - eth_accounts: + 'endowment:caip25': 'eth_accounts is disabled. For more information please see https://github.com/MetaMask/snaps/issues/990.', }); -export const ExcludedSnapEndowments = Object.freeze({ - 'endowment:caip25': 'endowment:caip25', -}); +export const ExcludedSnapEndowments = Object.freeze({}); -export const DynamicSnapPermissions = Object.freeze([ - 'eth_accounts', - 'endowment:caip25', -]); +export const DynamicSnapPermissions = Object.freeze(['endowment:caip25']); diff --git a/ui/helpers/utils/permission.js b/ui/helpers/utils/permission.js index 2924474f795c..13162945faa1 100644 --- a/ui/helpers/utils/permission.js +++ b/ui/helpers/utils/permission.js @@ -8,6 +8,7 @@ import { getSnapDerivationPathName, } from '@metamask/snaps-utils'; import { isNonEmptyArray } from '@metamask/controller-utils'; +import { Caip25EndowmentPermissionName } from '@metamask/multichain'; import { RestrictedMethods, EndowmentPermissions, @@ -50,12 +51,7 @@ function getSnapNameComponent(snapName) { } export const PERMISSION_DESCRIPTIONS = deepFreeze({ - [RestrictedMethods.eth_accounts]: ({ t }) => ({ - label: t('permission_ethereumAccounts'), - leftIcon: IconName.Eye, - weight: PermissionWeight.eth_accounts, - }), - [EndowmentPermissions['endowment:caip25']]: ({ t }) => ({ + [Caip25EndowmentPermissionName]: ({ t }) => ({ label: t('permission_ethereumAccounts'), leftIcon: IconName.Eye, weight: PermissionWeight.eth_accounts, From e8c2a1a836115732e308980c8c3e79b8d5def7a1 Mon Sep 17 00:00:00 2001 From: jiexi Date: Wed, 4 Dec 2024 11:05:00 -0800 Subject: [PATCH 093/148] Jl/caip25 permission migration/fix locked wallet behavior (#28855) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** * Fix `wallet_getPermissions` not returning `eth_accounts` when the wallet is locked * Fix `eth_requestAccounts` not returning accounts when existing accounts are permissions after a locked wallet is unlocked * Combine `getPermittedAccounts` and `getPermittedAccountsSorted` in `MetaMaskController` * Cleanup specs [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/28855?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: Alex Donesky --- .../handlers/eth-accounts.test.ts | 2 +- .../handlers/eth-accounts.ts | 4 +- .../handlers/request-accounts.test.ts | 18 +- .../handlers/request-accounts.ts | 6 +- .../handlers/wallet-getPermissions.test.ts | 10 +- .../handlers/wallet-getPermissions.ts | 4 +- .../wallet-requestPermissions.test.ts | 6 +- .../handlers/wallet-requestPermissions.ts | 4 +- app/scripts/metamask-controller.js | 50 +++-- app/scripts/metamask-controller.test.js | 188 ++++++++++-------- test/e2e/fixture-builder.js | 37 +++- test/e2e/json-rpc/eth_accounts.spec.ts | 2 +- .../tokens/increase-token-allowance.spec.js | 27 +-- 13 files changed, 203 insertions(+), 155 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.ts index 0e0f98b17412..7aa367ec6873 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.test.ts @@ -15,7 +15,7 @@ const baseRequest = { const createMockedHandler = () => { const next = jest.fn(); const end = jest.fn(); - const getAccounts = jest.fn().mockResolvedValue(['0xdead', '0xbeef']); + const getAccounts = jest.fn().mockReturnValue(['0xdead', '0xbeef']); const response: PendingJsonRpcResponse = { jsonrpc: '2.0' as const, id: 0, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.ts b/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.ts index 565e9def6582..75d17fb2997b 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/eth-accounts.ts @@ -11,7 +11,7 @@ import { MESSAGE_TYPE } from '../../../../../shared/constants/app'; import { HandlerWrapper } from './types'; type EthAccountsHandlerOptions = { - getAccounts: () => Promise; + getAccounts: () => string[]; }; type EthAccountsConstraint = { @@ -53,6 +53,6 @@ async function ethAccountsHandler( end: JsonRpcEngineEndCallback, { getAccounts }: EthAccountsHandlerOptions, ): Promise { - res.result = await getAccounts(); + res.result = getAccounts(); return end(); } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts index e354a1701494..b7ae8dbcfba5 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts @@ -41,7 +41,7 @@ const baseRequest = { const createMockedHandler = () => { const next = jest.fn(); const end = jest.fn(); - const getAccounts = jest.fn().mockResolvedValue([]); + const getAccounts = jest.fn().mockReturnValue([]); const getUnlockPromise = jest.fn(); const requestPermissionApprovalForOrigin = jest.fn().mockResolvedValue({ approvedChainIds: ['0x1', '0x5'], @@ -107,13 +107,13 @@ describe('requestEthereumAccountsHandler', () => { const { handler, getAccounts } = createMockedHandler(); await handler(baseRequest); - expect(getAccounts).toHaveBeenCalled(); + expect(getAccounts).toHaveBeenCalledWith(true); }); describe('eip155 account permissions exist', () => { it('waits for the wallet to unlock', async () => { const { handler, getUnlockPromise, getAccounts } = createMockedHandler(); - getAccounts.mockResolvedValue(['0xdead', '0xbeef']); + getAccounts.mockReturnValue(['0xdead', '0xbeef']); await handler(baseRequest); expect(getUnlockPromise).toHaveBeenCalledWith(true); @@ -121,7 +121,7 @@ describe('requestEthereumAccountsHandler', () => { it('returns the accounts', async () => { const { handler, response, getAccounts } = createMockedHandler(); - getAccounts.mockResolvedValue(['0xdead', '0xbeef']); + getAccounts.mockReturnValue(['0xdead', '0xbeef']); await handler(baseRequest); expect(response.result).toStrictEqual(['0xdead', '0xbeef']); @@ -132,7 +132,7 @@ describe('requestEthereumAccountsHandler', () => { createMockedHandler(); const { promise, resolve } = deferredPromise(); getUnlockPromise.mockReturnValue(promise); - getAccounts.mockResolvedValue(['0xdead', '0xbeef']); + getAccounts.mockReturnValue(['0xdead', '0xbeef']); handler(baseRequest); expect(response).toStrictEqual({ @@ -285,8 +285,8 @@ describe('requestEthereumAccountsHandler', () => { it('returns the newly granted and properly ordered eth accounts', async () => { const { handler, getAccounts, response } = createMockedHandler(); getAccounts - .mockResolvedValueOnce([]) - .mockResolvedValueOnce(['0xdead', '0xbeef']); + .mockReturnValueOnce([]) + .mockReturnValueOnce(['0xdead', '0xbeef']); await handler(baseRequest); expect(response.result).toStrictEqual(['0xdead', '0xbeef']); @@ -296,8 +296,8 @@ describe('requestEthereumAccountsHandler', () => { it('emits the dapp viewed metrics event', async () => { const { handler, getAccounts, sendMetrics } = createMockedHandler(); getAccounts - .mockResolvedValueOnce([]) - .mockResolvedValueOnce(['0xdead', '0xbeef']); + .mockReturnValueOnce([]) + .mockReturnValueOnce(['0xdead', '0xbeef']); await handler(baseRequest); expect(sendMetrics).toHaveBeenCalledWith({ diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts index 22b5ee5b3695..236af869af12 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts @@ -80,7 +80,7 @@ async function requestEthereumAccountsHandler( metamaskState, grantPermissions, }: { - getAccounts: () => Promise; + getAccounts: (ignoreLock?: boolean) => string[]; getUnlockPromise: (shouldShowUnlockRequest: true) => Promise; requestPermissionApprovalForOrigin: ( requestedPermissions: RequestedPermissions, @@ -107,7 +107,7 @@ async function requestEthereumAccountsHandler( return end(); } - let ethAccounts = await getAccounts(); + let ethAccounts = getAccounts(true); if (ethAccounts.length > 0) { // We wait for the extension to unlock in this case only, because permission // requests are handled when the extension is unlocked, regardless of the @@ -172,7 +172,7 @@ async function requestEthereumAccountsHandler( }, }); - ethAccounts = await getAccounts(); + ethAccounts = getAccounts(true); // first time connection to dapp will lead to no log in the permissionHistory // and if user has connected to dapp before, the dapp origin will be included in the permissionHistory state // we will leverage that to identify `is_first_visit` for metrics diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts index 9f1ca4a2f9ea..8806508548a6 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts @@ -67,7 +67,7 @@ const createMockedHandler = () => { ); const getAccounts = jest .fn() - .mockResolvedValue(['0x1', '0x2', '0x3', '0xdeadbeef']); + .mockReturnValue(['0x1', '0x2', '0x3', '0xdeadbeef']); const response: PendingJsonRpcResponse = { jsonrpc: '2.0' as const, id: 0, @@ -143,7 +143,7 @@ describe('getPermissionsHandler', () => { describe('CAIP-25 endowment permissions has been granted', () => { it('returns the permissions with the CAIP-25 permission removed', async () => { const { handler, getAccounts, response } = createMockedHandler(); - getAccounts.mockResolvedValue([]); + getAccounts.mockReturnValue([]); await handler(baseRequest); expect(response.result).toStrictEqual([ { @@ -163,7 +163,7 @@ describe('getPermissionsHandler', () => { it('gets the lastSelected sorted permissioned eth accounts for the origin', async () => { const { handler, getAccounts } = createMockedHandler(); await handler(baseRequest); - expect(getAccounts).toHaveBeenCalled(); + expect(getAccounts).toHaveBeenCalledWith(true); }); it('returns the permissions with an eth_accounts permission if some eth accounts are permissioned', async () => { @@ -217,7 +217,7 @@ describe('getPermissionsHandler', () => { it('returns the permissions with a permittedChains permission if some eip155 chainIds are permissioned', async () => { const { handler, getAccounts, response } = createMockedHandler(); - getAccounts.mockResolvedValue([]); + getAccounts.mockReturnValue([]); MockMultichain.getPermittedEthChainIds.mockReturnValue(['0x1', '0x64']); await handler(baseRequest); @@ -248,7 +248,7 @@ describe('getPermissionsHandler', () => { it('returns the permissions with a eth_accounts and permittedChains permission if some eip155 accounts and chainIds are permissioned', async () => { const { handler, getAccounts, response } = createMockedHandler(); - getAccounts.mockResolvedValue(['0x1', '0x2', '0xdeadbeef']); + getAccounts.mockReturnValue(['0x1', '0x2', '0xdeadbeef']); MockMultichain.getPermittedEthChainIds.mockReturnValue(['0x1', '0x64']); await handler(baseRequest); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts index fa95d6b27773..e23d34bcffa1 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts @@ -57,7 +57,7 @@ async function getPermissionsImplementation( CaveatSpecificationConstraint >['getPermissions'] >; - getAccounts: () => Promise; + getAccounts: (ignoreLock?: boolean) => string[]; }, ) { const permissions = { ...getPermissionsForOrigin() }; @@ -70,7 +70,7 @@ async function getPermissionsImplementation( if (caip25CaveatValue) { // We cannot derive ethAccounts directly from the CAIP-25 permission // because the accounts will not be in order of lastSelected - const ethAccounts = await getAccounts(); + const ethAccounts = getAccounts(true); if (ethAccounts.length > 0) { permissions[RestrictedMethods.eth_accounts] = { diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts index 2e8a4ad3eb24..bf273ec91115 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts @@ -109,7 +109,7 @@ const createMockedHandler = () => { approvedChainIds: ['0x1', '0x5'], approvedAccounts: ['0xdeadbeef'], }); - const getAccounts = jest.fn().mockResolvedValue([]); + const getAccounts = jest.fn().mockReturnValue([]); const response: PendingJsonRpcResponse = { jsonrpc: '2.0' as const, id: 0, @@ -697,7 +697,7 @@ describe('requestPermissionsHandler', () => { it('returns both eth_accounts and permittedChains permissions in addition to other permissions that were granted if origin is not snapId', async () => { const { handler, getAccounts, response } = createMockedHandler(); - getAccounts.mockResolvedValue(['0xdeadbeef']); + getAccounts.mockReturnValue(['0xdeadbeef']); await handler(getBaseRequest()); expect(response.result).toStrictEqual([ @@ -731,7 +731,7 @@ describe('requestPermissionsHandler', () => { it('returns only eth_accounts permissions in addition to other permissions that were granted if origin is snapId', async () => { const { handler, getAccounts, response } = createMockedHandler(); - getAccounts.mockResolvedValue(['0xdeadbeef']); + getAccounts.mockReturnValue(['0xdeadbeef']); await handler({ ...getBaseRequest(), origin: 'npm:snap' }); expect(response.result).toStrictEqual([ diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index 143d2e970dfd..9d508e8bc490 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -105,7 +105,7 @@ async function requestPermissionsImplementation( getPermissionsForOrigin: () => ReturnType< AbstractPermissionController['getPermissions'] >; - getAccounts: () => Promise; + getAccounts: () => string[]; }, ) { const { origin, params } = req; @@ -233,7 +233,7 @@ async function requestPermissionsImplementation( // We cannot derive ethAccounts directly from the CAIP-25 permission // because the accounts will not be in order of lastSelected - const ethAccounts = await getAccounts(); + const ethAccounts = getAccounts(); grantedPermissions[RestrictedMethods.eth_accounts] = { ...caip25Endowment, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index fb8257ff69c6..d03ea684d5f9 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -153,7 +153,7 @@ import { import { Interface } from '@ethersproject/abi'; import { abiERC1155, abiERC721 } from '@metamask/metamask-eth-abis'; import { isEvmAccountType } from '@metamask/keyring-api'; -import { isValidHexAddress, toCaipChainId } from '@metamask/utils'; +import { toCaipChainId } from '@metamask/utils'; import { AuthenticationController, UserStorageController, @@ -2369,13 +2369,13 @@ export default class MetamaskController extends EventEmitter { }, version, // account mgmt - getAccounts: async ({ origin: innerOrigin }) => { + getAccounts: ({ origin: innerOrigin }) => { if (innerOrigin === ORIGIN_METAMASK) { const selectedAddress = this.accountsController.getSelectedAccount().address; return selectedAddress ? [selectedAddress] : []; } else if (this.isUnlocked()) { - return await this.getPermittedAccounts(innerOrigin); + return this.getPermittedAccounts(innerOrigin); } return []; // changing this is a breaking change }, @@ -3251,7 +3251,7 @@ export default class MetamaskController extends EventEmitter { return { isUnlocked: this.isUnlocked(), - accounts: await this.getPermittedAccounts(origin), + accounts: this.getPermittedAccounts(origin), ...providerNetworkState, }; } @@ -5118,11 +5118,7 @@ export default class MetamaskController extends EventEmitter { ); } - async getAllEvmAccountsSorted() { - // We only consider EVM addresses here, hence the filtering: - const accounts = (await this.keyringController.getAccounts()).filter( - isValidHexAddress, - ); + sortAccountsByLastSelected(accounts) { const internalAccounts = this.accountsController.listAccounts(); return accounts.sort((firstAddress, secondAddress) => { @@ -5166,14 +5162,18 @@ export default class MetamaskController extends EventEmitter { } /** - * Gets the permitted accounts for the specified origin. Returns an empty - * array if no accounts are permitted. + * Gets the sorted permitted accounts for the specified origin. Returns an empty + * array if no accounts are permitted or the wallet is locked. Returns any permitted + * accounts if the wallet is locked and `ignoreLock` is true. This lock bypass is needed + * for the `eth_requestAccounts` & `wallet_getPermission` handlers both of which + * return permissioned accounts to the dapp when the wallet is locked. * * @param {string} origin - The origin whose exposed accounts to retrieve. + * @param {boolean} ignoreLock - If accounts should be returned even if the wallet is locked. * @returns {Promise} The origin's permitted accounts, or an empty * array. */ - getPermittedAccounts(origin) { + getPermittedAccounts(origin, ignoreLock) { let caveat; try { caveat = this.permissionController.getCaveat( @@ -5184,19 +5184,17 @@ export default class MetamaskController extends EventEmitter { } catch (err) { // noop } + if (!caveat) { return []; } - return getEthAccounts(caveat.value); - } + if (!this.isUnlocked() && !ignoreLock) { + return []; + } - async getPermittedAccountsSorted(origin) { - const permittedAccounts = this.getPermittedAccounts(origin); - const allEvmAccounts = await this.getAllEvmAccountsSorted(); - return allEvmAccounts.filter((account) => - permittedAccounts.includes(account), - ); + const ethAccounts = getEthAccounts(caveat.value); + return this.sortAccountsByLastSelected(ethAccounts); } /** @@ -6009,7 +6007,7 @@ export default class MetamaskController extends EventEmitter { // middleware. engine.push( createEthAccountsMethodMiddleware({ - getAccounts: this.getPermittedAccountsSorted.bind(this, origin), + getAccounts: this.getPermittedAccounts.bind(this, origin), }), ); @@ -6072,7 +6070,7 @@ export default class MetamaskController extends EventEmitter { this.metaMetricsController, ), // Permission-related - getAccounts: this.getPermittedAccountsSorted.bind(this, origin), + getAccounts: this.getPermittedAccounts.bind(this, origin), getPermissionsForOrigin: this.permissionController.getPermissions.bind( this.permissionController, origin, @@ -6522,12 +6520,12 @@ export default class MetamaskController extends EventEmitter { * account(s) are currently accessible, if any. */ _onUnlock() { - this.notifyAllConnections(async (origin) => { + this.notifyAllConnections((origin) => { return { method: NOTIFICATION_NAMES.unlockStateChanged, params: { isUnlocked: true, - accounts: await this.getPermittedAccountsSorted(origin), + accounts: this.getPermittedAccounts(origin), }, }; }); @@ -7100,7 +7098,7 @@ export default class MetamaskController extends EventEmitter { await this.txController.updateIncomingTransactions(); } - async _notifyAccountsChange(origin, newAccounts) { + _notifyAccountsChange(origin, newAccounts) { if (this.isUnlocked()) { this.notifyConnections(origin, { method: NOTIFICATION_NAMES.accountsChanged, @@ -7113,7 +7111,7 @@ export default class MetamaskController extends EventEmitter { newAccounts : // If the length is 2 or greater, we have to execute // `eth_accounts` vi this method. - await this.getPermittedAccountsSorted(origin), + this.getPermittedAccounts(origin), }); } diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 152b89a6d0ad..4c4117095d9f 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -836,63 +836,102 @@ describe('MetaMaskController', () => { ).toStrictEqual([]); }); - it('returns the eth accounts from the CAIP-25 permission', async () => { - jest - .spyOn(metamaskController.permissionController, 'getCaveat') - .mockReturnValue({ - value: { - requiredScopes: {}, - optionalScopes: { - 'eip155:1': { - accounts: ['eip155:1:0xdead', 'eip155:1:0xbeef'], + describe('the wallet is locked', () => { + beforeEach(() => { + jest.spyOn(metamaskController, 'isUnlocked').mockReturnValue(false); + }); + + it('returns empty array if there is a CAIP-25 permission for the origin and ignoreLock is false', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + accounts: ['eip155:1:0xdead', 'eip155:1:0xbeef'], + }, }, }, - }, - }); - - expect( - metamaskController.getPermittedAccounts('test.com'), - ).toStrictEqual(['0xdead', '0xbeef']); - }); - }); + }); - describe('#getPermittedAccountsSorted', () => { - it('gets the permitted accounts for the origin', async () => { - jest - .spyOn(metamaskController, 'getPermittedAccounts') - .mockReturnValue([]); + expect( + metamaskController.getPermittedAccounts('test.com', false), + ).toStrictEqual([]); + }); - await metamaskController.getPermittedAccountsSorted('test.com'); + it('returns accounts if there is a CAIP-25 permission for the origin and ignoreLock is true', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + accounts: ['eip155:1:0xdead', 'eip155:1:0xbeef'], + }, + }, + }, + }); + jest + .spyOn(metamaskController, 'sortAccountsByLastSelected') + .mockReturnValue(['not_empty']); - expect(metamaskController.getPermittedAccounts).toHaveBeenCalledWith( - 'test.com', - ); + expect( + metamaskController.getPermittedAccounts('test.com', true), + ).toStrictEqual(['not_empty']); + }); }); - it('gets all evm accounts sorted by most recently used', async () => { - jest - .spyOn(metamaskController, 'getPermittedAccounts') - .mockReturnValue([]); - jest - .spyOn(metamaskController, 'getAllEvmAccountsSorted') - .mockResolvedValue([]); - - await metamaskController.getPermittedAccountsSorted('test.com'); + describe('the wallet is unlocked', () => { + beforeEach(() => { + jest.spyOn(metamaskController, 'isUnlocked').mockReturnValue(true); + }); - expect(metamaskController.getAllEvmAccountsSorted).toHaveBeenCalled(); - }); + it('sorts the eth accounts from the CAIP-25 permission', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + accounts: ['eip155:1:0xdead', 'eip155:1:0xbeef'], + }, + }, + }, + }); + jest + .spyOn(metamaskController, 'sortAccountsByLastSelected') + .mockReturnValue([]); - it('returns the permitted accounts for the origin sorted by most recently used', async () => { - jest - .spyOn(metamaskController, 'getPermittedAccounts') - .mockReturnValue(['0x1', '0x3', '0x5']); - jest - .spyOn(metamaskController, 'getAllEvmAccountsSorted') - .mockResolvedValue([]); + metamaskController.getPermittedAccounts('test.com'); + expect( + metamaskController.sortAccountsByLastSelected, + ).toHaveBeenCalledWith(['0xdead', '0xbeef']); + }); - await metamaskController.getPermittedAccountsSorted('test.com'); + it('returns the sorted eth accounts from the CAIP-25 permission', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + accounts: ['eip155:1:0xdead', 'eip155:1:0xbeef'], + }, + }, + }, + }); + jest + .spyOn(metamaskController, 'sortAccountsByLastSelected') + .mockReturnValue(['0xbeef', '0xdead']); - expect(metamaskController.getAllEvmAccountsSorted).toHaveBeenCalled(); + expect( + metamaskController.getPermittedAccounts('test.com'), + ).toStrictEqual(['0xbeef', '0xdead']); + }); }); }); @@ -957,8 +996,8 @@ describe('MetaMaskController', () => { }); }); - describe('#getAllEvmAccountsSorted', () => { - it('returns the keyring accounts in lastSelected order', async () => { + describe('#sortAccountsByLastSelected', () => { + it('returns the keyring accounts in lastSelected order', () => { jest .spyOn(metamaskController.accountsController, 'listAccounts') .mockReturnValueOnce([ @@ -1018,14 +1057,6 @@ describe('MetaMaskController', () => { type: EthAccountType.Eoa, }, ]); - jest - .spyOn(metamaskController.keyringController, 'getAccounts') - .mockResolvedValueOnce([ - '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', - '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', - '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', - '0x04eBa9B766477d8eCA77F5f0e67AE1863C95a7E3', - ]); jest .spyOn(metamaskController, 'captureKeyringTypesWithMissingIdentities') .mockImplementation(() => { @@ -1033,7 +1064,12 @@ describe('MetaMaskController', () => { }); expect( - await metamaskController.getAllEvmAccountsSorted(), + metamaskController.sortAccountsByLastSelected([ + '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', + '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', + '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', + '0x04eBa9B766477d8eCA77F5f0e67AE1863C95a7E3', + ]), ).toStrictEqual([ '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', '0x04eBa9B766477d8eCA77F5f0e67AE1863C95a7E3', @@ -1042,7 +1078,7 @@ describe('MetaMaskController', () => { ]); }); - it('throws if a keyring account is missing an address (case 1)', async () => { + it('throws if a keyring account is missing an address (case 1)', () => { const internalAccounts = [ { address: '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', @@ -1076,22 +1112,19 @@ describe('MetaMaskController', () => { jest .spyOn(metamaskController.accountsController, 'listAccounts') .mockReturnValueOnce(internalAccounts); - jest - .spyOn(metamaskController.keyringController, 'getAccounts') - .mockResolvedValueOnce([ - '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', - '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', - '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', - ]); jest .spyOn(metamaskController, 'captureKeyringTypesWithMissingIdentities') .mockImplementation(() => { // noop }); - await expect(() => - metamaskController.getAllEvmAccountsSorted(), - ).rejects.toThrow( + expect(() => + metamaskController.sortAccountsByLastSelected([ + '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', + '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', + '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', + ]), + ).toThrow( 'Missing identity for address: "0x7A2Bd22810088523516737b4Dc238A4bC37c23F2".', ); expect( @@ -1103,7 +1136,7 @@ describe('MetaMaskController', () => { ]); }); - it('throws if a keyring account is missing an address (case 2)', async () => { + it('throws if a keyring account is missing an address (case 2)', () => { const internalAccounts = [ { address: '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', @@ -1137,22 +1170,19 @@ describe('MetaMaskController', () => { jest .spyOn(metamaskController.accountsController, 'listAccounts') .mockReturnValueOnce(internalAccounts); - jest - .spyOn(metamaskController.keyringController, 'getAccounts') - .mockResolvedValueOnce([ - '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', - '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', - '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', - ]); jest .spyOn(metamaskController, 'captureKeyringTypesWithMissingIdentities') .mockImplementation(() => { // noop }); - await expect(() => - metamaskController.getAllEvmAccountsSorted(), - ).rejects.toThrow( + expect(() => + metamaskController.sortAccountsByLastSelected([ + '0x7A2Bd22810088523516737b4Dc238A4bC37c23F2', + '0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3', + '0xDe70d2FF1995DC03EF1a3b584e3ae14da020C616', + ]), + ).toThrow( 'Missing identity for address: "0x7152f909e5EB3EF198f17e5Cb087c5Ced88294e3".', ); expect( diff --git a/test/e2e/fixture-builder.js b/test/e2e/fixture-builder.js index 2a3ae827e88d..0cee4670975e 100644 --- a/test/e2e/fixture-builder.js +++ b/test/e2e/fixture-builder.js @@ -484,6 +484,41 @@ class FixtureBuilder { 'eip155:1337': { accounts: [ `eip155:1337:${selectedAccount.toLowerCase()}`, + ], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + id: 'ZaqPEWxyhNCJYACFw93jE', + date: 1664388714636, + invoker: DAPP_URL, + parentCapability: 'endowment:caip25', + }, + }, + }, + }; + return this.withPermissionController({ + subjects, + }); + } + + withPermissionControllerConnectedToTestDappWithTwoAccounts() { + const subjects = { + [DAPP_URL]: { + origin: DAPP_URL, + permissions: { + 'endowment:caip25': { + caveats: [ + { + type: 'authorizedScopes', + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1337': { + accounts: [ + 'eip155:1337:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', ], }, @@ -553,7 +588,6 @@ class FixtureBuilder { 'eip155:1337': { accounts: [ 'eip155:1337:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', - 'eip155:1337:0x09781764c08de8ca82e156bbf156a3ca217c7950', ], }, }, @@ -581,7 +615,6 @@ class FixtureBuilder { 'eip155:1338': { accounts: [ 'eip155:1338:0x5cfe73b6021e818b776b421b1c4db2474086a7e1', - 'eip155:1338:0x09781764c08de8ca82e156bbf156a3ca217c7950', ], }, }, diff --git a/test/e2e/json-rpc/eth_accounts.spec.ts b/test/e2e/json-rpc/eth_accounts.spec.ts index 149021d40a57..c4928dc04c3b 100644 --- a/test/e2e/json-rpc/eth_accounts.spec.ts +++ b/test/e2e/json-rpc/eth_accounts.spec.ts @@ -14,7 +14,7 @@ describe('eth_accounts', function () { .withKeyringControllerAdditionalAccountVault() .withPreferencesControllerAdditionalAccountIdentities() .withAccountsControllerAdditionalAccountIdentities() - .withPermissionControllerConnectedToTestDapp() + .withPermissionControllerConnectedToTestDappWithTwoAccounts() .build(), ganacheOptions: defaultGanacheOptions, title: this.test?.fullTitle(), diff --git a/test/e2e/tests/tokens/increase-token-allowance.spec.js b/test/e2e/tests/tokens/increase-token-allowance.spec.js index 9ce8db2cc065..e0bd0f894897 100644 --- a/test/e2e/tests/tokens/increase-token-allowance.spec.js +++ b/test/e2e/tests/tokens/increase-token-allowance.spec.js @@ -24,7 +24,10 @@ describe('Increase Token Allowance', function () { { dapp: true, fixtures: new FixtureBuilder() - .withPermissionControllerConnectedToTestDapp() + .withKeyringControllerAdditionalAccountVault() + .withPreferencesControllerAdditionalAccountIdentities() + .withAccountsControllerAdditionalAccountIdentities() + .withPermissionControllerConnectedToTestDappWithTwoAccounts() .build(), ganacheOptions: defaultGanacheOptions, smartContract, @@ -32,7 +35,7 @@ describe('Increase Token Allowance', function () { }, async ({ driver, contractRegistry }) => { const ACCOUNT_1_NAME = 'Account 1'; - const ACCOUNT_2_NAME = '2nd Account'; + const ACCOUNT_2_NAME = 'Account 2'; const initialSpendingCap = '1'; const additionalSpendingCap = '1'; @@ -41,6 +44,8 @@ describe('Increase Token Allowance', function () { await tempToggleSettingRedesignedTransactionConfirmations(driver); + await switchToAccountWithName(driver, ACCOUNT_1_NAME); + const contractAddress = await contractRegistry.getContractAddress( smartContract, ); @@ -50,7 +55,6 @@ describe('Increase Token Allowance', function () { await approveTokenSpendingCapTo(driver, ACCOUNT_2, initialSpendingCap); await sendTransaction(driver, ACCOUNT_2, '1'); - await addAccount(driver, ACCOUNT_2_NAME); await triggerTransferFromTokens(driver, ACCOUNT_1, ACCOUNT_2); // 'Transfer From Tokens' on the test dApp attempts to transfer 1.5 TST. @@ -126,23 +130,6 @@ describe('Increase Token Allowance', function () { }); } - async function addAccount(driver, newAccountName) { - await driver.clickElement('[data-testid="account-menu-icon"]'); - await driver.clickElement( - '[data-testid="multichain-account-menu-popover-action-button"]', - ); - await driver.clickElement( - '[data-testid="multichain-account-menu-popover-add-account"]', - ); - - await driver.fill('[placeholder="Account 2"]', newAccountName); - await driver.clickElement({ text: 'Add account', tag: 'button' }); - await driver.findElement({ - css: '[data-testid="account-menu-icon"]', - text: newAccountName, - }); - } - async function triggerTransferFromTokens( driver, senderAccount, From 7869d3badc1551993e7641f4217d1abc8e43f9b1 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 5 Dec 2024 14:26:02 -0800 Subject: [PATCH 094/148] yarn dedupe --- yarn.lock | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/yarn.lock b/yarn.lock index d6f41216fbbe..89efc6714806 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5792,7 +5792,7 @@ __metadata: languageName: node linkType: hard -"@metamask/network-controller@npm:22.1.0": +"@metamask/network-controller@npm:22.1.0, @metamask/network-controller@npm:^22.0.2": version: 22.1.0 resolution: "@metamask/network-controller@npm:22.1.0" dependencies: @@ -5818,31 +5818,6 @@ __metadata: languageName: node linkType: hard -"@metamask/network-controller@npm:^22.0.2": - version: 22.0.2 - resolution: "@metamask/network-controller@npm:22.0.2" - dependencies: - "@metamask/base-controller": "npm:^7.0.2" - "@metamask/controller-utils": "npm:^11.4.3" - "@metamask/eth-block-tracker": "npm:^11.0.2" - "@metamask/eth-json-rpc-infura": "npm:^10.0.0" - "@metamask/eth-json-rpc-middleware": "npm:^15.0.0" - "@metamask/eth-json-rpc-provider": "npm:^4.1.6" - "@metamask/eth-query": "npm:^4.0.0" - "@metamask/json-rpc-engine": "npm:^10.0.1" - "@metamask/rpc-errors": "npm:^7.0.1" - "@metamask/swappable-obj-proxy": "npm:^2.2.0" - "@metamask/utils": "npm:^10.0.0" - async-mutex: "npm:^0.5.0" - immer: "npm:^9.0.6" - loglevel: "npm:^1.8.1" - reselect: "npm:^5.1.1" - uri-js: "npm:^4.4.1" - uuid: "npm:^8.3.2" - checksum: 10/9da27189a4263ef7fa4596ada2000d7f944bc3f4dea63a77cf6f8b2ea89412d499068cf0542785088d19437263bd0b3b3bb3299533f87439729ccd8ecee2b625 - languageName: node - linkType: hard - "@metamask/network-controller@patch:@metamask/network-controller@npm%3A22.1.0#~/.yarn/patches/@metamask-network-controller-npm-22.1.0-621c281f70.patch": version: 22.1.0 resolution: "@metamask/network-controller@patch:@metamask/network-controller@npm%3A22.1.0#~/.yarn/patches/@metamask-network-controller-npm-22.1.0-621c281f70.patch::version=22.1.0&hash=93e992" From 5e67909dca0da8064fd871ae6a599d6ed1f931bc Mon Sep 17 00:00:00 2001 From: jiexi Date: Fri, 6 Dec 2024 15:16:53 -0800 Subject: [PATCH 095/148] Update app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js Co-authored-by: Elliot Winkler --- .../handlers/switch-ethereum-chain.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js index 6cd8dbf51fa4..40e435e49514 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js @@ -65,7 +65,7 @@ describe('switchEthereumChainHandler', () => { jest.clearAllMocks(); }); - it('returns null if the current chain id for the domain matches the chainId in the params', async () => { + it('returns null and does not try to switch the network if the current chain id for the domain matches the chainId in the params', async () => { const { end, response, handler } = createMockedHandler(); await handler({ origin: 'example.com', From 1e759cf0a918d62d0603c053cfceef97b18c5741 Mon Sep 17 00:00:00 2001 From: jiexi Date: Fri, 6 Dec 2024 15:17:07 -0800 Subject: [PATCH 096/148] Update app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts Co-authored-by: Elliot Winkler --- .../handlers/wallet-revokePermissions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts index b322e2a144ed..f3a684f6e69a 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts @@ -89,7 +89,7 @@ describe('revokePermissionsHandler', () => { ); }); - it('returns an error if params only the CAIP-25 permission is specified', () => { + it('returns an error if params only contains the CAIP-25 permission', () => { const { handler, end } = createMockedHandler(); const emptyRequest = { From d0890993816f55e42c864fa2f543af01a4156710 Mon Sep 17 00:00:00 2001 From: jiexi Date: Fri, 6 Dec 2024 15:18:56 -0800 Subject: [PATCH 097/148] Update app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts Co-authored-by: Elliot Winkler --- .../rpc-method-middleware/handlers/wallet-getPermissions.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts index e23d34bcffa1..cecff3488d12 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts @@ -62,9 +62,7 @@ async function getPermissionsImplementation( ) { const permissions = { ...getPermissionsForOrigin() }; const caip25Endowment = permissions[Caip25EndowmentPermissionName]; - const caip25CaveatValue = caip25Endowment?.caveats?.find( - ({ type }) => type === Caip25CaveatType, - )?.value as Caip25CaveatValue; + )?.value as Caip25CaveatValue | undefined; delete permissions[Caip25EndowmentPermissionName]; if (caip25CaveatValue) { From 7f6c67f03e16726d371d55ca6bda6826fc7fa0b0 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Fri, 6 Dec 2024 15:22:10 -0800 Subject: [PATCH 098/148] Add '| undefined' to caveat.find() return type coercions --- .../lib/rpc-method-middleware/handlers/wallet-getPermissions.ts | 2 +- .../rpc-method-middleware/handlers/wallet-requestPermissions.ts | 2 +- .../rpc-method-middleware/handlers/wallet-revokePermissions.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts index e23d34bcffa1..bdd2332a857a 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.ts @@ -64,7 +64,7 @@ async function getPermissionsImplementation( const caip25Endowment = permissions[Caip25EndowmentPermissionName]; const caip25CaveatValue = caip25Endowment?.caveats?.find( ({ type }) => type === Caip25CaveatType, - )?.value as Caip25CaveatValue; + )?.value as Caip25CaveatValue | undefined; delete permissions[Caip25EndowmentPermissionName]; if (caip25CaveatValue) { diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index 9d508e8bc490..36c8c878a3c7 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -199,7 +199,7 @@ async function requestPermissionsImplementation( let caip25Endowment = permissions[Caip25EndowmentPermissionName]; const existingCaveatValue = caip25Endowment?.caveats?.find( ({ type }) => type === Caip25CaveatType, - )?.value as Caip25CaveatValue; + )?.value as Caip25CaveatValue | undefined; if (existingCaveatValue) { if (existingCaveatValue.isMultichainOrigin) { return end( diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts index dd59c0d7cce3..80d9591a36b1 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.ts @@ -97,7 +97,7 @@ function revokePermissionsImplementation( const caip25Endowment = permissions?.[Caip25EndowmentPermissionName]; const caip25CaveatValue = caip25Endowment?.caveats?.find( ({ type }) => type === Caip25CaveatType, - )?.value as Caip25CaveatValue; + )?.value as Caip25CaveatValue | undefined; if (caip25CaveatValue?.isMultichainOrigin) { return end( From b7205e40fa6a9a465f16ec06e23e0a77473ad9c7 Mon Sep 17 00:00:00 2001 From: jiexi Date: Fri, 6 Dec 2024 15:26:01 -0800 Subject: [PATCH 099/148] Update app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js Co-authored-by: Elliot Winkler --- .../rpc-method-middleware/handlers/ethereum-chain-utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index 6b214ead3cd1..57b5b811134a 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -15,8 +15,8 @@ import { CaveatTypes } from '../../../../../shared/constants/permissions'; import { PermissionNames } from '../../../controllers/permissions'; export function validateChainId(chainId) { - const _chainId = typeof chainId === 'string' ? chainId.toLowerCase() : ''; - if (!isPrefixedFormattedHexString(_chainId)) { + const lowercasedChainId = typeof chainId === 'string' ? chainId.toLowerCase() : null; + if (lowercasedChainId !== null && !isPrefixedFormattedHexString(lowercasedChainId)) { throw rpcErrors.invalidParams({ message: `Expected 0x-prefixed, unpadded, non-zero hexadecimal string 'chainId'. Received:\n${chainId}`, }); From 4b37d18338fa679c5668d467cda58facef0b7b9b Mon Sep 17 00:00:00 2001 From: jiexi Date: Fri, 6 Dec 2024 15:27:04 -0800 Subject: [PATCH 100/148] Update app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js Co-authored-by: Elliot Winkler --- .../handlers/switch-ethereum-chain.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js index 40e435e49514..bab7c89de19e 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js @@ -81,7 +81,7 @@ describe('switchEthereumChainHandler', () => { expect(EthChainUtils.switchChain).not.toHaveBeenCalled(); }); - it('throws an error if unable to find a network matching the chainId in the params', async () => { + it('throws an error and does not try to switch the network if unable to find a network matching the chainId in the params', async () => { const { mocks, end, handler } = createMockedHandler(); mocks.getCurrentChainIdForDomain.mockReturnValue('0x1'); mocks.getNetworkConfigurationByChainId.mockReturnValue(undefined); From bc076905fba617cc01c04eaa48dfa0ab8a8d04bc Mon Sep 17 00:00:00 2001 From: jiexi Date: Fri, 6 Dec 2024 15:28:37 -0800 Subject: [PATCH 101/148] Update app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts Co-authored-by: Elliot Winkler --- .../handlers/wallet-requestPermissions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index 36c8c878a3c7..0b1d4572c742 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -163,11 +163,11 @@ async function requestPermissionsImplementation( !haveLegacyPermissions) || Object.keys(requestedPermissions).length > 0 ) { - const [_grantedPermissions] = await requestPermissionsForOrigin( + const [frozenGrantedPermissions] = await requestPermissionsForOrigin( requestedPermissions, ); // permissions are frozen and must be cloned before modified - grantedPermissions = { ..._grantedPermissions }; + grantedPermissions = { ...frozenGrantedPermissions }; } if (legacyApproval) { From 58e100ee59c10d03081f812dfcf2bdf77aedfb6f Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Fri, 6 Dec 2024 15:42:10 -0800 Subject: [PATCH 102/148] address request-accounts.test.ts comments --- .../handlers/request-accounts.test.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts index b7ae8dbcfba5..ea1e7d7ab1ae 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts @@ -90,7 +90,6 @@ const createMockedHandler = () => { describe('requestEthereumAccountsHandler', () => { beforeEach(() => { - MockUtil.shouldEmitDappViewedEvent.mockReturnValue(true); MockMultichain.setEthAccounts.mockImplementation( (caveatValue) => caveatValue, ); @@ -160,7 +159,7 @@ describe('requestEthereumAccountsHandler', () => { const { handler, requestPermissionApprovalForOrigin } = createMockedHandler(); - await handler(baseRequest); + await handler({...baseRequest, origin: 'http://test.com'}); expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ [RestrictedMethods.eth_accounts]: {}, [PermissionNames.permittedChains]: {}, @@ -293,11 +292,12 @@ describe('requestEthereumAccountsHandler', () => { expect(getAccounts).toHaveBeenCalledTimes(2); }); - it('emits the dapp viewed metrics event', async () => { + it('emits the dapp viewed metrics event when shouldEmitDappViewedEvent returns true', async () => { const { handler, getAccounts, sendMetrics } = createMockedHandler(); getAccounts .mockReturnValueOnce([]) .mockReturnValueOnce(['0xdead', '0xbeef']); + MockUtil.shouldEmitDappViewedEvent.mockReturnValue(true) await handler(baseRequest); expect(sendMetrics).toHaveBeenCalledWith({ @@ -313,5 +313,16 @@ describe('requestEthereumAccountsHandler', () => { }, }); }); + + it('does not emit the dapp viewed metrics event when shouldEmitDappViewedEvent returns false', async () => { + const { handler, getAccounts, sendMetrics } = createMockedHandler(); + getAccounts + .mockReturnValueOnce([]) + .mockReturnValueOnce(['0xdead', '0xbeef']); + MockUtil.shouldEmitDappViewedEvent.mockReturnValue(false) + + await handler(baseRequest); + expect(sendMetrics).not.toHaveBeenCalled(); + }); }); }); From 3e3409757c89bc2f15aa0f0b5aa036571ece29c0 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Fri, 6 Dec 2024 16:23:11 -0800 Subject: [PATCH 103/148] fully mock ethereum-chain-utils in add/switchEthreumChain handlers --- .../handlers/add-ethereum-chain.test.js | 80 ++++++++++++------- .../handlers/switch-ethereum-chain.test.js | 75 ++++++++--------- 2 files changed, 87 insertions(+), 68 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js index 97d726860168..aa00e3de05f1 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js @@ -5,6 +5,7 @@ import EthChainUtils from './ethereum-chain-utils'; jest.mock('./ethereum-chain-utils', () => ({ ...jest.requireActual('./ethereum-chain-utils'), + validateAddEthereumChainParams: jest.fn(), switchChain: jest.fn(), })); @@ -96,10 +97,58 @@ const createMockedHandler = () => { }; describe('addEthereumChainHandler', () => { + beforeEach(() => { + EthChainUtils.validateAddEthereumChainParams.mockImplementation((params) => { + const { chainId, chainName, blockExplorerUrls, rpcUrls, nativeCurrency} = params; + return { + chainId, + chainName, + firstValidBlockExplorerUrl: blockExplorerUrls[0] ?? null, + firstValidRPCUrl: rpcUrls[0], + ticker: nativeCurrency.symbol, + } + }) + }) + afterEach(() => { jest.clearAllMocks(); }); + it('should validate the request params', async () => { + const { handler } = createMockedHandler(); + + const request = { + origin: 'example.com', + params: [{ + foo: true + }], + } + + await handler(request); + + expect(EthChainUtils.validateAddEthereumChainParams).toHaveBeenCalledWith(request.params[0]) + }); + + + it('should return an error if request params validation fails', async () => { + const { end, handler } = createMockedHandler(); + EthChainUtils.validateAddEthereumChainParams.mockImplementation(() => { + throw new Error('failed to validate params') + }) + + await handler({ + origin: 'example.com', + params: [{}], + }); + + expect(end).toHaveBeenCalledWith( + rpcErrors.invalidParams( + new Error('failed to validate params') + ), + ); + }); + + it('creates a new network configuration for the given chainid and switches to it if no networkConfigurations with the same chainId exist', async () => { const nonInfuraConfiguration = createMockNonInfuraConfiguration(); @@ -235,37 +284,6 @@ describe('addEthereumChainHandler', () => { }); }); - it('should return an error if an unexpected parameter is provided', async () => { - const { end, handler } = createMockedHandler(); - - const unexpectedParam = 'unexpected'; - - await handler({ - origin: 'example.com', - params: [ - { - chainId: createMockNonInfuraConfiguration().chainId, - chainName: createMockNonInfuraConfiguration().nickname, - rpcUrls: [createMockNonInfuraConfiguration().rpcUrl], - nativeCurrency: { - symbol: createMockNonInfuraConfiguration().ticker, - decimals: 18, - }, - blockExplorerUrls: [ - createMockNonInfuraConfiguration().blockExplorerUrls[0], - ], - [unexpectedParam]: 'parameter', - }, - ], - }); - - expect(end).toHaveBeenCalledWith( - rpcErrors.invalidParams({ - message: `Received unexpected keys on object parameter. Unsupported keys:\n${unexpectedParam}`, - }), - ); - }); - it('should return an error if nativeCurrency.symbol does not match an existing network with the same chainId', async () => { const { mocks, end, handler } = createMockedHandler(); mocks.getNetworkConfigurationByChainId.mockReturnValue( diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js index bab7c89de19e..8f08dcd30929 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js @@ -8,6 +8,7 @@ import EthChainUtils from './ethereum-chain-utils'; jest.mock('./ethereum-chain-utils', () => ({ ...jest.requireActual('./ethereum-chain-utils'), + validateSwitchEthereumChainParams: jest.fn(), switchChain: jest.fn(), })); @@ -61,10 +62,47 @@ const createMockedHandler = () => { }; describe('switchEthereumChainHandler', () => { + beforeEach(() => { + EthChainUtils.validateSwitchEthereumChainParams.mockImplementation((request) => { + return request.params[0].chainId + }) + }) + afterEach(() => { jest.clearAllMocks(); }); + it('should validate the request params', async () => { + const { handler } = createMockedHandler(); + + const request = { + origin: 'example.com', + params: [{ + foo: true + }], + } + + await handler(request); + + expect(EthChainUtils.validateSwitchEthereumChainParams).toHaveBeenCalledWith(request) + }); + + it('should return an error if request params validation fails', async () => { + const { end, handler } = createMockedHandler(); + EthChainUtils.validateSwitchEthereumChainParams.mockImplementation(() => { + throw new Error('failed to validate params') + }) + + await handler({ + origin: 'example.com', + params: [{}], + }); + + expect(end).toHaveBeenCalledWith( + new Error('failed to validate params') + ); + }); + it('returns null and does not try to switch the network if the current chain id for the domain matches the chainId in the params', async () => { const { end, response, handler } = createMockedHandler(); await handler({ @@ -135,41 +173,4 @@ describe('switchEthereumChainHandler', () => { }, ); }); - - it('should return an error if an unexpected parameter is provided', async () => { - const { end, handler } = createMockedHandler(); - - const unexpectedParam = 'unexpected'; - - await handler({ - origin: 'example.com', - params: [ - { - chainId: createMockMainnetConfiguration().chainId, - [unexpectedParam]: 'parameter', - }, - ], - }); - - expect(end).toHaveBeenCalledWith( - rpcErrors.invalidParams({ - message: `Received unexpected keys on object parameter. Unsupported keys:\n${unexpectedParam}`, - }), - ); - }); - - it('should return error for invalid chainId', async () => { - const { handler, end } = createMockedHandler(); - - await handler({ - origin: 'example.com', - params: [{ chainId: 'invalid_chain_id' }], - }); - - expect(end).toHaveBeenCalledWith( - rpcErrors.invalidParams({ - message: `Expected 0x-prefixed, unpadded, non-zero hexadecimal string 'chainId'. Received:\ninvalid_chain_id`, - }), - ); - }); }); From baeeb1e40e147d4e0c950e717629bce34072ac9c Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 10:17:26 -0800 Subject: [PATCH 104/148] Copy validateAdd/SwitchEthereumChainParams specs to ethereum-chain-utils.test.ts --- .../handlers/ethereum-chain-utils.js | 14 ++- .../handlers/ethereum-chain-utils.test.ts | 96 ++++++++++++++++++- 2 files changed, 104 insertions(+), 6 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index 57b5b811134a..8add1b52cedc 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -15,20 +15,24 @@ import { CaveatTypes } from '../../../../../shared/constants/permissions'; import { PermissionNames } from '../../../controllers/permissions'; export function validateChainId(chainId) { - const lowercasedChainId = typeof chainId === 'string' ? chainId.toLowerCase() : null; - if (lowercasedChainId !== null && !isPrefixedFormattedHexString(lowercasedChainId)) { + const lowercasedChainId = + typeof chainId === 'string' ? chainId.toLowerCase() : null; + if ( + lowercasedChainId !== null && + !isPrefixedFormattedHexString(lowercasedChainId) + ) { throw rpcErrors.invalidParams({ message: `Expected 0x-prefixed, unpadded, non-zero hexadecimal string 'chainId'. Received:\n${chainId}`, }); } - if (!isSafeChainId(parseInt(_chainId, 16))) { + if (!isSafeChainId(parseInt(chainId, 16))) { throw rpcErrors.invalidParams({ - message: `Invalid chain ID "${_chainId}": numerical value greater than max safe value. Received:\n${chainId}`, + message: `Invalid chain ID "${lowercasedChainId}": numerical value greater than max safe value. Received:\n${chainId}`, }); } - return _chainId; + return lowercasedChainId; } export function validateSwitchEthereumChainParams(req) { diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts index fa8a90bdf8f4..e01c510b72d6 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts @@ -1,4 +1,4 @@ -import { errorCodes } from '@metamask/rpc-errors'; +import { errorCodes, rpcErrors } from '@metamask/rpc-errors'; import { Caip25CaveatType, Caip25EndowmentPermissionName, @@ -351,4 +351,98 @@ describe('Ethereum Chain Utils', () => { }, ); }); + + describe('validateAddEthereumChainParams', () => { + it('throws an error if an unexpected parameter is provided', () => { + const unexpectedParam = 'unexpected'; + + expect(() => { + EthChainUtils.validateAddEthereumChainParams({ + chainId: '0x1', + chainName: 'Mainnet', + rpcUrls: ['https://test.com/rpc'], + nativeCurrency: { + symbol: 'ETH', + decimals: 18, + }, + blockExplorerUrls: ['https://explorer.test.com/'], + [unexpectedParam]: 'parameter', + }); + }).toThrow( + rpcErrors.invalidParams({ + message: `Received unexpected keys on object parameter. Unsupported keys:\n${unexpectedParam}`, + }), + ); + }); + + it('returns a flattened version of params if it is valid', () => { + expect( + EthChainUtils.validateAddEthereumChainParams({ + chainId: '0x1', + chainName: 'Mainnet', + rpcUrls: ['https://test.com/rpc'], + nativeCurrency: { + symbol: 'ETH', + decimals: 18, + }, + blockExplorerUrls: ['https://explorer.test.com/'], + }), + ).toStrictEqual({ + chainId: '0x1', + chainName: 'Mainnet', + firstValidBlockExplorerUrl: 'https://explorer.test.com/', + firstValidRPCUrl: 'https://test.com/rpc', + ticker: 'ETH', + }); + }); + }); + + describe('validateSwitchEthereumChainParams', () => { + it('throws an error if an unexpected parameter is provided', () => { + const unexpectedParam = 'unexpected'; + + expect(() => { + EthChainUtils.validateSwitchEthereumChainParams({ + params: [ + { + chainId: '0x1', + [unexpectedParam]: 'parameter', + }, + ], + }); + }).toThrow( + rpcErrors.invalidParams({ + message: `Received unexpected keys on object parameter. Unsupported keys:\n${unexpectedParam}`, + }), + ); + }); + + it('throws an error for invalid chainId', async () => { + expect(() => { + EthChainUtils.validateSwitchEthereumChainParams({ + params: [ + { + chainId: 'invalid_chain_id', + }, + ], + }); + }).toThrow( + rpcErrors.invalidParams({ + message: `Expected 0x-prefixed, unpadded, non-zero hexadecimal string 'chainId'. Received:\ninvalid_chain_id`, + }), + ); + }); + + it('returns the chainId if it is valid', () => { + expect( + EthChainUtils.validateSwitchEthereumChainParams({ + params: [ + { + chainId: '0x1', + }, + ], + }), + ).toStrictEqual('0x1'); + }); + }); }); From 0f08ab41138221338ebcb4b4fc2da4657d4d51be Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 10:17:33 -0800 Subject: [PATCH 105/148] Lint --- .../handlers/add-ethereum-chain.test.js | 54 +++++++++++-------- .../handlers/request-accounts.test.ts | 6 +-- .../handlers/switch-ethereum-chain.test.js | 32 ++++++----- 3 files changed, 52 insertions(+), 40 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js index aa00e3de05f1..3da5f98541b2 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js @@ -98,17 +98,25 @@ const createMockedHandler = () => { describe('addEthereumChainHandler', () => { beforeEach(() => { - EthChainUtils.validateAddEthereumChainParams.mockImplementation((params) => { - const { chainId, chainName, blockExplorerUrls, rpcUrls, nativeCurrency} = params; - return { - chainId, - chainName, - firstValidBlockExplorerUrl: blockExplorerUrls[0] ?? null, - firstValidRPCUrl: rpcUrls[0], - ticker: nativeCurrency.symbol, - } - }) - }) + EthChainUtils.validateAddEthereumChainParams.mockImplementation( + (params) => { + const { + chainId, + chainName, + blockExplorerUrls, + rpcUrls, + nativeCurrency, + } = params; + return { + chainId, + chainName, + firstValidBlockExplorerUrl: blockExplorerUrls[0] ?? null, + firstValidRPCUrl: rpcUrls[0], + ticker: nativeCurrency.symbol, + }; + }, + ); + }); afterEach(() => { jest.clearAllMocks(); @@ -119,22 +127,25 @@ describe('addEthereumChainHandler', () => { const request = { origin: 'example.com', - params: [{ - foo: true - }], - } + params: [ + { + foo: true, + }, + ], + }; await handler(request); - expect(EthChainUtils.validateAddEthereumChainParams).toHaveBeenCalledWith(request.params[0]) + expect(EthChainUtils.validateAddEthereumChainParams).toHaveBeenCalledWith( + request.params[0], + ); }); - it('should return an error if request params validation fails', async () => { const { end, handler } = createMockedHandler(); EthChainUtils.validateAddEthereumChainParams.mockImplementation(() => { - throw new Error('failed to validate params') - }) + throw new Error('failed to validate params'); + }); await handler({ origin: 'example.com', @@ -142,13 +153,10 @@ describe('addEthereumChainHandler', () => { }); expect(end).toHaveBeenCalledWith( - rpcErrors.invalidParams( - new Error('failed to validate params') - ), + rpcErrors.invalidParams(new Error('failed to validate params')), ); }); - it('creates a new network configuration for the given chainid and switches to it if no networkConfigurations with the same chainId exist', async () => { const nonInfuraConfiguration = createMockNonInfuraConfiguration(); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts index ea1e7d7ab1ae..23fcf7cdbc15 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts @@ -159,7 +159,7 @@ describe('requestEthereumAccountsHandler', () => { const { handler, requestPermissionApprovalForOrigin } = createMockedHandler(); - await handler({...baseRequest, origin: 'http://test.com'}); + await handler({ ...baseRequest, origin: 'http://test.com' }); expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ [RestrictedMethods.eth_accounts]: {}, [PermissionNames.permittedChains]: {}, @@ -297,7 +297,7 @@ describe('requestEthereumAccountsHandler', () => { getAccounts .mockReturnValueOnce([]) .mockReturnValueOnce(['0xdead', '0xbeef']); - MockUtil.shouldEmitDappViewedEvent.mockReturnValue(true) + MockUtil.shouldEmitDappViewedEvent.mockReturnValue(true); await handler(baseRequest); expect(sendMetrics).toHaveBeenCalledWith({ @@ -319,7 +319,7 @@ describe('requestEthereumAccountsHandler', () => { getAccounts .mockReturnValueOnce([]) .mockReturnValueOnce(['0xdead', '0xbeef']); - MockUtil.shouldEmitDappViewedEvent.mockReturnValue(false) + MockUtil.shouldEmitDappViewedEvent.mockReturnValue(false); await handler(baseRequest); expect(sendMetrics).not.toHaveBeenCalled(); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js index 8f08dcd30929..93ca48339707 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js @@ -63,10 +63,12 @@ const createMockedHandler = () => { describe('switchEthereumChainHandler', () => { beforeEach(() => { - EthChainUtils.validateSwitchEthereumChainParams.mockImplementation((request) => { - return request.params[0].chainId - }) - }) + EthChainUtils.validateSwitchEthereumChainParams.mockImplementation( + (request) => { + return request.params[0].chainId; + }, + ); + }); afterEach(() => { jest.clearAllMocks(); @@ -77,30 +79,32 @@ describe('switchEthereumChainHandler', () => { const request = { origin: 'example.com', - params: [{ - foo: true - }], - } + params: [ + { + foo: true, + }, + ], + }; await handler(request); - expect(EthChainUtils.validateSwitchEthereumChainParams).toHaveBeenCalledWith(request) + expect( + EthChainUtils.validateSwitchEthereumChainParams, + ).toHaveBeenCalledWith(request); }); it('should return an error if request params validation fails', async () => { const { end, handler } = createMockedHandler(); EthChainUtils.validateSwitchEthereumChainParams.mockImplementation(() => { - throw new Error('failed to validate params') - }) + throw new Error('failed to validate params'); + }); await handler({ origin: 'example.com', params: [{}], }); - expect(end).toHaveBeenCalledWith( - new Error('failed to validate params') - ); + expect(end).toHaveBeenCalledWith(new Error('failed to validate params')); }); it('returns null and does not try to switch the network if the current chain id for the domain matches the chainId in the params', async () => { From f7a09fda54e6e4998b098b2ea84e8557b0e13d3d Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 10:27:50 -0800 Subject: [PATCH 106/148] Make wallet_requestPermissions test origin explicit --- .../handlers/switch-ethereum-chain.test.js | 2 +- .../handlers/wallet-requestPermissions.test.ts | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js index 93ca48339707..d131f7a67231 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js @@ -1,4 +1,4 @@ -import { providerErrors, rpcErrors } from '@metamask/rpc-errors'; +import { providerErrors } from '@metamask/rpc-errors'; import { CHAIN_IDS, NETWORK_TYPES, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts index bf273ec91115..944fc601b2f0 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts @@ -177,6 +177,7 @@ describe('requestPermissionsHandler', () => { await handler({ ...getBaseRequest(), + origin: 'http://test.com', params: [ { [RestrictedMethods.eth_accounts]: { @@ -200,6 +201,7 @@ describe('requestPermissionsHandler', () => { await handler({ ...getBaseRequest(), + origin: 'http://test.com', params: [ { [PermissionNames.permittedChains]: { @@ -233,6 +235,7 @@ describe('requestPermissionsHandler', () => { await handler({ ...getBaseRequest(), + origin: 'http://test.com', params: [ { [RestrictedMethods.eth_accounts]: { @@ -540,7 +543,10 @@ describe('requestPermissionsHandler', () => { it('sets the approved chainIds on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is not snapId', async () => { const { handler } = createMockedHandler(); - await handler(getBaseRequest()); + await handler({ + ...getBaseRequest(), + origin: 'http://test.com', + }); expect(MockMultichain.setPermittedEthChainIds).toHaveBeenCalledWith( { requiredScopes: {}, @@ -560,7 +566,10 @@ describe('requestPermissionsHandler', () => { isMultichainOrigin: false, }); - await handler(getBaseRequest()); + await handler({ + ...getBaseRequest(), + origin: 'http://test.com', + }); expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( { requiredScopes: {}, @@ -699,7 +708,10 @@ describe('requestPermissionsHandler', () => { const { handler, getAccounts, response } = createMockedHandler(); getAccounts.mockReturnValue(['0xdeadbeef']); - await handler(getBaseRequest()); + await handler({ + ...getBaseRequest(), + origin: 'http://test.com', + }); expect(response.result).toStrictEqual([ { caveats: [{ value: { foo: 'bar' } }], From ed503e4cb43f06b9eae4391359e5e56901f2931b Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 10:37:54 -0800 Subject: [PATCH 107/148] make getBaseRequest take overrides in wallet-requestPermissions.test.ts --- .../wallet-requestPermissions.test.ts | 416 +++++++++--------- 1 file changed, 217 insertions(+), 199 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts index 944fc601b2f0..45376011ae22 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts @@ -22,7 +22,7 @@ jest.mock('@metamask/multichain', () => ({ })); const MockMultichain = jest.mocked(Multichain); -const getBaseRequest = () => ({ +const getBaseRequest = (overrides = {}) => ({ jsonrpc: '2.0' as const, id: 0, method: 'wallet_requestPermissions', @@ -35,6 +35,7 @@ const getBaseRequest = () => ({ otherPermission: {}, }, ], + ...overrides, }); const createMockedHandler = () => { @@ -161,10 +162,7 @@ describe('requestPermissionsHandler', () => { it('returns an error if params is malformed', async () => { const { handler, end } = createMockedHandler(); - const malformedRequest = { - ...getBaseRequest(), - params: [], - }; + const malformedRequest = getBaseRequest({ params: [] }); await handler(malformedRequest); expect(end).toHaveBeenCalledWith( invalidParams({ data: { request: malformedRequest } }), @@ -175,17 +173,18 @@ describe('requestPermissionsHandler', () => { const { handler, requestPermissionApprovalForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - origin: 'http://test.com', - params: [ - { - [RestrictedMethods.eth_accounts]: { - foo: 'bar', + await handler( + getBaseRequest({ + origin: 'http://test.com', + params: [ + { + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, }, - }, - ], - }); + ], + }), + ); expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ [RestrictedMethods.eth_accounts]: { @@ -199,22 +198,23 @@ describe('requestPermissionsHandler', () => { const { handler, requestPermissionApprovalForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - origin: 'http://test.com', - params: [ - { - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x64'], - }, - ], + await handler( + getBaseRequest({ + origin: 'http://test.com', + params: [ + { + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, }, - }, - ], - }); + ], + }), + ); expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ [RestrictedMethods.eth_accounts]: {}, @@ -233,25 +233,26 @@ describe('requestPermissionsHandler', () => { const { handler, requestPermissionApprovalForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - origin: 'http://test.com', - params: [ - { - [RestrictedMethods.eth_accounts]: { - foo: 'bar', - }, - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x64'], - }, - ], + await handler( + getBaseRequest({ + origin: 'http://test.com', + params: [ + { + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, }, - }, - ], - }); + ], + }), + ); expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ [RestrictedMethods.eth_accounts]: { @@ -272,17 +273,18 @@ describe('requestPermissionsHandler', () => { const { handler, requestPermissionApprovalForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - origin: 'npm:snap', - params: [ - { - [RestrictedMethods.eth_accounts]: { - foo: 'bar', + await handler( + getBaseRequest({ + origin: 'npm:snap', + params: [ + { + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, }, - }, - ], - }); + ], + }), + ); expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ [RestrictedMethods.eth_accounts]: { @@ -295,22 +297,23 @@ describe('requestPermissionsHandler', () => { const { handler, requestPermissionApprovalForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - origin: 'npm:snap', - params: [ - { - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x64'], - }, - ], + await handler( + getBaseRequest({ + origin: 'npm:snap', + params: [ + { + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, }, - }, - ], - }); + ], + }), + ); expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ [RestrictedMethods.eth_accounts]: {}, @@ -321,25 +324,26 @@ describe('requestPermissionsHandler', () => { const { handler, requestPermissionApprovalForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - origin: 'npm:snap', - params: [ - { - [RestrictedMethods.eth_accounts]: { - foo: 'bar', - }, - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x64'], - }, - ], + await handler( + getBaseRequest({ + origin: 'npm:snap', + params: [ + { + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, }, - }, - ], - }); + ], + }), + ); expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ [RestrictedMethods.eth_accounts]: { @@ -351,15 +355,16 @@ describe('requestPermissionsHandler', () => { it('requests other permissions in params from the PermissionController, but ignores CAIP-25 if specified', async () => { const { handler, requestPermissionsForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - params: [ - { - [Caip25EndowmentPermissionName]: {}, - otherPermission: {}, - }, - ], - }); + await handler( + getBaseRequest({ + params: [ + { + [Caip25EndowmentPermissionName]: {}, + otherPermission: {}, + }, + ], + }), + ); expect(requestPermissionsForOrigin).toHaveBeenCalledWith({ otherPermission: {}, }); @@ -368,15 +373,16 @@ describe('requestPermissionsHandler', () => { it('requests other permissions in params from the PermissionController, but ignores eth_accounts if specified', async () => { const { handler, requestPermissionsForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - params: [ - { - [RestrictedMethods.eth_accounts]: {}, - otherPermission: {}, - }, - ], - }); + await handler( + getBaseRequest({ + params: [ + { + [RestrictedMethods.eth_accounts]: {}, + otherPermission: {}, + }, + ], + }), + ); expect(requestPermissionsForOrigin).toHaveBeenCalledWith({ otherPermission: {}, }); @@ -385,15 +391,16 @@ describe('requestPermissionsHandler', () => { it('requests other permissions in params from the PermissionController, but ignores permittedChains if specified', async () => { const { handler, requestPermissionsForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - params: [ - { - [PermissionNames.permittedChains]: {}, - otherPermission: {}, - }, - ], - }); + await handler( + getBaseRequest({ + params: [ + { + [PermissionNames.permittedChains]: {}, + otherPermission: {}, + }, + ], + }), + ); expect(requestPermissionsForOrigin).toHaveBeenCalledWith({ otherPermission: {}, }); @@ -402,74 +409,79 @@ describe('requestPermissionsHandler', () => { it('does not request permissions from the PermissionController when only eth_accounts is provided in params', async () => { const { handler, requestPermissionsForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - params: [ - { - [RestrictedMethods.eth_accounts]: {}, - }, - ], - }); + await handler( + getBaseRequest({ + params: [ + { + [RestrictedMethods.eth_accounts]: {}, + }, + ], + }), + ); expect(requestPermissionsForOrigin).not.toHaveBeenCalled(); }); it('does not request permissions from the PermissionController when only permittedChains is provided in params', async () => { const { handler, requestPermissionsForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - params: [ - { - [PermissionNames.permittedChains]: {}, - }, - ], - }); + await handler( + getBaseRequest({ + params: [ + { + [PermissionNames.permittedChains]: {}, + }, + ], + }), + ); expect(requestPermissionsForOrigin).not.toHaveBeenCalled(); }); it('does not request permissions from the PermissionController when both eth_accounts and permittedChains are provided in params', async () => { const { handler, requestPermissionsForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - params: [ - { - [RestrictedMethods.eth_accounts]: {}, - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x64'], - }, - ], + await handler( + getBaseRequest({ + params: [ + { + [RestrictedMethods.eth_accounts]: {}, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, }, - }, - ], - }); + ], + }), + ); expect(requestPermissionsForOrigin).not.toHaveBeenCalled(); }); it('requests empty permissions from the PermissionController when only CAIP-25 permission is provided in params', async () => { const { handler, requestPermissionsForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - params: [ - { - [Caip25EndowmentPermissionName]: {}, - }, - ], - }); + await handler( + getBaseRequest({ + params: [ + { + [Caip25EndowmentPermissionName]: {}, + }, + ], + }), + ); expect(requestPermissionsForOrigin).toHaveBeenCalledWith({}); }); it('requests empty permissions from the PermissionController when no permissions are provided in params', async () => { const { handler, requestPermissionsForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - params: [{}], - }); + await handler( + getBaseRequest({ + params: [{}], + }), + ); expect(requestPermissionsForOrigin).toHaveBeenCalledWith({}); }); @@ -477,14 +489,15 @@ describe('requestPermissionsHandler', () => { const { handler, updateCaveat, grantPermissions, getPermissionsForOrigin } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - params: [ - { - otherPermission: {}, - }, - ], - }); + await handler( + getBaseRequest({ + params: [ + { + otherPermission: {}, + }, + ], + }), + ); expect(getPermissionsForOrigin).not.toHaveBeenCalled(); expect(updateCaveat).not.toHaveBeenCalled(); expect(grantPermissions).not.toHaveBeenCalled(); @@ -493,14 +506,15 @@ describe('requestPermissionsHandler', () => { it('returns the granted permissions if eth_accounts and permittedChains approvals were not requested', async () => { const { handler, response } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - params: [ - { - otherPermission: {}, - }, - ], - }); + await handler( + getBaseRequest({ + params: [ + { + otherPermission: {}, + }, + ], + }), + ); expect(response.result).toStrictEqual([ { caveats: [{ value: { foo: 'bar' } }], @@ -523,14 +537,15 @@ describe('requestPermissionsHandler', () => { ); try { - await handler({ - ...getBaseRequest(), - params: [ - { - [RestrictedMethods.eth_accounts]: {}, - }, - ], - }); + await handler( + getBaseRequest({ + params: [ + { + [RestrictedMethods.eth_accounts]: {}, + }, + ], + }), + ); } catch (err) { // noop } @@ -543,10 +558,11 @@ describe('requestPermissionsHandler', () => { it('sets the approved chainIds on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is not snapId', async () => { const { handler } = createMockedHandler(); - await handler({ - ...getBaseRequest(), - origin: 'http://test.com', - }); + await handler( + getBaseRequest({ + origin: 'http://test.com', + }), + ); expect(MockMultichain.setPermittedEthChainIds).toHaveBeenCalledWith( { requiredScopes: {}, @@ -566,10 +582,11 @@ describe('requestPermissionsHandler', () => { isMultichainOrigin: false, }); - await handler({ - ...getBaseRequest(), - origin: 'http://test.com', - }); + await handler( + getBaseRequest({ + origin: 'http://test.com', + }), + ); expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( { requiredScopes: {}, @@ -584,14 +601,14 @@ describe('requestPermissionsHandler', () => { it('does not set the approved chainIds on an empty CAIP-25 caveat if origin is snapId', async () => { const { handler } = createMockedHandler(); - await handler({ ...getBaseRequest(), origin: 'npm:snapm' }); + await handler(getBaseRequest({ origin: 'npm:snap' })); expect(MockMultichain.setPermittedEthChainIds).not.toHaveBeenCalled(); }); it('sets the approved accounts for the `wallet:eip155` scope with isMultichainOrigin: false if origin is snapId', async () => { const { handler } = createMockedHandler(); - await handler({ ...getBaseRequest(), origin: 'npm:snapm' }); + await handler(getBaseRequest({ origin: 'npm:snap' })); expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( { requiredScopes: {}, @@ -708,10 +725,11 @@ describe('requestPermissionsHandler', () => { const { handler, getAccounts, response } = createMockedHandler(); getAccounts.mockReturnValue(['0xdeadbeef']); - await handler({ - ...getBaseRequest(), - origin: 'http://test.com', - }); + await handler( + getBaseRequest({ + origin: 'http://test.com', + }), + ); expect(response.result).toStrictEqual([ { caveats: [{ value: { foo: 'bar' } }], From ac48b67c2052052fbe21e06f6a2e5fe76aaff3f7 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 11:00:33 -0800 Subject: [PATCH 108/148] catch PermissionDoesNotExistError in getCaveat calls --- .../controllers/permissions/background-api.js | 33 ++++- .../permissions/background-api.test.js | 113 ++++++++++++++++-- app/scripts/metamask-controller.js | 7 +- app/scripts/metamask-controller.test.js | 15 ++- 4 files changed, 154 insertions(+), 14 deletions(-) diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index 486642b41ade..ba3219a9711e 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -1,5 +1,8 @@ import nanoid from 'nanoid'; -import { MethodNames } from '@metamask/permission-controller'; +import { + MethodNames, + PermissionDoesNotExistError, +} from '@metamask/permission-controller'; import { Caip25CaveatType, Caip25EndowmentPermissionName, @@ -29,7 +32,12 @@ export function getPermissionBackgroundApiMethods({ Caip25CaveatType, ); } catch (err) { - // noop + if (err instanceof PermissionDoesNotExistError) { + // suppress expected error in case that the origin + // does not have the target permission yet + } else { + throw err; + } } if (!caip25Caveat) { @@ -66,7 +74,12 @@ export function getPermissionBackgroundApiMethods({ Caip25CaveatType, ); } catch (err) { - // noop + if (err instanceof PermissionDoesNotExistError) { + // suppress expected error in case that the origin + // does not have the target permission yet + } else { + throw err; + } } if (!caip25Caveat) { @@ -116,7 +129,12 @@ export function getPermissionBackgroundApiMethods({ Caip25CaveatType, ); } catch (err) { - // noop + if (err instanceof PermissionDoesNotExistError) { + // suppress expected error in case that the origin + // does not have the target permission yet + } else { + throw err; + } } if (!caip25Caveat) { @@ -167,7 +185,12 @@ export function getPermissionBackgroundApiMethods({ Caip25CaveatType, ); } catch (err) { - // noop + if (err instanceof PermissionDoesNotExistError) { + // suppress expected error in case that the origin + // does not have the target permission yet + } else { + throw err; + } } if (!caip25Caveat) { diff --git a/app/scripts/controllers/permissions/background-api.test.js b/app/scripts/controllers/permissions/background-api.test.js index bed2129354cb..74a357f35f52 100644 --- a/app/scripts/controllers/permissions/background-api.test.js +++ b/app/scripts/controllers/permissions/background-api.test.js @@ -1,4 +1,7 @@ -import { MethodNames } from '@metamask/permission-controller'; +import { + MethodNames, + PermissionDoesNotExistError, +} from '@metamask/permission-controller'; import { Caip25CaveatType, Caip25EndowmentPermissionName, @@ -36,7 +39,9 @@ describe('permission background API methods', () => { it('throws an error if there is no existing CAIP-25 caveat', () => { const permissionController = { - getCaveat: jest.fn(), + getCaveat: jest.fn().mockImplementation(() => { + throw new PermissionDoesNotExistError(); + }), }; expect(() => @@ -50,6 +55,20 @@ describe('permission background API methods', () => { ); }); + it('throws an error if getCaveat fails unexpectedly', () => { + const permissionController = { + getCaveat: jest.fn().mockImplementation(() => { + throw new Error('unexpected getCaveat error'); + }), + }; + + expect(() => + getPermissionBackgroundApiMethods({ + permissionController, + }).addPermittedAccount('foo.com', '0x1'), + ).toThrow(new Error(`unexpected getCaveat error`)); + }); + it('calls updateCaveat with the account added', () => { const permissionController = { getCaveat: jest.fn().mockReturnValue({ @@ -150,7 +169,9 @@ describe('permission background API methods', () => { it('throws an error if there is no existing CAIP-25 caveat', () => { const permissionController = { - getCaveat: jest.fn(), + getCaveat: jest.fn().mockImplementation(() => { + throw new PermissionDoesNotExistError(); + }), }; expect(() => @@ -164,6 +185,20 @@ describe('permission background API methods', () => { ); }); + it('throws an error if getCaveat fails unexpectedly', () => { + const permissionController = { + getCaveat: jest.fn().mockImplementation(() => { + throw new Error('unexpected getCaveat error'); + }), + }; + + expect(() => + getPermissionBackgroundApiMethods({ + permissionController, + }).addPermittedAccounts('foo.com', ['0x1']), + ).toThrow(new Error(`unexpected getCaveat error`)); + }); + it('calls updateCaveat with the accounts added to only eip155 scopes and all accounts for eip155 scopes synced', () => { const permissionController = { getCaveat: jest.fn().mockReturnValue({ @@ -267,7 +302,9 @@ describe('permission background API methods', () => { it('throws an error if there is no existing CAIP-25 caveat', () => { const permissionController = { - getCaveat: jest.fn(), + getCaveat: jest.fn().mockImplementation(() => { + throw new PermissionDoesNotExistError(); + }), }; expect(() => @@ -281,6 +318,20 @@ describe('permission background API methods', () => { ); }); + it('throws an error if getCaveat fails unexpectedly', () => { + const permissionController = { + getCaveat: jest.fn().mockImplementation(() => { + throw new Error('unexpected getCaveat error'); + }), + }; + + expect(() => + getPermissionBackgroundApiMethods({ + permissionController, + }).removePermittedAccount('foo.com', '0x1'), + ).toThrow(new Error(`unexpected getCaveat error`)); + }); + it('does nothing if the account being removed does not exist', () => { const permissionController = { getCaveat: jest.fn().mockReturnValue({ @@ -524,7 +575,9 @@ describe('permission background API methods', () => { it('throws an error if there is no existing CAIP-25 caveat', () => { const permissionController = { - getCaveat: jest.fn(), + getCaveat: jest.fn().mockImplementation(() => { + throw new PermissionDoesNotExistError(); + }), }; expect(() => @@ -538,6 +591,20 @@ describe('permission background API methods', () => { ); }); + it('throws an error if getCaveat fails unexpectedly', () => { + const permissionController = { + getCaveat: jest.fn().mockImplementation(() => { + throw new Error('unexpected getCaveat error'); + }), + }; + + expect(() => + getPermissionBackgroundApiMethods({ + permissionController, + }).addPermittedChain('foo.com', '0x1'), + ).toThrow(new Error(`unexpected getCaveat error`)); + }); + it('calls updateCaveat with the chain added and all eip155 accounts synced', () => { const permissionController = { getCaveat: jest.fn().mockReturnValue({ @@ -626,7 +693,9 @@ describe('permission background API methods', () => { it('throws an error if there is no existing CAIP-25 caveat', () => { const permissionController = { - getCaveat: jest.fn(), + getCaveat: jest.fn().mockImplementation(() => { + throw new PermissionDoesNotExistError(); + }), }; expect(() => @@ -640,6 +709,20 @@ describe('permission background API methods', () => { ); }); + it('throws an error if getCaveat fails unexpectedly', () => { + const permissionController = { + getCaveat: jest.fn().mockImplementation(() => { + throw new Error('unexpected getCaveat error'); + }), + }; + + expect(() => + getPermissionBackgroundApiMethods({ + permissionController, + }).addPermittedChains('foo.com', ['0x1']), + ).toThrow(new Error(`unexpected getCaveat error`)); + }); + it('calls updateCaveat with the chains added and all eip155 accounts synced', () => { const permissionController = { getCaveat: jest.fn().mockReturnValue({ @@ -731,7 +814,9 @@ describe('permission background API methods', () => { it('throws an error if there is no existing CAIP-25 caveat', () => { const permissionController = { - getCaveat: jest.fn(), + getCaveat: jest.fn().mockImplementation(() => { + throw new PermissionDoesNotExistError(); + }), }; expect(() => @@ -745,6 +830,20 @@ describe('permission background API methods', () => { ); }); + it('throws an error if getCaveat fails unexpectedly', () => { + const permissionController = { + getCaveat: jest.fn().mockImplementation(() => { + throw new Error('unexpected getCaveat error'); + }), + }; + + expect(() => + getPermissionBackgroundApiMethods({ + permissionController, + }).removePermittedChain('foo.com', '0x1'), + ).toThrow(new Error(`unexpected getCaveat error`)); + }); + it('does nothing if the chain being removed does not exist', () => { const permissionController = { getCaveat: jest.fn().mockReturnValue({ diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 3659cedf7ae1..c00d9edcb5b4 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -5201,7 +5201,12 @@ export default class MetamaskController extends EventEmitter { Caip25CaveatType, ); } catch (err) { - // noop + if (err instanceof PermissionDoesNotExistError) { + // suppress expected error in case that the origin + // does not have the target permission yet + } else { + throw err; + } } if (!caveat) { diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 4c4117095d9f..49995873735c 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -36,6 +36,7 @@ import { Caip25CaveatType, Caip25EndowmentPermissionName, } from '@metamask/multichain'; +import { PermissionDoesNotExistError } from '@metamask/permission-controller'; import { createTestProviderTools } from '../../test/stub/provider'; import { HardwareDeviceNames } from '../../shared/constants/hardware-wallets'; import { KeyringType } from '../../shared/constants/keyring'; @@ -828,7 +829,7 @@ describe('MetaMaskController', () => { jest .spyOn(metamaskController.permissionController, 'getCaveat') .mockImplementation(() => { - throw new Error('no caveat'); + throw new PermissionDoesNotExistError(); }); expect( @@ -836,6 +837,18 @@ describe('MetaMaskController', () => { ).toStrictEqual([]); }); + it('throws an error if getCaveat fails unexpectedly', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockImplementation(() => { + throw new Error('unexpected getCaveat error'); + }); + + expect(() => { + metamaskController.getPermittedAccounts('test.com'); + }).toThrow(new Error(`unexpected getCaveat error`)); + }); + describe('the wallet is locked', () => { beforeEach(() => { jest.spyOn(metamaskController, 'isUnlocked').mockReturnValue(false); From fb118fe7420fb53bf41c985bd4a2a82e5e96c921 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 11:12:47 -0800 Subject: [PATCH 109/148] update addMoreChains getEthAccounts comment --- app/scripts/controllers/permissions/background-api.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index ba3219a9711e..14b4886db7ba 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -88,7 +88,9 @@ export function getPermissionBackgroundApiMethods({ ); } - // get the list of permitted eth accounts before we modify the permitted chains and potentially lose some + // In the case that the new set of permitted eth chainIds does not overlap at all with + // the old set, we will lose context of the permitted eth accounts if we drop + // the old set of eth scopes without first noting which eth accounts are permitted first. const ethAccounts = getEthAccounts(caip25Caveat.value); const ethChainIds = getPermittedEthChainIds(caip25Caveat.value); From 3442d7dc85aaae0a52e67f683c4b49ace4ebf702 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 11:19:03 -0800 Subject: [PATCH 110/148] make otherPermission explicit in wallet-revokePermissions.test.ts --- .../handlers/wallet-revokePermissions.test.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts index f3a684f6e69a..b637784bbd73 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-revokePermissions.test.ts @@ -198,7 +198,15 @@ describe('revokePermissionsHandler', () => { it('revokes permissions other than eth_accounts, permittedChains, CAIP-25 if specified', () => { const { handler, revokePermissionsForOrigin } = createMockedHandler(); - handler(baseRequest); + handler({ + ...baseRequest, + params: [ + { + [Caip25EndowmentPermissionName]: {}, + otherPermission: {}, + }, + ], + }); expect(revokePermissionsForOrigin).toHaveBeenCalledWith([ 'otherPermission', ]); From c69eda3c5e978c880c9a10101111d24a222c89fa Mon Sep 17 00:00:00 2001 From: jiexi Date: Mon, 9 Dec 2024 11:21:21 -0800 Subject: [PATCH 111/148] Update app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts Co-authored-by: Elliot Winkler --- .../handlers/wallet-requestPermissions.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index 0b1d4572c742..2e49d1a1c0b0 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -260,6 +260,8 @@ async function requestPermissionsImplementation( } } - res.result = Object.values(grantedPermissions) as Json; + res.result = Object.values(grantedPermissions).filter( + (value) => value !== undefined, + ) as Json; return end(); } From 892af784ae5dfd9ddd08206a830c16855da86e4c Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 11:23:13 -0800 Subject: [PATCH 112/148] nest scenario in describe add-ethereum-chain.test.js --- .../handlers/add-ethereum-chain.test.js | 84 ++++++++++--------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js index 3da5f98541b2..3b71e04b24ce 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js @@ -245,50 +245,52 @@ describe('addEthereumChainHandler', () => { }); }); - it('should switch to the existing networkConfiguration if one already exists for the given chain id', async () => { - const { mocks, end, handler } = createMockedHandler(); - mocks.getCurrentChainIdForDomain.mockReturnValue(CHAIN_IDS.MAINNET); - mocks.getNetworkConfigurationByChainId.mockReturnValue( - createMockOptimismConfiguration(), - ); - await handler({ - origin: 'example.com', - params: [ - { - chainId: createMockOptimismConfiguration().chainId, - chainName: createMockOptimismConfiguration().name, - rpcUrls: createMockOptimismConfiguration().rpcEndpoints.map( - (rpc) => rpc.url, - ), - nativeCurrency: { - symbol: createMockOptimismConfiguration().nativeCurrency, - decimals: 18, + describe('if the proposed networkConfiguration does not have a different rpcUrl from the one already in state', () => { + it('should switch to the existing networkConfiguration if one already exists for the given chain id', async () => { + const { mocks, end, handler } = createMockedHandler(); + mocks.getCurrentChainIdForDomain.mockReturnValue(CHAIN_IDS.MAINNET); + mocks.getNetworkConfigurationByChainId.mockReturnValue( + createMockOptimismConfiguration(), + ); + await handler({ + origin: 'example.com', + params: [ + { + chainId: createMockOptimismConfiguration().chainId, + chainName: createMockOptimismConfiguration().name, + rpcUrls: createMockOptimismConfiguration().rpcEndpoints.map( + (rpc) => rpc.url, + ), + nativeCurrency: { + symbol: createMockOptimismConfiguration().nativeCurrency, + decimals: 18, + }, + blockExplorerUrls: + createMockOptimismConfiguration().blockExplorerUrls, }, - blockExplorerUrls: - createMockOptimismConfiguration().blockExplorerUrls, + ], + }); + + expect(EthChainUtils.switchChain).toHaveBeenCalledTimes(1); + expect(EthChainUtils.switchChain).toHaveBeenCalledWith( + {}, + end, + 'example.com', + '0xa', + createMockOptimismConfiguration().rpcEndpoints[0].networkClientId, + undefined, + { + isAddFlow: true, + endApprovalFlow: mocks.endApprovalFlow, + getCaveat: mocks.getCaveat, + requestPermissionApprovalForOrigin: + mocks.requestPermissionApprovalForOrigin, + setActiveNetwork: mocks.setActiveNetwork, + updateCaveat: mocks.updateCaveat, + grantPermissions: mocks.grantPermissions, }, - ], + ); }); - - expect(EthChainUtils.switchChain).toHaveBeenCalledTimes(1); - expect(EthChainUtils.switchChain).toHaveBeenCalledWith( - {}, - end, - 'example.com', - '0xa', - createMockOptimismConfiguration().rpcEndpoints[0].networkClientId, - undefined, - { - isAddFlow: true, - endApprovalFlow: mocks.endApprovalFlow, - getCaveat: mocks.getCaveat, - requestPermissionApprovalForOrigin: - mocks.requestPermissionApprovalForOrigin, - setActiveNetwork: mocks.setActiveNetwork, - updateCaveat: mocks.updateCaveat, - grantPermissions: mocks.grantPermissions, - }, - ); }); }); From 200a7785f79fe4e86c7661052439a90ca637ed61 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 11:45:27 -0800 Subject: [PATCH 113/148] cleanup add-ethereum-chain.test.ts --- .../handlers/add-ethereum-chain.test.js | 33 +++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js index 3b71e04b24ce..37351eef059e 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js @@ -203,9 +203,12 @@ describe('addEthereumChainHandler', () => { describe('if a networkConfiguration for the given chainId already exists', () => { describe('if the proposed networkConfiguration has a different rpcUrl from the one already in state', () => { - it('create a new networkConfiguration and switches to it', async () => { + it('updates the network with a new networkConfiguration and switches to it', async () => { const { mocks, end, handler } = createMockedHandler(); mocks.getCurrentChainIdForDomain.mockReturnValue(CHAIN_IDS.SEPOLIA); + mocks.getNetworkConfigurationByChainId.mockReturnValue( + createMockMainnetConfiguration(), + ); await handler({ origin: 'example.com', @@ -223,6 +226,30 @@ describe('addEthereumChainHandler', () => { ], }); + expect(mocks.updateNetwork).toHaveBeenCalledWith( + '0x1', + { + blockExplorerUrls: ['https://etherscan.io'], + chainId: '0x1', + defaultBlockExplorerUrlIndex: 0, + defaultRpcEndpointIndex: 1, + name: 'Ethereum Mainnet', + nativeCurrency: 'ETH', + rpcEndpoints: [ + { + networkClientId: 'mainnet', + type: 'infura', + url: 'https://mainnet.infura.io/v3/', + }, + { + name: 'Ethereum Mainnet', + type: 'custom', + url: 'https://eth.llamarpc.com', + }, + ], + }, + undefined, + ); expect(EthChainUtils.switchChain).toHaveBeenCalledTimes(1); expect(EthChainUtils.switchChain).toHaveBeenCalledWith( {}, @@ -246,7 +273,7 @@ describe('addEthereumChainHandler', () => { }); describe('if the proposed networkConfiguration does not have a different rpcUrl from the one already in state', () => { - it('should switch to the existing networkConfiguration if one already exists for the given chain id', async () => { + it('should only switch to the existing networkConfiguration if one already exists for the given chain id', async () => { const { mocks, end, handler } = createMockedHandler(); mocks.getCurrentChainIdForDomain.mockReturnValue(CHAIN_IDS.MAINNET); mocks.getNetworkConfigurationByChainId.mockReturnValue( @@ -271,6 +298,8 @@ describe('addEthereumChainHandler', () => { ], }); + expect(mocks.addNetwork).not.toHaveBeenCalled(); + expect(mocks.updateNetwork).not.toHaveBeenCalled(); expect(EthChainUtils.switchChain).toHaveBeenCalledTimes(1); expect(EthChainUtils.switchChain).toHaveBeenCalledWith( {}, From 16987cdfb816a5df9bca2830f9c80dc4883225cf Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 12:09:32 -0800 Subject: [PATCH 114/148] add jsdoc to switchChain --- .../handlers/ethereum-chain-utils.js | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index 8add1b52cedc..b4f37e308445 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -162,8 +162,28 @@ export function validateAddEthereumChainParams(params) { }; } +/** + * Switches the active network for the origin if already permitted + * otherwise requests approval to update permission first. + * + * @param response - The JSON RPC request's response object. + * @param end - The JSON RPC request's end callback. + * @param {string} origin - The origin for the request. + * @param {string} chainId - The chainId being switched to. + * @param {string} networkClientId - The network client being switched to. + * @param {string} approvalFlowId - The optional approval flow ID to handle. + * @param {object} hooks - The hooks object. + * @param {boolean} hooks.isAddFlow - The boolean determining if this call originates from wallet_addEthereumChain. + * @param {Function} hooks.setActiveNetwork - The callback to change the current network for the origin. + * @param {Function} hooks.endApprovalFlow - The optional callback to end the approval flow when approvalFlowId is provided. + * @param {Function} hooks.getCaveat - The callback to get the CAIP-25 caveat for the origin. + * @param {Function} hooks.requestPermissionApprovalForOrigin - The callback to prompt the user for permission approval. + * @param {Function} hooks.updateCaveat - The callback to update the CAIP-25 caveat value. + * @param {Function} hooks.grantPermissions - The callback to grant a CAIP-25 permission when one does not already exist. + * @returns a null response on success or an error if user rejects an approval when isAddFlow is false or on unexpected errors. + */ export async function switchChain( - res, + response, end, origin, chainId, @@ -259,7 +279,7 @@ export async function switchChain( } await setActiveNetwork(networkClientId); - res.result = null; + response.result = null; } catch (error) { // We don't want to return an error if user rejects the request // and this is a chained switch request after wallet_addEthereumChain. @@ -270,7 +290,7 @@ export async function switchChain( error.code === errorCodes.provider.userRejectedRequest && approvalFlowId ) { - res.result = null; + response.result = null; return end(); } return end(error); From fca9657a2ff25c87a6ecb920debadc9af3892cc8 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 13:47:45 -0800 Subject: [PATCH 115/148] cleanup getAccounts in wallet-getPermissions.test.ts --- .../handlers/wallet-getPermissions.test.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts index 8806508548a6..b842b7b01ca3 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts @@ -65,9 +65,7 @@ const createMockedHandler = () => { }, }), ); - const getAccounts = jest - .fn() - .mockReturnValue(['0x1', '0x2', '0x3', '0xdeadbeef']); + const getAccounts = jest.fn().mockReturnValue([]); const response: PendingJsonRpcResponse = { jsonrpc: '2.0' as const, id: 0, @@ -142,8 +140,7 @@ describe('getPermissionsHandler', () => { describe('CAIP-25 endowment permissions has been granted', () => { it('returns the permissions with the CAIP-25 permission removed', async () => { - const { handler, getAccounts, response } = createMockedHandler(); - getAccounts.mockReturnValue([]); + const { handler, response } = createMockedHandler(); await handler(baseRequest); expect(response.result).toStrictEqual([ { @@ -167,7 +164,8 @@ describe('getPermissionsHandler', () => { }); it('returns the permissions with an eth_accounts permission if some eth accounts are permissioned', async () => { - const { handler, response } = createMockedHandler(); + const { handler, getAccounts, response } = createMockedHandler(); + getAccounts.mockReturnValue(['0x1', '0x2', '0x3', '0xdeadbeef']); await handler(baseRequest); expect(response.result).toStrictEqual([ @@ -216,8 +214,7 @@ describe('getPermissionsHandler', () => { }); it('returns the permissions with a permittedChains permission if some eip155 chainIds are permissioned', async () => { - const { handler, getAccounts, response } = createMockedHandler(); - getAccounts.mockReturnValue([]); + const { handler, response } = createMockedHandler(); MockMultichain.getPermittedEthChainIds.mockReturnValue(['0x1', '0x64']); await handler(baseRequest); From 63b99ebb36013f26156e71f0195c34d16683f738 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 13:50:19 -0800 Subject: [PATCH 116/148] Fix switchChain approvalFlowId type --- .../lib/rpc-method-middleware/handlers/ethereum-chain-utils.js | 2 +- .../rpc-method-middleware/handlers/ethereum-chain-utils.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index b4f37e308445..b48ca518958d 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -171,7 +171,7 @@ export function validateAddEthereumChainParams(params) { * @param {string} origin - The origin for the request. * @param {string} chainId - The chainId being switched to. * @param {string} networkClientId - The network client being switched to. - * @param {string} approvalFlowId - The optional approval flow ID to handle. + * @param {string} [approvalFlowId] - The optional approval flow ID to handle. * @param {object} hooks - The hooks object. * @param {boolean} hooks.isAddFlow - The boolean determining if this call originates from wallet_addEthereumChain. * @param {Function} hooks.setActiveNetwork - The callback to change the current network for the origin. diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts index e01c510b72d6..f4746c45f0e6 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts @@ -25,7 +25,7 @@ describe('Ethereum Chain Utils', () => { origin: string, chainId: Hex, networkClientId: string, - approvalFlowId: string | null, + approvalFlowId?: string, ) => EthChainUtils.switchChain( response, From bf3358424266a21faddd55852176f7830b67af90 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 13:51:43 -0800 Subject: [PATCH 117/148] fix 'requests a switch chain approval' scenario --- .../handlers/ethereum-chain-utils.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts index f4746c45f0e6..b4b4aa992412 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts @@ -101,8 +101,9 @@ describe('Ethereum Chain Utils', () => { }); describe('with no existing CAIP-25 permission', () => { - it('requests a switch chain approval', async () => { + it('requests a switch chain approval if isAddFlow: false', async () => { const { mocks, switchChain } = createMockedSwitchChain(); + mocks.isAddFlow = false; await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ From 91df27d027240daf458bb0682719e95d99f47025 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 14:01:30 -0800 Subject: [PATCH 118/148] make wallet-getPermissions.test.ts clearer --- .../handlers/wallet-getPermissions.test.ts | 79 +++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts index b842b7b01ca3..d24c315a621d 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts @@ -140,7 +140,37 @@ describe('getPermissionsHandler', () => { describe('CAIP-25 endowment permissions has been granted', () => { it('returns the permissions with the CAIP-25 permission removed', async () => { - const { handler, response } = createMockedHandler(); + const { handler, getAccounts, getPermissionsForOrigin, response } = createMockedHandler(); + getPermissionsForOrigin.mockReturnValue( + Object.freeze({ + [Caip25EndowmentPermissionName]: { + id: '1', + parentCapability: Caip25EndowmentPermissionName, + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: {}, + }, + }, + ], + }, + otherPermission: { + id: '2', + parentCapability: 'otherPermission', + caveats: [ + { + value: { + foo: 'bar', + }, + }, + ], + }, + }), + ); + getAccounts.mockReturnValue([]); + MockMultichain.getPermittedEthChainIds.mockReturnValue([]); await handler(baseRequest); expect(response.result).toStrictEqual([ { @@ -194,20 +224,59 @@ describe('getPermissionsHandler', () => { }); it('gets the permitted eip155 chainIds from the CAIP-25 caveat value', async () => { - const { handler } = createMockedHandler(); + const { handler, getPermissionsForOrigin } = createMockedHandler(); + getPermissionsForOrigin.mockReturnValue( + Object.freeze({ + [Caip25EndowmentPermissionName]: { + id: '1', + parentCapability: Caip25EndowmentPermissionName, + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: { + 'eip155:1': { + accounts: [], + }, + 'eip155:5': { + accounts: [], + }, + }, + optionalScopes: { + 'eip155:1': { + accounts: [], + }, + }, + }, + }, + ], + }, + otherPermission: { + id: '2', + parentCapability: 'otherPermission', + caveats: [ + { + value: { + foo: 'bar', + }, + }, + ], + }, + }) + ) await handler(baseRequest); expect(MockMultichain.getPermittedEthChainIds).toHaveBeenCalledWith({ requiredScopes: { 'eip155:1': { - accounts: ['eip155:1:0x1', 'eip155:1:0x2'], + accounts: [], }, 'eip155:5': { - accounts: ['eip155:5:0x1', 'eip155:5:0x3'], + accounts: [], }, }, optionalScopes: { 'eip155:1': { - accounts: ['eip155:1:0xdeadbeef'], + accounts: [], }, }, }); From 01610673a14d810f914b86985cd30138d0bf839f Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 14:04:44 -0800 Subject: [PATCH 119/148] lint --- .../handlers/wallet-getPermissions.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts index d24c315a621d..c3f13c91468d 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-getPermissions.test.ts @@ -140,7 +140,8 @@ describe('getPermissionsHandler', () => { describe('CAIP-25 endowment permissions has been granted', () => { it('returns the permissions with the CAIP-25 permission removed', async () => { - const { handler, getAccounts, getPermissionsForOrigin, response } = createMockedHandler(); + const { handler, getAccounts, getPermissionsForOrigin, response } = + createMockedHandler(); getPermissionsForOrigin.mockReturnValue( Object.freeze({ [Caip25EndowmentPermissionName]: { @@ -262,8 +263,8 @@ describe('getPermissionsHandler', () => { }, ], }, - }) - ) + }), + ); await handler(baseRequest); expect(MockMultichain.getPermittedEthChainIds).toHaveBeenCalledWith({ requiredScopes: { From d160ce144f45c776f38b89e513f93bde2a0ff54b Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Mon, 9 Dec 2024 22:17:09 +0000 Subject: [PATCH 120/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 10 ++++++++++ lavamoat/browserify/flask/policy.json | 10 ++++++++++ lavamoat/browserify/main/policy.json | 10 ++++++++++ lavamoat/browserify/mmi/policy.json | 10 ++++++++++ 4 files changed, 40 insertions(+) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 9d441c704ed0..cad2d20505c0 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1651,6 +1651,16 @@ "browserify>url": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/controller-utils": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 9d441c704ed0..cad2d20505c0 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1651,6 +1651,16 @@ "browserify>url": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/controller-utils": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 9d441c704ed0..cad2d20505c0 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1651,6 +1651,16 @@ "browserify>url": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/controller-utils": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index 88f77a11d112..73c01fc31770 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -1743,6 +1743,16 @@ "browserify>url": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/controller-utils": true, + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true From 797273c26dce4180ff5a18d0cc5a1dea6617adbb Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 15:33:01 -0800 Subject: [PATCH 121/148] lint --- .../rpc-method-middleware/handlers/ethereum-chain-utils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts index b4b4aa992412..a64bd2103ca3 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts @@ -62,7 +62,7 @@ describe('Ethereum Chain Utils', () => { new Error('unexpected error'), ); - await switchChain('example.com', '0x1', 'mainnet', null); + await switchChain('example.com', '0x1', 'mainnet', undefined); expect(end).toHaveBeenCalledWith(new Error('unexpected error')); }); From d4c8df21d70c80d36643ddadbd10cdf64a5bdf0f Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Mon, 9 Dec 2024 15:49:48 -0800 Subject: [PATCH 122/148] move requestAccountsAndChainPermissionsWithId logic into separate async fn --- .../controllers/permissions/background-api.js | 93 +++++++++---------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index 14b4886db7ba..8a00e77a7e7a 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -115,6 +115,51 @@ export function getPermissionBackgroundApiMethods({ ); }; + const requestAccountsAndChainPermissions = async (origin, id) => { + const legacyApproval = await approvalController.addAndShowApprovalRequest({ + id, + origin, + requestData: { + metadata: { + id, + origin, + }, + permissions: { + [RestrictedMethods.eth_accounts]: {}, + [PermissionNames.permittedChains]: {}, + }, + }, + type: MethodNames.RequestPermissions, + }); + + let caveatValue = { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }; + + caveatValue = setPermittedEthChainIds( + caveatValue, + legacyApproval.approvedChainIds, + ); + + caveatValue = setEthAccounts(caveatValue, legacyApproval.approvedAccounts); + + permissionController.grantPermissions({ + subject: { origin }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: caveatValue, + }, + ], + }, + }, + }); + }; + return { addPermittedAccount: (origin, account) => addMoreAccounts(origin, [account]), @@ -232,53 +277,7 @@ export function getPermissionBackgroundApiMethods({ requestAccountsAndChainPermissionsWithId: (origin) => { const id = nanoid(); - approvalController - .addAndShowApprovalRequest({ - id, - origin, - requestData: { - metadata: { - id, - origin, - }, - permissions: { - [RestrictedMethods.eth_accounts]: {}, - [PermissionNames.permittedChains]: {}, - }, - }, - type: MethodNames.RequestPermissions, - }) - .then((legacyApproval) => { - let caveatValue = { - requiredScopes: {}, - optionalScopes: {}, - isMultichainOrigin: false, - }; - caveatValue = setPermittedEthChainIds( - caveatValue, - legacyApproval.approvedChainIds, - ); - - caveatValue = setEthAccounts( - caveatValue, - legacyApproval.approvedAccounts, - ); - - permissionController.grantPermissions({ - subject: { origin }, - approvedPermissions: { - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: caveatValue, - }, - ], - }, - }, - }); - }); - + requestAccountsAndChainPermissions(origin, id); return id; }, }; From b532861ceac2b94d7ee7d572d204012e3928daea Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 10 Dec 2024 13:53:12 -0800 Subject: [PATCH 123/148] remove addMoreChains comment --- app/scripts/controllers/permissions/background-api.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index 8a00e77a7e7a..996f0b61e365 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -88,11 +88,6 @@ export function getPermissionBackgroundApiMethods({ ); } - // In the case that the new set of permitted eth chainIds does not overlap at all with - // the old set, we will lose context of the permitted eth accounts if we drop - // the old set of eth scopes without first noting which eth accounts are permitted first. - const ethAccounts = getEthAccounts(caip25Caveat.value); - const ethChainIds = getPermittedEthChainIds(caip25Caveat.value); const updatedEthChainIds = Array.from( @@ -104,7 +99,8 @@ export function getPermissionBackgroundApiMethods({ updatedEthChainIds, ); - // ensure that the list of permitted eth accounts is intact after permitted chain updates + // ensure that the list of permitted eth accounts is set for the newly added eth scopes + const ethAccounts = getEthAccounts(updatedCaveatValue); updatedCaveatValue = setEthAccounts(updatedCaveatValue, ethAccounts); permissionController.updateCaveat( From ee043eb1f54913fa320cd4672fa5ea92a3e0b897 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 10 Dec 2024 14:18:00 -0800 Subject: [PATCH 124/148] add comment about addAndShowApprovalRequest usage in background-api --- app/scripts/controllers/permissions/background-api.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index 996f0b61e365..b63a01ea9e8f 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -112,6 +112,13 @@ export function getPermissionBackgroundApiMethods({ }; const requestAccountsAndChainPermissions = async (origin, id) => { + // Note that we are purposely requesting an approval from the ApprovalController + // and then manually forming the permission that is then granted via the + // PermissionController rather than calling the PermissionController.requestPermissions() + // directly because the Approval UI is still dependent on the notion of there + // being separate "eth_accounts" and "endowment:permitted-chains" permissions. + // After that depedency is refactored, we can move to requesting "endowment:caip25" + // directly from the PermissionController instead. const legacyApproval = await approvalController.addAndShowApprovalRequest({ id, origin, From 0ffeec2d4ce6189bd9b23cc9a3d2b82e90baf66d Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 10 Dec 2024 14:19:46 -0800 Subject: [PATCH 125/148] bump @metamask/multichain to ^1.1.2 --- package.json | 2 +- yarn.lock | 38 +++++++------------------------------- 2 files changed, 8 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index 8f805cd42c1c..b7d89c861676 100644 --- a/package.json +++ b/package.json @@ -324,7 +324,7 @@ "@metamask/message-manager": "^11.0.0", "@metamask/message-signing-snap": "^0.6.0", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "^1.1.0", + "@metamask/multichain": "^1.1.2", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A22.1.0#~/.yarn/patches/@metamask-network-controller-npm-22.1.0-621c281f70.patch", "@metamask/notification-services-controller": "^0.14.0", diff --git a/yarn.lock b/yarn.lock index 0560eea93662..0c7ca29309d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5220,19 +5220,6 @@ __metadata: languageName: node linkType: hard -"@metamask/eth-json-rpc-filters@npm:^7.0.0": - version: 7.0.1 - resolution: "@metamask/eth-json-rpc-filters@npm:7.0.1" - dependencies: - "@metamask/eth-query": "npm:^4.0.0" - "@metamask/json-rpc-engine": "npm:^8.0.2" - "@metamask/safe-event-emitter": "npm:^3.0.0" - async-mutex: "npm:^0.5.0" - pify: "npm:^5.0.0" - checksum: 10/5200f75cee48dfd79deba5e4f1b16ff6827e606da617891f5cb7b59c43ae4ac8420cb9a6a9ca31705c47d2c3d32a3754e052b30f61fd293cc37f009c4fe20c12 - languageName: node - linkType: hard - "@metamask/eth-json-rpc-filters@npm:^9.0.0": version: 9.0.0 resolution: "@metamask/eth-json-rpc-filters@npm:9.0.0" @@ -5579,17 +5566,6 @@ __metadata: languageName: node linkType: hard -"@metamask/json-rpc-engine@npm:^8.0.2": - version: 8.0.2 - resolution: "@metamask/json-rpc-engine@npm:8.0.2" - dependencies: - "@metamask/rpc-errors": "npm:^6.2.1" - "@metamask/safe-event-emitter": "npm:^3.0.0" - "@metamask/utils": "npm:^8.3.0" - checksum: 10/f088f4b648b9b55875b56e8237853e7282f13302a9db6a1f9bba06314dfd6cd0a23b3d27f8fde05a157b97ebb03b67bc2699ba455c99553dfb2ecccd73ab3474 - languageName: node - linkType: hard - "@metamask/json-rpc-engine@npm:^9.0.3": version: 9.0.3 resolution: "@metamask/json-rpc-engine@npm:9.0.3" @@ -5731,20 +5707,20 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:^1.1.0": - version: 1.1.0 - resolution: "@metamask/multichain@npm:1.1.0" +"@metamask/multichain@npm:^1.1.2": + version: 1.1.2 + resolution: "@metamask/multichain@npm:1.1.2" dependencies: "@metamask/api-specs": "npm:^0.10.12" - "@metamask/controller-utils": "npm:^11.4.3" - "@metamask/eth-json-rpc-filters": "npm:^7.0.0" + "@metamask/controller-utils": "npm:^11.4.4" + "@metamask/eth-json-rpc-filters": "npm:^9.0.0" "@metamask/rpc-errors": "npm:^7.0.1" "@metamask/utils": "npm:^10.0.0" lodash: "npm:^4.17.21" peerDependencies: "@metamask/network-controller": ^22.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/dab9a223c6cf1b11705f53cab3c1d6f2ecfb5f0c2b7b44d32847ea42b5cd2c3a9936b10239d3dbf0215b2656b3584a08a2ee979004cedd2edfb27813ec25665d + checksum: 10/ae2f9be92dc3d5e68a8a93f65f7dba30844c0ba98877005ad228f587a43fa06f45b441f3cb10260db95febdc405f06f3fb3b65d3545efb4c634cdecea846e5fb languageName: node linkType: hard @@ -26479,7 +26455,7 @@ __metadata: "@metamask/message-manager": "npm:^11.0.0" "@metamask/message-signing-snap": "npm:^0.6.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:^1.1.0" + "@metamask/multichain": "npm:^1.1.2" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A22.1.0#~/.yarn/patches/@metamask-network-controller-npm-22.1.0-621c281f70.patch" "@metamask/notification-services-controller": "npm:^0.14.0" From 88dd6bec95d5b93ed0a276f5d0f91f456ced5afa Mon Sep 17 00:00:00 2001 From: jiexi Date: Wed, 11 Dec 2024 10:14:31 -0800 Subject: [PATCH 126/148] Update app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js Co-authored-by: Mark Stacey --- app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js index dddc3e54d275..99f4f8d01d8b 100644 --- a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js +++ b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js @@ -16,6 +16,7 @@ import { revokePermissionsHandler } from './handlers/wallet-revokePermissions'; export const createEip1193MethodMiddleware = makeMethodMiddlewareMaker([ ...localHandlers, ...eip1193OnlyHandlers, + // EIP-2255 Permission handlers getPermissionsHandler, requestPermissionsHandler, revokePermissionsHandler, From 8d8036f7f559fa6b342b9249b0edb286bf978b79 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 11 Dec 2024 10:25:11 -0800 Subject: [PATCH 127/148] remove getRemovedAuthorizations --- .../controllers/permissions/selectors.js | 34 ------------------- .../controllers/permissions/selectors.test.js | 31 ----------------- 2 files changed, 65 deletions(-) diff --git a/app/scripts/controllers/permissions/selectors.js b/app/scripts/controllers/permissions/selectors.js index 50134a7bb607..97464885b7a6 100644 --- a/app/scripts/controllers/permissions/selectors.js +++ b/app/scripts/controllers/permissions/selectors.js @@ -112,37 +112,3 @@ export const diffMap = (currentMap, previousMap) => { } return changedMap; }; - -/** - * - * @param {Map} newAuthorizationsMap - The new origin:authorization map. - * @param {Map} [previousAuthorizationsMap] - The previous origin:authorization map. - * @returns {Map} The origin:authorization map of changed authorizations. - */ -export const getRemovedAuthorizations = ( - newAuthorizationsMap, - previousAuthorizationsMap, -) => { - const removedAuthorizations = new Map(); - - // If there are no previous authorizations, there are no removed authorizations. - // OR If the new authorizations map is the same as the previous authorizations map, - // there are no removed authorizations - if ( - previousAuthorizationsMap === undefined || - newAuthorizationsMap === previousAuthorizationsMap - ) { - return removedAuthorizations; - } - - const previousOrigins = new Set([...previousAuthorizationsMap.keys()]); - for (const origin of newAuthorizationsMap.keys()) { - previousOrigins.delete(origin); - } - - for (const origin of previousOrigins.keys()) { - removedAuthorizations.set(origin, previousAuthorizationsMap.get(origin)); - } - - return removedAuthorizations; -}; diff --git a/app/scripts/controllers/permissions/selectors.test.js b/app/scripts/controllers/permissions/selectors.test.js index 11524e373d3d..9a6cc10a9a07 100644 --- a/app/scripts/controllers/permissions/selectors.test.js +++ b/app/scripts/controllers/permissions/selectors.test.js @@ -7,7 +7,6 @@ import { diffMap, getPermittedAccountsByOrigin, getPermittedChainsByOrigin, - getRemovedAuthorizations, } from './selectors'; describe('PermissionController selectors', () => { @@ -168,36 +167,6 @@ describe('PermissionController selectors', () => { }); }); - describe('getRemovedAuthorizations', () => { - it('returns an empty map if the new and previous values are the same', () => { - const newAuthorizations = new Map(); - expect( - getRemovedAuthorizations(newAuthorizations, newAuthorizations), - ).toStrictEqual(new Map()); - }); - - it('returns a new map of the removed authorizations if the new and previous values differ', () => { - const mockAuthorization = { - requiredScopes: { - 'eip155:1': { - accounts: [], - }, - }, - optionalScopes: {}, - }; - const previousAuthorizations = new Map([ - ['foo.bar', mockAuthorization], - ['bar.baz', mockAuthorization], - ]); - - const newAuthorizations = new Map([['foo.bar', mockAuthorization]]); - - expect( - getRemovedAuthorizations(newAuthorizations, previousAuthorizations), - ).toStrictEqual(new Map([['bar.baz', mockAuthorization]])); - }); - }); - describe('getPermittedChainsByOrigin', () => { it('memoizes and gets permitted chains by origin', () => { const state1 = { From 61e886c1f284f18b6e9226aea540f512afbcbb6d Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 11 Dec 2024 11:28:18 -0800 Subject: [PATCH 128/148] mvoe endowment:caip25 into ExcludedSnapEndowments --- shared/constants/snaps/permissions.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/shared/constants/snaps/permissions.ts b/shared/constants/snaps/permissions.ts index 3aa94d9dd4ac..4a10960951b9 100644 --- a/shared/constants/snaps/permissions.ts +++ b/shared/constants/snaps/permissions.ts @@ -16,10 +16,11 @@ export const EndowmentPermissions = Object.freeze({ // Methods / permissions in external packages that we are temporarily excluding. export const ExcludedSnapPermissions = Object.freeze({ +}); + +export const ExcludedSnapEndowments = Object.freeze({ 'endowment:caip25': 'eth_accounts is disabled. For more information please see https://github.com/MetaMask/snaps/issues/990.', }); -export const ExcludedSnapEndowments = Object.freeze({}); - export const DynamicSnapPermissions = Object.freeze(['endowment:caip25']); From c9b023b7f34dc128dc3cc8eb64e0075b998008cb Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 11 Dec 2024 12:50:27 -0800 Subject: [PATCH 129/148] update getPermissionSpecifications jsdoc --- .../controllers/permissions/specifications.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index 42742db6739c..a0b44131ea5b 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -53,18 +53,12 @@ export const getCaveatSpecifications = () => { * Gets the specifications for all permissions that will be recognized by the * PermissionController. * - * @param {{ - * getAllAccounts: () => Promise, - * getInternalAccounts: () => Record, - * }} options - Options bag. - * @param options.getAllAccounts - A function that returns all Ethereum accounts - * in the current MetaMask instance. + * @param options - The options object. * @param options.listAccounts - A function that returns the - * `AccountsController` internalAccount objects for all accounts in the - * @param options.captureKeyringTypesWithMissingIdentities - A function that - * captures extra error information about the "Missing identity for address" - * error. - * current MetaMask instance. + * `AccountsController` internalAccount objects for all evm accounts. + * @param options.findNetworkClientIdByChainId - A function that + * returns the networkClientId given a chainId. + * @returns the permission specifications to construct the PermissionController. */ export const getPermissionSpecifications = ({ listAccounts, From 64778028a5ea48061657485f21d19b68b912b4c7 Mon Sep 17 00:00:00 2001 From: Erik Marks <25517051+rekmarks@users.noreply.github.com> Date: Thu, 12 Dec 2024 13:33:22 -0800 Subject: [PATCH 130/148] refactor: Robustify migration 135 (#29168) Robustifies migration 135 by using more of our own types throughout the migration file. The result of reviewing migration 135 and ensuring that it's functioning correctly. The only behavioral difference is that `getChainIdForNetworkClientId()` will capture an exception if no chain id can be found, as opposed to this occurring outside of that function. The relevant test cases have been updated to ensure that we capture this exception. --- app/scripts/migrations/135.test.ts | 9 +- app/scripts/migrations/135.ts | 131 ++++++++++++++++++----------- 2 files changed, 89 insertions(+), 51 deletions(-) diff --git a/app/scripts/migrations/135.test.ts b/app/scripts/migrations/135.test.ts index 53a47dbf61ca..c5fde9fedfc0 100644 --- a/app/scripts/migrations/135.test.ts +++ b/app/scripts/migrations/135.test.ts @@ -353,7 +353,7 @@ describe('migration #135', () => { expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( new Error( - `Migration ${version}: Invalid chainId for selectedNetworkClientId "nonExistentNetworkClientId" of type undefined`, + `Migration ${version}: No chainId found for selectedNetworkClientId "nonExistentNetworkClientId"`, ), ); expect(newStorage.data).toStrictEqual(oldStorage.data); @@ -621,6 +621,13 @@ describe('migration #135', () => { }; const newStorage = await migrate(oldStorage); + + expect(sentryCaptureExceptionMock).toHaveBeenCalledWith( + new Error( + `Migration ${version}: No chainId found for networkClientIdForOrigin "doesNotExist"`, + ), + ); + expect(newStorage.data).toStrictEqual({ ...baseData(), SelectedNetworkController: { diff --git a/app/scripts/migrations/135.ts b/app/scripts/migrations/135.ts index d7650f6930ed..b911b80c5e3f 100644 --- a/app/scripts/migrations/135.ts +++ b/app/scripts/migrations/135.ts @@ -1,41 +1,58 @@ -import { hasProperty, isObject, NonEmptyArray, Json } from '@metamask/utils'; +import { hasProperty, isObject } from '@metamask/utils'; +import type { CaipChainId, CaipAccountId, Json, Hex } from '@metamask/utils'; import { cloneDeep } from 'lodash'; +import type { + Caveat, + PermissionConstraint, + ValidPermission, +} from '@metamask/permission-controller'; -type CaveatConstraint = { - type: string; - value: Json; +type VersionedData = { + meta: { version: number }; + data: Record; }; -type PermissionConstraint = { - parentCapability: string; - caveats: null | NonEmptyArray; -}; +export const version = 135; -const PermissionNames = { - eth_accounts: 'eth_accounts', - permittedChains: 'endowment:permitted-chains', +// In-lined from @metamask/multichain +const Caip25CaveatType = 'authorizedScopes'; +const Caip25EndowmentPermissionName = 'endowment:caip25'; + +type InternalScopeObject = { + accounts: CaipAccountId[]; }; -const BUILT_IN_NETWORKS = { - goerli: '0x5', - sepolia: '0xaa36a7', - mainnet: '0x1', - 'linea-goerli': '0xe704', - 'linea-sepolia': '0xe705', - 'linea-mainnet': '0xe708', +type InternalScopesObject = Record; + +type Caip25CaveatValue = { + requiredScopes: InternalScopesObject; + optionalScopes: InternalScopesObject; + sessionProperties?: Record; + isMultichainOrigin: boolean; }; -const Caip25CaveatType = 'authorizedScopes'; -const Caip25EndowmentPermissionName = 'endowment:caip25'; +// Locally defined types +type Caip25Caveat = Caveat; +type Caip25Permission = ValidPermission< + typeof Caip25EndowmentPermissionName, + Caip25Caveat +>; -const snapsPrefixes = ['npm:', 'local:'] as const; +const PermissionNames = { + eth_accounts: 'eth_accounts', + permittedChains: 'endowment:permitted-chains', +} as const; -type VersionedData = { - meta: { version: number }; - data: Record; -}; +const BUILT_IN_NETWORKS: ReadonlyMap = new Map([ + ['goerli', '0x5'], + ['sepolia', '0xaa36a7'], + ['mainnet', '0x1'], + ['linea-goerli', '0xe704'], + ['linea-sepolia', '0xe705'], + ['linea-mainnet', '0xe708'], +]); -export const version = 135; +const snapsPrefixes = ['npm:', 'local:'] as const; /** * This migration transforms `eth_accounts` and `permittedChains` permissions into @@ -137,7 +154,10 @@ function transformState(state: Record) { return state; } - const getChainIdForNetworkClientId = (networkClientId: string) => { + const getChainIdForNetworkClientId = ( + networkClientId: string, + propertyName: string, + ): string | undefined => { for (const [chainId, networkConfiguration] of Object.entries( networkConfigurationsByChainId, )) { @@ -147,7 +167,7 @@ function transformState(state: Record) { `Migration ${version}: typeof state.NetworkController.networkConfigurationsByChainId["${chainId}"] is ${typeof networkConfiguration}`, ), ); - return null; + return undefined; } if (!Array.isArray(networkConfiguration.rpcEndpoints)) { global.sentry?.captureException( @@ -155,7 +175,7 @@ function transformState(state: Record) { `Migration ${version}: typeof state.NetworkController.networkConfigurationsByChainId["${chainId}"].rpcEndpoints is ${typeof networkConfiguration.rpcEndpoints}`, ), ); - return null; + return undefined; } for (const rpcEndpoint of networkConfiguration.rpcEndpoints) { if (!isObject(rpcEndpoint)) { @@ -164,7 +184,7 @@ function transformState(state: Record) { `Migration ${version}: typeof state.NetworkController.networkConfigurationsByChainId["${chainId}"].rpcEndpoints[] is ${typeof rpcEndpoint}`, ), ); - return null; + return undefined; } if (rpcEndpoint.networkClientId === networkClientId) { return chainId; @@ -172,18 +192,22 @@ function transformState(state: Record) { } } - return BUILT_IN_NETWORKS[ - networkClientId as unknown as keyof typeof BUILT_IN_NETWORKS - ]; + const builtInChainId = BUILT_IN_NETWORKS.get(networkClientId); + if (!builtInChainId) { + global.sentry?.captureException( + new Error( + `Migration ${version}: No chainId found for ${propertyName} "${networkClientId}"`, + ), + ); + } + return builtInChainId; }; - const currentChainId = getChainIdForNetworkClientId(selectedNetworkClientId); - if (!currentChainId || typeof currentChainId !== 'string') { - global.sentry?.captureException( - new Error( - `Migration ${version}: Invalid chainId for selectedNetworkClientId "${selectedNetworkClientId}" of type ${typeof currentChainId}`, - ), - ); + const currentChainId = getChainIdForNetworkClientId( + selectedNetworkClientId, + 'selectedNetworkClientId', + ); + if (!currentChainId) { return state; } @@ -209,7 +233,7 @@ function transformState(state: Record) { return state; } - let basePermission; + let basePermission: PermissionConstraint | undefined; let ethAccounts: string[] = []; if ( @@ -235,7 +259,7 @@ function transformState(state: Record) { } delete permissions[PermissionNames.permittedChains]; - if (ethAccounts.length === 0) { + if (ethAccounts.length === 0 || !basePermission) { continue; } @@ -243,25 +267,31 @@ function transformState(state: Record) { chainIds = [currentChainId]; const networkClientIdForOrigin = domains[origin]; - if (networkClientIdForOrigin) { + if ( + networkClientIdForOrigin && + typeof networkClientIdForOrigin === 'string' + ) { const chainIdForOrigin = getChainIdForNetworkClientId( - networkClientIdForOrigin as string, + networkClientIdForOrigin, + 'networkClientIdForOrigin', ); - if (chainIdForOrigin && typeof chainIdForOrigin === 'string') { + if (chainIdForOrigin) { chainIds = [chainIdForOrigin]; } } } const isSnap = snapsPrefixes.some((prefix) => origin.startsWith(prefix)); - const scopes: Record = {}; - const scopeStrings = isSnap + const scopes: InternalScopesObject = {}; + const scopeStrings: CaipChainId[] = isSnap ? [] - : chainIds.map((chainId) => `eip155:${parseInt(chainId, 16)}`); + : chainIds.map( + (chainId) => `eip155:${parseInt(chainId, 16)}`, + ); scopeStrings.push('wallet:eip155'); scopeStrings.forEach((scopeString) => { - const caipAccounts = ethAccounts.map( + const caipAccounts = ethAccounts.map( (account) => `${scopeString}:${account}`, ); scopes[scopeString] = { @@ -269,7 +299,7 @@ function transformState(state: Record) { }; }); - permissions[Caip25EndowmentPermissionName] = { + const caip25Permission: Caip25Permission = { ...basePermission, parentCapability: Caip25EndowmentPermissionName, caveats: [ @@ -283,6 +313,7 @@ function transformState(state: Record) { }, ], }; + permissions[Caip25EndowmentPermissionName] = caip25Permission; } return state; From cd66487d496eb369514b15f263c36074b383445f Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 12 Dec 2024 14:16:45 -0800 Subject: [PATCH 131/148] lint --- shared/constants/snaps/permissions.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shared/constants/snaps/permissions.ts b/shared/constants/snaps/permissions.ts index 4a10960951b9..0d1783c0e086 100644 --- a/shared/constants/snaps/permissions.ts +++ b/shared/constants/snaps/permissions.ts @@ -15,8 +15,7 @@ export const EndowmentPermissions = Object.freeze({ } as const); // Methods / permissions in external packages that we are temporarily excluding. -export const ExcludedSnapPermissions = Object.freeze({ -}); +export const ExcludedSnapPermissions = Object.freeze({}); export const ExcludedSnapEndowments = Object.freeze({ 'endowment:caip25': From 7af8dbb347e8e5b9db4e2781dcf45eaccc5ca41d Mon Sep 17 00:00:00 2001 From: jiexi Date: Thu, 12 Dec 2024 14:31:50 -0800 Subject: [PATCH 132/148] Jl/caip25 permission migration/bind origin to method hooks (#29174) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** * Binds the `updateCaveat` and `grantPermission` hooks provided to the rpc middleware handlers to the origin of the request * Cleans up resulting usage and unused params [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/29174?quickstart=1) ## **Related issues** See: https://github.com/MetaMask/metamask-extension/pull/27847#discussion_r1881120848 ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../handlers/add-ethereum-chain.js | 26 +++----- .../handlers/add-ethereum-chain.test.js | 3 - .../handlers/ethereum-chain-utils.js | 20 ++---- .../handlers/ethereum-chain-utils.test.ts | 66 +++++++++---------- .../handlers/request-accounts.test.ts | 27 ++++---- .../handlers/request-accounts.ts | 27 +++----- .../handlers/switch-ethereum-chain.js | 22 ++----- .../handlers/switch-ethereum-chain.test.js | 1 - .../wallet-requestPermissions.test.ts | 28 ++++---- .../handlers/wallet-requestPermissions.ts | 21 +++--- app/scripts/metamask-controller.js | 10 ++- 11 files changed, 98 insertions(+), 153 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js index 754df11b39b1..ef714a29c9bc 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js @@ -195,23 +195,15 @@ async function addEthereumChainHandler( const { networkClientId } = updatedNetwork.rpcEndpoints[updatedNetwork.defaultRpcEndpointIndex]; - return switchChain( - res, - end, - origin, - chainId, - networkClientId, - approvalFlowId, - { - isAddFlow: true, - setActiveNetwork, - getCaveat, - requestPermissionApprovalForOrigin, - updateCaveat, - endApprovalFlow, - grantPermissions, - }, - ); + return switchChain(res, end, chainId, networkClientId, approvalFlowId, { + isAddFlow: true, + setActiveNetwork, + getCaveat, + requestPermissionApprovalForOrigin, + updateCaveat, + endApprovalFlow, + grantPermissions, + }); } else if (approvalFlowId) { endApprovalFlow({ id: approvalFlowId }); } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js index 37351eef059e..7b91cfb8e8d4 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js @@ -184,7 +184,6 @@ describe('addEthereumChainHandler', () => { expect(EthChainUtils.switchChain).toHaveBeenCalledWith( {}, end, - 'example.com', NON_INFURA_CHAIN_ID, 123, 'approvalFlowId', @@ -254,7 +253,6 @@ describe('addEthereumChainHandler', () => { expect(EthChainUtils.switchChain).toHaveBeenCalledWith( {}, end, - 'example.com', '0x1', 123, 'approvalFlowId', @@ -304,7 +302,6 @@ describe('addEthereumChainHandler', () => { expect(EthChainUtils.switchChain).toHaveBeenCalledWith( {}, end, - 'example.com', '0xa', createMockOptimismConfiguration().rpcEndpoints[0].networkClientId, undefined, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index b48ca518958d..687ddebbb7f2 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -168,7 +168,6 @@ export function validateAddEthereumChainParams(params) { * * @param response - The JSON RPC request's response object. * @param end - The JSON RPC request's end callback. - * @param {string} origin - The origin for the request. * @param {string} chainId - The chainId being switched to. * @param {string} networkClientId - The network client being switched to. * @param {string} [approvalFlowId] - The optional approval flow ID to handle. @@ -185,7 +184,6 @@ export function validateAddEthereumChainParams(params) { export async function switchChain( response, end, - origin, chainId, networkClientId, approvalFlowId, @@ -236,7 +234,6 @@ export async function switchChain( ); updateCaveat( - origin, Caip25EndowmentPermissionName, Caip25CaveatType, updatedCaveatValue, @@ -264,16 +261,13 @@ export async function switchChain( caveatValue = addPermittedEthChainId(caveatValue, chainId); grantPermissions({ - subject: { origin }, - approvedPermissions: { - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: caveatValue, - }, - ], - }, + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: caveatValue, + }, + ], }, }); } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts index a64bd2103ca3..c2bd5ef2037b 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts @@ -22,7 +22,6 @@ describe('Ethereum Chain Utils', () => { }; const response: { result?: true } = {}; const switchChain = ( - origin: string, chainId: Hex, networkClientId: string, approvalFlowId?: string, @@ -30,7 +29,6 @@ describe('Ethereum Chain Utils', () => { EthChainUtils.switchChain( response, end, - origin, chainId, networkClientId, approvalFlowId, @@ -48,7 +46,7 @@ describe('Ethereum Chain Utils', () => { describe('switchChain', () => { it('gets the CAIP-25 caveat', async () => { const { mocks, switchChain } = createMockedSwitchChain(); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.getCaveat).toHaveBeenCalledWith({ target: Caip25EndowmentPermissionName, @@ -62,7 +60,7 @@ describe('Ethereum Chain Utils', () => { new Error('unexpected error'), ); - await switchChain('example.com', '0x1', 'mainnet', undefined); + await switchChain('0x1', 'mainnet', undefined); expect(end).toHaveBeenCalledWith(new Error('unexpected error')); }); @@ -73,7 +71,7 @@ describe('Ethereum Chain Utils', () => { new Error('unexpected error'), ); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(end).toHaveBeenCalledWith(new Error('unexpected error')); }); @@ -84,7 +82,7 @@ describe('Ethereum Chain Utils', () => { code: errorCodes.provider.userRejectedRequest, }); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(response.result).toStrictEqual(null); expect(end).toHaveBeenCalledWith(); @@ -93,7 +91,7 @@ describe('Ethereum Chain Utils', () => { it('ends the approval flow when approvalFlowId is provided', async () => { const { mocks, switchChain } = createMockedSwitchChain(); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.endApprovalFlow).toHaveBeenCalledWith({ id: 'approvalFlowId', @@ -104,7 +102,7 @@ describe('Ethereum Chain Utils', () => { it('requests a switch chain approval if isAddFlow: false', async () => { const { mocks, switchChain } = createMockedSwitchChain(); mocks.isAddFlow = false; - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ [PermissionNames.permittedChains]: { @@ -120,34 +118,31 @@ describe('Ethereum Chain Utils', () => { it('grants a new CAIP-25 permission with the chain', async () => { const { mocks, switchChain } = createMockedSwitchChain(); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.grantPermissions).toHaveBeenCalledWith({ - subject: { origin: 'example.com' }, - approvedPermissions: { - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: { - requiredScopes: {}, - optionalScopes: { - 'eip155:1': { - accounts: [], - }, + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + accounts: [], }, - isMultichainOrigin: false, }, + isMultichainOrigin: false, }, - ], - }, + }, + ], }, }); }); it('switches to the chain', async () => { const { mocks, switchChain } = createMockedSwitchChain(); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); }); @@ -158,7 +153,7 @@ describe('Ethereum Chain Utils', () => { code: errorCodes.provider.userRejectedRequest, }); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalled(); expect(mocks.grantPermissions).not.toHaveBeenCalled(); @@ -178,7 +173,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: false, }, }); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.requestPermissionApprovalForOrigin).not.toHaveBeenCalled(); expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); @@ -194,7 +189,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: false, }, }); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalled(); expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ @@ -219,10 +214,9 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: false, }, }); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.updateCaveat).toHaveBeenCalledWith( - 'example.com', Caip25EndowmentPermissionName, Caip25CaveatType, { @@ -249,7 +243,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: false, }, }); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalled(); expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); @@ -267,7 +261,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: true, }, }); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.requestPermissionApprovalForOrigin).not.toHaveBeenCalled(); }); @@ -281,7 +275,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: true, }, }); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); }); @@ -295,7 +289,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: true, }, }); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(end).toHaveBeenCalledWith( new Error( @@ -325,7 +319,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin, }, }); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect( mocks.requestPermissionApprovalForOrigin, @@ -345,7 +339,7 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin, }, }); - await switchChain('example.com', '0x1', 'mainnet', 'approvalFlowId'); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts index 23fcf7cdbc15..19185d1a133a 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts @@ -260,23 +260,18 @@ describe('requestEthereumAccountsHandler', () => { await handler(baseRequest); expect(grantPermissions).toHaveBeenCalledWith({ - subject: { - origin: 'http://test.com', - }, - approvedPermissions: { - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: { - requiredScopes: {}, - optionalScopes: {}, - sessionProperties: { caveatValueWithEthAccountsSet: true }, - isMultichainOrigin: false, - }, + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { caveatValueWithEthAccountsSet: true }, + isMultichainOrigin: false, }, - ], - }, + }, + ], }, }); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts index 236af869af12..be180e80ff07 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts @@ -7,9 +7,6 @@ import { } from '@metamask/multichain'; import { Caveat, - CaveatSpecificationConstraint, - PermissionController, - PermissionSpecificationConstraint, RequestedPermissions, ValidPermission, } from '@metamask/permission-controller'; @@ -59,11 +56,6 @@ const requestEthereumAccounts = { }; export default requestEthereumAccounts; -type AbstractPermissionController = PermissionController< - PermissionSpecificationConstraint, - CaveatSpecificationConstraint ->; - // Used to rate-limit pending requests to one per origin const locks = new Set(); @@ -95,7 +87,7 @@ async function requestEthereumAccountsHandler( accounts: Record; }; grantPermissions: ( - ...args: Parameters + requestedPermissions: RequestedPermissions, ) => Record>>; }, ) { @@ -159,16 +151,13 @@ async function requestEthereumAccountsHandler( caveatValue = setEthAccounts(caveatValue, legacyApproval.approvedAccounts); grantPermissions({ - subject: { origin }, - approvedPermissions: { - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: caveatValue, - }, - ], - }, + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: caveatValue, + }, + ], }, }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js index a1b30cb77a75..daf30faadc65 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js @@ -66,19 +66,11 @@ async function switchEthereumChainHandler( ); } - return switchChain( - res, - end, - origin, - chainId, - networkClientIdToSwitchTo, - null, - { - setActiveNetwork, - getCaveat, - updateCaveat, - requestPermissionApprovalForOrigin, - grantPermissions, - }, - ); + return switchChain(res, end, chainId, networkClientIdToSwitchTo, null, { + setActiveNetwork, + getCaveat, + updateCaveat, + requestPermissionApprovalForOrigin, + grantPermissions, + }); } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js index d131f7a67231..726fa20c2428 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js @@ -163,7 +163,6 @@ describe('switchEthereumChainHandler', () => { expect(EthChainUtils.switchChain).toHaveBeenCalledWith( {}, end, - 'example.com', '0xdeadbeef', 'mainnet', null, diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts index 45376011ae22..592d0c162714 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts @@ -668,7 +668,6 @@ describe('requestPermissionsHandler', () => { await handler(getBaseRequest()); expect(updateCaveat).toHaveBeenCalledWith( - 'http://test.com', Caip25EndowmentPermissionName, Caip25CaveatType, { @@ -693,23 +692,18 @@ describe('requestPermissionsHandler', () => { await handler(getBaseRequest()); expect(grantPermissions).toHaveBeenCalledWith({ - subject: { - origin: 'http://test.com', - }, - approvedPermissions: { - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: { - requiredScopes: {}, - optionalScopes: {}, - sessionProperties: { caveatValueWithEthAccountsSet: true }, - isMultichainOrigin: false, - }, + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: {}, + sessionProperties: { caveatValueWithEthAccountsSet: true }, + isMultichainOrigin: false, }, - ], - }, + }, + ], }, }); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index 2e49d1a1c0b0..a301b1b5ca25 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -90,13 +90,12 @@ async function requestPermissionsImplementation( requestedPermissions: RequestedPermissions, ) => Promise<[GrantedPermissions]>; updateCaveat: ( - origin: string, permissionName: string, caveatName: string, caveatValue: Caip25CaveatValue, ) => void; grantPermissions: ( - ...args: Parameters + requestedPermissions: RequestedPermissions, ) => Record>>; requestPermissionApprovalForOrigin: ( requestedPermissions: RequestedPermissions, @@ -210,23 +209,19 @@ async function requestPermissionsImplementation( } updateCaveat( - origin, Caip25EndowmentPermissionName, Caip25CaveatType, newCaveatValue, ); } else { caip25Endowment = grantPermissions({ - subject: { origin }, - approvedPermissions: { - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: newCaveatValue, - }, - ], - }, + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: newCaveatValue, + }, + ], }, })[Caip25EndowmentPermissionName]; } diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 4a6345fcd8d6..d1d7ac35b392 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -6205,11 +6205,15 @@ export default class MetamaskController extends EventEmitter { this.alertController, ), - grantPermissions: this.permissionController.grantPermissions.bind( - this.permissionController, - ), + grantPermissions: (approvedPermissions) => { + return this.permissionController.grantPermissions({ + subject: { origin }, + approvedPermissions, + }); + }, updateCaveat: this.permissionController.updateCaveat.bind( this.permissionController, + origin, ), ///: BEGIN:ONLY_INCLUDE_IF(build-mmi) From f83d41815ad0d058d70dc2194e7c1895bd81debc Mon Sep 17 00:00:00 2001 From: jiexi Date: Fri, 13 Dec 2024 09:07:03 -0800 Subject: [PATCH 133/148] Update app/scripts/migrations/135.ts --- app/scripts/migrations/135.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/scripts/migrations/135.ts b/app/scripts/migrations/135.ts index b911b80c5e3f..b48dba122cb4 100644 --- a/app/scripts/migrations/135.ts +++ b/app/scripts/migrations/135.ts @@ -44,10 +44,8 @@ const PermissionNames = { } as const; const BUILT_IN_NETWORKS: ReadonlyMap = new Map([ - ['goerli', '0x5'], ['sepolia', '0xaa36a7'], ['mainnet', '0x1'], - ['linea-goerli', '0xe704'], ['linea-sepolia', '0xe705'], ['linea-mainnet', '0xe708'], ]); From d00072583d8d671cf38861131cad53aab19decf0 Mon Sep 17 00:00:00 2001 From: jiexi Date: Mon, 16 Dec 2024 14:48:22 -0800 Subject: [PATCH 134/148] feat: Move CAIP-25 permission validation logic into caveat validator (#29166) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** Adopts `@metamask/mutlichain` changes that move validation logic out of the CAIP-25 permission and into the caveat itself. [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/29166?quickstart=1) ## **Related issues** Core: https://github.com/MetaMask/core/pull/5064 ## **Manual testing steps** 1. Go to this page... 2. 3. ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --- .../controllers/permissions/specifications.js | 37 +++++++++---------- app/scripts/metamask-controller.js | 20 +++++----- package.json | 2 +- yarn.lock | 10 ++--- 4 files changed, 34 insertions(+), 35 deletions(-) diff --git a/app/scripts/controllers/permissions/specifications.js b/app/scripts/controllers/permissions/specifications.js index a0b44131ea5b..85d6c3728a0b 100644 --- a/app/scripts/controllers/permissions/specifications.js +++ b/app/scripts/controllers/permissions/specifications.js @@ -6,6 +6,7 @@ import { createCaip25Caveat, Caip25CaveatType, caip25EndowmentBuilder, + caip25CaveatBuilder, } from '@metamask/multichain'; import { EndowmentTypes, @@ -38,12 +39,23 @@ export const CaveatFactories = Object.freeze({ /** * Gets the specifications for all caveats that will be recognized by the * PermissionController. + * + * @param options - The options object. + * @param options.listAccounts - A function that returns the + * `AccountsController` internalAccount objects for all evm accounts. + * @param options.findNetworkClientIdByChainId - A function that + * returns the networkClientId given a chainId. + * @returns the caveat specifications to construct the PermissionController. */ -export const getCaveatSpecifications = () => { +export const getCaveatSpecifications = ({ + listAccounts, + findNetworkClientIdByChainId, +}) => { return { - [Caip25CaveatType]: { - type: Caip25CaveatType, - }, + [Caip25CaveatType]: caip25CaveatBuilder({ + listAccounts, + findNetworkClientIdByChainId, + }), ...snapsCaveatsSpecifications, ...snapsEndowmentCaveatSpecifications, }; @@ -53,25 +65,12 @@ export const getCaveatSpecifications = () => { * Gets the specifications for all permissions that will be recognized by the * PermissionController. * - * @param options - The options object. - * @param options.listAccounts - A function that returns the - * `AccountsController` internalAccount objects for all evm accounts. - * @param options.findNetworkClientIdByChainId - A function that - * returns the networkClientId given a chainId. * @returns the permission specifications to construct the PermissionController. */ -export const getPermissionSpecifications = ({ - listAccounts, - findNetworkClientIdByChainId, -}) => { +export const getPermissionSpecifications = () => { return { [caip25EndowmentBuilder.targetName]: - caip25EndowmentBuilder.specificationBuilder({ - methodHooks: { - findNetworkClientIdByChainId, - listAccounts, - }, - }), + caip25EndowmentBuilder.specificationBuilder({}), }; }; diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 9089e7b3fb1f..2afaa91a0976 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -1268,17 +1268,17 @@ export default class MetamaskController extends EventEmitter { ], }), state: initState.PermissionController, - caveatSpecifications: getCaveatSpecifications(), - permissionSpecifications: { - ...getPermissionSpecifications({ - listAccounts: this.accountsController.listAccounts.bind( - this.accountsController, + caveatSpecifications: getCaveatSpecifications({ + listAccounts: this.accountsController.listAccounts.bind( + this.accountsController, + ), + findNetworkClientIdByChainId: + this.networkController.findNetworkClientIdByChainId.bind( + this.networkController, ), - findNetworkClientIdByChainId: - this.networkController.findNetworkClientIdByChainId.bind( - this.networkController, - ), - }), + }), + permissionSpecifications: { + ...getPermissionSpecifications(), ...this.getSnapPermissionSpecifications(), }, unrestrictedMethods, diff --git a/package.json b/package.json index d6f12cff8842..e5de3331ff91 100644 --- a/package.json +++ b/package.json @@ -323,7 +323,7 @@ "@metamask/message-manager": "^11.0.0", "@metamask/message-signing-snap": "^0.6.0", "@metamask/metamask-eth-abis": "^3.1.1", - "@metamask/multichain": "^1.1.2", + "@metamask/multichain": "^2.0.0", "@metamask/name-controller": "^8.0.0", "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A22.1.0#~/.yarn/patches/@metamask-network-controller-npm-22.1.0-621c281f70.patch", "@metamask/notification-services-controller": "^0.15.0", diff --git a/yarn.lock b/yarn.lock index a424e757afcb..13dfa9b05d53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5760,9 +5760,9 @@ __metadata: languageName: node linkType: hard -"@metamask/multichain@npm:^1.1.2": - version: 1.1.2 - resolution: "@metamask/multichain@npm:1.1.2" +"@metamask/multichain@npm:^2.0.0": + version: 2.0.0 + resolution: "@metamask/multichain@npm:2.0.0" dependencies: "@metamask/api-specs": "npm:^0.10.12" "@metamask/controller-utils": "npm:^11.4.4" @@ -5773,7 +5773,7 @@ __metadata: peerDependencies: "@metamask/network-controller": ^22.0.0 "@metamask/permission-controller": ^11.0.0 - checksum: 10/ae2f9be92dc3d5e68a8a93f65f7dba30844c0ba98877005ad228f587a43fa06f45b441f3cb10260db95febdc405f06f3fb3b65d3545efb4c634cdecea846e5fb + checksum: 10/3ae5a1b76070f06b952c1781d36b075a11cc7e94cb3dec35f93e20ed29c5e356cec320079e583e92e3cce52613c125c740bdd020fae0da30a2503b2eb3a2dca0 languageName: node linkType: hard @@ -26599,7 +26599,7 @@ __metadata: "@metamask/message-manager": "npm:^11.0.0" "@metamask/message-signing-snap": "npm:^0.6.0" "@metamask/metamask-eth-abis": "npm:^3.1.1" - "@metamask/multichain": "npm:^1.1.2" + "@metamask/multichain": "npm:^2.0.0" "@metamask/name-controller": "npm:^8.0.0" "@metamask/network-controller": "patch:@metamask/network-controller@npm%3A22.1.0#~/.yarn/patches/@metamask-network-controller-npm-22.1.0-621c281f70.patch" "@metamask/notification-services-controller": "npm:^0.15.0" From ff3b2725832dfb96cf950bad8436a985b05e62ad Mon Sep 17 00:00:00 2001 From: jiexi Date: Mon, 16 Dec 2024 16:09:56 -0800 Subject: [PATCH 135/148] Jl/caip25 permission migration/move request grant hooks into mmc (#29213) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## **Description** * Adds `requestPermittedChainsPermission`, `requestPermittedChainsPermissionIncremental`, and `requestCaip25Permission` methods to `MetamaskController` * Uses these new hooks in `wallet_requestPermissions`, `eth_requestAccounts`, `wallet_switchEthereumChain`, `wallet_addEthereumChain` * Removes `wallet_requestPermissions` update caveat flow * Make `wallet_requestPermissions` no longer support anything but `eth_requestAccounts` and `endowment:permittedChains` [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/29213?quickstart=1) ## **Related issues** See: https://github.com/MetaMask/metamask-extension/pull/27847#discussion_r1881143169 ## **Manual testing steps** Only user facing change is that `wallet_requestPermissions` should correctly replace instead update existing permissions again ## **Screenshots/Recordings** ### **Before** ### **After** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I’ve included tests if applicable - [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: Alex Donesky --- .../handlers/add-ethereum-chain.js | 15 +- .../handlers/add-ethereum-chain.test.js | 29 +- .../handlers/ethereum-chain-utils.js | 80 +- .../handlers/ethereum-chain-utils.test.ts | 142 +-- .../handlers/request-accounts.test.ts | 150 +-- .../handlers/request-accounts.ts | 69 +- .../handlers/switch-ethereum-chain.js | 15 +- .../handlers/switch-ethereum-chain.test.js | 13 +- .../wallet-requestPermissions.test.ts | 653 ++----------- .../handlers/wallet-requestPermissions.ts | 209 ++-- app/scripts/metamask-controller.js | 242 ++++- app/scripts/metamask-controller.test.js | 916 +++++++++++++++++- 12 files changed, 1388 insertions(+), 1145 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js index ef714a29c9bc..721fe6e82107 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.js @@ -22,9 +22,8 @@ const addEthereumChain = { endApprovalFlow: true, getCurrentChainIdForDomain: true, getCaveat: true, - requestPermissionApprovalForOrigin: true, - updateCaveat: true, - grantPermissions: true, + requestPermittedChainsPermissionForOrigin: true, + requestPermittedChainsPermissionIncrementalForOrigin: true, }, }; @@ -45,9 +44,8 @@ async function addEthereumChainHandler( endApprovalFlow, getCurrentChainIdForDomain, getCaveat, - requestPermissionApprovalForOrigin, - updateCaveat, - grantPermissions, + requestPermittedChainsPermissionForOrigin, + requestPermittedChainsPermissionIncrementalForOrigin, }, ) { let validParams; @@ -199,10 +197,9 @@ async function addEthereumChainHandler( isAddFlow: true, setActiveNetwork, getCaveat, - requestPermissionApprovalForOrigin, - updateCaveat, endApprovalFlow, - grantPermissions, + requestPermittedChainsPermissionForOrigin, + requestPermittedChainsPermissionIncrementalForOrigin, }); } else if (approvalFlowId) { endApprovalFlow({ id: approvalFlowId }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js index 7b91cfb8e8d4..517921570540 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/add-ethereum-chain.test.js @@ -68,7 +68,6 @@ const createMockedHandler = () => { getNetworkConfigurationByChainId: jest.fn(), setActiveNetwork: jest.fn(), requestUserApproval: jest.fn().mockResolvedValue(123), - requestPermissionApprovalForOrigin: jest.fn(), getCaveat: jest.fn(), startApprovalFlow: () => ({ id: 'approvalFlowId' }), endApprovalFlow: jest.fn(), @@ -80,8 +79,8 @@ const createMockedHandler = () => { defaultRpcEndpointIndex: 0, rpcEndpoints: [{ networkClientId: 123 }], }), - updateCaveat: jest.fn(), - grantPermissions: jest.fn(), + requestPermittedChainsPermissionForOrigin: jest.fn(), + requestPermittedChainsPermissionIncrementalForOrigin: jest.fn(), }; const response = {}; const handler = (request) => @@ -191,11 +190,11 @@ describe('addEthereumChainHandler', () => { isAddFlow: true, endApprovalFlow: mocks.endApprovalFlow, getCaveat: mocks.getCaveat, - requestPermissionApprovalForOrigin: - mocks.requestPermissionApprovalForOrigin, setActiveNetwork: mocks.setActiveNetwork, - updateCaveat: mocks.updateCaveat, - grantPermissions: mocks.grantPermissions, + requestPermittedChainsPermissionForOrigin: + mocks.requestPermittedChainsPermissionForOrigin, + requestPermittedChainsPermissionIncrementalForOrigin: + mocks.requestPermittedChainsPermissionIncrementalForOrigin, }, ); }); @@ -260,11 +259,11 @@ describe('addEthereumChainHandler', () => { isAddFlow: true, endApprovalFlow: mocks.endApprovalFlow, getCaveat: mocks.getCaveat, - requestPermissionApprovalForOrigin: - mocks.requestPermissionApprovalForOrigin, setActiveNetwork: mocks.setActiveNetwork, - updateCaveat: mocks.updateCaveat, - grantPermissions: mocks.grantPermissions, + requestPermittedChainsPermissionForOrigin: + mocks.requestPermittedChainsPermissionForOrigin, + requestPermittedChainsPermissionIncrementalForOrigin: + mocks.requestPermittedChainsPermissionIncrementalForOrigin, }, ); }); @@ -309,11 +308,11 @@ describe('addEthereumChainHandler', () => { isAddFlow: true, endApprovalFlow: mocks.endApprovalFlow, getCaveat: mocks.getCaveat, - requestPermissionApprovalForOrigin: - mocks.requestPermissionApprovalForOrigin, setActiveNetwork: mocks.setActiveNetwork, - updateCaveat: mocks.updateCaveat, - grantPermissions: mocks.grantPermissions, + requestPermittedChainsPermissionForOrigin: + mocks.requestPermittedChainsPermissionForOrigin, + requestPermittedChainsPermissionIncrementalForOrigin: + mocks.requestPermittedChainsPermissionIncrementalForOrigin, }, ); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index 687ddebbb7f2..b9fae856da98 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -1,9 +1,8 @@ -import { errorCodes, rpcErrors, providerErrors } from '@metamask/rpc-errors'; +import { errorCodes, rpcErrors } from '@metamask/rpc-errors'; import { Caip25CaveatType, Caip25EndowmentPermissionName, getPermittedEthChainIds, - addPermittedEthChainId, } from '@metamask/multichain'; import { isPrefixedFormattedHexString, @@ -11,8 +10,6 @@ import { } from '../../../../../shared/modules/network.utils'; import { UNKNOWN_TICKER_SYMBOL } from '../../../../../shared/constants/app'; import { getValidUrl } from '../../util'; -import { CaveatTypes } from '../../../../../shared/constants/permissions'; -import { PermissionNames } from '../../../controllers/permissions'; export function validateChainId(chainId) { const lowercasedChainId = @@ -176,9 +173,8 @@ export function validateAddEthereumChainParams(params) { * @param {Function} hooks.setActiveNetwork - The callback to change the current network for the origin. * @param {Function} hooks.endApprovalFlow - The optional callback to end the approval flow when approvalFlowId is provided. * @param {Function} hooks.getCaveat - The callback to get the CAIP-25 caveat for the origin. - * @param {Function} hooks.requestPermissionApprovalForOrigin - The callback to prompt the user for permission approval. - * @param {Function} hooks.updateCaveat - The callback to update the CAIP-25 caveat value. - * @param {Function} hooks.grantPermissions - The callback to grant a CAIP-25 permission when one does not already exist. + * @param {Function} hooks.requestPermittedChainsPermissionForOrigin - The callback to request a new permittedChains-equivalent CAIP-25 permission. + * @param {Function} hooks.requestPermittedChainsPermissionIncrementalForOrigin - The callback to add a new chain to the permittedChains-equivalent CAIP-25 permission. * @returns a null response on success or an error if user rejects an approval when isAddFlow is false or on unexpected errors. */ export async function switchChain( @@ -192,9 +188,8 @@ export async function switchChain( setActiveNetwork, endApprovalFlow, getCaveat, - requestPermissionApprovalForOrigin, - updateCaveat, - grantPermissions, + requestPermittedChainsPermissionForOrigin, + requestPermittedChainsPermissionIncrementalForOrigin, }, ) { try { @@ -207,68 +202,15 @@ export async function switchChain( const ethChainIds = getPermittedEthChainIds(caip25Caveat.value); if (!ethChainIds.includes(chainId)) { - if (caip25Caveat.value.isMultichainOrigin) { - return end( - providerErrors.unauthorized( - `Cannot switch to or add permissions for chainId '${chainId}' because permissions were granted over the Multichain API.`, - ), - ); - } - - if (!isAddFlow) { - await requestPermissionApprovalForOrigin({ - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: [chainId], - }, - ], - }, - }); - } - - const updatedCaveatValue = addPermittedEthChainId( - caip25Caveat.value, + await requestPermittedChainsPermissionIncrementalForOrigin({ chainId, - ); - - updateCaveat( - Caip25EndowmentPermissionName, - Caip25CaveatType, - updatedCaveatValue, - ); - } - } else { - if (!isAddFlow) { - await requestPermissionApprovalForOrigin({ - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: [chainId], - }, - ], - }, + autoApprove: isAddFlow, }); } - - let caveatValue = { - requiredScopes: {}, - optionalScopes: {}, - isMultichainOrigin: false, - }; - caveatValue = addPermittedEthChainId(caveatValue, chainId); - - grantPermissions({ - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: caveatValue, - }, - ], - }, + } else { + await requestPermittedChainsPermissionForOrigin({ + chainId, + autoApprove: isAddFlow, }); } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts index c2bd5ef2037b..51b77372dd09 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.test.ts @@ -4,8 +4,6 @@ import { Caip25EndowmentPermissionName, } from '@metamask/multichain'; import { Hex } from '@metamask/utils'; -import { CaveatTypes } from '../../../../../shared/constants/permissions'; -import { PermissionNames } from '../../../controllers/permissions'; import * as EthChainUtils from './ethereum-chain-utils'; describe('Ethereum Chain Utils', () => { @@ -16,9 +14,8 @@ describe('Ethereum Chain Utils', () => { setActiveNetwork: jest.fn(), endApprovalFlow: jest.fn(), getCaveat: jest.fn(), - requestPermissionApprovalForOrigin: jest.fn(), - updateCaveat: jest.fn(), - grantPermissions: jest.fn(), + requestPermittedChainsPermissionForOrigin: jest.fn(), + requestPermittedChainsPermissionIncrementalForOrigin: jest.fn(), }; const response: { result?: true } = {}; const switchChain = ( @@ -56,7 +53,7 @@ describe('Ethereum Chain Utils', () => { it('passes through unexpected errors if approvalFlowId is not provided', async () => { const { mocks, end, switchChain } = createMockedSwitchChain(); - mocks.requestPermissionApprovalForOrigin.mockRejectedValueOnce( + mocks.requestPermittedChainsPermissionForOrigin.mockRejectedValueOnce( new Error('unexpected error'), ); @@ -67,7 +64,7 @@ describe('Ethereum Chain Utils', () => { it('passes through unexpected errors if approvalFlowId is provided', async () => { const { mocks, end, switchChain } = createMockedSwitchChain(); - mocks.requestPermissionApprovalForOrigin.mockRejectedValueOnce( + mocks.requestPermittedChainsPermissionForOrigin.mockRejectedValueOnce( new Error('unexpected error'), ); @@ -78,7 +75,7 @@ describe('Ethereum Chain Utils', () => { it('ignores userRejectedRequest errors when approvalFlowId is provided', async () => { const { mocks, end, response, switchChain } = createMockedSwitchChain(); - mocks.requestPermissionApprovalForOrigin.mockRejectedValueOnce({ + mocks.requestPermittedChainsPermissionForOrigin.mockRejectedValueOnce({ code: errorCodes.provider.userRejectedRequest, }); @@ -99,45 +96,14 @@ describe('Ethereum Chain Utils', () => { }); describe('with no existing CAIP-25 permission', () => { - it('requests a switch chain approval if isAddFlow: false', async () => { + it('requests a switch chain approval without autoApprove if isAddFlow: false', async () => { const { mocks, switchChain } = createMockedSwitchChain(); mocks.isAddFlow = false; await switchChain('0x1', 'mainnet', 'approvalFlowId'); - expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x1'], - }, - ], - }, - }); - }); - - it('grants a new CAIP-25 permission with the chain', async () => { - const { mocks, switchChain } = createMockedSwitchChain(); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); - - expect(mocks.grantPermissions).toHaveBeenCalledWith({ - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: { - requiredScopes: {}, - optionalScopes: { - 'eip155:1': { - accounts: [], - }, - }, - isMultichainOrigin: false, - }, - }, - ], - }, - }); + expect( + mocks.requestPermittedChainsPermissionForOrigin, + ).toHaveBeenCalledWith({ chainId: '0x1', autoApprove: false }); }); it('switches to the chain', async () => { @@ -149,21 +115,22 @@ describe('Ethereum Chain Utils', () => { it('should handle errors if the switch chain approval is rejected', async () => { const { mocks, end, switchChain } = createMockedSwitchChain(); - mocks.requestPermissionApprovalForOrigin.mockRejectedValueOnce({ + mocks.requestPermittedChainsPermissionForOrigin.mockRejectedValueOnce({ code: errorCodes.provider.userRejectedRequest, }); await switchChain('0x1', 'mainnet', 'approvalFlowId'); - expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalled(); - expect(mocks.grantPermissions).not.toHaveBeenCalled(); + expect( + mocks.requestPermittedChainsPermissionForOrigin, + ).toHaveBeenCalled(); expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); expect(end).toHaveBeenCalledWith(); }); }); describe('with an existing CAIP-25 permission granted from the legacy flow (isMultichainOrigin: false) and the chainId is not already permissioned', () => { - it('skips permittedChains approval and switches to it if isAddFlow: true', async () => { + it('requests a switch chain approval with autoApprove and switches to it if isAddFlow: true', async () => { const { mocks, switchChain } = createMockedSwitchChain(); mocks.isAddFlow = true; mocks.getCaveat.mockReturnValue({ @@ -175,11 +142,13 @@ describe('Ethereum Chain Utils', () => { }); await switchChain('0x1', 'mainnet', 'approvalFlowId'); - expect(mocks.requestPermissionApprovalForOrigin).not.toHaveBeenCalled(); + expect( + mocks.requestPermittedChainsPermissionIncrementalForOrigin, + ).toHaveBeenCalledWith({ chainId: '0x1', autoApprove: true }); expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); }); - it('requests permittedChains approval then switches to it if isAddFlow: false', async () => { + it('requests permittedChains approval without autoApprove then switches to it if isAddFlow: false', async () => { const { mocks, switchChain } = createMockedSwitchChain(); mocks.isAddFlow = false; mocks.getCaveat.mockReturnValue({ @@ -191,51 +160,19 @@ describe('Ethereum Chain Utils', () => { }); await switchChain('0x1', 'mainnet', 'approvalFlowId'); - expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalled(); - expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x1'], - }, - ], - }, - }); + expect( + mocks.requestPermittedChainsPermissionIncrementalForOrigin, + ).toHaveBeenCalledWith({ chainId: '0x1', autoApprove: false }); expect(mocks.setActiveNetwork).toHaveBeenCalledWith('mainnet'); }); - it('updates the CAIP-25 caveat with the chain added', async () => { - const { mocks, switchChain } = createMockedSwitchChain(); - mocks.getCaveat.mockReturnValue({ - value: { - requiredScopes: {}, - optionalScopes: {}, - isMultichainOrigin: false, - }, - }); - await switchChain('0x1', 'mainnet', 'approvalFlowId'); - - expect(mocks.updateCaveat).toHaveBeenCalledWith( - Caip25EndowmentPermissionName, - Caip25CaveatType, + it('should handle errors if the permittedChains approval is rejected', async () => { + const { mocks, end, switchChain } = createMockedSwitchChain(); + mocks.requestPermittedChainsPermissionIncrementalForOrigin.mockRejectedValueOnce( { - requiredScopes: {}, - optionalScopes: { - 'eip155:1': { - accounts: [], - }, - }, - isMultichainOrigin: false, + code: errorCodes.provider.userRejectedRequest, }, ); - }); - - it('should handle errors if the permittedChains approval is rejected', async () => { - const { mocks, end, switchChain } = createMockedSwitchChain(); - mocks.requestPermissionApprovalForOrigin.mockRejectedValueOnce({ - code: errorCodes.provider.userRejectedRequest, - }); mocks.getCaveat.mockReturnValue({ value: { requiredScopes: {}, @@ -245,15 +182,22 @@ describe('Ethereum Chain Utils', () => { }); await switchChain('0x1', 'mainnet', 'approvalFlowId'); - expect(mocks.requestPermissionApprovalForOrigin).toHaveBeenCalled(); + expect( + mocks.requestPermittedChainsPermissionIncrementalForOrigin, + ).toHaveBeenCalled(); expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); expect(end).toHaveBeenCalledWith(); }); }); describe('with an existing CAIP-25 permission granted from the multichain flow (isMultichainOrigin: true) and the chainId is not already permissioned', () => { - it('does not request permittedChains approval', async () => { + it('requests permittedChains approval', async () => { const { mocks, switchChain } = createMockedSwitchChain(); + mocks.requestPermittedChainsPermissionIncrementalForOrigin.mockRejectedValue( + new Error( + "Cannot switch to or add permissions for chainId '0x1' because permissions were granted over the Multichain API.", + ), + ); mocks.getCaveat.mockReturnValue({ value: { requiredScopes: {}, @@ -263,7 +207,9 @@ describe('Ethereum Chain Utils', () => { }); await switchChain('0x1', 'mainnet', 'approvalFlowId'); - expect(mocks.requestPermissionApprovalForOrigin).not.toHaveBeenCalled(); + expect( + mocks.requestPermittedChainsPermissionIncrementalForOrigin, + ).toHaveBeenCalledWith({ chainId: '0x1', autoApprove: false }); }); it('does not switch the active network', async () => { @@ -275,6 +221,12 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: true, }, }); + mocks.requestPermittedChainsPermissionIncrementalForOrigin.mockRejectedValue( + new Error( + "Cannot switch to or add permissions for chainId '0x1' because permissions were granted over the Multichain API.", + ), + ); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(mocks.setActiveNetwork).not.toHaveBeenCalled(); @@ -289,6 +241,12 @@ describe('Ethereum Chain Utils', () => { isMultichainOrigin: true, }, }); + mocks.requestPermittedChainsPermissionIncrementalForOrigin.mockRejectedValue( + new Error( + "Cannot switch to or add permissions for chainId '0x1' because permissions were granted over the Multichain API.", + ), + ); + await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect(end).toHaveBeenCalledWith( @@ -322,7 +280,7 @@ describe('Ethereum Chain Utils', () => { await switchChain('0x1', 'mainnet', 'approvalFlowId'); expect( - mocks.requestPermissionApprovalForOrigin, + mocks.requestPermittedChainsPermissionIncrementalForOrigin, ).not.toHaveBeenCalled(); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts index 19185d1a133a..d3a52aad230b 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.test.ts @@ -1,9 +1,4 @@ import { rpcErrors } from '@metamask/rpc-errors'; -import { - Caip25CaveatType, - Caip25EndowmentPermissionName, -} from '@metamask/multichain'; -import * as Multichain from '@metamask/multichain'; import { JsonRpcParams, JsonRpcRequest, @@ -11,18 +6,9 @@ import { } from '@metamask/utils'; import { deferredPromise } from '../../util'; import * as Util from '../../util'; -import { RestrictedMethods } from '../../../../../shared/constants/permissions'; -import { PermissionNames } from '../../../controllers/permissions'; import { flushPromises } from '../../../../../test/lib/timer-helpers'; import requestEthereumAccounts from './request-accounts'; -jest.mock('@metamask/multichain', () => ({ - ...jest.requireActual('@metamask/multichain'), - setPermittedEthChainIds: jest.fn(), - setEthAccounts: jest.fn(), -})); -const MockMultichain = jest.mocked(Multichain); - jest.mock('../../util', () => ({ ...jest.requireActual('../../util'), shouldEmitDappViewedEvent: jest.fn(), @@ -43,10 +29,6 @@ const createMockedHandler = () => { const end = jest.fn(); const getAccounts = jest.fn().mockReturnValue([]); const getUnlockPromise = jest.fn(); - const requestPermissionApprovalForOrigin = jest.fn().mockResolvedValue({ - approvedChainIds: ['0x1', '0x5'], - approvedAccounts: ['0xdeadbeef'], - }); const sendMetrics = jest.fn(); const metamaskState = { permissionHistory: {}, @@ -57,7 +39,7 @@ const createMockedHandler = () => { '0x3': {}, }, }; - const grantPermissions = jest.fn(); + const requestCaip25PermissionForOrigin = jest.fn().mockResolvedValue({}); const response: PendingJsonRpcResponse = { jsonrpc: '2.0' as const, id: 0, @@ -69,10 +51,9 @@ const createMockedHandler = () => { requestEthereumAccounts.implementation(request, response, next, end, { getAccounts, getUnlockPromise, - requestPermissionApprovalForOrigin, sendMetrics, metamaskState, - grantPermissions, + requestCaip25PermissionForOrigin, }); return { @@ -81,23 +62,14 @@ const createMockedHandler = () => { end, getAccounts, getUnlockPromise, - requestPermissionApprovalForOrigin, sendMetrics, - grantPermissions, + metamaskState, + requestCaip25PermissionForOrigin, handler, }; }; describe('requestEthereumAccountsHandler', () => { - beforeEach(() => { - MockMultichain.setEthAccounts.mockImplementation( - (caveatValue) => caveatValue, - ); - MockMultichain.setPermittedEthChainIds.mockImplementation( - (caveatValue) => caveatValue, - ); - }); - afterEach(() => { jest.resetAllMocks(); }); @@ -155,31 +127,18 @@ describe('requestEthereumAccountsHandler', () => { }); describe('eip155 account permissions do not exist', () => { - it('requests eth_accounts and permittedChains approval if origin is not snapId', async () => { - const { handler, requestPermissionApprovalForOrigin } = + it('requests the CAIP-25 permission', async () => { + const { handler, requestCaip25PermissionForOrigin } = createMockedHandler(); await handler({ ...baseRequest, origin: 'http://test.com' }); - expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ - [RestrictedMethods.eth_accounts]: {}, - [PermissionNames.permittedChains]: {}, - }); + expect(requestCaip25PermissionForOrigin).toHaveBeenCalledWith(); }); - it('requests eth_accounts approval if origin is snapId', async () => { - const { handler, requestPermissionApprovalForOrigin } = + it('throws an error if the CAIP-25 permission approval is rejected', async () => { + const { handler, requestCaip25PermissionForOrigin, end } = createMockedHandler(); - - await handler({ ...baseRequest, origin: 'npm:snap' }); - expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ - [RestrictedMethods.eth_accounts]: {}, - }); - }); - - it('throws an error if the eth_accounts and permittedChains approval is rejected', async () => { - const { handler, requestPermissionApprovalForOrigin, end } = - createMockedHandler(); - requestPermissionApprovalForOrigin.mockRejectedValue( + requestCaip25PermissionForOrigin.mockRejectedValue( new Error('approval rejected'), ); @@ -187,95 +146,6 @@ describe('requestEthereumAccountsHandler', () => { expect(end).toHaveBeenCalledWith(new Error('approval rejected')); }); - it('sets the approved chainIds on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is not snapId', async () => { - const { handler } = createMockedHandler(); - - await handler(baseRequest); - expect(MockMultichain.setPermittedEthChainIds).toHaveBeenCalledWith( - { - requiredScopes: {}, - optionalScopes: {}, - isMultichainOrigin: false, - }, - ['0x1', '0x5'], - ); - }); - - it('sets the approved accounts on the CAIP-25 caveat after the approved chainIds if origin is not snapId', async () => { - const { handler } = createMockedHandler(); - - MockMultichain.setPermittedEthChainIds.mockReturnValue({ - requiredScopes: {}, - optionalScopes: {}, - sessionProperties: { caveatValueWithEthChainIdsSet: true }, - isMultichainOrigin: false, - }); - - await handler(baseRequest); - expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( - { - requiredScopes: {}, - optionalScopes: {}, - sessionProperties: { caveatValueWithEthChainIdsSet: true }, - isMultichainOrigin: false, - }, - ['0xdeadbeef'], - ); - }); - - it('does not set the approved chainIds on an empty CAIP-25 caveat if origin is snapId', async () => { - const { handler } = createMockedHandler(); - - await handler({ ...baseRequest, origin: 'npm:snap' }); - expect(MockMultichain.setPermittedEthChainIds).not.toHaveBeenCalled(); - }); - - it('sets the approved accounts for the `wallet:eip155` scope with isMultichainOrigin: false if origin is snapId', async () => { - const { handler } = createMockedHandler(); - - await handler({ ...baseRequest, origin: 'npm:snap' }); - expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( - { - requiredScopes: {}, - optionalScopes: { - 'wallet:eip155': { - accounts: [], - }, - }, - isMultichainOrigin: false, - }, - ['0xdeadbeef'], - ); - }); - - it('grants a CAIP-25 permission', async () => { - const { handler, grantPermissions } = createMockedHandler(); - - MockMultichain.setEthAccounts.mockReturnValue({ - requiredScopes: {}, - optionalScopes: {}, - sessionProperties: { caveatValueWithEthAccountsSet: true }, - isMultichainOrigin: false, - }); - - await handler(baseRequest); - expect(grantPermissions).toHaveBeenCalledWith({ - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: { - requiredScopes: {}, - optionalScopes: {}, - sessionProperties: { caveatValueWithEthAccountsSet: true }, - isMultichainOrigin: false, - }, - }, - ], - }, - }); - }); - it('returns the newly granted and properly ordered eth accounts', async () => { const { handler, getAccounts, response } = createMockedHandler(); getAccounts diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts index be180e80ff07..aac97378e24c 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts @@ -1,9 +1,8 @@ import { rpcErrors } from '@metamask/rpc-errors'; import { Caip25CaveatType, + Caip25CaveatValue, Caip25EndowmentPermissionName, - setEthAccounts, - setPermittedEthChainIds, } from '@metamask/multichain'; import { Caveat, @@ -11,8 +10,6 @@ import { ValidPermission, } from '@metamask/permission-controller'; import { - Hex, - Json, JsonRpcParams, JsonRpcRequest, PendingJsonRpcResponse, @@ -29,10 +26,6 @@ import { MetaMetricsEventOptions, } from '../../../../../shared/constants/metametrics'; import { shouldEmitDappViewedEvent } from '../../util'; -import { RestrictedMethods } from '../../../../../shared/constants/permissions'; -import { PermissionNames } from '../../../controllers/permissions'; -// eslint-disable-next-line import/no-restricted-paths -import { isSnapId } from '../../../../../ui/helpers/utils/snaps'; /** * This method attempts to retrieve the Ethereum accounts available to the @@ -48,10 +41,9 @@ const requestEthereumAccounts = { hookNames: { getAccounts: true, getUnlockPromise: true, - requestPermissionApprovalForOrigin: true, sendMetrics: true, metamaskState: true, - grantPermissions: true, + requestCaip25PermissionForOrigin: true, }, }; export default requestEthereumAccounts; @@ -67,16 +59,12 @@ async function requestEthereumAccountsHandler( { getAccounts, getUnlockPromise, - requestPermissionApprovalForOrigin, sendMetrics, metamaskState, - grantPermissions, + requestCaip25PermissionForOrigin, }: { getAccounts: (ignoreLock?: boolean) => string[]; getUnlockPromise: (shouldShowUnlockRequest: true) => Promise; - requestPermissionApprovalForOrigin: ( - requestedPermissions: RequestedPermissions, - ) => Promise<{ approvedAccounts: Hex[]; approvedChainIds: Hex[] }>; sendMetrics: ( payload: MetaMetricsEventPayload, options?: MetaMetricsEventOptions, @@ -86,9 +74,14 @@ async function requestEthereumAccountsHandler( permissionHistory: Record; accounts: Record; }; - grantPermissions: ( - requestedPermissions: RequestedPermissions, - ) => Record>>; + requestCaip25PermissionForOrigin: ( + requestedPermissions?: RequestedPermissions, + ) => Promise< + ValidPermission< + typeof Caip25EndowmentPermissionName, + Caveat + > + >; }, ) { const { origin } = req; @@ -117,50 +110,12 @@ async function requestEthereumAccountsHandler( return undefined; } - let legacyApproval; try { - legacyApproval = await requestPermissionApprovalForOrigin({ - [RestrictedMethods.eth_accounts]: {}, - ...(!isSnapId(origin) && { - [PermissionNames.permittedChains]: {}, - }), - }); + await requestCaip25PermissionForOrigin(); } catch (error) { return end(error as unknown as Error); } - let caveatValue = { - requiredScopes: {}, - optionalScopes: {}, - isMultichainOrigin: false, - }; - - if (isSnapId(origin)) { - caveatValue.optionalScopes = { - 'wallet:eip155': { - accounts: [], - }, - }; - } else { - caveatValue = setPermittedEthChainIds( - caveatValue, - legacyApproval.approvedChainIds, - ); - } - - caveatValue = setEthAccounts(caveatValue, legacyApproval.approvedAccounts); - - grantPermissions({ - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: caveatValue, - }, - ], - }, - }); - ethAccounts = getAccounts(true); // first time connection to dapp will lead to no log in the permissionHistory // and if user has connected to dapp before, the dapp origin will be included in the permissionHistory state diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js index daf30faadc65..4a5e0ef6a4f8 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.js @@ -13,9 +13,8 @@ const switchEthereumChain = { setActiveNetwork: true, getCaveat: true, getCurrentChainIdForDomain: true, - requestPermissionApprovalForOrigin: true, - updateCaveat: true, - grantPermissions: true, + requestPermittedChainsPermissionForOrigin: true, + requestPermittedChainsPermissionIncrementalForOrigin: true, }, }; @@ -31,9 +30,8 @@ async function switchEthereumChainHandler( setActiveNetwork, getCaveat, getCurrentChainIdForDomain, - requestPermissionApprovalForOrigin, - updateCaveat, - grantPermissions, + requestPermittedChainsPermissionForOrigin, + requestPermittedChainsPermissionIncrementalForOrigin, }, ) { let chainId; @@ -69,8 +67,7 @@ async function switchEthereumChainHandler( return switchChain(res, end, chainId, networkClientIdToSwitchTo, null, { setActiveNetwork, getCaveat, - updateCaveat, - requestPermissionApprovalForOrigin, - grantPermissions, + requestPermittedChainsPermissionForOrigin, + requestPermittedChainsPermissionIncrementalForOrigin, }); } diff --git a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js index 726fa20c2428..694839b4562b 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/switch-ethereum-chain.test.js @@ -44,9 +44,8 @@ const createMockedHandler = () => { setActiveNetwork: jest.fn(), getCaveat: jest.fn(), getCurrentChainIdForDomain: jest.fn().mockReturnValue(NON_INFURA_CHAIN_ID), - requestPermissionApprovalForOrigin: jest.fn(), - updateCaveat: jest.fn(), - grantPermissions: jest.fn(), + requestPermittedChainsPermissionForOrigin: jest.fn(), + requestPermittedChainsPermissionIncrementalForOrigin: jest.fn(), }; const response = {}; const handler = (request) => @@ -169,10 +168,10 @@ describe('switchEthereumChainHandler', () => { { setActiveNetwork: mocks.setActiveNetwork, getCaveat: mocks.getCaveat, - updateCaveat: mocks.updateCaveat, - requestPermissionApprovalForOrigin: - mocks.requestPermissionApprovalForOrigin, - grantPermissions: mocks.grantPermissions, + requestPermittedChainsPermissionForOrigin: + mocks.requestPermittedChainsPermissionForOrigin, + requestPermittedChainsPermissionIncrementalForOrigin: + mocks.requestPermittedChainsPermissionIncrementalForOrigin, }, ); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts index 592d0c162714..51ea8c744afb 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts @@ -6,7 +6,6 @@ import { Caip25CaveatType, Caip25EndowmentPermissionName, } from '@metamask/multichain'; -import * as Multichain from '@metamask/multichain'; import { Json, JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils'; import { CaveatTypes, @@ -15,13 +14,6 @@ import { import { PermissionNames } from '../../../controllers/permissions'; import { requestPermissionsHandler } from './wallet-requestPermissions'; -jest.mock('@metamask/multichain', () => ({ - ...jest.requireActual('@metamask/multichain'), - setEthAccounts: jest.fn(), - setPermittedEthChainIds: jest.fn(), -})); -const MockMultichain = jest.mocked(Multichain); - const getBaseRequest = (overrides = {}) => ({ jsonrpc: '2.0' as const, id: 0, @@ -31,8 +23,6 @@ const getBaseRequest = (overrides = {}) => ({ params: [ { eth_accounts: {}, - [Caip25EndowmentPermissionName]: {}, - otherPermission: {}, }, ], ...overrides, @@ -41,76 +31,11 @@ const getBaseRequest = (overrides = {}) => ({ const createMockedHandler = () => { const next = jest.fn(); const end = jest.fn(); - const requestPermissionsForOrigin = jest.fn().mockResolvedValue([ - Object.freeze({ - otherPermission: { - id: '2', - parentCapability: 'otherPermission', - caveats: [ - { - value: { - foo: 'bar', - }, - }, - ], - }, - }), - ]); - const getPermissionsForOrigin = jest.fn().mockReturnValue( - Object.freeze({ - [Caip25EndowmentPermissionName]: { - id: '1', - parentCapability: Caip25EndowmentPermissionName, - caveats: [ - { - type: Caip25CaveatType, - value: { - requiredScopes: { - 'eip155:1': { - accounts: ['eip155:1:0x1', 'eip155:1:0x2'], - }, - 'eip155:5': { - accounts: ['eip155:5:0x1', 'eip155:5:0x3'], - }, - }, - optionalScopes: { - 'eip155:1': { - accounts: ['eip155:1:0x4'], - }, - 'other:1': { - accounts: ['other:1:0x4'], - }, - }, - }, - isMultichainOrigin: false, - }, - ], - }, - }), - ); - const updateCaveat = jest.fn(); - const grantPermissions = jest.fn().mockReturnValue( - Object.freeze({ - [Caip25EndowmentPermissionName]: { - id: 'new', - parentCapability: Caip25EndowmentPermissionName, - caveats: [ - { - type: Caip25CaveatType, - value: { - requiredScopes: {}, - optionalScopes: {}, - }, - }, - ], - }, - }), - ); - const requestPermissionApprovalForOrigin = jest.fn().mockResolvedValue({ - approvedChainIds: ['0x1', '0x5'], - approvedAccounts: ['0xdeadbeef'], - }); + const requestPermissionsForOrigin = jest + .fn() + .mockRejectedValue(new Error('failed to request unexpected permission')); const getAccounts = jest.fn().mockReturnValue([]); + const requestCaip25PermissionForOrigin = jest.fn().mockResolvedValue({}); const response: PendingJsonRpcResponse = { jsonrpc: '2.0' as const, id: 0, @@ -122,12 +47,9 @@ const createMockedHandler = () => { next, end, { - requestPermissionsForOrigin, - getPermissionsForOrigin, - updateCaveat, - grantPermissions, - requestPermissionApprovalForOrigin, getAccounts, + requestPermissionsForOrigin, + requestCaip25PermissionForOrigin, }, ); @@ -135,12 +57,9 @@ const createMockedHandler = () => { response, next, end, - requestPermissionsForOrigin, - getPermissionsForOrigin, - updateCaveat, - grantPermissions, - requestPermissionApprovalForOrigin, getAccounts, + requestPermissionsForOrigin, + requestCaip25PermissionForOrigin, handler, }; }; @@ -150,15 +69,6 @@ describe('requestPermissionsHandler', () => { jest.resetAllMocks(); }); - beforeEach(() => { - MockMultichain.setEthAccounts.mockImplementation( - (caveatValue) => caveatValue, - ); - MockMultichain.setPermittedEthChainIds.mockImplementation( - (caveatValue) => caveatValue, - ); - }); - it('returns an error if params is malformed', async () => { const { handler, end } = createMockedHandler(); @@ -169,13 +79,11 @@ describe('requestPermissionsHandler', () => { ); }); - it('requests approval from the ApprovalController for eth_accounts and permittedChains when only eth_accounts is specified in params and origin is not snapId', async () => { - const { handler, requestPermissionApprovalForOrigin } = - createMockedHandler(); + it('requests the CAIP-25 permission using eth_accounts when only eth_accounts is specified in params', async () => { + const { handler, requestCaip25PermissionForOrigin } = createMockedHandler(); await handler( getBaseRequest({ - origin: 'http://test.com', params: [ { [RestrictedMethods.eth_accounts]: { @@ -186,21 +94,18 @@ describe('requestPermissionsHandler', () => { }), ); - expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ + expect(requestCaip25PermissionForOrigin).toHaveBeenCalledWith({ [RestrictedMethods.eth_accounts]: { foo: 'bar', }, - [PermissionNames.permittedChains]: {}, }); }); - it('requests approval from the ApprovalController for eth_accounts and permittedChains when only permittedChains is specified in params and origin is not snapId', async () => { - const { handler, requestPermissionApprovalForOrigin } = - createMockedHandler(); + it('requests the CAIP-25 permission for permittedChains when only permittedChains is specified in params', async () => { + const { handler, requestCaip25PermissionForOrigin } = createMockedHandler(); await handler( getBaseRequest({ - origin: 'http://test.com', params: [ { [PermissionNames.permittedChains]: { @@ -216,8 +121,7 @@ describe('requestPermissionsHandler', () => { }), ); - expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ - [RestrictedMethods.eth_accounts]: {}, + expect(requestCaip25PermissionForOrigin).toHaveBeenCalledWith({ [PermissionNames.permittedChains]: { caveats: [ { @@ -229,13 +133,11 @@ describe('requestPermissionsHandler', () => { }); }); - it('requests approval from the ApprovalController for eth_accounts and permittedChains when both are specified in params and origin is not snapId', async () => { - const { handler, requestPermissionApprovalForOrigin } = - createMockedHandler(); + it('requests the CAIP-25 permission for eth_accounts and permittedChains when both are specified in params', async () => { + const { handler, requestCaip25PermissionForOrigin } = createMockedHandler(); await handler( getBaseRequest({ - origin: 'http://test.com', params: [ { [RestrictedMethods.eth_accounts]: { @@ -254,7 +156,7 @@ describe('requestPermissionsHandler', () => { }), ); - expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ + expect(requestCaip25PermissionForOrigin).toHaveBeenCalledWith({ [RestrictedMethods.eth_accounts]: { foo: 'bar', }, @@ -269,64 +171,15 @@ describe('requestPermissionsHandler', () => { }); }); - it('requests approval from the ApprovalController for only eth_accounts when only eth_accounts is specified in params and origin is snapId', async () => { - const { handler, requestPermissionApprovalForOrigin } = + it('returns an error if requesting the CAIP-25 permission fails', async () => { + const { handler, requestCaip25PermissionForOrigin, end } = createMockedHandler(); - - await handler( - getBaseRequest({ - origin: 'npm:snap', - params: [ - { - [RestrictedMethods.eth_accounts]: { - foo: 'bar', - }, - }, - ], - }), + requestCaip25PermissionForOrigin.mockRejectedValue( + new Error('failed to request caip25 permission'), ); - expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ - [RestrictedMethods.eth_accounts]: { - foo: 'bar', - }, - }); - }); - - it('requests approval from the ApprovalController for only eth_accounts when only permittedChains is specified in params and origin is snapId', async () => { - const { handler, requestPermissionApprovalForOrigin } = - createMockedHandler(); - await handler( getBaseRequest({ - origin: 'npm:snap', - params: [ - { - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x64'], - }, - ], - }, - }, - ], - }), - ); - - expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ - [RestrictedMethods.eth_accounts]: {}, - }); - }); - - it('requests approval from the ApprovalController for only eth_accounts when both eth_accounts and permittedChains are specified in params and origin is snapId', async () => { - const { handler, requestPermissionApprovalForOrigin } = - createMockedHandler(); - - await handler( - getBaseRequest({ - origin: 'npm:snap', params: [ { [RestrictedMethods.eth_accounts]: { @@ -345,15 +198,13 @@ describe('requestPermissionsHandler', () => { }), ); - expect(requestPermissionApprovalForOrigin).toHaveBeenCalledWith({ - [RestrictedMethods.eth_accounts]: { - foo: 'bar', - }, - }); + expect(end).toHaveBeenCalledWith( + new Error('failed to request caip25 permission'), + ); }); - it('requests other permissions in params from the PermissionController, but ignores CAIP-25 if specified', async () => { - const { handler, requestPermissionsForOrigin } = createMockedHandler(); + it('returns an error by requesting other permissions in params from the PermissionController if specified', async () => { + const { handler, requestPermissionsForOrigin, end } = createMockedHandler(); await handler( getBaseRequest({ @@ -365,117 +216,18 @@ describe('requestPermissionsHandler', () => { ], }), ); - expect(requestPermissionsForOrigin).toHaveBeenCalledWith({ - otherPermission: {}, - }); - }); - - it('requests other permissions in params from the PermissionController, but ignores eth_accounts if specified', async () => { - const { handler, requestPermissionsForOrigin } = createMockedHandler(); - await handler( - getBaseRequest({ - params: [ - { - [RestrictedMethods.eth_accounts]: {}, - otherPermission: {}, - }, - ], - }), - ); - expect(requestPermissionsForOrigin).toHaveBeenCalledWith({ - otherPermission: {}, - }); - }); - - it('requests other permissions in params from the PermissionController, but ignores permittedChains if specified', async () => { - const { handler, requestPermissionsForOrigin } = createMockedHandler(); - - await handler( - getBaseRequest({ - params: [ - { - [PermissionNames.permittedChains]: {}, - otherPermission: {}, - }, - ], - }), - ); expect(requestPermissionsForOrigin).toHaveBeenCalledWith({ + [Caip25EndowmentPermissionName]: {}, otherPermission: {}, }); - }); - - it('does not request permissions from the PermissionController when only eth_accounts is provided in params', async () => { - const { handler, requestPermissionsForOrigin } = createMockedHandler(); - - await handler( - getBaseRequest({ - params: [ - { - [RestrictedMethods.eth_accounts]: {}, - }, - ], - }), - ); - expect(requestPermissionsForOrigin).not.toHaveBeenCalled(); - }); - - it('does not request permissions from the PermissionController when only permittedChains is provided in params', async () => { - const { handler, requestPermissionsForOrigin } = createMockedHandler(); - - await handler( - getBaseRequest({ - params: [ - { - [PermissionNames.permittedChains]: {}, - }, - ], - }), - ); - expect(requestPermissionsForOrigin).not.toHaveBeenCalled(); - }); - - it('does not request permissions from the PermissionController when both eth_accounts and permittedChains are provided in params', async () => { - const { handler, requestPermissionsForOrigin } = createMockedHandler(); - - await handler( - getBaseRequest({ - params: [ - { - [RestrictedMethods.eth_accounts]: {}, - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x64'], - }, - ], - }, - }, - ], - }), - ); - expect(requestPermissionsForOrigin).not.toHaveBeenCalled(); - }); - - it('requests empty permissions from the PermissionController when only CAIP-25 permission is provided in params', async () => { - const { handler, requestPermissionsForOrigin } = createMockedHandler(); - - await handler( - getBaseRequest({ - params: [ - { - [Caip25EndowmentPermissionName]: {}, - }, - ], - }), + expect(end).toHaveBeenCalledWith( + new Error('failed to request unexpected permission'), ); - expect(requestPermissionsForOrigin).toHaveBeenCalledWith({}); }); - it('requests empty permissions from the PermissionController when no permissions are provided in params', async () => { - const { handler, requestPermissionsForOrigin } = createMockedHandler(); + it('returns an error by requesting empty permissions in params from the PermissionController if no permissions specified', async () => { + const { handler, requestPermissionsForOrigin, end } = createMockedHandler(); await handler( getBaseRequest({ @@ -483,298 +235,95 @@ describe('requestPermissionsHandler', () => { }), ); expect(requestPermissionsForOrigin).toHaveBeenCalledWith({}); + expect(end).toHaveBeenCalledWith( + new Error('failed to request unexpected permission'), + ); }); - it('does not update or grant a CAIP-25 endowment permission if eth_accounts and permittedChains approvals were not requested', async () => { - const { handler, updateCaveat, grantPermissions, getPermissionsForOrigin } = + it('returns both eth_accounts and permittedChains permissions that were granted there are permitted chains', async () => { + const { handler, getAccounts, requestCaip25PermissionForOrigin, response } = createMockedHandler(); + getAccounts.mockReturnValue(['0xdeadbeef']); + requestCaip25PermissionForOrigin.mockResolvedValue({ + id: 'new', + parentCapability: Caip25EndowmentPermissionName, + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + accounts: ['0xdeadbeef'], + }, + 'eip155:5': { + accounts: ['0xdeadbeef'], + }, + }, + }, + }, + ], + }); - await handler( - getBaseRequest({ - params: [ + await handler(getBaseRequest()); + expect(response.result).toStrictEqual([ + { + caveats: [ { - otherPermission: {}, + type: CaveatTypes.restrictReturnedAccounts, + value: ['0xdeadbeef'], }, ], - }), - ); - expect(getPermissionsForOrigin).not.toHaveBeenCalled(); - expect(updateCaveat).not.toHaveBeenCalled(); - expect(grantPermissions).not.toHaveBeenCalled(); - }); - - it('returns the granted permissions if eth_accounts and permittedChains approvals were not requested', async () => { - const { handler, response } = createMockedHandler(); - - await handler( - getBaseRequest({ - params: [ + id: 'new', + parentCapability: RestrictedMethods.eth_accounts, + }, + { + caveats: [ { - otherPermission: {}, + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1', '0x5'], }, ], - }), - ); - expect(response.result).toStrictEqual([ - { - caveats: [{ value: { foo: 'bar' } }], - id: '2', - parentCapability: 'otherPermission', + id: 'new', + parentCapability: PermissionNames.permittedChains, }, ]); }); - it('does not update or grant a CAIP-25 endowment type permission if eth_accounts and permittedChains approvals were denied', async () => { - const { - handler, - updateCaveat, - grantPermissions, - getPermissionsForOrigin, - requestPermissionApprovalForOrigin, - } = createMockedHandler(); - requestPermissionApprovalForOrigin.mockRejectedValue( - new Error('user denied approval'), - ); - - try { - await handler( - getBaseRequest({ - params: [ - { - [RestrictedMethods.eth_accounts]: {}, - }, - ], - }), - ); - } catch (err) { - // noop - } - expect(getPermissionsForOrigin).not.toHaveBeenCalled(); - expect(updateCaveat).not.toHaveBeenCalled(); - expect(grantPermissions).not.toHaveBeenCalled(); - }); - - describe('eth_accounts and permittedChains approvals were accepted', () => { - it('sets the approved chainIds on an empty CAIP-25 caveat with isMultichainOrigin: false if origin is not snapId', async () => { - const { handler } = createMockedHandler(); - - await handler( - getBaseRequest({ - origin: 'http://test.com', - }), - ); - expect(MockMultichain.setPermittedEthChainIds).toHaveBeenCalledWith( - { - requiredScopes: {}, - optionalScopes: {}, - isMultichainOrigin: false, - }, - ['0x1', '0x5'], - ); - }); - - it('sets the approved accounts on the CAIP-25 caveat after the approved chainIds if origin is not snapId', async () => { - const { handler } = createMockedHandler(); - MockMultichain.setPermittedEthChainIds.mockReturnValue({ - requiredScopes: {}, - optionalScopes: {}, - sessionProperties: { caveatValueWithEthChainIdsSet: true }, - isMultichainOrigin: false, - }); - - await handler( - getBaseRequest({ - origin: 'http://test.com', - }), - ); - expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( - { - requiredScopes: {}, - optionalScopes: {}, - sessionProperties: { caveatValueWithEthChainIdsSet: true }, - isMultichainOrigin: false, - }, - ['0xdeadbeef'], - ); - }); - - it('does not set the approved chainIds on an empty CAIP-25 caveat if origin is snapId', async () => { - const { handler } = createMockedHandler(); - - await handler(getBaseRequest({ origin: 'npm:snap' })); - expect(MockMultichain.setPermittedEthChainIds).not.toHaveBeenCalled(); - }); - - it('sets the approved accounts for the `wallet:eip155` scope with isMultichainOrigin: false if origin is snapId', async () => { - const { handler } = createMockedHandler(); - - await handler(getBaseRequest({ origin: 'npm:snap' })); - expect(MockMultichain.setEthAccounts).toHaveBeenCalledWith( + it('returns only eth_accounts permission that was granted if there are no permitted chains', async () => { + const { handler, getAccounts, requestCaip25PermissionForOrigin, response } = + createMockedHandler(); + getAccounts.mockReturnValue(['0xdeadbeef']); + requestCaip25PermissionForOrigin.mockResolvedValue({ + id: 'new', + parentCapability: Caip25EndowmentPermissionName, + caveats: [ { - requiredScopes: {}, - optionalScopes: { - 'wallet:eip155': { - accounts: [], - }, - }, - isMultichainOrigin: false, - }, - ['0xdeadbeef'], - ); - }); - - it('gets permission for the origin', async () => { - const { handler, getPermissionsForOrigin } = createMockedHandler(); - - await handler(getBaseRequest()); - expect(getPermissionsForOrigin).toHaveBeenCalled(); - }); - - it('throws an error when a CAIP-25 already exists that was granted from the multichain flow (isMultichainOrigin: true)', async () => { - const { handler, getPermissionsForOrigin, end } = createMockedHandler(); - getPermissionsForOrigin.mockReturnValue({ - [Caip25EndowmentPermissionName]: { - id: '1', - parentCapability: Caip25EndowmentPermissionName, - caveats: [ - { - type: Caip25CaveatType, - value: { - requiredScopes: {}, - optionalScopes: {}, - isMultichainOrigin: true, + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'wallet:eip155': { + accounts: ['0xdeadbeef'], }, }, - ], - }, - }); - - await handler(getBaseRequest()); - expect(end).toHaveBeenCalledWith( - new Error( - 'Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.', - ), - ); - }); - - it('updates the caveat when a CAIP-25 already exists that was granted from the legacy flow (isMultichainOrigin: false)', async () => { - const { handler, updateCaveat } = createMockedHandler(); - MockMultichain.setEthAccounts.mockReturnValue({ - requiredScopes: {}, - optionalScopes: {}, - sessionProperties: { caveatValueWithEthAccountsSet: true }, - isMultichainOrigin: false, - }); - - await handler(getBaseRequest()); - expect(updateCaveat).toHaveBeenCalledWith( - Caip25EndowmentPermissionName, - Caip25CaveatType, - { - requiredScopes: {}, - optionalScopes: {}, - sessionProperties: { caveatValueWithEthAccountsSet: true }, - isMultichainOrigin: false, - }, - ); - }); - - it('grants a CAIP-25 permission if one does not already exist', async () => { - const { handler, getPermissionsForOrigin, grantPermissions } = - createMockedHandler(); - getPermissionsForOrigin.mockReturnValue({}); - MockMultichain.setEthAccounts.mockReturnValue({ - requiredScopes: {}, - optionalScopes: {}, - sessionProperties: { caveatValueWithEthAccountsSet: true }, - isMultichainOrigin: false, - }); - - await handler(getBaseRequest()); - expect(grantPermissions).toHaveBeenCalledWith({ - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: { - requiredScopes: {}, - optionalScopes: {}, - sessionProperties: { caveatValueWithEthAccountsSet: true }, - isMultichainOrigin: false, - }, - }, - ], - }, - }); - }); - - it('gets the ordered eth accounts', async () => { - const { handler, getAccounts } = createMockedHandler(); - - await handler(getBaseRequest()); - expect(getAccounts).toHaveBeenCalled(); - }); - - it('returns both eth_accounts and permittedChains permissions in addition to other permissions that were granted if origin is not snapId', async () => { - const { handler, getAccounts, response } = createMockedHandler(); - getAccounts.mockReturnValue(['0xdeadbeef']); - - await handler( - getBaseRequest({ - origin: 'http://test.com', - }), - ); - expect(response.result).toStrictEqual([ - { - caveats: [{ value: { foo: 'bar' } }], - id: '2', - parentCapability: 'otherPermission', - }, - { - caveats: [ - { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0xdeadbeef'], - }, - ], - id: '1', - parentCapability: RestrictedMethods.eth_accounts, - }, - { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x1', '0x5'], - }, - ], - id: '1', - parentCapability: PermissionNames.permittedChains, + }, }, - ]); + ], }); - it('returns only eth_accounts permissions in addition to other permissions that were granted if origin is snapId', async () => { - const { handler, getAccounts, response } = createMockedHandler(); - getAccounts.mockReturnValue(['0xdeadbeef']); - - await handler({ ...getBaseRequest(), origin: 'npm:snap' }); - expect(response.result).toStrictEqual([ - { - caveats: [{ value: { foo: 'bar' } }], - id: '2', - parentCapability: 'otherPermission', - }, - { - caveats: [ - { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0xdeadbeef'], - }, - ], - id: '1', - parentCapability: RestrictedMethods.eth_accounts, - }, - ]); - }); + await handler(getBaseRequest()); + expect(response.result).toStrictEqual([ + { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['0xdeadbeef'], + }, + ], + id: 'new', + parentCapability: RestrictedMethods.eth_accounts, + }, + ]); }); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index a301b1b5ca25..7fe30e32ec6b 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -14,15 +14,9 @@ import { Caip25CaveatType, Caip25CaveatValue, Caip25EndowmentPermissionName, - setEthAccounts, - setPermittedEthChainIds, + getPermittedEthChainIds, } from '@metamask/multichain'; -import { - Hex, - Json, - JsonRpcRequest, - PendingJsonRpcResponse, -} from '@metamask/utils'; +import { Json, JsonRpcRequest, PendingJsonRpcResponse } from '@metamask/utils'; import { AsyncJsonRpcEngineNextCallback, JsonRpcEngineEndCallback, @@ -32,19 +26,14 @@ import { RestrictedMethods, } from '../../../../../shared/constants/permissions'; import { PermissionNames } from '../../../controllers/permissions'; -// eslint-disable-next-line import/no-restricted-paths -import { isSnapId } from '../../../../../ui/helpers/utils/snaps'; export const requestPermissionsHandler = { methodNames: [MethodNames.RequestPermissions], implementation: requestPermissionsImplementation, hookNames: { - requestPermissionsForOrigin: true, - getPermissionsForOrigin: true, - updateCaveat: true, - grantPermissions: true, - requestPermissionApprovalForOrigin: true, getAccounts: true, + requestPermissionsForOrigin: true, + requestCaip25PermissionForOrigin: true, }, }; @@ -65,12 +54,9 @@ type GrantedPermissions = Awaited< * @param _next - JsonRpcEngine next() callback - unused * @param end - JsonRpcEngine end() callback * @param options - Method hooks passed to the method implementation - * @param options.requestPermissionsForOrigin - The specific method hook needed for this method implementation - * @param options.getPermissionsForOrigin - * @param options.updateCaveat - * @param options.grantPermissions - * @param options.requestPermissionApprovalForOrigin * @param options.getAccounts + * @param options.requestCaip25PermissionForOrigin + * @param options.requestPermissionsForOrigin * @returns A promise that resolves to nothing */ async function requestPermissionsImplementation( @@ -79,43 +65,31 @@ async function requestPermissionsImplementation( _next: AsyncJsonRpcEngineNextCallback, end: JsonRpcEngineEndCallback, { - requestPermissionsForOrigin, - getPermissionsForOrigin, - updateCaveat, - grantPermissions, - requestPermissionApprovalForOrigin, getAccounts, + requestPermissionsForOrigin, + requestCaip25PermissionForOrigin, }: { + getAccounts: () => string[]; requestPermissionsForOrigin: ( requestedPermissions: RequestedPermissions, ) => Promise<[GrantedPermissions]>; - updateCaveat: ( - permissionName: string, - caveatName: string, - caveatValue: Caip25CaveatValue, - ) => void; - grantPermissions: ( - requestedPermissions: RequestedPermissions, - ) => Record>>; - requestPermissionApprovalForOrigin: ( - requestedPermissions: RequestedPermissions, - ) => Promise<{ approvedAccounts: Hex[]; approvedChainIds: Hex[] }>; - - getPermissionsForOrigin: () => ReturnType< - AbstractPermissionController['getPermissions'] + requestCaip25PermissionForOrigin: ( + requestedPermissions?: RequestedPermissions, + ) => Promise< + ValidPermission< + typeof Caip25EndowmentPermissionName, + Caveat + > >; - getAccounts: () => string[]; }, ) { - const { origin, params } = req; + const { params } = req; if (!Array.isArray(params) || !isPlainObject(params[0])) { return end(invalidParams({ data: { request: req } })); } const [requestedPermissions] = params; - delete requestedPermissions[Caip25EndowmentPermissionName]; - const legacyRequestedPermissions: Partial< Pick > = pick(requestedPermissions, [ @@ -125,134 +99,63 @@ async function requestPermissionsImplementation( delete requestedPermissions[RestrictedMethods.eth_accounts]; delete requestedPermissions[PermissionNames.permittedChains]; - // We manually handle eth_accounts and permittedChains permissions - // by calling the ApprovalController rather than the PermissionController - // because these two permissions do not actually exist in the Permssion - // Specifications. Calling the PermissionController with them will - // cause an error to be thrown. Instead, we will use the approval result - // from the ApprovalController to form a CAIP-25 permission later. - let legacyApproval; - const haveLegacyPermissions = + const hasExpectedPermissions = Object.keys(requestedPermissions).length > 0; + const hasUnexpectedRequestedPermissions = Object.keys(legacyRequestedPermissions).length > 0; - if (haveLegacyPermissions) { - if (!legacyRequestedPermissions[RestrictedMethods.eth_accounts]) { - legacyRequestedPermissions[RestrictedMethods.eth_accounts] = {}; - } - if (!legacyRequestedPermissions[PermissionNames.permittedChains]) { - legacyRequestedPermissions[PermissionNames.permittedChains] = {}; + let caip25Endowment; + let caip25CaveatValue; + try { + if (hasExpectedPermissions || !hasUnexpectedRequestedPermissions) { + // This will throw. We are making this call purposely to get a proper error + await requestPermissionsForOrigin(requestedPermissions); } - if (isSnapId(origin)) { - delete legacyRequestedPermissions[PermissionNames.permittedChains]; - } - - legacyApproval = await requestPermissionApprovalForOrigin( + caip25Endowment = await requestCaip25PermissionForOrigin( legacyRequestedPermissions, ); - } - - let grantedPermissions: GrantedPermissions = {}; - // Request permissions from the PermissionController for any permissions other - // than eth_accounts and permittedChains in the params. If no permissions - // are in the params, then request empty permissions from the PermissionController - // to get an appropriate error to be returned to the dapp. - if ( - (Object.keys(requestedPermissions).length === 0 && - !haveLegacyPermissions) || - Object.keys(requestedPermissions).length > 0 - ) { - const [frozenGrantedPermissions] = await requestPermissionsForOrigin( - requestedPermissions, - ); - // permissions are frozen and must be cloned before modified - grantedPermissions = { ...frozenGrantedPermissions }; - } - - if (legacyApproval) { - let newCaveatValue = { - requiredScopes: {}, - optionalScopes: {}, - isMultichainOrigin: false, - }; - - if (isSnapId(origin)) { - newCaveatValue.optionalScopes = { - 'wallet:eip155': { - accounts: [], - }, - }; - } else { - newCaveatValue = setPermittedEthChainIds( - newCaveatValue, - legacyApproval.approvedChainIds, - ); - } - - newCaveatValue = setEthAccounts( - newCaveatValue, - legacyApproval.approvedAccounts, - ); - - const permissions = getPermissionsForOrigin() || {}; - let caip25Endowment = permissions[Caip25EndowmentPermissionName]; - const existingCaveatValue = caip25Endowment?.caveats?.find( + caip25CaveatValue = caip25Endowment?.caveats?.find( ({ type }) => type === Caip25CaveatType, )?.value as Caip25CaveatValue | undefined; - if (existingCaveatValue) { - if (existingCaveatValue.isMultichainOrigin) { - return end( - new Error( - 'Cannot modify permission granted via the Multichain API. Either modify the permission using the Multichain API or revoke permissions and request again.', - ), - ); - } - updateCaveat( - Caip25EndowmentPermissionName, - Caip25CaveatType, - newCaveatValue, + if (!caip25CaveatValue) { + throw new Error( + `could not find ${Caip25CaveatType} in granted ${Caip25EndowmentPermissionName} permission.`, ); - } else { - caip25Endowment = grantPermissions({ - [Caip25EndowmentPermissionName]: { - caveats: [ - { - type: Caip25CaveatType, - value: newCaveatValue, - }, - ], - }, - })[Caip25EndowmentPermissionName]; } + } catch (error) { + return end(error as unknown as Error); + } - // We cannot derive ethAccounts directly from the CAIP-25 permission - // because the accounts will not be in order of lastSelected - const ethAccounts = getAccounts(); - - grantedPermissions[RestrictedMethods.eth_accounts] = { + const grantedPermissions: GrantedPermissions = {}; + + // We cannot derive correct eth_accounts value directly from the CAIP-25 permission + // because the accounts will not be in order of lastSelected + const ethAccounts = getAccounts(); + + grantedPermissions[RestrictedMethods.eth_accounts] = { + ...caip25Endowment, + parentCapability: RestrictedMethods.eth_accounts, + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ethAccounts, + }, + ], + }; + + const ethChainIds = getPermittedEthChainIds(caip25CaveatValue); + if (ethChainIds.length > 0) { + grantedPermissions[PermissionNames.permittedChains] = { ...caip25Endowment, - parentCapability: RestrictedMethods.eth_accounts, + parentCapability: PermissionNames.permittedChains, caveats: [ { - type: CaveatTypes.restrictReturnedAccounts, - value: ethAccounts, + type: CaveatTypes.restrictNetworkSwitching, + value: ethChainIds, }, ], }; - - if (!isSnapId(origin)) { - grantedPermissions[PermissionNames.permittedChains] = { - ...caip25Endowment, - parentCapability: PermissionNames.permittedChains, - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: legacyApproval.approvedChainIds, - }, - ], - }; - } } res.result = Object.values(grantedPermissions).filter( diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 2afaa91a0976..4acbe9866e03 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -19,7 +19,7 @@ import { createEngineStream } from '@metamask/json-rpc-middleware-stream'; import { ObservableStore } from '@metamask/obs-store'; import { storeAsStream } from '@metamask/obs-store/dist/asStream'; import { providerAsMiddleware } from '@metamask/eth-json-rpc-middleware'; -import { debounce, throttle, memoize, wrap } from 'lodash'; +import { debounce, throttle, memoize, wrap, pick } from 'lodash'; import { KeyringController, keyringBuilderFactory, @@ -167,6 +167,9 @@ import { Caip25CaveatType, Caip25EndowmentPermissionName, getEthAccounts, + setPermittedEthChainIds, + setEthAccounts, + addPermittedEthChainId, } from '@metamask/multichain'; import { methodsRequiringNetworkSwitch, @@ -203,6 +206,7 @@ import { EndowmentPermissions, ExcludedSnapPermissions, ExcludedSnapEndowments, + CaveatTypes, } from '../../shared/constants/permissions'; import { UI_NOTIFICATIONS } from '../../shared/notifications'; import { MILLISECOND, MINUTE, SECOND } from '../../shared/constants/time'; @@ -5355,14 +5359,14 @@ export default class MetamaskController extends EventEmitter { } /** - * Requests approval for permissions for the specified origin + * Prompts the user with permittedChains approval for given chainId. * - * @param origin - The origin to request approval for. - * @param permissions - The permissions to request approval for. + * @param {string} origin - The origin to request approval for. + * @param {Hex} chainId - The chainId to add incrementally. */ - async requestPermissionApprovalForOrigin(origin, permissions) { + async requestApprovalPermittedChainsPermission(origin, chainId) { const id = nanoid(); - return this.approvalController.addAndShowApprovalRequest({ + await this.approvalController.addAndShowApprovalRequest({ id, origin, requestData: { @@ -5370,12 +5374,202 @@ export default class MetamaskController extends EventEmitter { id, origin, }, - permissions, + permissions: { + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: [chainId], + }, + ], + }, + }, }, type: MethodNames.RequestPermissions, }); } + /** + * Requests permittedChains permission for the specified origin + * and replaces any existing CAIP-25 permission with a new one. + * Allows for granting without prompting for user approval which + * would be used as part of flows like `wallet_addEthereumChain` + * requests where the addition of the network and the permitting + * of the chain are combined into one approval. + * + * @param {object} options - The options object + * @param {string} options.origin - The origin to request approval for. + * @param {Hex} options.chainId - The chainId to permit. + * @param {boolean} options.autoApprove - If the chain should be granted without prompting for user approval. + */ + async requestPermittedChainsPermission({ origin, chainId, autoApprove }) { + if (isSnapId(origin)) { + throw new Error( + `cannot request permittedChains permission for snaps with origin "${origin}"`, + ); + } + + if (!autoApprove) { + await this.requestApprovalPermittedChainsPermission(origin, chainId); + } + + let caveatValue = { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }; + caveatValue = addPermittedEthChainId(caveatValue, chainId); + + this.permissionController.grantPermissions({ + subject: { origin }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: caveatValue, + }, + ], + }, + }, + }); + } + + /** + * Requests incremental permittedChains permission for the specified origin. + * and updates the existing CAIP-25 permission. + * Allows for granting without prompting for user approval which + * would be used as part of flows like `wallet_addEthereumChain` + * requests where the addition of the network and the permitting + * of the chain are combined into one approval. + * + * @param {object} options - The options object + * @param {string} options.origin - The origin to request approval for. + * @param {Hex} options.chainId - The chainId to add to the existing permittedChains. + * @param {boolean} options.autoApprove - If the chain should be granted without prompting for user approval. + */ + async requestPermittedChainsPermissionIncremental({ + origin, + chainId, + autoApprove, + }) { + if (isSnapId(origin)) { + throw new Error( + `cannot request permittedChains permission for snaps with origin "${origin}"`, + ); + } + + const caip25Caveat = this.permissionController.getCaveat( + origin, + Caip25EndowmentPermissionName, + Caip25CaveatType, + ); + + if (caip25Caveat.value.isMultichainOrigin) { + throw providerErrors.unauthorized( + `Cannot switch to or add permissions for chainId '${chainId}' because permissions were granted over the Multichain API.`, + ); + } + + if (!autoApprove) { + await this.requestApprovalPermittedChainsPermission(origin, chainId); + } + + let updatedCaveatValue = addPermittedEthChainId( + caip25Caveat.value, + chainId, + ); + + const ethAccounts = getEthAccounts(caip25Caveat.value); + updatedCaveatValue = setEthAccounts(updatedCaveatValue, ethAccounts); + + this.permissionController.updateCaveat( + origin, + Caip25EndowmentPermissionName, + Caip25CaveatType, + updatedCaveatValue, + ); + } + + /** + * Requests CAIP-25 for permissions for the specified origin + * and replaces any existing CAIP-25 permission with a new one. + * + * @param {string} origin - The origin to request approval for. + * @param requestedPermissions - The legacy permissions to request approval for. + * @returns the granted CAIP-25 Permission. + */ + async requestCaip25Permission(origin, requestedPermissions = {}) { + const permissions = pick(requestedPermissions, [ + RestrictedMethods.eth_accounts, + PermissionNames.permittedChains, + ]); + + if (!permissions[RestrictedMethods.eth_accounts]) { + permissions[RestrictedMethods.eth_accounts] = {}; + } + + if (!permissions[PermissionNames.permittedChains]) { + permissions[PermissionNames.permittedChains] = {}; + } + + if (isSnapId(origin)) { + delete permissions[PermissionNames.permittedChains]; + } + + const id = nanoid(); + const legacyApproval = + await this.approvalController.addAndShowApprovalRequest({ + id, + origin, + requestData: { + metadata: { + id, + origin, + }, + permissions, + }, + type: MethodNames.RequestPermissions, + }); + + let caveatValue = { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }; + + if (isSnapId(origin)) { + caveatValue.optionalScopes = { + 'wallet:eip155': { + accounts: [], + }, + }; + } else { + caveatValue = setPermittedEthChainIds( + caveatValue, + legacyApproval.approvedChainIds, + ); + } + + caveatValue = setEthAccounts(caveatValue, legacyApproval.approvedAccounts); + + const grantedPermissions = this.permissionController.grantPermissions({ + subject: { origin }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: caveatValue, + }, + ], + }, + }, + }); + + return grantedPermissions[Caip25EndowmentPermissionName]; + } + // --------------------------------------------------------------------------- // Identity Management (signature operations) @@ -6151,25 +6345,28 @@ export default class MetamaskController extends EventEmitter { ), // Permission-related getAccounts: this.getPermittedAccounts.bind(this, origin), + requestCaip25PermissionForOrigin: this.requestCaip25Permission.bind( + this, + origin, + ), getPermissionsForOrigin: this.permissionController.getPermissions.bind( this.permissionController, origin, ), - requestPermissionApprovalForOrigin: - this.requestPermissionApprovalForOrigin.bind(this, origin), + requestPermittedChainsPermissionForOrigin: (options) => + this.requestPermittedChainsPermission({ + ...options, + origin, + }), + requestPermittedChainsPermissionIncrementalForOrigin: (options) => + this.requestPermittedChainsPermissionIncremental({ + ...options, + origin, + }), requestPermissionsForOrigin: (requestedPermissions) => this.permissionController.requestPermissions( { origin }, - { - ...(requestedPermissions[PermissionNames.eth_accounts] && - !isSnapId(origin) && { - [PermissionNames.permittedChains]: {}, - }), - ...(requestedPermissions[PermissionNames.permittedChains] && { - [PermissionNames.eth_accounts]: {}, - }), - ...requestedPermissions, - }, + requestedPermissions, ), revokePermissionsForOrigin: (permissionKeys) => { try { @@ -6248,13 +6445,6 @@ export default class MetamaskController extends EventEmitter { this.alertController.setWeb3ShimUsageRecorded.bind( this.alertController, ), - - grantPermissions: (approvedPermissions) => { - return this.permissionController.grantPermissions({ - subject: { origin }, - approvedPermissions, - }); - }, updateCaveat: this.permissionController.updateCaveat.bind( this.permissionController, origin, diff --git a/app/scripts/metamask-controller.test.js b/app/scripts/metamask-controller.test.js index 9acd2a2acb2d..5255622956c3 100644 --- a/app/scripts/metamask-controller.test.js +++ b/app/scripts/metamask-controller.test.js @@ -49,6 +49,10 @@ import { createMockInternalAccount } from '../../test/jest/mocks'; import { mockNetworkState } from '../../test/stub/networks'; import { ENVIRONMENT } from '../../development/build/constants'; import { SECOND } from '../../shared/constants/time'; +import { + CaveatTypes, + RestrictedMethods, +} from '../../shared/constants/permissions'; import { BalancesController as MultichainBalancesController, BTC_BALANCES_UPDATE_TIME as MULTICHAIN_BALANCES_UPDATE_TIME, @@ -59,6 +63,7 @@ import { METAMASK_COOKIE_HANDLER } from './constants/stream'; import MetaMaskController, { ONE_KEY_VIA_TREZOR_MINOR_VERSION, } from './metamask-controller'; +import { PermissionNames } from './controllers/permissions'; const { Ganache } = require('../../test/e2e/seeder/ganache'); @@ -948,21 +953,134 @@ describe('MetaMaskController', () => { }); }); - describe('#requestPermissionApprovalForOrigin', () => { - it('requests permissions for the origin from the ApprovalController', async () => { + describe('#requestCaip25Permission', () => { + it('requests approval with well formed id and origin', async () => { jest .spyOn( metamaskController.approvalController, 'addAndShowApprovalRequest', ) - .mockResolvedValue(); + .mockResolvedValue({ + approvedAccounts: [], + approvedChainIds: [], + }); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue({ + [Caip25EndowmentPermissionName]: { + foo: 'bar', + }, + }); - await metamaskController.requestPermissionApprovalForOrigin( - 'test.com', - { - eth_accounts: {}, + await metamaskController.requestCaip25Permission('test.com', {}); + + expect( + metamaskController.approvalController.addAndShowApprovalRequest, + ).toHaveBeenCalledWith( + expect.objectContaining({ + id: expect.stringMatching(/.{21}/u), + origin: 'test.com', + requestData: expect.objectContaining({ + metadata: { + id: expect.stringMatching(/.{21}/u), + origin: 'test.com', + }, + }), + type: 'wallet_requestPermissions', + }), + ); + + const [params] = + metamaskController.approvalController.addAndShowApprovalRequest.mock + .calls[0]; + expect(params.id).toStrictEqual(params.requestData.metadata.id); + }); + + it('requests approval from the ApprovalController for eth_accounts and permittedChains when only eth_accounts is specified in params and origin is not snapId', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockResolvedValue({ + approvedAccounts: [], + approvedChainIds: [], + }); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue({ + [Caip25EndowmentPermissionName]: { + foo: 'bar', + }, + }); + + await metamaskController.requestCaip25Permission('test.com', { + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['foo'], + }, + ], }, + }); + + expect( + metamaskController.approvalController.addAndShowApprovalRequest, + ).toHaveBeenCalledWith( + expect.objectContaining({ + id: expect.stringMatching(/.{21}/u), + origin: 'test.com', + requestData: { + metadata: { + id: expect.stringMatching(/.{21}/u), + origin: 'test.com', + }, + permissions: { + [RestrictedMethods.eth_accounts]: { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['foo'], + }, + ], + }, + [PermissionNames.permittedChains]: {}, + }, + }, + type: 'wallet_requestPermissions', + }), ); + }); + + it('requests approval from the ApprovalController for eth_accounts and permittedChains when only permittedChains is specified in params and origin is not snapId', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockResolvedValue({ + approvedAccounts: [], + approvedChainIds: [], + }); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue({ + [Caip25EndowmentPermissionName]: { + foo: 'bar', + }, + }); + + await metamaskController.requestCaip25Permission('test.com', { + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }); expect( metamaskController.approvalController.addAndShowApprovalRequest, @@ -976,12 +1094,434 @@ describe('MetaMaskController', () => { origin: 'test.com', }, permissions: { - eth_accounts: {}, + [RestrictedMethods.eth_accounts]: {}, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, }, }, type: 'wallet_requestPermissions', }), ); + }); + + it('requests approval from the ApprovalController for eth_accounts and permittedChains when both are specified in params and origin is not snapId', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockResolvedValue({ + approvedAccounts: [], + approvedChainIds: [], + }); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue({ + [Caip25EndowmentPermissionName]: { + foo: 'bar', + }, + }); + + await metamaskController.requestCaip25Permission('test.com', { + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['foo'], + }, + ], + }, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }); + + expect( + metamaskController.approvalController.addAndShowApprovalRequest, + ).toHaveBeenCalledWith( + expect.objectContaining({ + id: expect.stringMatching(/.{21}/u), + origin: 'test.com', + requestData: { + metadata: { + id: expect.stringMatching(/.{21}/u), + origin: 'test.com', + }, + permissions: { + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['foo'], + }, + ], + }, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }, + }, + type: 'wallet_requestPermissions', + }), + ); + }); + + it('requests approval from the ApprovalController for only eth_accounts when only eth_accounts is specified in params and origin is snapId', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockResolvedValue({ + approvedAccounts: [], + approvedChainIds: [], + }); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue({ + [Caip25EndowmentPermissionName]: { + foo: 'bar', + }, + }); + + await metamaskController.requestCaip25Permission('npm:snap', { + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['foo'], + }, + ], + }, + }); + + expect( + metamaskController.approvalController.addAndShowApprovalRequest, + ).toHaveBeenCalledWith( + expect.objectContaining({ + id: expect.stringMatching(/.{21}/u), + origin: 'npm:snap', + requestData: { + metadata: { + id: expect.stringMatching(/.{21}/u), + origin: 'npm:snap', + }, + permissions: { + [RestrictedMethods.eth_accounts]: { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['foo'], + }, + ], + }, + }, + }, + type: 'wallet_requestPermissions', + }), + ); + }); + + it('requests approval from the ApprovalController for only eth_accounts when only permittedChains is specified in params and origin is snapId', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockResolvedValue({ + approvedAccounts: [], + approvedChainIds: [], + }); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue({ + [Caip25EndowmentPermissionName]: { + foo: 'bar', + }, + }); + + await metamaskController.requestCaip25Permission('npm:snap', { + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }); + + expect( + metamaskController.approvalController.addAndShowApprovalRequest, + ).toHaveBeenCalledWith( + expect.objectContaining({ + id: expect.stringMatching(/.{21}/u), + origin: 'npm:snap', + requestData: { + metadata: { + id: expect.stringMatching(/.{21}/u), + origin: 'npm:snap', + }, + permissions: { + [PermissionNames.eth_accounts]: {}, + }, + }, + type: 'wallet_requestPermissions', + }), + ); + }); + + it('requests approval from the ApprovalController for only eth_accounts when both eth_accounts and permittedChains are specified in params and origin is snapId', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockResolvedValue({ + approvedAccounts: [], + approvedChainIds: [], + }); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue({ + [Caip25EndowmentPermissionName]: { + foo: 'bar', + }, + }); + + await metamaskController.requestCaip25Permission('npm:snap', { + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['foo'], + }, + ], + }, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }); + + expect( + metamaskController.approvalController.addAndShowApprovalRequest, + ).toHaveBeenCalledWith( + expect.objectContaining({ + id: expect.stringMatching(/.{21}/u), + origin: 'npm:snap', + requestData: { + metadata: { + id: expect.stringMatching(/.{21}/u), + origin: 'npm:snap', + }, + permissions: { + [PermissionNames.eth_accounts]: { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['foo'], + }, + ], + }, + }, + }, + type: 'wallet_requestPermissions', + }), + ); + }); + + it('throws an error if the eth_accounts and permittedChains approval is rejected', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockRejectedValue(new Error('approval rejected')); + + await expect(() => + metamaskController.requestCaip25Permission('test.com', { + eth_accounts: {}, + }), + ).rejects.toThrow(new Error('approval rejected')); + }); + + it('grants the CAIP-25 permission with eth accounts, chainIds, and isMultichainOrigin: false if origin is not snapId', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockResolvedValue({ + approvedChainIds: ['0x1', '0x5'], + approvedAccounts: ['0xdeadbeef'], + }); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue({ + [Caip25EndowmentPermissionName]: { + foo: 'bar', + }, + }); + + await metamaskController.requestCaip25Permission('test.com', {}); + + expect( + metamaskController.permissionController.grantPermissions, + ).toHaveBeenCalledWith({ + subject: { origin: 'test.com' }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + accounts: ['eip155:1:0xdeadbeef'], + }, + 'eip155:5': { + accounts: ['eip155:5:0xdeadbeef'], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + }, + }, + }); + }); + + it('grants the CAIP-25 permission approved accounts for the `wallet:eip155` scope (and no approved chainIds) with isMultichainOrigin: false if origin is snapId', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockResolvedValue({ + approvedChainIds: ['0x1', '0x5'], + approvedAccounts: ['0xdeadbeef'], + }); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue({ + [Caip25EndowmentPermissionName]: { + foo: 'bar', + }, + }); + + await metamaskController.requestCaip25Permission('npm:snap', {}); + + expect( + metamaskController.permissionController.grantPermissions, + ).toHaveBeenCalledWith({ + subject: { origin: 'npm:snap' }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'wallet:eip155': { + accounts: ['wallet:eip155:0xdeadbeef'], + }, + }, + isMultichainOrigin: false, + }, + }, + ], + }, + }, + }); + }); + + it('returns the result from the ApprovalController', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockResolvedValue({ + approvedChainIds: ['0x1', '0x5'], + approvedAccounts: ['0xdeadbeef'], + }); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue({ + [Caip25EndowmentPermissionName]: { + foo: 'bar', + }, + }); + + expect( + await metamaskController.requestCaip25Permission('test.com', {}), + ).toStrictEqual({ foo: 'bar' }); + }); + }); + + describe('requestApprovalPermittedChainsPermission', () => { + it('requests approval with well formed id and origin', async () => { + jest + .spyOn( + metamaskController.approvalController, + 'addAndShowApprovalRequest', + ) + .mockResolvedValue(); + + await metamaskController.requestApprovalPermittedChainsPermission( + 'test.com', + '0x1', + ); + + expect( + metamaskController.approvalController.addAndShowApprovalRequest, + ).toHaveBeenCalledWith( + expect.objectContaining({ + id: expect.stringMatching(/.{21}/u), + origin: 'test.com', + requestData: expect.objectContaining({ + metadata: { + id: expect.stringMatching(/.{21}/u), + origin: 'test.com', + }, + permissions: { + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1'], + }, + ], + }, + }, + }), + type: 'wallet_requestPermissions', + }), + ); const [params] = metamaskController.approvalController.addAndShowApprovalRequest.mock @@ -989,23 +1529,367 @@ describe('MetaMaskController', () => { expect(params.id).toStrictEqual(params.requestData.metadata.id); }); - it('returns the result from the ApprovalController', async () => { + it('throws if the approval is rejected', async () => { jest .spyOn( metamaskController.approvalController, 'addAndShowApprovalRequest', ) - .mockResolvedValue('approvalResult'); + .mockRejectedValue(new Error('approval rejected')); - const result = - await metamaskController.requestPermissionApprovalForOrigin( + await expect(() => + metamaskController.requestApprovalPermittedChainsPermission( 'test.com', - { - eth_accounts: {}, + '0x1', + ), + ).rejects.toThrow(new Error('approval rejected')); + }); + }); + + describe('requestPermittedChainsPermission', () => { + it('throws if the origin is snapId', async () => { + await expect(() => + metamaskController.requestPermittedChainsPermission({ + origin: 'npm:snap', + chainId: '0x1', + }), + ).rejects.toThrow( + new Error( + 'cannot request permittedChains permission for snaps with origin "npm:snap"', + ), + ); + }); + + it('requests approval for permittedChains permissions from the ApprovalController if autoApprove: false', async () => { + jest + .spyOn(metamaskController, 'requestApprovalPermittedChainsPermission') + .mockResolvedValue(); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue(); + + await metamaskController.requestPermittedChainsPermission({ + origin: 'test.com', + chainId: '0x1', + autoApprove: false, + }); + + expect( + metamaskController.requestApprovalPermittedChainsPermission, + ).toHaveBeenCalledWith('test.com', '0x1'); + }); + + it('throws if permittedChains approval is rejected', async () => { + jest + .spyOn(metamaskController, 'requestApprovalPermittedChainsPermission') + .mockRejectedValue(new Error('approval rejected')); + + await expect(() => + metamaskController.requestPermittedChainsPermission({ + origin: 'test.com', + chainId: '0x1', + autoApprove: false, + }), + ).rejects.toThrow(new Error('approval rejected')); + }); + + it('does not request approval for permittedChains permissions from the ApprovalController if autoApprove: true', async () => { + jest + .spyOn(metamaskController, 'requestApprovalPermittedChainsPermission') + .mockResolvedValue(); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue(); + + await metamaskController.requestPermittedChainsPermission({ + origin: 'test.com', + chainId: '0x1', + autoApprove: true, + }); + + expect( + metamaskController.requestApprovalPermittedChainsPermission, + ).not.toHaveBeenCalled(); + }); + + it('grants the CAIP-25 permission', async () => { + jest + .spyOn(metamaskController, 'requestApprovalPermittedChainsPermission') + .mockResolvedValue(); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockReturnValue(); + + await metamaskController.requestPermittedChainsPermission({ + origin: 'test.com', + chainId: '0x1', + }); + + expect( + metamaskController.permissionController.grantPermissions, + ).toHaveBeenCalledWith({ + subject: { origin: 'test.com' }, + approvedPermissions: { + [Caip25EndowmentPermissionName]: { + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + accounts: [], + }, + }, + isMultichainOrigin: false, + }, + }, + ], }, - ); + }, + }); + }); - expect(result).toStrictEqual('approvalResult'); + it('throws if CAIP-25 permission grant fails', async () => { + jest + .spyOn(metamaskController, 'requestApprovalPermittedChainsPermission') + .mockResolvedValue(); + jest + .spyOn(metamaskController.permissionController, 'grantPermissions') + .mockImplementation(() => { + throw new Error('grant failed'); + }); + + await expect(() => + metamaskController.requestPermittedChainsPermission({ + origin: 'test.com', + chainId: '0x1', + }), + ).rejects.toThrow(new Error('grant failed')); + }); + }); + + describe('requestPermittedChainsPermissionIncremental', () => { + it('throws if the origin is snapId', async () => { + await expect(() => + metamaskController.requestPermittedChainsPermissionIncremental({ + origin: 'npm:snap', + chainId: '0x1', + }), + ).rejects.toThrow( + new Error( + 'cannot request permittedChains permission for snaps with origin "npm:snap"', + ), + ); + }); + + it('gets the CAIP-25 caveat', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + }); + jest + .spyOn(metamaskController, 'requestApprovalPermittedChainsPermission') + .mockResolvedValue(); + jest + .spyOn(metamaskController.permissionController, 'updateCaveat') + .mockReturnValue(); + + await metamaskController.requestPermittedChainsPermissionIncremental({ + origin: 'test.com', + chainId: '0x1', + }); + + expect( + metamaskController.permissionController.getCaveat, + ).toHaveBeenCalledWith( + 'test.com', + Caip25EndowmentPermissionName, + Caip25CaveatType, + ); + }); + + it('throws if getting the caveat fails', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + }); + jest + .spyOn(metamaskController, 'requestApprovalPermittedChainsPermission') + .mockImplementation(() => { + throw new Error('no caveat found'); + }); + + await expect(() => + metamaskController.requestPermittedChainsPermissionIncremental({ + origin: 'test.com', + chainId: '0x1', + autoApprove: false, + }), + ).rejects.toThrow(new Error('no caveat found')); + }); + + it('requests permittedChains approval if autoApprove: false', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + }); + jest + .spyOn(metamaskController, 'requestApprovalPermittedChainsPermission') + .mockResolvedValue(); + jest + .spyOn(metamaskController.permissionController, 'updateCaveat') + .mockReturnValue(); + + await metamaskController.requestPermittedChainsPermissionIncremental({ + origin: 'test.com', + chainId: '0x1', + autoApprove: false, + }); + + expect( + metamaskController.requestApprovalPermittedChainsPermission, + ).toHaveBeenCalledWith('test.com', '0x1'); + }); + + it('throws if permittedChains approval is rejected', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + }); + jest + .spyOn(metamaskController, 'requestApprovalPermittedChainsPermission') + .mockRejectedValue(new Error('approval rejected')); + + await expect(() => + metamaskController.requestPermittedChainsPermissionIncremental({ + origin: 'test.com', + chainId: '0x1', + autoApprove: false, + }), + ).rejects.toThrow(new Error('approval rejected')); + }); + + it('does not request permittedChains approval if autoApprove: true', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + }); + jest + .spyOn(metamaskController, 'requestApprovalPermittedChainsPermission') + .mockResolvedValue(); + jest + .spyOn(metamaskController.permissionController, 'updateCaveat') + .mockReturnValue(); + + await metamaskController.requestPermittedChainsPermissionIncremental({ + origin: 'test.com', + chainId: '0x1', + autoApprove: true, + }); + + expect( + metamaskController.requestApprovalPermittedChainsPermission, + ).not.toHaveBeenCalled(); + }); + + it('updates the CAIP-25 permission', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:5': { + accounts: ['eip155:5:0xdeadbeef'], + }, + }, + isMultichainOrigin: false, + }, + }); + jest + .spyOn(metamaskController, 'requestApprovalPermittedChainsPermission') + .mockResolvedValue(); + jest + .spyOn(metamaskController.permissionController, 'updateCaveat') + .mockReturnValue(); + + await metamaskController.requestPermittedChainsPermissionIncremental({ + origin: 'test.com', + chainId: '0x1', + }); + + expect( + metamaskController.permissionController.updateCaveat, + ).toHaveBeenCalledWith( + 'test.com', + Caip25EndowmentPermissionName, + Caip25CaveatType, + { + requiredScopes: {}, + optionalScopes: { + 'eip155:5': { + accounts: ['eip155:5:0xdeadbeef'], + }, + 'eip155:1': { + accounts: ['eip155:1:0xdeadbeef'], + }, + }, + isMultichainOrigin: false, + }, + ); + }); + + it('throws if CAIP-25 permission update fails', async () => { + jest + .spyOn(metamaskController.permissionController, 'getCaveat') + .mockReturnValue({ + value: { + requiredScopes: {}, + optionalScopes: {}, + isMultichainOrigin: false, + }, + }); + jest + .spyOn(metamaskController, 'requestApprovalPermittedChainsPermission') + .mockResolvedValue(); + jest + .spyOn(metamaskController.permissionController, 'updateCaveat') + .mockImplementation(() => { + throw new Error('grant failed'); + }); + + await expect(() => + metamaskController.requestPermittedChainsPermissionIncremental({ + origin: 'test.com', + chainId: '0x1', + }), + ).rejects.toThrow(new Error('grant failed')); }); }); From 9dd6ee256bca16c5a97fe311cbb70d3b5ef34637 Mon Sep 17 00:00:00 2001 From: jiexi Date: Tue, 17 Dec 2024 08:27:48 -0800 Subject: [PATCH 136/148] Update app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js Co-authored-by: Elliot Winkler --- app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js index 99f4f8d01d8b..9be36a3dbced 100644 --- a/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js +++ b/app/scripts/lib/rpc-method-middleware/createMethodMiddleware.js @@ -23,7 +23,7 @@ export const createEip1193MethodMiddleware = makeMethodMiddlewareMaker([ ]); // A collection of RPC method implementations that, for legacy reasons, MAY precede -// our permissioning logic on the in the EIP-1193 JSON-RPC middleware pipeline. +// our permissioning logic in the EIP-1193 JSON-RPC middleware pipeline. export const createEthAccountsMethodMiddleware = makeMethodMiddlewareMaker([ ethAccountsHandler, ]); From 9215ba41645e1db7a3e9a78cf81f2c129828476f Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 17 Dec 2024 08:29:50 -0800 Subject: [PATCH 137/148] move comment block to right before requestEthereumAccountsHandler --- .../handlers/request-accounts.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts index aac97378e24c..a7a9a34deddb 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts @@ -27,14 +27,6 @@ import { } from '../../../../../shared/constants/metametrics'; import { shouldEmitDappViewedEvent } from '../../util'; -/** - * This method attempts to retrieve the Ethereum accounts available to the - * requester, or initiate a request for account access if none are currently - * available. It is essentially a wrapper of wallet_requestPermissions that - * only errors if the user rejects the request. We maintain the method for - * backwards compatibility reasons. - */ - const requestEthereumAccounts = { methodNames: [MESSAGE_TYPE.ETH_REQUEST_ACCOUNTS], implementation: requestEthereumAccountsHandler, @@ -51,6 +43,13 @@ export default requestEthereumAccounts; // Used to rate-limit pending requests to one per origin const locks = new Set(); +/** + * This method attempts to retrieve the Ethereum accounts available to the + * requester, or initiate a request for account access if none are currently + * available. It is essentially a wrapper of wallet_requestPermissions that + * only errors if the user rejects the request. We maintain the method for + * backwards compatibility reasons. + */ async function requestEthereumAccountsHandler( req: JsonRpcRequest & { origin: string }, res: PendingJsonRpcResponse, From d28618b350fd199e829ab6a3cc146f661d0f2f75 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 17 Dec 2024 08:48:45 -0800 Subject: [PATCH 138/148] add jsdoc to requestEthereumAccountsHandler --- .../handlers/request-accounts.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts index a7a9a34deddb..5efded2fe007 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/request-accounts.ts @@ -49,6 +49,21 @@ const locks = new Set(); * available. It is essentially a wrapper of wallet_requestPermissions that * only errors if the user rejects the request. We maintain the method for * backwards compatibility reasons. + * + * @param req - The JsonRpcEngine request + * @param res - The JsonRpcEngine result object + * @param _next - JsonRpcEngine next() callback - unused + * @param end - JsonRpcEngine end() callback + * @param options - Method hooks passed to the method implementation + * @param options.getAccounts - A hook that returns the permitted eth accounts for the origin sorted by lastSelected. + * @param options.getUnlockPromise - A hook that resolves when the wallet is unlocked. + * @param options.sendMetrics - A hook that helps track metric events. + * @param options.metamaskState - The MetaMask app state. + * @param options.requestCaip25PermissionForOrigin - A hook that requests the CAIP-25 permission for the origin. + * @param options.metamaskState.metaMetricsId + * @param options.metamaskState.permissionHistory + * @param options.metamaskState.accounts + * @returns A promise that resolves to nothing */ async function requestEthereumAccountsHandler( req: JsonRpcRequest & { origin: string }, From 0c97ce9f5b6b10a786c822f242f2a4587a78838d Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Tue, 17 Dec 2024 11:37:12 -0800 Subject: [PATCH 139/148] fix testDapp.connectAccount usage --- test/e2e/flask/user-operations.spec.ts | 2 +- .../account/snap-account-signatures-and-disconnects.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/flask/user-operations.spec.ts b/test/e2e/flask/user-operations.spec.ts index 582abd746e29..4e8b51043583 100644 --- a/test/e2e/flask/user-operations.spec.ts +++ b/test/e2e/flask/user-operations.spec.ts @@ -240,7 +240,7 @@ async function withAccountSnap( const testDapp = new TestDapp(driver); await testDapp.openTestDappPage(); - await testDapp.connectAccount(ERC_4337_ACCOUNT); + await testDapp.connectAccount({ publicAddress: ERC_4337_ACCOUNT }); await driver.switchToWindowWithTitle( WINDOW_TITLES.ExtensionInFullScreenView, diff --git a/test/e2e/tests/account/snap-account-signatures-and-disconnects.spec.ts b/test/e2e/tests/account/snap-account-signatures-and-disconnects.spec.ts index 57421338079e..7a97130a3ef5 100644 --- a/test/e2e/tests/account/snap-account-signatures-and-disconnects.spec.ts +++ b/test/e2e/tests/account/snap-account-signatures-and-disconnects.spec.ts @@ -48,7 +48,7 @@ describe('Snap Account Signatures and Disconnects @no-mmi', function (this: Suit // Open the Test Dapp and connect const testDapp = new TestDapp(driver); await testDapp.openTestDappPage(); - await testDapp.connectAccount(newPublicKey); + await testDapp.connectAccount({ publicAddress: newPublicKey }); // SignedTypedDataV3 with Test Dapp await signTypedDataV3WithSnapAccount(driver, newPublicKey, false, true); From 60d02c9129ccf740db39a5477363b20604bbe3d7 Mon Sep 17 00:00:00 2001 From: MetaMask Bot Date: Wed, 18 Dec 2024 16:06:44 +0000 Subject: [PATCH 140/148] Update LavaMoat policies --- lavamoat/browserify/beta/policy.json | 10 ++++++++++ lavamoat/browserify/flask/policy.json | 10 ++++++++++ lavamoat/browserify/main/policy.json | 10 ++++++++++ lavamoat/browserify/mmi/policy.json | 2 +- 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lavamoat/browserify/beta/policy.json b/lavamoat/browserify/beta/policy.json index 82075f709022..ebc0a2930bd0 100644 --- a/lavamoat/browserify/beta/policy.json +++ b/lavamoat/browserify/beta/policy.json @@ -1371,6 +1371,16 @@ "uuid": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/controller-utils": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true diff --git a/lavamoat/browserify/flask/policy.json b/lavamoat/browserify/flask/policy.json index 82075f709022..ebc0a2930bd0 100644 --- a/lavamoat/browserify/flask/policy.json +++ b/lavamoat/browserify/flask/policy.json @@ -1371,6 +1371,16 @@ "uuid": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/controller-utils": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true diff --git a/lavamoat/browserify/main/policy.json b/lavamoat/browserify/main/policy.json index 82075f709022..ebc0a2930bd0 100644 --- a/lavamoat/browserify/main/policy.json +++ b/lavamoat/browserify/main/policy.json @@ -1371,6 +1371,16 @@ "uuid": true } }, + "@metamask/multichain": { + "packages": { + "@metamask/multichain>@metamask/api-specs": true, + "@metamask/controller-utils": true, + "@metamask/permission-controller": true, + "@metamask/rpc-errors": true, + "@metamask/utils": true, + "lodash": true + } + }, "@metamask/name-controller": { "globals": { "fetch": true diff --git a/lavamoat/browserify/mmi/policy.json b/lavamoat/browserify/mmi/policy.json index d96a00774bfe..91c85d6e1645 100644 --- a/lavamoat/browserify/mmi/policy.json +++ b/lavamoat/browserify/mmi/policy.json @@ -1465,8 +1465,8 @@ }, "@metamask/multichain": { "packages": { - "@metamask/controller-utils": true, "@metamask/multichain>@metamask/api-specs": true, + "@metamask/controller-utils": true, "@metamask/permission-controller": true, "@metamask/rpc-errors": true, "@metamask/utils": true, From 2657bfefee336b73b75f0e1989e13b52ab75159d Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Wed, 18 Dec 2024 09:47:52 -0800 Subject: [PATCH 141/148] fix validateChainId guard --- .../rpc-method-middleware/handlers/ethereum-chain-utils.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js index b9fae856da98..d4b62576df63 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js +++ b/app/scripts/lib/rpc-method-middleware/handlers/ethereum-chain-utils.js @@ -14,10 +14,7 @@ import { getValidUrl } from '../../util'; export function validateChainId(chainId) { const lowercasedChainId = typeof chainId === 'string' ? chainId.toLowerCase() : null; - if ( - lowercasedChainId !== null && - !isPrefixedFormattedHexString(lowercasedChainId) - ) { + if (!isPrefixedFormattedHexString(lowercasedChainId)) { throw rpcErrors.invalidParams({ message: `Expected 0x-prefixed, unpadded, non-zero hexadecimal string 'chainId'. Received:\n${chainId}`, }); From 2c40696d550ff899c40066a507a143c91ff75a35 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 19 Dec 2024 12:43:36 -0800 Subject: [PATCH 142/148] convert getDescriptionNode params to object params --- .../permissions-connect-permission-list.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js b/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js index 39f9bb015b98..e23e23ee789e 100644 --- a/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js +++ b/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js @@ -11,13 +11,14 @@ import { Box } from '../../component-library'; /** * Get one or more permission descriptions for a permission name. * - * @param permission - The permission to render. - * @param index - The index of the permission. - * @param accounts - An array representing list of accounts for which permission is used. - * @param requestedChainIds - An array representing list of chain ids for which permission is used. + * @param options - The options object. + * @param options.permission - The permission to render. + * @param options.index - The index of the permission. + * @param options.accounts - An array representing list of accounts for which permission is used. + * @param options.requestedChainIds - An array representing list of chain ids for which permission is used. * @returns {JSX.Element} A permission description node. */ -function getDescriptionNode(permission, index, accounts, requestedChainIds) { +function getDescriptionNode({permission, index, accounts, requestedChainIds}) { return ( { - return getDescriptionNode( + return getDescriptionNode({ permission, index, accounts, requestedChainIds, - ); + }); })} ); From a8f4b55fb3a3fb84d14ce10d543e527f21b953ee Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 19 Dec 2024 15:06:00 -0800 Subject: [PATCH 143/148] Update wallet_requestPermissions to handle other permissions again --- .../wallet-requestPermissions.test.ts | 707 +++++++++++++----- .../handlers/wallet-requestPermissions.ts | 114 +-- .../permissions-connect-permission-list.js | 7 +- 3 files changed, 579 insertions(+), 249 deletions(-) diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts index 51ea8c744afb..622cf33b38f3 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.test.ts @@ -31,9 +31,7 @@ const getBaseRequest = (overrides = {}) => ({ const createMockedHandler = () => { const next = jest.fn(); const end = jest.fn(); - const requestPermissionsForOrigin = jest - .fn() - .mockRejectedValue(new Error('failed to request unexpected permission')); + const requestPermissionsForOrigin = jest.fn().mockResolvedValue({}); const getAccounts = jest.fn().mockReturnValue([]); const requestCaip25PermissionForOrigin = jest.fn().mockResolvedValue({}); const response: PendingJsonRpcResponse = { @@ -79,251 +77,562 @@ describe('requestPermissionsHandler', () => { ); }); - it('requests the CAIP-25 permission using eth_accounts when only eth_accounts is specified in params', async () => { - const { handler, requestCaip25PermissionForOrigin } = createMockedHandler(); + describe('only other permissions (non CAIP-25 equivalent) requested', () => { + it('it treats "endowment:caip25" as an other permission', async () => { + const { + handler, + requestPermissionsForOrigin, + requestCaip25PermissionForOrigin, + } = createMockedHandler(); - await handler( - getBaseRequest({ - params: [ - { - [RestrictedMethods.eth_accounts]: { - foo: 'bar', + await handler( + getBaseRequest({ + params: [ + { + [Caip25EndowmentPermissionName]: {}, }, - }, - ], - }), - ); + ], + }), + ); - expect(requestCaip25PermissionForOrigin).toHaveBeenCalledWith({ - [RestrictedMethods.eth_accounts]: { - foo: 'bar', - }, + expect(requestPermissionsForOrigin).toHaveBeenCalledWith({ + [Caip25EndowmentPermissionName]: {}, + }); + expect(requestCaip25PermissionForOrigin).not.toHaveBeenCalled(); }); - }); - it('requests the CAIP-25 permission for permittedChains when only permittedChains is specified in params', async () => { - const { handler, requestCaip25PermissionForOrigin } = createMockedHandler(); + it('requests the permission for the other permissions', async () => { + const { handler, requestPermissionsForOrigin } = createMockedHandler(); - await handler( - getBaseRequest({ - params: [ - { - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x64'], - }, - ], + await handler( + getBaseRequest({ + params: [ + { + otherPermissionA: {}, + otherPermissionB: {}, }, - }, - ], - }), - ); + ], + }), + ); - expect(requestCaip25PermissionForOrigin).toHaveBeenCalledWith({ - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x64'], - }, - ], - }, + expect(requestPermissionsForOrigin).toHaveBeenCalledWith({ + otherPermissionA: {}, + otherPermissionB: {}, + }); }); - }); - it('requests the CAIP-25 permission for eth_accounts and permittedChains when both are specified in params', async () => { - const { handler, requestCaip25PermissionForOrigin } = createMockedHandler(); + it('returns an error if requesting other permissions fails', async () => { + const { handler, requestPermissionsForOrigin, end } = + createMockedHandler(); - await handler( - getBaseRequest({ - params: [ - { - [RestrictedMethods.eth_accounts]: { - foo: 'bar', - }, - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x64'], - }, - ], + requestPermissionsForOrigin.mockRejectedValue( + new Error('failed to request other permissions'), + ); + + await handler( + getBaseRequest({ + params: [ + { + otherPermissionA: {}, + otherPermissionB: {}, }, - }, - ], - }), - ); + ], + }), + ); - expect(requestCaip25PermissionForOrigin).toHaveBeenCalledWith({ - [RestrictedMethods.eth_accounts]: { - foo: 'bar', - }, - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x64'], - }, - ], - }, + expect(end).toHaveBeenCalledWith( + new Error('failed to request other permissions'), + ); }); - }); - it('returns an error if requesting the CAIP-25 permission fails', async () => { - const { handler, requestCaip25PermissionForOrigin, end } = - createMockedHandler(); - requestCaip25PermissionForOrigin.mockRejectedValue( - new Error('failed to request caip25 permission'), - ); + it('returns the other permissions that are granted', async () => { + const { handler, requestPermissionsForOrigin, response } = + createMockedHandler(); - await handler( - getBaseRequest({ - params: [ - { - [RestrictedMethods.eth_accounts]: { - foo: 'bar', - }, - [PermissionNames.permittedChains]: { - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x64'], - }, - ], + requestPermissionsForOrigin.mockResolvedValue([ + { + otherPermissionA: { foo: 'bar' }, + otherPermissionB: { hello: true }, + }, + ]); + + await handler( + getBaseRequest({ + params: [ + { + otherPermissionA: {}, + otherPermissionB: {}, }, - }, - ], - }), - ); + ], + }), + ); - expect(end).toHaveBeenCalledWith( - new Error('failed to request caip25 permission'), - ); + expect(response.result).toStrictEqual([{ foo: 'bar' }, { hello: true }]); + }); }); - it('returns an error by requesting other permissions in params from the PermissionController if specified', async () => { - const { handler, requestPermissionsForOrigin, end } = createMockedHandler(); + describe('only CAIP-25 equivalent permissions ("eth_accounts" and/or "endowment:permittedChains") requested', () => { + it('requests the CAIP-25 permission using eth_accounts when only eth_accounts is specified in params', async () => { + const { handler, requestCaip25PermissionForOrigin } = + createMockedHandler(); - await handler( - getBaseRequest({ - params: [ - { - [Caip25EndowmentPermissionName]: {}, - otherPermission: {}, - }, - ], - }), - ); + await handler( + getBaseRequest({ + params: [ + { + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + }, + ], + }), + ); - expect(requestPermissionsForOrigin).toHaveBeenCalledWith({ - [Caip25EndowmentPermissionName]: {}, - otherPermission: {}, + expect(requestCaip25PermissionForOrigin).toHaveBeenCalledWith({ + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + }); }); - expect(end).toHaveBeenCalledWith( - new Error('failed to request unexpected permission'), - ); - }); - it('returns an error by requesting empty permissions in params from the PermissionController if no permissions specified', async () => { - const { handler, requestPermissionsForOrigin, end } = createMockedHandler(); + it('requests the CAIP-25 permission for permittedChains when only permittedChains is specified in params', async () => { + const { handler, requestCaip25PermissionForOrigin } = + createMockedHandler(); - await handler( - getBaseRequest({ - params: [{}], - }), - ); - expect(requestPermissionsForOrigin).toHaveBeenCalledWith({}); - expect(end).toHaveBeenCalledWith( - new Error('failed to request unexpected permission'), - ); - }); + await handler( + getBaseRequest({ + params: [ + { + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }, + ], + }), + ); - it('returns both eth_accounts and permittedChains permissions that were granted there are permitted chains', async () => { - const { handler, getAccounts, requestCaip25PermissionForOrigin, response } = - createMockedHandler(); - getAccounts.mockReturnValue(['0xdeadbeef']); - requestCaip25PermissionForOrigin.mockResolvedValue({ - id: 'new', - parentCapability: Caip25EndowmentPermissionName, - caveats: [ - { - type: Caip25CaveatType, - value: { - requiredScopes: {}, - optionalScopes: { - 'eip155:1': { - accounts: ['0xdeadbeef'], + expect(requestCaip25PermissionForOrigin).toHaveBeenCalledWith({ + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }); + }); + + it('requests the CAIP-25 permission for eth_accounts and permittedChains when both are specified in params', async () => { + const { handler, requestCaip25PermissionForOrigin } = + createMockedHandler(); + + await handler( + getBaseRequest({ + params: [ + { + [RestrictedMethods.eth_accounts]: { + foo: 'bar', }, - 'eip155:5': { - accounts: ['0xdeadbeef'], + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], }, }, - }, + ], + }), + ); + + expect(requestCaip25PermissionForOrigin).toHaveBeenCalledWith({ + [RestrictedMethods.eth_accounts]: { + foo: 'bar', }, - ], + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }); }); - await handler(getBaseRequest()); - expect(response.result).toStrictEqual([ - { + it('returns an error if requesting the CAIP-25 permission fails', async () => { + const { handler, requestCaip25PermissionForOrigin, end } = + createMockedHandler(); + requestCaip25PermissionForOrigin.mockRejectedValue( + new Error('failed to request caip25 permission'), + ); + + await handler( + getBaseRequest({ + params: [ + { + [RestrictedMethods.eth_accounts]: { + foo: 'bar', + }, + [PermissionNames.permittedChains]: { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x64'], + }, + ], + }, + }, + ], + }), + ); + + expect(end).toHaveBeenCalledWith( + new Error('failed to request caip25 permission'), + ); + }); + + it('returns both eth_accounts and permittedChains permissions that were granted if there are permitted chains', async () => { + const { + handler, + getAccounts, + requestCaip25PermissionForOrigin, + response, + } = createMockedHandler(); + getAccounts.mockReturnValue(['0xdeadbeef']); + requestCaip25PermissionForOrigin.mockResolvedValue({ + id: 'new', + parentCapability: Caip25EndowmentPermissionName, caveats: [ { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0xdeadbeef'], + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + accounts: ['0xdeadbeef'], + }, + 'eip155:5': { + accounts: ['0xdeadbeef'], + }, + }, + }, }, ], + }); + + await handler(getBaseRequest()); + expect(response.result).toStrictEqual([ + { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['0xdeadbeef'], + }, + ], + id: 'new', + parentCapability: RestrictedMethods.eth_accounts, + }, + { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1', '0x5'], + }, + ], + id: 'new', + parentCapability: PermissionNames.permittedChains, + }, + ]); + }); + + it('returns only eth_accounts permission that was granted if there are no permitted chains', async () => { + const { + handler, + getAccounts, + requestCaip25PermissionForOrigin, + response, + } = createMockedHandler(); + getAccounts.mockReturnValue(['0xdeadbeef']); + requestCaip25PermissionForOrigin.mockResolvedValue({ id: 'new', - parentCapability: RestrictedMethods.eth_accounts, - }, - { + parentCapability: Caip25EndowmentPermissionName, caveats: [ { - type: CaveatTypes.restrictNetworkSwitching, - value: ['0x1', '0x5'], + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'wallet:eip155': { + accounts: ['0xdeadbeef'], + }, + }, + }, }, ], - id: 'new', - parentCapability: PermissionNames.permittedChains, - }, - ]); - }); + }); - it('returns only eth_accounts permission that was granted if there are no permitted chains', async () => { - const { handler, getAccounts, requestCaip25PermissionForOrigin, response } = - createMockedHandler(); - getAccounts.mockReturnValue(['0xdeadbeef']); - requestCaip25PermissionForOrigin.mockResolvedValue({ - id: 'new', - parentCapability: Caip25EndowmentPermissionName, - caveats: [ + await handler(getBaseRequest()); + expect(response.result).toStrictEqual([ { - type: Caip25CaveatType, - value: { - requiredScopes: {}, - optionalScopes: { - 'wallet:eip155': { - accounts: ['0xdeadbeef'], + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['0xdeadbeef'], + }, + ], + id: 'new', + parentCapability: RestrictedMethods.eth_accounts, + }, + ]); + }); + }); + + describe('both CAIP-25 equivalent and other permissions requested', () => { + describe('both CAIP-25 equivalent permissions and other permissions are granted', () => { + it('returns eth_accounts, permittedChains, and other permissions that were granted', async () => { + const { + handler, + getAccounts, + requestPermissionsForOrigin, + requestCaip25PermissionForOrigin, + response, + } = createMockedHandler(); + requestPermissionsForOrigin.mockResolvedValue([ + { + otherPermissionA: { foo: 'bar' }, + otherPermissionB: { hello: true }, + }, + ]); + getAccounts.mockReturnValue(['0xdeadbeef']); + requestCaip25PermissionForOrigin.mockResolvedValue({ + id: 'new', + parentCapability: Caip25EndowmentPermissionName, + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + accounts: ['0xdeadbeef'], + }, + 'eip155:5': { + accounts: ['0xdeadbeef'], + }, + }, + }, + }, + ], + }); + + await handler( + getBaseRequest({ + params: [ + { + eth_accounts: {}, + 'endowment:permitted-chains': {}, + otherPermissionA: {}, + otherPermissionB: {}, + }, + ], + }), + ); + expect(response.result).toStrictEqual([ + { foo: 'bar' }, + { hello: true }, + { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['0xdeadbeef'], + }, + ], + id: 'new', + parentCapability: RestrictedMethods.eth_accounts, + }, + { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1', '0x5'], + }, + ], + id: 'new', + parentCapability: PermissionNames.permittedChains, + }, + ]); + }); + }); + + describe('CAIP-25 equivalent permissions are granted, but other permissions are not granted', () => { + it('returns both eth_accounts and permittedChains permissions that were granted', async () => { + const { + handler, + getAccounts, + requestPermissionsForOrigin, + requestCaip25PermissionForOrigin, + response, + } = createMockedHandler(); + requestPermissionsForOrigin.mockRejectedValue( + new Error('other permissions rejected'), + ); + getAccounts.mockReturnValue(['0xdeadbeef']); + requestCaip25PermissionForOrigin.mockResolvedValue({ + id: 'new', + parentCapability: Caip25EndowmentPermissionName, + caveats: [ + { + type: Caip25CaveatType, + value: { + requiredScopes: {}, + optionalScopes: { + 'eip155:1': { + accounts: ['0xdeadbeef'], + }, + 'eip155:5': { + accounts: ['0xdeadbeef'], + }, + }, }, }, + ], + }); + + await handler( + getBaseRequest({ + params: [ + { + eth_accounts: {}, + 'endowment:permitted-chains': {}, + otherPermissionA: {}, + otherPermissionB: {}, + }, + ], + }), + ); + expect(response.result).toStrictEqual([ + { + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ['0xdeadbeef'], + }, + ], + id: 'new', + parentCapability: RestrictedMethods.eth_accounts, }, - }, - ], + { + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ['0x1', '0x5'], + }, + ], + id: 'new', + parentCapability: PermissionNames.permittedChains, + }, + ]); + }); }); - await handler(getBaseRequest()); - expect(response.result).toStrictEqual([ - { - caveats: [ + describe('CAIP-25 equivalent permissions are not granted, but other permissions are granted', () => { + it('returns the other permissions that are granted', async () => { + const { + handler, + requestPermissionsForOrigin, + requestCaip25PermissionForOrigin, + response, + } = createMockedHandler(); + requestPermissionsForOrigin.mockResolvedValue([ { - type: CaveatTypes.restrictReturnedAccounts, - value: ['0xdeadbeef'], + otherPermissionA: { foo: 'bar' }, + otherPermissionB: { hello: true }, }, - ], - id: 'new', - parentCapability: RestrictedMethods.eth_accounts, - }, - ]); + ]); + requestCaip25PermissionForOrigin.mockRejectedValue( + new Error('caip25 permission rejected'), + ); + + await handler( + getBaseRequest({ + params: [ + { + eth_accounts: {}, + 'endowment:permitted-chains': {}, + otherPermissionA: {}, + otherPermissionB: {}, + }, + ], + }), + ); + + expect(response.result).toStrictEqual([ + { foo: 'bar' }, + { hello: true }, + ]); + }); + }); + + describe('both CAIP-25 equivalent permissions and other permissions are not granted', () => { + it('returns the error from the rejected CAIP-25 permission grant', async () => { + const { + handler, + requestPermissionsForOrigin, + requestCaip25PermissionForOrigin, + end, + } = createMockedHandler(); + requestPermissionsForOrigin.mockRejectedValue( + new Error('other permissions rejected'), + ); + requestCaip25PermissionForOrigin.mockRejectedValue( + new Error('caip25 permission rejected'), + ); + + await handler( + getBaseRequest({ + params: [ + { + eth_accounts: {}, + 'endowment:permitted-chains': {}, + otherPermissionA: {}, + otherPermissionB: {}, + }, + ], + }), + ); + + expect(end).toHaveBeenCalledWith( + new Error('caip25 permission rejected'), + ); + }); + }); + }); + + describe('no permissions requested', () => { + it('returns an error by requesting empty permissions in params from the PermissionController if no permissions specified', async () => { + const { handler, requestPermissionsForOrigin, end } = + createMockedHandler(); + requestPermissionsForOrigin.mockRejectedValue( + new Error('failed to request unexpected permission'), + ); + + await handler( + getBaseRequest({ + params: [{}], + }), + ); + expect(requestPermissionsForOrigin).toHaveBeenCalledWith({}); + expect(end).toHaveBeenCalledWith( + new Error('failed to request unexpected permission'), + ); + }); }); }); diff --git a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts index 7fe30e32ec6b..0ee80669758a 100644 --- a/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts +++ b/app/scripts/lib/rpc-method-middleware/handlers/wallet-requestPermissions.ts @@ -90,7 +90,7 @@ async function requestPermissionsImplementation( } const [requestedPermissions] = params; - const legacyRequestedPermissions: Partial< + const caip25EquivalentPermissions: Partial< Pick > = pick(requestedPermissions, [ RestrictedMethods.eth_accounts, @@ -99,63 +99,79 @@ async function requestPermissionsImplementation( delete requestedPermissions[RestrictedMethods.eth_accounts]; delete requestedPermissions[PermissionNames.permittedChains]; - const hasExpectedPermissions = Object.keys(requestedPermissions).length > 0; - const hasUnexpectedRequestedPermissions = - Object.keys(legacyRequestedPermissions).length > 0; + const hasCaip25EquivalentPermissions = + Object.keys(caip25EquivalentPermissions).length > 0; + const hasOtherRequestedPermissions = + Object.keys(requestedPermissions).length > 0; - let caip25Endowment; - let caip25CaveatValue; - try { - if (hasExpectedPermissions || !hasUnexpectedRequestedPermissions) { - // This will throw. We are making this call purposely to get a proper error - await requestPermissionsForOrigin(requestedPermissions); - } + let grantedPermissions: GrantedPermissions = {}; + let didGrantOtherPermissions; - caip25Endowment = await requestCaip25PermissionForOrigin( - legacyRequestedPermissions, - ); - caip25CaveatValue = caip25Endowment?.caveats?.find( - ({ type }) => type === Caip25CaveatType, - )?.value as Caip25CaveatValue | undefined; - - if (!caip25CaveatValue) { - throw new Error( - `could not find ${Caip25CaveatType} in granted ${Caip25EndowmentPermissionName} permission.`, + if (hasOtherRequestedPermissions || !hasCaip25EquivalentPermissions) { + try { + const [frozenGrantedPermissions] = await requestPermissionsForOrigin( + requestedPermissions, ); + // permissions are frozen and must be cloned before modified + grantedPermissions = { ...frozenGrantedPermissions }; + didGrantOtherPermissions = true; + } catch (error) { + if (!hasCaip25EquivalentPermissions) { + return end(error as unknown as Error); + } } - } catch (error) { - return end(error as unknown as Error); } - const grantedPermissions: GrantedPermissions = {}; + let caip25Endowment; + let caip25CaveatValue; + if (hasCaip25EquivalentPermissions) { + try { + caip25Endowment = await requestCaip25PermissionForOrigin( + caip25EquivalentPermissions, + ); + caip25CaveatValue = caip25Endowment?.caveats?.find( + ({ type }) => type === Caip25CaveatType, + )?.value as Caip25CaveatValue | undefined; - // We cannot derive correct eth_accounts value directly from the CAIP-25 permission - // because the accounts will not be in order of lastSelected - const ethAccounts = getAccounts(); + if (!caip25CaveatValue) { + throw new Error( + `could not find ${Caip25CaveatType} in granted ${Caip25EndowmentPermissionName} permission.`, + ); + } - grantedPermissions[RestrictedMethods.eth_accounts] = { - ...caip25Endowment, - parentCapability: RestrictedMethods.eth_accounts, - caveats: [ - { - type: CaveatTypes.restrictReturnedAccounts, - value: ethAccounts, - }, - ], - }; + // We cannot derive correct eth_accounts value directly from the CAIP-25 permission + // because the accounts will not be in order of lastSelected + const ethAccounts = getAccounts(); - const ethChainIds = getPermittedEthChainIds(caip25CaveatValue); - if (ethChainIds.length > 0) { - grantedPermissions[PermissionNames.permittedChains] = { - ...caip25Endowment, - parentCapability: PermissionNames.permittedChains, - caveats: [ - { - type: CaveatTypes.restrictNetworkSwitching, - value: ethChainIds, - }, - ], - }; + grantedPermissions[RestrictedMethods.eth_accounts] = { + ...caip25Endowment, + parentCapability: RestrictedMethods.eth_accounts, + caveats: [ + { + type: CaveatTypes.restrictReturnedAccounts, + value: ethAccounts, + }, + ], + }; + + const ethChainIds = getPermittedEthChainIds(caip25CaveatValue); + if (ethChainIds.length > 0) { + grantedPermissions[PermissionNames.permittedChains] = { + ...caip25Endowment, + parentCapability: PermissionNames.permittedChains, + caveats: [ + { + type: CaveatTypes.restrictNetworkSwitching, + value: ethChainIds, + }, + ], + }; + } + } catch (error) { + if (!didGrantOtherPermissions) { + return end(error as unknown as Error); + } + } } res.result = Object.values(grantedPermissions).filter( diff --git a/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js b/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js index e23e23ee789e..a54573a3c6e9 100644 --- a/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js +++ b/ui/components/app/permissions-connect-permission-list/permissions-connect-permission-list.js @@ -18,7 +18,12 @@ import { Box } from '../../component-library'; * @param options.requestedChainIds - An array representing list of chain ids for which permission is used. * @returns {JSX.Element} A permission description node. */ -function getDescriptionNode({permission, index, accounts, requestedChainIds}) { +function getDescriptionNode({ + permission, + index, + accounts, + requestedChainIds, +}) { return ( Date: Thu, 19 Dec 2024 15:16:52 -0800 Subject: [PATCH 144/148] Fix Snaps permission UI --- ui/helpers/utils/permission.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ui/helpers/utils/permission.js b/ui/helpers/utils/permission.js index 13162945faa1..7526732d5ec7 100644 --- a/ui/helpers/utils/permission.js +++ b/ui/helpers/utils/permission.js @@ -51,11 +51,18 @@ function getSnapNameComponent(snapName) { } export const PERMISSION_DESCRIPTIONS = deepFreeze({ + // "endowment:caip25" entry is needed for the Snaps Permissions Review UI [Caip25EndowmentPermissionName]: ({ t }) => ({ label: t('permission_ethereumAccounts'), leftIcon: IconName.Eye, weight: PermissionWeight.eth_accounts, }), + // "eth_accounts" entry is needed for the Snaps Permissions Grant UI + [RestrictedMethods.eth_accounts]: ({ t }) => ({ + label: t('permission_ethereumAccounts'), + leftIcon: IconName.Eye, + weight: PermissionWeight.eth_accounts, + }), [PermissionNames.permittedChains]: ({ t }) => ({ label: t('permission_walletSwitchEthereumChain'), leftIcon: IconName.Wifi, From 8ac50a4d5cb2ea50937cc612ad701a8324764720 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 19 Dec 2024 15:19:08 -0800 Subject: [PATCH 145/148] incremental permitted chains view still needed --- ui/pages/permissions-connect/permissions-connect.component.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/pages/permissions-connect/permissions-connect.component.js b/ui/pages/permissions-connect/permissions-connect.component.js index 3d7428735cf5..0aa736399dbe 100644 --- a/ui/pages/permissions-connect/permissions-connect.component.js +++ b/ui/pages/permissions-connect/permissions-connect.component.js @@ -159,7 +159,6 @@ export default class PermissionConnect extends Component { // if this is an incremental permission request for permitted chains, skip the account selection if ( - // TODO pretty sure this is not needed anymore. permissionsRequest?.diff?.permissionDiffMap?.[ PermissionNames.permittedChains ] From ca74a5d3ed4e024a3cf2be4949c32918ee69391a Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 19 Dec 2024 15:31:51 -0800 Subject: [PATCH 146/148] replace updatedCaveatValue let assignment with const --- .../controllers/permissions/background-api.js | 24 +++++++---- app/scripts/metamask-controller.js | 43 +++++++++++-------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/app/scripts/controllers/permissions/background-api.js b/app/scripts/controllers/permissions/background-api.js index 8f607269ba60..628431f570ce 100644 --- a/app/scripts/controllers/permissions/background-api.js +++ b/app/scripts/controllers/permissions/background-api.js @@ -94,20 +94,23 @@ export function getPermissionBackgroundApiMethods({ new Set([...ethChainIds, ...chainIds]), ); - let updatedCaveatValue = setPermittedEthChainIds( + const caveatValueWithChains = setPermittedEthChainIds( caip25Caveat.value, updatedEthChainIds, ); // ensure that the list of permitted eth accounts is set for the newly added eth scopes - const ethAccounts = getEthAccounts(updatedCaveatValue); - updatedCaveatValue = setEthAccounts(updatedCaveatValue, ethAccounts); + const ethAccounts = getEthAccounts(caveatValueWithChains); + const caveatValueWithAccountsSynced = setEthAccounts( + caveatValueWithChains, + ethAccounts, + ); permissionController.updateCaveat( origin, Caip25EndowmentPermissionName, Caip25CaveatType, - updatedCaveatValue, + caveatValueWithAccountsSynced, ); }; @@ -135,18 +138,21 @@ export function getPermissionBackgroundApiMethods({ type: MethodNames.RequestPermissions, }); - let caveatValue = { + const newCaveatValue = { requiredScopes: {}, optionalScopes: {}, isMultichainOrigin: false, }; - caveatValue = setPermittedEthChainIds( - caveatValue, + const caveatValueWithChains = setPermittedEthChainIds( + newCaveatValue, legacyApproval.approvedChainIds, ); - caveatValue = setEthAccounts(caveatValue, legacyApproval.approvedAccounts); + const caveatValueWithAccounts = setEthAccounts( + caveatValueWithChains, + legacyApproval.approvedAccounts, + ); permissionController.grantPermissions({ subject: { origin }, @@ -155,7 +161,7 @@ export function getPermissionBackgroundApiMethods({ caveats: [ { type: Caip25CaveatType, - value: caveatValue, + value: caveatValueWithAccounts, }, ], }, diff --git a/app/scripts/metamask-controller.js b/app/scripts/metamask-controller.js index 8f51c805a9a9..31f0a71a24be 100644 --- a/app/scripts/metamask-controller.js +++ b/app/scripts/metamask-controller.js @@ -5475,19 +5475,22 @@ export default class MetamaskController extends EventEmitter { await this.requestApprovalPermittedChainsPermission(origin, chainId); } - let updatedCaveatValue = addPermittedEthChainId( + const caveatValueWithChainsAdded = addPermittedEthChainId( caip25Caveat.value, chainId, ); const ethAccounts = getEthAccounts(caip25Caveat.value); - updatedCaveatValue = setEthAccounts(updatedCaveatValue, ethAccounts); + const caveatValueWithAccountsSynced = setEthAccounts( + caveatValueWithChainsAdded, + ethAccounts, + ); this.permissionController.updateCaveat( origin, Caip25EndowmentPermissionName, Caip25CaveatType, - updatedCaveatValue, + caveatValueWithAccountsSynced, ); } @@ -5532,26 +5535,30 @@ export default class MetamaskController extends EventEmitter { type: MethodNames.RequestPermissions, }); - let caveatValue = { + const newCaveatValue = { requiredScopes: {}, optionalScopes: {}, isMultichainOrigin: false, }; - if (isSnapId(origin)) { - caveatValue.optionalScopes = { - 'wallet:eip155': { - accounts: [], - }, - }; - } else { - caveatValue = setPermittedEthChainIds( - caveatValue, - legacyApproval.approvedChainIds, - ); - } + const caveatValueWithChains = isSnapId(origin) + ? { + ...newCaveatValue, + optionalScopes: { + 'wallet:eip155': { + accounts: [], + }, + }, + } + : setPermittedEthChainIds( + newCaveatValue, + legacyApproval.approvedChainIds, + ); - caveatValue = setEthAccounts(caveatValue, legacyApproval.approvedAccounts); + const caveatValueWithAccounts = setEthAccounts( + caveatValueWithChains, + legacyApproval.approvedAccounts, + ); const grantedPermissions = this.permissionController.grantPermissions({ subject: { origin }, @@ -5560,7 +5567,7 @@ export default class MetamaskController extends EventEmitter { caveats: [ { type: Caip25CaveatType, - value: caveatValue, + value: caveatValueWithAccounts, }, ], }, From a7ba0434ef2562f198be61b3fe245a5172e09605 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Thu, 19 Dec 2024 16:07:59 -0800 Subject: [PATCH 147/148] fix wallet_requestPermissions e2e spec --- test/e2e/json-rpc/wallet_requestPermissions.spec.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/e2e/json-rpc/wallet_requestPermissions.spec.ts b/test/e2e/json-rpc/wallet_requestPermissions.spec.ts index d2a033dcf3c5..f06d28a1609d 100644 --- a/test/e2e/json-rpc/wallet_requestPermissions.spec.ts +++ b/test/e2e/json-rpc/wallet_requestPermissions.spec.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import { PermissionConstraint } from '@metamask/permission-controller'; import { withFixtures } from '../helpers'; import FixtureBuilder from '../fixture-builder'; import { loginWithBalanceValidation } from '../page-objects/flows/login.flow'; @@ -36,7 +37,17 @@ describe('wallet_requestPermissions', function () { const getPermissions = await driver.executeScript( `return window.ethereum.request(${getPermissionsRequest})`, ); - assert.strictEqual(getPermissions[1].parentCapability, 'eth_accounts'); + + const grantedPermissionNames = getPermissions + .map( + (permission: PermissionConstraint) => permission.parentCapability, + ) + .sort(); + + assert.deepStrictEqual(grantedPermissionNames, [ + 'endowment:permitted-chains', + 'eth_accounts', + ]); }, ); }); From eb0876123481f6cf2f5a9a956a18a3d08d8cf1e4 Mon Sep 17 00:00:00 2001 From: Jiexi Luan Date: Fri, 20 Dec 2024 08:51:57 -0800 Subject: [PATCH 148/148] remove incremental redirect for permittedChains --- .../permissions-connect/permissions-connect.component.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ui/pages/permissions-connect/permissions-connect.component.js b/ui/pages/permissions-connect/permissions-connect.component.js index 0aa736399dbe..c39750c54666 100644 --- a/ui/pages/permissions-connect/permissions-connect.component.js +++ b/ui/pages/permissions-connect/permissions-connect.component.js @@ -157,14 +157,6 @@ export default class PermissionConnect extends Component { return; } - // if this is an incremental permission request for permitted chains, skip the account selection - if ( - permissionsRequest?.diff?.permissionDiffMap?.[ - PermissionNames.permittedChains - ] - ) { - history.replace(confirmPermissionPath); - } if (history.location.pathname === connectPath && !isRequestingAccounts) { switch (requestType) { case 'wallet_installSnap':