@sip-protocol/sdk 0.1.0 → 0.1.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 +3236 -1554
- package/dist/index.d.ts +3236 -1554
- package/dist/index.js +9185 -3521
- package/dist/index.mjs +8995 -3376
- package/package.json +5 -2
- package/src/adapters/near-intents.ts +48 -35
- package/src/adapters/oneclick-client.ts +9 -1
- package/src/compliance/compliance-manager.ts +1035 -0
- package/src/compliance/index.ts +43 -0
- package/src/index.ts +129 -2
- package/src/payment/index.ts +54 -0
- package/src/payment/payment.ts +623 -0
- package/src/payment/stablecoins.ts +306 -0
- package/src/privacy.ts +127 -94
- package/src/proofs/circuits/fulfillment_proof.json +1 -0
- package/src/proofs/circuits/funding_proof.json +1 -0
- package/src/proofs/circuits/validity_proof.json +1 -0
- package/src/proofs/interface.ts +13 -1
- package/src/proofs/noir.ts +967 -97
- package/src/secure-memory.ts +147 -0
- package/src/sip.ts +399 -37
- package/src/stealth.ts +116 -84
- package/src/treasury/index.ts +43 -0
- package/src/treasury/treasury.ts +911 -0
- package/src/wallet/hardware/index.ts +87 -0
- package/src/wallet/hardware/ledger.ts +628 -0
- package/src/wallet/hardware/mock.ts +667 -0
- package/src/wallet/hardware/trezor.ts +657 -0
- package/src/wallet/hardware/types.ts +317 -0
- package/src/wallet/index.ts +40 -0
- package/src/zcash/shielded-service.ts +59 -1
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trezor Hardware Wallet Adapter
|
|
3
|
+
*
|
|
4
|
+
* Provides integration with Trezor hardware wallets (Model One, Model T, Safe 3/5)
|
|
5
|
+
* for secure transaction signing.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { TrezorWalletAdapter } from '@sip-protocol/sdk'
|
|
10
|
+
*
|
|
11
|
+
* const trezor = new TrezorWalletAdapter({
|
|
12
|
+
* chain: 'ethereum',
|
|
13
|
+
* accountIndex: 0,
|
|
14
|
+
* manifestEmail: 'dev@myapp.com',
|
|
15
|
+
* manifestAppName: 'My DApp',
|
|
16
|
+
* manifestUrl: 'https://myapp.com',
|
|
17
|
+
* })
|
|
18
|
+
*
|
|
19
|
+
* await trezor.connect()
|
|
20
|
+
* const signature = await trezor.signMessage(message)
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* Uses Trezor Connect for device communication.
|
|
25
|
+
*
|
|
26
|
+
* External dependency (install separately):
|
|
27
|
+
* - @trezor/connect-web
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import type {
|
|
31
|
+
ChainId,
|
|
32
|
+
HexString,
|
|
33
|
+
Asset,
|
|
34
|
+
Signature,
|
|
35
|
+
UnsignedTransaction,
|
|
36
|
+
SignedTransaction,
|
|
37
|
+
TransactionReceipt,
|
|
38
|
+
} from '@sip-protocol/types'
|
|
39
|
+
import { WalletErrorCode } from '@sip-protocol/types'
|
|
40
|
+
import { BaseWalletAdapter } from '../base-adapter'
|
|
41
|
+
import { WalletError } from '../errors'
|
|
42
|
+
import {
|
|
43
|
+
type TrezorConfig,
|
|
44
|
+
type HardwareDeviceInfo,
|
|
45
|
+
type HardwareAccount,
|
|
46
|
+
type HardwareSignature,
|
|
47
|
+
type HardwareEthereumTx,
|
|
48
|
+
HardwareErrorCode,
|
|
49
|
+
HardwareWalletError,
|
|
50
|
+
getDerivationPath,
|
|
51
|
+
} from './types'
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Trezor wallet adapter
|
|
55
|
+
*
|
|
56
|
+
* Supports Ethereum chain via Trezor Connect.
|
|
57
|
+
*/
|
|
58
|
+
export class TrezorWalletAdapter extends BaseWalletAdapter {
|
|
59
|
+
readonly chain: ChainId
|
|
60
|
+
readonly name: string = 'trezor'
|
|
61
|
+
|
|
62
|
+
private config: TrezorConfig
|
|
63
|
+
private trezorConnect: TrezorConnectType | null = null
|
|
64
|
+
private initialized: boolean = false
|
|
65
|
+
private _derivationPath: string
|
|
66
|
+
private _deviceInfo: HardwareDeviceInfo | null = null
|
|
67
|
+
private _account: HardwareAccount | null = null
|
|
68
|
+
|
|
69
|
+
constructor(config: TrezorConfig) {
|
|
70
|
+
super()
|
|
71
|
+
this.chain = config.chain
|
|
72
|
+
this.config = {
|
|
73
|
+
accountIndex: 0,
|
|
74
|
+
timeout: 30000,
|
|
75
|
+
popup: true,
|
|
76
|
+
manifestEmail: 'support@sip-protocol.org',
|
|
77
|
+
manifestAppName: 'SIP Protocol',
|
|
78
|
+
manifestUrl: 'https://sip-protocol.org',
|
|
79
|
+
...config,
|
|
80
|
+
}
|
|
81
|
+
this._derivationPath = config.derivationPath ??
|
|
82
|
+
getDerivationPath(config.chain, config.accountIndex ?? 0)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Get device information
|
|
87
|
+
*/
|
|
88
|
+
get deviceInfo(): HardwareDeviceInfo | null {
|
|
89
|
+
return this._deviceInfo
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Get current derivation path
|
|
94
|
+
*/
|
|
95
|
+
get derivationPath(): string {
|
|
96
|
+
return this._derivationPath
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Get current account
|
|
101
|
+
*/
|
|
102
|
+
get account(): HardwareAccount | null {
|
|
103
|
+
return this._account
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Connect to Trezor device
|
|
108
|
+
*/
|
|
109
|
+
async connect(): Promise<void> {
|
|
110
|
+
this._connectionState = 'connecting'
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
// Load and initialize Trezor Connect
|
|
114
|
+
await this.initializeTrezorConnect()
|
|
115
|
+
|
|
116
|
+
// Get account from device
|
|
117
|
+
this._account = await this.getAccountFromDevice()
|
|
118
|
+
|
|
119
|
+
// Get device features
|
|
120
|
+
const features = await this.getDeviceFeatures()
|
|
121
|
+
this._deviceInfo = {
|
|
122
|
+
manufacturer: 'trezor',
|
|
123
|
+
model: features.model ?? 'unknown',
|
|
124
|
+
firmwareVersion: features.firmwareVersion,
|
|
125
|
+
isLocked: features.pinProtection && !features.pinCached,
|
|
126
|
+
label: features.label,
|
|
127
|
+
deviceId: features.deviceId,
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
this.setConnected(this._account.address, this._account.publicKey)
|
|
131
|
+
} catch (error) {
|
|
132
|
+
this._connectionState = 'error'
|
|
133
|
+
throw this.handleError(error)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Disconnect from Trezor device
|
|
139
|
+
*/
|
|
140
|
+
async disconnect(): Promise<void> {
|
|
141
|
+
if (this.trezorConnect) {
|
|
142
|
+
// Trezor Connect doesn't have explicit disconnect
|
|
143
|
+
// Just clear state
|
|
144
|
+
}
|
|
145
|
+
this._account = null
|
|
146
|
+
this._deviceInfo = null
|
|
147
|
+
this.setDisconnected()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Sign a message
|
|
152
|
+
*/
|
|
153
|
+
async signMessage(message: Uint8Array): Promise<Signature> {
|
|
154
|
+
this.requireConnected()
|
|
155
|
+
|
|
156
|
+
if (!this.trezorConnect) {
|
|
157
|
+
throw new HardwareWalletError(
|
|
158
|
+
'Trezor Connect not initialized',
|
|
159
|
+
HardwareErrorCode.TRANSPORT_ERROR,
|
|
160
|
+
'trezor'
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const sig = await this.signMessageOnDevice(message)
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
signature: sig.signature,
|
|
169
|
+
publicKey: this._account!.publicKey,
|
|
170
|
+
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
throw this.handleError(error)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Sign a transaction
|
|
178
|
+
*/
|
|
179
|
+
async signTransaction(tx: UnsignedTransaction): Promise<SignedTransaction> {
|
|
180
|
+
this.requireConnected()
|
|
181
|
+
|
|
182
|
+
if (!this.trezorConnect) {
|
|
183
|
+
throw new HardwareWalletError(
|
|
184
|
+
'Trezor Connect not initialized',
|
|
185
|
+
HardwareErrorCode.TRANSPORT_ERROR,
|
|
186
|
+
'trezor'
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const sig = await this.signTransactionOnDevice(tx)
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
unsigned: tx,
|
|
195
|
+
signatures: [
|
|
196
|
+
{
|
|
197
|
+
signature: sig.signature,
|
|
198
|
+
publicKey: this._account!.publicKey,
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
serialized: sig.signature,
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
throw this.handleError(error)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Sign and send transaction
|
|
210
|
+
*
|
|
211
|
+
* Note: Hardware wallets can only sign, not send. This returns a signed
|
|
212
|
+
* transaction that must be broadcast separately.
|
|
213
|
+
*/
|
|
214
|
+
async signAndSendTransaction(tx: UnsignedTransaction): Promise<TransactionReceipt> {
|
|
215
|
+
const signed = await this.signTransaction(tx)
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
txHash: signed.serialized.slice(0, 66) as HexString,
|
|
219
|
+
status: 'pending',
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get native token balance
|
|
225
|
+
*
|
|
226
|
+
* Note: Hardware wallets don't track balances - this requires RPC.
|
|
227
|
+
*/
|
|
228
|
+
async getBalance(): Promise<bigint> {
|
|
229
|
+
throw new WalletError(
|
|
230
|
+
'Hardware wallets do not track balances. Use an RPC provider.',
|
|
231
|
+
WalletErrorCode.UNSUPPORTED_OPERATION
|
|
232
|
+
)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Get token balance
|
|
237
|
+
*
|
|
238
|
+
* Note: Hardware wallets don't track balances - this requires RPC.
|
|
239
|
+
*/
|
|
240
|
+
async getTokenBalance(_asset: Asset): Promise<bigint> {
|
|
241
|
+
throw new WalletError(
|
|
242
|
+
'Hardware wallets do not track balances. Use an RPC provider.',
|
|
243
|
+
WalletErrorCode.UNSUPPORTED_OPERATION
|
|
244
|
+
)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ─── Account Management ─────────────────────────────────────────────────────
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Get multiple accounts from device
|
|
251
|
+
*/
|
|
252
|
+
async getAccounts(startIndex: number = 0, count: number = 5): Promise<HardwareAccount[]> {
|
|
253
|
+
this.requireConnected()
|
|
254
|
+
|
|
255
|
+
const accounts: HardwareAccount[] = []
|
|
256
|
+
|
|
257
|
+
for (let i = startIndex; i < startIndex + count; i++) {
|
|
258
|
+
const path = getDerivationPath(this.chain, i)
|
|
259
|
+
const account = await this.getAccountAtPath(path, i)
|
|
260
|
+
accounts.push(account)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return accounts
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Switch to different account index
|
|
268
|
+
*/
|
|
269
|
+
async switchAccount(accountIndex: number): Promise<HardwareAccount> {
|
|
270
|
+
this.requireConnected()
|
|
271
|
+
|
|
272
|
+
this._derivationPath = getDerivationPath(this.chain, accountIndex)
|
|
273
|
+
this._account = await this.getAccountFromDevice()
|
|
274
|
+
|
|
275
|
+
const previousAddress = this._address
|
|
276
|
+
this.setConnected(this._account.address, this._account.publicKey)
|
|
277
|
+
|
|
278
|
+
if (previousAddress !== this._account.address) {
|
|
279
|
+
this.emitAccountChanged(previousAddress, this._account.address)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return this._account
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ─── Private Methods ────────────────────────────────────────────────────────
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Initialize Trezor Connect
|
|
289
|
+
*/
|
|
290
|
+
private async initializeTrezorConnect(): Promise<void> {
|
|
291
|
+
if (this.initialized) return
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
// Dynamic import of Trezor Connect
|
|
295
|
+
// @ts-expect-error - Dynamic import
|
|
296
|
+
const TrezorConnect = await import('@trezor/connect-web')
|
|
297
|
+
this.trezorConnect = TrezorConnect.default
|
|
298
|
+
|
|
299
|
+
// Initialize with manifest
|
|
300
|
+
await this.trezorConnect!.init({
|
|
301
|
+
manifest: {
|
|
302
|
+
email: this.config.manifestEmail!,
|
|
303
|
+
appUrl: this.config.manifestUrl!,
|
|
304
|
+
},
|
|
305
|
+
popup: this.config.popup,
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
this.initialized = true
|
|
309
|
+
} catch (error) {
|
|
310
|
+
throw new HardwareWalletError(
|
|
311
|
+
'Failed to load Trezor Connect. Install @trezor/connect-web',
|
|
312
|
+
HardwareErrorCode.TRANSPORT_ERROR,
|
|
313
|
+
'trezor',
|
|
314
|
+
error
|
|
315
|
+
)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Get device features
|
|
321
|
+
*/
|
|
322
|
+
private async getDeviceFeatures(): Promise<TrezorFeatures> {
|
|
323
|
+
if (!this.trezorConnect) {
|
|
324
|
+
throw new HardwareWalletError(
|
|
325
|
+
'Trezor Connect not initialized',
|
|
326
|
+
HardwareErrorCode.TRANSPORT_ERROR,
|
|
327
|
+
'trezor'
|
|
328
|
+
)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
const result = await this.trezorConnect.getFeatures()
|
|
332
|
+
|
|
333
|
+
if (!result.success) {
|
|
334
|
+
const errorPayload = result.payload as { error?: string }
|
|
335
|
+
throw new HardwareWalletError(
|
|
336
|
+
errorPayload.error ?? 'Failed to get device features',
|
|
337
|
+
HardwareErrorCode.TRANSPORT_ERROR,
|
|
338
|
+
'trezor'
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Type assertion after success check - payload is now the features object
|
|
343
|
+
const features = result.payload as {
|
|
344
|
+
model: string
|
|
345
|
+
major_version: number
|
|
346
|
+
minor_version: number
|
|
347
|
+
patch_version: number
|
|
348
|
+
label?: string
|
|
349
|
+
device_id?: string
|
|
350
|
+
pin_protection?: boolean
|
|
351
|
+
pin_cached?: boolean
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return {
|
|
355
|
+
model: features.model,
|
|
356
|
+
firmwareVersion: `${features.major_version}.${features.minor_version}.${features.patch_version}`,
|
|
357
|
+
label: features.label ?? undefined,
|
|
358
|
+
deviceId: features.device_id ?? undefined,
|
|
359
|
+
pinProtection: features.pin_protection ?? false,
|
|
360
|
+
pinCached: features.pin_cached ?? false,
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Get account from device at current derivation path
|
|
366
|
+
*/
|
|
367
|
+
private async getAccountFromDevice(): Promise<HardwareAccount> {
|
|
368
|
+
return this.getAccountAtPath(this._derivationPath, this.config.accountIndex ?? 0)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Get account at specific derivation path
|
|
373
|
+
*/
|
|
374
|
+
private async getAccountAtPath(path: string, index: number): Promise<HardwareAccount> {
|
|
375
|
+
if (!this.trezorConnect) {
|
|
376
|
+
throw new HardwareWalletError(
|
|
377
|
+
'Trezor Connect not initialized',
|
|
378
|
+
HardwareErrorCode.TRANSPORT_ERROR,
|
|
379
|
+
'trezor'
|
|
380
|
+
)
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (this.chain === 'ethereum') {
|
|
384
|
+
const result = await this.trezorConnect.ethereumGetAddress({
|
|
385
|
+
path,
|
|
386
|
+
showOnTrezor: false,
|
|
387
|
+
})
|
|
388
|
+
|
|
389
|
+
if (!result.success) {
|
|
390
|
+
const errorPayload = result.payload as { error?: string }
|
|
391
|
+
throw new HardwareWalletError(
|
|
392
|
+
errorPayload.error ?? 'Failed to get address',
|
|
393
|
+
HardwareErrorCode.TRANSPORT_ERROR,
|
|
394
|
+
'trezor'
|
|
395
|
+
)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const addressPayload = result.payload as { address: string }
|
|
399
|
+
return {
|
|
400
|
+
address: addressPayload.address,
|
|
401
|
+
publicKey: addressPayload.address as HexString, // Trezor returns address, not public key for Ethereum
|
|
402
|
+
derivationPath: path,
|
|
403
|
+
index,
|
|
404
|
+
chain: this.chain,
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
throw new HardwareWalletError(
|
|
409
|
+
`Chain ${this.chain} not supported by Trezor adapter`,
|
|
410
|
+
HardwareErrorCode.UNSUPPORTED,
|
|
411
|
+
'trezor'
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Sign message on device
|
|
417
|
+
*/
|
|
418
|
+
private async signMessageOnDevice(message: Uint8Array): Promise<HardwareSignature> {
|
|
419
|
+
if (!this.trezorConnect) {
|
|
420
|
+
throw new HardwareWalletError(
|
|
421
|
+
'Trezor Connect not initialized',
|
|
422
|
+
HardwareErrorCode.TRANSPORT_ERROR,
|
|
423
|
+
'trezor'
|
|
424
|
+
)
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (this.chain === 'ethereum') {
|
|
428
|
+
const messageHex = `0x${Buffer.from(message).toString('hex')}`
|
|
429
|
+
|
|
430
|
+
const result = await this.trezorConnect.ethereumSignMessage({
|
|
431
|
+
path: this._derivationPath,
|
|
432
|
+
message: messageHex,
|
|
433
|
+
hex: true,
|
|
434
|
+
})
|
|
435
|
+
|
|
436
|
+
if (!result.success) {
|
|
437
|
+
const errorPayload = result.payload as { error?: string }
|
|
438
|
+
throw new HardwareWalletError(
|
|
439
|
+
errorPayload.error ?? 'Failed to sign message',
|
|
440
|
+
HardwareErrorCode.USER_REJECTED,
|
|
441
|
+
'trezor'
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const signaturePayload = result.payload as { signature: string }
|
|
446
|
+
return {
|
|
447
|
+
r: '0x' as HexString,
|
|
448
|
+
s: '0x' as HexString,
|
|
449
|
+
v: 0,
|
|
450
|
+
signature: `0x${signaturePayload.signature}` as HexString,
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
throw new HardwareWalletError(
|
|
455
|
+
`Message signing not supported for ${this.chain}`,
|
|
456
|
+
HardwareErrorCode.UNSUPPORTED,
|
|
457
|
+
'trezor'
|
|
458
|
+
)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Sign transaction on device
|
|
463
|
+
*/
|
|
464
|
+
private async signTransactionOnDevice(tx: UnsignedTransaction): Promise<HardwareSignature> {
|
|
465
|
+
if (!this.trezorConnect) {
|
|
466
|
+
throw new HardwareWalletError(
|
|
467
|
+
'Trezor Connect not initialized',
|
|
468
|
+
HardwareErrorCode.TRANSPORT_ERROR,
|
|
469
|
+
'trezor'
|
|
470
|
+
)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (this.chain === 'ethereum') {
|
|
474
|
+
const ethTx = tx.data as HardwareEthereumTx
|
|
475
|
+
|
|
476
|
+
const result = await this.trezorConnect.ethereumSignTransaction({
|
|
477
|
+
path: this._derivationPath,
|
|
478
|
+
transaction: {
|
|
479
|
+
to: ethTx.to,
|
|
480
|
+
value: ethTx.value,
|
|
481
|
+
gasLimit: ethTx.gasLimit,
|
|
482
|
+
gasPrice: ethTx.gasPrice,
|
|
483
|
+
nonce: ethTx.nonce,
|
|
484
|
+
data: ethTx.data ?? '0x',
|
|
485
|
+
chainId: ethTx.chainId,
|
|
486
|
+
},
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
if (!result.success) {
|
|
490
|
+
const errorPayload = result.payload as { error?: string }
|
|
491
|
+
throw new HardwareWalletError(
|
|
492
|
+
errorPayload.error ?? 'Failed to sign transaction',
|
|
493
|
+
HardwareErrorCode.USER_REJECTED,
|
|
494
|
+
'trezor'
|
|
495
|
+
)
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const txPayload = result.payload as { r: string; s: string; v: string }
|
|
499
|
+
return {
|
|
500
|
+
r: `0x${txPayload.r}` as HexString,
|
|
501
|
+
s: `0x${txPayload.s}` as HexString,
|
|
502
|
+
v: parseInt(txPayload.v, 16),
|
|
503
|
+
signature: `0x${txPayload.r}${txPayload.s}${txPayload.v}` as HexString,
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
throw new HardwareWalletError(
|
|
508
|
+
`Transaction signing not supported for ${this.chain}`,
|
|
509
|
+
HardwareErrorCode.UNSUPPORTED,
|
|
510
|
+
'trezor'
|
|
511
|
+
)
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Handle and transform errors
|
|
516
|
+
*/
|
|
517
|
+
private handleError(error: unknown): Error {
|
|
518
|
+
if (error instanceof HardwareWalletError) {
|
|
519
|
+
return error
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (error instanceof WalletError) {
|
|
523
|
+
return error
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const err = error as { code?: string; message?: string }
|
|
527
|
+
|
|
528
|
+
// Trezor Connect error codes
|
|
529
|
+
if (err.code) {
|
|
530
|
+
switch (err.code) {
|
|
531
|
+
case 'Failure_ActionCancelled':
|
|
532
|
+
case 'Method_Cancel':
|
|
533
|
+
return new HardwareWalletError(
|
|
534
|
+
'Action cancelled on device',
|
|
535
|
+
HardwareErrorCode.USER_REJECTED,
|
|
536
|
+
'trezor'
|
|
537
|
+
)
|
|
538
|
+
case 'Failure_PinCancelled':
|
|
539
|
+
return new HardwareWalletError(
|
|
540
|
+
'PIN entry cancelled',
|
|
541
|
+
HardwareErrorCode.USER_REJECTED,
|
|
542
|
+
'trezor'
|
|
543
|
+
)
|
|
544
|
+
case 'Device_CallInProgress':
|
|
545
|
+
return new HardwareWalletError(
|
|
546
|
+
'Another operation in progress',
|
|
547
|
+
HardwareErrorCode.TRANSPORT_ERROR,
|
|
548
|
+
'trezor'
|
|
549
|
+
)
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
if (err.message?.includes('cancelled') || err.message?.includes('Cancelled')) {
|
|
554
|
+
return new HardwareWalletError(
|
|
555
|
+
'Operation cancelled',
|
|
556
|
+
HardwareErrorCode.USER_REJECTED,
|
|
557
|
+
'trezor'
|
|
558
|
+
)
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (err.message?.includes('not found') || err.message?.includes('No device')) {
|
|
562
|
+
return new HardwareWalletError(
|
|
563
|
+
'Trezor device not found',
|
|
564
|
+
HardwareErrorCode.DEVICE_NOT_FOUND,
|
|
565
|
+
'trezor'
|
|
566
|
+
)
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
return new HardwareWalletError(
|
|
570
|
+
err.message ?? 'Unknown Trezor error',
|
|
571
|
+
HardwareErrorCode.TRANSPORT_ERROR,
|
|
572
|
+
'trezor',
|
|
573
|
+
error
|
|
574
|
+
)
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// ─── Type Stubs for Dynamic Imports ───────────────────────────────────────────
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Trezor Connect type stub
|
|
582
|
+
*/
|
|
583
|
+
interface TrezorConnectType {
|
|
584
|
+
init(params: {
|
|
585
|
+
manifest: { email: string; appUrl: string }
|
|
586
|
+
popup?: boolean
|
|
587
|
+
}): Promise<void>
|
|
588
|
+
|
|
589
|
+
getFeatures(): Promise<TrezorResponse<TrezorFeaturesPayload>>
|
|
590
|
+
|
|
591
|
+
ethereumGetAddress(params: {
|
|
592
|
+
path: string
|
|
593
|
+
showOnTrezor?: boolean
|
|
594
|
+
}): Promise<TrezorResponse<{ address: string }>>
|
|
595
|
+
|
|
596
|
+
ethereumSignMessage(params: {
|
|
597
|
+
path: string
|
|
598
|
+
message: string
|
|
599
|
+
hex?: boolean
|
|
600
|
+
}): Promise<TrezorResponse<{ signature: string }>>
|
|
601
|
+
|
|
602
|
+
ethereumSignTransaction(params: {
|
|
603
|
+
path: string
|
|
604
|
+
transaction: {
|
|
605
|
+
to: string
|
|
606
|
+
value: HexString
|
|
607
|
+
gasLimit: HexString
|
|
608
|
+
gasPrice?: HexString
|
|
609
|
+
nonce: HexString
|
|
610
|
+
data?: HexString
|
|
611
|
+
chainId: number
|
|
612
|
+
}
|
|
613
|
+
}): Promise<TrezorResponse<{ r: string; s: string; v: string }>>
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Trezor response wrapper
|
|
618
|
+
*/
|
|
619
|
+
interface TrezorResponse<T> {
|
|
620
|
+
success: boolean
|
|
621
|
+
payload: T | { error: string }
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Trezor device features payload
|
|
626
|
+
*/
|
|
627
|
+
interface TrezorFeaturesPayload {
|
|
628
|
+
model?: string
|
|
629
|
+
major_version?: number
|
|
630
|
+
minor_version?: number
|
|
631
|
+
patch_version?: number
|
|
632
|
+
label?: string
|
|
633
|
+
device_id?: string
|
|
634
|
+
pin_protection?: boolean
|
|
635
|
+
pin_cached?: boolean
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Parsed Trezor features
|
|
640
|
+
*/
|
|
641
|
+
interface TrezorFeatures {
|
|
642
|
+
model?: string
|
|
643
|
+
firmwareVersion?: string
|
|
644
|
+
label?: string
|
|
645
|
+
deviceId?: string
|
|
646
|
+
pinProtection: boolean
|
|
647
|
+
pinCached: boolean
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// ─── Factory Function ─────────────────────────────────────────────────────────
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Create a Trezor wallet adapter
|
|
654
|
+
*/
|
|
655
|
+
export function createTrezorAdapter(config: TrezorConfig): TrezorWalletAdapter {
|
|
656
|
+
return new TrezorWalletAdapter(config)
|
|
657
|
+
}
|