@sip-protocol/sdk 0.6.0 → 0.6.2
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/README.md +58 -0
- package/dist/browser.d.mts +4 -4
- package/dist/browser.d.ts +4 -4
- package/dist/browser.js +2752 -448
- package/dist/browser.mjs +31 -1
- package/dist/chunk-7QZPORY5.mjs +15604 -0
- package/dist/chunk-C2NPCUAJ.mjs +17010 -0
- package/dist/chunk-FCVLFUIC.mjs +16699 -0
- package/dist/chunk-G5UHXECN.mjs +16340 -0
- package/dist/chunk-GEDEIZHJ.mjs +16798 -0
- package/dist/chunk-GOOEOAMV.mjs +17026 -0
- package/dist/chunk-MTNYSNR7.mjs +16269 -0
- package/dist/chunk-O5PIB2EA.mjs +16698 -0
- package/dist/chunk-PCFM7FQO.mjs +17010 -0
- package/dist/chunk-QK464ARC.mjs +16946 -0
- package/dist/chunk-VNBMNGC3.mjs +16698 -0
- package/dist/chunk-W5TUELDQ.mjs +16947 -0
- package/dist/index-CD_zShu-.d.ts +10870 -0
- package/dist/index-CQBYdLYy.d.mts +10976 -0
- package/dist/index-Cg9TYEPv.d.mts +11321 -0
- package/dist/index-CqZJOO8C.d.mts +11323 -0
- package/dist/index-CywN9Bnp.d.ts +11321 -0
- package/dist/index-DHy5ZjCD.d.ts +10976 -0
- package/dist/index-DfsVsmxu.d.ts +11323 -0
- package/dist/index-ObjwyVDX.d.mts +10870 -0
- package/dist/index-m0xbSfmT.d.mts +11318 -0
- package/dist/index-rWLEgvhN.d.ts +11318 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2737 -427
- package/dist/index.mjs +31 -1
- package/dist/noir-DKfEzWy9.d.mts +482 -0
- package/dist/noir-DKfEzWy9.d.ts +482 -0
- package/dist/proofs/noir.d.mts +1 -1
- package/dist/proofs/noir.d.ts +1 -1
- package/dist/proofs/noir.js +12 -3
- package/dist/proofs/noir.mjs +12 -3
- package/package.json +16 -14
- package/src/adapters/near-intents.ts +13 -3
- package/src/auction/index.ts +20 -0
- package/src/auction/sealed-bid.ts +1037 -0
- package/src/compliance/derivation.ts +13 -3
- package/src/compliance/reports.ts +5 -4
- package/src/cosmos/ibc-stealth.ts +2 -2
- package/src/cosmos/stealth.ts +2 -2
- package/src/governance/index.ts +19 -0
- package/src/governance/private-vote.ts +1116 -0
- package/src/index.ts +50 -2
- package/src/intent.ts +145 -8
- package/src/nft/index.ts +27 -0
- package/src/nft/private-nft.ts +811 -0
- package/src/proofs/browser-utils.ts +1 -7
- package/src/proofs/noir.ts +34 -7
- package/src/settlement/backends/direct-chain.ts +14 -3
- package/src/stealth.ts +31 -13
- package/src/types/browser.d.ts +67 -0
- package/src/validation.ts +4 -2
- package/src/wallet/bitcoin/adapter.ts +159 -15
- package/src/wallet/bitcoin/types.ts +340 -15
- package/src/wallet/cosmos/mock.ts +16 -12
- package/src/wallet/hardware/ledger.ts +82 -12
- package/src/wallet/hardware/types.ts +2 -0
- package/LICENSE +0 -21
|
@@ -174,11 +174,333 @@ export interface UnisatAPI {
|
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
/**
|
|
177
|
-
*
|
|
177
|
+
* Xverse wallet API interface
|
|
178
|
+
*
|
|
179
|
+
* Official API: https://docs.xverse.app/sats-connect
|
|
180
|
+
*/
|
|
181
|
+
export interface XverseAPI {
|
|
182
|
+
/**
|
|
183
|
+
* Request wallet connection
|
|
184
|
+
*/
|
|
185
|
+
request(
|
|
186
|
+
method: 'getAccounts' | 'getAddresses',
|
|
187
|
+
params?: unknown
|
|
188
|
+
): Promise<{
|
|
189
|
+
result: {
|
|
190
|
+
addresses: Array<{
|
|
191
|
+
address: string
|
|
192
|
+
publicKey: string
|
|
193
|
+
purpose: 'payment' | 'ordinals' | 'stacks'
|
|
194
|
+
addressType: 'p2tr' | 'p2wpkh' | 'p2sh-p2wpkh' | 'stacks'
|
|
195
|
+
}>
|
|
196
|
+
}
|
|
197
|
+
}>
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Sign a PSBT
|
|
201
|
+
*/
|
|
202
|
+
request(
|
|
203
|
+
method: 'signPsbt',
|
|
204
|
+
params: {
|
|
205
|
+
psbt: string // base64 encoded PSBT
|
|
206
|
+
signInputs: Record<string, number[]>
|
|
207
|
+
broadcast?: boolean
|
|
208
|
+
}
|
|
209
|
+
): Promise<{
|
|
210
|
+
result: {
|
|
211
|
+
psbt: string // base64 encoded signed PSBT
|
|
212
|
+
txid?: string // if broadcast: true
|
|
213
|
+
}
|
|
214
|
+
}>
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Sign a message
|
|
218
|
+
*/
|
|
219
|
+
request(
|
|
220
|
+
method: 'signMessage',
|
|
221
|
+
params: {
|
|
222
|
+
address: string
|
|
223
|
+
message: string
|
|
224
|
+
protocol?: 'ECDSA' | 'BIP322'
|
|
225
|
+
}
|
|
226
|
+
): Promise<{
|
|
227
|
+
result: {
|
|
228
|
+
signature: string
|
|
229
|
+
messageHash: string
|
|
230
|
+
address: string
|
|
231
|
+
}
|
|
232
|
+
}>
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Send BTC
|
|
236
|
+
*/
|
|
237
|
+
request(
|
|
238
|
+
method: 'sendTransfer',
|
|
239
|
+
params: {
|
|
240
|
+
recipients: Array<{ address: string; amount: number }>
|
|
241
|
+
}
|
|
242
|
+
): Promise<{
|
|
243
|
+
result: { txid: string }
|
|
244
|
+
}>
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Leather wallet API interface
|
|
249
|
+
*
|
|
250
|
+
* Official API: https://leather.gitbook.io/developers/bitcoin/connect
|
|
251
|
+
*/
|
|
252
|
+
export interface LeatherAPI {
|
|
253
|
+
/**
|
|
254
|
+
* Request wallet connection and get addresses
|
|
255
|
+
*/
|
|
256
|
+
request(
|
|
257
|
+
method: 'getAddresses'
|
|
258
|
+
): Promise<{
|
|
259
|
+
result: {
|
|
260
|
+
addresses: Array<{
|
|
261
|
+
symbol: 'BTC' | 'STX'
|
|
262
|
+
type?: 'p2wpkh' | 'p2tr'
|
|
263
|
+
address: string
|
|
264
|
+
publicKey: string
|
|
265
|
+
derivationPath?: string
|
|
266
|
+
}>
|
|
267
|
+
}
|
|
268
|
+
}>
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Sign a PSBT
|
|
272
|
+
*/
|
|
273
|
+
request(
|
|
274
|
+
method: 'signPsbt',
|
|
275
|
+
params: {
|
|
276
|
+
hex: string // hex encoded PSBT
|
|
277
|
+
account?: number
|
|
278
|
+
allowedSighash?: number[]
|
|
279
|
+
signAtIndex?: number | number[]
|
|
280
|
+
broadcast?: boolean
|
|
281
|
+
}
|
|
282
|
+
): Promise<{
|
|
283
|
+
result: {
|
|
284
|
+
hex: string // hex encoded signed PSBT
|
|
285
|
+
txid?: string // if broadcast: true
|
|
286
|
+
}
|
|
287
|
+
}>
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Sign a message
|
|
291
|
+
*/
|
|
292
|
+
request(
|
|
293
|
+
method: 'signMessage',
|
|
294
|
+
params: {
|
|
295
|
+
message: string
|
|
296
|
+
paymentType?: 'p2wpkh' | 'p2tr'
|
|
297
|
+
account?: number
|
|
298
|
+
}
|
|
299
|
+
): Promise<{
|
|
300
|
+
result: {
|
|
301
|
+
signature: string
|
|
302
|
+
address: string
|
|
303
|
+
message: string
|
|
304
|
+
}
|
|
305
|
+
}>
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Send BTC
|
|
309
|
+
*/
|
|
310
|
+
request(
|
|
311
|
+
method: 'sendTransfer',
|
|
312
|
+
params: {
|
|
313
|
+
recipients: Array<{ address: string; amount: string }>
|
|
314
|
+
account?: number
|
|
315
|
+
}
|
|
316
|
+
): Promise<{
|
|
317
|
+
result: { txid: string }
|
|
318
|
+
}>
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Global window interface for Bitcoin wallets
|
|
178
323
|
*/
|
|
179
324
|
declare global {
|
|
180
325
|
interface Window {
|
|
181
326
|
unisat?: UnisatAPI
|
|
327
|
+
XverseProviders?: {
|
|
328
|
+
BitcoinProvider?: XverseAPI
|
|
329
|
+
}
|
|
330
|
+
LeatherProvider?: LeatherAPI
|
|
331
|
+
btc?: LeatherAPI // Alternative Leather injection
|
|
332
|
+
okxwallet?: {
|
|
333
|
+
bitcoin?: UnisatAPI // OKX uses same API as Unisat
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Wrapper to adapt Xverse API to UnisatAPI interface
|
|
340
|
+
*/
|
|
341
|
+
export function createXverseWrapper(xverse: XverseAPI): UnisatAPI {
|
|
342
|
+
let cachedAddress: string | undefined
|
|
343
|
+
let cachedPublicKey: string | undefined
|
|
344
|
+
|
|
345
|
+
return {
|
|
346
|
+
async requestAccounts(): Promise<string[]> {
|
|
347
|
+
const response = await xverse.request('getAddresses')
|
|
348
|
+
const btcAddresses = response.result.addresses.filter(
|
|
349
|
+
(a) => a.purpose === 'payment' || a.purpose === 'ordinals'
|
|
350
|
+
)
|
|
351
|
+
if (btcAddresses.length > 0) {
|
|
352
|
+
// Prefer Taproot (ordinals) address
|
|
353
|
+
const taprootAddr = btcAddresses.find((a) => a.addressType === 'p2tr')
|
|
354
|
+
const primaryAddr = taprootAddr || btcAddresses[0]
|
|
355
|
+
cachedAddress = primaryAddr.address
|
|
356
|
+
cachedPublicKey = primaryAddr.publicKey
|
|
357
|
+
}
|
|
358
|
+
return btcAddresses.map((a) => a.address)
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
async getAccounts(): Promise<string[]> {
|
|
362
|
+
if (cachedAddress) return [cachedAddress]
|
|
363
|
+
return this.requestAccounts()
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
async getPublicKey(): Promise<string> {
|
|
367
|
+
if (cachedPublicKey) return cachedPublicKey
|
|
368
|
+
await this.requestAccounts()
|
|
369
|
+
return cachedPublicKey || ''
|
|
370
|
+
},
|
|
371
|
+
|
|
372
|
+
async getBalance(): Promise<{ confirmed: number; unconfirmed: number; total: number }> {
|
|
373
|
+
// Xverse doesn't expose balance directly, return 0s
|
|
374
|
+
// Balance should be fetched from external API
|
|
375
|
+
return { confirmed: 0, unconfirmed: 0, total: 0 }
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
async signPsbt(psbtHex: string, options?: SignPsbtOptions): Promise<string> {
|
|
379
|
+
// Convert hex to base64 for Xverse
|
|
380
|
+
const psbtBase64 = Buffer.from(psbtHex, 'hex').toString('base64')
|
|
381
|
+
const signInputs: Record<string, number[]> = {}
|
|
382
|
+
|
|
383
|
+
if (options?.toSignInputs && cachedAddress) {
|
|
384
|
+
signInputs[cachedAddress] = options.toSignInputs.map((i) => i.index)
|
|
385
|
+
} else if (cachedAddress) {
|
|
386
|
+
// Sign all inputs by default
|
|
387
|
+
signInputs[cachedAddress] = [0]
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const response = await xverse.request('signPsbt', {
|
|
391
|
+
psbt: psbtBase64,
|
|
392
|
+
signInputs,
|
|
393
|
+
broadcast: false,
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
// Convert back to hex
|
|
397
|
+
return Buffer.from(response.result.psbt, 'base64').toString('hex')
|
|
398
|
+
},
|
|
399
|
+
|
|
400
|
+
async signMessage(message: string, type?: 'ecdsa' | 'bip322-simple'): Promise<string> {
|
|
401
|
+
if (!cachedAddress) {
|
|
402
|
+
await this.requestAccounts()
|
|
403
|
+
}
|
|
404
|
+
const response = await xverse.request('signMessage', {
|
|
405
|
+
address: cachedAddress!,
|
|
406
|
+
message,
|
|
407
|
+
protocol: type === 'bip322-simple' ? 'BIP322' : 'ECDSA',
|
|
408
|
+
})
|
|
409
|
+
return response.result.signature
|
|
410
|
+
},
|
|
411
|
+
|
|
412
|
+
async pushTx(_rawTx: string): Promise<string> {
|
|
413
|
+
// Xverse requires using signPsbt with broadcast: true
|
|
414
|
+
throw new Error('Use signPsbt with broadcast: true for Xverse')
|
|
415
|
+
},
|
|
416
|
+
|
|
417
|
+
async getNetwork(): Promise<BitcoinNetwork> {
|
|
418
|
+
// Xverse doesn't expose network, assume mainnet
|
|
419
|
+
return 'livenet'
|
|
420
|
+
},
|
|
421
|
+
|
|
422
|
+
async switchNetwork(_network: BitcoinNetwork): Promise<void> {
|
|
423
|
+
throw new Error('Network switching not supported by Xverse')
|
|
424
|
+
},
|
|
425
|
+
|
|
426
|
+
async getChain(): Promise<{ enum: string; name: string }> {
|
|
427
|
+
return { enum: 'BITCOIN_MAINNET', name: 'Bitcoin Mainnet' }
|
|
428
|
+
},
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Wrapper to adapt Leather API to UnisatAPI interface
|
|
434
|
+
*/
|
|
435
|
+
export function createLeatherWrapper(leather: LeatherAPI): UnisatAPI {
|
|
436
|
+
let cachedAddress: string | undefined
|
|
437
|
+
let cachedPublicKey: string | undefined
|
|
438
|
+
|
|
439
|
+
return {
|
|
440
|
+
async requestAccounts(): Promise<string[]> {
|
|
441
|
+
const response = await leather.request('getAddresses')
|
|
442
|
+
const btcAddresses = response.result.addresses.filter((a) => a.symbol === 'BTC')
|
|
443
|
+
if (btcAddresses.length > 0) {
|
|
444
|
+
// Prefer Taproot address
|
|
445
|
+
const taprootAddr = btcAddresses.find((a) => a.type === 'p2tr')
|
|
446
|
+
const primaryAddr = taprootAddr || btcAddresses[0]
|
|
447
|
+
cachedAddress = primaryAddr.address
|
|
448
|
+
cachedPublicKey = primaryAddr.publicKey
|
|
449
|
+
}
|
|
450
|
+
return btcAddresses.map((a) => a.address)
|
|
451
|
+
},
|
|
452
|
+
|
|
453
|
+
async getAccounts(): Promise<string[]> {
|
|
454
|
+
if (cachedAddress) return [cachedAddress]
|
|
455
|
+
return this.requestAccounts()
|
|
456
|
+
},
|
|
457
|
+
|
|
458
|
+
async getPublicKey(): Promise<string> {
|
|
459
|
+
if (cachedPublicKey) return cachedPublicKey
|
|
460
|
+
await this.requestAccounts()
|
|
461
|
+
return cachedPublicKey || ''
|
|
462
|
+
},
|
|
463
|
+
|
|
464
|
+
async getBalance(): Promise<{ confirmed: number; unconfirmed: number; total: number }> {
|
|
465
|
+
// Leather doesn't expose balance directly
|
|
466
|
+
return { confirmed: 0, unconfirmed: 0, total: 0 }
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
async signPsbt(psbtHex: string, options?: SignPsbtOptions): Promise<string> {
|
|
470
|
+
const signAtIndex = options?.toSignInputs?.map((i) => i.index)
|
|
471
|
+
|
|
472
|
+
const response = await leather.request('signPsbt', {
|
|
473
|
+
hex: psbtHex,
|
|
474
|
+
signAtIndex,
|
|
475
|
+
broadcast: false,
|
|
476
|
+
})
|
|
477
|
+
|
|
478
|
+
return response.result.hex
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
async signMessage(message: string, type?: 'ecdsa' | 'bip322-simple'): Promise<string> {
|
|
482
|
+
const response = await leather.request('signMessage', {
|
|
483
|
+
message,
|
|
484
|
+
paymentType: 'p2tr',
|
|
485
|
+
})
|
|
486
|
+
return response.result.signature
|
|
487
|
+
},
|
|
488
|
+
|
|
489
|
+
async pushTx(_rawTx: string): Promise<string> {
|
|
490
|
+
throw new Error('Use signPsbt with broadcast: true for Leather')
|
|
491
|
+
},
|
|
492
|
+
|
|
493
|
+
async getNetwork(): Promise<BitcoinNetwork> {
|
|
494
|
+
return 'livenet'
|
|
495
|
+
},
|
|
496
|
+
|
|
497
|
+
async switchNetwork(_network: BitcoinNetwork): Promise<void> {
|
|
498
|
+
throw new Error('Network switching not supported by Leather')
|
|
499
|
+
},
|
|
500
|
+
|
|
501
|
+
async getChain(): Promise<{ enum: string; name: string }> {
|
|
502
|
+
return { enum: 'BITCOIN_MAINNET', name: 'Bitcoin Mainnet' }
|
|
503
|
+
},
|
|
182
504
|
}
|
|
183
505
|
}
|
|
184
506
|
|
|
@@ -195,11 +517,15 @@ export function getBitcoinProvider(wallet: BitcoinWalletName): UnisatAPI | undef
|
|
|
195
517
|
return window.unisat
|
|
196
518
|
case 'okx':
|
|
197
519
|
// OKX wallet has bitcoin namespace
|
|
198
|
-
return
|
|
199
|
-
case 'xverse':
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
520
|
+
return window.okxwallet?.bitcoin
|
|
521
|
+
case 'xverse': {
|
|
522
|
+
const xverse = window.XverseProviders?.BitcoinProvider
|
|
523
|
+
return xverse ? createXverseWrapper(xverse) : undefined
|
|
524
|
+
}
|
|
525
|
+
case 'leather': {
|
|
526
|
+
const leather = window.LeatherProvider || window.btc
|
|
527
|
+
return leather ? createLeatherWrapper(leather) : undefined
|
|
528
|
+
}
|
|
203
529
|
default:
|
|
204
530
|
return undefined
|
|
205
531
|
}
|
|
@@ -219,18 +545,17 @@ export function detectBitcoinWallets(): BitcoinWalletName[] {
|
|
|
219
545
|
wallets.push('unisat')
|
|
220
546
|
}
|
|
221
547
|
|
|
222
|
-
if (
|
|
548
|
+
if (window.okxwallet?.bitcoin) {
|
|
223
549
|
wallets.push('okx')
|
|
224
550
|
}
|
|
225
551
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
// }
|
|
552
|
+
if (window.XverseProviders?.BitcoinProvider) {
|
|
553
|
+
wallets.push('xverse')
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (window.LeatherProvider || window.btc) {
|
|
557
|
+
wallets.push('leather')
|
|
558
|
+
}
|
|
234
559
|
|
|
235
560
|
return wallets
|
|
236
561
|
}
|
|
@@ -28,6 +28,8 @@ import type {
|
|
|
28
28
|
CosmosAlgo,
|
|
29
29
|
StdSignature,
|
|
30
30
|
PubKey,
|
|
31
|
+
OfflineSigner,
|
|
32
|
+
OfflineAminoSigner,
|
|
31
33
|
} from './types'
|
|
32
34
|
import {
|
|
33
35
|
cosmosPublicKeyToHex,
|
|
@@ -477,7 +479,7 @@ export function createMockCosmosProvider(
|
|
|
477
479
|
bech32Address: address,
|
|
478
480
|
}
|
|
479
481
|
|
|
480
|
-
|
|
482
|
+
const mockKeplr: Keplr = {
|
|
481
483
|
async enable() {
|
|
482
484
|
if (config.shouldFailConnect) {
|
|
483
485
|
throw new Error('Request rejected by user')
|
|
@@ -554,26 +556,26 @@ export function createMockCosmosProvider(
|
|
|
554
556
|
return true
|
|
555
557
|
},
|
|
556
558
|
|
|
557
|
-
async getOfflineSignerAuto(_chainId: string) {
|
|
559
|
+
async getOfflineSignerAuto(_chainId: string): Promise<OfflineSigner> {
|
|
558
560
|
return {
|
|
559
|
-
async
|
|
561
|
+
getAccounts: async (): Promise<readonly CosmosAccountData[]> => {
|
|
560
562
|
return [{ address, algo, pubkey: mockPubKey }]
|
|
561
563
|
},
|
|
562
|
-
async
|
|
563
|
-
return
|
|
564
|
+
signDirect: async (signerAddress: string, signDoc: DirectSignDoc): Promise<DirectSignResponse> => {
|
|
565
|
+
return mockKeplr.signDirect(chainId, signerAddress, signDoc)
|
|
564
566
|
},
|
|
565
|
-
}
|
|
567
|
+
}
|
|
566
568
|
},
|
|
567
569
|
|
|
568
|
-
async getOfflineSignerOnlyAmino(_chainId: string) {
|
|
570
|
+
async getOfflineSignerOnlyAmino(_chainId: string): Promise<OfflineAminoSigner> {
|
|
569
571
|
return {
|
|
570
|
-
async
|
|
572
|
+
getAccounts: async (): Promise<readonly CosmosAccountData[]> => {
|
|
571
573
|
return [{ address, algo, pubkey: mockPubKey }]
|
|
572
574
|
},
|
|
573
|
-
async
|
|
574
|
-
return
|
|
575
|
+
signAmino: async (signerAddress: string, signDoc: StdSignDoc): Promise<AminoSignResponse> => {
|
|
576
|
+
return mockKeplr.signAmino(chainId, signerAddress, signDoc)
|
|
575
577
|
},
|
|
576
|
-
}
|
|
578
|
+
}
|
|
577
579
|
},
|
|
578
580
|
|
|
579
581
|
async getOfflineSigner(chainId: string) {
|
|
@@ -583,7 +585,9 @@ export function createMockCosmosProvider(
|
|
|
583
585
|
async experimentalSuggestChain() {
|
|
584
586
|
// No-op for mock
|
|
585
587
|
},
|
|
586
|
-
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
return mockKeplr
|
|
587
591
|
}
|
|
588
592
|
|
|
589
593
|
/**
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
* - @ledgerhq/hw-app-solana (for Solana)
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
+
import { RLP } from '@ethereumjs/rlp'
|
|
31
32
|
import type {
|
|
32
33
|
ChainId,
|
|
33
34
|
HexString,
|
|
@@ -498,20 +499,89 @@ export class LedgerWalletAdapter extends BaseWalletAdapter {
|
|
|
498
499
|
|
|
499
500
|
/**
|
|
500
501
|
* Build raw Ethereum transaction for Ledger signing
|
|
502
|
+
*
|
|
503
|
+
* @throws {HardwareWalletError} Always throws - RLP encoding not yet implemented
|
|
504
|
+
*
|
|
505
|
+
* @remarks
|
|
506
|
+
* Proper Ethereum transaction signing requires RLP (Recursive Length Prefix)
|
|
507
|
+
* encoding. This is a non-trivial implementation that requires either:
|
|
508
|
+
*
|
|
509
|
+
* 1. Adding @ethereumjs/rlp dependency
|
|
510
|
+
* 2. Adding @ethersproject/transactions dependency
|
|
511
|
+
* 3. Manual RLP implementation
|
|
512
|
+
*
|
|
513
|
+
* For now, this method throws to prevent silent failures. To enable
|
|
514
|
+
* Ledger transaction signing, implement proper RLP encoding.
|
|
515
|
+
*
|
|
516
|
+
* @see https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/
|
|
501
517
|
*/
|
|
502
518
|
private buildRawEthereumTx(tx: HardwareEthereumTx): string {
|
|
503
|
-
//
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
519
|
+
// Helper to convert hex string to bytes, handling empty values
|
|
520
|
+
const hexToBytes = (hex: HexString | undefined): Uint8Array => {
|
|
521
|
+
if (!hex || hex === '0x' || hex === '0x0' || hex === '0x00') {
|
|
522
|
+
return new Uint8Array(0)
|
|
523
|
+
}
|
|
524
|
+
// Remove 0x prefix and pad to even length
|
|
525
|
+
let cleanHex = hex.slice(2)
|
|
526
|
+
if (cleanHex.length % 2 !== 0) {
|
|
527
|
+
cleanHex = '0' + cleanHex
|
|
528
|
+
}
|
|
529
|
+
const bytes = new Uint8Array(cleanHex.length / 2)
|
|
530
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
531
|
+
bytes[i] = parseInt(cleanHex.slice(i * 2, i * 2 + 2), 16)
|
|
532
|
+
}
|
|
533
|
+
return bytes
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// Determine if EIP-1559 transaction (type 2)
|
|
537
|
+
const isEIP1559 = tx.maxFeePerGas !== undefined && tx.maxPriorityFeePerGas !== undefined
|
|
538
|
+
|
|
539
|
+
if (isEIP1559) {
|
|
540
|
+
// EIP-1559 transaction (type 2):
|
|
541
|
+
// 0x02 || RLP([chainId, nonce, maxPriorityFeePerGas, maxFeePerGas,
|
|
542
|
+
// gasLimit, to, value, data, accessList])
|
|
543
|
+
const txData = [
|
|
544
|
+
hexToBytes(`0x${tx.chainId.toString(16)}`), // chainId
|
|
545
|
+
hexToBytes(tx.nonce), // nonce
|
|
546
|
+
hexToBytes(tx.maxPriorityFeePerGas), // maxPriorityFeePerGas
|
|
547
|
+
hexToBytes(tx.maxFeePerGas), // maxFeePerGas
|
|
548
|
+
hexToBytes(tx.gasLimit), // gasLimit
|
|
549
|
+
hexToBytes(tx.to as HexString), // to
|
|
550
|
+
hexToBytes(tx.value), // value
|
|
551
|
+
hexToBytes(tx.data), // data
|
|
552
|
+
[], // accessList (empty)
|
|
553
|
+
]
|
|
554
|
+
const encoded = RLP.encode(txData)
|
|
555
|
+
// Prepend type byte (0x02)
|
|
556
|
+
const result = new Uint8Array(1 + encoded.length)
|
|
557
|
+
result[0] = 0x02
|
|
558
|
+
result.set(encoded, 1)
|
|
559
|
+
return '0x' + Buffer.from(result).toString('hex')
|
|
560
|
+
} else {
|
|
561
|
+
// Legacy transaction (type 0):
|
|
562
|
+
// RLP([nonce, gasPrice, gasLimit, to, value, data, v, r, s])
|
|
563
|
+
// For signing, we use chain ID for v and empty r, s (EIP-155)
|
|
564
|
+
if (!tx.gasPrice) {
|
|
565
|
+
throw new HardwareWalletError(
|
|
566
|
+
'Legacy transaction requires gasPrice',
|
|
567
|
+
HardwareErrorCode.INVALID_PARAMS,
|
|
568
|
+
'ledger'
|
|
569
|
+
)
|
|
570
|
+
}
|
|
571
|
+
const txData = [
|
|
572
|
+
hexToBytes(tx.nonce), // nonce
|
|
573
|
+
hexToBytes(tx.gasPrice), // gasPrice
|
|
574
|
+
hexToBytes(tx.gasLimit), // gasLimit
|
|
575
|
+
hexToBytes(tx.to as HexString), // to
|
|
576
|
+
hexToBytes(tx.value), // value
|
|
577
|
+
hexToBytes(tx.data), // data
|
|
578
|
+
hexToBytes(`0x${tx.chainId.toString(16)}`), // v (chainId for EIP-155)
|
|
579
|
+
new Uint8Array(0), // r (empty for unsigned)
|
|
580
|
+
new Uint8Array(0), // s (empty for unsigned)
|
|
581
|
+
]
|
|
582
|
+
const encoded = RLP.encode(txData)
|
|
583
|
+
return '0x' + Buffer.from(encoded).toString('hex')
|
|
584
|
+
}
|
|
515
585
|
}
|
|
516
586
|
|
|
517
587
|
/**
|
|
@@ -254,6 +254,8 @@ export const HardwareErrorCode = {
|
|
|
254
254
|
UNSUPPORTED: 'HARDWARE_UNSUPPORTED',
|
|
255
255
|
/** Invalid derivation path */
|
|
256
256
|
INVALID_PATH: 'HARDWARE_INVALID_PATH',
|
|
257
|
+
/** Invalid parameters provided */
|
|
258
|
+
INVALID_PARAMS: 'HARDWARE_INVALID_PARAMS',
|
|
257
259
|
} as const
|
|
258
260
|
|
|
259
261
|
export type HardwareErrorCodeType = typeof HardwareErrorCode[keyof typeof HardwareErrorCode]
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 RECTOR Labs
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|