@@ -24,6 +24,73 @@ function isTauri(): boolean {
2424 return ! ! anyWin . __TAURI__ || ! ! anyWin . __TAURI_INTERNALS__ ;
2525}
2626
27+ /**
28+ * Check if the browser/environment supports WebHID natively.
29+ * If WebHID is supported, we should use WebHID SDK instead of native implementation.
30+ */
31+ async function isWebHidSupported ( ) : Promise < boolean > {
32+ try {
33+ // Check if WebHID is available in the browser
34+ if ( typeof window !== 'undefined' && 'navigator' in window && 'hid' in navigator ) {
35+ console . log ( 'WebHID is supported by the browser' ) ;
36+ return true ;
37+ }
38+
39+ console . log ( 'WebHID is not supported by the browser' ) ;
40+ return false ;
41+ } catch ( error ) {
42+ console . log ( 'WebHID support check failed:' , error ) ;
43+ return false ;
44+ }
45+ }
46+
47+ /**
48+ * Check if we should use native implementation.
49+ * We use native only when:
50+ * 1. We are in Tauri environment
51+ * 2. WebHID is NOT supported by the browser
52+ * 3. Tauri HID commands are available
53+ */
54+ async function shouldUseNative ( ) : Promise < boolean > {
55+ try {
56+ // First, check if we are in a Tauri environment
57+ if ( ! isTauri ( ) ) {
58+ console . log ( 'Not in Tauri environment, using WebHID SDK' ) ;
59+ return false ;
60+ }
61+
62+ // Check if WebHID is supported by the browser
63+ const webHidSupported = await isWebHidSupported ( ) ;
64+ if ( webHidSupported ) {
65+ console . log ( 'WebHID is supported, using WebHID SDK instead of native' ) ;
66+ return false ;
67+ }
68+
69+ // If WebHID is not supported, check if Tauri HID commands are available
70+ const core = await import ( '@tauri-apps/api/core' ) ;
71+ if ( core ?. invoke ) {
72+ // Try to call the ledger_list_devices command; if successful, native is available
73+ await core . invoke ( 'ledger_list_devices' ) ;
74+ console . log ( 'Native HID implementation available and will be used' ) ;
75+ return true ;
76+ }
77+
78+ // Fallback to global Tauri API
79+ const anyWin : any = typeof window !== 'undefined' ? ( window as any ) : undefined ;
80+ if ( anyWin ?. __TAURI__ ?. invoke ) {
81+ await anyWin . __TAURI__ . invoke ( 'ledger_list_devices' ) ;
82+ console . log ( 'Native HID implementation available via global API and will be used' ) ;
83+ return true ;
84+ }
85+
86+ console . log ( 'Tauri API not available, falling back to WebHID SDK' ) ;
87+ return false ;
88+ } catch ( error ) {
89+ console . log ( 'Native HID check failed:' , error ) ;
90+ return false ;
91+ }
92+ }
93+
2794async function invoke < T > ( cmd : string , args ?: Record < string , unknown > ) : Promise < T > {
2895 // Prefer official Tauri API (works across child windows/iframes in v2)
2996 try {
@@ -40,9 +107,9 @@ async function invoke<T>(cmd: string, args?: Record<string, unknown>): Promise<T
40107export class NativeLedgerTransport {
41108 private deviceId : string | null = null ;
42109
43- static isAvailable ( ) : boolean {
44- console . log ( 'isTauri' , isTauri ( ) ) ;
45- return isTauri ( ) ;
110+ static async isAvailable ( ) : Promise < boolean > {
111+ const shouldUse = await shouldUseNative ( ) ;
112+ return shouldUse ;
46113 }
47114
48115 async listDevices ( ) : Promise < DeviceDescriptor [ ] > {
@@ -64,6 +131,43 @@ export class NativeLedgerTransport {
64131 this . deviceId = null ;
65132 }
66133
134+ // 使用 ledger-zondax-generic 的新命令
135+ async getDeviceInfo ( ) : Promise < {
136+ targetId : [ number , number , number , number ] ;
137+ seVersion : string ;
138+ flag : number [ ] ;
139+ mcuVersion : string ;
140+ } > {
141+ if ( ! this . deviceId ) throw new Error ( 'Device not connected' ) ;
142+ return invoke ( 'ledger_get_device_info' , { deviceId : this . deviceId } ) ;
143+ }
144+
145+ async getAppInfo ( ) : Promise < {
146+ appName : string ;
147+ appVersion : string ;
148+ flagLen : number ;
149+ flagsValue : number ;
150+ flagRecovery : boolean ;
151+ flagSignedMCUCode : boolean ;
152+ flagOnboarded : boolean ;
153+ flagPINValidated : boolean ;
154+ } > {
155+ if ( ! this . deviceId ) throw new Error ( 'Device not connected' ) ;
156+ return invoke ( 'ledger_get_app_info' , { deviceId : this . deviceId } ) ;
157+ }
158+
159+ async getVersion ( ) : Promise < {
160+ mode : number ;
161+ major : number ;
162+ minor : number ;
163+ patch : number ;
164+ locked : boolean ;
165+ targetId : [ number , number , number , number ] ;
166+ } > {
167+ if ( ! this . deviceId ) throw new Error ( 'Device not connected' ) ;
168+ return invoke ( 'ledger_get_version' , { deviceId : this . deviceId } ) ;
169+ }
170+
67171 async getEthAddress ( derivationPath : string , display = false ) : Promise < { address : string ; publicKey : string } > {
68172 if ( ! this . deviceId ) throw new Error ( 'Device not connected' ) ;
69173 const [ address , pubkey ] = await invoke < [ string , number [ ] ] > ( 'ledger_eth_get_address' , {
@@ -83,8 +187,51 @@ export class NativeLedgerTransport {
83187 timeoutMs,
84188 } ) ;
85189 }
190+
191+ async signMessage (
192+ derivationPath : string ,
193+ message : string | Uint8Array
194+ ) : Promise < { signature : { r : string ; s : string ; v : number ; signature : string } } > {
195+ if ( ! this . deviceId ) throw new Error ( 'Device not connected' ) ;
196+ const messageStr = typeof message === 'string' ? message : Buffer . from ( message ) . toString ( 'hex' ) ;
197+ return invoke < { signature : { r : string ; s : string ; v : number ; signature : string } } > ( 'ledger_eth_sign_message' , {
198+ deviceId : this . deviceId ,
199+ derivationPath,
200+ message : messageStr ,
201+ } ) ;
202+ }
203+
204+ async signTransaction (
205+ derivationPath : string ,
206+ transaction : Uint8Array
207+ ) : Promise < { signature : { r : string ; s : string ; v : number ; signature : string } } > {
208+ if ( ! this . deviceId ) throw new Error ( 'Device not connected' ) ;
209+ return invoke < { signature : { r : string ; s : string ; v : number ; signature : string } } > (
210+ 'ledger_eth_sign_transaction' ,
211+ {
212+ deviceId : this . deviceId ,
213+ derivationPath,
214+ transaction : Array . from ( transaction ) ,
215+ }
216+ ) ;
217+ }
218+
219+ async signTypedData (
220+ derivationPath : string ,
221+ typedData : any
222+ ) : Promise < { signature : { r : string ; s : string ; v : number ; signature : string } } > {
223+ if ( ! this . deviceId ) throw new Error ( 'Device not connected' ) ;
224+ const typedDataStr = typeof typedData === 'string' ? typedData : JSON . stringify ( typedData ) ;
225+ return invoke < { signature : { r : string ; s : string ; v : number ; signature : string } } > ( 'ledger_eth_sign_typed_data' , {
226+ deviceId : this . deviceId ,
227+ derivationPath,
228+ typedData : typedDataStr ,
229+ } ) ;
230+ }
86231}
87232
88- export function getNativeLedgerTransport ( ) : NativeLedgerTransport | null {
89- return NativeLedgerTransport . isAvailable ( ) ? new NativeLedgerTransport ( ) : null ;
233+ export async function getNativeLedgerTransport ( ) : Promise < NativeLedgerTransport | null > {
234+ const isAvailable = await NativeLedgerTransport . isAvailable ( ) ;
235+ // return isAvailable ? new NativeLedgerTransport() : null;
236+ return new NativeLedgerTransport ( ) ;
90237}
0 commit comments