1
+ import Eth from '@ledgerhq/hw-app-eth' ;
1
2
import type {
2
3
OnRpcRequestHandler ,
3
4
OnUserInputHandler ,
4
5
} from '@metamask/snaps-sdk' ;
5
- import { Box , Button , Text , Copyable } from '@metamask/snaps-sdk/jsx' ;
6
6
import { MethodNotFoundError } from '@metamask/snaps-sdk' ;
7
- import Eth from '@ledgerhq/hw-app-eth' ;
7
+ import { Box , Button , Text , Copyable } from '@metamask/snaps-sdk/jsx' ;
8
+ import { bytesToHex , stringToBytes } from '@metamask/utils' ;
8
9
10
+ import { ConnectHID , Unsupported } from './components' ;
9
11
import TransportSnapsHID from './transport' ;
12
+ import { signatureToHex } from './utils' ;
10
13
14
+ /**
15
+ * Handle incoming JSON-RPC requests from the dapp, sent through the
16
+ * `wallet_invokeSnap` method. This handler handles one method:
17
+ *
18
+ * - `request`: Display a dialog with a button to request a Ledger device. This
19
+ * demonstrates how to request a device using Snaps, and how to handle user
20
+ * input events, in order to sign a message with the device.
21
+ *
22
+ * Note that this only works in browsers that support the WebHID API, and
23
+ * the Ledger device must be connected and unlocked.
24
+ *
25
+ * @param params - The request parameters.
26
+ * @param params.request - The JSON-RPC request object.
27
+ * @returns The JSON-RPC response.
28
+ * @see https://docs.metamask.io/snaps/reference/exports/#onrpcrequest
29
+ */
11
30
export const onRpcRequest : OnRpcRequestHandler = async ( { request } ) => {
12
31
switch ( request . method ) {
13
32
case 'request' : {
33
+ const Component = ( await TransportSnapsHID . isSupported ( ) )
34
+ ? ConnectHID
35
+ : Unsupported ;
36
+
14
37
return snap . request ( {
15
38
method : 'snap_dialog' ,
16
39
params : {
17
- content : (
18
- < Box >
19
- < Button > Request Devices</ Button >
20
- </ Box >
21
- ) ,
40
+ content : < Component /> ,
22
41
} ,
23
42
} ) ;
24
43
}
@@ -30,49 +49,51 @@ export const onRpcRequest: OnRpcRequestHandler = async ({ request }) => {
30
49
}
31
50
} ;
32
51
33
- function hexlifySignature ( signature : { r : string ; s : string ; v : number } ) {
34
- const adjustedV = signature . v - 27 ;
35
- let hexlifiedV = adjustedV . toString ( 16 ) ;
36
- if ( hexlifiedV . length < 2 ) {
37
- hexlifiedV = '0' + hexlifiedV ;
38
- }
39
- return `0x${ signature . r } ${ signature . s } ${ hexlifiedV } ` ;
40
- }
41
-
52
+ /**
53
+ * Handle incoming user events coming from the Snap interface. This handler
54
+ * handles one event:
55
+ *
56
+ * - `connect-hid`: Request a Ledger device, sign a message, and display the
57
+ * signature in the Snap interface.
58
+ *
59
+ * @param params - The event parameters.
60
+ * @param params.id - The Snap interface ID where the event was fired.
61
+ * @see https://docs.metamask.io/snaps/reference/exports/#onuserinput
62
+ */
42
63
export const onUserInput : OnUserInputHandler = async ( { id } ) => {
43
- try {
44
- const transport = await TransportSnapsHID . request ( ) ;
45
- const eth = new Eth ( transport ) ;
46
- const msg = 'test' ;
47
- const { address } = await eth . getAddress ( "44'/60'/0'/0/0" ) ;
48
- const signature = await eth . signPersonalMessage (
49
- "44'/60'/0'/0/0" ,
50
- Buffer . from ( msg ) . toString ( 'hex' ) ,
51
- ) ;
64
+ // TODO: Handle errors (i.e., Ledger locked, disconnected, etc.)
65
+ const transport = await TransportSnapsHID . request ( ) ;
66
+ const eth = new Eth ( transport ) ;
52
67
53
- const signatureHex = hexlifySignature ( signature ) ;
54
- const message = {
55
- address,
56
- msg,
57
- sig : signatureHex ,
58
- version : 2 ,
59
- } ;
60
- await snap . request ( {
61
- method : 'snap_updateInterface' ,
62
- params : {
63
- id,
64
- ui : (
65
- < Box >
66
- < Button > Request Devices</ Button >
67
- < Text > Signature:</ Text >
68
- < Copyable value = { signatureHex } />
69
- < Text > JSON:</ Text >
70
- < Copyable value = { JSON . stringify ( message , null , 2 ) } />
71
- </ Box >
72
- ) ,
73
- } ,
74
- } ) ;
75
- } catch ( error ) {
76
- console . error ( error ) ;
77
- }
68
+ // TODO: Make this message configurable.
69
+ const message = 'test' ;
70
+ const { address } = await eth . getAddress ( "44'/60'/0'/0/0" ) ;
71
+
72
+ const signature = await eth . signPersonalMessage (
73
+ "44'/60'/0'/0/0" ,
74
+ bytesToHex ( stringToBytes ( message ) ) ,
75
+ ) ;
76
+
77
+ const signatureHex = signatureToHex ( signature ) ;
78
+ const signatureObject = {
79
+ address,
80
+ message,
81
+ signature : signatureHex ,
82
+ } ;
83
+
84
+ await snap . request ( {
85
+ method : 'snap_updateInterface' ,
86
+ params : {
87
+ id,
88
+ ui : (
89
+ < Box >
90
+ < Button > Request Devices</ Button >
91
+ < Text > Signature:</ Text >
92
+ < Copyable value = { signatureHex } />
93
+ < Text > JSON:</ Text >
94
+ < Copyable value = { JSON . stringify ( signatureObject , null , 2 ) } />
95
+ </ Box >
96
+ ) ,
97
+ } ,
98
+ } ) ;
78
99
} ;
0 commit comments