Skip to content

Commit 54a9165

Browse files
wip: Add DeviceController
1 parent 24319a4 commit 54a9165

File tree

5 files changed

+160
-0
lines changed

5 files changed

+160
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import type {
2+
RestrictedControllerMessenger,
3+
ControllerGetStateAction,
4+
ControllerStateChangeEvent,
5+
} from '@metamask/base-controller';
6+
import { BaseController } from '@metamask/base-controller';
7+
import type { GetPermissions } from '@metamask/permission-controller';
8+
9+
import type { DeleteInterface } from '../interface';
10+
import type { GetAllSnaps, HandleSnapRequest } from '../snaps';
11+
import type {
12+
TransactionControllerUnapprovedTransactionAddedEvent,
13+
SignatureStateChange,
14+
TransactionControllerTransactionStatusUpdatedEvent,
15+
} from '../types';
16+
17+
const controllerName = 'DeviceController';
18+
19+
export type DeviceControllerAllowedActions =
20+
| HandleSnapRequest
21+
| GetAllSnaps
22+
| GetPermissions
23+
| DeleteInterface;
24+
25+
export type DeviceControllerGetStateAction = ControllerGetStateAction<
26+
typeof controllerName,
27+
DeviceControllerState
28+
>;
29+
30+
export type DeviceControllerActions = DeviceControllerGetStateAction;
31+
32+
export type DeviceControllerStateChangeEvent = ControllerStateChangeEvent<
33+
typeof controllerName,
34+
DeviceControllerState
35+
>;
36+
37+
export type DeviceControllerEvents = DeviceControllerStateChangeEvent;
38+
39+
export type DeviceControllerAllowedEvents =
40+
| TransactionControllerUnapprovedTransactionAddedEvent
41+
| TransactionControllerTransactionStatusUpdatedEvent
42+
| SignatureStateChange;
43+
44+
export type DeviceControllerMessenger = RestrictedControllerMessenger<
45+
typeof controllerName,
46+
DeviceControllerActions | DeviceControllerAllowedActions,
47+
DeviceControllerEvents | DeviceControllerAllowedEvents,
48+
DeviceControllerAllowedActions['type'],
49+
DeviceControllerAllowedEvents['type']
50+
>;
51+
52+
export type Device = {};
53+
54+
export type DeviceControllerState = {
55+
devices: Record<string, Device>;
56+
};
57+
58+
export type DeviceControllerArgs = {
59+
messenger: DeviceControllerMessenger;
60+
state?: DeviceControllerState;
61+
};
62+
/**
63+
* Controller for managing access to devices for Snaps.
64+
*/
65+
export class DeviceController extends BaseController<
66+
typeof controllerName,
67+
DeviceControllerState,
68+
DeviceControllerMessenger
69+
> {
70+
constructor({ messenger, state }: DeviceControllerArgs) {
71+
super({
72+
messenger,
73+
metadata: {
74+
devices: { persist: true, anonymous: false },
75+
},
76+
name: controllerName,
77+
state: { ...state, devices: {} },
78+
});
79+
}
80+
81+
async requestDevices() {
82+
const devices = await (navigator as any).hid.requestDevice({ filters: [] });
83+
return devices;
84+
}
85+
86+
async getDevices() {
87+
const devices = await (navigator as any).hid.getDevices();
88+
return devices;
89+
}
90+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './DeviceController';

packages/snaps-controllers/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export * from './utils';
55
export * from './cronjob';
66
export * from './interface';
77
export * from './insights';
8+
export * from './devices';

packages/snaps-rpc-methods/src/permitted/handlers.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { createInterfaceHandler } from './createInterface';
22
import { providerRequestHandler } from './experimentalProviderRequest';
3+
import { providerRequestHandler as requestDeviceHandler } from './requestDevice';
34
import { getAllSnapsHandler } from './getAllSnaps';
45
import { getClientStatusHandler } from './getClientStatus';
56
import { getCurrencyRateHandler } from './getCurrencyRate';
@@ -12,6 +13,7 @@ import { requestSnapsHandler } from './requestSnaps';
1213
import { resolveInterfaceHandler } from './resolveInterface';
1314
import { updateInterfaceHandler } from './updateInterface';
1415

16+
1517
/* eslint-disable @typescript-eslint/naming-convention */
1618
export const methodHandlers = {
1719
wallet_getAllSnaps: getAllSnapsHandler,
@@ -27,6 +29,7 @@ export const methodHandlers = {
2729
snap_resolveInterface: resolveInterfaceHandler,
2830
snap_getCurrencyRate: getCurrencyRateHandler,
2931
snap_experimentalProviderRequest: providerRequestHandler,
32+
snap_requestDevice: requestDeviceHandler,
3033
};
3134
/* eslint-enable @typescript-eslint/naming-convention */
3235

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import type { JsonRpcEngineEndCallback } from '@metamask/json-rpc-engine';
2+
import type { PermittedHandlerExport } from '@metamask/permission-controller';
3+
import type {
4+
JsonRpcRequest,
5+
ProviderRequestParams,
6+
ProviderRequestResult,
7+
} from '@metamask/snaps-sdk';
8+
import { type InferMatching } from '@metamask/snaps-utils';
9+
import { object, optional, string, type } from '@metamask/superstruct';
10+
import {
11+
type PendingJsonRpcResponse,
12+
CaipChainIdStruct,
13+
JsonRpcParamsStruct,
14+
} from '@metamask/utils';
15+
16+
import type { MethodHooksObject } from '../utils';
17+
18+
const hookNames: MethodHooksObject<ProviderRequestMethodHooks> = {
19+
requestDevices: true,
20+
};
21+
22+
export type ProviderRequestMethodHooks = {
23+
requestDevices: () => any;
24+
};
25+
26+
export const providerRequestHandler: PermittedHandlerExport<
27+
ProviderRequestMethodHooks,
28+
ProviderRequestParameters,
29+
ProviderRequestResult
30+
> = {
31+
methodNames: ['snap_requestDevice'],
32+
implementation: providerRequestImplementation,
33+
hookNames,
34+
};
35+
36+
const ProviderRequestParametersStruct = object({
37+
chainId: CaipChainIdStruct,
38+
request: type({
39+
method: string(),
40+
params: optional(JsonRpcParamsStruct),
41+
}),
42+
});
43+
44+
export type ProviderRequestParameters = InferMatching<
45+
typeof ProviderRequestParametersStruct,
46+
ProviderRequestParams
47+
>;
48+
49+
async function providerRequestImplementation(
50+
req: JsonRpcRequest<ProviderRequestParams>,
51+
res: PendingJsonRpcResponse<ProviderRequestResult>,
52+
_next: unknown,
53+
end: JsonRpcEngineEndCallback,
54+
{ requestDevices }: ProviderRequestMethodHooks,
55+
): Promise<void> {
56+
const { params } = req;
57+
58+
try {
59+
res.result = await requestDevices();
60+
} catch (error) {
61+
return end(error);
62+
}
63+
64+
return end();
65+
}

0 commit comments

Comments
 (0)