@bytezhang/hardware-trezor-adapter 0.0.1
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 +193 -0
- package/dist/index.d.ts +193 -0
- package/dist/index.js +697 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +676 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +30 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
TrezorAdapter: () => TrezorAdapter,
|
|
24
|
+
TrezorProxyClient: () => TrezorProxyClient
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/TrezorAdapter.ts
|
|
29
|
+
var import_hardware_wallet_core = require("@bytezhang/hardware-wallet-core");
|
|
30
|
+
var TrezorAdapter = class {
|
|
31
|
+
constructor(connector) {
|
|
32
|
+
this.vendor = "trezor";
|
|
33
|
+
this.emitter = new import_hardware_wallet_core.TypedEventEmitter();
|
|
34
|
+
this._uiHandler = null;
|
|
35
|
+
// Device cache: tracks discovered devices from connector events
|
|
36
|
+
this._discoveredDevices = /* @__PURE__ */ new Map();
|
|
37
|
+
// Session tracking: maps connectId -> sessionId
|
|
38
|
+
this._sessions = /* @__PURE__ */ new Map();
|
|
39
|
+
// ─── Event translation ────────────────────────────────────
|
|
40
|
+
this.deviceConnectHandler = (data) => {
|
|
41
|
+
const deviceInfo = this.connectorDeviceToDeviceInfo(data.device);
|
|
42
|
+
this._discoveredDevices.set(deviceInfo.connectId, deviceInfo);
|
|
43
|
+
this.emitter.emit(import_hardware_wallet_core.DEVICE.CONNECT, {
|
|
44
|
+
type: import_hardware_wallet_core.DEVICE.CONNECT,
|
|
45
|
+
payload: deviceInfo
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
this.deviceDisconnectHandler = (data) => {
|
|
49
|
+
this._discoveredDevices.delete(data.connectId);
|
|
50
|
+
this.emitter.emit(import_hardware_wallet_core.DEVICE.DISCONNECT, {
|
|
51
|
+
type: import_hardware_wallet_core.DEVICE.DISCONNECT,
|
|
52
|
+
payload: { connectId: data.connectId }
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
this.uiRequestHandler = (data) => {
|
|
56
|
+
this.handleUiEvent(data);
|
|
57
|
+
};
|
|
58
|
+
this.uiEventHandler = (data) => {
|
|
59
|
+
this.handleUiEvent(data);
|
|
60
|
+
};
|
|
61
|
+
this.connector = connector;
|
|
62
|
+
this.registerEventListeners();
|
|
63
|
+
}
|
|
64
|
+
// ─── Transport ──────────────────────────────────────────
|
|
65
|
+
// Transport is decided at connector creation time. These methods
|
|
66
|
+
// satisfy the IHardwareWallet interface with sensible defaults.
|
|
67
|
+
get activeTransport() {
|
|
68
|
+
return "usb";
|
|
69
|
+
}
|
|
70
|
+
getAvailableTransports() {
|
|
71
|
+
return ["usb"];
|
|
72
|
+
}
|
|
73
|
+
async switchTransport(_type) {
|
|
74
|
+
}
|
|
75
|
+
// ─── UI handler ────────────────────────────────────────────
|
|
76
|
+
setUiHandler(handler) {
|
|
77
|
+
this._uiHandler = handler;
|
|
78
|
+
}
|
|
79
|
+
// ─── Lifecycle ────────────────────────────────────────────
|
|
80
|
+
async init(_config) {
|
|
81
|
+
}
|
|
82
|
+
async dispose() {
|
|
83
|
+
this.unregisterEventListeners();
|
|
84
|
+
this.connector.reset();
|
|
85
|
+
this._uiHandler = null;
|
|
86
|
+
this._discoveredDevices.clear();
|
|
87
|
+
this._sessions.clear();
|
|
88
|
+
this.emitter.removeAllListeners();
|
|
89
|
+
}
|
|
90
|
+
// ─── Device management ────────────────────────────────────
|
|
91
|
+
async searchDevices() {
|
|
92
|
+
await this._ensureDevicePermission();
|
|
93
|
+
const devices = await this.connector.searchDevices();
|
|
94
|
+
for (const d of devices) {
|
|
95
|
+
if (d.connectId && !this._discoveredDevices.has(d.connectId)) {
|
|
96
|
+
this._discoveredDevices.set(d.connectId, this.connectorDeviceToDeviceInfo(d));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (this._discoveredDevices.size === 0) {
|
|
100
|
+
await this._ensureDevicePermission();
|
|
101
|
+
}
|
|
102
|
+
return Array.from(this._discoveredDevices.values());
|
|
103
|
+
}
|
|
104
|
+
async connectDevice(connectId) {
|
|
105
|
+
try {
|
|
106
|
+
const session = await this.connector.connect(connectId);
|
|
107
|
+
this._sessions.set(connectId, session.sessionId);
|
|
108
|
+
if (session.deviceInfo) {
|
|
109
|
+
this._discoveredDevices.set(connectId, session.deviceInfo);
|
|
110
|
+
}
|
|
111
|
+
return (0, import_hardware_wallet_core.success)(connectId);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
return this.errorToFailure(err);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async disconnectDevice(connectId) {
|
|
117
|
+
const sessionId = this._sessions.get(connectId);
|
|
118
|
+
if (sessionId) {
|
|
119
|
+
await this.connector.disconnect(sessionId);
|
|
120
|
+
this._sessions.delete(connectId);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async getDeviceInfo(connectId, deviceId) {
|
|
124
|
+
await this._ensureDevicePermission(connectId, deviceId);
|
|
125
|
+
const cached = this._discoveredDevices.get(connectId) ?? Array.from(this._discoveredDevices.values()).find(
|
|
126
|
+
(d) => d.deviceId === deviceId
|
|
127
|
+
);
|
|
128
|
+
if (cached) {
|
|
129
|
+
return (0, import_hardware_wallet_core.success)(cached);
|
|
130
|
+
}
|
|
131
|
+
return (0, import_hardware_wallet_core.failure)(
|
|
132
|
+
import_hardware_wallet_core.HardwareErrorCode.DeviceNotFound,
|
|
133
|
+
"Device not found in cache. Call searchDevices() or wait for a device-connected event first."
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
getSupportedChains() {
|
|
137
|
+
return ["evm", "btc", "sol"];
|
|
138
|
+
}
|
|
139
|
+
on(event, listener) {
|
|
140
|
+
this.emitter.on(event, listener);
|
|
141
|
+
}
|
|
142
|
+
off(event, listener) {
|
|
143
|
+
this.emitter.off(event, listener);
|
|
144
|
+
}
|
|
145
|
+
cancel(connectId) {
|
|
146
|
+
const sessionId = this._sessions.get(connectId) ?? connectId;
|
|
147
|
+
void this.connector.cancel(sessionId);
|
|
148
|
+
}
|
|
149
|
+
// ─── EVM methods ──────────────────────────────────────────
|
|
150
|
+
async evmGetAddress(connectId, _deviceId, params) {
|
|
151
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
152
|
+
try {
|
|
153
|
+
const result = await this.connectorCall(connectId, "evmGetAddress", {
|
|
154
|
+
path: params.path,
|
|
155
|
+
showOnDevice: params.showOnDevice,
|
|
156
|
+
chainId: params.chainId
|
|
157
|
+
});
|
|
158
|
+
return (0, import_hardware_wallet_core.success)({
|
|
159
|
+
address: result.address,
|
|
160
|
+
path: params.path
|
|
161
|
+
});
|
|
162
|
+
} catch (err) {
|
|
163
|
+
return this.errorToFailure(err);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async evmGetAddresses(connectId, deviceId, params, onProgress) {
|
|
167
|
+
return this.batchCall(
|
|
168
|
+
params,
|
|
169
|
+
(p) => this.evmGetAddress(connectId, deviceId, p),
|
|
170
|
+
onProgress
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
async evmGetPublicKey(connectId, _deviceId, params) {
|
|
174
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
175
|
+
try {
|
|
176
|
+
const result = await this.connectorCall(connectId, "evmGetPublicKey", {
|
|
177
|
+
path: params.path,
|
|
178
|
+
showOnDevice: params.showOnDevice
|
|
179
|
+
});
|
|
180
|
+
return (0, import_hardware_wallet_core.success)({
|
|
181
|
+
publicKey: result.publicKey,
|
|
182
|
+
path: params.path
|
|
183
|
+
});
|
|
184
|
+
} catch (err) {
|
|
185
|
+
return this.errorToFailure(err);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async evmSignTransaction(connectId, _deviceId, params) {
|
|
189
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
190
|
+
try {
|
|
191
|
+
const result = await this.connectorCall(connectId, "evmSignTransaction", {
|
|
192
|
+
path: params.path,
|
|
193
|
+
transaction: {
|
|
194
|
+
to: params.to,
|
|
195
|
+
value: params.value,
|
|
196
|
+
chainId: params.chainId,
|
|
197
|
+
nonce: params.nonce,
|
|
198
|
+
gasLimit: params.gasLimit,
|
|
199
|
+
gasPrice: params.gasPrice,
|
|
200
|
+
maxFeePerGas: params.maxFeePerGas,
|
|
201
|
+
maxPriorityFeePerGas: params.maxPriorityFeePerGas,
|
|
202
|
+
accessList: params.accessList,
|
|
203
|
+
data: params.data
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
return (0, import_hardware_wallet_core.success)({
|
|
207
|
+
v: this.ensure0x(result.v),
|
|
208
|
+
r: this.padHex64(result.r),
|
|
209
|
+
s: this.padHex64(result.s),
|
|
210
|
+
serializedTx: result.serializedTx
|
|
211
|
+
});
|
|
212
|
+
} catch (err) {
|
|
213
|
+
return this.errorToFailure(err);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async evmSignMessage(connectId, _deviceId, params) {
|
|
217
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
218
|
+
try {
|
|
219
|
+
const result = await this.connectorCall(connectId, "evmSignMessage", {
|
|
220
|
+
path: params.path,
|
|
221
|
+
message: params.message,
|
|
222
|
+
hex: params.hex
|
|
223
|
+
});
|
|
224
|
+
return (0, import_hardware_wallet_core.success)({
|
|
225
|
+
signature: this.ensure0x(result.signature),
|
|
226
|
+
address: result.address
|
|
227
|
+
});
|
|
228
|
+
} catch (err) {
|
|
229
|
+
return this.errorToFailure(err);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async evmSignTypedData(connectId, _deviceId, params) {
|
|
233
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
234
|
+
try {
|
|
235
|
+
const callParams = {
|
|
236
|
+
path: params.path
|
|
237
|
+
};
|
|
238
|
+
if (params.mode !== "hash") {
|
|
239
|
+
callParams["mode"] = params.mode ?? "full";
|
|
240
|
+
callParams["data"] = params.data;
|
|
241
|
+
callParams["metamaskV4Compat"] = params.metamaskV4Compat ?? true;
|
|
242
|
+
} else {
|
|
243
|
+
callParams["mode"] = "hash";
|
|
244
|
+
callParams["domainSeparatorHash"] = params.domainSeparatorHash;
|
|
245
|
+
callParams["messageHash"] = params.messageHash;
|
|
246
|
+
}
|
|
247
|
+
const result = await this.connectorCall(connectId, "evmSignTypedData", callParams);
|
|
248
|
+
return (0, import_hardware_wallet_core.success)({
|
|
249
|
+
signature: this.ensure0x(result.signature),
|
|
250
|
+
address: result.address
|
|
251
|
+
});
|
|
252
|
+
} catch (err) {
|
|
253
|
+
return this.errorToFailure(err);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
// ─── BTC methods ──────────────────────────────────────────
|
|
257
|
+
async btcGetAddress(connectId, _deviceId, params) {
|
|
258
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
259
|
+
try {
|
|
260
|
+
const result = await this.connectorCall(connectId, "btcGetAddress", {
|
|
261
|
+
path: params.path,
|
|
262
|
+
coin: params.coin,
|
|
263
|
+
showOnDevice: params.showOnDevice,
|
|
264
|
+
scriptType: params.scriptType
|
|
265
|
+
});
|
|
266
|
+
return (0, import_hardware_wallet_core.success)({
|
|
267
|
+
address: result.address,
|
|
268
|
+
path: params.path
|
|
269
|
+
});
|
|
270
|
+
} catch (err) {
|
|
271
|
+
return this.errorToFailure(err);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async btcGetAddresses(connectId, deviceId, params, onProgress) {
|
|
275
|
+
return this.batchCall(
|
|
276
|
+
params,
|
|
277
|
+
(p) => this.btcGetAddress(connectId, deviceId, p),
|
|
278
|
+
onProgress
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
async btcGetPublicKey(connectId, _deviceId, params) {
|
|
282
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
283
|
+
try {
|
|
284
|
+
const result = await this.connectorCall(connectId, "btcGetPublicKey", {
|
|
285
|
+
path: params.path,
|
|
286
|
+
coin: params.coin,
|
|
287
|
+
showOnDevice: params.showOnDevice
|
|
288
|
+
});
|
|
289
|
+
return (0, import_hardware_wallet_core.success)({
|
|
290
|
+
xpub: result.xpub,
|
|
291
|
+
publicKey: result.publicKey,
|
|
292
|
+
fingerprint: result.fingerprint,
|
|
293
|
+
chainCode: result.chainCode,
|
|
294
|
+
path: params.path,
|
|
295
|
+
depth: result.depth
|
|
296
|
+
});
|
|
297
|
+
} catch (err) {
|
|
298
|
+
return this.errorToFailure(err);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async btcSignTransaction(connectId, _deviceId, params) {
|
|
302
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
303
|
+
try {
|
|
304
|
+
const result = await this.connectorCall(connectId, "btcSignTransaction", {
|
|
305
|
+
inputs: params.inputs ?? [],
|
|
306
|
+
outputs: params.outputs ?? [],
|
|
307
|
+
refTxs: params.refTxs,
|
|
308
|
+
coin: params.coin,
|
|
309
|
+
locktime: params.locktime,
|
|
310
|
+
version: params.version
|
|
311
|
+
});
|
|
312
|
+
return (0, import_hardware_wallet_core.success)({
|
|
313
|
+
signatures: result.signatures,
|
|
314
|
+
serializedTx: result.serializedTx,
|
|
315
|
+
txid: result.txid
|
|
316
|
+
});
|
|
317
|
+
} catch (err) {
|
|
318
|
+
return this.errorToFailure(err);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
async btcSignMessage(connectId, _deviceId, params) {
|
|
322
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
323
|
+
try {
|
|
324
|
+
const result = await this.connectorCall(connectId, "btcSignMessage", {
|
|
325
|
+
path: params.path,
|
|
326
|
+
message: params.message,
|
|
327
|
+
coin: params.coin
|
|
328
|
+
});
|
|
329
|
+
return (0, import_hardware_wallet_core.success)({
|
|
330
|
+
signature: result.signature,
|
|
331
|
+
address: result.address
|
|
332
|
+
});
|
|
333
|
+
} catch (err) {
|
|
334
|
+
return this.errorToFailure(err);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
async btcGetMasterFingerprint(connectId, _deviceId) {
|
|
338
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
339
|
+
try {
|
|
340
|
+
const result = await this.connectorCall(connectId, "btcGetPublicKey", {
|
|
341
|
+
path: "m/0'"
|
|
342
|
+
});
|
|
343
|
+
const fp = result.fingerprint >>> 0;
|
|
344
|
+
const hex = fp.toString(16).padStart(8, "0");
|
|
345
|
+
return (0, import_hardware_wallet_core.success)({ masterFingerprint: hex });
|
|
346
|
+
} catch (err) {
|
|
347
|
+
return this.errorToFailure(err);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
// ─── Solana methods ───────────────────────────────────────
|
|
351
|
+
async solGetAddress(connectId, _deviceId, params) {
|
|
352
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
353
|
+
try {
|
|
354
|
+
const result = await this.connectorCall(connectId, "solGetAddress", {
|
|
355
|
+
path: params.path,
|
|
356
|
+
showOnDevice: params.showOnDevice
|
|
357
|
+
});
|
|
358
|
+
return (0, import_hardware_wallet_core.success)({
|
|
359
|
+
address: result.address,
|
|
360
|
+
path: params.path
|
|
361
|
+
});
|
|
362
|
+
} catch (err) {
|
|
363
|
+
return this.errorToFailure(err);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
async solGetAddresses(connectId, deviceId, params, onProgress) {
|
|
367
|
+
return this.batchCall(
|
|
368
|
+
params,
|
|
369
|
+
(p) => this.solGetAddress(connectId, deviceId, p),
|
|
370
|
+
onProgress
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
async solGetPublicKey(connectId, _deviceId, params) {
|
|
374
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
375
|
+
try {
|
|
376
|
+
const result = await this.connectorCall(connectId, "solGetAddress", {
|
|
377
|
+
path: params.path,
|
|
378
|
+
showOnDevice: params.showOnDevice
|
|
379
|
+
});
|
|
380
|
+
return (0, import_hardware_wallet_core.success)({
|
|
381
|
+
publicKey: result.address,
|
|
382
|
+
path: params.path
|
|
383
|
+
});
|
|
384
|
+
} catch (err) {
|
|
385
|
+
return this.errorToFailure(err);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
async solSignTransaction(connectId, _deviceId, params) {
|
|
389
|
+
await this._ensureDevicePermission(connectId, _deviceId);
|
|
390
|
+
try {
|
|
391
|
+
const result = await this.connectorCall(connectId, "solSignTransaction", {
|
|
392
|
+
path: params.path,
|
|
393
|
+
serializedTx: params.serializedTx,
|
|
394
|
+
additionalInfo: params.additionalInfo
|
|
395
|
+
});
|
|
396
|
+
return (0, import_hardware_wallet_core.success)({
|
|
397
|
+
signature: result.signature
|
|
398
|
+
});
|
|
399
|
+
} catch (err) {
|
|
400
|
+
return this.errorToFailure(err);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
async solSignMessage(_connectId, _deviceId, _params) {
|
|
404
|
+
return (0, import_hardware_wallet_core.failure)(
|
|
405
|
+
import_hardware_wallet_core.HardwareErrorCode.MethodNotSupported,
|
|
406
|
+
"Solana signMessage is not supported by Trezor Connect"
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
// ─── Private helpers ──────────────────────────────────────
|
|
410
|
+
/**
|
|
411
|
+
* Call the connector with session resolution.
|
|
412
|
+
* Looks up sessionId from connectId, falls back to connectId itself.
|
|
413
|
+
*/
|
|
414
|
+
async connectorCall(connectId, method, params) {
|
|
415
|
+
const sessionId = this._sessions.get(connectId) ?? connectId;
|
|
416
|
+
return this.connector.call(sessionId, method, params);
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Ensure device permission before proceeding.
|
|
420
|
+
* - No connectId (searchDevices): check environment-level permission
|
|
421
|
+
* - With connectId (business methods): check device-level permission
|
|
422
|
+
* If not granted, calls onDevicePermission so the consumer can request access.
|
|
423
|
+
*/
|
|
424
|
+
async _ensureDevicePermission(connectId, deviceId) {
|
|
425
|
+
const transportType = "usb";
|
|
426
|
+
let granted = false;
|
|
427
|
+
let context;
|
|
428
|
+
if (this._uiHandler?.checkDevicePermission) {
|
|
429
|
+
try {
|
|
430
|
+
const result = await this._uiHandler.checkDevicePermission({ transportType, connectId, deviceId });
|
|
431
|
+
granted = result.granted;
|
|
432
|
+
context = result.context;
|
|
433
|
+
} catch {
|
|
434
|
+
granted = false;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (!granted) {
|
|
438
|
+
try {
|
|
439
|
+
await this._uiHandler?.onDevicePermission?.({ transportType, context });
|
|
440
|
+
} catch {
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Convert a thrown error to a Response failure.
|
|
446
|
+
* Parses TrezorConnect error strings to map to HardwareErrorCode values.
|
|
447
|
+
*/
|
|
448
|
+
errorToFailure(err) {
|
|
449
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
450
|
+
const code = this.parseErrorCode(message);
|
|
451
|
+
const enriched = this.enrichErrorMessage(code, message);
|
|
452
|
+
return (0, import_hardware_wallet_core.failure)(code, enriched);
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Parse TrezorConnect error codes from error message strings.
|
|
456
|
+
* The connector throws errors with messages like "Trezor ethereumGetAddress failed: <error>".
|
|
457
|
+
* We also check for embedded code patterns.
|
|
458
|
+
*/
|
|
459
|
+
parseErrorCode(message) {
|
|
460
|
+
if (message.includes("Failure_ActionCancelled") || message.includes("Failure_Cancel")) {
|
|
461
|
+
return import_hardware_wallet_core.HardwareErrorCode.UserRejected;
|
|
462
|
+
}
|
|
463
|
+
if (message.includes("Failure_PinInvalid") || message.includes("Failure_PinMismatch")) {
|
|
464
|
+
return import_hardware_wallet_core.HardwareErrorCode.PinInvalid;
|
|
465
|
+
}
|
|
466
|
+
if (message.includes("Failure_PinCancelled")) {
|
|
467
|
+
return import_hardware_wallet_core.HardwareErrorCode.PinCancelled;
|
|
468
|
+
}
|
|
469
|
+
if (message.includes("Failure_PassphraseRejected")) {
|
|
470
|
+
return import_hardware_wallet_core.HardwareErrorCode.PassphraseRejected;
|
|
471
|
+
}
|
|
472
|
+
if (message.includes("Device_UsedElsewhere")) {
|
|
473
|
+
return import_hardware_wallet_core.HardwareErrorCode.DeviceBusy;
|
|
474
|
+
}
|
|
475
|
+
if (message.includes("Device_NotFound")) {
|
|
476
|
+
return import_hardware_wallet_core.HardwareErrorCode.DeviceNotFound;
|
|
477
|
+
}
|
|
478
|
+
if (message.includes("Device_InvalidState")) {
|
|
479
|
+
return import_hardware_wallet_core.HardwareErrorCode.DeviceNotInitialized;
|
|
480
|
+
}
|
|
481
|
+
if (message.includes("Transport_Missing")) {
|
|
482
|
+
return import_hardware_wallet_core.HardwareErrorCode.TransportNotAvailable;
|
|
483
|
+
}
|
|
484
|
+
if (message.includes("Transport_DeviceDisconnected")) {
|
|
485
|
+
return import_hardware_wallet_core.HardwareErrorCode.DeviceDisconnected;
|
|
486
|
+
}
|
|
487
|
+
if (message.includes("Failure_FirmwareError")) {
|
|
488
|
+
return import_hardware_wallet_core.HardwareErrorCode.FirmwareTooOld;
|
|
489
|
+
}
|
|
490
|
+
if (message.includes("Method_InvalidParameter") || message.includes("Method_InvalidParams")) {
|
|
491
|
+
return import_hardware_wallet_core.HardwareErrorCode.InvalidParams;
|
|
492
|
+
}
|
|
493
|
+
if (message.includes("Method_NotAllowed")) {
|
|
494
|
+
return import_hardware_wallet_core.HardwareErrorCode.MethodNotSupported;
|
|
495
|
+
}
|
|
496
|
+
return import_hardware_wallet_core.HardwareErrorCode.UnknownError;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Enrich error messages with actionable recovery info for the caller.
|
|
500
|
+
*/
|
|
501
|
+
enrichErrorMessage(code, originalMessage) {
|
|
502
|
+
switch (code) {
|
|
503
|
+
case import_hardware_wallet_core.HardwareErrorCode.PinInvalid:
|
|
504
|
+
return `${originalMessage}. Please re-enter your PIN.`;
|
|
505
|
+
case import_hardware_wallet_core.HardwareErrorCode.PinCancelled:
|
|
506
|
+
return `${originalMessage}. PIN entry was cancelled.`;
|
|
507
|
+
case import_hardware_wallet_core.HardwareErrorCode.DeviceBusy:
|
|
508
|
+
return `${originalMessage}. The device is in use by another application. Close other wallet apps and try again.`;
|
|
509
|
+
case import_hardware_wallet_core.HardwareErrorCode.DeviceDisconnected:
|
|
510
|
+
return `${originalMessage}. Please reconnect the device and try again.`;
|
|
511
|
+
case import_hardware_wallet_core.HardwareErrorCode.TransportNotAvailable:
|
|
512
|
+
return `${originalMessage}. Ensure Trezor Bridge is installed and running, or connect via USB.`;
|
|
513
|
+
case import_hardware_wallet_core.HardwareErrorCode.FirmwareTooOld:
|
|
514
|
+
return `${originalMessage}. Please update your Trezor firmware via Trezor Suite.`;
|
|
515
|
+
case import_hardware_wallet_core.HardwareErrorCode.DeviceNotInitialized:
|
|
516
|
+
return `${originalMessage}. The device may need to be set up first via Trezor Suite.`;
|
|
517
|
+
default:
|
|
518
|
+
return originalMessage;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
/**
|
|
522
|
+
* Generic batch call with progress reporting.
|
|
523
|
+
* If any single call fails, returns the failure immediately.
|
|
524
|
+
*/
|
|
525
|
+
async batchCall(params, callFn, onProgress) {
|
|
526
|
+
const results = [];
|
|
527
|
+
for (let i = 0; i < params.length; i++) {
|
|
528
|
+
const result = await callFn(params[i]);
|
|
529
|
+
if (!result.success) {
|
|
530
|
+
return result;
|
|
531
|
+
}
|
|
532
|
+
results.push(result.payload);
|
|
533
|
+
onProgress?.({ index: i, total: params.length });
|
|
534
|
+
}
|
|
535
|
+
return (0, import_hardware_wallet_core.success)(results);
|
|
536
|
+
}
|
|
537
|
+
// ─── Hex formatting ──────────────────────────────────────
|
|
538
|
+
/** Ensure a hex string has the `0x` prefix. */
|
|
539
|
+
ensure0x(hex) {
|
|
540
|
+
return hex.startsWith("0x") ? hex : `0x${hex}`;
|
|
541
|
+
}
|
|
542
|
+
/** Ensure a hex string is `0x`-prefixed and zero-padded to 64 hex chars (32 bytes). */
|
|
543
|
+
padHex64(hex) {
|
|
544
|
+
const stripped = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
545
|
+
return `0x${stripped.padStart(64, "0")}`;
|
|
546
|
+
}
|
|
547
|
+
registerEventListeners() {
|
|
548
|
+
this.connector.on("device-connect", this.deviceConnectHandler);
|
|
549
|
+
this.connector.on("device-disconnect", this.deviceDisconnectHandler);
|
|
550
|
+
this.connector.on("ui-request", this.uiRequestHandler);
|
|
551
|
+
this.connector.on("ui-event", this.uiEventHandler);
|
|
552
|
+
}
|
|
553
|
+
unregisterEventListeners() {
|
|
554
|
+
this.connector.off("device-connect", this.deviceConnectHandler);
|
|
555
|
+
this.connector.off("device-disconnect", this.deviceDisconnectHandler);
|
|
556
|
+
this.connector.off("ui-request", this.uiRequestHandler);
|
|
557
|
+
this.connector.off("ui-event", this.uiEventHandler);
|
|
558
|
+
}
|
|
559
|
+
handleUiEvent(event) {
|
|
560
|
+
if (!event.type) return;
|
|
561
|
+
const payload = event.payload;
|
|
562
|
+
const devicePayload = payload?.["device"] ?? payload;
|
|
563
|
+
const deviceInfo = devicePayload ? this.extractDeviceInfoFromPayload(devicePayload) : this.unknownDevice();
|
|
564
|
+
switch (event.type) {
|
|
565
|
+
case "ui-request_pin":
|
|
566
|
+
this.emitter.emit(import_hardware_wallet_core.UI_REQUEST.REQUEST_PIN, {
|
|
567
|
+
type: import_hardware_wallet_core.UI_REQUEST.REQUEST_PIN,
|
|
568
|
+
payload: { device: deviceInfo }
|
|
569
|
+
});
|
|
570
|
+
if (this._uiHandler?.onPinRequest) {
|
|
571
|
+
this._uiHandler.onPinRequest(deviceInfo).then((pin) => {
|
|
572
|
+
this.connector.uiResponse({
|
|
573
|
+
type: "receive-pin",
|
|
574
|
+
payload: pin
|
|
575
|
+
});
|
|
576
|
+
}).catch(() => {
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
break;
|
|
580
|
+
case "ui-request_passphrase":
|
|
581
|
+
this.emitter.emit(import_hardware_wallet_core.UI_REQUEST.REQUEST_PASSPHRASE, {
|
|
582
|
+
type: import_hardware_wallet_core.UI_REQUEST.REQUEST_PASSPHRASE,
|
|
583
|
+
payload: { device: deviceInfo }
|
|
584
|
+
});
|
|
585
|
+
if (this._uiHandler?.onPassphraseRequest) {
|
|
586
|
+
this._uiHandler.onPassphraseRequest(deviceInfo).then((result) => {
|
|
587
|
+
const response = typeof result === "string" ? { passphrase: result, onDevice: false } : result ?? { passphrase: "", onDevice: false };
|
|
588
|
+
if (response.onDevice) {
|
|
589
|
+
this.connector.uiResponse({
|
|
590
|
+
type: "receive-passphrase",
|
|
591
|
+
payload: {
|
|
592
|
+
value: "",
|
|
593
|
+
passphraseOnDevice: true,
|
|
594
|
+
save: false
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
} else {
|
|
598
|
+
this.connector.uiResponse({
|
|
599
|
+
type: "receive-passphrase",
|
|
600
|
+
payload: {
|
|
601
|
+
value: response.passphrase,
|
|
602
|
+
passphraseOnDevice: false,
|
|
603
|
+
save: false
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
}).catch(() => {
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
break;
|
|
611
|
+
case "ui-request_confirmation":
|
|
612
|
+
this.emitter.emit(import_hardware_wallet_core.UI_REQUEST.REQUEST_BUTTON, {
|
|
613
|
+
type: import_hardware_wallet_core.UI_REQUEST.REQUEST_BUTTON,
|
|
614
|
+
payload: { device: deviceInfo }
|
|
615
|
+
});
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
connectorDeviceToDeviceInfo(device) {
|
|
620
|
+
return {
|
|
621
|
+
vendor: "trezor",
|
|
622
|
+
model: device.model ?? "unknown",
|
|
623
|
+
firmwareVersion: "",
|
|
624
|
+
deviceId: device.deviceId,
|
|
625
|
+
connectId: device.connectId,
|
|
626
|
+
label: device.name,
|
|
627
|
+
connectionType: "usb"
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
extractDeviceInfoFromPayload(payload) {
|
|
631
|
+
const features = payload["features"];
|
|
632
|
+
return {
|
|
633
|
+
vendor: "trezor",
|
|
634
|
+
model: features?.["model"] ?? payload["model"] ?? "unknown",
|
|
635
|
+
firmwareVersion: features ? `${features["major_version"] ?? 0}.${features["minor_version"] ?? 0}.${features["patch_version"] ?? 0}` : "",
|
|
636
|
+
deviceId: features?.["device_id"] ?? payload["id"] ?? "",
|
|
637
|
+
connectId: payload["path"] ?? "",
|
|
638
|
+
label: features?.["label"] ?? payload["label"],
|
|
639
|
+
connectionType: "usb"
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
unknownDevice() {
|
|
643
|
+
return {
|
|
644
|
+
vendor: "trezor",
|
|
645
|
+
model: "unknown",
|
|
646
|
+
firmwareVersion: "",
|
|
647
|
+
deviceId: "",
|
|
648
|
+
connectId: "",
|
|
649
|
+
connectionType: "usb"
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
};
|
|
653
|
+
|
|
654
|
+
// src/TrezorProxyClient.ts
|
|
655
|
+
var import_hardware_transport_core = require("@bytezhang/hardware-transport-core");
|
|
656
|
+
var TrezorProxyClient = class extends import_hardware_transport_core.AbstractProxyClient {
|
|
657
|
+
// ─── TrezorConnect method stubs — all delegate to call() ────
|
|
658
|
+
ethereumGetAddress(params) {
|
|
659
|
+
return this.call("ethereumGetAddress", params);
|
|
660
|
+
}
|
|
661
|
+
ethereumGetPublicKey(params) {
|
|
662
|
+
return this.call("ethereumGetPublicKey", params);
|
|
663
|
+
}
|
|
664
|
+
ethereumSignTransaction(params) {
|
|
665
|
+
return this.call("ethereumSignTransaction", params);
|
|
666
|
+
}
|
|
667
|
+
ethereumSignMessage(params) {
|
|
668
|
+
return this.call("ethereumSignMessage", params);
|
|
669
|
+
}
|
|
670
|
+
ethereumSignTypedData(params) {
|
|
671
|
+
return this.call("ethereumSignTypedData", params);
|
|
672
|
+
}
|
|
673
|
+
getAddress(params) {
|
|
674
|
+
return this.call("getAddress", params);
|
|
675
|
+
}
|
|
676
|
+
getPublicKey(params) {
|
|
677
|
+
return this.call("getPublicKey", params);
|
|
678
|
+
}
|
|
679
|
+
signTransaction(params) {
|
|
680
|
+
return this.call("signTransaction", params);
|
|
681
|
+
}
|
|
682
|
+
signMessage(params) {
|
|
683
|
+
return this.call("signMessage", params);
|
|
684
|
+
}
|
|
685
|
+
solanaGetAddress(params) {
|
|
686
|
+
return this.call("solanaGetAddress", params);
|
|
687
|
+
}
|
|
688
|
+
solanaSignTransaction(params) {
|
|
689
|
+
return this.call("solanaSignTransaction", params);
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
693
|
+
0 && (module.exports = {
|
|
694
|
+
TrezorAdapter,
|
|
695
|
+
TrezorProxyClient
|
|
696
|
+
});
|
|
697
|
+
//# sourceMappingURL=index.js.map
|