Repository: MetaMask/accounts
Pull request #273: feat!: add `KeyringRequest.origin`
Author: login: ccharly
Created: 2025-04-02T16:45:29Z | Merged: 2025-05-26T15:23:22Z

Adding a `origin` field to `KeyringRequest`. This will be used to provide the actual caller's origin (dapp origin in this case) to the Snap, so we can use it/display it when we need strong confirmations from a user.

The normal request `origin` is not enough since `submitRequest` will be actually dispatched by MM itself, but we still keep track of the initial caller in some other context, and this one got re-injected directly on the "keyring request".


Diff:
diff --git a/README.md b/README.md
index eb83f3d43..39a66916b 100644
--- a/README.md
+++ b/README.md
@@ -53,6 +53,7 @@ linkStyle default opacity:0.5
   keyring_internal_api --> keyring_api;
   keyring_internal_api --> keyring_utils;
   keyring_internal_snap_client --> keyring_api;
+  keyring_internal_snap_client --> keyring_internal_api;
   keyring_internal_snap_client --> keyring_snap_client;
   keyring_internal_snap_client --> keyring_utils;
   eth_snap_keyring --> keyring_api;
diff --git a/package.json b/package.json
index efc1bc565..f87b07943 100644
--- a/package.json
+++ b/package.json
@@ -34,6 +34,7 @@
     "test:types": "yarn foreach test:types"
   },
   "resolutions": {
+    "@metamask/snaps-sdk@^6.24.0": "npm:@metamask/snaps-sdk@^7.0.0",
     "@types/node": "^20.12.12",
     "@types/web": "^0.0.69",
     "@typescript/lib-dom": "npm:@types/web@^0.0.69",
diff --git a/packages/keyring-api/src/api/request.test.ts b/packages/keyring-api/src/api/request.test.ts
index 53fe0db26..4629c7c49 100644
--- a/packages/keyring-api/src/api/request.test.ts
+++ b/packages/keyring-api/src/api/request.test.ts
@@ -10,6 +10,7 @@ describe('KeyringRequest', () => {
         id: '47d782ac-15c8-4c81-8bfe-759ae1be4a3e',
         scope: 'eip155:1',
         account: 'd6311e3c-a4ec-43fa-b341-592ffefd9797',
+        origin: 'metamask',
         request: {
           method: 'eth_personalSign',
           params: {
@@ -24,6 +25,7 @@ describe('KeyringRequest', () => {
         id: '47d782ac-15c8-4c81-8bfe-759ae1be4a3e',
         scope: 'eip155:1',
         account: 'd6311e3c-a4ec-43fa-b341-592ffefd9797',
+        origin: 'metamask',
         request: {
           method: 'eth_somethingElseWithNoParameters',
         },
@@ -36,6 +38,7 @@ describe('KeyringRequest', () => {
       request: {
         scope: 'eip155:1',
         account: 'd6311e3c-a4ec-43fa-b341-592ffefd9797',
+        origin: 'metamask',
         request: {
           method: 'eth_personalSign',
           params: {
@@ -50,6 +53,7 @@ describe('KeyringRequest', () => {
       request: {
         id: '47d782ac-15c8-4c81-8bfe-759ae1be4a3e',
         account: 'd6311e3c-a4ec-43fa-b341-592ffefd9797',
+        origin: 'metamask',
         request: {
           method: 'eth_personalSign',
           params: {
@@ -64,6 +68,22 @@ describe('KeyringRequest', () => {
       request: {
         id: '47d782ac-15c8-4c81-8bfe-759ae1be4a3e',
         scope: 'eip155:1',
+        origin: 'metamask',
+        request: {
+          method: 'eth_personalSign',
+          params: {
+            data: '0x00...',
+          },
+        },
+      },
+      expected: false,
+    },
+    // Missing origin
+    {
+      request: {
+        id: '47d782ac-15c8-4c81-8bfe-759ae1be4a3e',
+        scope: 'eip155:1',
+        account: 'd6311e3c-a4ec-43fa-b341-592ffefd9797',
         request: {
           method: 'eth_personalSign',
           params: {
@@ -79,6 +99,7 @@ describe('KeyringRequest', () => {
         id: '47d782ac-15c8-4c81-8bfe-759ae1be4a3e',
         scope: 'eip155:1',
         account: 'd6311e3c-a4ec-43fa-b341-592ffefd9797',
+        origin: 'metamask',
       },
       expected: false,
     },
diff --git a/packages/keyring-api/src/api/request.ts b/packages/keyring-api/src/api/request.ts
index 3b3c4c729..9cef5d87b 100644
--- a/packages/keyring-api/src/api/request.ts
+++ b/packages/keyring-api/src/api/request.ts
@@ -19,6 +19,11 @@ export const KeyringRequestStruct = object({
    */
   account: UuidStruct,
 
+  /**
+   * Origin of the sender.
+   */
+  origin: string(),
+
   /**
    * Inner request sent by the client application.
    */
diff --git a/packages/keyring-internal-api/src/compatibility/index.ts b/packages/keyring-internal-api/src/compatibility/index.ts
new file mode 100644
index 000000000..5b98253d9
--- /dev/null
+++ b/packages/keyring-internal-api/src/compatibility/index.ts
@@ -0,0 +1 @@
+export * from './v1';
diff --git a/packages/keyring-internal-api/src/compatibility/v1.test.ts b/packages/keyring-internal-api/src/compatibility/v1.test.ts
new file mode 100644
index 000000000..b4d1fc4b9
--- /dev/null
+++ b/packages/keyring-internal-api/src/compatibility/v1.test.ts
@@ -0,0 +1,23 @@
+import { EthMethod, EthScope } from '@metamask/keyring-api';
+
+import { toKeyringRequestV1 } from './v1';
+
+describe('v1', () => {
+  describe('toKeyringRequestV1', () => {
+    const request = {
+      id: 'mock-request-id',
+      scope: EthScope.Mainnet,
+      account: '55583f38-d81b-48f8-8494-fc543c2b5c95',
+      origin: 'test',
+      request: {
+        method: EthMethod.PersonalSign,
+        params: {},
+      },
+    };
+    const { origin, ...requestV1 } = request;
+
+    it('converts a keyring request to a keyring request v1', () => {
+      expect(toKeyringRequestV1(request)).toStrictEqual(requestV1);
+    });
+  });
+});
diff --git a/packages/keyring-internal-api/src/compatibility/v1.ts b/packages/keyring-internal-api/src/compatibility/v1.ts
new file mode 100644
index 000000000..d51a09463
--- /dev/null
+++ b/packages/keyring-internal-api/src/compatibility/v1.ts
@@ -0,0 +1,34 @@
+import type { KeyringRequest } from '@metamask/keyring-api';
+import {
+  KeyringRequestStruct,
+  KeyringResponseStruct,
+} from '@metamask/keyring-api';
+import { omit, type Infer } from '@metamask/superstruct';
+
+/**
+ * Keyring request (v1).
+ */
+export const KeyringRequestV1Struct = omit(KeyringRequestStruct, ['origin']);
+
+export type KeyringRequestV1 = Infer<typeof KeyringRequestV1Struct>;
+
+/**
+ * Response to a call to `submitRequest` (v1).
+ */
+export const KeyringResponseV1Struct = KeyringResponseStruct;
+
+export type KeyringResponseV1 = Infer<typeof KeyringResponseV1Struct>;
+
+export const SubmitRequestResponseV1Struct = KeyringResponseV1Struct;
+
+/**
+ * Converts a keyring request to a keyring request v1.
+ *
+ * @param request - A keyring request.
+ * @returns A keyring request v1.
+ */
+export function toKeyringRequestV1(request: KeyringRequest): KeyringRequestV1 {
+  const { origin, ...requestV1 } = request;
+
+  return requestV1;
+}
diff --git a/packages/keyring-internal-api/src/index.ts b/packages/keyring-internal-api/src/index.ts
index 5286ea07a..90f0544fa 100644
--- a/packages/keyring-internal-api/src/index.ts
+++ b/packages/keyring-internal-api/src/index.ts
@@ -1,2 +1,4 @@
+export * from './compatibility';
 export type * from './eth';
 export * from './types';
+export * from './versions';
diff --git a/packages/keyring-internal-api/src/versions.ts b/packages/keyring-internal-api/src/versions.ts
new file mode 100644
index 000000000..4a42f19cd
--- /dev/null
+++ b/packages/keyring-internal-api/src/versions.ts
@@ -0,0 +1,12 @@
+export enum KeyringVersion {
+  /** Default. */
+  V1 = 'v1',
+
+  /**
+   * Introduction of `KeyringRequest.origin`.
+   *
+   * Snap will now receive the `origin` as part of a `KeyringRequest` when `submitRequest` is invoked.
+   * We also expect Snaps to display this `origin` in their confirmation screens (if any).
+   */
+  V2 = 'v2',
+}
diff --git a/packages/keyring-internal-snap-client/package.json b/packages/keyring-internal-snap-client/package.json
index 1a1fbc260..e8f93819d 100644
--- a/packages/keyring-internal-snap-client/package.json
+++ b/packages/keyring-internal-snap-client/package.json
@@ -47,6 +47,7 @@
   "dependencies": {
     "@metamask/base-controller": "^7.1.1",
     "@metamask/keyring-api": "workspace:^",
+    "@metamask/keyring-internal-api": "workspace:^",
     "@metamask/keyring-snap-client": "workspace:^",
     "@metamask/keyring-utils": "workspace:^"
   },
@@ -54,9 +55,9 @@
     "@lavamoat/allow-scripts": "^3.2.1",
     "@lavamoat/preinstall-always-fail": "^2.1.0",
     "@metamask/auto-changelog": "^3.4.4",
-    "@metamask/snaps-controllers": "^9.18.0",
-    "@metamask/snaps-sdk": "^6.16.0",
-    "@metamask/snaps-utils": "^8.9.1",
+    "@metamask/snaps-controllers": "^12.2.0",
+    "@metamask/snaps-sdk": "^7.0.0",
+    "@metamask/snaps-utils": "^9.3.0",
     "@metamask/utils": "^11.1.0",
     "@ts-bridge/cli": "^0.6.3",
     "@types/jest": "^29.5.12",
diff --git a/packages/keyring-internal-snap-client/src/KeyringInternalSnapClient.test.ts b/packages/keyring-internal-snap-client/src/KeyringInternalSnapClient.test.ts
index 982850ac9..525ec0f56 100644
--- a/packages/keyring-internal-snap-client/src/KeyringInternalSnapClient.test.ts
+++ b/packages/keyring-internal-snap-client/src/KeyringInternalSnapClient.test.ts
@@ -1,4 +1,9 @@
-import type { KeyringAccount } from '@metamask/keyring-api';
+import {
+  EthMethod,
+  EthScope,
+  type KeyringAccount,
+} from '@metamask/keyring-api';
+import type { KeyringRequestV1 } from '@metamask/keyring-internal-api';
 import type { SnapId } from '@metamask/snaps-sdk';
 
 import {
@@ -6,19 +11,19 @@ import {
   type KeyringInternalSnapClientMessenger,
 } from './KeyringInternalSnapClient';
 
+const MOCK_ACCOUNT: KeyringAccount = {
+  id: '13f94041-6ae6-451f-a0fe-afdd2fda18a7',
+  address: '0xE9A74AACd7df8112911ca93260fC5a046f8a64Ae',
+  options: {},
+  methods: [],
+  scopes: ['eip155:0'],
+  type: 'eip155:eoa',
+};
+
 describe('KeyringInternalSnapClient', () => {
   const snapId = 'local:localhost:3000' as SnapId;
 
-  const accountsList: KeyringAccount[] = [
-    {
-      id: '13f94041-6ae6-451f-a0fe-afdd2fda18a7',
-      address: '0xE9A74AACd7df8112911ca93260fC5a046f8a64Ae',
-      options: {},
-      methods: [],
-      scopes: ['eip155:0'],
-      type: 'eip155:eoa',
-    },
-  ];
+  const accountsList: KeyringAccount[] = [MOCK_ACCOUNT];
 
   const messenger = {
     call: jest.fn(),
@@ -81,4 +86,44 @@ describe('KeyringInternalSnapClient', () => {
       );
     });
   });
+
+  describe('submitRequestV1', () => {
+    const keyringRequest: KeyringRequestV1 = {
+      id: 'mock-request-id',
+      scope: EthScope.Mainnet,
+      account: MOCK_ACCOUNT.id,
+      request: {
+        method: EthMethod.PersonalSign,
+        params: {},
+      },
+    };
+    const request = {
+      snapId,
+      origin: 'metamask',
+      handler: 'onKeyringRequest',
+      request: {
+        id: expect.any(String),
+        jsonrpc: '2.0',
+        method: 'keyring_submitRequest',
+        params: keyringRequest,
+      },
+    };
+
+    it('calls the submitRequestV1 method', async () => {
+      const client = new KeyringInternalSnapClient({
+        messenger: messenger as unknown as KeyringInternalSnapClientMessenger,
+        snapId,
+      });
+
+      messenger.call.mockResolvedValue({
+        pending: false,
+        result: null,
+      });
+      await client.submitRequestV1(keyringRequest);
+      expect(messenger.call).toHaveBeenCalledWith(
+        'SnapController:handleRequest',
+        request,
+      );
+    });
+  });
 });
diff --git a/packages/keyring-internal-snap-client/src/KeyringInternalSnapClient.ts b/packages/keyring-internal-snap-client/src/KeyringInternalSnapClient.ts
index 54345d5b1..6ec69ff4f 100644
--- a/packages/keyring-internal-snap-client/src/KeyringInternalSnapClient.ts
+++ b/packages/keyring-internal-snap-client/src/KeyringInternalSnapClient.ts
@@ -1,6 +1,12 @@
 import type { RestrictedMessenger } from '@metamask/base-controller';
+import { KeyringRpcMethod } from '@metamask/keyring-api';
+import type {
+  KeyringRequestV1,
+  KeyringResponseV1,
+} from '@metamask/keyring-internal-api';
+import { SubmitRequestResponseV1Struct } from '@metamask/keyring-internal-api';
 import { KeyringClient, type Sender } from '@metamask/keyring-snap-client';
-import type { JsonRpcRequest } from '@metamask/keyring-utils';
+import { strictMask, type JsonRpcRequest } from '@metamask/keyring-utils';
 import type { HandleSnapRequest } from '@metamask/snaps-controllers';
 import type { SnapId } from '@metamask/snaps-sdk';
 import type { HandlerType } from '@metamask/snaps-utils';
@@ -121,4 +127,20 @@ export class KeyringInternalSnapClient extends KeyringClient {
       snapId,
     });
   }
+
+  /**
+   * Submit a keyring request v1 (with no `origin`).
+   *
+   * @param request - Keyring request.
+   * @returns Keyring request's response.
+   */
+  async submitRequestV1(request: KeyringRequestV1): Promise<KeyringResponseV1> {
+    return strictMask(
+      await this.send({
+        method: KeyringRpcMethod.SubmitRequest,
+        params: request,
+      }),
+      SubmitRequestResponseV1Struct,
+    );
+  }
 }
diff --git a/packages/keyring-internal-snap-client/tsconfig.build.json b/packages/keyring-internal-snap-client/tsconfig.build.json
index d54bf4a4a..1947d62b6 100644
--- a/packages/keyring-internal-snap-client/tsconfig.build.json
+++ b/packages/keyring-internal-snap-client/tsconfig.build.json
@@ -12,6 +12,9 @@
     {
       "path": "../keyring-api/tsconfig.build.json"
     },
+    {
+      "path": "../keyring-internal-api/tsconfig.build.json"
+    },
     {
       "path": "../keyring-snap-client/tsconfig.build.json"
     }
diff --git a/packages/keyring-snap-bridge/package.json b/packages/keyring-snap-bridge/package.json
index 1ea2aab18..425f7ecca 100644
--- a/packages/keyring-snap-bridge/package.json
+++ b/packages/keyring-snap-bridge/package.json
@@ -53,9 +53,9 @@
     "@lavamoat/allow-scripts": "^3.2.1",
     "@lavamoat/preinstall-always-fail": "^2.1.0",
     "@metamask/auto-changelog": "^3.4.4",
-    "@metamask/snaps-controllers": "^9.18.0",
-    "@metamask/snaps-sdk": "^6.16.0",
-    "@metamask/snaps-utils": "^8.9.1",
+    "@metamask/snaps-controllers": "^12.2.0",
+    "@metamask/snaps-sdk": "^7.0.0",
+    "@metamask/snaps-utils": "^9.3.0",
     "@ts-bridge/cli": "^0.6.3",
     "@types/jest": "^29.5.12",
     "@types/node": "^20.12.12",
diff --git a/packages/keyring-snap-bridge/src/SnapKeyring.test.ts b/packages/keyring-snap-bridge/src/SnapKeyring.test.ts
index 52d045660..127940274 100644
--- a/packages/keyring-snap-bridge/src/SnapKeyring.test.ts
+++ b/packages/keyring-snap-bridge/src/SnapKeyring.test.ts
@@ -83,6 +83,7 @@ describe('SnapKeyring', () => {
   const mockMessenger = {
     get: jest.fn(),
     handleRequest: jest.fn(),
+    isMinimumPlatformVersion: jest.fn(),
   };
 
   const mockCallbacks = {
@@ -243,13 +244,21 @@ describe('SnapKeyring', () => {
     'SnapController:handleRequest',
     mockMessenger.handleRequest,
   );
+  messenger.registerActionHandler(
+    'SnapController:isMinimumPlatformVersion',
+    mockMessenger.isMinimumPlatformVersion,
+  );
 
   // Now extracts a restricted messenger for the Snap keyring only.
   const mockSnapKeyringMessenger: SnapKeyringMessenger =
     messenger.getRestricted({
       name: 'SnapKeyring',
       allowedEvents: [],
-      allowedActions: ['SnapController:get', 'SnapController:handleRequest'],
+      allowedActions: [
+        'SnapController:get',
+        'SnapController:handleRequest',
+        'SnapController:isMinimumPlatformVersion',
+      ],
     });
 
   // Allow to map a mocked value for a given keyring RPC method
@@ -315,6 +324,8 @@ describe('SnapKeyring', () => {
 
     mockMessenger.get.mockReset();
     mockMessenger.handleRequest.mockReset();
+    mockMessenger.isMinimumPlatformVersion.mockReset();
+    mockMessenger.isMinimumPlatformVersion.mockReturnValue('999.999.999'); // Assuming the platform is always up-to-date.
     for (const account of accounts) {
       mockMessengerHandleRequest({
         [KeyringRpcMethod.ListAccounts]: () => accounts,
@@ -1593,6 +1604,7 @@ describe('SnapKeyring', () => {
           params: {
             id: expect.any(String),
             scope: expectedScope,
+            origin: 'metamask',
             account: ethEoaAccount1.id,
             request: {
               method: 'eth_signTransaction',
@@ -1674,6 +1686,7 @@ describe('SnapKeyring', () => {
           params: {
             id: expect.any(String),
             scope: expectedScope,
+            origin: 'metamask',
             account: ethEoaAccount1.id,
             request: {
               method: 'eth_signTypedData_v1',
@@ -1707,6 +1720,7 @@ describe('SnapKeyring', () => {
           params: {
             id: expect.any(String),
             scope: expectedScope,
+            origin: 'metamask',
             account: ethEoaAccount1.id,
             request: {
               method: 'eth_signTypedData_v4',
@@ -1740,6 +1754,7 @@ describe('SnapKeyring', () => {
           params: {
             id: expect.any(String),
             scope: expectedScope,
+            origin: 'metamask',
             account: ethEoaAccount1.id,
             request: {
               method: 'eth_signTypedData_v1',
@@ -1787,6 +1802,7 @@ describe('SnapKeyring', () => {
             // Without chainId alongside the typed message, we cannot
             // compute the scope for this request!
             scope: '', // Default value for `signTypedTransaction`
+            origin: 'metamask',
             account: ethEoaAccount1.id,
             request: {
               method: 'eth_signTypedData_v4',
@@ -1851,6 +1867,7 @@ describe('SnapKeyring', () => {
               KnownCaipNamespace.Eip155,
               executionContext.chainId,
             ),
+            origin: 'metamask',
             account: ethErc4337Account.id,
             request: {
               method: 'eth_prepareUserOperation',
@@ -1907,6 +1924,7 @@ describe('SnapKeyring', () => {
               KnownCaipNamespace.Eip155,
               executionContext.chainId,
             ),
+            origin: 'metamask',
             account: ethErc4337Account.id,
             request: {
               method: 'eth_patchUserOperation',
@@ -1959,6 +1977,7 @@ describe('SnapKeyring', () => {
               KnownCaipNamespace.Eip155,
               executionContext.chainId,
             ),
+            origin: 'metamask',
             account: ethErc4337Account.id,
             request: {
               method: 'eth_signUserOperation',
@@ -2019,6 +2038,7 @@ describe('SnapKeyring', () => {
           params: {
             id: expect.any(String),
             scope: expect.any(String),
+            origin: 'metamask',
             account: ethEoaAccount1.id,
             request: {
               method: 'eth_sign',
@@ -2410,12 +2430,55 @@ describe('SnapKeyring', () => {
     };
 
     it('submits a request', async () => {
+      const origin = 'test';
+
+      mockMessenger.handleRequest.mockResolvedValue({
+        pending: false,
+        result: null,
+      });
+
+      await keyring.submitRequest({
+        origin,
+        account: account.id,
+        method,
+        params,
+        scope,
+      });
+
+      expect(mockMessenger.handleRequest).toHaveBeenCalledWith({
+        handler: 'onKeyringRequest',
+        origin: 'metamask',
+        request: {
+          id: expect.any(String),
+          jsonrpc: '2.0',
+          method: 'keyring_submitRequest',
+          params: {
+            id: expect.any(String),
+            scope,
+            origin,
+            account: account.id,
+            request: {
+              method,
+              params,
+            },
+          },
+        },
+        snapId,
+      });
+    });
+
+    it('submits a request (v1)', async () => {
+      const origin = 'test';
+
+      // <7.0.0, when `KeyringRequest.origin` got introduced.
+      mockMessenger.isMinimumPlatformVersion.mockReturnValue(false);
       mockMessenger.handleRequest.mockResolvedValue({
         pending: false,
         result: null,
       });
 
       await keyring.submitRequest({
+        origin,
         account: account.id,
         method,
         params,
@@ -2432,6 +2495,7 @@ describe('SnapKeyring', () => {
           params: {
             id: expect.any(String),
             scope,
+            // No origin.
             account: account.id,
             request: {
               method,
@@ -2450,6 +2514,7 @@ describe('SnapKeyring', () => {
 
       await expect(
         keyring.submitRequest({
+          origin: 'test',
           account: account.id,
           method,
           params,
@@ -2463,6 +2528,7 @@ describe('SnapKeyring', () => {
 
       await expect(
         keyring.submitRequest({
+          origin: 'test',
           account: unknownAccountId,
           method,
           params,
@@ -2478,6 +2544,7 @@ describe('SnapKeyring', () => {
 
       await expect(
         keyring.submitRequest({
+          origin: 'test',
           account: account.id,
           method: unknownAccountMethod,
           params,
@@ -2487,6 +2554,30 @@ describe('SnapKeyring', () => {
         `Method '${unknownAccountMethod}' not supported for account ${account.address}`,
       );
     });
+
+    it('throws an error if origin is not being passed', async () => {
+      await expect(
+        keyring.submitRequest({
+          // No origin
+          account: account.id,
+          method: EthMethod.PersonalSign,
+          params,
+          scope,
+        } as unknown as Parameters<SnapKeyring['submitRequest']>[0]),
+      ).rejects.toThrow('An `origin` is required');
+    });
+
+    it('throws an error if origin is empty', async () => {
+      await expect(
+        keyring.submitRequest({
+          origin: '',
+          account: account.id,
+          method: EthMethod.PersonalSign,
+          params,
+          scope,
+        }),
+      ).rejects.toThrow('An `origin` is required');
+    });
   });
 
   describe('prepareUserOperation', () => {
@@ -2537,6 +2628,7 @@ describe('SnapKeyring', () => {
               KnownCaipNamespace.Eip155,
               executionContext.chainId,
             ),
+            origin: 'metamask',
             account: ethErc4337Account.id,
             request: {
               method: 'eth_prepareUserOperation',
@@ -2607,6 +2699,7 @@ describe('SnapKeyring', () => {
               KnownCaipNamespace.Eip155,
               executionContext.chainId,
             ),
+            origin: 'metamask',
             account: ethErc4337Account.id,
             request: {
               method: 'eth_patchUserOperation',
diff --git a/packages/keyring-snap-bridge/src/SnapKeyring.ts b/packages/keyring-snap-bridge/src/SnapKeyring.ts
index aceb996d0..911fb152f 100644
--- a/packages/keyring-snap-bridge/src/SnapKeyring.ts
+++ b/packages/keyring-snap-bridge/src/SnapKeyring.ts
@@ -7,17 +7,6 @@ import { TransactionFactory } from '@ethereumjs/tx';
 import type { ExtractEventPayload } from '@metamask/base-controller';
 import type { TypedDataV1, TypedMessage } from '@metamask/eth-sig-util';
 import { SignTypedDataVersion } from '@metamask/eth-sig-util';
-import {
-  EthBytesStruct,
-  EthMethod,
-  EthBaseUserOperationStruct,
-  EthUserOperationPatchStruct,
-  isEvmAccountType,
-  KeyringEvent,
-  AccountAssetListUpdatedEventStruct,
-  AccountBalancesUpdatedEventStruct,
-  AccountTransactionsUpdatedEventStruct,
-} from '@metamask/keyring-api';
 import type {
   KeyringAccount,
   KeyringExecutionContext,
@@ -29,15 +18,32 @@ import type {
   ResolvedAccountAddress,
   CaipChainId,
   MetaMaskOptions,
+  KeyringRequest,
+  KeyringResponse,
 } from '@metamask/keyring-api';
-import type { InternalAccount } from '@metamask/keyring-internal-api';
+import {
+  EthBytesStruct,
+  EthMethod,
+  EthBaseUserOperationStruct,
+  EthUserOperationPatchStruct,
+  isEvmAccountType,
+  KeyringEvent,
+  AccountAssetListUpdatedEventStruct,
+  AccountBalancesUpdatedEventStruct,
+  AccountTransactionsUpdatedEventStruct,
+} from '@metamask/keyring-api';
+import {
+  KeyringVersion,
+  toKeyringRequestV1,
+  type InternalAccount,
+} from '@metamask/keyring-internal-api';
 import { KeyringInternalSnapClient } from '@metamask/keyring-internal-snap-client';
 import type { AccountId, JsonRpcRequest } from '@metamask/keyring-utils';
 import { strictMask } from '@metamask/keyring-utils';
 import type { SnapId } from '@metamask/snaps-sdk';
 import { type Snap } from '@metamask/snaps-utils';
 import { assert, mask, object, string } from '@metamask/superstruct';
-import type { Hex, Json } from '@metamask/utils';
+import type { Hex, Json, SemVerVersion } from '@metamask/utils';
 import {
   bigIntToHex,
   hasProperty,
@@ -76,6 +82,7 @@ import {
   toJson,
   unique,
 } from './util';
+import { getKeyringVersionFromPlatform } from './versions';
 
 export const SNAP_KEYRING_TYPE = 'Snap Keyring';
 
@@ -213,6 +220,22 @@ export class SnapKeyring extends EventEmitter {
     this.#callbacks = callbacks;
   }
 
+  /**
+   * Gets keyring's version for a given Snap.
+   *
+   * @param snapId - The Snap ID.
+   * @returns The Snap's keyring version.
+   */
+  #getKeyringVersion(snapId: SnapId): KeyringVersion {
+    return getKeyringVersionFromPlatform((version: SemVerVersion) => {
+      return this.#messenger.call(
+        'SnapController:isMinimumPlatformVersion',
+        snapId,
+        version,
+      );
+    });
+  }
+
   /**
    * Get internal options given a correlation ID.
    *
@@ -831,6 +854,7 @@ export class SnapKeyring extends EventEmitter {
    * This request cannot be an asynchronous keyring request.
    *
    * @param opts - Request options.
+   * @param opts.origin - Send origin.
    * @param opts.account - Account ID.
    * @param opts.method - Method to call.
    * @param opts.params - Method parameters.
@@ -838,11 +862,13 @@ export class SnapKeyring extends EventEmitter {
    * @returns Promise that resolves to the result of the method call.
    */
   async submitRequest({
+    origin,
     account: accountId,
     method,
     params,
     scope,
   }: {
+    origin: string;
     // NOTE: We use `account` here rather than `id` to avoid ambiguity with a "request ID".
     // We already use this same field name for `KeyringAccount`s.
     account: string;
@@ -853,6 +879,7 @@ export class SnapKeyring extends EventEmitter {
     const { account, snapId } = this.#getAccount(accountId);
 
     return await this.#submitSnapRequest({
+      origin,
       snapId,
       account,
       method: method as AccountMethod,
@@ -868,6 +895,7 @@ export class SnapKeyring extends EventEmitter {
    * Submit a request to a Snap from an account address.
    *
    * @param opts - Request options.
+   * @param opts.origin - Sender origin.
    * @param opts.address - Account address.
    * @param opts.method - Method to call.
    * @param opts.params - Method parameters.
@@ -876,12 +904,14 @@ export class SnapKeyring extends EventEmitter {
    * @returns Promise that resolves to the result of the method call.
    */
   async #submitRequest<Response extends Json>({
+    origin,
     address,
     method,
     params,
     scope = '',
     noPending = false,
   }: {
+    origin: string;
     address: string;
     method: string;
     params?: Json[] | Record<string, Json>;
@@ -891,6 +921,7 @@ export class SnapKeyring extends EventEmitter {
     const { account, snapId } = this.#resolveAddress(address);
 
     return await this.#submitSnapRequest<Response>({
+      origin,
       snapId,
       account,
       method: method as AccountMethod,
@@ -900,10 +931,39 @@ export class SnapKeyring extends EventEmitter {
     });
   }
 
+  /**
+   * Submits a request to a Snap and fix-up the payload depending on the version currently supported.
+   *
+   * @param options - The options for the Snap request.
+   * @param options.version - The supported keyring version for the Snap.
+   * @param options.snapId - The Snap ID to submit the request to.
+   * @param options.request - The Snap request.
+   * @returns A promise that resolves to the keyring response from the Snap.
+   */
+  async #submitSnapRequestForVersion({
+    snapId,
+    version,
+    request,
+  }: {
+    snapId: SnapId;
+    version: KeyringVersion;
+    request: KeyringRequest;
+  }): Promise<KeyringResponse> {
+    // Get specific client for that Snap.
+    const client = this.#snapClient.withSnapId(snapId);
+
+    if (version === KeyringVersion.V1) {
+      return await client.submitRequestV1(toKeyringRequestV1(request));
+    }
+
+    return await client.submitRequest(request);
+  }
+
   /**
    * Submits a request to a Snap.
    *
    * @param options - The options for the Snap request.
+   * @param options.origin - The sender origin.
    * @param options.snapId - The Snap ID to submit the request to.
    * @param options.account - The account to use for the request.
    * @param options.method - The Ethereum method to call.
@@ -914,6 +974,7 @@ export class SnapKeyring extends EventEmitter {
    * @throws An error if the Snap fails to respond or if there's an issue with the request submission.
    */
   async #submitSnapRequest<Response extends Json>({
+    origin,
     snapId,
     account,
     method,
@@ -921,6 +982,7 @@ export class SnapKeyring extends EventEmitter {
     scope,
     noPending,
   }: {
+    origin: string;
     snapId: SnapId;
     account: KeyringAccount;
     method: AccountMethod;
@@ -934,6 +996,11 @@ export class SnapKeyring extends EventEmitter {
       );
     }
 
+    // Will both catch `undefined` and "empty" origins.
+    if (!origin?.trim()) {
+      throw new Error('An `origin` is required');
+    }
+
     // Generate a new random request ID to keep track of the request execution flow.
     const requestId = uuid();
 
@@ -945,8 +1012,11 @@ export class SnapKeyring extends EventEmitter {
     );
 
     try {
+      const version = this.#getKeyringVersion(snapId);
+
       const request = {
         id: requestId,
+        origin,
         scope,
         account: account.id,
         request: {
@@ -957,9 +1027,11 @@ export class SnapKeyring extends EventEmitter {
 
       log('Submit Snap request: ', request);
 
-      const response = await this.#snapClient
-        .withSnapId(snapId)
-        .submitRequest(request);
+      const response = await this.#submitSnapRequestForVersion({
+        version,
+        snapId,
+        request,
+      });
 
       // Some methods, like the ones used to prepare and patch user operations,
       // require the Snap to answer synchronously in order to work with the
@@ -1123,6 +1195,7 @@ export class SnapKeyring extends EventEmitter {
     });
 
     const signedTx = await this.#submitRequest({
+      origin: 'metamask',
       address,
       method: EthMethod.SignTransaction,
       params: [tx],
@@ -1177,6 +1250,7 @@ export class SnapKeyring extends EventEmitter {
 
     return strictMask(
       await this.#submitRequest({
+        origin: 'metamask',
         address,
         method,
         params: toJson<Json[]>([address, data]),
@@ -1200,6 +1274,7 @@ export class SnapKeyring extends EventEmitter {
   async signMessage(address: string, hash: any): Promise<string> {
     return strictMask(
       await this.#submitRequest({
+        origin: 'metamask',
         address,
         method: EthMethod.Sign,
         params: toJson<Json[]>([address, hash]),
@@ -1221,6 +1296,7 @@ export class SnapKeyring extends EventEmitter {
   async signPersonalMessage(address: string, data: any): Promise<string> {
     return strictMask(
       await this.#submitRequest({
+        origin: 'metamask',
         address,
         method: EthMethod.PersonalSign,
         params: toJson<Json[]>([data, address]),
@@ -1244,6 +1320,7 @@ export class SnapKeyring extends EventEmitter {
   ): Promise<EthBaseUserOperation> {
     return strictMask(
       await this.#submitRequest({
+        origin: 'metamask',
         address,
         method: EthMethod.PrepareUserOperation,
         params: toJson<Json[]>(transactions),
@@ -1271,6 +1348,7 @@ export class SnapKeyring extends EventEmitter {
   ): Promise<EthUserOperationPatch> {
     return strictMask(
       await this.#submitRequest({
+        origin: 'metamask',
         address,
         method: EthMethod.PatchUserOperation,
         params: toJson<Json[]>([userOp]),
@@ -1297,6 +1375,7 @@ export class SnapKeyring extends EventEmitter {
   ): Promise<string> {
     return strictMask(
       await this.#submitRequest({
+        origin: 'metamask',
         address,
         method: EthMethod.SignUserOperation,
         params: toJson<Json[]>([userOp]),
diff --git a/packages/keyring-snap-bridge/src/SnapKeyringMessenger.ts b/packages/keyring-snap-bridge/src/SnapKeyringMessenger.ts
index 581a34af7..8e5ecc9ef 100644
--- a/packages/keyring-snap-bridge/src/SnapKeyringMessenger.ts
+++ b/packages/keyring-snap-bridge/src/SnapKeyringMessenger.ts
@@ -4,7 +4,11 @@ import type {
   AccountBalancesUpdatedEventPayload,
   AccountTransactionsUpdatedEventPayload,
 } from '@metamask/keyring-api';
-import type { HandleSnapRequest, GetSnap } from '@metamask/snaps-controllers';
+import type {
+  HandleSnapRequest as SnapControllerHandleSnapRequest,
+  GetSnap as SnapControllerGetSnap,
+  IsMinimumPlatformVersion as SnapControllerIsMinimumPlatformVersion,
+} from '@metamask/snaps-controllers';
 
 export type SnapKeyringGetAccountsAction = {
   type: `SnapKeyring:getAccounts`;
@@ -31,7 +35,10 @@ export type SnapKeyringEvents =
   | SnapKeyringAccountBalancesUpdatedEvent
   | SnapKeyringAccountTransactionsUpdatedEvent;
 
-export type SnapKeyringAllowedActions = HandleSnapRequest | GetSnap;
+export type SnapKeyringAllowedActions =
+  | SnapControllerHandleSnapRequest
+  | SnapControllerGetSnap
+  | SnapControllerIsMinimumPlatformVersion;
 
 export type SnapKeyringMessenger = RestrictedMessenger<
   'SnapKeyring',
diff --git a/packages/keyring-snap-bridge/src/versions.test.ts b/packages/keyring-snap-bridge/src/versions.test.ts
new file mode 100644
index 000000000..0c39b1579
--- /dev/null
+++ b/packages/keyring-snap-bridge/src/versions.test.ts
@@ -0,0 +1,38 @@
+import { KeyringVersion } from '@metamask/keyring-internal-api';
+import { isValidSemVerVersion, type SemVerVersion } from '@metamask/utils';
+
+import {
+  getKeyringVersionFromPlatform,
+  PLATFORM_VERSION_TO_KEYRING_VERSION,
+} from './versions';
+
+describe('getKeyringVersionFromPlatform', () => {
+  it('gets the keyring version v1 as default', () => {
+    const isSupportedVersion = (): boolean => false;
+
+    expect(getKeyringVersionFromPlatform(isSupportedVersion)).toBe(
+      KeyringVersion.V1,
+    );
+  });
+
+  it('gets the keyring version v2 if the platform version is 7.0.0', () => {
+    const isSupportedVersion = (version: SemVerVersion): boolean => {
+      return version === '7.0.0';
+    };
+
+    expect(getKeyringVersionFromPlatform(isSupportedVersion)).toBe(
+      KeyringVersion.V2,
+    );
+  });
+});
+
+describe('PLATFORM_VERSION_TO_KEYRING_VERSION', () => {
+  it.each(PLATFORM_VERSION_TO_KEYRING_VERSION)(
+    'is a valid semver version: %s',
+    // `PLATFORM_VERSION_TO_KEYRING_VERSION` items are flattened by `it.each`, no need
+    // to destructure the tuple here.
+    (version) => {
+      expect(isValidSemVerVersion(version)).toBe(true);
+    },
+  );
+});
diff --git a/packages/keyring-snap-bridge/src/versions.ts b/packages/keyring-snap-bridge/src/versions.ts
new file mode 100644
index 000000000..b09c783e6
--- /dev/null
+++ b/packages/keyring-snap-bridge/src/versions.ts
@@ -0,0 +1,36 @@
+import { KeyringVersion } from '@metamask/keyring-internal-api';
+import type { SemVerVersion } from '@metamask/utils';
+
+/**
+ * Mapping of the Snap platform version to its `KeyringVersion` equivalent.
+ *
+ * NOTE: We use an array here to preserve ordering of each versions.
+ */
+export const PLATFORM_VERSION_TO_KEYRING_VERSION = [
+  // ! NOTE: This versions needs to be sorted in a descending order (highest platform version first).
+
+  // Introduction of `KeyringRequest.origin`.
+  ['7.0.0', KeyringVersion.V2],
+] as const;
+
+/**
+ * Gets keyring's version for a given Snap.
+ *
+ * @param isSupportedVersion - Predicate to check if the version is supported.
+ * @returns The Snap's keyring version.
+ */
+export function getKeyringVersionFromPlatform(
+  isSupportedVersion: (version: SemVerVersion) => boolean,
+): KeyringVersion {
+  for (const [
+    platformVersion,
+    keyringVersion,
+  ] of PLATFORM_VERSION_TO_KEYRING_VERSION) {
+    // NOTE: We are type-casting, but we have a unit tests that make sure all
+    // versions are following the semver spec.
+    if (isSupportedVersion(platformVersion as SemVerVersion)) {
+      return keyringVersion;
+    }
+  }
+  return KeyringVersion.V1;
+}
diff --git a/packages/keyring-snap-client/src/KeyringClient.test.ts b/packages/keyring-snap-client/src/KeyringClient.test.ts
index 5f1046955..770b5947b 100644
--- a/packages/keyring-snap-client/src/KeyringClient.test.ts
+++ b/packages/keyring-snap-client/src/KeyringClient.test.ts
@@ -10,89 +10,265 @@ import type {
 } from '@metamask/keyring-api';
 import type { JsonRpcRequest } from '@metamask/keyring-utils';
 
-import { KeyringClient } from '.'; // Import from `index.ts` to test the public API
+import { KeyringClient, KeyringPublicClient } from '.'; // Import from `index.ts` to test the public API
 
 describe('KeyringClient', () => {
   const mockSender = {
     send: jest.fn(),
   };
 
-  const keyring = new KeyringClient(mockSender);
+  const keyringClient = new KeyringClient(mockSender);
+  const publicClient = new KeyringPublicClient(mockSender);
 
   beforeEach(() => {
     mockSender.send.mockClear();
   });
 
-  describe('listAccounts', () => {
-    it('should send a request to list accounts and return the response', async () => {
-      const expectedResponse: KeyringAccount[] = [
-        {
-          id: '49116980-0712-4fa5-b045-e4294f1d440e',
-          address: '0xE9A74AACd7df8112911ca93260fC5a046f8a64Ae',
-          options: {},
-          methods: [],
-          scopes: ['eip155:0'],
-          type: 'eip155:eoa',
-        },
-      ];
-
-      mockSender.send.mockResolvedValue(expectedResponse);
-      const accounts = await keyring.listAccounts();
-      expect(mockSender.send).toHaveBeenCalledWith({
-        jsonrpc: '2.0',
-        id: expect.any(String),
-        method: 'keyring_listAccounts',
+  // -----------------------------------------------------------------------------------------------
+  // Public methods (can be used by a companion dapp and MetaMask)
+
+  describe.each([keyringClient, publicClient])(
+    'Public methods',
+    (client: KeyringClient | KeyringPublicClient) => {
+      describe('listAccounts', () => {
+        it('should send a request to list accounts and return the response', async () => {
+          const expectedResponse: KeyringAccount[] = [
+            {
+              id: '49116980-0712-4fa5-b045-e4294f1d440e',
+              address: '0xE9A74AACd7df8112911ca93260fC5a046f8a64Ae',
+              options: {},
+              methods: [],
+              scopes: ['eip155:0'],
+              type: 'eip155:eoa',
+            },
+          ];
+
+          mockSender.send.mockResolvedValue(expectedResponse);
+          const accounts = await client.listAccounts();
+          expect(mockSender.send).toHaveBeenCalledWith({
+            jsonrpc: '2.0',
+            id: expect.any(String),
+            method: 'keyring_listAccounts',
+          });
+          expect(accounts).toStrictEqual(expectedResponse);
+        });
       });
-      expect(accounts).toStrictEqual(expectedResponse);
-    });
-  });
 
-  describe('getAccount', () => {
-    it('should send a request to get an account by ID and return the response', async () => {
-      const id = '49116980-0712-4fa5-b045-e4294f1d440e';
-      const expectedResponse: KeyringAccount = {
-        id: '49116980-0712-4fa5-b045-e4294f1d440e',
-        address: '0xE9A74AACd7df8112911ca93260fC5a046f8a64Ae',
-        options: {},
-        methods: [],
-        scopes: ['eip155:0'],
-        type: 'eip155:eoa',
-      };
+      describe('getAccount', () => {
+        it('should send a request to get an account by ID and return the response', async () => {
+          const id = '49116980-0712-4fa5-b045-e4294f1d440e';
+          const expectedResponse: KeyringAccount = {
+            id: '49116980-0712-4fa5-b045-e4294f1d440e',
+            address: '0xE9A74AACd7df8112911ca93260fC5a046f8a64Ae',
+            options: {},
+            methods: [],
+            scopes: ['eip155:0'],
+            type: 'eip155:eoa',
+          };
+
+          mockSender.send.mockResolvedValue(expectedResponse);
+          const account = await client.getAccount(id);
+          expect(mockSender.send).toHaveBeenCalledWith({
+            jsonrpc: '2.0',
+            id: expect.any(String),
+            method: 'keyring_getAccount',
+            params: { id },
+          });
+          expect(account).toStrictEqual(expectedResponse);
+        });
+      });
 
-      mockSender.send.mockResolvedValue(expectedResponse);
-      const account = await keyring.getAccount(id);
-      expect(mockSender.send).toHaveBeenCalledWith({
-        jsonrpc: '2.0',
-        id: expect.any(String),
-        method: 'keyring_getAccount',
-        params: { id },
+      describe('createAccount', () => {
+        it('should send a request to create an account and return the response', async () => {
+          const expectedResponse: KeyringAccount = {
+            id: '49116980-0712-4fa5-b045-e4294f1d440e',
+            address: '0xE9A74AACd7df8112911ca93260fC5a046f8a64Ae',
+            options: {},
+            methods: [],
+            scopes: ['eip155:0'],
+            type: 'eip155:eoa',
+          };
+
+          mockSender.send.mockResolvedValue(expectedResponse);
+          const account = await client.createAccount();
+          expect(mockSender.send).toHaveBeenCalledWith({
+            jsonrpc: '2.0',
+            id: expect.any(String),
+            method: 'keyring_createAccount',
+            params: { options: {} },
+          });
+          expect(account).toStrictEqual(expectedResponse);
+        });
+
+        describe('filterAccountChains', () => {
+          it('should send a request to filter the chains supported by an account and return the response', async () => {
+            const id = '49116980-0712-4fa5-b045-e4294f1d440e';
+            const expectedResponse = ['eip155:1', 'eip155:137'];
+
+            mockSender.send.mockResolvedValue(expectedResponse);
+            const account = await client.filterAccountChains(
+              '49116980-0712-4fa5-b045-e4294f1d440e',
+              ['eip155:1', 'eip155:137', 'other:chain'],
+            );
+            expect(mockSender.send).toHaveBeenCalledWith({
+              jsonrpc: '2.0',
+              id: expect.any(String),
+              method: 'keyring_filterAccountChains',
+              params: { id, chains: ['eip155:1', 'eip155:137', 'other:chain'] },
+            });
+            expect(account).toStrictEqual(expectedResponse);
+          });
+        });
+
+        describe('updateAccount', () => {
+          it('should send a request to update an account', async () => {
+            const account: KeyringAccount = {
+              id: '49116980-0712-4fa5-b045-e4294f1d440e',
+              address: '0xE9A74AACd7df8112911ca93260fC5a046f8a64Ae',
+              options: {},
+              methods: [],
+              scopes: ['eip155:0'],
+              type: 'eip155:eoa',
+            };
+
+            mockSender.send.mockResolvedValue(null);
+            const response = await client.updateAccount(account);
+            expect(mockSender.send).toHaveBeenCalledWith({
+              jsonrpc: '2.0',
+              id: expect.any(String),
+              method: 'keyring_updateAccount',
+              params: { account },
+            });
+            expect(response).toBeUndefined();
+          });
+        });
+
+        describe('deleteAccount', () => {
+          it('should send a request to delete an account', async () => {
+            const id = '49116980-0712-4fa5-b045-e4294f1d440e';
+
+            mockSender.send.mockResolvedValue(null);
+            const response = await client.deleteAccount(id);
+            expect(mockSender.send).toHaveBeenCalledWith({
+              jsonrpc: '2.0',
+              id: expect.any(String),
+              method: 'keyring_deleteAccount',
+              params: { id },
+            });
+            expect(response).toBeUndefined();
+          });
+        });
+
+        describe('exportAccount', () => {
+          it('should send a request to export an account', async () => {
+            const id = '49116980-0712-4fa5-b045-e4294f1d440e';
+            const expectedResponse = {
+              privateKey: '0x000000000',
+            };
+
+            mockSender.send.mockResolvedValue(expectedResponse);
+            const response = await client.exportAccount(id);
+            expect(mockSender.send).toHaveBeenCalledWith({
+              jsonrpc: '2.0',
+              id: expect.any(String),
+              method: 'keyring_exportAccount',
+              params: { id },
+            });
+            expect(response).toStrictEqual(expectedResponse);
+          });
+        });
+
+        describe('listRequests', () => {
+          it('should send a request to list requests and return the response', async () => {
+            const expectedResponse: KeyringRequest[] = [
+              {
+                id: '71621d8d-62a4-4bf4-97cc-fb8f243679b0',
+                scope: 'eip155:1',
+                origin: 'test',
+                account: '46b5ccd3-4786-427c-89d2-cef626dffe9b',
+                request: {
+                  method: 'personal_sign',
+                  params: ['0xe9a74aacd7df8112911ca93260fc5a046f8a64ae', '0x0'],
+                },
+              },
+            ];
+
+            mockSender.send.mockResolvedValue(expectedResponse);
+            const response = await client.listRequests();
+            expect(mockSender.send).toHaveBeenCalledWith({
+              jsonrpc: '2.0',
+              id: expect.any(String),
+              method: 'keyring_listRequests',
+            });
+            expect(response).toStrictEqual(expectedResponse);
+          });
+        });
+
+        describe('getRequest', () => {
+          it('should send a request to get a request and return the response', async () => {
+            const id = '71621d8d-62a4-4bf4-97cc-fb8f243679b0';
+            const expectedResponse: KeyringRequest = {
+              id,
+              scope: 'eip155:1',
+              origin: 'test',
+              account: '46b5ccd3-4786-427c-89d2-cef626dffe9b',
+              request: {
+                method: 'personal_sign',
+                params: ['0xe9a74aacd7df8112911ca93260fc5a046f8a64ae', '0x0'],
+              },
+            };
+
+            mockSender.send.mockResolvedValue(expectedResponse);
+            const response = await client.getRequest(id);
+            expect(mockSender.send).toHaveBeenCalledWith({
+              jsonrpc: '2.0',
+              id: expect.any(String),
+              method: 'keyring_getRequest',
+              params: { id },
+            });
+            expect(response).toStrictEqual(expectedResponse);
+          });
+        });
+
+        describe('approveRequest', () => {
+          it('should send a request to approve a request', async () => {
+            const id = '71621d8d-62a4-4bf4-97cc-fb8f243679b0';
+
+            mockSender.send.mockResolvedValue(null);
+            const response = await client.approveRequest(id);
+            expect(mockSender.send).toHaveBeenCalledWith({
+              jsonrpc: '2.0',
+              id: expect.any(String),
+              method: 'keyring_approveRequest',
+              params: { id, data: {} },
+            });
+            expect(response).toBeUndefined();
+          });
+        });
+
+        describe('rejectRequest', () => {
+          it('should send a request to approve a request', async () => {
+            const id = '71621d8d-62a4-4bf4-97cc-fb8f243679b0';
+
+            mockSender.send.mockResolvedValue(null);
+            const response = await client.rejectRequest(id);
+            expect(mockSender.send).toHaveBeenCalledWith({
+              jsonrpc: '2.0',
+              id: expect.any(String),
+              method: 'keyring_rejectRequest',
+              params: { id },
+            });
+            expect(response).toBeUndefined();
+          });
+        });
       });
-      expect(account).toStrictEqual(expectedResponse);
-    });
-  });
+    },
+  );
 
-  describe('createAccount', () => {
-    it('should send a request to create an account and return the response', async () => {
-      const expectedResponse: KeyringAccount = {
-        id: '49116980-0712-4fa5-b045-e4294f1d440e',
-        address: '0xE9A74AACd7df8112911ca93260fC5a046f8a64Ae',
-        options: {},
-        methods: [],
-        scopes: ['eip155:0'],
-        type: 'eip155:eoa',
-      };
+  // -----------------------------------------------------------------------------------------------
+  // Private/Internal methods (meant to be used by MetaMask)
 
-      mockSender.send.mockResolvedValue(expectedResponse);
-      const account = await keyring.createAccount();
-      expect(mockSender.send).toHaveBeenCalledWith({
-        jsonrpc: '2.0',
-        id: expect.any(String),
-        method: 'keyring_createAccount',
-        params: { options: {} },
-      });
-      expect(account).toStrictEqual(expectedResponse);
-    });
-  });
+  const client = keyringClient;
 
   describe('discoverAccounts', () => {
     const scopes = [BtcScope.Mainnet, BtcScope.Testnet];
@@ -103,7 +279,7 @@ describe('KeyringClient', () => {
       const expectedResponse: DiscoveredAccount[] = [];
 
       mockSender.send.mockResolvedValue(expectedResponse);
-      const accounts = await keyring.discoverAccounts(
+      const accounts = await client.discoverAccounts(
         scopes,
         entropySource,
         groupIndex,
@@ -130,10 +306,7 @@ describe('KeyringClient', () => {
 
       mockSender.send.mockResolvedValue(expectedResponse);
       const pagination = { limit: 10 };
-      const transactions = await keyring.listAccountTransactions(
-        id,
-        pagination,
-      );
+      const transactions = await client.listAccountTransactions(id, pagination);
 
       expect(mockSender.send).toHaveBeenCalledWith({
         jsonrpc: '2.0',
@@ -179,10 +352,7 @@ describe('KeyringClient', () => {
       };
 
       mockSender.send.mockResolvedValue(expectedResponse);
-      const transactions = await keyring.listAccountTransactions(
-        id,
-        pagination,
-      );
+      const transactions = await client.listAccountTransactions(id, pagination);
 
       expect(mockSender.send).toHaveBeenCalledWith({
         jsonrpc: '2.0',
@@ -228,10 +398,7 @@ describe('KeyringClient', () => {
       };
 
       mockSender.send.mockResolvedValue(expectedResponse);
-      const transactions = await keyring.listAccountTransactions(
-        id,
-        pagination,
-      );
+      const transactions = await client.listAccountTransactions(id, pagination);
 
       expect(mockSender.send).toHaveBeenCalledWith({
         jsonrpc: '2.0',
@@ -275,7 +442,7 @@ describe('KeyringClient', () => {
 
       mockSender.send.mockResolvedValue(expectedResponse);
       await expect(
-        keyring.listAccountTransactions(id, {
+        client.listAccountTransactions(id, {
           limit: 2,
         }),
       ).rejects.toThrow(
@@ -315,7 +482,7 @@ describe('KeyringClient', () => {
 
       mockSender.send.mockResolvedValue(expectedResponse);
       await expect(
-        keyring.listAccountTransactions(id, {
+        client.listAccountTransactions(id, {
           limit: 2,
         }),
       ).rejects.toThrow(
@@ -330,7 +497,7 @@ describe('KeyringClient', () => {
       const expectedResponse: CaipAssetTypeOrId[] = [];
 
       mockSender.send.mockResolvedValue(expectedResponse);
-      const assets = await keyring.listAccountAssets(id);
+      const assets = await client.listAccountAssets(id);
 
       expect(mockSender.send).toHaveBeenCalledWith({
         jsonrpc: '2.0',
@@ -354,7 +521,7 @@ describe('KeyringClient', () => {
       ];
 
       mockSender.send.mockResolvedValue(expectedResponse);
-      const assets = await keyring.listAccountAssets(id);
+      const assets = await client.listAccountAssets(id);
 
       expect(mockSender.send).toHaveBeenCalledWith({
         jsonrpc: '2.0',
@@ -371,7 +538,7 @@ describe('KeyringClient', () => {
       const expectedResponse = ['not:compliant'];
 
       mockSender.send.mockResolvedValue(expectedResponse);
-      await expect(keyring.listAccountAssets(id)).rejects.toThrow(
+      await expect(client.listAccountAssets(id)).rejects.toThrow(
         'At path: 0 -- Expected a value of type `CaipAssetTypeOrId`, but received: `"not:compliant"`',
       );
     });
@@ -391,7 +558,7 @@ describe('KeyringClient', () => {
       };
 
       mockSender.send.mockResolvedValue(expectedResponse);
-      const balances = await keyring.getAccountBalances(id, assets);
+      const balances = await client.getAccountBalances(id, assets);
 
       expect(mockSender.send).toHaveBeenCalledWith({
         jsonrpc: '2.0',
@@ -416,7 +583,7 @@ describe('KeyringClient', () => {
       };
 
       mockSender.send.mockResolvedValue(expectedResponse);
-      await expect(keyring.getAccountBalances(id, assets)).rejects.toThrow(
+      await expect(client.getAccountBalances(id, assets)).rejects.toThrow(
         'At path: bip122:000000000019d6689c085ae165831e93/slip44:0.amount -- Expected a value of type `StringNumber`, but received: `1234`',
       );
     });
@@ -434,7 +601,7 @@ describe('KeyringClient', () => {
       };
 
       mockSender.send.mockResolvedValue(expectedResponse);
-      await expect(keyring.getAccountBalances(id, assets)).rejects.toThrow(
+      await expect(client.getAccountBalances(id, assets)).rejects.toThrow(
         'At path: bip122:000000000019d6689c085ae165831e93/slip44:0.amount -- Expected a value of type `StringNumber`, but received: `"not-a-string-number"`',
       );
     });
@@ -460,7 +627,7 @@ describe('KeyringClient', () => {
       };
 
       mockSender.send.mockResolvedValue(expectedResponse);
-      const account = await keyring.resolveAccountAddress(scope, request);
+      const account = await client.resolveAccountAddress(scope, request);
       expect(mockSender.send).toHaveBeenCalledWith({
         jsonrpc: '2.0',
         id: expect.any(String),
@@ -477,7 +644,7 @@ describe('KeyringClient', () => {
       const expectedResponse = null;
 
       mockSender.send.mockResolvedValue(expectedResponse);
-      const account = await keyring.resolveAccountAddress(scope, request);
+      const account = await client.resolveAccountAddress(scope, request);
       expect(mockSender.send).toHaveBeenCalledWith({
         jsonrpc: '2.0',
         id: expect.any(String),
@@ -497,146 +664,19 @@ describe('KeyringClient', () => {
 
       mockSender.send.mockResolvedValue(expectedResponse);
       await expect(
-        keyring.resolveAccountAddress(scope, request),
+        client.resolveAccountAddress(scope, request),
       ).rejects.toThrow(
         'At path: address -- Expected a value of type `CaipAccountId`, but received: `undefined`',
       );
     });
   });
 
-  describe('filterAccountChains', () => {
-    it('should send a request to filter the chains supported by an account and return the response', async () => {
-      const id = '49116980-0712-4fa5-b045-e4294f1d440e';
-      const expectedResponse = ['eip155:1', 'eip155:137'];
-
-      mockSender.send.mockResolvedValue(expectedResponse);
-      const account = await keyring.filterAccountChains(
-        '49116980-0712-4fa5-b045-e4294f1d440e',
-        ['eip155:1', 'eip155:137', 'other:chain'],
-      );
-      expect(mockSender.send).toHaveBeenCalledWith({
-        jsonrpc: '2.0',
-        id: expect.any(String),
-        method: 'keyring_filterAccountChains',
-        params: { id, chains: ['eip155:1', 'eip155:137', 'other:chain'] },
-      });
-      expect(account).toStrictEqual(expectedResponse);
-    });
-  });
-
-  describe('updateAccount', () => {
-    it('should send a request to update an account', async () => {
-      const account: KeyringAccount = {
-        id: '49116980-0712-4fa5-b045-e4294f1d440e',
-        address: '0xE9A74AACd7df8112911ca93260fC5a046f8a64Ae',
-        options: {},
-        methods: [],
-        scopes: ['eip155:0'],
-        type: 'eip155:eoa',
-      };
-
-      mockSender.send.mockResolvedValue(null);
-      const response = await keyring.updateAccount(account);
-      expect(mockSender.send).toHaveBeenCalledWith({
-        jsonrpc: '2.0',
-        id: expect.any(String),
-        method: 'keyring_updateAccount',
-        params: { account },
-      });
-      expect(response).toBeUndefined();
-    });
-  });
-
-  describe('deleteAccount', () => {
-    it('should send a request to delete an account', async () => {
-      const id = '49116980-0712-4fa5-b045-e4294f1d440e';
-
-      mockSender.send.mockResolvedValue(null);
-      const response = await keyring.deleteAccount(id);
-      expect(mockSender.send).toHaveBeenCalledWith({
-        jsonrpc: '2.0',
-        id: expect.any(String),
-        method: 'keyring_deleteAccount',
-        params: { id },
-      });
-      expect(response).toBeUndefined();
-    });
-  });
-
-  describe('exportAccounts', () => {
-    it('should send a request to export an account', async () => {
-      const id = '49116980-0712-4fa5-b045-e4294f1d440e';
-      const expectedResponse = {
-        privateKey: '0x000000000',
-      };
-
-      mockSender.send.mockResolvedValue(expectedResponse);
-      const response = await keyring.exportAccount(id);
-      expect(mockSender.send).toHaveBeenCalledWith({
-        jsonrpc: '2.0',
-        id: expect.any(String),
-        method: 'keyring_exportAccount',
-        params: { id },
-      });
-      expect(response).toStrictEqual(expectedResponse);
-    });
-  });
-
-  describe('listRequests', () => {
-    it('should send a request to list requests and return the response', async () => {
-      const expectedResponse: KeyringRequest[] = [
-        {
-          id: '71621d8d-62a4-4bf4-97cc-fb8f243679b0',
-          scope: 'eip155:1',
-          account: '46b5ccd3-4786-427c-89d2-cef626dffe9b',
-          request: {
-            method: 'personal_sign',
-            params: ['0xe9a74aacd7df8112911ca93260fc5a046f8a64ae', '0x0'],
-          },
-        },
-      ];
-
-      mockSender.send.mockResolvedValue(expectedResponse);
-      const response = await keyring.listRequests();
-      expect(mockSender.send).toHaveBeenCalledWith({
-        jsonrpc: '2.0',
-        id: expect.any(String),
-        method: 'keyring_listRequests',
-      });
-      expect(response).toStrictEqual(expectedResponse);
-    });
-  });
-
-  describe('getRequest', () => {
-    it('should send a request to get a request and return the response', async () => {
-      const id = '71621d8d-62a4-4bf4-97cc-fb8f243679b0';
-      const expectedResponse: KeyringRequest = {
-        id,
-        scope: 'eip155:1',
-        account: '46b5ccd3-4786-427c-89d2-cef626dffe9b',
-        request: {
-          method: 'personal_sign',
-          params: ['0xe9a74aacd7df8112911ca93260fc5a046f8a64ae', '0x0'],
-        },
-      };
-
-      mockSender.send.mockResolvedValue(expectedResponse);
-      const response = await keyring.getRequest(id);
-      expect(mockSender.send).toHaveBeenCalledWith({
-        jsonrpc: '2.0',
-        id: expect.any(String),
-        method: 'keyring_getRequest',
-        params: { id },
-      });
-      expect(response).toStrictEqual(expectedResponse);
-    });
-  });
-
   describe('submitRequest', () => {
     it('should send a request to submit a request', async () => {
       const request: KeyringRequest = {
         id: '71621d8d-62a4-4bf4-97cc-fb8f243679b0',
         scope: 'eip155:1',
+        origin: 'test',
         account: '46b5ccd3-4786-427c-89d2-cef626dffe9b',
         request: {
           method: 'personal_sign',
@@ -652,7 +692,7 @@ describe('KeyringClient', () => {
       };
 
       mockSender.send.mockResolvedValue(expectedResponse);
-      const response = await keyring.submitRequest(request);
+      const response = await client.submitRequest(request);
       expect(mockSender.send).toHaveBeenCalledWith({
         jsonrpc: '2.0',
         id: expect.any(String),
@@ -662,36 +702,4 @@ describe('KeyringClient', () => {
       expect(response).toStrictEqual(expectedResponse);
     });
   });
-
-  describe('approveRequest', () => {
-    it('should send a request to approve a request', async () => {
-      const id = '71621d8d-62a4-4bf4-97cc-fb8f243679b0';
-
-      mockSender.send.mockResolvedValue(null);
-      const response = await keyring.approveRequest(id);
-      expect(mockSender.send).toHaveBeenCalledWith({
-        jsonrpc: '2.0',
-        id: expect.any(String),
-        method: 'keyring_approveRequest',
-        params: { id, data: {} },
-      });
-      expect(response).toBeUndefined();
-    });
-  });
-
-  describe('rejectRequest', () => {
-    it('should send a request to approve a request', async () => {
-      const id = '71621d8d-62a4-4bf4-97cc-fb8f243679b0';
-
-      mockSender.send.mockResolvedValue(null);
-      const response = await keyring.rejectRequest(id);
-      expect(mockSender.send).toHaveBeenCalledWith({
-        jsonrpc: '2.0',
-        id: expect.any(String),
-        method: 'keyring_rejectRequest',
-        params: { id },
-      });
-      expect(response).toBeUndefined();
-    });
-  });
 });
diff --git a/packages/keyring-snap-client/src/KeyringClient.ts b/packages/keyring-snap-client/src/KeyringClient.ts
index 8725fe873..82007dc83 100644
--- a/packages/keyring-snap-client/src/KeyringClient.ts
+++ b/packages/keyring-snap-client/src/KeyringClient.ts
@@ -62,7 +62,9 @@ export class KeyringClient implements Keyring {
    * @param partial - A partial JSON-RPC request (method and params).
    * @returns A promise that resolves to the response to the request.
    */
-  async #send(partial: Omit<JsonRpcRequest, 'jsonrpc' | 'id'>): Promise<Json> {
+  protected async send(
+    partial: Omit<JsonRpcRequest, 'jsonrpc' | 'id'>,
+  ): Promise<Json> {
     return this.#sender.send({
       jsonrpc: '2.0',
       id: uuid(),
@@ -72,7 +74,7 @@ export class KeyringClient implements Keyring {
 
   async listAccounts(): Promise<KeyringAccount[]> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.ListAccounts,
       }),
       ListAccountsResponseStruct,
@@ -81,7 +83,7 @@ export class KeyringClient implements Keyring {
 
   async getAccount(id: string): Promise<KeyringAccount> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.GetAccount,
         params: { id },
       }),
@@ -94,7 +96,7 @@ export class KeyringClient implements Keyring {
     assets: CaipAssetType[],
   ): Promise<Record<CaipAssetType, Balance>> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.GetAccountBalances,
         params: { id, assets },
       }),
@@ -106,7 +108,7 @@ export class KeyringClient implements Keyring {
     options: Record<string, Json> = {},
   ): Promise<KeyringAccount> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.CreateAccount,
         params: { options },
       }),
@@ -120,7 +122,7 @@ export class KeyringClient implements Keyring {
     groupIndex: number,
   ): Promise<DiscoveredAccount[]> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.DiscoverAccounts,
         params: { scopes, entropySource, groupIndex },
       }),
@@ -133,7 +135,7 @@ export class KeyringClient implements Keyring {
     pagination: Pagination,
   ): Promise<TransactionsPage> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.ListAccountTransactions,
         params: { id, pagination },
       }),
@@ -143,7 +145,7 @@ export class KeyringClient implements Keyring {
 
   async listAccountAssets(id: string): Promise<CaipAssetTypeOrId[]> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.ListAccountAssets,
         params: { id },
       }),
@@ -158,7 +160,7 @@ export class KeyringClient implements Keyring {
     // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
   ): Promise<ResolvedAccountAddress | null> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.ResolveAccountAddress,
         params: { scope, request },
       }),
@@ -168,7 +170,7 @@ export class KeyringClient implements Keyring {
 
   async filterAccountChains(id: string, chains: string[]): Promise<string[]> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.FilterAccountChains,
         params: { id, chains },
       }),
@@ -178,7 +180,7 @@ export class KeyringClient implements Keyring {
 
   async updateAccount(account: KeyringAccount): Promise<void> {
     assert(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.UpdateAccount,
         params: { account },
       }),
@@ -188,7 +190,7 @@ export class KeyringClient implements Keyring {
 
   async deleteAccount(id: string): Promise<void> {
     assert(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.DeleteAccount,
         params: { id },
       }),
@@ -198,7 +200,7 @@ export class KeyringClient implements Keyring {
 
   async exportAccount(id: string): Promise<KeyringAccountData> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.ExportAccount,
         params: { id },
       }),
@@ -208,7 +210,7 @@ export class KeyringClient implements Keyring {
 
   async listRequests(): Promise<KeyringRequest[]> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.ListRequests,
       }),
       ListRequestsResponseStruct,
@@ -217,7 +219,7 @@ export class KeyringClient implements Keyring {
 
   async getRequest(id: string): Promise<KeyringRequest> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.GetRequest,
         params: { id },
       }),
@@ -227,7 +229,7 @@ export class KeyringClient implements Keyring {
 
   async submitRequest(request: KeyringRequest): Promise<KeyringResponse> {
     return strictMask(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.SubmitRequest,
         params: request,
       }),
@@ -240,7 +242,7 @@ export class KeyringClient implements Keyring {
     data: Record<string, Json> = {},
   ): Promise<void> {
     assert(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.ApproveRequest,
         params: { id, data },
       }),
@@ -250,7 +252,7 @@ export class KeyringClient implements Keyring {
 
   async rejectRequest(id: string): Promise<void> {
     assert(
-      await this.#send({
+      await this.send({
         method: KeyringRpcMethod.RejectRequest,
         params: { id },
       }),
diff --git a/packages/keyring-snap-client/src/KeyringPublicClient.test.ts b/packages/keyring-snap-client/src/KeyringPublicClient.test.ts
new file mode 100644
index 000000000..99954cdd3
--- /dev/null
+++ b/packages/keyring-snap-client/src/KeyringPublicClient.test.ts
@@ -0,0 +1,15 @@
+import { KeyringPublicClient } from '.';
+
+describe('KeyringPublicCient', () => {
+  // IMPORTANT:
+  // See `KeyringClient.test.ts` for additional tests.
+
+  it('can be constructed', () => {
+    const mockSender = {
+      send: jest.fn(),
+    };
+
+    const client = new KeyringPublicClient(mockSender);
+    expect(client).toBeDefined();
+  });
+});
diff --git a/packages/keyring-snap-client/src/KeyringPublicClient.ts b/packages/keyring-snap-client/src/KeyringPublicClient.ts
new file mode 100644
index 000000000..b197c06d8
--- /dev/null
+++ b/packages/keyring-snap-client/src/KeyringPublicClient.ts
@@ -0,0 +1,108 @@
+import type {
+  KeyringAccount,
+  KeyringAccountData,
+  KeyringRequest,
+} from '@metamask/keyring-api';
+import { KeyringRpcMethod } from '@metamask/keyring-api';
+import type { Json } from '@metamask/utils';
+
+import type { Sender } from './KeyringClient';
+import { KeyringClient } from './KeyringClient';
+
+/**
+ * Convert a tuple to a union.
+ */
+type TupleToUnion<Tuple extends readonly unknown[]> = Tuple[number];
+
+/**
+ * Extract the method name from a RPC method name.
+ */
+type KeyringRpcMethodToMethodName<RpcMethod extends string> =
+  RpcMethod extends `keyring_${infer Method}` ? Method : never;
+
+/**
+ * Public methods list.
+ */
+export const KeyringPublicRpcMethod = [
+  KeyringRpcMethod.CreateAccount,
+  KeyringRpcMethod.GetAccount,
+  KeyringRpcMethod.UpdateAccount,
+  KeyringRpcMethod.DeleteAccount,
+  KeyringRpcMethod.ExportAccount,
+  KeyringRpcMethod.ListAccounts,
+  KeyringRpcMethod.FilterAccountChains,
+  KeyringRpcMethod.GetRequest,
+  KeyringRpcMethod.ApproveRequest,
+  KeyringRpcMethod.RejectRequest,
+  KeyringRpcMethod.ListRequests,
+] as const;
+
+/**
+ * Union of all public methods.
+ */
+type KeyringPublicMethod = KeyringRpcMethodToMethodName<
+  TupleToUnion<typeof KeyringPublicRpcMethod>
+>;
+
+/**
+ * A client that can be used by companion dapp. Only some keyring methods are
+ * available with this client.
+ */
+export class KeyringPublicClient
+  implements Pick<KeyringClient, KeyringPublicMethod>
+{
+  readonly #client: KeyringClient;
+
+  /**
+   * Create a new instance of `KeyringPublicClient`.
+   *
+   * @param sender - The `Sender` instance to use to send requests to the snap.
+   */
+  constructor(sender: Sender) {
+    this.#client = new KeyringClient(sender);
+  }
+
+  async createAccount(options?: Record<string, Json>): Promise<KeyringAccount> {
+    return this.#client.createAccount(options);
+  }
+
+  async deleteAccount(id: string): Promise<void> {
+    return this.#client.deleteAccount(id);
+  }
+
+  async listAccounts(): Promise<KeyringAccount[]> {
+    return this.#client.listAccounts();
+  }
+
+  async getAccount(id: string): Promise<KeyringAccount> {
+    return this.#client.getAccount(id);
+  }
+
+  async getRequest(id: string): Promise<KeyringRequest> {
+    return this.#client.getRequest(id);
+  }
+
+  async listRequests(): Promise<KeyringRequest[]> {
+    return this.#client.listRequests();
+  }
+
+  async updateAccount(account: KeyringAccount): Promise<void> {
+    return this.#client.updateAccount(account);
+  }
+
+  async approveRequest(id: string, data?: Record<string, Json>): Promise<void> {
+    return this.#client.approveRequest(id, data);
+  }
+
+  async rejectRequest(id: string): Promise<void> {
+    return this.#client.rejectRequest(id);
+  }
+
+  async filterAccountChains(id: string, chains: string[]): Promise<string[]> {
+    return this.#client.filterAccountChains(id, chains);
+  }
+
+  async exportAccount(id: string): Promise<KeyringAccountData> {
+    return this.#client.exportAccount(id);
+  }
+}
diff --git a/packages/keyring-snap-client/src/KeyringSnapRpcClient.ts b/packages/keyring-snap-client/src/KeyringSnapRpcClient.ts
index b70019ffa..b041a6af0 100644
--- a/packages/keyring-snap-client/src/KeyringSnapRpcClient.ts
+++ b/packages/keyring-snap-client/src/KeyringSnapRpcClient.ts
@@ -3,7 +3,7 @@ import type { MetaMaskInpageProvider } from '@metamask/providers';
 import type { Json } from '@metamask/utils';
 
 import type { Sender } from './KeyringClient';
-import { KeyringClient } from './KeyringClient';
+import { KeyringPublicClient } from './KeyringPublicClient';
 
 /**
  * Implementation of the `Sender` interface that can be used to send requests
@@ -43,10 +43,10 @@ export class SnapRpcSender implements Sender {
 }
 
 /**
- * A `KeyringClient` that allows the communication with a snap through the snap
- * JSON-RPC API.
+ * A client that allows the communication with a snap through the snap
+ * JSON-RPC API to call keyring methods.
  */
-export class KeyringSnapRpcClient extends KeyringClient {
+export class KeyringSnapRpcClient extends KeyringPublicClient {
   /**
    * Create a new instance of `KeyringSnapRpcClient`.
    *
diff --git a/packages/keyring-snap-client/src/index.ts b/packages/keyring-snap-client/src/index.ts
index a2792ea97..3e845b705 100644
--- a/packages/keyring-snap-client/src/index.ts
+++ b/packages/keyring-snap-client/src/index.ts
@@ -1,2 +1,3 @@
 export * from './KeyringClient';
 export * from './KeyringSnapRpcClient';
+export * from './KeyringPublicClient';
diff --git a/packages/keyring-snap-sdk/package.json b/packages/keyring-snap-sdk/package.json
index 0351e58d9..6e15b0b6d 100644
--- a/packages/keyring-snap-sdk/package.json
+++ b/packages/keyring-snap-sdk/package.json
@@ -46,7 +46,7 @@
   },
   "dependencies": {
     "@metamask/keyring-utils": "workspace:^",
-    "@metamask/snaps-sdk": "^6.16.0",
+    "@metamask/snaps-sdk": "^7.0.0",
     "@metamask/superstruct": "^3.1.0",
     "@metamask/utils": "^11.1.0",
     "webextension-polyfill": "^0.12.0"
diff --git a/packages/keyring-snap-sdk/src/rpc-handler.test.ts b/packages/keyring-snap-sdk/src/rpc-handler.test.ts
index 1d967a162..60aee2dd4 100644
--- a/packages/keyring-snap-sdk/src/rpc-handler.test.ts
+++ b/packages/keyring-snap-sdk/src/rpc-handler.test.ts
@@ -496,6 +496,7 @@ describe('handleKeyringRequest', () => {
       id: 'c555de37-cf4b-4ff2-8273-39db7fb58f1c',
       scope: 'eip155:1',
       account: '4abdd17e-8b0f-4d06-a017-947a64823b3d',
+      origin: 'metamask',
       request: {
         method: 'eth_method',
         params: [1, 2, 3],
diff --git a/yarn.lock b/yarn.lock
index 52b5edcca..efc653c1d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -502,10 +502,10 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@endo/env-options@npm:^1.1.5":
-  version: 1.1.5
-  resolution: "@endo/env-options@npm:1.1.5"
-  checksum: 10/ce4cb29ecf387f52f7d1c9e7e43b0a1064326587ebac62e7c239bf2df71aa4c3296d2a05cf169d1efcd8c1ddf73aeede8afd86e7b5c9387b80e8e0939d1af0f6
+"@endo/env-options@npm:^1.1.8":
+  version: 1.1.8
+  resolution: "@endo/env-options@npm:1.1.8"
+  checksum: 10/f7e84346599dd2bcb6365c314e9a8129c5ebbb457476de72ed896ea461d616c0b7e0dfc7733e20c0abb8400212fb5eafdae993bcfd4cbfe92acbb5c881a6ad0d
   languageName: node
   linkType: hard
 
@@ -1468,15 +1468,15 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@metamask/approval-controller@npm:^7.1.2":
-  version: 7.1.2
-  resolution: "@metamask/approval-controller@npm:7.1.2"
+"@metamask/approval-controller@npm:^7.1.3":
+  version: 7.1.3
+  resolution: "@metamask/approval-controller@npm:7.1.3"
   dependencies:
-    "@metamask/base-controller": "npm:^7.1.1"
+    "@metamask/base-controller": "npm:^8.0.0"
     "@metamask/rpc-errors": "npm:^7.0.2"
-    "@metamask/utils": "npm:^11.0.1"
+    "@metamask/utils": "npm:^11.1.0"
     nanoid: "npm:^3.3.8"
-  checksum: 10/e5903e8c3799484a3f42b2683ed733e68aca5204070c84f6012ee3830b04ebffdc7fc21fe80d2ea46f2a9ee3557a38e4031186f47e84d9079c2c902361543b0d
+  checksum: 10/2d88378dcc4f6c32ad544766cf26b7f59b1aee1d7ca0909b966c8c08eaac1d7109004120ca254d7ad008a6e5241b144d4dd831f926ec928a66f93cbd2e6f1693
   languageName: node
   linkType: hard
 
@@ -1510,7 +1510,7 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@metamask/base-controller@npm:^7.0.2, @metamask/base-controller@npm:^7.0.3, @metamask/base-controller@npm:^7.1.1":
+"@metamask/base-controller@npm:^7.1.1":
   version: 7.1.1
   resolution: "@metamask/base-controller@npm:7.1.1"
   dependencies:
@@ -1520,6 +1520,16 @@ __metadata:
   languageName: node
   linkType: hard
 
+"@metamask/base-controller@npm:^8.0.0, @metamask/base-controller@npm:^8.0.1":
+  version: 8.0.1
+  resolution: "@metamask/base-controller@npm:8.0.1"
+  dependencies:
+    "@metamask/utils": "npm:^11.2.0"
+    immer: "npm:^9.0.6"
+  checksum: 10/5ef02099ce2e2246c534a7742b45704417beebf2c21db70241d09c3ddbb549ff3375284ece00edf4051029facff181e5e05f135e97b943ec6c514eecce4fa37a
+  languageName: node
+  linkType: hard
+
 "@metamask/bip39@npm:^4.0.0":
   version: 4.0.0
   resolution: "@metamask/bip39@npm:4.0.0"
@@ -1532,23 +1542,24 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@metamask/controller-utils@npm:^11.4.4, @metamask/controller-utils@npm:^11.4.5":
-  version: 11.4.5
-  resolution: "@metamask/controller-utils@npm:11.4.5"
+"@metamask/controller-utils@npm:^11.5.0, @metamask/controller-utils@npm:^11.7.0":
+  version: 11.9.0
+  resolution: "@metamask/controller-utils@npm:11.9.0"
   dependencies:
-    "@ethereumjs/util": "npm:^8.1.0"
+    "@ethereumjs/util": "npm:^9.1.0"
     "@metamask/eth-query": "npm:^4.0.0"
     "@metamask/ethjs-unit": "npm:^0.3.0"
-    "@metamask/utils": "npm:^11.0.1"
+    "@metamask/utils": "npm:^11.2.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"
+    cockatiel: "npm:^3.1.2"
     eth-ens-namehash: "npm:^2.0.8"
     fast-deep-equal: "npm:^3.1.3"
   peerDependencies:
     "@babel/runtime": ^7.0.0
-  checksum: 10/28c637197b569c437be116961a94f59f1476439484042f59b24573d70cdc575ba5ccc614d7062388945461c9c5af319a6004a0d98a07cadd3fa3fa623adb688d
+  checksum: 10/90173f27ec476e03141db4bfeb3264064f17564562cb24d1585a9002cb0a8e2edd3db4b29ee70a0554f3395496c8656ecf64324672e3bd3a27a1ddde855ce35c
   languageName: node
   linkType: hard
 
@@ -1769,9 +1780,9 @@ __metadata:
     "@metamask/keyring-internal-api": "workspace:^"
     "@metamask/keyring-internal-snap-client": "workspace:^"
     "@metamask/keyring-utils": "workspace:^"
-    "@metamask/snaps-controllers": "npm:^9.18.0"
-    "@metamask/snaps-sdk": "npm:^6.16.0"
-    "@metamask/snaps-utils": "npm:^8.9.1"
+    "@metamask/snaps-controllers": "npm:^12.2.0"
+    "@metamask/snaps-sdk": "npm:^7.0.0"
+    "@metamask/snaps-utils": "npm:^9.3.0"
     "@metamask/superstruct": "npm:^3.1.0"
     "@metamask/utils": "npm:^11.1.0"
     "@ts-bridge/cli": "npm:^0.6.3"
@@ -1843,39 +1854,39 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@metamask/json-rpc-engine@npm:^10.0.2":
-  version: 10.0.2
-  resolution: "@metamask/json-rpc-engine@npm:10.0.2"
+"@metamask/json-rpc-engine@npm:^10.0.2, @metamask/json-rpc-engine@npm:^10.0.3":
+  version: 10.0.3
+  resolution: "@metamask/json-rpc-engine@npm:10.0.3"
   dependencies:
     "@metamask/rpc-errors": "npm:^7.0.2"
     "@metamask/safe-event-emitter": "npm:^3.0.0"
-    "@metamask/utils": "npm:^11.0.1"
-  checksum: 10/479e4c36ee10ecaa9b26bf8aaea375f7dbe68b5379fabc0f78ac087e310d0040b0e7a2d55eccebd820089404a2170f498c4e2b82eb7f0d34c5becbd811340d49
+    "@metamask/utils": "npm:^11.1.0"
+  checksum: 10/0558f511aada9bfb13d3b55f6a834543431cc6148a681d3a2885f6171fefbcf092ea4aabc7bbb547de6fdf382cdaf6a73ca5175c63c2d1b6560f763b4b37162e
   languageName: node
   linkType: hard
 
-"@metamask/json-rpc-middleware-stream@npm:^8.0.6":
-  version: 8.0.6
-  resolution: "@metamask/json-rpc-middleware-stream@npm:8.0.6"
+"@metamask/json-rpc-middleware-stream@npm:^8.0.6, @metamask/json-rpc-middleware-stream@npm:^8.0.7":
+  version: 8.0.7
+  resolution: "@metamask/json-rpc-middleware-stream@npm:8.0.7"
   dependencies:
-    "@metamask/json-rpc-engine": "npm:^10.0.2"
+    "@metamask/json-rpc-engine": "npm:^10.0.3"
     "@metamask/safe-event-emitter": "npm:^3.0.0"
-    "@metamask/utils": "npm:^11.0.1"
+    "@metamask/utils": "npm:^11.1.0"
     readable-stream: "npm:^3.6.2"
-  checksum: 10/4df2ddf068ee935b5ea29b833df243ee43e0a17ea0151bc312d4eaeec541612f7416761be2b66f316c0b12f577f0257831b83844f6b9addbaf5fe9d9c5638262
+  checksum: 10/54dadd16876ad1637b1fccd0d35c66dcc9a03d8614814d78b6730f9bb325e80ad1fdbb99d5b75785245aadf4064417ac35bc710a5549d13128f12153d0cc0432
   languageName: node
   linkType: hard
 
-"@metamask/key-tree@npm:^10.0.2":
-  version: 10.0.2
-  resolution: "@metamask/key-tree@npm:10.0.2"
+"@metamask/key-tree@npm:^10.0.2, @metamask/key-tree@npm:^10.1.1":
+  version: 10.1.1
+  resolution: "@metamask/key-tree@npm:10.1.1"
   dependencies:
     "@metamask/scure-bip39": "npm:^2.1.1"
     "@metamask/utils": "npm:^11.0.1"
-    "@noble/curves": "npm:^1.2.0"
+    "@noble/curves": "npm:^1.8.1"
     "@noble/hashes": "npm:^1.3.2"
     "@scure/base": "npm:^1.0.0"
-  checksum: 10/fd2e445c75dc3cd3976fdc38a5029ee71a6f4afcbbf5c9a17152bba70cf35df8095caa853ae62eef90a51b43f23eeb9546fc6eb7d93a099d82effe8dc7592259
+  checksum: 10/29b2db7f2626414f6147e6a25aae16b1a012485aa394fb6ad2b3f26519455dae7e6e6fdd502f279e1924251b7058a853982297f37761372ed034db5f150fc720
   languageName: node
   linkType: hard
 
@@ -1941,11 +1952,12 @@ __metadata:
     "@metamask/auto-changelog": "npm:^3.4.4"
     "@metamask/base-controller": "npm:^7.1.1"
     "@metamask/keyring-api": "workspace:^"
+    "@metamask/keyring-internal-api": "workspace:^"
     "@metamask/keyring-snap-client": "workspace:^"
     "@metamask/keyring-utils": "workspace:^"
-    "@metamask/snaps-controllers": "npm:^9.18.0"
-    "@metamask/snaps-sdk": "npm:^6.16.0"
-    "@metamask/snaps-utils": "npm:^8.9.1"
+    "@metamask/snaps-controllers": "npm:^12.2.0"
+    "@metamask/snaps-sdk": "npm:^7.0.0"
+    "@metamask/snaps-utils": "npm:^9.3.0"
     "@metamask/utils": "npm:^11.1.0"
     "@ts-bridge/cli": "npm:^0.6.3"
     "@types/jest": "npm:^29.5.12"
@@ -2006,7 +2018,7 @@ __metadata:
     "@metamask/keyring-api": "workspace:^"
     "@metamask/keyring-utils": "workspace:^"
     "@metamask/providers": "npm:^19.0.0"
-    "@metamask/snaps-sdk": "npm:^6.16.0"
+    "@metamask/snaps-sdk": "npm:^7.0.0"
     "@metamask/superstruct": "npm:^3.1.0"
     "@metamask/utils": "npm:^11.1.0"
     "@ts-bridge/cli": "npm:^0.6.3"
@@ -2076,53 +2088,53 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@metamask/permission-controller@npm:^11.0.5":
-  version: 11.0.5
-  resolution: "@metamask/permission-controller@npm:11.0.5"
+"@metamask/permission-controller@npm:^11.0.6":
+  version: 11.0.6
+  resolution: "@metamask/permission-controller@npm:11.0.6"
   dependencies:
-    "@metamask/base-controller": "npm:^7.1.1"
-    "@metamask/controller-utils": "npm:^11.4.5"
-    "@metamask/json-rpc-engine": "npm:^10.0.2"
+    "@metamask/base-controller": "npm:^8.0.0"
+    "@metamask/controller-utils": "npm:^11.5.0"
+    "@metamask/json-rpc-engine": "npm:^10.0.3"
     "@metamask/rpc-errors": "npm:^7.0.2"
-    "@metamask/utils": "npm:^11.0.1"
+    "@metamask/utils": "npm:^11.1.0"
     "@types/deep-freeze-strict": "npm:^1.1.0"
     deep-freeze-strict: "npm:^1.1.1"
     immer: "npm:^9.0.6"
     nanoid: "npm:^3.3.8"
   peerDependencies:
     "@metamask/approval-controller": ^7.0.0
-  checksum: 10/e592f5da0a2efdf17f7d7a15e0acea5000f7c40ca6b97ab295d15e59c3d7950a0ec388f927ded8ec15f04d75393ba28f8b4858ed2c1da963a92dc360451d5d9a
+  checksum: 10/d1497033738a2c4d84cf5d884e4eea65593d08539cc0592429db101852d11c46fd9a0a7ae1649daee2f01148a15bcdfda77ce4e300a3a9e2c7955dcca6142f9f
   languageName: node
   linkType: hard
 
-"@metamask/phishing-controller@npm:^12.3.1":
-  version: 12.3.1
-  resolution: "@metamask/phishing-controller@npm:12.3.1"
+"@metamask/phishing-controller@npm:^12.5.0":
+  version: 12.5.0
+  resolution: "@metamask/phishing-controller@npm:12.5.0"
   dependencies:
-    "@metamask/base-controller": "npm:^7.0.2"
-    "@metamask/controller-utils": "npm:^11.4.4"
+    "@metamask/base-controller": "npm:^8.0.0"
+    "@metamask/controller-utils": "npm:^11.7.0"
     "@noble/hashes": "npm:^1.4.0"
     "@types/punycode": "npm:^2.1.0"
     ethereum-cryptography: "npm:^2.1.2"
     fastest-levenshtein: "npm:^1.0.16"
     punycode: "npm:^2.1.1"
-  checksum: 10/b8060e7277239ea5ebeddb7589e26b7fb49114b8dc4b48e72393ea0efae28ff3eff17160585429ef33e9022ea146637c78b7cea6d927d62708dfdc0c0c544d5d
+  checksum: 10/bcee86c7b4423c40564dcc1bc8e77dffd6ed70bf6f8507c4c69a6695f49ecb86868dc90f19b9d58f57e64cad90289fbec88ecf67e0ea54ff88c77f7ea7cfac11
   languageName: node
   linkType: hard
 
-"@metamask/post-message-stream@npm:^9.0.0":
-  version: 9.0.0
-  resolution: "@metamask/post-message-stream@npm:9.0.0"
+"@metamask/post-message-stream@npm:^10.0.0":
+  version: 10.0.0
+  resolution: "@metamask/post-message-stream@npm:10.0.0"
   dependencies:
-    "@metamask/utils": "npm:^11.0.1"
+    "@metamask/utils": "npm:^11.4.0"
     readable-stream: "npm:3.6.2"
-  checksum: 10/5da711d3274e724452322939a5a77c60ed1d7ed73cdaa62e95c16debc443804d5a16de116dce742e05b3fbfa962e009dfeafc3a12a66f20e163617567f2cace5
+  checksum: 10/7892b30e6107b662680dfba75c078ac925c9f45bf1f90a0c86035f206a6305ddf903086a02b08e6fe9aec9ec32a0fecd2ff31941d5961d45ee782c07993846c5
   languageName: node
   linkType: hard
 
-"@metamask/providers@npm:^18.3.1":
-  version: 18.3.1
-  resolution: "@metamask/providers@npm:18.3.1"
+"@metamask/providers@npm:^19.0.0":
+  version: 19.0.0
+  resolution: "@metamask/providers@npm:19.0.0"
   dependencies:
     "@metamask/json-rpc-engine": "npm:^10.0.2"
     "@metamask/json-rpc-middleware-stream": "npm:^8.0.6"
@@ -2137,13 +2149,13 @@ __metadata:
     readable-stream: "npm:^3.6.2"
   peerDependencies:
     webextension-polyfill: ^0.10.0 || ^0.11.0 || ^0.12.0
-  checksum: 10/0e21ba9cce926a49dedbfe30fc964cd2349ee6bf9156f525fb894dcbc147a3ae480384884131a6b1a0a508989b547d8c8d2aeb3d10e11f67a8ee5230c45631a8
+  checksum: 10/e45b2e90aa45db689bed582e85181d6964daff3c4b651ffc8503edcec2206a1b76677c0ae6fc0b29cef37a1b33775545e73bd1cf62802a4fc5e2841973cdafb6
   languageName: node
   linkType: hard
 
-"@metamask/providers@npm:^19.0.0":
-  version: 19.0.0
-  resolution: "@metamask/providers@npm:19.0.0"
+"@metamask/providers@npm:^22.1.0":
+  version: 22.1.0
+  resolution: "@metamask/providers@npm:22.1.0"
   dependencies:
     "@metamask/json-rpc-engine": "npm:^10.0.2"
     "@metamask/json-rpc-middleware-stream": "npm:^8.0.6"
@@ -2158,7 +2170,7 @@ __metadata:
     readable-stream: "npm:^3.6.2"
   peerDependencies:
     webextension-polyfill: ^0.10.0 || ^0.11.0 || ^0.12.0
-  checksum: 10/e45b2e90aa45db689bed582e85181d6964daff3c4b651ffc8503edcec2206a1b76677c0ae6fc0b29cef37a1b33775545e73bd1cf62802a4fc5e2841973cdafb6
+  checksum: 10/d6dc969296e3d478a904228f27adae3b6dcbfdbf49eb6d571c9d73d7506df3c6e3bf3c3464f1e69e4c0acb6f6072d12d1c8182348e626ca1572f4f22f9c585e6
   languageName: node
   linkType: hard
 
@@ -2196,25 +2208,25 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@metamask/snaps-controllers@npm:^9.18.0":
-  version: 9.18.0
-  resolution: "@metamask/snaps-controllers@npm:9.18.0"
+"@metamask/snaps-controllers@npm:^12.2.0":
+  version: 12.2.0
+  resolution: "@metamask/snaps-controllers@npm:12.2.0"
   dependencies:
-    "@metamask/approval-controller": "npm:^7.1.2"
-    "@metamask/base-controller": "npm:^7.0.3"
+    "@metamask/approval-controller": "npm:^7.1.3"
+    "@metamask/base-controller": "npm:^8.0.1"
     "@metamask/json-rpc-engine": "npm:^10.0.2"
-    "@metamask/json-rpc-middleware-stream": "npm:^8.0.6"
-    "@metamask/key-tree": "npm:^10.0.2"
+    "@metamask/json-rpc-middleware-stream": "npm:^8.0.7"
+    "@metamask/key-tree": "npm:^10.1.1"
     "@metamask/object-multiplex": "npm:^2.1.0"
-    "@metamask/permission-controller": "npm:^11.0.5"
-    "@metamask/phishing-controller": "npm:^12.3.1"
-    "@metamask/post-message-stream": "npm:^9.0.0"
+    "@metamask/permission-controller": "npm:^11.0.6"
+    "@metamask/phishing-controller": "npm:^12.5.0"
+    "@metamask/post-message-stream": "npm:^10.0.0"
     "@metamask/rpc-errors": "npm:^7.0.2"
     "@metamask/snaps-registry": "npm:^3.2.3"
-    "@metamask/snaps-rpc-methods": "npm:^11.10.0"
-    "@metamask/snaps-sdk": "npm:^6.16.0"
-    "@metamask/snaps-utils": "npm:^8.9.0"
-    "@metamask/utils": "npm:^11.0.1"
+    "@metamask/snaps-rpc-methods": "npm:^12.3.0"
+    "@metamask/snaps-sdk": "npm:^6.24.0"
+    "@metamask/snaps-utils": "npm:^9.3.0"
+    "@metamask/utils": "npm:^11.4.0"
     "@xstate/fsm": "npm:^2.0.0"
     async-mutex: "npm:^0.5.0"
     browserify-zlib: "npm:^0.2.0"
@@ -2223,17 +2235,17 @@ __metadata:
     get-npm-tarball-url: "npm:^2.0.3"
     immer: "npm:^9.0.6"
     luxon: "npm:^3.5.0"
-    nanoid: "npm:^3.1.31"
+    nanoid: "npm:^3.3.10"
     readable-stream: "npm:^3.6.2"
     readable-web-to-node-stream: "npm:^3.0.2"
     semver: "npm:^7.5.4"
     tar-stream: "npm:^3.1.7"
   peerDependencies:
-    "@metamask/snaps-execution-environments": ^6.13.0
+    "@metamask/snaps-execution-environments": ^8.1.0
   peerDependenciesMeta:
     "@metamask/snaps-execution-environments":
       optional: true
-  checksum: 10/a8691243fe69a4bd7b39074571950cb9ce01b4d8ce129925f4e08c8a4c895d9736f7bb7a6d5ebc94e2a2db1605a44382938300e560bcdf5ff894e6c17386e37d
+  checksum: 10/1213f43875bc2dc82bea558f1111d9ef4604b2608a21922fb823f6e85e55c5d924e2f17db31459d4e291e3df55e882815a180b3f5f90116febd21132686c11aa
   languageName: node
   linkType: hard
 
@@ -2249,77 +2261,78 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@metamask/snaps-rpc-methods@npm:^11.10.0":
-  version: 11.10.0
-  resolution: "@metamask/snaps-rpc-methods@npm:11.10.0"
+"@metamask/snaps-rpc-methods@npm:^12.3.0":
+  version: 12.3.0
+  resolution: "@metamask/snaps-rpc-methods@npm:12.3.0"
   dependencies:
-    "@metamask/key-tree": "npm:^10.0.2"
-    "@metamask/permission-controller": "npm:^11.0.5"
+    "@metamask/key-tree": "npm:^10.1.1"
+    "@metamask/permission-controller": "npm:^11.0.6"
     "@metamask/rpc-errors": "npm:^7.0.2"
-    "@metamask/snaps-sdk": "npm:^6.16.0"
-    "@metamask/snaps-utils": "npm:^8.9.0"
-    "@metamask/superstruct": "npm:^3.1.0"
-    "@metamask/utils": "npm:^11.0.1"
-    "@noble/hashes": "npm:^1.3.1"
+    "@metamask/snaps-sdk": "npm:^6.24.0"
+    "@metamask/snaps-utils": "npm:^9.3.0"
+    "@metamask/superstruct": "npm:^3.2.1"
+    "@metamask/utils": "npm:^11.4.0"
+    "@noble/hashes": "npm:^1.7.1"
     luxon: "npm:^3.5.0"
-  checksum: 10/6a5d4efaa1ae7df4a5f3ea3e6917a1f837bb0458c576bed74ab769e6f73c78cd292776da94d9feb5dfc023baa88dae67442a0502462aa37138473af1f5cc91f3
+  checksum: 10/0a21db072a046766d8375b3bb53cee07278d0e3408a02652bdffe518fe05d9090854a85fc2ea01bf1e59228ab1198a562538e789345d56106c0fdbd49a708550
   languageName: node
   linkType: hard
 
-"@metamask/snaps-sdk@npm:^6.16.0":
-  version: 6.16.0
-  resolution: "@metamask/snaps-sdk@npm:6.16.0"
+"@metamask/snaps-sdk@npm:@metamask/snaps-sdk@^7.0.0, @metamask/snaps-sdk@npm:^7.0.0":
+  version: 7.0.0
+  resolution: "@metamask/snaps-sdk@npm:7.0.0"
   dependencies:
-    "@metamask/key-tree": "npm:^10.0.2"
-    "@metamask/providers": "npm:^18.3.1"
+    "@metamask/key-tree": "npm:^10.1.1"
+    "@metamask/providers": "npm:^22.1.0"
     "@metamask/rpc-errors": "npm:^7.0.2"
-    "@metamask/superstruct": "npm:^3.1.0"
-    "@metamask/utils": "npm:^11.0.1"
-  checksum: 10/eb76c00df13a845f2c19ec014b1439647adda1455486f4d4b0153e0690eccf7bc1ba0dc8a5472566f267eba722ef26bd99a15a49ed7c5273ff7b452f5d4b7600
+    "@metamask/superstruct": "npm:^3.2.1"
+    "@metamask/utils": "npm:^11.4.0"
+  checksum: 10/ce75b88cd49cc98d75cf337f71ef49c759db89fe14ee87ccffcbe40723ae432f87eadf3f7b1ae2b072a2cabe9ba3b3bb64c4f08dead1839fa53ca594d2f85854
   languageName: node
   linkType: hard
 
-"@metamask/snaps-utils@npm:^8.9.0, @metamask/snaps-utils@npm:^8.9.1":
-  version: 8.9.1
-  resolution: "@metamask/snaps-utils@npm:8.9.1"
+"@metamask/snaps-utils@npm:^9.3.0":
+  version: 9.3.0
+  resolution: "@metamask/snaps-utils@npm:9.3.0"
   dependencies:
     "@babel/core": "npm:^7.23.2"
     "@babel/types": "npm:^7.23.0"
-    "@metamask/base-controller": "npm:^7.0.3"
-    "@metamask/key-tree": "npm:^10.0.2"
-    "@metamask/permission-controller": "npm:^11.0.5"
+    "@metamask/base-controller": "npm:^8.0.1"
+    "@metamask/key-tree": "npm:^10.1.1"
+    "@metamask/permission-controller": "npm:^11.0.6"
     "@metamask/rpc-errors": "npm:^7.0.2"
     "@metamask/slip44": "npm:^4.1.0"
     "@metamask/snaps-registry": "npm:^3.2.3"
-    "@metamask/snaps-sdk": "npm:^6.16.0"
-    "@metamask/superstruct": "npm:^3.1.0"
-    "@metamask/utils": "npm:^11.0.1"
-    "@noble/hashes": "npm:^1.3.1"
+    "@metamask/snaps-sdk": "npm:^6.24.0"
+    "@metamask/superstruct": "npm:^3.2.1"
+    "@metamask/utils": "npm:^11.4.0"
+    "@noble/hashes": "npm:^1.7.1"
     "@scure/base": "npm:^1.1.1"
     chalk: "npm:^4.1.2"
     cron-parser: "npm:^4.5.0"
     fast-deep-equal: "npm:^3.1.3"
     fast-json-stable-stringify: "npm:^2.1.0"
     fast-xml-parser: "npm:^4.4.1"
+    luxon: "npm:^3.5.0"
     marked: "npm:^12.0.1"
     rfdc: "npm:^1.3.0"
     semver: "npm:^7.5.4"
-    ses: "npm:^1.1.0"
+    ses: "npm:^1.12.0"
     validate-npm-package-name: "npm:^5.0.0"
-  checksum: 10/2917fbdccb23c831b613857c3a5738d34352697dae84173a3d24dbcb4c38edf23989fb089870187885dcf867a55ec731c1ac05e9809a7d6804dbe28c57e6da50
+  checksum: 10/1eeb3aa795e4a82b005dae418a7a6e9564cab05bbc828c3a43ea5651dcc12541f0441b6dae741d1b90163e9880a93c992758f0c728fc102eadc4a26812157798
   languageName: node
   linkType: hard
 
-"@metamask/superstruct@npm:^3.1.0":
-  version: 3.1.0
-  resolution: "@metamask/superstruct@npm:3.1.0"
-  checksum: 10/5066fe228d5f11da387606d7f9545de2b473ab5a9e0f1bb8aea2f52d3e2c9d25e427151acde61f4a2de80a07a9871fe9505ad06abca6a61b7c3b54ed5c403b01
+"@metamask/superstruct@npm:^3.1.0, @metamask/superstruct@npm:^3.2.1":
+  version: 3.2.1
+  resolution: "@metamask/superstruct@npm:3.2.1"
+  checksum: 10/9e29380f2cf8b129283ccb2b568296d92682b705109ba62dbd7739ffd6a1982fe38c7228cdcf3cbee94dbcdd5fcc1c846ab9d1dd3582167154f914422fcff547
   languageName: node
   linkType: hard
 
-"@metamask/utils@npm:^11.0.1, @metamask/utils@npm:^11.1.0":
-  version: 11.1.0
-  resolution: "@metamask/utils@npm:11.1.0"
+"@metamask/utils@npm:^11.0.1, @metamask/utils@npm:^11.1.0, @metamask/utils@npm:^11.2.0, @metamask/utils@npm:^11.4.0":
+  version: 11.4.0
+  resolution: "@metamask/utils@npm:11.4.0"
   dependencies:
     "@ethereumjs/tx": "npm:^4.2.0"
     "@metamask/superstruct": "npm:^3.1.0"
@@ -2330,7 +2343,7 @@ __metadata:
     pony-cause: "npm:^2.1.10"
     semver: "npm:^7.5.4"
     uuid: "npm:^9.0.1"
-  checksum: 10/756f13987881fe26adaa0a54354bc5af20cedee4dd228a736d481697dc634adb9e6e54d8f1dcc1d487b2376ab4ba8c576ecbb24beab2fb63aff721d0d5c0f5fe
+  checksum: 10/7c976268e944b542b5e936bae89f58a50eef58501bd3512944995c6d416cb1a7dd3f712aec8c7ca0969dcee889ab963b815fbc3e863dc80ccf16e9258eaec3ff
   languageName: node
   linkType: hard
 
@@ -2367,22 +2380,29 @@ __metadata:
   languageName: node
   linkType: hard
 
-"@noble/curves@npm:^1.2.0":
-  version: 1.5.0
-  resolution: "@noble/curves@npm:1.5.0"
+"@noble/curves@npm:^1.2.0, @noble/curves@npm:^1.8.1":
+  version: 1.9.1
+  resolution: "@noble/curves@npm:1.9.1"
   dependencies:
-    "@noble/hashes": "npm:1.4.0"
-  checksum: 10/d7707d756a887a0daf9eba709526017ac6905d4be58760947e0f0652961926295ba62a5a699d9a9f0bf2a2e0c6803381373e14542be5ff3885b3434bb59be86c
+    "@noble/hashes": "npm:1.8.0"
+  checksum: 10/5c82ec828ca4a4218b1666ba0ddffde17afd224d0bd5e07b64c2a0c83a3362483387f55c11cfd8db0fc046605394fe4e2c67fe024628a713e864acb541a7d2bb
   languageName: node
   linkType: hard
 
-"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.1.2, @noble/hashes@npm:^1.2.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.2, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:~1.4.0":
+"@noble/hashes@npm:1.4.0, @noble/hashes@npm:~1.4.0":
   version: 1.4.0
   resolution: "@noble/hashes@npm:1.4.0"
   checksum: 10/e156e65794c473794c52fa9d06baf1eb20903d0d96719530f523cc4450f6c721a957c544796e6efd0197b2296e7cd70efeb312f861465e17940a3e3c7e0febc6
   languageName: node
   linkType: hard
 
+"@noble/hashes@npm:1.8.0, @noble/hashes@npm:^1.1.2, @noble/hashes@npm:^1.2.0, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:^1.3.2, @noble/hashes@npm:^1.4.0, @noble/hashes@npm:^1.7.1":
+  version: 1.8.0
+  resolution: "@noble/hashes@npm:1.8.0"
+  checksum: 10/474b7f56bc6fb2d5b3a42132561e221b0ea4f91e590f4655312ca13667840896b34195e2b53b7f097ec080a1fdd3b58d902c2a8d0fbdf51d2e238b53808a177e
+  languageName: node
+  linkType: hard
+
 "@noble/hashes@npm:~1.3.2":
   version: 1.3.3
   resolution: "@noble/hashes@npm:1.3.3"
@@ -5223,6 +5243,13 @@ __metadata:
   languageName: node
   linkType: hard
 
+"cockatiel@npm:^3.1.2":
+  version: 3.2.1
+  resolution: "cockatiel@npm:3.2.1"
+  checksum: 10/b022d588dc1e31db59a17527b89acd6ed52c6d8761402afe112c7688af8836363e260ebf71b6f07c7edd69e62047adbf9fc1b3ffcb9081b7a0a751e6018773a6
+  languageName: node
+  linkType: hard
+
 "collect-v8-coverage@npm:^1.0.0":
   version: 1.0.1
   resolution: "collect-v8-coverage@npm:1.0.1"
@@ -9034,12 +9061,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"nanoid@npm:^3.1.31, nanoid@npm:^3.3.7, nanoid@npm:^3.3.8":
-  version: 3.3.8
-  resolution: "nanoid@npm:3.3.8"
+"nanoid@npm:^3.3.10, nanoid@npm:^3.3.7, nanoid@npm:^3.3.8":
+  version: 3.3.11
+  resolution: "nanoid@npm:3.3.11"
   bin:
     nanoid: bin/nanoid.cjs
-  checksum: 10/2d1766606cf0d6f47b6f0fdab91761bb81609b2e3d367027aff45e6ee7006f660fb7e7781f4a34799fe6734f1268eeed2e37a5fdee809ade0c2d4eb11b0f9c40
+  checksum: 10/73b5afe5975a307aaa3c95dfe3334c52cdf9ae71518176895229b8d65ab0d1c0417dd081426134eb7571c055720428ea5d57c645138161e7d10df80815527c48
   languageName: node
   linkType: hard
 
@@ -10338,12 +10365,12 @@ __metadata:
   languageName: node
   linkType: hard
 
-"ses@npm:^1.1.0":
-  version: 1.7.0
-  resolution: "ses@npm:1.7.0"
+"ses@npm:^1.12.0":
+  version: 1.12.0
+  resolution: "ses@npm:1.12.0"
   dependencies:
-    "@endo/env-options": "npm:^1.1.5"
-  checksum: 10/8d1227fadcd06653d1b49083c067ae07e55164af984c9e8b393238fbbd315f47216472e3ac65a78638955f3f1a2537e9c9865f0ab142639a6862b902cb1cf6f2
+    "@endo/env-options": "npm:^1.1.8"
+  checksum: 10/209731eb2f6cfcc9e12296964f8f31cab7fefb53de97aff8d75e357aa6c85e40f69e62ebc0a8d946c6cbdd7ef644caf247f38d5c85a6ad891c00a1c5653f0e39
   languageName: node
   linkType: hard
 


Reviews:
user: login: danroc
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T10:06:42Z
user: login: ccharly
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-04T12:03:56Z
user: login: danroc
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T08:18:33Z
user: login: ccharly
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T09:28:16Z
user: login: ccharly
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-07T10:14:10Z
user: login: ccharly
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T16:26:46Z
user: login: ccharly
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T16:44:45Z
user: login: ccharly
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-23T08:08:22Z
user: login: danroc
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T09:00:05Z
user: login: danroc
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T12:17:24Z
user: login: danroc
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T12:30:30Z
user: login: ccharly
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-26T15:19:47Z

Review comments:
diff_hunk: @@ -0,0 +1,47 @@
+import { KeyringRpcMethod } from '@metamask/keyring-api';
+
+import type { KeyringClient } from './KeyringClient';
+
+/**
+ * Convert a tuple to a union.
+ */
+type TupleToUnion<Tuple extends readonly unknown[]> = Tuple[number];
+
+/**
+ * Extract the method name from a RPC method name.
+ */
+type KeyringRpcMethodToMethodName<RpcMethod extends string> =
+  RpcMethod extends `keyring_${infer Method}` ? Method : never;
+
+/**
+ * Restricted methods list.
+ */
+export const RestrictedKeyringRpcMethod = [
path: packages/keyring-snap-client/src/RestrictedKeyringClient.ts
user: login: danroc
body: I think **restricted** is not the best name here, it gives the impression that it's something that requires more rights, which is not completely correct. Maybe `Exposed*`, `External*`, `Public*`, or even `CompanionDapp*`.
created_at: 2025-04-04T10:06:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+import { KeyringRpcMethod } from '@metamask/keyring-api';
+
+import type { KeyringClient } from './KeyringClient';
+
+/**
+ * Convert a tuple to a union.
+ */
+type TupleToUnion<Tuple extends readonly unknown[]> = Tuple[number];
+
+/**
+ * Extract the method name from a RPC method name.
+ */
+type KeyringRpcMethodToMethodName<RpcMethod extends string> =
+  RpcMethod extends `keyring_${infer Method}` ? Method : never;
+
+/**
+ * Restricted methods list.
+ */
+export const RestrictedKeyringRpcMethod = [
path: packages/keyring-snap-client/src/RestrictedKeyringClient.ts
user: login: ccharly
body: Yes, that's why I wanted your opinion on this 😄

I also thought of `Public*` TBH, but `CompanionDapp*` is also a good candidate 🤔 basically I wanted to expose that somewhere on the `accounts`-packages, so we can re-use it afterward on the extension/mobile-app.
created_at: 2025-04-04T12:03:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1123,6 +1135,7 @@ export class SnapKeyring extends EventEmitter {
     });
 
     const signedTx = await this.#submitRequest({
+      origin: 'metamask',
path: packages/keyring-snap-bridge/src/SnapKeyring.ts
user: login: danroc
body: > **Note:** we are setting the origin to `metamask` here regardless of the real origin.

Do you have an idea how we are going to send the real origin? Also, will this new field break the existing Snaps?
created_at: 2025-04-07T08:18:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,47 @@
+import { KeyringRpcMethod } from '@metamask/keyring-api';
+
+import type { KeyringClient } from './KeyringClient';
+
+/**
+ * Convert a tuple to a union.
+ */
+type TupleToUnion<Tuple extends readonly unknown[]> = Tuple[number];
+
+/**
+ * Extract the method name from a RPC method name.
+ */
+type KeyringRpcMethodToMethodName<RpcMethod extends string> =
+  RpcMethod extends `keyring_${infer Method}` ? Method : never;
+
+/**
+ * Restricted methods list.
+ */
+export const RestrictedKeyringRpcMethod = [
path: packages/keyring-snap-client/src/RestrictedKeyringClient.ts
user: login: ccharly
body: I went for `Public`!
created_at: 2025-04-07T09:28:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1123,6 +1135,7 @@ export class SnapKeyring extends EventEmitter {
     });
 
     const signedTx = await this.#submitRequest({
+      origin: 'metamask',
path: packages/keyring-snap-bridge/src/SnapKeyring.ts
user: login: ccharly
body: Good point 🤔 I wrongly assumed we were using those methods when interacting through the MetaMask UI, but this will also be called externally with call like `eth_sign*`, in which case, the true `origin` would not be `metamask`.

And yes, this new field might also break existing Snaps. I don't know else I could trick that. My only idea right now would be to "version the API", and have a runtime check on the version, something like:

```ts
getVersion() {
  const { version } = await this.#snapClient
    .withSnapId(snapId)
    .getVersion();
    
  if (!version) {
    return ...; // First version when we introduced the new `getVersion`?
  }
}
```

But even know, we cannot check which methods are being implemented on a Snap, which would result in a runtime error for this ⬆️.

And adding a version won't solve everything, cause we would have type-issue here to (here we need to pass `origin`, or we need to have another variant type for `submitRequest` with an optional `origin`, that won't scale well IMO..)  
created_at: 2025-04-07T10:14:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -38,7 +38,8 @@
     "@types/web": "^0.0.69",
     "@typescript/lib-dom": "npm:@types/web@^0.0.69",
     "axios@1.7.3": "^1.7.7",
-    "ws@7.4.6": "^7.5.10"
+    "ws@7.4.6": "^7.5.10",
+    "@metamask/snaps-sdk@^6.24.0": "npm:@metamask/snaps-sdk@^7.0.0"
path: package.json
user: login: ccharly
body: This one is required because the latest `@metamask/snaps-utils` is using this version and not the `@metamask/snaps-sdk@^7.0.0` version.

An alternative would be to not use the `@metamask/snaps-sdk@^7.0.0` ourselves, but I thought it made sense to use it here, given that this change is "associated" with this new platform version (=== Snaps SDK version).
created_at: 2025-05-22T16:26:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -34,6 +34,7 @@
     "test:types": "yarn foreach test:types"
   },
   "resolutions": {
+    "@metamask/snaps-sdk@^6.24.0": "npm:@metamask/snaps-sdk@^7.0.0",
path: package.json
user: login: ccharly
body: This one is required because the latest `@metamask/snaps-utils` is using this version and not the `@metamask/snaps-sdk@^7.0.0` version.

An alternative would be to not use the `@metamask/snaps-sdk@^7.0.0` ourselves, but I thought it made sense to use it here, given that this change is "associated" with this new platform version (=== Snaps SDK version).
created_at: 2025-05-22T16:44:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,9 +54,9 @@
     "@lavamoat/allow-scripts": "^3.2.1",
     "@lavamoat/preinstall-always-fail": "^2.1.0",
     "@metamask/auto-changelog": "^3.4.4",
-    "@metamask/snaps-controllers": "^9.18.0",
-    "@metamask/snaps-sdk": "^6.16.0",
-    "@metamask/snaps-utils": "^8.9.1",
+    "@metamask/snaps-controllers": "^12.2.0",
path: packages/keyring-internal-snap-client/package.json
user: login: ccharly
body: This one is required for the new `isMinimumPlatformVersion` action.

I just aligned the other `snaps-*` package to be up-to-date with the latest, also, te `7.0.0` for the SDK is the "breaking change release" to introduce the `KeyringRequest.origin` so IMO that makes sense to also bump it now.
created_at: 2025-05-23T08:08:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,12 @@
+export enum KeyringVersion {
+  /** Default. */
+  V1 = 'v1',
+
+  /**
+   * Introduction of `KeyringRequest.origin`.
+   *
+   * Snap will now receive the `origin` as part of a `KeyringRquest` when `submitRequest` is invoked.
path: packages/keyring-internal-api/src/versions.ts
user: login: danroc
body: ```suggestion
   * Snap will now receive the `origin` as part of a `KeyringRequest` when `submitRequest` is invoked.
```
created_at: 2025-05-26T09:00:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,36 @@
+import { KeyringVersion } from '@metamask/keyring-internal-api';
+import type { SemVerVersion } from '@metamask/utils';
+
+/**
+ * Mapping of the Snap platform version to its `KeyringVersion` equivalent.
+ *
+ * NOTE: We use an array here to preserve ordering of each versions.
+ */
+export const PLATFORM_VERSION_TO_KEYRING_VERSION = [
+  // NOTE: ! This versions needs to be sorted in a descending order (highest platform version first).
path: packages/keyring-snap-bridge/src/versions.ts
user: login: danroc
body: ```suggestion
  // ! NOTE: This versions needs to be sorted in a descending order (highest platform version first).
```
created_at: 2025-05-26T12:17:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1123,6 +1135,7 @@ export class SnapKeyring extends EventEmitter {
     });
 
     const signedTx = await this.#submitRequest({
+      origin: 'metamask',
path: packages/keyring-snap-bridge/src/SnapKeyring.ts
user: login: ccharly
body: Just for the record, we solved this problem thanks to the new `SnapController:isMinimumPlatformVersion` action + introducing a new platform version (7.0.0) which would act as the next version to introduce this breaking change.

Snap that declares this version (and higher) are expected to use the new `KeyringRequest.origin`.

Older Snaps (<7.0.0) won't receive this new field to keep the retro-compatibility. 
created_at: 2025-05-26T15:19:46Z
author_association: CONTRIBUTOR

PR comments:
user: login: ccharly
created_at: 2025-05-20T12:22:01Z
body: @metamaskbot publish-preview
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-20T12:23:48Z
body: 
Preview builds have been published. [See these instructions (from the `core` monorepo)](https://github.com/MetaMask/core/blob/main/docs/contributing.md#using-preview-builds) for more information about preview builds.

<details>

<summary>Expand for full list of packages and versions.</summary>


```
{
  "@metamask-previews/keyring-api": "17.5.0-2d7389c",
  "@metamask-previews/eth-hd-keyring": "12.1.0-2d7389c",
  "@metamask-previews/eth-ledger-bridge-keyring": "11.0.3-2d7389c",
  "@metamask-previews/eth-simple-keyring": "10.0.0-2d7389c",
  "@metamask-previews/eth-trezor-keyring": "8.0.0-2d7389c",
  "@metamask-previews/keyring-internal-api": "6.0.1-2d7389c",
  "@metamask-previews/keyring-internal-snap-client": "4.0.2-2d7389c",
  "@metamask-previews/eth-snap-keyring": "12.1.1-2d7389c",
  "@metamask-previews/keyring-snap-client": "4.1.0-2d7389c",
  "@metamask-previews/keyring-snap-sdk": "3.2.0-2d7389c",
  "@metamask-previews/keyring-utils": "3.0.0-2d7389c"
}
```

</details>

author_association: NONE
user: login: socket-security[bot]
created_at: 2025-05-22T16:25:06Z
body: **Review the following changes in direct dependencies.** Learn more about [Socket for GitHub](https://socket.dev?utm_medium=gh).

<table>
<thead>
<tr>
<th>Diff</th>
<th width="200px">Package</th>
<th align="left" width="100px">Supply Chain<br/>Security</th>
<th align="left" width="100px">Vulnerability</th>
<th align="left" width="100px">Quality</th>
<th align="left" width="100px">Maintenance</th>
<th align="left" width="100px">License</th>
</tr>
</thead>
<tbody>
<tr><td align="center"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=33213768069"><img src="https://github-app-statics.socket.dev/diff-added.svg" title="Added" alt="Added" width="20" height="20"></a></td><td><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=33213768069">@​metamask/​snaps-sdk@​7.0.0</a></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=33213768069"><img src="https://github-app-statics.socket.dev/score-95.svg" title="Supply Chain Security" width="40" height="40" alt="95"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=33213768069"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Vulnerability" width="40" height="40" alt="100"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=33213768069"><img src="https://github-app-statics.socket.dev/score-76.svg" title="Quality" width="40" height="40" alt="76"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=33213768069"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Maintenance" width="40" height="40" alt="100"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=33213768069"><img src="https://github-app-statics.socket.dev/score-100.svg" title="License" width="40" height="40" alt="100"></a></td></tr>
<tr><td align="center"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=16248828028"><img src="https://github-app-statics.socket.dev/diff-updated.svg" title="Updated" alt="Updated" width="20" height="20"></a></td><td><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=16248828028">nanoid@​3.3.8 ⏵ 3.3.11</a></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=16248828028"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Supply Chain Security" width="40" height="40" alt="100"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=16248828028"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Vulnerability" width="40" height="40" alt="100"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=16248828028"><img src="https://github-app-statics.socket.dev/score-80.svg" title="Quality" width="40" height="40" alt="80"></a> <sup>-19</sup></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=16248828028"><img src="https://github-app-statics.socket.dev/score-83.svg" title="Maintenance" width="40" height="40" alt="83"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=16248828028"><img src="https://github-app-statics.socket.dev/score-100.svg" title="License" width="40" height="40" alt="100"></a></td></tr>
<tr><td align="center"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=32695797544"><img src="https://github-app-statics.socket.dev/diff-updated.svg" title="Updated" alt="Updated" width="20" height="20"></a></td><td><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=32695797544">@​metamask/​snaps-controllers@​9.19.1 ⏵ 12.2.0</a></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=32695797544"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Supply Chain Security" width="40" height="40" alt="100"></a> <sup>+8</sup></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=32695797544"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Vulnerability" width="40" height="40" alt="100"></a></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=32695797544"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Quality" width="40" height="40" alt="100"></a> <sup>+27</sup></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=32695797544"><img src="https://github-app-statics.socket.dev/score-100.svg" title="Maintenance" width="40" height="40" alt="100"></a> <sup>+1</sup></td><td align="left"><a href="https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies&dependency_item_key=32695797544"><img src="https://github-app-statics.socket.dev/score-100.svg" title="License" width="40" height="40" alt="100"></a> <sup>+50</sup></td></tr>
</tbody>
</table>

[View full report](https://socket.dev/dashboard/org/MetaMask/diff/ea51094d-50e3-4d5b-9046-397a6c590e7a/9905b113-5a42-469c-97a9-d587050db6f0?tab=dependencies)

<!-- overview-comment -->
author_association: NONE
user: login: ccharly
created_at: 2025-05-23T08:12:12Z
body: @metamaskbot publish-preview
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-23T08:13:47Z
body: 
Preview builds have been published. [See these instructions (from the `core` monorepo)](https://github.com/MetaMask/core/blob/main/docs/contributing.md#using-preview-builds) for more information about preview builds.

<details>

<summary>Expand for full list of packages and versions.</summary>


```
{
  "@metamask-previews/keyring-api": "17.5.0-ad9f8f1",
  "@metamask-previews/eth-hd-keyring": "12.1.0-ad9f8f1",
  "@metamask-previews/eth-ledger-bridge-keyring": "11.0.3-ad9f8f1",
  "@metamask-previews/eth-simple-keyring": "10.0.0-ad9f8f1",
  "@metamask-previews/eth-trezor-keyring": "8.0.0-ad9f8f1",
  "@metamask-previews/keyring-internal-api": "6.0.1-ad9f8f1",
  "@metamask-previews/keyring-internal-snap-client": "4.0.2-ad9f8f1",
  "@metamask-previews/eth-snap-keyring": "12.1.1-ad9f8f1",
  "@metamask-previews/keyring-snap-client": "4.1.0-ad9f8f1",
  "@metamask-previews/keyring-snap-sdk": "3.2.0-ad9f8f1",
  "@metamask-previews/keyring-utils": "3.0.0-ad9f8f1"
}
```

</details>

author_association: NONE
user: login: ccharly
created_at: 2025-05-23T10:17:01Z
body: @metamaskbot publish-preview
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-23T10:18:41Z
body: 
Preview builds have been published. [See these instructions (from the `core` monorepo)](https://github.com/MetaMask/core/blob/main/docs/contributing.md#using-preview-builds) for more information about preview builds.

<details>

<summary>Expand for full list of packages and versions.</summary>


```
{
  "@metamask-previews/keyring-api": "17.5.0-c335bed",
  "@metamask-previews/eth-hd-keyring": "12.1.0-c335bed",
  "@metamask-previews/eth-ledger-bridge-keyring": "11.0.3-c335bed",
  "@metamask-previews/eth-simple-keyring": "10.0.0-c335bed",
  "@metamask-previews/eth-trezor-keyring": "8.0.0-c335bed",
  "@metamask-previews/keyring-internal-api": "6.0.1-c335bed",
  "@metamask-previews/keyring-internal-snap-client": "4.0.2-c335bed",
  "@metamask-previews/eth-snap-keyring": "12.1.1-c335bed",
  "@metamask-previews/keyring-snap-client": "4.1.0-c335bed",
  "@metamask-previews/keyring-snap-sdk": "3.2.0-c335bed",
  "@metamask-previews/keyring-utils": "3.0.0-c335bed"
}
```

</details>

author_association: NONE
user: login: ccharly
created_at: 2025-05-26T08:15:24Z
body: @metamaskbot publish-preview
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-26T08:22:44Z
body: 
Preview builds have been published. [See these instructions (from the `core` monorepo)](https://github.com/MetaMask/core/blob/main/docs/contributing.md#using-preview-builds) for more information about preview builds.

<details>

<summary>Expand for full list of packages and versions.</summary>


```
{
  "@metamask-previews/keyring-api": "17.5.0-cf843c1",
  "@metamask-previews/eth-hd-keyring": "12.1.0-cf843c1",
  "@metamask-previews/eth-ledger-bridge-keyring": "11.0.3-cf843c1",
  "@metamask-previews/eth-simple-keyring": "10.0.0-cf843c1",
  "@metamask-previews/eth-trezor-keyring": "8.0.0-cf843c1",
  "@metamask-previews/keyring-internal-api": "6.0.1-cf843c1",
  "@metamask-previews/keyring-internal-snap-client": "4.0.2-cf843c1",
  "@metamask-previews/eth-snap-keyring": "12.1.1-cf843c1",
  "@metamask-previews/keyring-snap-client": "4.1.0-cf843c1",
  "@metamask-previews/keyring-snap-sdk": "3.2.0-cf843c1",
  "@metamask-previews/keyring-utils": "3.0.0-cf843c1"
}
```

</details>

author_association: NONE
user: login: ccharly
created_at: 2025-05-26T09:53:40Z
body: @metamaskbot publish-preview
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-26T09:55:18Z
body: 
Preview builds have been published. [See these instructions (from the `core` monorepo)](https://github.com/MetaMask/core/blob/main/docs/contributing.md#using-preview-builds) for more information about preview builds.

<details>

<summary>Expand for full list of packages and versions.</summary>


```
{
  "@metamask-previews/keyring-api": "17.5.0-03fdc7b",
  "@metamask-previews/eth-hd-keyring": "12.1.0-03fdc7b",
  "@metamask-previews/eth-ledger-bridge-keyring": "11.0.3-03fdc7b",
  "@metamask-previews/eth-simple-keyring": "10.0.0-03fdc7b",
  "@metamask-previews/eth-trezor-keyring": "8.0.0-03fdc7b",
  "@metamask-previews/keyring-internal-api": "6.0.1-03fdc7b",
  "@metamask-previews/keyring-internal-snap-client": "4.0.2-03fdc7b",
  "@metamask-previews/eth-snap-keyring": "12.1.1-03fdc7b",
  "@metamask-previews/keyring-snap-client": "4.1.0-03fdc7b",
  "@metamask-previews/keyring-snap-sdk": "3.2.0-03fdc7b",
  "@metamask-previews/keyring-utils": "3.0.0-03fdc7b"
}
```

</details>

author_association: NONE
user: login: ccharly
created_at: 2025-05-26T10:44:17Z
body: @metamaskbot publish-preview
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-26T10:45:54Z
body: 
Preview builds have been published. [See these instructions (from the `core` monorepo)](https://github.com/MetaMask/core/blob/main/docs/contributing.md#using-preview-builds) for more information about preview builds.

<details>

<summary>Expand for full list of packages and versions.</summary>


```
{
  "@metamask-previews/keyring-api": "17.5.0-2352190",
  "@metamask-previews/eth-hd-keyring": "12.1.0-2352190",
  "@metamask-previews/eth-ledger-bridge-keyring": "11.0.3-2352190",
  "@metamask-previews/eth-simple-keyring": "10.0.0-2352190",
  "@metamask-previews/eth-trezor-keyring": "8.0.0-2352190",
  "@metamask-previews/keyring-internal-api": "6.0.1-2352190",
  "@metamask-previews/keyring-internal-snap-client": "4.0.2-2352190",
  "@metamask-previews/eth-snap-keyring": "12.1.1-2352190",
  "@metamask-previews/keyring-snap-client": "4.1.0-2352190",
  "@metamask-previews/keyring-snap-sdk": "3.2.0-2352190",
  "@metamask-previews/keyring-utils": "3.0.0-2352190"
}
```

</details>

author_association: NONE
user: login: danroc
created_at: 2025-05-26T12:30:18Z
body: I reviewed this with @ccharly on a call. LGTM. Key points:

- We currently send `metamask` as the origin for EVM signing requests. This is problematic but requires significant client changes to fix.

- ✅ `PLATFORM_VERSION_TO_KEYRING_VERSION` was previously a plain object, which caused issues due to order sensitivity. It's now an array to preserve order.

- ✅ The `send` method in the internal keyring client was public. This was resolved by extending `KeyringInternalSnapClient` with a `submitRequestV1` method.

- The `@metamask/keyring-api` dependency in `keyring-snap-sdk` should likely be a `peerDependency`. This will be addressed in a separate PR.
author_association: CONTRIBUTOR
user: login: ccharly
created_at: 2025-05-26T12:45:39Z
body: @metamaskbot publish-preview
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-05-26T12:47:11Z
body: 
Preview builds have been published. [See these instructions (from the `core` monorepo)](https://github.com/MetaMask/core/blob/main/docs/contributing.md#using-preview-builds) for more information about preview builds.

<details>

<summary>Expand for full list of packages and versions.</summary>


```
{
  "@metamask-previews/keyring-api": "17.5.0-747bf47",
  "@metamask-previews/eth-hd-keyring": "12.1.0-747bf47",
  "@metamask-previews/eth-ledger-bridge-keyring": "11.0.3-747bf47",
  "@metamask-previews/eth-simple-keyring": "10.0.0-747bf47",
  "@metamask-previews/eth-trezor-keyring": "8.0.0-747bf47",
  "@metamask-previews/keyring-internal-api": "6.0.1-747bf47",
  "@metamask-previews/keyring-internal-snap-client": "4.0.2-747bf47",
  "@metamask-previews/eth-snap-keyring": "12.1.1-747bf47",
  "@metamask-previews/keyring-snap-client": "4.1.0-747bf47",
  "@metamask-previews/keyring-snap-sdk": "3.2.0-747bf47",
  "@metamask-previews/keyring-utils": "3.0.0-747bf47"
}
```

</details>

author_association: NONE