@onekeyfe/hwk-adapter-core 1.1.26-alpha.106 → 1.1.26-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +24 -28
- package/dist/index.d.ts +24 -28
- package/dist/index.js +17 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +17 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.d.mts
CHANGED
|
@@ -43,12 +43,11 @@ interface Failure {
|
|
|
43
43
|
payload: {
|
|
44
44
|
error: string;
|
|
45
45
|
code: HardwareErrorCode;
|
|
46
|
-
appName?: string;
|
|
47
46
|
};
|
|
48
47
|
}
|
|
49
48
|
type Response<T> = Success<T> | Failure;
|
|
50
49
|
declare function success<T>(payload: T): Success<T>;
|
|
51
|
-
declare function failure(code: HardwareErrorCode, error: string
|
|
50
|
+
declare function failure(code: HardwareErrorCode, error: string): Failure;
|
|
52
51
|
|
|
53
52
|
type VendorType = 'trezor' | 'ledger';
|
|
54
53
|
type ConnectionType = 'usb' | 'ble';
|
|
@@ -250,10 +249,10 @@ interface BtcRefTransaction {
|
|
|
250
249
|
locktime: number;
|
|
251
250
|
}
|
|
252
251
|
interface BtcSignedTx {
|
|
253
|
-
signatures: string[];
|
|
254
252
|
serializedTx: string;
|
|
253
|
+
/** Present when the wallet returns per-input sigs separately (e.g. Trezor). */
|
|
254
|
+
signatures?: string[];
|
|
255
255
|
txid?: string;
|
|
256
|
-
signedPsbt?: string;
|
|
257
256
|
}
|
|
258
257
|
interface BtcSignPsbtParams {
|
|
259
258
|
psbt: string;
|
|
@@ -271,7 +270,8 @@ interface BtcSignMsgParams {
|
|
|
271
270
|
}
|
|
272
271
|
interface BtcSignature {
|
|
273
272
|
signature: string;
|
|
274
|
-
|
|
273
|
+
/** Optional — not all adapters return it (e.g. Ledger DMK). Derive via btcGetAddress if needed. */
|
|
274
|
+
address?: string;
|
|
275
275
|
}
|
|
276
276
|
interface IBtcMethods {
|
|
277
277
|
btcGetAddress(connectId: string, deviceId: string, params: BtcGetAddressParams): Promise<Response<BtcAddress>>;
|
|
@@ -383,12 +383,19 @@ interface QrResponseData {
|
|
|
383
383
|
declare const CHAIN_FINGERPRINT_PATHS: Record<ChainForFingerprint, string>;
|
|
384
384
|
type ChainForFingerprint = 'evm' | 'btc' | 'sol' | 'tron';
|
|
385
385
|
/**
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
* device identity matching. Not used for security decisions.
|
|
386
|
+
* 16-char SHA-256 fingerprint for device-identity verification.
|
|
387
|
+
* Callers must canonicalize input (e.g. EVM address → lowercase) —
|
|
388
|
+
* encoding variations otherwise cause false DeviceMismatch.
|
|
390
389
|
*/
|
|
391
|
-
declare function deriveDeviceFingerprint(
|
|
390
|
+
declare function deriveDeviceFingerprint(value: string): string;
|
|
391
|
+
|
|
392
|
+
declare const DEVICE_EVENT = "DEVICE_EVENT";
|
|
393
|
+
/** Events originating from the hardware device. */
|
|
394
|
+
declare const DEVICE: {
|
|
395
|
+
readonly CONNECT: "device-connect";
|
|
396
|
+
readonly DISCONNECT: "device-disconnect";
|
|
397
|
+
readonly CHANGED: "device-changed";
|
|
398
|
+
};
|
|
392
399
|
|
|
393
400
|
/** 10 min — every UI request is human-in-the-loop; bias toward "wait long". */
|
|
394
401
|
declare const UI_REQUEST_DEFAULT_TIMEOUT_MS = 600000;
|
|
@@ -535,18 +542,6 @@ type UiResponseEvent = {
|
|
|
535
542
|
payload?: undefined;
|
|
536
543
|
};
|
|
537
544
|
|
|
538
|
-
declare const DEVICE_EVENT = "DEVICE_EVENT";
|
|
539
|
-
/** Events originating from the hardware device. */
|
|
540
|
-
declare const DEVICE: {
|
|
541
|
-
readonly CONNECT: "device-connect";
|
|
542
|
-
readonly DISCONNECT: "device-disconnect";
|
|
543
|
-
readonly CHANGED: "device-changed";
|
|
544
|
-
readonly ACQUIRE: "device-acquire";
|
|
545
|
-
readonly RELEASE: "device-release";
|
|
546
|
-
readonly FEATURES: "features";
|
|
547
|
-
readonly SUPPORT_FEATURES: "support_features";
|
|
548
|
-
};
|
|
549
|
-
|
|
550
545
|
/** Events generated by SDK internal detection (not from hardware directly). */
|
|
551
546
|
declare const SDK: {
|
|
552
547
|
readonly DEVICE_STUCK: "device-stuck";
|
|
@@ -617,8 +612,6 @@ type UiRequestEvent = {
|
|
|
617
612
|
type: typeof UI_REQUEST.REQUEST_DEVICE_CONNECT;
|
|
618
613
|
payload: {
|
|
619
614
|
message: string;
|
|
620
|
-
retryCount: number;
|
|
621
|
-
maxRetries: number;
|
|
622
615
|
};
|
|
623
616
|
} | {
|
|
624
617
|
type: typeof UI_REQUEST.REQUEST_PREEMPTION;
|
|
@@ -731,8 +724,6 @@ interface HardwareEventMap {
|
|
|
731
724
|
type: typeof UI_REQUEST.REQUEST_DEVICE_CONNECT;
|
|
732
725
|
payload: {
|
|
733
726
|
message: string;
|
|
734
|
-
retryCount: number;
|
|
735
|
-
maxRetries: number;
|
|
736
727
|
};
|
|
737
728
|
};
|
|
738
729
|
[UI_REQUEST.REQUEST_PREEMPTION]: {
|
|
@@ -943,6 +934,8 @@ interface ConnectorEventMap {
|
|
|
943
934
|
'ui-event': ConnectorUiEvent;
|
|
944
935
|
}
|
|
945
936
|
interface IConnector {
|
|
937
|
+
/** Physical connection type this connector uses. Fixed at construction. */
|
|
938
|
+
readonly connectionType: ConnectionType;
|
|
946
939
|
searchDevices(): Promise<ConnectorDevice[]>;
|
|
947
940
|
connect(deviceId?: string): Promise<ConnectorSession>;
|
|
948
941
|
disconnect(sessionId: string): Promise<void>;
|
|
@@ -1006,7 +999,7 @@ interface IHardwareBridge {
|
|
|
1006
999
|
* Use this anywhere the actual hardware lives behind a process / context boundary
|
|
1007
1000
|
* (Electron main, extension background, native module, worker, iframe).
|
|
1008
1001
|
*/
|
|
1009
|
-
declare function createBridgedConnector(vendor: VendorType, bridge: IHardwareBridge): IConnector;
|
|
1002
|
+
declare function createBridgedConnector(vendor: VendorType, connectionType: ConnectionType, bridge: IHardwareBridge): IConnector;
|
|
1010
1003
|
|
|
1011
1004
|
/**
|
|
1012
1005
|
* Minimal typed event emitter using Map<string, Set<listener>>.
|
|
@@ -1044,7 +1037,10 @@ declare function ensure0x(hex: string): string;
|
|
|
1044
1037
|
declare function stripHex(hex: string): string;
|
|
1045
1038
|
/** Pad hex to 64 chars (32 bytes) with 0x prefix */
|
|
1046
1039
|
declare function padHex64(hex: string): string;
|
|
1047
|
-
/**
|
|
1040
|
+
/**
|
|
1041
|
+
* Convert a hex string (with or without 0x prefix) to a Uint8Array.
|
|
1042
|
+
* Throws on invalid hex (non-hex characters, odd length).
|
|
1043
|
+
*/
|
|
1048
1044
|
declare function hexToBytes(hex: string): Uint8Array;
|
|
1049
1045
|
/** Convert a Uint8Array to a hex string (no 0x prefix). */
|
|
1050
1046
|
declare function bytesToHex(bytes: Uint8Array): string;
|
package/dist/index.d.ts
CHANGED
|
@@ -43,12 +43,11 @@ interface Failure {
|
|
|
43
43
|
payload: {
|
|
44
44
|
error: string;
|
|
45
45
|
code: HardwareErrorCode;
|
|
46
|
-
appName?: string;
|
|
47
46
|
};
|
|
48
47
|
}
|
|
49
48
|
type Response<T> = Success<T> | Failure;
|
|
50
49
|
declare function success<T>(payload: T): Success<T>;
|
|
51
|
-
declare function failure(code: HardwareErrorCode, error: string
|
|
50
|
+
declare function failure(code: HardwareErrorCode, error: string): Failure;
|
|
52
51
|
|
|
53
52
|
type VendorType = 'trezor' | 'ledger';
|
|
54
53
|
type ConnectionType = 'usb' | 'ble';
|
|
@@ -250,10 +249,10 @@ interface BtcRefTransaction {
|
|
|
250
249
|
locktime: number;
|
|
251
250
|
}
|
|
252
251
|
interface BtcSignedTx {
|
|
253
|
-
signatures: string[];
|
|
254
252
|
serializedTx: string;
|
|
253
|
+
/** Present when the wallet returns per-input sigs separately (e.g. Trezor). */
|
|
254
|
+
signatures?: string[];
|
|
255
255
|
txid?: string;
|
|
256
|
-
signedPsbt?: string;
|
|
257
256
|
}
|
|
258
257
|
interface BtcSignPsbtParams {
|
|
259
258
|
psbt: string;
|
|
@@ -271,7 +270,8 @@ interface BtcSignMsgParams {
|
|
|
271
270
|
}
|
|
272
271
|
interface BtcSignature {
|
|
273
272
|
signature: string;
|
|
274
|
-
|
|
273
|
+
/** Optional — not all adapters return it (e.g. Ledger DMK). Derive via btcGetAddress if needed. */
|
|
274
|
+
address?: string;
|
|
275
275
|
}
|
|
276
276
|
interface IBtcMethods {
|
|
277
277
|
btcGetAddress(connectId: string, deviceId: string, params: BtcGetAddressParams): Promise<Response<BtcAddress>>;
|
|
@@ -383,12 +383,19 @@ interface QrResponseData {
|
|
|
383
383
|
declare const CHAIN_FINGERPRINT_PATHS: Record<ChainForFingerprint, string>;
|
|
384
384
|
type ChainForFingerprint = 'evm' | 'btc' | 'sol' | 'tron';
|
|
385
385
|
/**
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
*
|
|
389
|
-
* device identity matching. Not used for security decisions.
|
|
386
|
+
* 16-char SHA-256 fingerprint for device-identity verification.
|
|
387
|
+
* Callers must canonicalize input (e.g. EVM address → lowercase) —
|
|
388
|
+
* encoding variations otherwise cause false DeviceMismatch.
|
|
390
389
|
*/
|
|
391
|
-
declare function deriveDeviceFingerprint(
|
|
390
|
+
declare function deriveDeviceFingerprint(value: string): string;
|
|
391
|
+
|
|
392
|
+
declare const DEVICE_EVENT = "DEVICE_EVENT";
|
|
393
|
+
/** Events originating from the hardware device. */
|
|
394
|
+
declare const DEVICE: {
|
|
395
|
+
readonly CONNECT: "device-connect";
|
|
396
|
+
readonly DISCONNECT: "device-disconnect";
|
|
397
|
+
readonly CHANGED: "device-changed";
|
|
398
|
+
};
|
|
392
399
|
|
|
393
400
|
/** 10 min — every UI request is human-in-the-loop; bias toward "wait long". */
|
|
394
401
|
declare const UI_REQUEST_DEFAULT_TIMEOUT_MS = 600000;
|
|
@@ -535,18 +542,6 @@ type UiResponseEvent = {
|
|
|
535
542
|
payload?: undefined;
|
|
536
543
|
};
|
|
537
544
|
|
|
538
|
-
declare const DEVICE_EVENT = "DEVICE_EVENT";
|
|
539
|
-
/** Events originating from the hardware device. */
|
|
540
|
-
declare const DEVICE: {
|
|
541
|
-
readonly CONNECT: "device-connect";
|
|
542
|
-
readonly DISCONNECT: "device-disconnect";
|
|
543
|
-
readonly CHANGED: "device-changed";
|
|
544
|
-
readonly ACQUIRE: "device-acquire";
|
|
545
|
-
readonly RELEASE: "device-release";
|
|
546
|
-
readonly FEATURES: "features";
|
|
547
|
-
readonly SUPPORT_FEATURES: "support_features";
|
|
548
|
-
};
|
|
549
|
-
|
|
550
545
|
/** Events generated by SDK internal detection (not from hardware directly). */
|
|
551
546
|
declare const SDK: {
|
|
552
547
|
readonly DEVICE_STUCK: "device-stuck";
|
|
@@ -617,8 +612,6 @@ type UiRequestEvent = {
|
|
|
617
612
|
type: typeof UI_REQUEST.REQUEST_DEVICE_CONNECT;
|
|
618
613
|
payload: {
|
|
619
614
|
message: string;
|
|
620
|
-
retryCount: number;
|
|
621
|
-
maxRetries: number;
|
|
622
615
|
};
|
|
623
616
|
} | {
|
|
624
617
|
type: typeof UI_REQUEST.REQUEST_PREEMPTION;
|
|
@@ -731,8 +724,6 @@ interface HardwareEventMap {
|
|
|
731
724
|
type: typeof UI_REQUEST.REQUEST_DEVICE_CONNECT;
|
|
732
725
|
payload: {
|
|
733
726
|
message: string;
|
|
734
|
-
retryCount: number;
|
|
735
|
-
maxRetries: number;
|
|
736
727
|
};
|
|
737
728
|
};
|
|
738
729
|
[UI_REQUEST.REQUEST_PREEMPTION]: {
|
|
@@ -943,6 +934,8 @@ interface ConnectorEventMap {
|
|
|
943
934
|
'ui-event': ConnectorUiEvent;
|
|
944
935
|
}
|
|
945
936
|
interface IConnector {
|
|
937
|
+
/** Physical connection type this connector uses. Fixed at construction. */
|
|
938
|
+
readonly connectionType: ConnectionType;
|
|
946
939
|
searchDevices(): Promise<ConnectorDevice[]>;
|
|
947
940
|
connect(deviceId?: string): Promise<ConnectorSession>;
|
|
948
941
|
disconnect(sessionId: string): Promise<void>;
|
|
@@ -1006,7 +999,7 @@ interface IHardwareBridge {
|
|
|
1006
999
|
* Use this anywhere the actual hardware lives behind a process / context boundary
|
|
1007
1000
|
* (Electron main, extension background, native module, worker, iframe).
|
|
1008
1001
|
*/
|
|
1009
|
-
declare function createBridgedConnector(vendor: VendorType, bridge: IHardwareBridge): IConnector;
|
|
1002
|
+
declare function createBridgedConnector(vendor: VendorType, connectionType: ConnectionType, bridge: IHardwareBridge): IConnector;
|
|
1010
1003
|
|
|
1011
1004
|
/**
|
|
1012
1005
|
* Minimal typed event emitter using Map<string, Set<listener>>.
|
|
@@ -1044,7 +1037,10 @@ declare function ensure0x(hex: string): string;
|
|
|
1044
1037
|
declare function stripHex(hex: string): string;
|
|
1045
1038
|
/** Pad hex to 64 chars (32 bytes) with 0x prefix */
|
|
1046
1039
|
declare function padHex64(hex: string): string;
|
|
1047
|
-
/**
|
|
1040
|
+
/**
|
|
1041
|
+
* Convert a hex string (with or without 0x prefix) to a Uint8Array.
|
|
1042
|
+
* Throws on invalid hex (non-hex characters, odd length).
|
|
1043
|
+
*/
|
|
1048
1044
|
declare function hexToBytes(hex: string): Uint8Array;
|
|
1049
1045
|
/** Convert a Uint8Array to a hex string (no 0x prefix). */
|
|
1050
1046
|
declare function bytesToHex(bytes: Uint8Array): string;
|
package/dist/index.js
CHANGED
|
@@ -87,8 +87,8 @@ var HardwareErrorCode = /* @__PURE__ */ ((HardwareErrorCode2) => {
|
|
|
87
87
|
function success(payload) {
|
|
88
88
|
return { success: true, payload };
|
|
89
89
|
}
|
|
90
|
-
function failure(code, error
|
|
91
|
-
return { success: false, payload: { error, code
|
|
90
|
+
function failure(code, error) {
|
|
91
|
+
return { success: false, payload: { error, code } };
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// src/types/fingerprint.ts
|
|
@@ -102,8 +102,8 @@ var CHAIN_FINGERPRINT_PATHS = {
|
|
|
102
102
|
sol: "m/44'/501'/0'",
|
|
103
103
|
tron: "m/44'/195'/0'/0/0"
|
|
104
104
|
};
|
|
105
|
-
function deriveDeviceFingerprint(
|
|
106
|
-
return (0, import_utils.bytesToHex)((0, import_sha256.sha256)((0, import_utils.utf8ToBytes)(
|
|
105
|
+
function deriveDeviceFingerprint(value) {
|
|
106
|
+
return (0, import_utils.bytesToHex)((0, import_sha256.sha256)((0, import_utils.utf8ToBytes)(value))).slice(0, 16);
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// src/events/device.ts
|
|
@@ -111,11 +111,7 @@ var DEVICE_EVENT = "DEVICE_EVENT";
|
|
|
111
111
|
var DEVICE = {
|
|
112
112
|
CONNECT: "device-connect",
|
|
113
113
|
DISCONNECT: "device-disconnect",
|
|
114
|
-
CHANGED: "device-changed"
|
|
115
|
-
ACQUIRE: "device-acquire",
|
|
116
|
-
RELEASE: "device-release",
|
|
117
|
-
FEATURES: "features",
|
|
118
|
-
SUPPORT_FEATURES: "support_features"
|
|
114
|
+
CHANGED: "device-changed"
|
|
119
115
|
};
|
|
120
116
|
|
|
121
117
|
// src/events/ui-request.ts
|
|
@@ -201,6 +197,8 @@ var DeviceJobQueue = class {
|
|
|
201
197
|
);
|
|
202
198
|
case "wait":
|
|
203
199
|
break;
|
|
200
|
+
default:
|
|
201
|
+
break;
|
|
204
202
|
}
|
|
205
203
|
break;
|
|
206
204
|
}
|
|
@@ -297,9 +295,10 @@ var EConnectorInteraction = /* @__PURE__ */ ((EConnectorInteraction2) => {
|
|
|
297
295
|
EConnectorInteraction2["InteractionComplete"] = "interaction-complete";
|
|
298
296
|
return EConnectorInteraction2;
|
|
299
297
|
})(EConnectorInteraction || {});
|
|
300
|
-
function createBridgedConnector(vendor, bridge) {
|
|
298
|
+
function createBridgedConnector(vendor, connectionType, bridge) {
|
|
301
299
|
const handlerMap = /* @__PURE__ */ new Map();
|
|
302
300
|
return {
|
|
301
|
+
connectionType,
|
|
303
302
|
searchDevices: () => bridge.searchDevices({ vendor }),
|
|
304
303
|
connect: (deviceId) => bridge.connect({ vendor, deviceId }),
|
|
305
304
|
disconnect: (sessionId) => bridge.disconnect({ vendor, sessionId }),
|
|
@@ -472,6 +471,7 @@ function compareSemver(a, b) {
|
|
|
472
471
|
}
|
|
473
472
|
|
|
474
473
|
// src/utils/hex.ts
|
|
474
|
+
var import_utils2 = require("@noble/hashes/utils");
|
|
475
475
|
function ensure0x(hex) {
|
|
476
476
|
return hex.startsWith("0x") ? hex : `0x${hex}`;
|
|
477
477
|
}
|
|
@@ -482,15 +482,10 @@ function padHex64(hex) {
|
|
|
482
482
|
return `0x${stripHex(hex).padStart(64, "0")}`;
|
|
483
483
|
}
|
|
484
484
|
function hexToBytes(hex) {
|
|
485
|
-
|
|
486
|
-
const bytes = new Uint8Array(clean.length / 2);
|
|
487
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
488
|
-
bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
|
|
489
|
-
}
|
|
490
|
-
return bytes;
|
|
485
|
+
return (0, import_utils2.hexToBytes)(stripHex(hex));
|
|
491
486
|
}
|
|
492
487
|
function bytesToHex2(bytes) {
|
|
493
|
-
return
|
|
488
|
+
return (0, import_utils2.bytesToHex)(bytes);
|
|
494
489
|
}
|
|
495
490
|
|
|
496
491
|
// src/utils/errorMessages.ts
|
|
@@ -521,15 +516,15 @@ function enrichErrorMessage(code, originalMessage) {
|
|
|
521
516
|
case 9 /* OperationTimeout */:
|
|
522
517
|
return `${originalMessage}. The operation timed out. Please try again.`;
|
|
523
518
|
case 7001 /* EvmBlindSigningRequired */:
|
|
524
|
-
return "
|
|
519
|
+
return "Ledger: Blind signing not enabled in Ethereum app.";
|
|
525
520
|
case 7002 /* EvmClearSignPluginMissing */:
|
|
526
|
-
return "Required
|
|
521
|
+
return "Ledger: Required plugin not installed (install via Ledger Live).";
|
|
527
522
|
case 7003 /* EvmDataTooLarge */:
|
|
528
|
-
return "Transaction data
|
|
523
|
+
return "Ledger: Transaction data too large for device memory.";
|
|
529
524
|
case 7004 /* EvmTxTypeNotSupported */:
|
|
530
|
-
return "Transaction type
|
|
525
|
+
return "Ledger: Transaction type not supported by current Ethereum app.";
|
|
531
526
|
case 7005 /* AppTooOld */:
|
|
532
|
-
return "
|
|
527
|
+
return "Ledger: App out of date (update via Ledger Live).";
|
|
533
528
|
default:
|
|
534
529
|
return originalMessage;
|
|
535
530
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/types/errors.ts","../src/types/response.ts","../src/types/fingerprint.ts","../src/events/device.ts","../src/events/ui-request.ts","../src/events/sdk.ts","../src/utils/DeviceJobQueue.ts","../src/types/connector.ts","../src/utils/TypedEventEmitter.ts","../src/utils/UiRequestRegistry.ts","../src/utils/semver.ts","../src/utils/hex.ts","../src/utils/errorMessages.ts","../src/utils/batchCall.ts"],"sourcesContent":["export { HardwareErrorCode } from './types/errors';\n\nexport type { Success, Failure, Response } from './types/response';\nexport { success, failure } from './types/response';\n\nexport type {\n VendorType,\n ConnectionType,\n TransportType,\n DeviceInfo,\n DeviceTarget,\n DeviceCapabilities,\n} from './types/device';\n\nexport type {\n EvmGetAddressParams,\n EvmAddress,\n EvmSignTxParams,\n EvmSignedTx,\n EvmSignMsgParams,\n EvmSignTypedDataParams,\n EvmSignTypedDataFull,\n EvmSignTypedDataHash,\n EIP712Domain,\n EvmSignature,\n ProgressCallback,\n IEvmMethods,\n} from './types/chain-evm';\n\nexport type {\n BtcGetAddressParams,\n BtcAddress,\n BtcGetPublicKeyParams,\n BtcPublicKey,\n BtcSignTxParams,\n BtcTxInput,\n BtcTxOutput,\n BtcRefTransaction,\n BtcSignedTx,\n BtcSignPsbtParams,\n BtcSignedPsbt,\n BtcSignMsgParams,\n BtcSignature,\n IBtcMethods,\n} from './types/chain-btc';\n\nexport type {\n SolGetAddressParams,\n SolAddress,\n SolSignTxParams,\n SolSignedTx,\n SolSignMsgParams,\n SolSignature,\n ISolMethods,\n} from './types/chain-sol';\n\nexport type {\n TronGetAddressParams,\n TronAddress,\n TronSignTxParams,\n TronSignedTx,\n TronSignMsgParams,\n TronSignature,\n ITronMethods,\n} from './types/chain-tron';\n\nexport type { QrDisplayData, QrResponseData } from './types/qr';\n\nexport type { ChainForFingerprint } from './types/fingerprint';\nexport { CHAIN_FINGERPRINT_PATHS, deriveDeviceFingerprint } from './types/fingerprint';\n\nexport type {\n IHardwareWallet,\n IUiHandler,\n PassphraseResponse,\n ChainCapability,\n DeviceEvent,\n UiRequestEvent,\n SdkEvent,\n HardwareEvent,\n HardwareEventMap,\n DeviceEventListener,\n} from './types/wallet';\n\nexport { DEVICE_EVENT, DEVICE } from './events/device';\nexport { UI_EVENT, UI_REQUEST, UI_RESPONSE } from './events/ui-request';\nexport type { UiResponseEvent } from './events/ui-request';\nexport { SDK } from './events/sdk';\n\nexport type {\n DeviceDescriptor,\n DeviceConnectEvent,\n DeviceDisconnectEvent,\n DeviceChangeEvent,\n} from './types/transport';\n\nexport { DeviceJobQueue } from './utils/DeviceJobQueue';\nexport type {\n Interruptibility,\n PreemptionDecision,\n JobOptions,\n ActiveJobInfo,\n PreemptionEvent,\n DeviceJobQueueDeps,\n} from './utils/DeviceJobQueue';\n\nexport type {\n ConnectorDevice,\n ConnectorSession,\n ConnectorEventType,\n ConnectorEventMap,\n IConnector,\n IHardwareBridge,\n} from './types/connector';\nexport { createBridgedConnector, EConnectorInteraction } from './types/connector';\n\nexport { TypedEventEmitter } from './utils/TypedEventEmitter';\nexport {\n UiRequestRegistry,\n UI_REQUEST_DEFAULT_TIMEOUT_MS,\n UI_REQUEST_PREEMPTED_TAG,\n UI_REQUEST_CANCELLED_TAG,\n UI_REQUEST_TIMEOUT_TAG,\n} from './utils/UiRequestRegistry';\nexport { compareSemver } from './utils/semver';\nexport { ensure0x, stripHex, padHex64, hexToBytes, bytesToHex } from './utils/hex';\nexport { enrichErrorMessage } from './utils/errorMessages';\nexport { batchCall } from './utils/batchCall';\n","export enum HardwareErrorCode {\n UnknownError = 0,\n DeviceNotFound = 1,\n DeviceDisconnected = 2,\n UserRejected = 3,\n DeviceBusy = 4,\n FirmwareUpdateRequired = 5,\n AppNotOpen = 6,\n InvalidParams = 7,\n TransportError = 8,\n OperationTimeout = 9,\n MethodNotSupported = 10,\n\n // PIN / Passphrase\n PinInvalid = 5520,\n PinCancelled = 5521,\n PassphraseRejected = 5522,\n\n // Device state\n DeviceLocked = 5530,\n DeviceNotInitialized = 5531,\n DeviceInBootloader = 5532,\n FirmwareTooOld = 5533,\n\n // Ledger specific\n WrongApp = 5540,\n\n // Device identity\n DeviceMismatch = 5560,\n\n // Transport\n BridgeNotFound = 5550,\n TransportNotAvailable = 5551,\n\n // --- EVM (Ledger Ethereum App) APDU-specific errors ---\n /** 0x6a80 Invalid data — observed on blindSignTransactionFallback when the\n * user has not enabled Blind signing on the device. */\n EvmBlindSigningRequired = 7001,\n /** 0x6984 Plugin not installed */\n EvmClearSignPluginMissing = 7002,\n /** 0x6a84 Insufficient memory (typical on Nano S with large calldata) */\n EvmDataTooLarge = 7003,\n /** 0x6501 TransactionType not supported (app too old for EIP-1559 / blob / 7702) */\n EvmTxTypeNotSupported = 7004,\n /** 0x911c Command code not supported — app predates current SDK */\n AppTooOld = 7005,\n}\n","import type { HardwareErrorCode } from './errors';\n\nexport interface Success<T> {\n success: true;\n payload: T;\n}\n\nexport interface Failure {\n success: false;\n payload: {\n error: string;\n code: HardwareErrorCode;\n appName?: string;\n };\n}\n\nexport type Response<T> = Success<T> | Failure;\n\nexport function success<T>(payload: T): Success<T> {\n return { success: true, payload };\n}\n\nexport function failure(code: HardwareErrorCode, error: string, appName?: string): Failure {\n return { success: false, payload: { error, code, appName } };\n}\n","/**\n * Chain fingerprint utilities for device identity verification.\n *\n * Ledger devices have ephemeral IDs that change every session.\n * To verify that the same seed/device is connected, we derive an address\n * at a fixed path (account 0, index 0) and hash it into a stable \"chain fingerprint\".\n */\nimport { sha256 } from '@noble/hashes/sha256';\nimport { bytesToHex, utf8ToBytes } from '@noble/hashes/utils';\n\n/**\n * Fixed derivation paths used to generate chain fingerprints.\n * Uses standard index 0 — Ledger firmware shows device confirmation for\n * non-standard paths (e.g., index=100), which would interrupt wallet creation.\n * The address is hashed into a 16-char fingerprint, not exposed directly.\n * - EVM: cointype 60 (Ledger ETH App only supports 60)\n * - BTC: cointype 1 (testnet)\n * - SOL: cointype 501, standard 3-level hardened\n */\nexport const CHAIN_FINGERPRINT_PATHS: Record<ChainForFingerprint, string> = {\n evm: \"m/44'/60'/0'/0/0\",\n // BTC: account-level path (3 levels), mainnet cointype 0.\n // Cointype 1 (testnet) is rejected by some Ledger BTC App configurations.\n btc: \"m/44'/0'/0'\",\n sol: \"m/44'/501'/0'\",\n tron: \"m/44'/195'/0'/0/0\",\n};\n\nexport type ChainForFingerprint = 'evm' | 'btc' | 'sol' | 'tron';\n\n/**\n * Hash an address string into a 16-character hex fingerprint.\n *\n * Uses the first 16 hex chars (64 bits) of SHA-256 — sufficient for\n * device identity matching. Not used for security decisions.\n */\nexport function deriveDeviceFingerprint(address: string): string {\n return bytesToHex(sha256(utf8ToBytes(address))).slice(0, 16);\n}\n","export const DEVICE_EVENT = 'DEVICE_EVENT';\n\n/** Events originating from the hardware device. */\nexport const DEVICE = {\n CONNECT: 'device-connect',\n DISCONNECT: 'device-disconnect',\n CHANGED: 'device-changed',\n ACQUIRE: 'device-acquire',\n RELEASE: 'device-release',\n FEATURES: 'features',\n SUPPORT_FEATURES: 'support_features',\n} as const;\n","import type { QrResponseData } from '../types/qr';\nimport type { PreemptionDecision } from '../utils/DeviceJobQueue';\n\nexport const UI_EVENT = 'UI_EVENT';\n\nexport const UI_REQUEST = {\n REQUEST_PIN: 'ui-request-pin',\n REQUEST_PASSPHRASE: 'ui-request-passphrase',\n REQUEST_PASSPHRASE_ON_DEVICE: 'ui-request-passphrase-on-device',\n REQUEST_BUTTON: 'ui-request-button',\n REQUEST_QR_DISPLAY: 'ui-request-qr-display',\n REQUEST_QR_SCAN: 'ui-request-qr-scan',\n REQUEST_DEVICE_PERMISSION: 'ui-request-device-permission',\n REQUEST_SELECT_DEVICE: 'ui-request-select-device',\n REQUEST_DEVICE_CONNECT: 'ui-request-device-connect',\n REQUEST_PREEMPTION: 'ui-request-preemption',\n CLOSE_UI_WINDOW: 'ui-close',\n DEVICE_PROGRESS: 'ui-device_progress',\n FIRMWARE_PROGRESS: 'ui-firmware-progress',\n FIRMWARE_TIP: 'ui-firmware-tip',\n} as const;\n\nexport const UI_RESPONSE = {\n RECEIVE_PIN: 'receive-pin',\n RECEIVE_PASSPHRASE: 'receive-passphrase',\n RECEIVE_PASSPHRASE_ON_DEVICE: 'receive-passphrase-on-device',\n RECEIVE_QR_RESPONSE: 'receive-qr-response',\n RECEIVE_SELECT_DEVICE: 'receive-select-device',\n RECEIVE_DEVICE_CONNECT: 'receive-device-connect',\n RECEIVE_PREEMPTION: 'receive-preemption',\n CANCEL: 'cancel',\n} as const;\n\nexport type UiResponseEvent =\n | {\n type: typeof UI_RESPONSE.RECEIVE_PIN;\n payload: string;\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PASSPHRASE;\n payload: {\n value: string;\n passphraseOnDevice?: boolean;\n save?: boolean;\n };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PASSPHRASE_ON_DEVICE;\n payload?: undefined;\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_QR_RESPONSE;\n payload: QrResponseData;\n }\n | {\n // sdkConnectId echoes one of the DeviceInfo.connectId values emitted in\n // the REQUEST_SELECT_DEVICE event's `devices` list. Scope is the current\n // search session; may be ephemeral (e.g. Ledger USB DMK UUID).\n type: typeof UI_RESPONSE.RECEIVE_SELECT_DEVICE;\n payload: { sdkConnectId: string };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_DEVICE_CONNECT;\n payload: { confirmed: boolean };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PREEMPTION;\n payload: { decision: PreemptionDecision };\n }\n | {\n type: typeof UI_RESPONSE.CANCEL;\n payload?: undefined;\n };\n","/** Events generated by SDK internal detection (not from hardware directly). */\nexport const SDK = {\n DEVICE_STUCK: 'device-stuck',\n DEVICE_UNRESPONSIVE: 'device-unresponsive',\n DEVICE_RECOVERED: 'device-recovered',\n DEVICE_INTERACTION: 'device-interaction',\n} as const;\n","/**\n * Per-device serial job queue with preemption support and stuck recovery.\n * Ensures that only one operation runs at a time per device, with intelligent\n * handling of conflicting operations.\n *\n * The 'confirm' level uses the standard UI request/response flow:\n * emits REQUEST_PREEMPTION → waits for RECEIVE_PREEMPTION via UiRequestRegistry.\n */\n\nimport { UI_REQUEST } from '../events/ui-request';\n\nimport type { UiRequestRegistry } from './UiRequestRegistry';\n\nexport type Interruptibility = 'none' | 'safe' | 'confirm';\n\nexport type PreemptionDecision = 'cancel-current' | 'wait' | 'reject-new';\n\nexport interface JobOptions {\n interruptibility?: Interruptibility;\n label?: string;\n}\n\nexport interface ActiveJobInfo {\n label?: string;\n interruptibility: Interruptibility;\n startedAt: number;\n}\n\nexport interface PreemptionEvent {\n deviceId: string;\n currentJob: ActiveJobInfo;\n newJob: { label?: string; interruptibility: Interruptibility };\n}\n\ninterface ActiveJob {\n options: Required<Pick<JobOptions, 'interruptibility'>> & Pick<JobOptions, 'label'>;\n abortController: AbortController;\n startedAt: number;\n}\n\nexport interface DeviceJobQueueDeps {\n /** Emit a UI request event to the frontend. */\n emit: (event: string, data: unknown) => void;\n /** Registry for waiting on UI responses. */\n uiRegistry: UiRequestRegistry;\n}\n\nexport class DeviceJobQueue {\n private readonly _queues = new Map<string, Promise<unknown>>();\n\n private readonly _active = new Map<string, ActiveJob>();\n\n private readonly _deps: DeviceJobQueueDeps | null;\n\n constructor(deps?: DeviceJobQueueDeps) {\n this._deps = deps ?? null;\n }\n\n /**\n * Enqueue a job for a specific device.\n * If a job is already running for this device, behavior depends on interruptibility:\n * - 'none': new job queues silently (no preemption possible)\n * - 'safe': current job is auto-cancelled, new job runs immediately after\n * - 'confirm': emits REQUEST_PREEMPTION and waits for UI response\n */\n async enqueue<T>(\n deviceId: string,\n job: (signal: AbortSignal) => Promise<T>,\n options: JobOptions = {}\n ): Promise<T> {\n const interruptibility = options.interruptibility ?? 'confirm';\n const active = this._active.get(deviceId);\n\n if (active) {\n switch (active.options.interruptibility) {\n case 'none':\n // Cannot interrupt, just queue behind\n break;\n case 'safe':\n // Auto-cancel current safe operation\n active.abortController.abort(new Error('Preempted by new operation'));\n break;\n case 'confirm': {\n // If a preemption dialog is already pending, reject immediately.\n // The user is already deciding for a previous request — don't\n // overwrite the UiRequestRegistry slot (single-slot per type).\n if (this._deps?.uiRegistry.hasPending(UI_REQUEST.REQUEST_PREEMPTION)) {\n throw Object.assign(\n new Error(`Device busy: a preemption decision is already pending`),\n { hardwareErrorCode: 'DEVICE_BUSY' }\n );\n }\n const decision = await this._requestPreemptionDecision(deviceId, active, {\n label: options.label,\n interruptibility,\n });\n switch (decision) {\n case 'cancel-current':\n active.abortController.abort(new Error('Cancelled by user via preemption'));\n break;\n case 'reject-new':\n throw Object.assign(\n new Error(`Device busy: ${active.options.label ?? 'unknown operation'}`),\n { hardwareErrorCode: 'DEVICE_BUSY' }\n );\n case 'wait':\n break;\n }\n break;\n }\n }\n }\n\n const ac = new AbortController();\n const prev = this._queues.get(deviceId) ?? Promise.resolve();\n\n const next = prev\n .catch(() => {})\n .then(async () => {\n this._active.set(deviceId, {\n options: { interruptibility, label: options.label },\n abortController: ac,\n startedAt: Date.now(),\n });\n try {\n return await job(ac.signal);\n } finally {\n this._active.delete(deviceId);\n }\n });\n\n const tail = next.catch(() => {});\n this._queues.set(deviceId, tail);\n tail.then(() => {\n if (this._queues.get(deviceId) === tail) {\n this._queues.delete(deviceId);\n }\n });\n return next;\n }\n\n /** Manually cancel the active job on a device. Returns false if job is non-interruptible. */\n cancelActive(deviceId: string): boolean {\n const active = this._active.get(deviceId);\n if (!active) return false;\n if (active.options.interruptibility === 'none') return false;\n active.abortController.abort(new Error('Manually cancelled'));\n return true;\n }\n\n /** Force cancel regardless of interruptibility. Use for device stuck recovery. */\n forceCancelActive(deviceId: string): boolean {\n const active = this._active.get(deviceId);\n if (!active) return false;\n active.abortController.abort(new Error('Force cancelled for recovery'));\n return true;\n }\n\n /** Get info about the currently active job for a device, or null if idle. */\n getActiveJob(deviceId: string): ActiveJobInfo | null {\n const active = this._active.get(deviceId);\n if (!active) return null;\n return {\n label: active.options.label,\n interruptibility: active.options.interruptibility,\n startedAt: active.startedAt,\n };\n }\n\n clear(): void {\n for (const active of this._active.values()) {\n active.abortController.abort(new Error('Queue cleared'));\n }\n this._active.clear();\n this._queues.clear();\n }\n\n /**\n * Request preemption decision via UI request/response flow.\n * Falls back to 'wait' if deps are not provided.\n */\n private async _requestPreemptionDecision(\n deviceId: string,\n active: ActiveJob,\n newJob: { label?: string; interruptibility: Interruptibility }\n ): Promise<PreemptionDecision> {\n if (!this._deps) return 'wait';\n\n const { emit, uiRegistry } = this._deps;\n\n emit(UI_REQUEST.REQUEST_PREEMPTION, {\n type: UI_REQUEST.REQUEST_PREEMPTION,\n payload: {\n deviceId,\n currentJob: {\n label: active.options.label,\n interruptibility: active.options.interruptibility,\n startedAt: active.startedAt,\n },\n newJob,\n },\n });\n\n const response = await uiRegistry.wait<{ decision: PreemptionDecision }>(\n UI_REQUEST.REQUEST_PREEMPTION\n );\n\n return response?.decision ?? 'wait';\n }\n}\n","import type { DeviceCapabilities, DeviceInfo, VendorType } from './device';\nimport type { UiResponseEvent } from '../events/ui-request';\n\n// =====================================================================\n// Connector types — transport-level abstraction for device communication\n// =====================================================================\n\n/**\n * Minimal device info returned during discovery (searchDevices).\n * At scan time, full DeviceInfo fields like firmwareVersion are not yet available.\n */\nexport interface ConnectorDevice {\n connectId: string;\n deviceId: string;\n name: string;\n model?: string;\n\n /** Device capabilities — available from scan time */\n capabilities?: DeviceCapabilities;\n}\n\nexport interface ConnectorSession {\n sessionId: string;\n deviceInfo: DeviceInfo;\n}\n\nexport type ConnectorEventType = 'device-connect' | 'device-disconnect' | 'ui-request' | 'ui-event';\n\n/**\n * Interaction event types emitted via 'ui-event'.\n * These map to user-facing prompts (confirm on device, open app, etc.).\n */\nexport enum EConnectorInteraction {\n /** Device requires user to open a specific app */\n ConfirmOpenApp = 'confirm-open-app',\n /** Device requires user to unlock */\n UnlockDevice = 'unlock-device',\n /** Device needs user to confirm on device (sign, verify, etc.) */\n ConfirmOnDevice = 'confirm-on-device',\n /** Previous interaction completed — clear UI prompt */\n InteractionComplete = 'interaction-complete',\n}\n\nexport type ConnectorUiEvent =\n | { type: EConnectorInteraction.ConfirmOpenApp; payload: { sessionId: string } }\n | { type: EConnectorInteraction.UnlockDevice; payload: { sessionId: string } }\n | { type: EConnectorInteraction.ConfirmOnDevice; payload: { sessionId: string } }\n | { type: EConnectorInteraction.InteractionComplete; payload: { sessionId: string } };\n\nexport interface ConnectorEventMap {\n 'device-connect': { device: ConnectorDevice };\n 'device-disconnect': { connectId: string };\n 'ui-request': { type: string; payload?: unknown };\n 'ui-event': ConnectorUiEvent;\n}\n\nexport interface IConnector {\n searchDevices(): Promise<ConnectorDevice[]>;\n connect(deviceId?: string): Promise<ConnectorSession>;\n disconnect(sessionId: string): Promise<void>;\n call(sessionId: string, method: string, params: unknown): Promise<unknown>;\n cancel(sessionId: string): Promise<void>;\n\n /** Send a UI response (e.g. PIN, passphrase) to the device. */\n uiResponse(response: UiResponseEvent): void;\n\n on<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;\n off<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;\n\n reset(): void;\n}\n\n// =====================================================================\n// Hardware bridge — generic forwarding interface for cross-boundary IConnector\n//\n// The same shape works across any process / context boundary that needs to\n// expose a multi-vendor backend behind a single-vendor IConnector facade:\n// - Electron (renderer ↔ preload/main via contextBridge)\n// - Browser Extension (popup/content ↔ background/offscreen via chrome.runtime)\n// - React Native (JS ↔ native module via NativeModules)\n// - Web Worker / iframe (postMessage)\n//\n// Each method takes a `vendor` discriminator so a single bridge implementation\n// can multiplex across vendors (ledger, trezor, ...).\n// =====================================================================\n\nexport interface IHardwareBridge {\n searchDevices(params: { vendor: VendorType }): Promise<ConnectorDevice[]>;\n connect(params: { vendor: VendorType; deviceId?: string }): Promise<ConnectorSession>;\n disconnect(params: { vendor: VendorType; sessionId: string }): Promise<void>;\n call(params: {\n vendor: VendorType;\n sessionId: string;\n method: string;\n callParams: unknown;\n }): Promise<unknown>;\n cancel(params: { vendor: VendorType; sessionId: string }): Promise<void>;\n uiResponse(params: { vendor: VendorType; response: UiResponseEvent }): void;\n reset(params: { vendor: VendorType }): void;\n\n /** Register an event handler for connector events forwarded across the bridge. */\n onEvent(\n params: { vendor: VendorType },\n handler: (event: { type: ConnectorEventType; data: unknown }) => void\n ): void;\n\n /** Unregister a previously registered event handler. */\n offEvent(\n params: { vendor: VendorType },\n handler: (event: { type: ConnectorEventType; data: unknown }) => void\n ): void;\n}\n\n/**\n * Adapt an IHardwareBridge (multi-vendor backend) into a single-vendor IConnector.\n * Every IConnector method becomes a transparent forward to bridge.<method>({ vendor, ... }).\n * Events are forwarded via bridge.onEvent / offEvent.\n *\n * Use this anywhere the actual hardware lives behind a process / context boundary\n * (Electron main, extension background, native module, worker, iframe).\n */\nexport function createBridgedConnector(vendor: VendorType, bridge: IHardwareBridge): IConnector {\n // Map from typed IConnector handlers to the bridge handler so we can\n // unregister them correctly via off().\n const handlerMap = new Map<\n (data: ConnectorEventMap[ConnectorEventType]) => void,\n (event: { type: ConnectorEventType; data: unknown }) => void\n >();\n\n return {\n searchDevices: () => bridge.searchDevices({ vendor }),\n connect: deviceId => bridge.connect({ vendor, deviceId }),\n disconnect: sessionId => bridge.disconnect({ vendor, sessionId }),\n call: (sessionId, method, callParams) => bridge.call({ vendor, sessionId, method, callParams }),\n cancel: sessionId => bridge.cancel({ vendor, sessionId }),\n uiResponse: response => bridge.uiResponse({ vendor, response }),\n on: (event, handler) => {\n const bridgeHandler = (e: { type: ConnectorEventType; data: unknown }) => {\n if (e.type === event) {\n handler(e.data as ConnectorEventMap[typeof event]);\n }\n };\n handlerMap.set(\n handler as (data: ConnectorEventMap[ConnectorEventType]) => void,\n bridgeHandler\n );\n bridge.onEvent({ vendor }, bridgeHandler);\n },\n off: (_event, handler) => {\n const bridgeHandler = handlerMap.get(\n handler as (data: ConnectorEventMap[ConnectorEventType]) => void\n );\n if (bridgeHandler) {\n bridge.offEvent({ vendor }, bridgeHandler);\n handlerMap.delete(handler as (data: ConnectorEventMap[ConnectorEventType]) => void);\n }\n },\n reset: () => bridge.reset({ vendor }),\n };\n}\n","/**\n * Minimal typed event emitter using Map<string, Set<listener>>.\n * Each adapter uses this for device events (connect, disconnect, pin, etc.).\n *\n * TMap is a record mapping event name strings to their payload types.\n * Example:\n * type MyEvents = { 'connect': { id: string }; 'disconnect': { id: string } };\n * const emitter = new TypedEventEmitter<MyEvents>();\n * emitter.on('connect', (data) => { data.id }); // data is { id: string }\n *\n * For backward compatibility, TMap defaults to Record<string, any> so that\n * existing code using `new TypedEventEmitter<SomeUnionType>()` still compiles.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class TypedEventEmitter<TMap extends Record<string, any> = Record<string, any>> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private readonly _listeners = new Map<string, Set<(event: any) => void>>();\n\n on<K extends keyof TMap & string>(event: K, listener: (event: TMap[K]) => void): void;\n\n on(event: string, listener: (event: any) => void): void;\n\n on(event: string, listener: (event: any) => void): void {\n let set = this._listeners.get(event);\n if (!set) {\n set = new Set();\n this._listeners.set(event, set);\n }\n set.add(listener);\n }\n\n off<K extends keyof TMap & string>(event: K, listener: (event: TMap[K]) => void): void;\n\n off(event: string, listener: (event: any) => void): void;\n\n off(event: string, listener: (event: any) => void): void {\n const set = this._listeners.get(event);\n if (set) {\n set.delete(listener);\n if (set.size === 0) this._listeners.delete(event);\n }\n }\n\n emit<K extends keyof TMap & string>(event: K, data: TMap[K]): void;\n\n emit(event: string, data: unknown): void;\n\n emit(event: string, data: unknown): void {\n const set = this._listeners.get(event);\n if (set) {\n for (const listener of set) listener(data);\n }\n }\n\n removeAllListeners(): void {\n this._listeners.clear();\n }\n}\n","import { UI_REQUEST, UI_RESPONSE } from '../events/ui-request';\n\n/** 10 min — every UI request is human-in-the-loop; bias toward \"wait long\". */\nexport const UI_REQUEST_DEFAULT_TIMEOUT_MS = 600_000;\n\nconst RESPONSE_TO_REQUEST: Record<string, string> = {\n [UI_RESPONSE.RECEIVE_PIN]: UI_REQUEST.REQUEST_PIN,\n [UI_RESPONSE.RECEIVE_PASSPHRASE]: UI_REQUEST.REQUEST_PASSPHRASE,\n [UI_RESPONSE.RECEIVE_PASSPHRASE_ON_DEVICE]: UI_REQUEST.REQUEST_PASSPHRASE_ON_DEVICE,\n [UI_RESPONSE.RECEIVE_SELECT_DEVICE]: UI_REQUEST.REQUEST_SELECT_DEVICE,\n [UI_RESPONSE.RECEIVE_DEVICE_CONNECT]: UI_REQUEST.REQUEST_DEVICE_CONNECT,\n [UI_RESPONSE.RECEIVE_PREEMPTION]: UI_REQUEST.REQUEST_PREEMPTION,\n // RECEIVE_QR_RESPONSE maps to QR_DISPLAY or QR_SCAN — resolved dynamically below.\n};\n\nexport const UI_REQUEST_PREEMPTED_TAG = 'UiRequestPreempted';\nexport const UI_REQUEST_CANCELLED_TAG = 'UiRequestCancelled';\nexport const UI_REQUEST_TIMEOUT_TAG = 'UiRequestTimeout';\n\ntype PendingEntry = {\n resolve: (payload: unknown) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\n/**\n * Per-type single-slot registry for adapter-level UI requests. A new `wait`\n * of the same type preempts (rejects) the prior one. Unknown response types\n * are dropped silently so the public `uiResponse` entry never throws.\n */\nexport class UiRequestRegistry {\n private pending = new Map<string, PendingEntry>();\n\n wait<T = unknown>(requestType: string, options?: { timeoutMs?: number }): Promise<T> {\n const existing = this.pending.get(requestType);\n if (existing) {\n clearTimeout(existing.timer);\n this.pending.delete(requestType);\n existing.reject(\n Object.assign(new Error(`UI request '${requestType}' was superseded by a new request`), {\n _tag: UI_REQUEST_PREEMPTED_TAG,\n })\n );\n }\n\n const timeoutMs = options?.timeoutMs ?? UI_REQUEST_DEFAULT_TIMEOUT_MS;\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this.pending.get(requestType)?.timer === timer) {\n this.pending.delete(requestType);\n reject(\n Object.assign(new Error(`UI request '${requestType}' timed out after ${timeoutMs}ms`), {\n _tag: UI_REQUEST_TIMEOUT_TAG,\n })\n );\n }\n }, timeoutMs);\n\n this.pending.set(requestType, {\n resolve: resolve as (payload: unknown) => void,\n reject,\n timer,\n });\n });\n }\n\n resolve(responseType: string, payload: unknown): void {\n let requestType = RESPONSE_TO_REQUEST[responseType];\n if (responseType === UI_RESPONSE.RECEIVE_QR_RESPONSE) {\n if (this.pending.has(UI_REQUEST.REQUEST_QR_DISPLAY)) {\n requestType = UI_REQUEST.REQUEST_QR_DISPLAY;\n } else if (this.pending.has(UI_REQUEST.REQUEST_QR_SCAN)) {\n requestType = UI_REQUEST.REQUEST_QR_SCAN;\n }\n }\n if (!requestType) return;\n\n const entry = this.pending.get(requestType);\n if (!entry) return;\n\n clearTimeout(entry.timer);\n this.pending.delete(requestType);\n entry.resolve(payload);\n }\n\n cancel(requestType?: string): void {\n if (requestType) {\n const entry = this.pending.get(requestType);\n if (!entry) return;\n clearTimeout(entry.timer);\n this.pending.delete(requestType);\n entry.reject(\n Object.assign(new Error(`UI request '${requestType}' was cancelled`), {\n _tag: UI_REQUEST_CANCELLED_TAG,\n })\n );\n return;\n }\n\n for (const [type, entry] of this.pending) {\n clearTimeout(entry.timer);\n entry.reject(\n Object.assign(new Error(`UI request '${type}' was cancelled`), {\n _tag: UI_REQUEST_CANCELLED_TAG,\n })\n );\n }\n this.pending.clear();\n }\n\n reset(): void {\n this.cancel();\n }\n\n hasPending(requestType: string): boolean {\n return this.pending.has(requestType);\n }\n}\n","/**\n * Compare two semver strings (e.g. \"2.1.0\" vs \"2.3.1\").\n * Returns -1 if a < b, 0 if equal, 1 if a > b.\n */\nexport function compareSemver(a: string, b: string): number {\n const pa = a.split('.').map(Number);\n const pb = b.split('.').map(Number);\n for (let i = 0; i < 3; i++) {\n const va = pa[i] ?? 0;\n const vb = pb[i] ?? 0;\n if (va < vb) return -1;\n if (va > vb) return 1;\n }\n return 0;\n}\n","/** Ensure hex string has 0x prefix */\nexport function ensure0x(hex: string): string {\n return hex.startsWith('0x') ? hex : `0x${hex}`;\n}\n\n/** Strip 0x prefix from hex string */\nexport function stripHex(hex: string): string {\n return hex.startsWith('0x') ? hex.slice(2) : hex;\n}\n\n/** Pad hex to 64 chars (32 bytes) with 0x prefix */\nexport function padHex64(hex: string): string {\n return `0x${stripHex(hex).padStart(64, '0')}`;\n}\n\n/** Convert a hex string (with or without 0x prefix) to a Uint8Array. */\nexport function hexToBytes(hex: string): Uint8Array {\n const clean = stripHex(hex);\n const bytes = new Uint8Array(clean.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/** Convert a Uint8Array to a hex string (no 0x prefix). */\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n}\n","import { HardwareErrorCode } from '../types/errors';\n\n/**\n * Enrich a hardware error message with actionable recovery hints.\n * Shared across adapters (Ledger, Trezor, etc.).\n */\nexport function enrichErrorMessage(code: HardwareErrorCode, originalMessage: string): string {\n switch (code) {\n case HardwareErrorCode.PinInvalid:\n return `${originalMessage}. Please re-enter your PIN.`;\n case HardwareErrorCode.PinCancelled:\n return `${originalMessage}. PIN entry was cancelled.`;\n case HardwareErrorCode.DeviceBusy:\n return `${originalMessage}. The device is in use by another application. Close other wallet apps and try again.`;\n case HardwareErrorCode.DeviceDisconnected:\n return `${originalMessage}. Please reconnect the device and try again.`;\n case HardwareErrorCode.DeviceLocked:\n return `${originalMessage}. Please unlock your device and try again.`;\n case HardwareErrorCode.UserRejected:\n return `${originalMessage}. The request was rejected on the device.`;\n case HardwareErrorCode.WrongApp:\n return `${originalMessage}. Please open the correct app on your device.`;\n case HardwareErrorCode.AppNotOpen:\n return `${originalMessage}. The required app is not installed on the device.`;\n case HardwareErrorCode.TransportNotAvailable:\n return `${originalMessage}. Ensure the device bridge/transport is available and running.`;\n case HardwareErrorCode.FirmwareTooOld:\n return `${originalMessage}. Please update your device firmware.`;\n case HardwareErrorCode.DeviceNotInitialized:\n return `${originalMessage}. The device may need to be set up first.`;\n case HardwareErrorCode.OperationTimeout:\n return `${originalMessage}. The operation timed out. Please try again.`;\n case HardwareErrorCode.EvmBlindSigningRequired:\n return 'Please enable Blind signing on your Ledger: open the Ethereum app → Settings → Blind signing → Enabled, then try again.';\n case HardwareErrorCode.EvmClearSignPluginMissing:\n return 'Required Ledger plugin is not installed. Please install the matching plugin on Ledger Live, then try again.';\n case HardwareErrorCode.EvmDataTooLarge:\n return 'Transaction data is too large for your Ledger device. Try a simpler transaction or use a Ledger with more memory.';\n case HardwareErrorCode.EvmTxTypeNotSupported:\n return 'Transaction type is not supported by your Ledger Ethereum app. Please update the app to the latest version.';\n case HardwareErrorCode.AppTooOld:\n return 'Your Ledger app is out of date. Please update it via Ledger Live.';\n default:\n return originalMessage;\n }\n}\n","import type { Response } from '../types/response';\n\n/**\n * Generic batch call with progress reporting.\n * If any single call fails, returns the failure immediately.\n */\nexport async function batchCall<TParam, TResult>(\n params: TParam[],\n callFn: (p: TParam) => Promise<Response<TResult>>,\n onProgress?: (progress: { index: number; total: number }) => void\n): Promise<Response<TResult[]>> {\n const results: TResult[] = [];\n for (let i = 0; i < params.length; i++) {\n const result = await callFn(params[i]);\n if (!result.success) {\n return result;\n }\n results.push(result.payload);\n onProgress?.({ index: i, total: params.length });\n }\n return { success: true, payload: results };\n}\n"],"mappings":"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","names":["bytesToHex","HardwareErrorCode","EConnectorInteraction","bytesToHex"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/types/errors.ts","../src/types/response.ts","../src/types/fingerprint.ts","../src/events/device.ts","../src/events/ui-request.ts","../src/events/sdk.ts","../src/utils/DeviceJobQueue.ts","../src/types/connector.ts","../src/utils/TypedEventEmitter.ts","../src/utils/UiRequestRegistry.ts","../src/utils/semver.ts","../src/utils/hex.ts","../src/utils/errorMessages.ts","../src/utils/batchCall.ts"],"sourcesContent":["export { HardwareErrorCode } from './types/errors';\n\nexport type { Success, Failure, Response } from './types/response';\nexport { success, failure } from './types/response';\n\nexport type {\n VendorType,\n ConnectionType,\n TransportType,\n DeviceInfo,\n DeviceTarget,\n DeviceCapabilities,\n} from './types/device';\n\nexport type {\n EvmGetAddressParams,\n EvmAddress,\n EvmSignTxParams,\n EvmSignedTx,\n EvmSignMsgParams,\n EvmSignTypedDataParams,\n EvmSignTypedDataFull,\n EvmSignTypedDataHash,\n EIP712Domain,\n EvmSignature,\n ProgressCallback,\n IEvmMethods,\n} from './types/chain-evm';\n\nexport type {\n BtcGetAddressParams,\n BtcAddress,\n BtcGetPublicKeyParams,\n BtcPublicKey,\n BtcSignTxParams,\n BtcTxInput,\n BtcTxOutput,\n BtcRefTransaction,\n BtcSignedTx,\n BtcSignPsbtParams,\n BtcSignedPsbt,\n BtcSignMsgParams,\n BtcSignature,\n IBtcMethods,\n} from './types/chain-btc';\n\nexport type {\n SolGetAddressParams,\n SolAddress,\n SolSignTxParams,\n SolSignedTx,\n SolSignMsgParams,\n SolSignature,\n ISolMethods,\n} from './types/chain-sol';\n\nexport type {\n TronGetAddressParams,\n TronAddress,\n TronSignTxParams,\n TronSignedTx,\n TronSignMsgParams,\n TronSignature,\n ITronMethods,\n} from './types/chain-tron';\n\nexport type { QrDisplayData, QrResponseData } from './types/qr';\n\nexport type { ChainForFingerprint } from './types/fingerprint';\nexport { CHAIN_FINGERPRINT_PATHS, deriveDeviceFingerprint } from './types/fingerprint';\n\nexport type {\n IHardwareWallet,\n IUiHandler,\n PassphraseResponse,\n ChainCapability,\n DeviceEvent,\n UiRequestEvent,\n SdkEvent,\n HardwareEvent,\n HardwareEventMap,\n DeviceEventListener,\n} from './types/wallet';\n\nexport { DEVICE_EVENT, DEVICE } from './events/device';\nexport { UI_EVENT, UI_REQUEST, UI_RESPONSE } from './events/ui-request';\nexport type { UiResponseEvent } from './events/ui-request';\nexport { SDK } from './events/sdk';\n\nexport type {\n DeviceDescriptor,\n DeviceConnectEvent,\n DeviceDisconnectEvent,\n DeviceChangeEvent,\n} from './types/transport';\n\nexport { DeviceJobQueue } from './utils/DeviceJobQueue';\nexport type {\n Interruptibility,\n PreemptionDecision,\n JobOptions,\n ActiveJobInfo,\n PreemptionEvent,\n DeviceJobQueueDeps,\n} from './utils/DeviceJobQueue';\n\nexport type {\n ConnectorDevice,\n ConnectorSession,\n ConnectorEventType,\n ConnectorEventMap,\n IConnector,\n IHardwareBridge,\n} from './types/connector';\nexport { createBridgedConnector, EConnectorInteraction } from './types/connector';\n\nexport { TypedEventEmitter } from './utils/TypedEventEmitter';\nexport {\n UiRequestRegistry,\n UI_REQUEST_DEFAULT_TIMEOUT_MS,\n UI_REQUEST_PREEMPTED_TAG,\n UI_REQUEST_CANCELLED_TAG,\n UI_REQUEST_TIMEOUT_TAG,\n} from './utils/UiRequestRegistry';\nexport { compareSemver } from './utils/semver';\nexport { ensure0x, stripHex, padHex64, hexToBytes, bytesToHex } from './utils/hex';\nexport { enrichErrorMessage } from './utils/errorMessages';\nexport { batchCall } from './utils/batchCall';\n","export enum HardwareErrorCode {\n UnknownError = 0,\n DeviceNotFound = 1,\n DeviceDisconnected = 2,\n UserRejected = 3,\n DeviceBusy = 4,\n FirmwareUpdateRequired = 5,\n AppNotOpen = 6,\n InvalidParams = 7,\n TransportError = 8,\n OperationTimeout = 9,\n MethodNotSupported = 10,\n\n // PIN / Passphrase\n PinInvalid = 5520,\n PinCancelled = 5521,\n PassphraseRejected = 5522,\n\n // Device state\n DeviceLocked = 5530,\n DeviceNotInitialized = 5531,\n DeviceInBootloader = 5532,\n FirmwareTooOld = 5533,\n\n // Ledger specific\n WrongApp = 5540,\n\n // Device identity\n DeviceMismatch = 5560,\n\n // Transport\n BridgeNotFound = 5550,\n TransportNotAvailable = 5551,\n\n // --- EVM (Ledger Ethereum App) APDU-specific errors ---\n /** 0x6a80 Invalid data — observed on blindSignTransactionFallback when the\n * user has not enabled Blind signing on the device. */\n EvmBlindSigningRequired = 7001,\n /** 0x6984 Plugin not installed */\n EvmClearSignPluginMissing = 7002,\n /** 0x6a84 Insufficient memory (typical on Nano S with large calldata) */\n EvmDataTooLarge = 7003,\n /** 0x6501 TransactionType not supported (app too old for EIP-1559 / blob / 7702) */\n EvmTxTypeNotSupported = 7004,\n /** 0x911c Command code not supported — app predates current SDK */\n AppTooOld = 7005,\n}\n","import type { HardwareErrorCode } from './errors';\n\nexport interface Success<T> {\n success: true;\n payload: T;\n}\n\nexport interface Failure {\n success: false;\n payload: {\n error: string;\n code: HardwareErrorCode;\n };\n}\n\nexport type Response<T> = Success<T> | Failure;\n\nexport function success<T>(payload: T): Success<T> {\n return { success: true, payload };\n}\n\nexport function failure(code: HardwareErrorCode, error: string): Failure {\n return { success: false, payload: { error, code } };\n}\n","/**\n * Chain fingerprint utilities for device identity verification.\n *\n * Ledger devices have ephemeral IDs that change every session.\n * To verify that the same seed/device is connected, we derive an address\n * at a fixed path (account 0, index 0) and hash it into a stable \"chain fingerprint\".\n */\nimport { sha256 } from '@noble/hashes/sha256';\nimport { bytesToHex, utf8ToBytes } from '@noble/hashes/utils';\n\n/**\n * Fixed derivation paths used to generate chain fingerprints.\n * Uses standard index 0 — Ledger firmware shows device confirmation for\n * non-standard paths (e.g., index=100), which would interrupt wallet creation.\n * The address is hashed into a 16-char fingerprint, not exposed directly.\n * - EVM: cointype 60 (Ledger ETH App only supports 60)\n * - BTC: cointype 1 (testnet)\n * - SOL: cointype 501, standard 3-level hardened\n */\nexport const CHAIN_FINGERPRINT_PATHS: Record<ChainForFingerprint, string> = {\n evm: \"m/44'/60'/0'/0/0\",\n // BTC: account-level path (3 levels), mainnet cointype 0.\n // Cointype 1 (testnet) is rejected by some Ledger BTC App configurations.\n btc: \"m/44'/0'/0'\",\n sol: \"m/44'/501'/0'\",\n tron: \"m/44'/195'/0'/0/0\",\n};\n\nexport type ChainForFingerprint = 'evm' | 'btc' | 'sol' | 'tron';\n\n/**\n * 16-char SHA-256 fingerprint for device-identity verification.\n * Callers must canonicalize input (e.g. EVM address → lowercase) —\n * encoding variations otherwise cause false DeviceMismatch.\n */\nexport function deriveDeviceFingerprint(value: string): string {\n return bytesToHex(sha256(utf8ToBytes(value))).slice(0, 16);\n}\n","export const DEVICE_EVENT = 'DEVICE_EVENT';\n\n/** Events originating from the hardware device. */\nexport const DEVICE = {\n CONNECT: 'device-connect',\n DISCONNECT: 'device-disconnect',\n CHANGED: 'device-changed',\n} as const;\n","import type { QrResponseData } from '../types/qr';\nimport type { PreemptionDecision } from '../utils/DeviceJobQueue';\n\nexport const UI_EVENT = 'UI_EVENT';\n\nexport const UI_REQUEST = {\n REQUEST_PIN: 'ui-request-pin',\n REQUEST_PASSPHRASE: 'ui-request-passphrase',\n REQUEST_PASSPHRASE_ON_DEVICE: 'ui-request-passphrase-on-device',\n REQUEST_BUTTON: 'ui-request-button',\n REQUEST_QR_DISPLAY: 'ui-request-qr-display',\n REQUEST_QR_SCAN: 'ui-request-qr-scan',\n REQUEST_DEVICE_PERMISSION: 'ui-request-device-permission',\n REQUEST_SELECT_DEVICE: 'ui-request-select-device',\n REQUEST_DEVICE_CONNECT: 'ui-request-device-connect',\n REQUEST_PREEMPTION: 'ui-request-preemption',\n CLOSE_UI_WINDOW: 'ui-close',\n DEVICE_PROGRESS: 'ui-device_progress',\n FIRMWARE_PROGRESS: 'ui-firmware-progress',\n FIRMWARE_TIP: 'ui-firmware-tip',\n} as const;\n\nexport const UI_RESPONSE = {\n RECEIVE_PIN: 'receive-pin',\n RECEIVE_PASSPHRASE: 'receive-passphrase',\n RECEIVE_PASSPHRASE_ON_DEVICE: 'receive-passphrase-on-device',\n RECEIVE_QR_RESPONSE: 'receive-qr-response',\n RECEIVE_SELECT_DEVICE: 'receive-select-device',\n RECEIVE_DEVICE_CONNECT: 'receive-device-connect',\n RECEIVE_PREEMPTION: 'receive-preemption',\n CANCEL: 'cancel',\n} as const;\n\nexport type UiResponseEvent =\n | {\n type: typeof UI_RESPONSE.RECEIVE_PIN;\n payload: string;\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PASSPHRASE;\n payload: {\n value: string;\n passphraseOnDevice?: boolean;\n save?: boolean;\n };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PASSPHRASE_ON_DEVICE;\n payload?: undefined;\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_QR_RESPONSE;\n payload: QrResponseData;\n }\n | {\n // sdkConnectId echoes one of the DeviceInfo.connectId values emitted in\n // the REQUEST_SELECT_DEVICE event's `devices` list. Scope is the current\n // search session; may be ephemeral (e.g. Ledger USB DMK UUID).\n type: typeof UI_RESPONSE.RECEIVE_SELECT_DEVICE;\n payload: { sdkConnectId: string };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_DEVICE_CONNECT;\n payload: { confirmed: boolean };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PREEMPTION;\n payload: { decision: PreemptionDecision };\n }\n | {\n type: typeof UI_RESPONSE.CANCEL;\n payload?: undefined;\n };\n","/** Events generated by SDK internal detection (not from hardware directly). */\nexport const SDK = {\n DEVICE_STUCK: 'device-stuck',\n DEVICE_UNRESPONSIVE: 'device-unresponsive',\n DEVICE_RECOVERED: 'device-recovered',\n DEVICE_INTERACTION: 'device-interaction',\n} as const;\n","/**\n * Per-device serial job queue with preemption support and stuck recovery.\n * Ensures that only one operation runs at a time per device, with intelligent\n * handling of conflicting operations.\n *\n * The 'confirm' level uses the standard UI request/response flow:\n * emits REQUEST_PREEMPTION → waits for RECEIVE_PREEMPTION via UiRequestRegistry.\n */\n\nimport { UI_REQUEST } from '../events/ui-request';\n\nimport type { UiRequestRegistry } from './UiRequestRegistry';\n\nexport type Interruptibility = 'none' | 'safe' | 'confirm';\n\nexport type PreemptionDecision = 'cancel-current' | 'wait' | 'reject-new';\n\nexport interface JobOptions {\n interruptibility?: Interruptibility;\n label?: string;\n}\n\nexport interface ActiveJobInfo {\n label?: string;\n interruptibility: Interruptibility;\n startedAt: number;\n}\n\nexport interface PreemptionEvent {\n deviceId: string;\n currentJob: ActiveJobInfo;\n newJob: { label?: string; interruptibility: Interruptibility };\n}\n\ninterface ActiveJob {\n options: Required<Pick<JobOptions, 'interruptibility'>> & Pick<JobOptions, 'label'>;\n abortController: AbortController;\n startedAt: number;\n}\n\nexport interface DeviceJobQueueDeps {\n /** Emit a UI request event to the frontend. */\n emit: (event: string, data: unknown) => void;\n /** Registry for waiting on UI responses. */\n uiRegistry: UiRequestRegistry;\n}\n\nexport class DeviceJobQueue {\n private readonly _queues = new Map<string, Promise<unknown>>();\n\n private readonly _active = new Map<string, ActiveJob>();\n\n private readonly _deps: DeviceJobQueueDeps | null;\n\n constructor(deps?: DeviceJobQueueDeps) {\n this._deps = deps ?? null;\n }\n\n /**\n * Enqueue a job for a specific device.\n * If a job is already running for this device, behavior depends on interruptibility:\n * - 'none': new job queues silently (no preemption possible)\n * - 'safe': current job is auto-cancelled, new job runs immediately after\n * - 'confirm': emits REQUEST_PREEMPTION and waits for UI response\n */\n async enqueue<T>(\n deviceId: string,\n job: (signal: AbortSignal) => Promise<T>,\n options: JobOptions = {}\n ): Promise<T> {\n const interruptibility = options.interruptibility ?? 'confirm';\n const active = this._active.get(deviceId);\n\n if (active) {\n switch (active.options.interruptibility) {\n case 'none':\n // Cannot interrupt, just queue behind\n break;\n case 'safe':\n // Auto-cancel current safe operation\n active.abortController.abort(new Error('Preempted by new operation'));\n break;\n case 'confirm': {\n // If a preemption dialog is already pending, reject immediately.\n // The user is already deciding for a previous request — don't\n // overwrite the UiRequestRegistry slot (single-slot per type).\n if (this._deps?.uiRegistry.hasPending(UI_REQUEST.REQUEST_PREEMPTION)) {\n throw Object.assign(\n new Error(`Device busy: a preemption decision is already pending`),\n { hardwareErrorCode: 'DEVICE_BUSY' }\n );\n }\n const decision = await this._requestPreemptionDecision(deviceId, active, {\n label: options.label,\n interruptibility,\n });\n switch (decision) {\n case 'cancel-current':\n active.abortController.abort(new Error('Cancelled by user via preemption'));\n break;\n case 'reject-new':\n throw Object.assign(\n new Error(`Device busy: ${active.options.label ?? 'unknown operation'}`),\n { hardwareErrorCode: 'DEVICE_BUSY' }\n );\n case 'wait':\n break;\n default:\n // Unknown decision value from UI — fall back to safest option: wait.\n break;\n }\n break;\n }\n }\n }\n\n const ac = new AbortController();\n const prev = this._queues.get(deviceId) ?? Promise.resolve();\n\n const next = prev\n .catch(() => {})\n .then(async () => {\n this._active.set(deviceId, {\n options: { interruptibility, label: options.label },\n abortController: ac,\n startedAt: Date.now(),\n });\n try {\n return await job(ac.signal);\n } finally {\n this._active.delete(deviceId);\n }\n });\n\n const tail = next.catch(() => {});\n this._queues.set(deviceId, tail);\n tail.then(() => {\n if (this._queues.get(deviceId) === tail) {\n this._queues.delete(deviceId);\n }\n });\n return next;\n }\n\n /** Manually cancel the active job on a device. Returns false if job is non-interruptible. */\n cancelActive(deviceId: string): boolean {\n const active = this._active.get(deviceId);\n if (!active) return false;\n if (active.options.interruptibility === 'none') return false;\n active.abortController.abort(new Error('Manually cancelled'));\n return true;\n }\n\n /** Force cancel regardless of interruptibility. Use for device stuck recovery. */\n forceCancelActive(deviceId: string): boolean {\n const active = this._active.get(deviceId);\n if (!active) return false;\n active.abortController.abort(new Error('Force cancelled for recovery'));\n return true;\n }\n\n /** Get info about the currently active job for a device, or null if idle. */\n getActiveJob(deviceId: string): ActiveJobInfo | null {\n const active = this._active.get(deviceId);\n if (!active) return null;\n return {\n label: active.options.label,\n interruptibility: active.options.interruptibility,\n startedAt: active.startedAt,\n };\n }\n\n clear(): void {\n for (const active of this._active.values()) {\n active.abortController.abort(new Error('Queue cleared'));\n }\n this._active.clear();\n this._queues.clear();\n }\n\n /**\n * Request preemption decision via UI request/response flow.\n * Falls back to 'wait' if deps are not provided.\n */\n private async _requestPreemptionDecision(\n deviceId: string,\n active: ActiveJob,\n newJob: { label?: string; interruptibility: Interruptibility }\n ): Promise<PreemptionDecision> {\n if (!this._deps) return 'wait';\n\n const { emit, uiRegistry } = this._deps;\n\n emit(UI_REQUEST.REQUEST_PREEMPTION, {\n type: UI_REQUEST.REQUEST_PREEMPTION,\n payload: {\n deviceId,\n currentJob: {\n label: active.options.label,\n interruptibility: active.options.interruptibility,\n startedAt: active.startedAt,\n },\n newJob,\n },\n });\n\n const response = await uiRegistry.wait<{ decision: PreemptionDecision }>(\n UI_REQUEST.REQUEST_PREEMPTION\n );\n\n return response?.decision ?? 'wait';\n }\n}\n","import type { ConnectionType, DeviceCapabilities, DeviceInfo, VendorType } from './device';\nimport type { UiResponseEvent } from '../events/ui-request';\n\n// =====================================================================\n// Connector types — transport-level abstraction for device communication\n// =====================================================================\n\n/**\n * Minimal device info returned during discovery (searchDevices).\n * At scan time, full DeviceInfo fields like firmwareVersion are not yet available.\n */\nexport interface ConnectorDevice {\n connectId: string;\n deviceId: string;\n name: string;\n model?: string;\n\n /** Device capabilities — available from scan time */\n capabilities?: DeviceCapabilities;\n}\n\nexport interface ConnectorSession {\n sessionId: string;\n deviceInfo: DeviceInfo;\n}\n\nexport type ConnectorEventType = 'device-connect' | 'device-disconnect' | 'ui-request' | 'ui-event';\n\n/**\n * Interaction event types emitted via 'ui-event'.\n * These map to user-facing prompts (confirm on device, open app, etc.).\n */\nexport enum EConnectorInteraction {\n /** Device requires user to open a specific app */\n ConfirmOpenApp = 'confirm-open-app',\n /** Device requires user to unlock */\n UnlockDevice = 'unlock-device',\n /** Device needs user to confirm on device (sign, verify, etc.) */\n ConfirmOnDevice = 'confirm-on-device',\n /** Previous interaction completed — clear UI prompt */\n InteractionComplete = 'interaction-complete',\n}\n\nexport type ConnectorUiEvent =\n | { type: EConnectorInteraction.ConfirmOpenApp; payload: { sessionId: string } }\n | { type: EConnectorInteraction.UnlockDevice; payload: { sessionId: string } }\n | { type: EConnectorInteraction.ConfirmOnDevice; payload: { sessionId: string } }\n | { type: EConnectorInteraction.InteractionComplete; payload: { sessionId: string } };\n\nexport interface ConnectorEventMap {\n 'device-connect': { device: ConnectorDevice };\n 'device-disconnect': { connectId: string };\n 'ui-request': { type: string; payload?: unknown };\n 'ui-event': ConnectorUiEvent;\n}\n\nexport interface IConnector {\n /** Physical connection type this connector uses. Fixed at construction. */\n readonly connectionType: ConnectionType;\n\n searchDevices(): Promise<ConnectorDevice[]>;\n connect(deviceId?: string): Promise<ConnectorSession>;\n disconnect(sessionId: string): Promise<void>;\n call(sessionId: string, method: string, params: unknown): Promise<unknown>;\n cancel(sessionId: string): Promise<void>;\n\n /** Send a UI response (e.g. PIN, passphrase) to the device. */\n uiResponse(response: UiResponseEvent): void;\n\n on<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;\n off<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;\n\n reset(): void;\n}\n\n// =====================================================================\n// Hardware bridge — generic forwarding interface for cross-boundary IConnector\n//\n// The same shape works across any process / context boundary that needs to\n// expose a multi-vendor backend behind a single-vendor IConnector facade:\n// - Electron (renderer ↔ preload/main via contextBridge)\n// - Browser Extension (popup/content ↔ background/offscreen via chrome.runtime)\n// - React Native (JS ↔ native module via NativeModules)\n// - Web Worker / iframe (postMessage)\n//\n// Each method takes a `vendor` discriminator so a single bridge implementation\n// can multiplex across vendors (ledger, trezor, ...).\n// =====================================================================\n\nexport interface IHardwareBridge {\n searchDevices(params: { vendor: VendorType }): Promise<ConnectorDevice[]>;\n connect(params: { vendor: VendorType; deviceId?: string }): Promise<ConnectorSession>;\n disconnect(params: { vendor: VendorType; sessionId: string }): Promise<void>;\n call(params: {\n vendor: VendorType;\n sessionId: string;\n method: string;\n callParams: unknown;\n }): Promise<unknown>;\n cancel(params: { vendor: VendorType; sessionId: string }): Promise<void>;\n uiResponse(params: { vendor: VendorType; response: UiResponseEvent }): void;\n reset(params: { vendor: VendorType }): void;\n\n /** Register an event handler for connector events forwarded across the bridge. */\n onEvent(\n params: { vendor: VendorType },\n handler: (event: { type: ConnectorEventType; data: unknown }) => void\n ): void;\n\n /** Unregister a previously registered event handler. */\n offEvent(\n params: { vendor: VendorType },\n handler: (event: { type: ConnectorEventType; data: unknown }) => void\n ): void;\n}\n\n/**\n * Adapt an IHardwareBridge (multi-vendor backend) into a single-vendor IConnector.\n * Every IConnector method becomes a transparent forward to bridge.<method>({ vendor, ... }).\n * Events are forwarded via bridge.onEvent / offEvent.\n *\n * Use this anywhere the actual hardware lives behind a process / context boundary\n * (Electron main, extension background, native module, worker, iframe).\n */\nexport function createBridgedConnector(\n vendor: VendorType,\n connectionType: ConnectionType,\n bridge: IHardwareBridge\n): IConnector {\n // Map from typed IConnector handlers to the bridge handler so we can\n // unregister them correctly via off().\n const handlerMap = new Map<\n (data: ConnectorEventMap[ConnectorEventType]) => void,\n (event: { type: ConnectorEventType; data: unknown }) => void\n >();\n\n return {\n connectionType,\n searchDevices: () => bridge.searchDevices({ vendor }),\n connect: deviceId => bridge.connect({ vendor, deviceId }),\n disconnect: sessionId => bridge.disconnect({ vendor, sessionId }),\n call: (sessionId, method, callParams) => bridge.call({ vendor, sessionId, method, callParams }),\n cancel: sessionId => bridge.cancel({ vendor, sessionId }),\n uiResponse: response => bridge.uiResponse({ vendor, response }),\n on: (event, handler) => {\n const bridgeHandler = (e: { type: ConnectorEventType; data: unknown }) => {\n if (e.type === event) {\n handler(e.data as ConnectorEventMap[typeof event]);\n }\n };\n handlerMap.set(\n handler as (data: ConnectorEventMap[ConnectorEventType]) => void,\n bridgeHandler\n );\n bridge.onEvent({ vendor }, bridgeHandler);\n },\n off: (_event, handler) => {\n const bridgeHandler = handlerMap.get(\n handler as (data: ConnectorEventMap[ConnectorEventType]) => void\n );\n if (bridgeHandler) {\n bridge.offEvent({ vendor }, bridgeHandler);\n handlerMap.delete(handler as (data: ConnectorEventMap[ConnectorEventType]) => void);\n }\n },\n reset: () => bridge.reset({ vendor }),\n };\n}\n","/**\n * Minimal typed event emitter using Map<string, Set<listener>>.\n * Each adapter uses this for device events (connect, disconnect, pin, etc.).\n *\n * TMap is a record mapping event name strings to their payload types.\n * Example:\n * type MyEvents = { 'connect': { id: string }; 'disconnect': { id: string } };\n * const emitter = new TypedEventEmitter<MyEvents>();\n * emitter.on('connect', (data) => { data.id }); // data is { id: string }\n *\n * For backward compatibility, TMap defaults to Record<string, any> so that\n * existing code using `new TypedEventEmitter<SomeUnionType>()` still compiles.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class TypedEventEmitter<TMap extends Record<string, any> = Record<string, any>> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private readonly _listeners = new Map<string, Set<(event: any) => void>>();\n\n on<K extends keyof TMap & string>(event: K, listener: (event: TMap[K]) => void): void;\n\n on(event: string, listener: (event: any) => void): void;\n\n on(event: string, listener: (event: any) => void): void {\n let set = this._listeners.get(event);\n if (!set) {\n set = new Set();\n this._listeners.set(event, set);\n }\n set.add(listener);\n }\n\n off<K extends keyof TMap & string>(event: K, listener: (event: TMap[K]) => void): void;\n\n off(event: string, listener: (event: any) => void): void;\n\n off(event: string, listener: (event: any) => void): void {\n const set = this._listeners.get(event);\n if (set) {\n set.delete(listener);\n if (set.size === 0) this._listeners.delete(event);\n }\n }\n\n emit<K extends keyof TMap & string>(event: K, data: TMap[K]): void;\n\n emit(event: string, data: unknown): void;\n\n emit(event: string, data: unknown): void {\n const set = this._listeners.get(event);\n if (set) {\n for (const listener of set) listener(data);\n }\n }\n\n removeAllListeners(): void {\n this._listeners.clear();\n }\n}\n","import { UI_REQUEST, UI_RESPONSE } from '../events/ui-request';\n\n/** 10 min — every UI request is human-in-the-loop; bias toward \"wait long\". */\nexport const UI_REQUEST_DEFAULT_TIMEOUT_MS = 600_000;\n\nconst RESPONSE_TO_REQUEST: Record<string, string> = {\n [UI_RESPONSE.RECEIVE_PIN]: UI_REQUEST.REQUEST_PIN,\n [UI_RESPONSE.RECEIVE_PASSPHRASE]: UI_REQUEST.REQUEST_PASSPHRASE,\n [UI_RESPONSE.RECEIVE_PASSPHRASE_ON_DEVICE]: UI_REQUEST.REQUEST_PASSPHRASE_ON_DEVICE,\n [UI_RESPONSE.RECEIVE_SELECT_DEVICE]: UI_REQUEST.REQUEST_SELECT_DEVICE,\n [UI_RESPONSE.RECEIVE_DEVICE_CONNECT]: UI_REQUEST.REQUEST_DEVICE_CONNECT,\n [UI_RESPONSE.RECEIVE_PREEMPTION]: UI_REQUEST.REQUEST_PREEMPTION,\n // RECEIVE_QR_RESPONSE maps to QR_DISPLAY or QR_SCAN — resolved dynamically below.\n};\n\nexport const UI_REQUEST_PREEMPTED_TAG = 'UiRequestPreempted';\nexport const UI_REQUEST_CANCELLED_TAG = 'UiRequestCancelled';\nexport const UI_REQUEST_TIMEOUT_TAG = 'UiRequestTimeout';\n\ntype PendingEntry = {\n resolve: (payload: unknown) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\n/**\n * Per-type single-slot registry for adapter-level UI requests. A new `wait`\n * of the same type preempts (rejects) the prior one. Unknown response types\n * are dropped silently so the public `uiResponse` entry never throws.\n */\nexport class UiRequestRegistry {\n private pending = new Map<string, PendingEntry>();\n\n wait<T = unknown>(requestType: string, options?: { timeoutMs?: number }): Promise<T> {\n const existing = this.pending.get(requestType);\n if (existing) {\n clearTimeout(existing.timer);\n this.pending.delete(requestType);\n existing.reject(\n Object.assign(new Error(`UI request '${requestType}' was superseded by a new request`), {\n _tag: UI_REQUEST_PREEMPTED_TAG,\n })\n );\n }\n\n const timeoutMs = options?.timeoutMs ?? UI_REQUEST_DEFAULT_TIMEOUT_MS;\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this.pending.get(requestType)?.timer === timer) {\n this.pending.delete(requestType);\n reject(\n Object.assign(new Error(`UI request '${requestType}' timed out after ${timeoutMs}ms`), {\n _tag: UI_REQUEST_TIMEOUT_TAG,\n })\n );\n }\n }, timeoutMs);\n\n this.pending.set(requestType, {\n resolve: resolve as (payload: unknown) => void,\n reject,\n timer,\n });\n });\n }\n\n resolve(responseType: string, payload: unknown): void {\n let requestType = RESPONSE_TO_REQUEST[responseType];\n if (responseType === UI_RESPONSE.RECEIVE_QR_RESPONSE) {\n if (this.pending.has(UI_REQUEST.REQUEST_QR_DISPLAY)) {\n requestType = UI_REQUEST.REQUEST_QR_DISPLAY;\n } else if (this.pending.has(UI_REQUEST.REQUEST_QR_SCAN)) {\n requestType = UI_REQUEST.REQUEST_QR_SCAN;\n }\n }\n if (!requestType) return;\n\n const entry = this.pending.get(requestType);\n if (!entry) return;\n\n clearTimeout(entry.timer);\n this.pending.delete(requestType);\n entry.resolve(payload);\n }\n\n cancel(requestType?: string): void {\n if (requestType) {\n const entry = this.pending.get(requestType);\n if (!entry) return;\n clearTimeout(entry.timer);\n this.pending.delete(requestType);\n entry.reject(\n Object.assign(new Error(`UI request '${requestType}' was cancelled`), {\n _tag: UI_REQUEST_CANCELLED_TAG,\n })\n );\n return;\n }\n\n for (const [type, entry] of this.pending) {\n clearTimeout(entry.timer);\n entry.reject(\n Object.assign(new Error(`UI request '${type}' was cancelled`), {\n _tag: UI_REQUEST_CANCELLED_TAG,\n })\n );\n }\n this.pending.clear();\n }\n\n reset(): void {\n this.cancel();\n }\n\n hasPending(requestType: string): boolean {\n return this.pending.has(requestType);\n }\n}\n","/**\n * Compare two semver strings (e.g. \"2.1.0\" vs \"2.3.1\").\n * Returns -1 if a < b, 0 if equal, 1 if a > b.\n */\nexport function compareSemver(a: string, b: string): number {\n const pa = a.split('.').map(Number);\n const pb = b.split('.').map(Number);\n for (let i = 0; i < 3; i++) {\n const va = pa[i] ?? 0;\n const vb = pb[i] ?? 0;\n if (va < vb) return -1;\n if (va > vb) return 1;\n }\n return 0;\n}\n","import { bytesToHex as nobleBytesToHex, hexToBytes as nobleHexToBytes } from '@noble/hashes/utils';\n\n/** Ensure hex string has 0x prefix */\nexport function ensure0x(hex: string): string {\n return hex.startsWith('0x') ? hex : `0x${hex}`;\n}\n\n/** Strip 0x prefix from hex string */\nexport function stripHex(hex: string): string {\n return hex.startsWith('0x') ? hex.slice(2) : hex;\n}\n\n/** Pad hex to 64 chars (32 bytes) with 0x prefix */\nexport function padHex64(hex: string): string {\n return `0x${stripHex(hex).padStart(64, '0')}`;\n}\n\n/**\n * Convert a hex string (with or without 0x prefix) to a Uint8Array.\n * Throws on invalid hex (non-hex characters, odd length).\n */\nexport function hexToBytes(hex: string): Uint8Array {\n return nobleHexToBytes(stripHex(hex));\n}\n\n/** Convert a Uint8Array to a hex string (no 0x prefix). */\nexport function bytesToHex(bytes: Uint8Array): string {\n return nobleBytesToHex(bytes);\n}\n","import { HardwareErrorCode } from '../types/errors';\n\n/**\n * Enrich a hardware error message with actionable recovery hints.\n * Shared across adapters (Ledger, Trezor, etc.).\n */\nexport function enrichErrorMessage(code: HardwareErrorCode, originalMessage: string): string {\n switch (code) {\n case HardwareErrorCode.PinInvalid:\n return `${originalMessage}. Please re-enter your PIN.`;\n case HardwareErrorCode.PinCancelled:\n return `${originalMessage}. PIN entry was cancelled.`;\n case HardwareErrorCode.DeviceBusy:\n return `${originalMessage}. The device is in use by another application. Close other wallet apps and try again.`;\n case HardwareErrorCode.DeviceDisconnected:\n return `${originalMessage}. Please reconnect the device and try again.`;\n case HardwareErrorCode.DeviceLocked:\n return `${originalMessage}. Please unlock your device and try again.`;\n case HardwareErrorCode.UserRejected:\n return `${originalMessage}. The request was rejected on the device.`;\n case HardwareErrorCode.WrongApp:\n return `${originalMessage}. Please open the correct app on your device.`;\n case HardwareErrorCode.AppNotOpen:\n return `${originalMessage}. The required app is not installed on the device.`;\n case HardwareErrorCode.TransportNotAvailable:\n return `${originalMessage}. Ensure the device bridge/transport is available and running.`;\n case HardwareErrorCode.FirmwareTooOld:\n return `${originalMessage}. Please update your device firmware.`;\n case HardwareErrorCode.DeviceNotInitialized:\n return `${originalMessage}. The device may need to be set up first.`;\n case HardwareErrorCode.OperationTimeout:\n return `${originalMessage}. The operation timed out. Please try again.`;\n case HardwareErrorCode.EvmBlindSigningRequired:\n return 'Ledger: Blind signing not enabled in Ethereum app.';\n case HardwareErrorCode.EvmClearSignPluginMissing:\n return 'Ledger: Required plugin not installed (install via Ledger Live).';\n case HardwareErrorCode.EvmDataTooLarge:\n return 'Ledger: Transaction data too large for device memory.';\n case HardwareErrorCode.EvmTxTypeNotSupported:\n return 'Ledger: Transaction type not supported by current Ethereum app.';\n case HardwareErrorCode.AppTooOld:\n return 'Ledger: App out of date (update via Ledger Live).';\n default:\n return originalMessage;\n }\n}\n","import type { Response } from '../types/response';\n\n/**\n * Generic batch call with progress reporting.\n * If any single call fails, returns the failure immediately.\n */\nexport async function batchCall<TParam, TResult>(\n params: TParam[],\n callFn: (p: TParam) => Promise<Response<TResult>>,\n onProgress?: (progress: { index: number; total: number }) => void\n): Promise<Response<TResult[]>> {\n const results: TResult[] = [];\n for (let i = 0; i < params.length; i++) {\n const result = await callFn(params[i]);\n if (!result.success) {\n return result;\n }\n results.push(result.payload);\n onProgress?.({ index: i, total: params.length });\n }\n return { success: true, payload: results };\n}\n"],"mappings":"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","names":["bytesToHex","HardwareErrorCode","EConnectorInteraction","import_utils","nobleHexToBytes","bytesToHex","nobleBytesToHex"]}
|
package/dist/index.mjs
CHANGED
|
@@ -34,8 +34,8 @@ var HardwareErrorCode = /* @__PURE__ */ ((HardwareErrorCode2) => {
|
|
|
34
34
|
function success(payload) {
|
|
35
35
|
return { success: true, payload };
|
|
36
36
|
}
|
|
37
|
-
function failure(code, error
|
|
38
|
-
return { success: false, payload: { error, code
|
|
37
|
+
function failure(code, error) {
|
|
38
|
+
return { success: false, payload: { error, code } };
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// src/types/fingerprint.ts
|
|
@@ -49,8 +49,8 @@ var CHAIN_FINGERPRINT_PATHS = {
|
|
|
49
49
|
sol: "m/44'/501'/0'",
|
|
50
50
|
tron: "m/44'/195'/0'/0/0"
|
|
51
51
|
};
|
|
52
|
-
function deriveDeviceFingerprint(
|
|
53
|
-
return bytesToHex(sha256(utf8ToBytes(
|
|
52
|
+
function deriveDeviceFingerprint(value) {
|
|
53
|
+
return bytesToHex(sha256(utf8ToBytes(value))).slice(0, 16);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// src/events/device.ts
|
|
@@ -58,11 +58,7 @@ var DEVICE_EVENT = "DEVICE_EVENT";
|
|
|
58
58
|
var DEVICE = {
|
|
59
59
|
CONNECT: "device-connect",
|
|
60
60
|
DISCONNECT: "device-disconnect",
|
|
61
|
-
CHANGED: "device-changed"
|
|
62
|
-
ACQUIRE: "device-acquire",
|
|
63
|
-
RELEASE: "device-release",
|
|
64
|
-
FEATURES: "features",
|
|
65
|
-
SUPPORT_FEATURES: "support_features"
|
|
61
|
+
CHANGED: "device-changed"
|
|
66
62
|
};
|
|
67
63
|
|
|
68
64
|
// src/events/ui-request.ts
|
|
@@ -148,6 +144,8 @@ var DeviceJobQueue = class {
|
|
|
148
144
|
);
|
|
149
145
|
case "wait":
|
|
150
146
|
break;
|
|
147
|
+
default:
|
|
148
|
+
break;
|
|
151
149
|
}
|
|
152
150
|
break;
|
|
153
151
|
}
|
|
@@ -244,9 +242,10 @@ var EConnectorInteraction = /* @__PURE__ */ ((EConnectorInteraction2) => {
|
|
|
244
242
|
EConnectorInteraction2["InteractionComplete"] = "interaction-complete";
|
|
245
243
|
return EConnectorInteraction2;
|
|
246
244
|
})(EConnectorInteraction || {});
|
|
247
|
-
function createBridgedConnector(vendor, bridge) {
|
|
245
|
+
function createBridgedConnector(vendor, connectionType, bridge) {
|
|
248
246
|
const handlerMap = /* @__PURE__ */ new Map();
|
|
249
247
|
return {
|
|
248
|
+
connectionType,
|
|
250
249
|
searchDevices: () => bridge.searchDevices({ vendor }),
|
|
251
250
|
connect: (deviceId) => bridge.connect({ vendor, deviceId }),
|
|
252
251
|
disconnect: (sessionId) => bridge.disconnect({ vendor, sessionId }),
|
|
@@ -419,6 +418,7 @@ function compareSemver(a, b) {
|
|
|
419
418
|
}
|
|
420
419
|
|
|
421
420
|
// src/utils/hex.ts
|
|
421
|
+
import { bytesToHex as nobleBytesToHex, hexToBytes as nobleHexToBytes } from "@noble/hashes/utils";
|
|
422
422
|
function ensure0x(hex) {
|
|
423
423
|
return hex.startsWith("0x") ? hex : `0x${hex}`;
|
|
424
424
|
}
|
|
@@ -429,15 +429,10 @@ function padHex64(hex) {
|
|
|
429
429
|
return `0x${stripHex(hex).padStart(64, "0")}`;
|
|
430
430
|
}
|
|
431
431
|
function hexToBytes(hex) {
|
|
432
|
-
|
|
433
|
-
const bytes = new Uint8Array(clean.length / 2);
|
|
434
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
435
|
-
bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);
|
|
436
|
-
}
|
|
437
|
-
return bytes;
|
|
432
|
+
return nobleHexToBytes(stripHex(hex));
|
|
438
433
|
}
|
|
439
434
|
function bytesToHex2(bytes) {
|
|
440
|
-
return
|
|
435
|
+
return nobleBytesToHex(bytes);
|
|
441
436
|
}
|
|
442
437
|
|
|
443
438
|
// src/utils/errorMessages.ts
|
|
@@ -468,15 +463,15 @@ function enrichErrorMessage(code, originalMessage) {
|
|
|
468
463
|
case 9 /* OperationTimeout */:
|
|
469
464
|
return `${originalMessage}. The operation timed out. Please try again.`;
|
|
470
465
|
case 7001 /* EvmBlindSigningRequired */:
|
|
471
|
-
return "
|
|
466
|
+
return "Ledger: Blind signing not enabled in Ethereum app.";
|
|
472
467
|
case 7002 /* EvmClearSignPluginMissing */:
|
|
473
|
-
return "Required
|
|
468
|
+
return "Ledger: Required plugin not installed (install via Ledger Live).";
|
|
474
469
|
case 7003 /* EvmDataTooLarge */:
|
|
475
|
-
return "Transaction data
|
|
470
|
+
return "Ledger: Transaction data too large for device memory.";
|
|
476
471
|
case 7004 /* EvmTxTypeNotSupported */:
|
|
477
|
-
return "Transaction type
|
|
472
|
+
return "Ledger: Transaction type not supported by current Ethereum app.";
|
|
478
473
|
case 7005 /* AppTooOld */:
|
|
479
|
-
return "
|
|
474
|
+
return "Ledger: App out of date (update via Ledger Live).";
|
|
480
475
|
default:
|
|
481
476
|
return originalMessage;
|
|
482
477
|
}
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types/errors.ts","../src/types/response.ts","../src/types/fingerprint.ts","../src/events/device.ts","../src/events/ui-request.ts","../src/events/sdk.ts","../src/utils/DeviceJobQueue.ts","../src/types/connector.ts","../src/utils/TypedEventEmitter.ts","../src/utils/UiRequestRegistry.ts","../src/utils/semver.ts","../src/utils/hex.ts","../src/utils/errorMessages.ts","../src/utils/batchCall.ts"],"sourcesContent":["export enum HardwareErrorCode {\n UnknownError = 0,\n DeviceNotFound = 1,\n DeviceDisconnected = 2,\n UserRejected = 3,\n DeviceBusy = 4,\n FirmwareUpdateRequired = 5,\n AppNotOpen = 6,\n InvalidParams = 7,\n TransportError = 8,\n OperationTimeout = 9,\n MethodNotSupported = 10,\n\n // PIN / Passphrase\n PinInvalid = 5520,\n PinCancelled = 5521,\n PassphraseRejected = 5522,\n\n // Device state\n DeviceLocked = 5530,\n DeviceNotInitialized = 5531,\n DeviceInBootloader = 5532,\n FirmwareTooOld = 5533,\n\n // Ledger specific\n WrongApp = 5540,\n\n // Device identity\n DeviceMismatch = 5560,\n\n // Transport\n BridgeNotFound = 5550,\n TransportNotAvailable = 5551,\n\n // --- EVM (Ledger Ethereum App) APDU-specific errors ---\n /** 0x6a80 Invalid data — observed on blindSignTransactionFallback when the\n * user has not enabled Blind signing on the device. */\n EvmBlindSigningRequired = 7001,\n /** 0x6984 Plugin not installed */\n EvmClearSignPluginMissing = 7002,\n /** 0x6a84 Insufficient memory (typical on Nano S with large calldata) */\n EvmDataTooLarge = 7003,\n /** 0x6501 TransactionType not supported (app too old for EIP-1559 / blob / 7702) */\n EvmTxTypeNotSupported = 7004,\n /** 0x911c Command code not supported — app predates current SDK */\n AppTooOld = 7005,\n}\n","import type { HardwareErrorCode } from './errors';\n\nexport interface Success<T> {\n success: true;\n payload: T;\n}\n\nexport interface Failure {\n success: false;\n payload: {\n error: string;\n code: HardwareErrorCode;\n appName?: string;\n };\n}\n\nexport type Response<T> = Success<T> | Failure;\n\nexport function success<T>(payload: T): Success<T> {\n return { success: true, payload };\n}\n\nexport function failure(code: HardwareErrorCode, error: string, appName?: string): Failure {\n return { success: false, payload: { error, code, appName } };\n}\n","/**\n * Chain fingerprint utilities for device identity verification.\n *\n * Ledger devices have ephemeral IDs that change every session.\n * To verify that the same seed/device is connected, we derive an address\n * at a fixed path (account 0, index 0) and hash it into a stable \"chain fingerprint\".\n */\nimport { sha256 } from '@noble/hashes/sha256';\nimport { bytesToHex, utf8ToBytes } from '@noble/hashes/utils';\n\n/**\n * Fixed derivation paths used to generate chain fingerprints.\n * Uses standard index 0 — Ledger firmware shows device confirmation for\n * non-standard paths (e.g., index=100), which would interrupt wallet creation.\n * The address is hashed into a 16-char fingerprint, not exposed directly.\n * - EVM: cointype 60 (Ledger ETH App only supports 60)\n * - BTC: cointype 1 (testnet)\n * - SOL: cointype 501, standard 3-level hardened\n */\nexport const CHAIN_FINGERPRINT_PATHS: Record<ChainForFingerprint, string> = {\n evm: \"m/44'/60'/0'/0/0\",\n // BTC: account-level path (3 levels), mainnet cointype 0.\n // Cointype 1 (testnet) is rejected by some Ledger BTC App configurations.\n btc: \"m/44'/0'/0'\",\n sol: \"m/44'/501'/0'\",\n tron: \"m/44'/195'/0'/0/0\",\n};\n\nexport type ChainForFingerprint = 'evm' | 'btc' | 'sol' | 'tron';\n\n/**\n * Hash an address string into a 16-character hex fingerprint.\n *\n * Uses the first 16 hex chars (64 bits) of SHA-256 — sufficient for\n * device identity matching. Not used for security decisions.\n */\nexport function deriveDeviceFingerprint(address: string): string {\n return bytesToHex(sha256(utf8ToBytes(address))).slice(0, 16);\n}\n","export const DEVICE_EVENT = 'DEVICE_EVENT';\n\n/** Events originating from the hardware device. */\nexport const DEVICE = {\n CONNECT: 'device-connect',\n DISCONNECT: 'device-disconnect',\n CHANGED: 'device-changed',\n ACQUIRE: 'device-acquire',\n RELEASE: 'device-release',\n FEATURES: 'features',\n SUPPORT_FEATURES: 'support_features',\n} as const;\n","import type { QrResponseData } from '../types/qr';\nimport type { PreemptionDecision } from '../utils/DeviceJobQueue';\n\nexport const UI_EVENT = 'UI_EVENT';\n\nexport const UI_REQUEST = {\n REQUEST_PIN: 'ui-request-pin',\n REQUEST_PASSPHRASE: 'ui-request-passphrase',\n REQUEST_PASSPHRASE_ON_DEVICE: 'ui-request-passphrase-on-device',\n REQUEST_BUTTON: 'ui-request-button',\n REQUEST_QR_DISPLAY: 'ui-request-qr-display',\n REQUEST_QR_SCAN: 'ui-request-qr-scan',\n REQUEST_DEVICE_PERMISSION: 'ui-request-device-permission',\n REQUEST_SELECT_DEVICE: 'ui-request-select-device',\n REQUEST_DEVICE_CONNECT: 'ui-request-device-connect',\n REQUEST_PREEMPTION: 'ui-request-preemption',\n CLOSE_UI_WINDOW: 'ui-close',\n DEVICE_PROGRESS: 'ui-device_progress',\n FIRMWARE_PROGRESS: 'ui-firmware-progress',\n FIRMWARE_TIP: 'ui-firmware-tip',\n} as const;\n\nexport const UI_RESPONSE = {\n RECEIVE_PIN: 'receive-pin',\n RECEIVE_PASSPHRASE: 'receive-passphrase',\n RECEIVE_PASSPHRASE_ON_DEVICE: 'receive-passphrase-on-device',\n RECEIVE_QR_RESPONSE: 'receive-qr-response',\n RECEIVE_SELECT_DEVICE: 'receive-select-device',\n RECEIVE_DEVICE_CONNECT: 'receive-device-connect',\n RECEIVE_PREEMPTION: 'receive-preemption',\n CANCEL: 'cancel',\n} as const;\n\nexport type UiResponseEvent =\n | {\n type: typeof UI_RESPONSE.RECEIVE_PIN;\n payload: string;\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PASSPHRASE;\n payload: {\n value: string;\n passphraseOnDevice?: boolean;\n save?: boolean;\n };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PASSPHRASE_ON_DEVICE;\n payload?: undefined;\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_QR_RESPONSE;\n payload: QrResponseData;\n }\n | {\n // sdkConnectId echoes one of the DeviceInfo.connectId values emitted in\n // the REQUEST_SELECT_DEVICE event's `devices` list. Scope is the current\n // search session; may be ephemeral (e.g. Ledger USB DMK UUID).\n type: typeof UI_RESPONSE.RECEIVE_SELECT_DEVICE;\n payload: { sdkConnectId: string };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_DEVICE_CONNECT;\n payload: { confirmed: boolean };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PREEMPTION;\n payload: { decision: PreemptionDecision };\n }\n | {\n type: typeof UI_RESPONSE.CANCEL;\n payload?: undefined;\n };\n","/** Events generated by SDK internal detection (not from hardware directly). */\nexport const SDK = {\n DEVICE_STUCK: 'device-stuck',\n DEVICE_UNRESPONSIVE: 'device-unresponsive',\n DEVICE_RECOVERED: 'device-recovered',\n DEVICE_INTERACTION: 'device-interaction',\n} as const;\n","/**\n * Per-device serial job queue with preemption support and stuck recovery.\n * Ensures that only one operation runs at a time per device, with intelligent\n * handling of conflicting operations.\n *\n * The 'confirm' level uses the standard UI request/response flow:\n * emits REQUEST_PREEMPTION → waits for RECEIVE_PREEMPTION via UiRequestRegistry.\n */\n\nimport { UI_REQUEST } from '../events/ui-request';\n\nimport type { UiRequestRegistry } from './UiRequestRegistry';\n\nexport type Interruptibility = 'none' | 'safe' | 'confirm';\n\nexport type PreemptionDecision = 'cancel-current' | 'wait' | 'reject-new';\n\nexport interface JobOptions {\n interruptibility?: Interruptibility;\n label?: string;\n}\n\nexport interface ActiveJobInfo {\n label?: string;\n interruptibility: Interruptibility;\n startedAt: number;\n}\n\nexport interface PreemptionEvent {\n deviceId: string;\n currentJob: ActiveJobInfo;\n newJob: { label?: string; interruptibility: Interruptibility };\n}\n\ninterface ActiveJob {\n options: Required<Pick<JobOptions, 'interruptibility'>> & Pick<JobOptions, 'label'>;\n abortController: AbortController;\n startedAt: number;\n}\n\nexport interface DeviceJobQueueDeps {\n /** Emit a UI request event to the frontend. */\n emit: (event: string, data: unknown) => void;\n /** Registry for waiting on UI responses. */\n uiRegistry: UiRequestRegistry;\n}\n\nexport class DeviceJobQueue {\n private readonly _queues = new Map<string, Promise<unknown>>();\n\n private readonly _active = new Map<string, ActiveJob>();\n\n private readonly _deps: DeviceJobQueueDeps | null;\n\n constructor(deps?: DeviceJobQueueDeps) {\n this._deps = deps ?? null;\n }\n\n /**\n * Enqueue a job for a specific device.\n * If a job is already running for this device, behavior depends on interruptibility:\n * - 'none': new job queues silently (no preemption possible)\n * - 'safe': current job is auto-cancelled, new job runs immediately after\n * - 'confirm': emits REQUEST_PREEMPTION and waits for UI response\n */\n async enqueue<T>(\n deviceId: string,\n job: (signal: AbortSignal) => Promise<T>,\n options: JobOptions = {}\n ): Promise<T> {\n const interruptibility = options.interruptibility ?? 'confirm';\n const active = this._active.get(deviceId);\n\n if (active) {\n switch (active.options.interruptibility) {\n case 'none':\n // Cannot interrupt, just queue behind\n break;\n case 'safe':\n // Auto-cancel current safe operation\n active.abortController.abort(new Error('Preempted by new operation'));\n break;\n case 'confirm': {\n // If a preemption dialog is already pending, reject immediately.\n // The user is already deciding for a previous request — don't\n // overwrite the UiRequestRegistry slot (single-slot per type).\n if (this._deps?.uiRegistry.hasPending(UI_REQUEST.REQUEST_PREEMPTION)) {\n throw Object.assign(\n new Error(`Device busy: a preemption decision is already pending`),\n { hardwareErrorCode: 'DEVICE_BUSY' }\n );\n }\n const decision = await this._requestPreemptionDecision(deviceId, active, {\n label: options.label,\n interruptibility,\n });\n switch (decision) {\n case 'cancel-current':\n active.abortController.abort(new Error('Cancelled by user via preemption'));\n break;\n case 'reject-new':\n throw Object.assign(\n new Error(`Device busy: ${active.options.label ?? 'unknown operation'}`),\n { hardwareErrorCode: 'DEVICE_BUSY' }\n );\n case 'wait':\n break;\n }\n break;\n }\n }\n }\n\n const ac = new AbortController();\n const prev = this._queues.get(deviceId) ?? Promise.resolve();\n\n const next = prev\n .catch(() => {})\n .then(async () => {\n this._active.set(deviceId, {\n options: { interruptibility, label: options.label },\n abortController: ac,\n startedAt: Date.now(),\n });\n try {\n return await job(ac.signal);\n } finally {\n this._active.delete(deviceId);\n }\n });\n\n const tail = next.catch(() => {});\n this._queues.set(deviceId, tail);\n tail.then(() => {\n if (this._queues.get(deviceId) === tail) {\n this._queues.delete(deviceId);\n }\n });\n return next;\n }\n\n /** Manually cancel the active job on a device. Returns false if job is non-interruptible. */\n cancelActive(deviceId: string): boolean {\n const active = this._active.get(deviceId);\n if (!active) return false;\n if (active.options.interruptibility === 'none') return false;\n active.abortController.abort(new Error('Manually cancelled'));\n return true;\n }\n\n /** Force cancel regardless of interruptibility. Use for device stuck recovery. */\n forceCancelActive(deviceId: string): boolean {\n const active = this._active.get(deviceId);\n if (!active) return false;\n active.abortController.abort(new Error('Force cancelled for recovery'));\n return true;\n }\n\n /** Get info about the currently active job for a device, or null if idle. */\n getActiveJob(deviceId: string): ActiveJobInfo | null {\n const active = this._active.get(deviceId);\n if (!active) return null;\n return {\n label: active.options.label,\n interruptibility: active.options.interruptibility,\n startedAt: active.startedAt,\n };\n }\n\n clear(): void {\n for (const active of this._active.values()) {\n active.abortController.abort(new Error('Queue cleared'));\n }\n this._active.clear();\n this._queues.clear();\n }\n\n /**\n * Request preemption decision via UI request/response flow.\n * Falls back to 'wait' if deps are not provided.\n */\n private async _requestPreemptionDecision(\n deviceId: string,\n active: ActiveJob,\n newJob: { label?: string; interruptibility: Interruptibility }\n ): Promise<PreemptionDecision> {\n if (!this._deps) return 'wait';\n\n const { emit, uiRegistry } = this._deps;\n\n emit(UI_REQUEST.REQUEST_PREEMPTION, {\n type: UI_REQUEST.REQUEST_PREEMPTION,\n payload: {\n deviceId,\n currentJob: {\n label: active.options.label,\n interruptibility: active.options.interruptibility,\n startedAt: active.startedAt,\n },\n newJob,\n },\n });\n\n const response = await uiRegistry.wait<{ decision: PreemptionDecision }>(\n UI_REQUEST.REQUEST_PREEMPTION\n );\n\n return response?.decision ?? 'wait';\n }\n}\n","import type { DeviceCapabilities, DeviceInfo, VendorType } from './device';\nimport type { UiResponseEvent } from '../events/ui-request';\n\n// =====================================================================\n// Connector types — transport-level abstraction for device communication\n// =====================================================================\n\n/**\n * Minimal device info returned during discovery (searchDevices).\n * At scan time, full DeviceInfo fields like firmwareVersion are not yet available.\n */\nexport interface ConnectorDevice {\n connectId: string;\n deviceId: string;\n name: string;\n model?: string;\n\n /** Device capabilities — available from scan time */\n capabilities?: DeviceCapabilities;\n}\n\nexport interface ConnectorSession {\n sessionId: string;\n deviceInfo: DeviceInfo;\n}\n\nexport type ConnectorEventType = 'device-connect' | 'device-disconnect' | 'ui-request' | 'ui-event';\n\n/**\n * Interaction event types emitted via 'ui-event'.\n * These map to user-facing prompts (confirm on device, open app, etc.).\n */\nexport enum EConnectorInteraction {\n /** Device requires user to open a specific app */\n ConfirmOpenApp = 'confirm-open-app',\n /** Device requires user to unlock */\n UnlockDevice = 'unlock-device',\n /** Device needs user to confirm on device (sign, verify, etc.) */\n ConfirmOnDevice = 'confirm-on-device',\n /** Previous interaction completed — clear UI prompt */\n InteractionComplete = 'interaction-complete',\n}\n\nexport type ConnectorUiEvent =\n | { type: EConnectorInteraction.ConfirmOpenApp; payload: { sessionId: string } }\n | { type: EConnectorInteraction.UnlockDevice; payload: { sessionId: string } }\n | { type: EConnectorInteraction.ConfirmOnDevice; payload: { sessionId: string } }\n | { type: EConnectorInteraction.InteractionComplete; payload: { sessionId: string } };\n\nexport interface ConnectorEventMap {\n 'device-connect': { device: ConnectorDevice };\n 'device-disconnect': { connectId: string };\n 'ui-request': { type: string; payload?: unknown };\n 'ui-event': ConnectorUiEvent;\n}\n\nexport interface IConnector {\n searchDevices(): Promise<ConnectorDevice[]>;\n connect(deviceId?: string): Promise<ConnectorSession>;\n disconnect(sessionId: string): Promise<void>;\n call(sessionId: string, method: string, params: unknown): Promise<unknown>;\n cancel(sessionId: string): Promise<void>;\n\n /** Send a UI response (e.g. PIN, passphrase) to the device. */\n uiResponse(response: UiResponseEvent): void;\n\n on<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;\n off<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;\n\n reset(): void;\n}\n\n// =====================================================================\n// Hardware bridge — generic forwarding interface for cross-boundary IConnector\n//\n// The same shape works across any process / context boundary that needs to\n// expose a multi-vendor backend behind a single-vendor IConnector facade:\n// - Electron (renderer ↔ preload/main via contextBridge)\n// - Browser Extension (popup/content ↔ background/offscreen via chrome.runtime)\n// - React Native (JS ↔ native module via NativeModules)\n// - Web Worker / iframe (postMessage)\n//\n// Each method takes a `vendor` discriminator so a single bridge implementation\n// can multiplex across vendors (ledger, trezor, ...).\n// =====================================================================\n\nexport interface IHardwareBridge {\n searchDevices(params: { vendor: VendorType }): Promise<ConnectorDevice[]>;\n connect(params: { vendor: VendorType; deviceId?: string }): Promise<ConnectorSession>;\n disconnect(params: { vendor: VendorType; sessionId: string }): Promise<void>;\n call(params: {\n vendor: VendorType;\n sessionId: string;\n method: string;\n callParams: unknown;\n }): Promise<unknown>;\n cancel(params: { vendor: VendorType; sessionId: string }): Promise<void>;\n uiResponse(params: { vendor: VendorType; response: UiResponseEvent }): void;\n reset(params: { vendor: VendorType }): void;\n\n /** Register an event handler for connector events forwarded across the bridge. */\n onEvent(\n params: { vendor: VendorType },\n handler: (event: { type: ConnectorEventType; data: unknown }) => void\n ): void;\n\n /** Unregister a previously registered event handler. */\n offEvent(\n params: { vendor: VendorType },\n handler: (event: { type: ConnectorEventType; data: unknown }) => void\n ): void;\n}\n\n/**\n * Adapt an IHardwareBridge (multi-vendor backend) into a single-vendor IConnector.\n * Every IConnector method becomes a transparent forward to bridge.<method>({ vendor, ... }).\n * Events are forwarded via bridge.onEvent / offEvent.\n *\n * Use this anywhere the actual hardware lives behind a process / context boundary\n * (Electron main, extension background, native module, worker, iframe).\n */\nexport function createBridgedConnector(vendor: VendorType, bridge: IHardwareBridge): IConnector {\n // Map from typed IConnector handlers to the bridge handler so we can\n // unregister them correctly via off().\n const handlerMap = new Map<\n (data: ConnectorEventMap[ConnectorEventType]) => void,\n (event: { type: ConnectorEventType; data: unknown }) => void\n >();\n\n return {\n searchDevices: () => bridge.searchDevices({ vendor }),\n connect: deviceId => bridge.connect({ vendor, deviceId }),\n disconnect: sessionId => bridge.disconnect({ vendor, sessionId }),\n call: (sessionId, method, callParams) => bridge.call({ vendor, sessionId, method, callParams }),\n cancel: sessionId => bridge.cancel({ vendor, sessionId }),\n uiResponse: response => bridge.uiResponse({ vendor, response }),\n on: (event, handler) => {\n const bridgeHandler = (e: { type: ConnectorEventType; data: unknown }) => {\n if (e.type === event) {\n handler(e.data as ConnectorEventMap[typeof event]);\n }\n };\n handlerMap.set(\n handler as (data: ConnectorEventMap[ConnectorEventType]) => void,\n bridgeHandler\n );\n bridge.onEvent({ vendor }, bridgeHandler);\n },\n off: (_event, handler) => {\n const bridgeHandler = handlerMap.get(\n handler as (data: ConnectorEventMap[ConnectorEventType]) => void\n );\n if (bridgeHandler) {\n bridge.offEvent({ vendor }, bridgeHandler);\n handlerMap.delete(handler as (data: ConnectorEventMap[ConnectorEventType]) => void);\n }\n },\n reset: () => bridge.reset({ vendor }),\n };\n}\n","/**\n * Minimal typed event emitter using Map<string, Set<listener>>.\n * Each adapter uses this for device events (connect, disconnect, pin, etc.).\n *\n * TMap is a record mapping event name strings to their payload types.\n * Example:\n * type MyEvents = { 'connect': { id: string }; 'disconnect': { id: string } };\n * const emitter = new TypedEventEmitter<MyEvents>();\n * emitter.on('connect', (data) => { data.id }); // data is { id: string }\n *\n * For backward compatibility, TMap defaults to Record<string, any> so that\n * existing code using `new TypedEventEmitter<SomeUnionType>()` still compiles.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class TypedEventEmitter<TMap extends Record<string, any> = Record<string, any>> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private readonly _listeners = new Map<string, Set<(event: any) => void>>();\n\n on<K extends keyof TMap & string>(event: K, listener: (event: TMap[K]) => void): void;\n\n on(event: string, listener: (event: any) => void): void;\n\n on(event: string, listener: (event: any) => void): void {\n let set = this._listeners.get(event);\n if (!set) {\n set = new Set();\n this._listeners.set(event, set);\n }\n set.add(listener);\n }\n\n off<K extends keyof TMap & string>(event: K, listener: (event: TMap[K]) => void): void;\n\n off(event: string, listener: (event: any) => void): void;\n\n off(event: string, listener: (event: any) => void): void {\n const set = this._listeners.get(event);\n if (set) {\n set.delete(listener);\n if (set.size === 0) this._listeners.delete(event);\n }\n }\n\n emit<K extends keyof TMap & string>(event: K, data: TMap[K]): void;\n\n emit(event: string, data: unknown): void;\n\n emit(event: string, data: unknown): void {\n const set = this._listeners.get(event);\n if (set) {\n for (const listener of set) listener(data);\n }\n }\n\n removeAllListeners(): void {\n this._listeners.clear();\n }\n}\n","import { UI_REQUEST, UI_RESPONSE } from '../events/ui-request';\n\n/** 10 min — every UI request is human-in-the-loop; bias toward \"wait long\". */\nexport const UI_REQUEST_DEFAULT_TIMEOUT_MS = 600_000;\n\nconst RESPONSE_TO_REQUEST: Record<string, string> = {\n [UI_RESPONSE.RECEIVE_PIN]: UI_REQUEST.REQUEST_PIN,\n [UI_RESPONSE.RECEIVE_PASSPHRASE]: UI_REQUEST.REQUEST_PASSPHRASE,\n [UI_RESPONSE.RECEIVE_PASSPHRASE_ON_DEVICE]: UI_REQUEST.REQUEST_PASSPHRASE_ON_DEVICE,\n [UI_RESPONSE.RECEIVE_SELECT_DEVICE]: UI_REQUEST.REQUEST_SELECT_DEVICE,\n [UI_RESPONSE.RECEIVE_DEVICE_CONNECT]: UI_REQUEST.REQUEST_DEVICE_CONNECT,\n [UI_RESPONSE.RECEIVE_PREEMPTION]: UI_REQUEST.REQUEST_PREEMPTION,\n // RECEIVE_QR_RESPONSE maps to QR_DISPLAY or QR_SCAN — resolved dynamically below.\n};\n\nexport const UI_REQUEST_PREEMPTED_TAG = 'UiRequestPreempted';\nexport const UI_REQUEST_CANCELLED_TAG = 'UiRequestCancelled';\nexport const UI_REQUEST_TIMEOUT_TAG = 'UiRequestTimeout';\n\ntype PendingEntry = {\n resolve: (payload: unknown) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\n/**\n * Per-type single-slot registry for adapter-level UI requests. A new `wait`\n * of the same type preempts (rejects) the prior one. Unknown response types\n * are dropped silently so the public `uiResponse` entry never throws.\n */\nexport class UiRequestRegistry {\n private pending = new Map<string, PendingEntry>();\n\n wait<T = unknown>(requestType: string, options?: { timeoutMs?: number }): Promise<T> {\n const existing = this.pending.get(requestType);\n if (existing) {\n clearTimeout(existing.timer);\n this.pending.delete(requestType);\n existing.reject(\n Object.assign(new Error(`UI request '${requestType}' was superseded by a new request`), {\n _tag: UI_REQUEST_PREEMPTED_TAG,\n })\n );\n }\n\n const timeoutMs = options?.timeoutMs ?? UI_REQUEST_DEFAULT_TIMEOUT_MS;\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this.pending.get(requestType)?.timer === timer) {\n this.pending.delete(requestType);\n reject(\n Object.assign(new Error(`UI request '${requestType}' timed out after ${timeoutMs}ms`), {\n _tag: UI_REQUEST_TIMEOUT_TAG,\n })\n );\n }\n }, timeoutMs);\n\n this.pending.set(requestType, {\n resolve: resolve as (payload: unknown) => void,\n reject,\n timer,\n });\n });\n }\n\n resolve(responseType: string, payload: unknown): void {\n let requestType = RESPONSE_TO_REQUEST[responseType];\n if (responseType === UI_RESPONSE.RECEIVE_QR_RESPONSE) {\n if (this.pending.has(UI_REQUEST.REQUEST_QR_DISPLAY)) {\n requestType = UI_REQUEST.REQUEST_QR_DISPLAY;\n } else if (this.pending.has(UI_REQUEST.REQUEST_QR_SCAN)) {\n requestType = UI_REQUEST.REQUEST_QR_SCAN;\n }\n }\n if (!requestType) return;\n\n const entry = this.pending.get(requestType);\n if (!entry) return;\n\n clearTimeout(entry.timer);\n this.pending.delete(requestType);\n entry.resolve(payload);\n }\n\n cancel(requestType?: string): void {\n if (requestType) {\n const entry = this.pending.get(requestType);\n if (!entry) return;\n clearTimeout(entry.timer);\n this.pending.delete(requestType);\n entry.reject(\n Object.assign(new Error(`UI request '${requestType}' was cancelled`), {\n _tag: UI_REQUEST_CANCELLED_TAG,\n })\n );\n return;\n }\n\n for (const [type, entry] of this.pending) {\n clearTimeout(entry.timer);\n entry.reject(\n Object.assign(new Error(`UI request '${type}' was cancelled`), {\n _tag: UI_REQUEST_CANCELLED_TAG,\n })\n );\n }\n this.pending.clear();\n }\n\n reset(): void {\n this.cancel();\n }\n\n hasPending(requestType: string): boolean {\n return this.pending.has(requestType);\n }\n}\n","/**\n * Compare two semver strings (e.g. \"2.1.0\" vs \"2.3.1\").\n * Returns -1 if a < b, 0 if equal, 1 if a > b.\n */\nexport function compareSemver(a: string, b: string): number {\n const pa = a.split('.').map(Number);\n const pb = b.split('.').map(Number);\n for (let i = 0; i < 3; i++) {\n const va = pa[i] ?? 0;\n const vb = pb[i] ?? 0;\n if (va < vb) return -1;\n if (va > vb) return 1;\n }\n return 0;\n}\n","/** Ensure hex string has 0x prefix */\nexport function ensure0x(hex: string): string {\n return hex.startsWith('0x') ? hex : `0x${hex}`;\n}\n\n/** Strip 0x prefix from hex string */\nexport function stripHex(hex: string): string {\n return hex.startsWith('0x') ? hex.slice(2) : hex;\n}\n\n/** Pad hex to 64 chars (32 bytes) with 0x prefix */\nexport function padHex64(hex: string): string {\n return `0x${stripHex(hex).padStart(64, '0')}`;\n}\n\n/** Convert a hex string (with or without 0x prefix) to a Uint8Array. */\nexport function hexToBytes(hex: string): Uint8Array {\n const clean = stripHex(hex);\n const bytes = new Uint8Array(clean.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/** Convert a Uint8Array to a hex string (no 0x prefix). */\nexport function bytesToHex(bytes: Uint8Array): string {\n return Array.from(bytes)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n}\n","import { HardwareErrorCode } from '../types/errors';\n\n/**\n * Enrich a hardware error message with actionable recovery hints.\n * Shared across adapters (Ledger, Trezor, etc.).\n */\nexport function enrichErrorMessage(code: HardwareErrorCode, originalMessage: string): string {\n switch (code) {\n case HardwareErrorCode.PinInvalid:\n return `${originalMessage}. Please re-enter your PIN.`;\n case HardwareErrorCode.PinCancelled:\n return `${originalMessage}. PIN entry was cancelled.`;\n case HardwareErrorCode.DeviceBusy:\n return `${originalMessage}. The device is in use by another application. Close other wallet apps and try again.`;\n case HardwareErrorCode.DeviceDisconnected:\n return `${originalMessage}. Please reconnect the device and try again.`;\n case HardwareErrorCode.DeviceLocked:\n return `${originalMessage}. Please unlock your device and try again.`;\n case HardwareErrorCode.UserRejected:\n return `${originalMessage}. The request was rejected on the device.`;\n case HardwareErrorCode.WrongApp:\n return `${originalMessage}. Please open the correct app on your device.`;\n case HardwareErrorCode.AppNotOpen:\n return `${originalMessage}. The required app is not installed on the device.`;\n case HardwareErrorCode.TransportNotAvailable:\n return `${originalMessage}. Ensure the device bridge/transport is available and running.`;\n case HardwareErrorCode.FirmwareTooOld:\n return `${originalMessage}. Please update your device firmware.`;\n case HardwareErrorCode.DeviceNotInitialized:\n return `${originalMessage}. The device may need to be set up first.`;\n case HardwareErrorCode.OperationTimeout:\n return `${originalMessage}. The operation timed out. Please try again.`;\n case HardwareErrorCode.EvmBlindSigningRequired:\n return 'Please enable Blind signing on your Ledger: open the Ethereum app → Settings → Blind signing → Enabled, then try again.';\n case HardwareErrorCode.EvmClearSignPluginMissing:\n return 'Required Ledger plugin is not installed. Please install the matching plugin on Ledger Live, then try again.';\n case HardwareErrorCode.EvmDataTooLarge:\n return 'Transaction data is too large for your Ledger device. Try a simpler transaction or use a Ledger with more memory.';\n case HardwareErrorCode.EvmTxTypeNotSupported:\n return 'Transaction type is not supported by your Ledger Ethereum app. Please update the app to the latest version.';\n case HardwareErrorCode.AppTooOld:\n return 'Your Ledger app is out of date. Please update it via Ledger Live.';\n default:\n return originalMessage;\n }\n}\n","import type { Response } from '../types/response';\n\n/**\n * Generic batch call with progress reporting.\n * If any single call fails, returns the failure immediately.\n */\nexport async function batchCall<TParam, TResult>(\n params: TParam[],\n callFn: (p: TParam) => Promise<Response<TResult>>,\n onProgress?: (progress: { index: number; total: number }) => void\n): Promise<Response<TResult[]>> {\n const results: TResult[] = [];\n for (let i = 0; i < params.length; i++) {\n const result = await callFn(params[i]);\n if (!result.success) {\n return result;\n }\n results.push(result.payload);\n onProgress?.({ index: i, total: params.length });\n }\n return { success: true, payload: results };\n}\n"],"mappings":";AAAO,IAAK,oBAAL,kBAAKA,uBAAL;AACL,EAAAA,sCAAA,kBAAe,KAAf;AACA,EAAAA,sCAAA,oBAAiB,KAAjB;AACA,EAAAA,sCAAA,wBAAqB,KAArB;AACA,EAAAA,sCAAA,kBAAe,KAAf;AACA,EAAAA,sCAAA,gBAAa,KAAb;AACA,EAAAA,sCAAA,4BAAyB,KAAzB;AACA,EAAAA,sCAAA,gBAAa,KAAb;AACA,EAAAA,sCAAA,mBAAgB,KAAhB;AACA,EAAAA,sCAAA,oBAAiB,KAAjB;AACA,EAAAA,sCAAA,sBAAmB,KAAnB;AACA,EAAAA,sCAAA,wBAAqB,MAArB;AAGA,EAAAA,sCAAA,gBAAa,QAAb;AACA,EAAAA,sCAAA,kBAAe,QAAf;AACA,EAAAA,sCAAA,wBAAqB,QAArB;AAGA,EAAAA,sCAAA,kBAAe,QAAf;AACA,EAAAA,sCAAA,0BAAuB,QAAvB;AACA,EAAAA,sCAAA,wBAAqB,QAArB;AACA,EAAAA,sCAAA,oBAAiB,QAAjB;AAGA,EAAAA,sCAAA,cAAW,QAAX;AAGA,EAAAA,sCAAA,oBAAiB,QAAjB;AAGA,EAAAA,sCAAA,oBAAiB,QAAjB;AACA,EAAAA,sCAAA,2BAAwB,QAAxB;AAKA,EAAAA,sCAAA,6BAA0B,QAA1B;AAEA,EAAAA,sCAAA,+BAA4B,QAA5B;AAEA,EAAAA,sCAAA,qBAAkB,QAAlB;AAEA,EAAAA,sCAAA,2BAAwB,QAAxB;AAEA,EAAAA,sCAAA,eAAY,QAAZ;AA7CU,SAAAA;AAAA,GAAA;;;ACkBL,SAAS,QAAW,SAAwB;AACjD,SAAO,EAAE,SAAS,MAAM,QAAQ;AAClC;AAEO,SAAS,QAAQ,MAAyB,OAAe,SAA2B;AACzF,SAAO,EAAE,SAAS,OAAO,SAAS,EAAE,OAAO,MAAM,QAAQ,EAAE;AAC7D;;;ACjBA,SAAS,cAAc;AACvB,SAAS,YAAY,mBAAmB;AAWjC,IAAM,0BAA+D;AAAA,EAC1E,KAAK;AAAA;AAAA;AAAA,EAGL,KAAK;AAAA,EACL,KAAK;AAAA,EACL,MAAM;AACR;AAUO,SAAS,wBAAwB,SAAyB;AAC/D,SAAO,WAAW,OAAO,YAAY,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,EAAE;AAC7D;;;ACtCO,IAAM,eAAe;AAGrB,IAAM,SAAS;AAAA,EACpB,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,SAAS;AAAA,EACT,SAAS;AAAA,EACT,UAAU;AAAA,EACV,kBAAkB;AACpB;;;ACRO,IAAM,WAAW;AAEjB,IAAM,aAAa;AAAA,EACxB,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB,8BAA8B;AAAA,EAC9B,gBAAgB;AAAA,EAChB,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,2BAA2B;AAAA,EAC3B,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,mBAAmB;AAAA,EACnB,cAAc;AAChB;AAEO,IAAM,cAAc;AAAA,EACzB,aAAa;AAAA,EACb,oBAAoB;AAAA,EACpB,8BAA8B;AAAA,EAC9B,qBAAqB;AAAA,EACrB,uBAAuB;AAAA,EACvB,wBAAwB;AAAA,EACxB,oBAAoB;AAAA,EACpB,QAAQ;AACV;;;AC9BO,IAAM,MAAM;AAAA,EACjB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,oBAAoB;AACtB;;;ACyCO,IAAM,iBAAN,MAAqB;AAAA,EAO1B,YAAY,MAA2B;AANvC,SAAiB,UAAU,oBAAI,IAA8B;AAE7D,SAAiB,UAAU,oBAAI,IAAuB;AAKpD,SAAK,QAAQ,QAAQ;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,QACJ,UACA,KACA,UAAsB,CAAC,GACX;AACZ,UAAM,mBAAmB,QAAQ,oBAAoB;AACrD,UAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ;AAExC,QAAI,QAAQ;AACV,cAAQ,OAAO,QAAQ,kBAAkB;AAAA,QACvC,KAAK;AAEH;AAAA,QACF,KAAK;AAEH,iBAAO,gBAAgB,MAAM,IAAI,MAAM,4BAA4B,CAAC;AACpE;AAAA,QACF,KAAK,WAAW;AAId,cAAI,KAAK,OAAO,WAAW,WAAW,WAAW,kBAAkB,GAAG;AACpE,kBAAM,OAAO;AAAA,cACX,IAAI,MAAM,uDAAuD;AAAA,cACjE,EAAE,mBAAmB,cAAc;AAAA,YACrC;AAAA,UACF;AACA,gBAAM,WAAW,MAAM,KAAK,2BAA2B,UAAU,QAAQ;AAAA,YACvE,OAAO,QAAQ;AAAA,YACf;AAAA,UACF,CAAC;AACD,kBAAQ,UAAU;AAAA,YAChB,KAAK;AACH,qBAAO,gBAAgB,MAAM,IAAI,MAAM,kCAAkC,CAAC;AAC1E;AAAA,YACF,KAAK;AACH,oBAAM,OAAO;AAAA,gBACX,IAAI,MAAM,gBAAgB,OAAO,QAAQ,SAAS,mBAAmB,EAAE;AAAA,gBACvE,EAAE,mBAAmB,cAAc;AAAA,cACrC;AAAA,YACF,KAAK;AACH;AAAA,UACJ;AACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,KAAK,IAAI,gBAAgB;AAC/B,UAAM,OAAO,KAAK,QAAQ,IAAI,QAAQ,KAAK,QAAQ,QAAQ;AAE3D,UAAM,OAAO,KACV,MAAM,MAAM;AAAA,IAAC,CAAC,EACd,KAAK,YAAY;AAChB,WAAK,QAAQ,IAAI,UAAU;AAAA,QACzB,SAAS,EAAE,kBAAkB,OAAO,QAAQ,MAAM;AAAA,QAClD,iBAAiB;AAAA,QACjB,WAAW,KAAK,IAAI;AAAA,MACtB,CAAC;AACD,UAAI;AACF,eAAO,MAAM,IAAI,GAAG,MAAM;AAAA,MAC5B,UAAE;AACA,aAAK,QAAQ,OAAO,QAAQ;AAAA,MAC9B;AAAA,IACF,CAAC;AAEH,UAAM,OAAO,KAAK,MAAM,MAAM;AAAA,IAAC,CAAC;AAChC,SAAK,QAAQ,IAAI,UAAU,IAAI;AAC/B,SAAK,KAAK,MAAM;AACd,UAAI,KAAK,QAAQ,IAAI,QAAQ,MAAM,MAAM;AACvC,aAAK,QAAQ,OAAO,QAAQ;AAAA,MAC9B;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,aAAa,UAA2B;AACtC,UAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ;AACxC,QAAI,CAAC,OAAQ,QAAO;AACpB,QAAI,OAAO,QAAQ,qBAAqB,OAAQ,QAAO;AACvD,WAAO,gBAAgB,MAAM,IAAI,MAAM,oBAAoB,CAAC;AAC5D,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,kBAAkB,UAA2B;AAC3C,UAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ;AACxC,QAAI,CAAC,OAAQ,QAAO;AACpB,WAAO,gBAAgB,MAAM,IAAI,MAAM,8BAA8B,CAAC;AACtE,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,aAAa,UAAwC;AACnD,UAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ;AACxC,QAAI,CAAC,OAAQ,QAAO;AACpB,WAAO;AAAA,MACL,OAAO,OAAO,QAAQ;AAAA,MACtB,kBAAkB,OAAO,QAAQ;AAAA,MACjC,WAAW,OAAO;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,eAAW,UAAU,KAAK,QAAQ,OAAO,GAAG;AAC1C,aAAO,gBAAgB,MAAM,IAAI,MAAM,eAAe,CAAC;AAAA,IACzD;AACA,SAAK,QAAQ,MAAM;AACnB,SAAK,QAAQ,MAAM;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,2BACZ,UACA,QACA,QAC6B;AAC7B,QAAI,CAAC,KAAK,MAAO,QAAO;AAExB,UAAM,EAAE,MAAM,WAAW,IAAI,KAAK;AAElC,SAAK,WAAW,oBAAoB;AAAA,MAClC,MAAM,WAAW;AAAA,MACjB,SAAS;AAAA,QACP;AAAA,QACA,YAAY;AAAA,UACV,OAAO,OAAO,QAAQ;AAAA,UACtB,kBAAkB,OAAO,QAAQ;AAAA,UACjC,WAAW,OAAO;AAAA,QACpB;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAED,UAAM,WAAW,MAAM,WAAW;AAAA,MAChC,WAAW;AAAA,IACb;AAEA,WAAO,UAAU,YAAY;AAAA,EAC/B;AACF;;;ACjLO,IAAK,wBAAL,kBAAKC,2BAAL;AAEL,EAAAA,uBAAA,oBAAiB;AAEjB,EAAAA,uBAAA,kBAAe;AAEf,EAAAA,uBAAA,qBAAkB;AAElB,EAAAA,uBAAA,yBAAsB;AARZ,SAAAA;AAAA,GAAA;AAyFL,SAAS,uBAAuB,QAAoB,QAAqC;AAG9F,QAAM,aAAa,oBAAI,IAGrB;AAEF,SAAO;AAAA,IACL,eAAe,MAAM,OAAO,cAAc,EAAE,OAAO,CAAC;AAAA,IACpD,SAAS,cAAY,OAAO,QAAQ,EAAE,QAAQ,SAAS,CAAC;AAAA,IACxD,YAAY,eAAa,OAAO,WAAW,EAAE,QAAQ,UAAU,CAAC;AAAA,IAChE,MAAM,CAAC,WAAW,QAAQ,eAAe,OAAO,KAAK,EAAE,QAAQ,WAAW,QAAQ,WAAW,CAAC;AAAA,IAC9F,QAAQ,eAAa,OAAO,OAAO,EAAE,QAAQ,UAAU,CAAC;AAAA,IACxD,YAAY,cAAY,OAAO,WAAW,EAAE,QAAQ,SAAS,CAAC;AAAA,IAC9D,IAAI,CAAC,OAAO,YAAY;AACtB,YAAM,gBAAgB,CAAC,MAAmD;AACxE,YAAI,EAAE,SAAS,OAAO;AACpB,kBAAQ,EAAE,IAAuC;AAAA,QACnD;AAAA,MACF;AACA,iBAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AACA,aAAO,QAAQ,EAAE,OAAO,GAAG,aAAa;AAAA,IAC1C;AAAA,IACA,KAAK,CAAC,QAAQ,YAAY;AACxB,YAAM,gBAAgB,WAAW;AAAA,QAC/B;AAAA,MACF;AACA,UAAI,eAAe;AACjB,eAAO,SAAS,EAAE,OAAO,GAAG,aAAa;AACzC,mBAAW,OAAO,OAAgE;AAAA,MACpF;AAAA,IACF;AAAA,IACA,OAAO,MAAM,OAAO,MAAM,EAAE,OAAO,CAAC;AAAA,EACtC;AACF;;;ACjJO,IAAM,oBAAN,MAAgF;AAAA,EAAhF;AAEL;AAAA,SAAiB,aAAa,oBAAI,IAAuC;AAAA;AAAA,EAMzE,GAAG,OAAe,UAAsC;AACtD,QAAI,MAAM,KAAK,WAAW,IAAI,KAAK;AACnC,QAAI,CAAC,KAAK;AACR,YAAM,oBAAI,IAAI;AACd,WAAK,WAAW,IAAI,OAAO,GAAG;AAAA,IAChC;AACA,QAAI,IAAI,QAAQ;AAAA,EAClB;AAAA,EAMA,IAAI,OAAe,UAAsC;AACvD,UAAM,MAAM,KAAK,WAAW,IAAI,KAAK;AACrC,QAAI,KAAK;AACP,UAAI,OAAO,QAAQ;AACnB,UAAI,IAAI,SAAS,EAAG,MAAK,WAAW,OAAO,KAAK;AAAA,IAClD;AAAA,EACF;AAAA,EAMA,KAAK,OAAe,MAAqB;AACvC,UAAM,MAAM,KAAK,WAAW,IAAI,KAAK;AACrC,QAAI,KAAK;AACP,iBAAW,YAAY,IAAK,UAAS,IAAI;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,qBAA2B;AACzB,SAAK,WAAW,MAAM;AAAA,EACxB;AACF;;;ACtDO,IAAM,gCAAgC;AAE7C,IAAM,sBAA8C;AAAA,EAClD,CAAC,YAAY,WAAW,GAAG,WAAW;AAAA,EACtC,CAAC,YAAY,kBAAkB,GAAG,WAAW;AAAA,EAC7C,CAAC,YAAY,4BAA4B,GAAG,WAAW;AAAA,EACvD,CAAC,YAAY,qBAAqB,GAAG,WAAW;AAAA,EAChD,CAAC,YAAY,sBAAsB,GAAG,WAAW;AAAA,EACjD,CAAC,YAAY,kBAAkB,GAAG,WAAW;AAAA;AAE/C;AAEO,IAAM,2BAA2B;AACjC,IAAM,2BAA2B;AACjC,IAAM,yBAAyB;AAa/B,IAAM,oBAAN,MAAwB;AAAA,EAAxB;AACL,SAAQ,UAAU,oBAAI,IAA0B;AAAA;AAAA,EAEhD,KAAkB,aAAqB,SAA8C;AACnF,UAAM,WAAW,KAAK,QAAQ,IAAI,WAAW;AAC7C,QAAI,UAAU;AACZ,mBAAa,SAAS,KAAK;AAC3B,WAAK,QAAQ,OAAO,WAAW;AAC/B,eAAS;AAAA,QACP,OAAO,OAAO,IAAI,MAAM,eAAe,WAAW,mCAAmC,GAAG;AAAA,UACtF,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,YAAY,SAAS,aAAa;AAExC,WAAO,IAAI,QAAW,CAAC,SAAS,WAAW;AACzC,YAAM,QAAQ,WAAW,MAAM;AAC7B,YAAI,KAAK,QAAQ,IAAI,WAAW,GAAG,UAAU,OAAO;AAClD,eAAK,QAAQ,OAAO,WAAW;AAC/B;AAAA,YACE,OAAO,OAAO,IAAI,MAAM,eAAe,WAAW,qBAAqB,SAAS,IAAI,GAAG;AAAA,cACrF,MAAM;AAAA,YACR,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,GAAG,SAAS;AAEZ,WAAK,QAAQ,IAAI,aAAa;AAAA,QAC5B;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA,EAEA,QAAQ,cAAsB,SAAwB;AACpD,QAAI,cAAc,oBAAoB,YAAY;AAClD,QAAI,iBAAiB,YAAY,qBAAqB;AACpD,UAAI,KAAK,QAAQ,IAAI,WAAW,kBAAkB,GAAG;AACnD,sBAAc,WAAW;AAAA,MAC3B,WAAW,KAAK,QAAQ,IAAI,WAAW,eAAe,GAAG;AACvD,sBAAc,WAAW;AAAA,MAC3B;AAAA,IACF;AACA,QAAI,CAAC,YAAa;AAElB,UAAM,QAAQ,KAAK,QAAQ,IAAI,WAAW;AAC1C,QAAI,CAAC,MAAO;AAEZ,iBAAa,MAAM,KAAK;AACxB,SAAK,QAAQ,OAAO,WAAW;AAC/B,UAAM,QAAQ,OAAO;AAAA,EACvB;AAAA,EAEA,OAAO,aAA4B;AACjC,QAAI,aAAa;AACf,YAAM,QAAQ,KAAK,QAAQ,IAAI,WAAW;AAC1C,UAAI,CAAC,MAAO;AACZ,mBAAa,MAAM,KAAK;AACxB,WAAK,QAAQ,OAAO,WAAW;AAC/B,YAAM;AAAA,QACJ,OAAO,OAAO,IAAI,MAAM,eAAe,WAAW,iBAAiB,GAAG;AAAA,UACpE,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AACA;AAAA,IACF;AAEA,eAAW,CAAC,MAAM,KAAK,KAAK,KAAK,SAAS;AACxC,mBAAa,MAAM,KAAK;AACxB,YAAM;AAAA,QACJ,OAAO,OAAO,IAAI,MAAM,eAAe,IAAI,iBAAiB,GAAG;AAAA,UAC7D,MAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AACA,SAAK,QAAQ,MAAM;AAAA,EACrB;AAAA,EAEA,QAAc;AACZ,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,WAAW,aAA8B;AACvC,WAAO,KAAK,QAAQ,IAAI,WAAW;AAAA,EACrC;AACF;;;AClHO,SAAS,cAAc,GAAW,GAAmB;AAC1D,QAAM,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AAClC,QAAM,KAAK,EAAE,MAAM,GAAG,EAAE,IAAI,MAAM;AAClC,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,UAAM,KAAK,GAAG,CAAC,KAAK;AACpB,UAAM,KAAK,GAAG,CAAC,KAAK;AACpB,QAAI,KAAK,GAAI,QAAO;AACpB,QAAI,KAAK,GAAI,QAAO;AAAA,EACtB;AACA,SAAO;AACT;;;ACbO,SAAS,SAAS,KAAqB;AAC5C,SAAO,IAAI,WAAW,IAAI,IAAI,MAAM,KAAK,GAAG;AAC9C;AAGO,SAAS,SAAS,KAAqB;AAC5C,SAAO,IAAI,WAAW,IAAI,IAAI,IAAI,MAAM,CAAC,IAAI;AAC/C;AAGO,SAAS,SAAS,KAAqB;AAC5C,SAAO,KAAK,SAAS,GAAG,EAAE,SAAS,IAAI,GAAG,CAAC;AAC7C;AAGO,SAAS,WAAW,KAAyB;AAClD,QAAM,QAAQ,SAAS,GAAG;AAC1B,QAAM,QAAQ,IAAI,WAAW,MAAM,SAAS,CAAC;AAC7C,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,CAAC,IAAI,SAAS,MAAM,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;AAAA,EACvD;AACA,SAAO;AACT;AAGO,SAASC,YAAW,OAA2B;AACpD,SAAO,MAAM,KAAK,KAAK,EACpB,IAAI,OAAK,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EACxC,KAAK,EAAE;AACZ;;;ACxBO,SAAS,mBAAmB,MAAyB,iBAAiC;AAC3F,UAAQ,MAAM;AAAA,IACZ;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO,GAAG,eAAe;AAAA,IAC3B;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;;;ACvCA,eAAsB,UACpB,QACA,QACA,YAC8B;AAC9B,QAAM,UAAqB,CAAC;AAC5B,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,SAAS,MAAM,OAAO,OAAO,CAAC,CAAC;AACrC,QAAI,CAAC,OAAO,SAAS;AACnB,aAAO;AAAA,IACT;AACA,YAAQ,KAAK,OAAO,OAAO;AAC3B,iBAAa,EAAE,OAAO,GAAG,OAAO,OAAO,OAAO,CAAC;AAAA,EACjD;AACA,SAAO,EAAE,SAAS,MAAM,SAAS,QAAQ;AAC3C;","names":["HardwareErrorCode","EConnectorInteraction","bytesToHex"]}
|
|
1
|
+
{"version":3,"sources":["../src/types/errors.ts","../src/types/response.ts","../src/types/fingerprint.ts","../src/events/device.ts","../src/events/ui-request.ts","../src/events/sdk.ts","../src/utils/DeviceJobQueue.ts","../src/types/connector.ts","../src/utils/TypedEventEmitter.ts","../src/utils/UiRequestRegistry.ts","../src/utils/semver.ts","../src/utils/hex.ts","../src/utils/errorMessages.ts","../src/utils/batchCall.ts"],"sourcesContent":["export enum HardwareErrorCode {\n UnknownError = 0,\n DeviceNotFound = 1,\n DeviceDisconnected = 2,\n UserRejected = 3,\n DeviceBusy = 4,\n FirmwareUpdateRequired = 5,\n AppNotOpen = 6,\n InvalidParams = 7,\n TransportError = 8,\n OperationTimeout = 9,\n MethodNotSupported = 10,\n\n // PIN / Passphrase\n PinInvalid = 5520,\n PinCancelled = 5521,\n PassphraseRejected = 5522,\n\n // Device state\n DeviceLocked = 5530,\n DeviceNotInitialized = 5531,\n DeviceInBootloader = 5532,\n FirmwareTooOld = 5533,\n\n // Ledger specific\n WrongApp = 5540,\n\n // Device identity\n DeviceMismatch = 5560,\n\n // Transport\n BridgeNotFound = 5550,\n TransportNotAvailable = 5551,\n\n // --- EVM (Ledger Ethereum App) APDU-specific errors ---\n /** 0x6a80 Invalid data — observed on blindSignTransactionFallback when the\n * user has not enabled Blind signing on the device. */\n EvmBlindSigningRequired = 7001,\n /** 0x6984 Plugin not installed */\n EvmClearSignPluginMissing = 7002,\n /** 0x6a84 Insufficient memory (typical on Nano S with large calldata) */\n EvmDataTooLarge = 7003,\n /** 0x6501 TransactionType not supported (app too old for EIP-1559 / blob / 7702) */\n EvmTxTypeNotSupported = 7004,\n /** 0x911c Command code not supported — app predates current SDK */\n AppTooOld = 7005,\n}\n","import type { HardwareErrorCode } from './errors';\n\nexport interface Success<T> {\n success: true;\n payload: T;\n}\n\nexport interface Failure {\n success: false;\n payload: {\n error: string;\n code: HardwareErrorCode;\n };\n}\n\nexport type Response<T> = Success<T> | Failure;\n\nexport function success<T>(payload: T): Success<T> {\n return { success: true, payload };\n}\n\nexport function failure(code: HardwareErrorCode, error: string): Failure {\n return { success: false, payload: { error, code } };\n}\n","/**\n * Chain fingerprint utilities for device identity verification.\n *\n * Ledger devices have ephemeral IDs that change every session.\n * To verify that the same seed/device is connected, we derive an address\n * at a fixed path (account 0, index 0) and hash it into a stable \"chain fingerprint\".\n */\nimport { sha256 } from '@noble/hashes/sha256';\nimport { bytesToHex, utf8ToBytes } from '@noble/hashes/utils';\n\n/**\n * Fixed derivation paths used to generate chain fingerprints.\n * Uses standard index 0 — Ledger firmware shows device confirmation for\n * non-standard paths (e.g., index=100), which would interrupt wallet creation.\n * The address is hashed into a 16-char fingerprint, not exposed directly.\n * - EVM: cointype 60 (Ledger ETH App only supports 60)\n * - BTC: cointype 1 (testnet)\n * - SOL: cointype 501, standard 3-level hardened\n */\nexport const CHAIN_FINGERPRINT_PATHS: Record<ChainForFingerprint, string> = {\n evm: \"m/44'/60'/0'/0/0\",\n // BTC: account-level path (3 levels), mainnet cointype 0.\n // Cointype 1 (testnet) is rejected by some Ledger BTC App configurations.\n btc: \"m/44'/0'/0'\",\n sol: \"m/44'/501'/0'\",\n tron: \"m/44'/195'/0'/0/0\",\n};\n\nexport type ChainForFingerprint = 'evm' | 'btc' | 'sol' | 'tron';\n\n/**\n * 16-char SHA-256 fingerprint for device-identity verification.\n * Callers must canonicalize input (e.g. EVM address → lowercase) —\n * encoding variations otherwise cause false DeviceMismatch.\n */\nexport function deriveDeviceFingerprint(value: string): string {\n return bytesToHex(sha256(utf8ToBytes(value))).slice(0, 16);\n}\n","export const DEVICE_EVENT = 'DEVICE_EVENT';\n\n/** Events originating from the hardware device. */\nexport const DEVICE = {\n CONNECT: 'device-connect',\n DISCONNECT: 'device-disconnect',\n CHANGED: 'device-changed',\n} as const;\n","import type { QrResponseData } from '../types/qr';\nimport type { PreemptionDecision } from '../utils/DeviceJobQueue';\n\nexport const UI_EVENT = 'UI_EVENT';\n\nexport const UI_REQUEST = {\n REQUEST_PIN: 'ui-request-pin',\n REQUEST_PASSPHRASE: 'ui-request-passphrase',\n REQUEST_PASSPHRASE_ON_DEVICE: 'ui-request-passphrase-on-device',\n REQUEST_BUTTON: 'ui-request-button',\n REQUEST_QR_DISPLAY: 'ui-request-qr-display',\n REQUEST_QR_SCAN: 'ui-request-qr-scan',\n REQUEST_DEVICE_PERMISSION: 'ui-request-device-permission',\n REQUEST_SELECT_DEVICE: 'ui-request-select-device',\n REQUEST_DEVICE_CONNECT: 'ui-request-device-connect',\n REQUEST_PREEMPTION: 'ui-request-preemption',\n CLOSE_UI_WINDOW: 'ui-close',\n DEVICE_PROGRESS: 'ui-device_progress',\n FIRMWARE_PROGRESS: 'ui-firmware-progress',\n FIRMWARE_TIP: 'ui-firmware-tip',\n} as const;\n\nexport const UI_RESPONSE = {\n RECEIVE_PIN: 'receive-pin',\n RECEIVE_PASSPHRASE: 'receive-passphrase',\n RECEIVE_PASSPHRASE_ON_DEVICE: 'receive-passphrase-on-device',\n RECEIVE_QR_RESPONSE: 'receive-qr-response',\n RECEIVE_SELECT_DEVICE: 'receive-select-device',\n RECEIVE_DEVICE_CONNECT: 'receive-device-connect',\n RECEIVE_PREEMPTION: 'receive-preemption',\n CANCEL: 'cancel',\n} as const;\n\nexport type UiResponseEvent =\n | {\n type: typeof UI_RESPONSE.RECEIVE_PIN;\n payload: string;\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PASSPHRASE;\n payload: {\n value: string;\n passphraseOnDevice?: boolean;\n save?: boolean;\n };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PASSPHRASE_ON_DEVICE;\n payload?: undefined;\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_QR_RESPONSE;\n payload: QrResponseData;\n }\n | {\n // sdkConnectId echoes one of the DeviceInfo.connectId values emitted in\n // the REQUEST_SELECT_DEVICE event's `devices` list. Scope is the current\n // search session; may be ephemeral (e.g. Ledger USB DMK UUID).\n type: typeof UI_RESPONSE.RECEIVE_SELECT_DEVICE;\n payload: { sdkConnectId: string };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_DEVICE_CONNECT;\n payload: { confirmed: boolean };\n }\n | {\n type: typeof UI_RESPONSE.RECEIVE_PREEMPTION;\n payload: { decision: PreemptionDecision };\n }\n | {\n type: typeof UI_RESPONSE.CANCEL;\n payload?: undefined;\n };\n","/** Events generated by SDK internal detection (not from hardware directly). */\nexport const SDK = {\n DEVICE_STUCK: 'device-stuck',\n DEVICE_UNRESPONSIVE: 'device-unresponsive',\n DEVICE_RECOVERED: 'device-recovered',\n DEVICE_INTERACTION: 'device-interaction',\n} as const;\n","/**\n * Per-device serial job queue with preemption support and stuck recovery.\n * Ensures that only one operation runs at a time per device, with intelligent\n * handling of conflicting operations.\n *\n * The 'confirm' level uses the standard UI request/response flow:\n * emits REQUEST_PREEMPTION → waits for RECEIVE_PREEMPTION via UiRequestRegistry.\n */\n\nimport { UI_REQUEST } from '../events/ui-request';\n\nimport type { UiRequestRegistry } from './UiRequestRegistry';\n\nexport type Interruptibility = 'none' | 'safe' | 'confirm';\n\nexport type PreemptionDecision = 'cancel-current' | 'wait' | 'reject-new';\n\nexport interface JobOptions {\n interruptibility?: Interruptibility;\n label?: string;\n}\n\nexport interface ActiveJobInfo {\n label?: string;\n interruptibility: Interruptibility;\n startedAt: number;\n}\n\nexport interface PreemptionEvent {\n deviceId: string;\n currentJob: ActiveJobInfo;\n newJob: { label?: string; interruptibility: Interruptibility };\n}\n\ninterface ActiveJob {\n options: Required<Pick<JobOptions, 'interruptibility'>> & Pick<JobOptions, 'label'>;\n abortController: AbortController;\n startedAt: number;\n}\n\nexport interface DeviceJobQueueDeps {\n /** Emit a UI request event to the frontend. */\n emit: (event: string, data: unknown) => void;\n /** Registry for waiting on UI responses. */\n uiRegistry: UiRequestRegistry;\n}\n\nexport class DeviceJobQueue {\n private readonly _queues = new Map<string, Promise<unknown>>();\n\n private readonly _active = new Map<string, ActiveJob>();\n\n private readonly _deps: DeviceJobQueueDeps | null;\n\n constructor(deps?: DeviceJobQueueDeps) {\n this._deps = deps ?? null;\n }\n\n /**\n * Enqueue a job for a specific device.\n * If a job is already running for this device, behavior depends on interruptibility:\n * - 'none': new job queues silently (no preemption possible)\n * - 'safe': current job is auto-cancelled, new job runs immediately after\n * - 'confirm': emits REQUEST_PREEMPTION and waits for UI response\n */\n async enqueue<T>(\n deviceId: string,\n job: (signal: AbortSignal) => Promise<T>,\n options: JobOptions = {}\n ): Promise<T> {\n const interruptibility = options.interruptibility ?? 'confirm';\n const active = this._active.get(deviceId);\n\n if (active) {\n switch (active.options.interruptibility) {\n case 'none':\n // Cannot interrupt, just queue behind\n break;\n case 'safe':\n // Auto-cancel current safe operation\n active.abortController.abort(new Error('Preempted by new operation'));\n break;\n case 'confirm': {\n // If a preemption dialog is already pending, reject immediately.\n // The user is already deciding for a previous request — don't\n // overwrite the UiRequestRegistry slot (single-slot per type).\n if (this._deps?.uiRegistry.hasPending(UI_REQUEST.REQUEST_PREEMPTION)) {\n throw Object.assign(\n new Error(`Device busy: a preemption decision is already pending`),\n { hardwareErrorCode: 'DEVICE_BUSY' }\n );\n }\n const decision = await this._requestPreemptionDecision(deviceId, active, {\n label: options.label,\n interruptibility,\n });\n switch (decision) {\n case 'cancel-current':\n active.abortController.abort(new Error('Cancelled by user via preemption'));\n break;\n case 'reject-new':\n throw Object.assign(\n new Error(`Device busy: ${active.options.label ?? 'unknown operation'}`),\n { hardwareErrorCode: 'DEVICE_BUSY' }\n );\n case 'wait':\n break;\n default:\n // Unknown decision value from UI — fall back to safest option: wait.\n break;\n }\n break;\n }\n }\n }\n\n const ac = new AbortController();\n const prev = this._queues.get(deviceId) ?? Promise.resolve();\n\n const next = prev\n .catch(() => {})\n .then(async () => {\n this._active.set(deviceId, {\n options: { interruptibility, label: options.label },\n abortController: ac,\n startedAt: Date.now(),\n });\n try {\n return await job(ac.signal);\n } finally {\n this._active.delete(deviceId);\n }\n });\n\n const tail = next.catch(() => {});\n this._queues.set(deviceId, tail);\n tail.then(() => {\n if (this._queues.get(deviceId) === tail) {\n this._queues.delete(deviceId);\n }\n });\n return next;\n }\n\n /** Manually cancel the active job on a device. Returns false if job is non-interruptible. */\n cancelActive(deviceId: string): boolean {\n const active = this._active.get(deviceId);\n if (!active) return false;\n if (active.options.interruptibility === 'none') return false;\n active.abortController.abort(new Error('Manually cancelled'));\n return true;\n }\n\n /** Force cancel regardless of interruptibility. Use for device stuck recovery. */\n forceCancelActive(deviceId: string): boolean {\n const active = this._active.get(deviceId);\n if (!active) return false;\n active.abortController.abort(new Error('Force cancelled for recovery'));\n return true;\n }\n\n /** Get info about the currently active job for a device, or null if idle. */\n getActiveJob(deviceId: string): ActiveJobInfo | null {\n const active = this._active.get(deviceId);\n if (!active) return null;\n return {\n label: active.options.label,\n interruptibility: active.options.interruptibility,\n startedAt: active.startedAt,\n };\n }\n\n clear(): void {\n for (const active of this._active.values()) {\n active.abortController.abort(new Error('Queue cleared'));\n }\n this._active.clear();\n this._queues.clear();\n }\n\n /**\n * Request preemption decision via UI request/response flow.\n * Falls back to 'wait' if deps are not provided.\n */\n private async _requestPreemptionDecision(\n deviceId: string,\n active: ActiveJob,\n newJob: { label?: string; interruptibility: Interruptibility }\n ): Promise<PreemptionDecision> {\n if (!this._deps) return 'wait';\n\n const { emit, uiRegistry } = this._deps;\n\n emit(UI_REQUEST.REQUEST_PREEMPTION, {\n type: UI_REQUEST.REQUEST_PREEMPTION,\n payload: {\n deviceId,\n currentJob: {\n label: active.options.label,\n interruptibility: active.options.interruptibility,\n startedAt: active.startedAt,\n },\n newJob,\n },\n });\n\n const response = await uiRegistry.wait<{ decision: PreemptionDecision }>(\n UI_REQUEST.REQUEST_PREEMPTION\n );\n\n return response?.decision ?? 'wait';\n }\n}\n","import type { ConnectionType, DeviceCapabilities, DeviceInfo, VendorType } from './device';\nimport type { UiResponseEvent } from '../events/ui-request';\n\n// =====================================================================\n// Connector types — transport-level abstraction for device communication\n// =====================================================================\n\n/**\n * Minimal device info returned during discovery (searchDevices).\n * At scan time, full DeviceInfo fields like firmwareVersion are not yet available.\n */\nexport interface ConnectorDevice {\n connectId: string;\n deviceId: string;\n name: string;\n model?: string;\n\n /** Device capabilities — available from scan time */\n capabilities?: DeviceCapabilities;\n}\n\nexport interface ConnectorSession {\n sessionId: string;\n deviceInfo: DeviceInfo;\n}\n\nexport type ConnectorEventType = 'device-connect' | 'device-disconnect' | 'ui-request' | 'ui-event';\n\n/**\n * Interaction event types emitted via 'ui-event'.\n * These map to user-facing prompts (confirm on device, open app, etc.).\n */\nexport enum EConnectorInteraction {\n /** Device requires user to open a specific app */\n ConfirmOpenApp = 'confirm-open-app',\n /** Device requires user to unlock */\n UnlockDevice = 'unlock-device',\n /** Device needs user to confirm on device (sign, verify, etc.) */\n ConfirmOnDevice = 'confirm-on-device',\n /** Previous interaction completed — clear UI prompt */\n InteractionComplete = 'interaction-complete',\n}\n\nexport type ConnectorUiEvent =\n | { type: EConnectorInteraction.ConfirmOpenApp; payload: { sessionId: string } }\n | { type: EConnectorInteraction.UnlockDevice; payload: { sessionId: string } }\n | { type: EConnectorInteraction.ConfirmOnDevice; payload: { sessionId: string } }\n | { type: EConnectorInteraction.InteractionComplete; payload: { sessionId: string } };\n\nexport interface ConnectorEventMap {\n 'device-connect': { device: ConnectorDevice };\n 'device-disconnect': { connectId: string };\n 'ui-request': { type: string; payload?: unknown };\n 'ui-event': ConnectorUiEvent;\n}\n\nexport interface IConnector {\n /** Physical connection type this connector uses. Fixed at construction. */\n readonly connectionType: ConnectionType;\n\n searchDevices(): Promise<ConnectorDevice[]>;\n connect(deviceId?: string): Promise<ConnectorSession>;\n disconnect(sessionId: string): Promise<void>;\n call(sessionId: string, method: string, params: unknown): Promise<unknown>;\n cancel(sessionId: string): Promise<void>;\n\n /** Send a UI response (e.g. PIN, passphrase) to the device. */\n uiResponse(response: UiResponseEvent): void;\n\n on<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;\n off<K extends ConnectorEventType>(event: K, handler: (data: ConnectorEventMap[K]) => void): void;\n\n reset(): void;\n}\n\n// =====================================================================\n// Hardware bridge — generic forwarding interface for cross-boundary IConnector\n//\n// The same shape works across any process / context boundary that needs to\n// expose a multi-vendor backend behind a single-vendor IConnector facade:\n// - Electron (renderer ↔ preload/main via contextBridge)\n// - Browser Extension (popup/content ↔ background/offscreen via chrome.runtime)\n// - React Native (JS ↔ native module via NativeModules)\n// - Web Worker / iframe (postMessage)\n//\n// Each method takes a `vendor` discriminator so a single bridge implementation\n// can multiplex across vendors (ledger, trezor, ...).\n// =====================================================================\n\nexport interface IHardwareBridge {\n searchDevices(params: { vendor: VendorType }): Promise<ConnectorDevice[]>;\n connect(params: { vendor: VendorType; deviceId?: string }): Promise<ConnectorSession>;\n disconnect(params: { vendor: VendorType; sessionId: string }): Promise<void>;\n call(params: {\n vendor: VendorType;\n sessionId: string;\n method: string;\n callParams: unknown;\n }): Promise<unknown>;\n cancel(params: { vendor: VendorType; sessionId: string }): Promise<void>;\n uiResponse(params: { vendor: VendorType; response: UiResponseEvent }): void;\n reset(params: { vendor: VendorType }): void;\n\n /** Register an event handler for connector events forwarded across the bridge. */\n onEvent(\n params: { vendor: VendorType },\n handler: (event: { type: ConnectorEventType; data: unknown }) => void\n ): void;\n\n /** Unregister a previously registered event handler. */\n offEvent(\n params: { vendor: VendorType },\n handler: (event: { type: ConnectorEventType; data: unknown }) => void\n ): void;\n}\n\n/**\n * Adapt an IHardwareBridge (multi-vendor backend) into a single-vendor IConnector.\n * Every IConnector method becomes a transparent forward to bridge.<method>({ vendor, ... }).\n * Events are forwarded via bridge.onEvent / offEvent.\n *\n * Use this anywhere the actual hardware lives behind a process / context boundary\n * (Electron main, extension background, native module, worker, iframe).\n */\nexport function createBridgedConnector(\n vendor: VendorType,\n connectionType: ConnectionType,\n bridge: IHardwareBridge\n): IConnector {\n // Map from typed IConnector handlers to the bridge handler so we can\n // unregister them correctly via off().\n const handlerMap = new Map<\n (data: ConnectorEventMap[ConnectorEventType]) => void,\n (event: { type: ConnectorEventType; data: unknown }) => void\n >();\n\n return {\n connectionType,\n searchDevices: () => bridge.searchDevices({ vendor }),\n connect: deviceId => bridge.connect({ vendor, deviceId }),\n disconnect: sessionId => bridge.disconnect({ vendor, sessionId }),\n call: (sessionId, method, callParams) => bridge.call({ vendor, sessionId, method, callParams }),\n cancel: sessionId => bridge.cancel({ vendor, sessionId }),\n uiResponse: response => bridge.uiResponse({ vendor, response }),\n on: (event, handler) => {\n const bridgeHandler = (e: { type: ConnectorEventType; data: unknown }) => {\n if (e.type === event) {\n handler(e.data as ConnectorEventMap[typeof event]);\n }\n };\n handlerMap.set(\n handler as (data: ConnectorEventMap[ConnectorEventType]) => void,\n bridgeHandler\n );\n bridge.onEvent({ vendor }, bridgeHandler);\n },\n off: (_event, handler) => {\n const bridgeHandler = handlerMap.get(\n handler as (data: ConnectorEventMap[ConnectorEventType]) => void\n );\n if (bridgeHandler) {\n bridge.offEvent({ vendor }, bridgeHandler);\n handlerMap.delete(handler as (data: ConnectorEventMap[ConnectorEventType]) => void);\n }\n },\n reset: () => bridge.reset({ vendor }),\n };\n}\n","/**\n * Minimal typed event emitter using Map<string, Set<listener>>.\n * Each adapter uses this for device events (connect, disconnect, pin, etc.).\n *\n * TMap is a record mapping event name strings to their payload types.\n * Example:\n * type MyEvents = { 'connect': { id: string }; 'disconnect': { id: string } };\n * const emitter = new TypedEventEmitter<MyEvents>();\n * emitter.on('connect', (data) => { data.id }); // data is { id: string }\n *\n * For backward compatibility, TMap defaults to Record<string, any> so that\n * existing code using `new TypedEventEmitter<SomeUnionType>()` still compiles.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport class TypedEventEmitter<TMap extends Record<string, any> = Record<string, any>> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private readonly _listeners = new Map<string, Set<(event: any) => void>>();\n\n on<K extends keyof TMap & string>(event: K, listener: (event: TMap[K]) => void): void;\n\n on(event: string, listener: (event: any) => void): void;\n\n on(event: string, listener: (event: any) => void): void {\n let set = this._listeners.get(event);\n if (!set) {\n set = new Set();\n this._listeners.set(event, set);\n }\n set.add(listener);\n }\n\n off<K extends keyof TMap & string>(event: K, listener: (event: TMap[K]) => void): void;\n\n off(event: string, listener: (event: any) => void): void;\n\n off(event: string, listener: (event: any) => void): void {\n const set = this._listeners.get(event);\n if (set) {\n set.delete(listener);\n if (set.size === 0) this._listeners.delete(event);\n }\n }\n\n emit<K extends keyof TMap & string>(event: K, data: TMap[K]): void;\n\n emit(event: string, data: unknown): void;\n\n emit(event: string, data: unknown): void {\n const set = this._listeners.get(event);\n if (set) {\n for (const listener of set) listener(data);\n }\n }\n\n removeAllListeners(): void {\n this._listeners.clear();\n }\n}\n","import { UI_REQUEST, UI_RESPONSE } from '../events/ui-request';\n\n/** 10 min — every UI request is human-in-the-loop; bias toward \"wait long\". */\nexport const UI_REQUEST_DEFAULT_TIMEOUT_MS = 600_000;\n\nconst RESPONSE_TO_REQUEST: Record<string, string> = {\n [UI_RESPONSE.RECEIVE_PIN]: UI_REQUEST.REQUEST_PIN,\n [UI_RESPONSE.RECEIVE_PASSPHRASE]: UI_REQUEST.REQUEST_PASSPHRASE,\n [UI_RESPONSE.RECEIVE_PASSPHRASE_ON_DEVICE]: UI_REQUEST.REQUEST_PASSPHRASE_ON_DEVICE,\n [UI_RESPONSE.RECEIVE_SELECT_DEVICE]: UI_REQUEST.REQUEST_SELECT_DEVICE,\n [UI_RESPONSE.RECEIVE_DEVICE_CONNECT]: UI_REQUEST.REQUEST_DEVICE_CONNECT,\n [UI_RESPONSE.RECEIVE_PREEMPTION]: UI_REQUEST.REQUEST_PREEMPTION,\n // RECEIVE_QR_RESPONSE maps to QR_DISPLAY or QR_SCAN — resolved dynamically below.\n};\n\nexport const UI_REQUEST_PREEMPTED_TAG = 'UiRequestPreempted';\nexport const UI_REQUEST_CANCELLED_TAG = 'UiRequestCancelled';\nexport const UI_REQUEST_TIMEOUT_TAG = 'UiRequestTimeout';\n\ntype PendingEntry = {\n resolve: (payload: unknown) => void;\n reject: (error: Error) => void;\n timer: ReturnType<typeof setTimeout>;\n};\n\n/**\n * Per-type single-slot registry for adapter-level UI requests. A new `wait`\n * of the same type preempts (rejects) the prior one. Unknown response types\n * are dropped silently so the public `uiResponse` entry never throws.\n */\nexport class UiRequestRegistry {\n private pending = new Map<string, PendingEntry>();\n\n wait<T = unknown>(requestType: string, options?: { timeoutMs?: number }): Promise<T> {\n const existing = this.pending.get(requestType);\n if (existing) {\n clearTimeout(existing.timer);\n this.pending.delete(requestType);\n existing.reject(\n Object.assign(new Error(`UI request '${requestType}' was superseded by a new request`), {\n _tag: UI_REQUEST_PREEMPTED_TAG,\n })\n );\n }\n\n const timeoutMs = options?.timeoutMs ?? UI_REQUEST_DEFAULT_TIMEOUT_MS;\n\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => {\n if (this.pending.get(requestType)?.timer === timer) {\n this.pending.delete(requestType);\n reject(\n Object.assign(new Error(`UI request '${requestType}' timed out after ${timeoutMs}ms`), {\n _tag: UI_REQUEST_TIMEOUT_TAG,\n })\n );\n }\n }, timeoutMs);\n\n this.pending.set(requestType, {\n resolve: resolve as (payload: unknown) => void,\n reject,\n timer,\n });\n });\n }\n\n resolve(responseType: string, payload: unknown): void {\n let requestType = RESPONSE_TO_REQUEST[responseType];\n if (responseType === UI_RESPONSE.RECEIVE_QR_RESPONSE) {\n if (this.pending.has(UI_REQUEST.REQUEST_QR_DISPLAY)) {\n requestType = UI_REQUEST.REQUEST_QR_DISPLAY;\n } else if (this.pending.has(UI_REQUEST.REQUEST_QR_SCAN)) {\n requestType = UI_REQUEST.REQUEST_QR_SCAN;\n }\n }\n if (!requestType) return;\n\n const entry = this.pending.get(requestType);\n if (!entry) return;\n\n clearTimeout(entry.timer);\n this.pending.delete(requestType);\n entry.resolve(payload);\n }\n\n cancel(requestType?: string): void {\n if (requestType) {\n const entry = this.pending.get(requestType);\n if (!entry) return;\n clearTimeout(entry.timer);\n this.pending.delete(requestType);\n entry.reject(\n Object.assign(new Error(`UI request '${requestType}' was cancelled`), {\n _tag: UI_REQUEST_CANCELLED_TAG,\n })\n );\n return;\n }\n\n for (const [type, entry] of this.pending) {\n clearTimeout(entry.timer);\n entry.reject(\n Object.assign(new Error(`UI request '${type}' was cancelled`), {\n _tag: UI_REQUEST_CANCELLED_TAG,\n })\n );\n }\n this.pending.clear();\n }\n\n reset(): void {\n this.cancel();\n }\n\n hasPending(requestType: string): boolean {\n return this.pending.has(requestType);\n }\n}\n","/**\n * Compare two semver strings (e.g. \"2.1.0\" vs \"2.3.1\").\n * Returns -1 if a < b, 0 if equal, 1 if a > b.\n */\nexport function compareSemver(a: string, b: string): number {\n const pa = a.split('.').map(Number);\n const pb = b.split('.').map(Number);\n for (let i = 0; i < 3; i++) {\n const va = pa[i] ?? 0;\n const vb = pb[i] ?? 0;\n if (va < vb) return -1;\n if (va > vb) return 1;\n }\n return 0;\n}\n","import { bytesToHex as nobleBytesToHex, hexToBytes as nobleHexToBytes } from '@noble/hashes/utils';\n\n/** Ensure hex string has 0x prefix */\nexport function ensure0x(hex: string): string {\n return hex.startsWith('0x') ? hex : `0x${hex}`;\n}\n\n/** Strip 0x prefix from hex string */\nexport function stripHex(hex: string): string {\n return hex.startsWith('0x') ? hex.slice(2) : hex;\n}\n\n/** Pad hex to 64 chars (32 bytes) with 0x prefix */\nexport function padHex64(hex: string): string {\n return `0x${stripHex(hex).padStart(64, '0')}`;\n}\n\n/**\n * Convert a hex string (with or without 0x prefix) to a Uint8Array.\n * Throws on invalid hex (non-hex characters, odd length).\n */\nexport function hexToBytes(hex: string): Uint8Array {\n return nobleHexToBytes(stripHex(hex));\n}\n\n/** Convert a Uint8Array to a hex string (no 0x prefix). */\nexport function bytesToHex(bytes: Uint8Array): string {\n return nobleBytesToHex(bytes);\n}\n","import { HardwareErrorCode } from '../types/errors';\n\n/**\n * Enrich a hardware error message with actionable recovery hints.\n * Shared across adapters (Ledger, Trezor, etc.).\n */\nexport function enrichErrorMessage(code: HardwareErrorCode, originalMessage: string): string {\n switch (code) {\n case HardwareErrorCode.PinInvalid:\n return `${originalMessage}. Please re-enter your PIN.`;\n case HardwareErrorCode.PinCancelled:\n return `${originalMessage}. PIN entry was cancelled.`;\n case HardwareErrorCode.DeviceBusy:\n return `${originalMessage}. The device is in use by another application. Close other wallet apps and try again.`;\n case HardwareErrorCode.DeviceDisconnected:\n return `${originalMessage}. Please reconnect the device and try again.`;\n case HardwareErrorCode.DeviceLocked:\n return `${originalMessage}. Please unlock your device and try again.`;\n case HardwareErrorCode.UserRejected:\n return `${originalMessage}. The request was rejected on the device.`;\n case HardwareErrorCode.WrongApp:\n return `${originalMessage}. Please open the correct app on your device.`;\n case HardwareErrorCode.AppNotOpen:\n return `${originalMessage}. The required app is not installed on the device.`;\n case HardwareErrorCode.TransportNotAvailable:\n return `${originalMessage}. Ensure the device bridge/transport is available and running.`;\n case HardwareErrorCode.FirmwareTooOld:\n return `${originalMessage}. Please update your device firmware.`;\n case HardwareErrorCode.DeviceNotInitialized:\n return `${originalMessage}. The device may need to be set up first.`;\n case HardwareErrorCode.OperationTimeout:\n return `${originalMessage}. The operation timed out. Please try again.`;\n case HardwareErrorCode.EvmBlindSigningRequired:\n return 'Ledger: Blind signing not enabled in Ethereum app.';\n case HardwareErrorCode.EvmClearSignPluginMissing:\n return 'Ledger: Required plugin not installed (install via Ledger Live).';\n case HardwareErrorCode.EvmDataTooLarge:\n return 'Ledger: Transaction data too large for device memory.';\n case HardwareErrorCode.EvmTxTypeNotSupported:\n return 'Ledger: Transaction type not supported by current Ethereum app.';\n case HardwareErrorCode.AppTooOld:\n return 'Ledger: App out of date (update via Ledger Live).';\n default:\n return originalMessage;\n }\n}\n","import type { Response } from '../types/response';\n\n/**\n * Generic batch call with progress reporting.\n * If any single call fails, returns the failure immediately.\n */\nexport async function batchCall<TParam, TResult>(\n params: TParam[],\n callFn: (p: TParam) => Promise<Response<TResult>>,\n onProgress?: (progress: { index: number; total: number }) => void\n): Promise<Response<TResult[]>> {\n const results: TResult[] = [];\n for (let i = 0; i < params.length; i++) {\n const result = await callFn(params[i]);\n if (!result.success) {\n return result;\n }\n results.push(result.payload);\n onProgress?.({ index: i, total: params.length });\n }\n return { success: true, payload: results };\n}\n"],"mappings":"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","names":["HardwareErrorCode","EConnectorInteraction","bytesToHex"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/hwk-adapter-core",
|
|
3
|
-
"version": "1.1.26-alpha.
|
|
3
|
+
"version": "1.1.26-alpha.4",
|
|
4
4
|
"description": "Shared types and utilities for OneKey hardware wallet kit",
|
|
5
5
|
"author": "OneKey",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,14 +26,14 @@
|
|
|
26
26
|
"build": "tsup",
|
|
27
27
|
"dev": "tsup --watch",
|
|
28
28
|
"clean": "rimraf dist",
|
|
29
|
-
"test": "
|
|
29
|
+
"test": "jest"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|
|
36
|
-
"url": "https://github.com/OneKeyHQ/hardware-
|
|
36
|
+
"url": "git+https://github.com/OneKeyHQ/hardware-js-sdk.git",
|
|
37
37
|
"directory": "packages/hwk-adapter-core"
|
|
38
38
|
},
|
|
39
39
|
"keywords": [
|
|
@@ -50,5 +50,5 @@
|
|
|
50
50
|
"tsup": "^8.0.0",
|
|
51
51
|
"typescript": "^5.4.0"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "c03e3a11a9871dc22b711589ad4962399381c92e"
|
|
54
54
|
}
|