@swapkit/wallet-hardware 4.9.5 → 4.9.7
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/chunk-3gajmdgz.js +4 -0
- package/dist/chunk-3gajmdgz.js.map +10 -0
- package/dist/chunk-jttnd211.js +4 -0
- package/dist/chunk-jttnd211.js.map +10 -0
- package/dist/keepkey/index.cjs +2 -2
- package/dist/keepkey/index.cjs.map +3 -3
- package/dist/keepkey/index.js +2 -2
- package/dist/keepkey/index.js.map +3 -3
- package/dist/ledger/index.cjs +3 -3
- package/dist/ledger/index.cjs.map +3 -3
- package/dist/ledger/index.js +3 -3
- package/dist/ledger/index.js.map +3 -3
- package/dist/trezor/index.cjs +2 -2
- package/dist/trezor/index.cjs.map +3 -3
- package/dist/trezor/index.js +2 -2
- package/dist/trezor/index.js.map +3 -3
- package/dist/types/ledger/clients/utxo-psbt.d.ts.map +1 -1
- package/dist/types/trezor/index.d.ts.map +1 -1
- package/package.json +7 -5
- package/src/keepkey/chains/utxo.ts +2 -2
- package/src/ledger/clients/utxo-psbt.ts +30 -10
- package/src/ledger/index.ts +2 -2
- package/src/trezor/index.ts +333 -27
- package/dist/chunk-9jd3dhjv.js +0 -4
- package/dist/chunk-9jd3dhjv.js.map +0 -10
- package/dist/chunk-m08x6an5.js +0 -4
- package/dist/chunk-m08x6an5.js.map +0 -10
package/src/trezor/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { HDKey } from "@scure/bip32";
|
|
1
2
|
import {
|
|
2
3
|
Chain,
|
|
3
4
|
type DerivationPathArray,
|
|
@@ -13,6 +14,7 @@ import {
|
|
|
13
14
|
import {
|
|
14
15
|
assertDerivationIndex,
|
|
15
16
|
createHDWalletHelpers,
|
|
17
|
+
deriveAddressesFromXpub,
|
|
16
18
|
getNetworkForChain,
|
|
17
19
|
getUTXOAccountIndexFromPath,
|
|
18
20
|
getUTXOAccountPath,
|
|
@@ -24,6 +26,62 @@ import type { BTCNetwork, PCZT, Transaction, ZcashTransaction } from "@swapkit/u
|
|
|
24
26
|
import { NETWORKS, ZcashConsensusBranchId, ZcashVersionGroupId } from "@swapkit/utxo-signer";
|
|
25
27
|
import { createWallet, getWalletSupportedChains } from "@swapkit/wallet-core";
|
|
26
28
|
|
|
29
|
+
type TrezorBip32Derivation = [Uint8Array, { fingerprint: number; path: number[] }];
|
|
30
|
+
type TrezorCoreMode = "auto" | "iframe" | "popup" | "suite-desktop" | "suite-web";
|
|
31
|
+
type TrezorTransport = "BridgeTransport" | "WebUsbTransport" | "NodeUsbTransport";
|
|
32
|
+
type TrezorExtendedPublicKeyInfo = {
|
|
33
|
+
accountIndex: number;
|
|
34
|
+
chainCode?: string;
|
|
35
|
+
depth?: number;
|
|
36
|
+
fingerprint?: number;
|
|
37
|
+
path: string;
|
|
38
|
+
publicKey?: string;
|
|
39
|
+
xpub: string;
|
|
40
|
+
xpubSegwit?: string;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const TREZOR_CORE_MODES = new Set<TrezorCoreMode>(["auto", "iframe", "popup", "suite-desktop", "suite-web"]);
|
|
44
|
+
const TREZOR_TRANSPORTS = new Set<TrezorTransport>(["BridgeTransport", "WebUsbTransport", "NodeUsbTransport"]);
|
|
45
|
+
const trezorXpubCache = new Map<string, TrezorExtendedPublicKeyInfo>();
|
|
46
|
+
let trezorSessionDispose: Promise<void> | undefined;
|
|
47
|
+
|
|
48
|
+
async function disconnectTrezorSession() {
|
|
49
|
+
trezorXpubCache.clear();
|
|
50
|
+
|
|
51
|
+
const dispose = (async () => {
|
|
52
|
+
try {
|
|
53
|
+
const TrezorConnect = (await import("@trezor/connect-web")).default;
|
|
54
|
+
await TrezorConnect.dispose();
|
|
55
|
+
} catch {
|
|
56
|
+
// Ignore stale or already-disposed sessions.
|
|
57
|
+
}
|
|
58
|
+
})();
|
|
59
|
+
|
|
60
|
+
trezorSessionDispose = dispose;
|
|
61
|
+
await dispose;
|
|
62
|
+
|
|
63
|
+
if (trezorSessionDispose === dispose) {
|
|
64
|
+
trezorSessionDispose = undefined;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function normalizeTrezorCoreMode(coreMode: unknown): TrezorCoreMode | undefined {
|
|
69
|
+
return typeof coreMode === "string" && TREZOR_CORE_MODES.has(coreMode as TrezorCoreMode)
|
|
70
|
+
? (coreMode as TrezorCoreMode)
|
|
71
|
+
: undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function normalizeTrezorTransports(transports: unknown): TrezorTransport[] | undefined {
|
|
75
|
+
if (!Array.isArray(transports)) return undefined;
|
|
76
|
+
|
|
77
|
+
const normalized = transports.filter(
|
|
78
|
+
(transport): transport is TrezorTransport =>
|
|
79
|
+
typeof transport === "string" && TREZOR_TRANSPORTS.has(transport as TrezorTransport),
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
27
85
|
function decodeOpReturnData(script: Uint8Array): string | null {
|
|
28
86
|
if (script.length < 2 || script[0] !== 0x6a) return null;
|
|
29
87
|
const dataLen = script[1];
|
|
@@ -50,6 +108,48 @@ function hardenDerivationPath(derivationPath: DerivationPathArray): number[] {
|
|
|
50
108
|
);
|
|
51
109
|
}
|
|
52
110
|
|
|
111
|
+
function isTrezorBip32Derivation(value: unknown): value is TrezorBip32Derivation {
|
|
112
|
+
return (
|
|
113
|
+
Array.isArray(value) &&
|
|
114
|
+
value[0] instanceof Uint8Array &&
|
|
115
|
+
typeof value[1] === "object" &&
|
|
116
|
+
value[1] !== null &&
|
|
117
|
+
typeof (value[1] as { fingerprint?: unknown }).fingerprint === "number" &&
|
|
118
|
+
Array.isArray((value[1] as { path?: unknown }).path)
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function getFirstBip32Derivation(input: { bip32Derivation?: unknown }): TrezorBip32Derivation | undefined {
|
|
123
|
+
if (!Array.isArray(input.bip32Derivation)) return undefined;
|
|
124
|
+
const [firstDerivation] = input.bip32Derivation;
|
|
125
|
+
|
|
126
|
+
return isTrezorBip32Derivation(firstDerivation) ? firstDerivation : undefined;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function getPrevoutAmount(input: {
|
|
130
|
+
index?: number;
|
|
131
|
+
nonWitnessUtxo?: { outputs?: Array<{ amount?: bigint | number }> };
|
|
132
|
+
witnessUtxo?: { amount?: bigint | number };
|
|
133
|
+
}) {
|
|
134
|
+
if (input.witnessUtxo?.amount !== undefined) return input.witnessUtxo.amount.toString();
|
|
135
|
+
|
|
136
|
+
const prevout = input.index !== undefined ? input.nonWitnessUtxo?.outputs?.[input.index] : undefined;
|
|
137
|
+
if (prevout?.amount !== undefined) return prevout.amount.toString();
|
|
138
|
+
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function normalizeTrezorSignature(signatureHex: string) {
|
|
143
|
+
const signature = Buffer.from(signatureHex, "hex");
|
|
144
|
+
const derLength = signature[1] !== undefined ? signature[1] + 2 : undefined;
|
|
145
|
+
|
|
146
|
+
if (derLength !== undefined && signature.length === derLength) {
|
|
147
|
+
return new Uint8Array([...signature, 0x01]);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return new Uint8Array(signature);
|
|
151
|
+
}
|
|
152
|
+
|
|
53
153
|
function buildPCZTInputsForTrezor(
|
|
54
154
|
pczt: PCZT,
|
|
55
155
|
address_n: number[],
|
|
@@ -206,8 +306,27 @@ function buildUtxoOutputsForTrezor(
|
|
|
206
306
|
const outputAddress = tx.getOutputAddress(i, network);
|
|
207
307
|
|
|
208
308
|
if (!outputAddress) {
|
|
209
|
-
|
|
210
|
-
|
|
309
|
+
const opReturnData = output.script ? decodeOpReturnData(output.script) : null;
|
|
310
|
+
if (opReturnData !== null || memo) {
|
|
311
|
+
outputs.push({
|
|
312
|
+
amount: "0",
|
|
313
|
+
op_return_data: opReturnData ?? Buffer.from(memo).toString("hex"),
|
|
314
|
+
script_type: "PAYTOOPRETURN",
|
|
315
|
+
});
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
throw new SwapKitError({
|
|
320
|
+
errorKey: "wallet_trezor_failed_to_sign_transaction",
|
|
321
|
+
info: { chain, error: "Unable to decode output address from scriptPubkey" },
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (output.amount === undefined) {
|
|
326
|
+
throw new SwapKitError({
|
|
327
|
+
errorKey: "wallet_trezor_failed_to_sign_transaction",
|
|
328
|
+
info: { chain, error: "Output amount is missing" },
|
|
329
|
+
});
|
|
211
330
|
}
|
|
212
331
|
|
|
213
332
|
const isBch = chain === Chain.BitcoinCash;
|
|
@@ -218,8 +337,8 @@ function buildUtxoOutputsForTrezor(
|
|
|
218
337
|
|
|
219
338
|
outputs.push(
|
|
220
339
|
isChangeAddress
|
|
221
|
-
? { address_n, amount:
|
|
222
|
-
: { address: cashAddrWithPrefix, amount:
|
|
340
|
+
? { address_n, amount: output.amount.toString(), script_type: scriptType.output }
|
|
341
|
+
: { address: cashAddrWithPrefix, amount: output.amount.toString(), script_type: "PAYTOADDRESS" },
|
|
223
342
|
);
|
|
224
343
|
}
|
|
225
344
|
return outputs;
|
|
@@ -434,7 +553,7 @@ async function getTrezorWallet<T extends Chain>({
|
|
|
434
553
|
case Chain.Dash:
|
|
435
554
|
case Chain.Dogecoin:
|
|
436
555
|
case Chain.Litecoin: {
|
|
437
|
-
const { toCashAddress, getUtxoToolbox } = await import("@swapkit/toolboxes/utxo");
|
|
556
|
+
const { toCashAddress, getUtxoToolbox, stripPrefix } = await import("@swapkit/toolboxes/utxo");
|
|
438
557
|
const utxoChain = chain as UTXOChain;
|
|
439
558
|
const scriptType = getScriptType(derivationPath);
|
|
440
559
|
|
|
@@ -446,7 +565,11 @@ async function getTrezorWallet<T extends Chain>({
|
|
|
446
565
|
|
|
447
566
|
const getAddress = async (path: DerivationPathArray = derivationPath) => {
|
|
448
567
|
const TrezorConnect = (await import("@trezor/connect-web")).default;
|
|
449
|
-
const { success, payload } = await TrezorConnect.getAddress({
|
|
568
|
+
const { success, payload } = await TrezorConnect.getAddress({
|
|
569
|
+
coin,
|
|
570
|
+
path: derivationPathToString(path),
|
|
571
|
+
showOnTrezor: false,
|
|
572
|
+
});
|
|
450
573
|
|
|
451
574
|
if (!success) {
|
|
452
575
|
throw new SwapKitError({
|
|
@@ -456,19 +579,57 @@ async function getTrezorWallet<T extends Chain>({
|
|
|
456
579
|
}
|
|
457
580
|
|
|
458
581
|
if (chain === Chain.BitcoinCash) {
|
|
459
|
-
|
|
460
|
-
return toolbox.stripPrefix(payload.address);
|
|
582
|
+
return stripPrefix(payload.address);
|
|
461
583
|
}
|
|
462
584
|
|
|
463
585
|
return payload.address;
|
|
464
586
|
};
|
|
465
587
|
|
|
466
|
-
|
|
588
|
+
async function getAddressFromExtendedPublicKey() {
|
|
589
|
+
const accountInfo = await getExtendedPublicKeyInfo();
|
|
590
|
+
const addressIndex = Number(derivationPath[4] ?? 0);
|
|
591
|
+
const change = Boolean(derivationPath[3] ?? 0);
|
|
592
|
+
|
|
593
|
+
try {
|
|
594
|
+
// deriveAddressesFromXpub returns both external and change branches for each index.
|
|
595
|
+
const derivedAddress = deriveAddressesFromXpub({
|
|
596
|
+
accountIndex: accountInfo.accountIndex,
|
|
597
|
+
chain: utxoChain,
|
|
598
|
+
count: 1,
|
|
599
|
+
startIndex: addressIndex,
|
|
600
|
+
xpub: accountInfo.xpub,
|
|
601
|
+
}).find((derived) => derived.change === change && derived.index === addressIndex);
|
|
602
|
+
|
|
603
|
+
if (!derivedAddress) {
|
|
604
|
+
throw new SwapKitError({
|
|
605
|
+
errorKey: "wallet_trezor_failed_to_get_address",
|
|
606
|
+
info: { chain, error: "Unable to derive address from Trezor account public key" },
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return derivedAddress.address;
|
|
611
|
+
} catch (error) {
|
|
612
|
+
if (error instanceof SwapKitError) throw error;
|
|
613
|
+
|
|
614
|
+
throw new SwapKitError({
|
|
615
|
+
errorKey: "wallet_trezor_failed_to_get_address",
|
|
616
|
+
info: {
|
|
617
|
+
chain,
|
|
618
|
+
error: error instanceof Error ? error.message : "Unable to derive address from Trezor xpub",
|
|
619
|
+
},
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const address =
|
|
625
|
+
chain === Chain.Bitcoin || chain === Chain.Litecoin
|
|
626
|
+
? await getAddressFromExtendedPublicKey()
|
|
627
|
+
: await getAddress();
|
|
628
|
+
const baseToolbox = getUtxoToolbox(chain);
|
|
467
629
|
|
|
468
630
|
const signTransaction = async (tx: Transaction, inputs: UTXOType[], memo = "") => {
|
|
469
631
|
const TrezorConnect = (await import("@trezor/connect-web")).default;
|
|
470
632
|
const address_n = hardenDerivationPath(derivationPath);
|
|
471
|
-
const toolbox = getUtxoToolbox(chain as typeof Chain.BitcoinCash);
|
|
472
633
|
const network = getNetworkForChain(chain as UTXOChain);
|
|
473
634
|
|
|
474
635
|
const outputs = buildUtxoOutputsForTrezor(
|
|
@@ -480,7 +641,7 @@ async function getTrezorWallet<T extends Chain>({
|
|
|
480
641
|
chain,
|
|
481
642
|
scriptType,
|
|
482
643
|
toCashAddress,
|
|
483
|
-
|
|
644
|
+
stripPrefix,
|
|
484
645
|
);
|
|
485
646
|
|
|
486
647
|
const trezorInputs = inputs.map(({ hash, index, value }) => ({
|
|
@@ -504,13 +665,108 @@ async function getTrezorWallet<T extends Chain>({
|
|
|
504
665
|
});
|
|
505
666
|
};
|
|
506
667
|
|
|
668
|
+
const signPsbtTransaction = async (tx: Transaction): Promise<Transaction> => {
|
|
669
|
+
const TrezorConnect = (await import("@trezor/connect-web")).default;
|
|
670
|
+
const { hex: hexEncode } = await import("@scure/base");
|
|
671
|
+
const address_n = hardenDerivationPath(derivationPath);
|
|
672
|
+
const network = getNetworkForChain(chain as UTXOChain);
|
|
673
|
+
let fallbackPublicKey: Uint8Array | undefined;
|
|
674
|
+
|
|
675
|
+
async function getFallbackDerivation(): Promise<TrezorBip32Derivation> {
|
|
676
|
+
if (!fallbackPublicKey) {
|
|
677
|
+
const accountInfo = await getExtendedPublicKeyInfo();
|
|
678
|
+
const accountKey = HDKey.fromExtendedKey(accountInfo.xpub);
|
|
679
|
+
const leaf = accountKey.derive(`m/${Number(derivationPath[3] ?? 0)}/${Number(derivationPath[4] ?? 0)}`);
|
|
680
|
+
|
|
681
|
+
if (!leaf.publicKey) {
|
|
682
|
+
throw new SwapKitError({
|
|
683
|
+
errorKey: "wallet_trezor_failed_to_get_public_key",
|
|
684
|
+
info: { chain, error: "Unable to derive Trezor leaf public key from account xpub" },
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
fallbackPublicKey = leaf.publicKey;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
return [fallbackPublicKey, { fingerprint: 0, path: address_n }];
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const signerPubkeys: Uint8Array[] = [];
|
|
695
|
+
const trezorInputs = [];
|
|
696
|
+
|
|
697
|
+
for (let inputIndex = 0; inputIndex < tx.inputsLength; inputIndex++) {
|
|
698
|
+
const input = tx.getInput(inputIndex);
|
|
699
|
+
const existingDerivation = getFirstBip32Derivation(input);
|
|
700
|
+
const derivation = existingDerivation ?? (await getFallbackDerivation());
|
|
701
|
+
const amount = getPrevoutAmount(input);
|
|
702
|
+
|
|
703
|
+
if (!input.txid || input.index === undefined || !amount) {
|
|
704
|
+
throw new SwapKitError({
|
|
705
|
+
errorKey: "wallet_trezor_failed_to_sign_transaction",
|
|
706
|
+
info: { chain, error: `Input ${inputIndex} is missing prevout data required by Trezor` },
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
signerPubkeys[inputIndex] = derivation[0];
|
|
711
|
+
|
|
712
|
+
if (!existingDerivation) {
|
|
713
|
+
tx.updateInput(inputIndex, { bip32Derivation: [derivation] });
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
trezorInputs.push({
|
|
717
|
+
address_n: derivation[1].path,
|
|
718
|
+
amount,
|
|
719
|
+
prev_hash: hexEncode.encode(input.txid),
|
|
720
|
+
prev_index: input.index,
|
|
721
|
+
script_type: scriptType.input,
|
|
722
|
+
...(input.sequence !== undefined ? { sequence: input.sequence } : {}),
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const outputs = buildUtxoOutputsForTrezor(
|
|
727
|
+
tx,
|
|
728
|
+
network,
|
|
729
|
+
address_n,
|
|
730
|
+
address,
|
|
731
|
+
"",
|
|
732
|
+
chain,
|
|
733
|
+
scriptType,
|
|
734
|
+
toCashAddress,
|
|
735
|
+
stripPrefix,
|
|
736
|
+
);
|
|
737
|
+
|
|
738
|
+
const result = await TrezorConnect.signTransaction({
|
|
739
|
+
coin,
|
|
740
|
+
inputs: trezorInputs,
|
|
741
|
+
locktime: tx.lockTime,
|
|
742
|
+
outputs,
|
|
743
|
+
version: tx.version,
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
if (!result.success) {
|
|
747
|
+
const payload = result.payload as { error?: string; code?: string };
|
|
748
|
+
throw new SwapKitError({
|
|
749
|
+
errorKey: "wallet_trezor_failed_to_sign_transaction",
|
|
750
|
+
info: { chain, code: payload?.code ?? "unknown", error: payload?.error ?? "unknown", payload },
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
result.payload.signatures.forEach((signatureHex, inputIndex) => {
|
|
755
|
+
const pubkey = signerPubkeys[inputIndex];
|
|
756
|
+
if (!(signatureHex && pubkey)) return;
|
|
757
|
+
|
|
758
|
+
tx.updateInput(inputIndex, { partialSig: [[pubkey, normalizeTrezorSignature(signatureHex)]] });
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
return tx;
|
|
762
|
+
};
|
|
763
|
+
|
|
507
764
|
const signTransactionWithMultipleInputs = async (
|
|
508
765
|
tx: Transaction,
|
|
509
766
|
inputs: Array<{ hash: string; index: number; value: number; derivationIndex: number; isChange: boolean }>,
|
|
510
767
|
memo = "",
|
|
511
768
|
) => {
|
|
512
769
|
const TrezorConnect = (await import("@trezor/connect-web")).default;
|
|
513
|
-
const toolbox = await getUtxoToolbox(chain as typeof Chain.BitcoinCash);
|
|
514
770
|
const network = getNetworkForChain(chain as UTXOChain);
|
|
515
771
|
const baseAddressN = hardenDerivationPath(derivationPath.slice(0, 3) as DerivationPathArray);
|
|
516
772
|
|
|
@@ -523,7 +779,7 @@ async function getTrezorWallet<T extends Chain>({
|
|
|
523
779
|
chain,
|
|
524
780
|
scriptType,
|
|
525
781
|
toCashAddress,
|
|
526
|
-
|
|
782
|
+
stripPrefix,
|
|
527
783
|
);
|
|
528
784
|
|
|
529
785
|
const trezorInputs = inputs.map(({ hash, index: inputIndex, value, derivationIndex, isChange }) => {
|
|
@@ -629,12 +885,21 @@ async function getTrezorWallet<T extends Chain>({
|
|
|
629
885
|
return txHash;
|
|
630
886
|
};
|
|
631
887
|
|
|
632
|
-
const toolbox =
|
|
888
|
+
const toolbox =
|
|
889
|
+
chain === Chain.Bitcoin || chain === Chain.Litecoin
|
|
890
|
+
? await getUtxoToolbox(utxoChain, {
|
|
891
|
+
signer: { getAddress: async () => address, signTransaction: signPsbtTransaction },
|
|
892
|
+
})
|
|
893
|
+
: baseToolbox;
|
|
633
894
|
|
|
634
895
|
async function getExtendedPublicKeyInfo({ accountIndex }: { accountIndex?: number } = {}) {
|
|
635
896
|
const TrezorConnect = (await import("@trezor/connect-web")).default;
|
|
636
897
|
const resolvedAccountPath = getUTXOAccountPath({ accountIndex, chain: utxoChain, derivationPath });
|
|
637
898
|
const path = derivationPathToString(resolvedAccountPath);
|
|
899
|
+
const cacheKey = `${chain}:${path}`;
|
|
900
|
+
const cached = trezorXpubCache.get(cacheKey);
|
|
901
|
+
if (cached) return cached;
|
|
902
|
+
|
|
638
903
|
const { success, payload } = await TrezorConnect.getPublicKey({ coin, path });
|
|
639
904
|
|
|
640
905
|
if (!success) {
|
|
@@ -644,7 +909,7 @@ async function getTrezorWallet<T extends Chain>({
|
|
|
644
909
|
});
|
|
645
910
|
}
|
|
646
911
|
|
|
647
|
-
|
|
912
|
+
const info = {
|
|
648
913
|
accountIndex: getUTXOAccountIndexFromPath(resolvedAccountPath),
|
|
649
914
|
chainCode: payload.chainCode,
|
|
650
915
|
depth: payload.depth,
|
|
@@ -654,10 +919,13 @@ async function getTrezorWallet<T extends Chain>({
|
|
|
654
919
|
xpub: payload.xpub,
|
|
655
920
|
xpubSegwit: payload.xpubSegwit,
|
|
656
921
|
};
|
|
922
|
+
|
|
923
|
+
trezorXpubCache.set(cacheKey, info);
|
|
924
|
+
return info;
|
|
657
925
|
}
|
|
658
926
|
|
|
659
|
-
function getExtendedPublicKey() {
|
|
660
|
-
return getExtendedPublicKeyInfo();
|
|
927
|
+
function getExtendedPublicKey(params: { accountIndex?: number } = {}) {
|
|
928
|
+
return getExtendedPublicKeyInfo(params);
|
|
661
929
|
}
|
|
662
930
|
|
|
663
931
|
async function deriveAddressAtIndex({
|
|
@@ -791,19 +1059,55 @@ export const trezorWallet = createWallet({
|
|
|
791
1059
|
}
|
|
792
1060
|
|
|
793
1061
|
const TrezorConnect = (await import("@trezor/connect-web")).default;
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
1062
|
+
|
|
1063
|
+
const trezorConfig = SKConfig.get("integrations").trezor as Record<string, unknown> | undefined;
|
|
1064
|
+
const {
|
|
1065
|
+
connectSrc,
|
|
1066
|
+
coreMode,
|
|
1067
|
+
debug,
|
|
1068
|
+
interactionTimeout,
|
|
1069
|
+
lazyLoad,
|
|
1070
|
+
pendingTransportEvent,
|
|
1071
|
+
popup,
|
|
1072
|
+
transportReconnect,
|
|
1073
|
+
transports,
|
|
1074
|
+
...manifestConfig
|
|
1075
|
+
} = trezorConfig ?? {};
|
|
1076
|
+
const manifest = {
|
|
1077
|
+
...manifestConfig,
|
|
1078
|
+
appName: String(trezorConfig?.appName || "SwapKit"),
|
|
1079
|
+
appUrl: String(trezorConfig?.appUrl || ""),
|
|
1080
|
+
email: String(trezorConfig?.email || ""),
|
|
1081
|
+
};
|
|
1082
|
+
const isLocalhost =
|
|
1083
|
+
typeof globalThis.location !== "undefined" && ["localhost", "127.0.0.1"].includes(globalThis.location.hostname);
|
|
1084
|
+
const resolvedCoreMode = isLocalhost ? "popup" : normalizeTrezorCoreMode(coreMode);
|
|
1085
|
+
const resolvedTransports = isLocalhost ? ["WebUsbTransport" as const] : normalizeTrezorTransports(transports);
|
|
1086
|
+
|
|
1087
|
+
if (trezorSessionDispose) {
|
|
1088
|
+
await trezorSessionDispose;
|
|
802
1089
|
}
|
|
803
1090
|
|
|
1091
|
+
if (isLocalhost) {
|
|
1092
|
+
await TrezorConnect.dispose();
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
await TrezorConnect.init({
|
|
1096
|
+
connectSrc: connectSrc as string | undefined,
|
|
1097
|
+
coreMode: resolvedCoreMode,
|
|
1098
|
+
debug: debug as boolean | undefined,
|
|
1099
|
+
interactionTimeout: interactionTimeout as number | undefined,
|
|
1100
|
+
lazyLoad: isLocalhost ? false : ((lazyLoad as boolean | undefined) ?? true),
|
|
1101
|
+
manifest,
|
|
1102
|
+
pendingTransportEvent: pendingTransportEvent as boolean | undefined,
|
|
1103
|
+
popup: isLocalhost ? true : (popup as boolean | undefined),
|
|
1104
|
+
transportReconnect: transportReconnect as boolean | undefined,
|
|
1105
|
+
transports: resolvedTransports,
|
|
1106
|
+
});
|
|
1107
|
+
|
|
804
1108
|
const wallet = await getTrezorWallet({ chain, derivationPath });
|
|
805
1109
|
|
|
806
|
-
addChain({ ...wallet, chain, walletType });
|
|
1110
|
+
addChain({ ...wallet, chain, disconnect: disconnectTrezorSession, walletType });
|
|
807
1111
|
|
|
808
1112
|
return true;
|
|
809
1113
|
},
|
|
@@ -814,13 +1118,15 @@ export const trezorWallet = createWallet({
|
|
|
814
1118
|
[Chain.Base]: true,
|
|
815
1119
|
[Chain.Berachain]: true,
|
|
816
1120
|
[Chain.BinanceSmartChain]: true,
|
|
1121
|
+
[Chain.Bitcoin]: true,
|
|
817
1122
|
[Chain.Ethereum]: true,
|
|
818
1123
|
[Chain.Gnosis]: true,
|
|
1124
|
+
[Chain.Litecoin]: true,
|
|
819
1125
|
[Chain.Monad]: true,
|
|
820
1126
|
[Chain.Optimism]: true,
|
|
821
1127
|
[Chain.Polygon]: true,
|
|
822
1128
|
[Chain.XLayer]: true,
|
|
823
|
-
//
|
|
1129
|
+
// BCH/DASH/DOGE/ZEC: pending PSBT→TrezorConnect converter (V3 plan PR)
|
|
824
1130
|
},
|
|
825
1131
|
name: "connectTrezor",
|
|
826
1132
|
supportedChains: [
|
package/dist/chunk-9jd3dhjv.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{c as w}from"./chunk-3yr76n9s.js";import{d as E}from"./chunk-n05bv2n5.js";import{base64 as x}from"@scure/base";import{HDKey as f}from"@scure/bip32";import{derivationPathToString as m,getWalletFormatFor as y,SwapKitError as K}from"@swapkit/helpers";function b(z){switch(z){case"bech32":return"wpkh(@0/**)";case"p2sh":return"sh(wpkh(@0/**))";case"legacy":return"pkh(@0/**)";default:return"wpkh(@0/**)"}}function P(z){return typeof z==="string"?z:m(z)}function g(z){return z.replace(/^m\//,"").split("/").filter(Boolean).map((M)=>{let Y=M.endsWith("'"),Q=Number.parseInt(Y?M.slice(0,-1):M,10);return Y?(Q|2147483648)>>>0:Q})}var B=({chain:z})=>{return(M,Y)=>{let Q,$;async function Z(){if(!Q){let q=Y??await w(),{AppClient:G}=await import("ledger-bitcoin");Q=new G(q)}return Q}async function X(){if(!$)$=await(await Z()).getMasterFingerprint();return $}let R=M?P(M):"84'/0'/0'/0/0",H=R.split("/").slice(0,3).join("/"),N=R.split("/").slice(3),O=Number(N[0]??0),U=Number(N[1]??0),k=y(R),S=b(k),_,j;async function D(){let q=await Z(),G=await X();if(!_)_=await q.getExtendedPubkey(`m/${H}`);let{DefaultWalletPolicy:J}=await import("ledger-bitcoin"),I=new J(S,`[${G}/${H}]${_}`);return{app:q,fpr:G,policy:I,xpub:_}}async function C(){if(!j){let{xpub:q}=await D(),J=f.fromExtendedKey(q).derive(`m/${O}/${U}`);if(!J.publicKey)throw new K("wallet_ledger_get_address_error",{message:`Cannot derive leaf pubkey for ${z}`});j=J.publicKey}return j}return{connect:async()=>{await Z()},getAddress:async()=>{let{app:q,policy:G}=await D(),J=await q.getWalletAddress(G,null,O,U,!1);if(!J)throw new K("wallet_ledger_get_address_error",{message:`Cannot get ${z} address from ledger derivation path: ${R}`});return J},getExtendedPublicKey:async(q=`m/${H}`)=>{return(await Z()).getExtendedPubkey(q)},signTransaction:async(q)=>{let{app:G,policy:J,fpr:I}=await D(),F=Number.parseInt(I,16)>>>0,L=g(R),T=await C();for(let V=0;V<q.inputsLength;V++)q.updateInput(V,{bip32Derivation:[[T,{fingerprint:F,path:L}]]});let v=x.encode(q.toPSBT(0)),A=await G.signPsbt(v,J,null);for(let[V,W]of A)q.updateInput(V,{partialSig:[[new Uint8Array(W.pubkey),new Uint8Array(W.signature)]]});return q.finalize(),q}}}},o=B({chain:"bitcoin"}),d=B({chain:"litecoin"});export{d as LitecoinPsbtLedger,o as BitcoinPsbtLedger};
|
|
2
|
-
|
|
3
|
-
//# debugId=AE9B8E2F206F534564756E2164756E21
|
|
4
|
-
//# sourceMappingURL=chunk-9jd3dhjv.js.map
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/ledger/clients/utxo-psbt.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"import type Transport from \"@ledgerhq/hw-transport\";\nimport { base64 } from \"@scure/base\";\nimport { HDKey } from \"@scure/bip32\";\nimport { type DerivationPathArray, derivationPathToString, getWalletFormatFor, SwapKitError } from \"@swapkit/helpers\";\nimport type { Transaction } from \"@swapkit/utxo-signer\";\n\nimport { getLedgerTransport } from \"../helpers/getLedgerTransport\";\n\ntype SupportedCoin = \"bitcoin\" | \"litecoin\";\n\ntype DefaultDescriptorTemplate = \"wpkh(@0/**)\" | \"tr(@0/**)\" | \"sh(wpkh(@0/**))\" | \"pkh(@0/**)\";\n\nfunction templateForFormat(format: ReturnType<typeof getWalletFormatFor>): DefaultDescriptorTemplate {\n switch (format) {\n case \"bech32\":\n return \"wpkh(@0/**)\";\n case \"p2sh\":\n return \"sh(wpkh(@0/**))\";\n case \"legacy\":\n return \"pkh(@0/**)\";\n default:\n return \"wpkh(@0/**)\";\n }\n}\n\nfunction pathToString(path: DerivationPathArray | string): string {\n return typeof path === \"string\" ? path : derivationPathToString(path);\n}\n\nfunction pathToNumberArray(path: string): number[] {\n return path\n .replace(/^m\\//, \"\")\n .split(\"/\")\n .filter(Boolean)\n .map((p) => {\n const hardened = p.endsWith(\"'\");\n const num = Number.parseInt(hardened ? p.slice(0, -1) : p, 10);\n return hardened ? (num | 0x80000000) >>> 0 : num;\n });\n}\n\nconst BaseLedgerPsbtUTXO = ({ chain }: { chain: SupportedCoin }) => {\n return (derivationPathArray?: DerivationPathArray | string, injectedTransport?: Transport) => {\n // Per-call state — each BitcoinPsbtLedger/LitecoinPsbtLedger invocation has its own\n // AppClient and master fingerprint so different consumers (e.g. concurrent MCP\n // sessions on different devices) cannot inherit each other's ledger bindings or xpub.\n let appClient: import(\"ledger-bitcoin\").AppClient | undefined;\n let masterFingerprint: string | undefined;\n\n async function getAppClient() {\n if (!appClient) {\n const transport = injectedTransport ?? (await getLedgerTransport());\n const { AppClient } = await import(\"ledger-bitcoin\");\n appClient = new AppClient(transport);\n }\n return appClient;\n }\n\n async function getFingerprint() {\n if (!masterFingerprint) {\n const app = await getAppClient();\n masterFingerprint = await app.getMasterFingerprint();\n }\n return masterFingerprint;\n }\n\n // Single-address account: change == index == 0 by default.\n const derivationPath = derivationPathArray ? pathToString(derivationPathArray) : \"84'/0'/0'/0/0\";\n const accountPath = derivationPath.split(\"/\").slice(0, 3).join(\"/\");\n const leafSegments = derivationPath.split(\"/\").slice(3);\n const change = Number(leafSegments[0] ?? 0);\n const addressIndex = Number(leafSegments[1] ?? 0);\n const format = getWalletFormatFor(derivationPath);\n const template = templateForFormat(format);\n\n let cachedAccountXpub: string | undefined;\n let cachedLeafPubkey: Uint8Array | undefined;\n\n async function buildPolicy() {\n const app = await getAppClient();\n const fpr = await getFingerprint();\n if (!cachedAccountXpub) {\n cachedAccountXpub = await app.getExtendedPubkey(`m/${accountPath}`);\n }\n const { DefaultWalletPolicy } = await import(\"ledger-bitcoin\");\n const policy = new DefaultWalletPolicy(template, `[${fpr}/${accountPath}]${cachedAccountXpub}`);\n return { app, fpr, policy, xpub: cachedAccountXpub };\n }\n\n async function getLeafPubkey() {\n if (!cachedLeafPubkey) {\n const { xpub } = await buildPolicy();\n const accountKey = HDKey.fromExtendedKey(xpub);\n const leaf = accountKey.derive(`m/${change}/${addressIndex}`);\n if (!leaf.publicKey) {\n throw new SwapKitError(\"wallet_ledger_get_address_error\", {\n message: `Cannot derive leaf pubkey for ${chain}`,\n });\n }\n cachedLeafPubkey = leaf.publicKey;\n }\n return cachedLeafPubkey;\n }\n\n return {\n connect: async () => {\n await getAppClient();\n },\n getAddress: async () => {\n const { app, policy } = await buildPolicy();\n const address = await app.getWalletAddress(policy, null, change, addressIndex, false);\n if (!address) {\n throw new SwapKitError(\"wallet_ledger_get_address_error\", {\n message: `Cannot get ${chain} address from ledger derivation path: ${derivationPath}`,\n });\n }\n return address;\n },\n getExtendedPublicKey: async (path = `m/${accountPath}`) => {\n const app = await getAppClient();\n return app.getExtendedPubkey(path);\n },\n signTransaction: async (tx: Transaction): Promise<Transaction> => {\n const { app, policy, fpr } = await buildPolicy();\n const fingerprintBE = Number.parseInt(fpr, 16) >>> 0;\n const pathNumbers = pathToNumberArray(derivationPath);\n const leafPubkey = await getLeafPubkey();\n\n // Single-address account: every input is owned by the same key + path.\n for (let i = 0; i < tx.inputsLength; i++) {\n tx.updateInput(i, { bip32Derivation: [[leafPubkey, { fingerprint: fingerprintBE, path: pathNumbers }]] });\n }\n\n const psbtB64 = base64.encode(tx.toPSBT(0));\n const sigs = await app.signPsbt(psbtB64, policy, null);\n\n for (const [idx, partial] of sigs) {\n tx.updateInput(idx, { partialSig: [[new Uint8Array(partial.pubkey), new Uint8Array(partial.signature)]] });\n }\n\n tx.finalize();\n return tx;\n },\n };\n };\n};\n\nexport const BitcoinPsbtLedger = BaseLedgerPsbtUTXO({ chain: \"bitcoin\" });\nexport const LitecoinPsbtLedger = BaseLedgerPsbtUTXO({ chain: \"litecoin\" });\n"
|
|
6
|
-
],
|
|
7
|
-
"mappings": "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",
|
|
8
|
-
"debugId": "AE9B8E2F206F534564756E2164756E21",
|
|
9
|
-
"names": []
|
|
10
|
-
}
|
package/dist/chunk-m08x6an5.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
var w=require("@scure/base"),B=require("@scure/bip32"),M=require("@swapkit/helpers");function f(z){switch(z){case"bech32":return"wpkh(@0/**)";case"p2sh":return"sh(wpkh(@0/**))";case"legacy":return"pkh(@0/**)";default:return"wpkh(@0/**)"}}function m(z){return typeof z==="string"?z:M.derivationPathToString(z)}function y(z){return z.replace(/^m\//,"").split("/").filter(Boolean).map((Q)=>{let Z=Q.endsWith("'"),R=Number.parseInt(Z?Q.slice(0,-1):Q,10);return Z?(R|2147483648)>>>0:R})}var X=({chain:z})=>{return(Q,Z)=>{let R,H;async function _(){if(!R){let q=Z??await K(),{AppClient:G}=await import("ledger-bitcoin");R=new G(q)}return R}async function k(){if(!H)H=await(await _()).getMasterFingerprint();return H}let V=Q?m(Q):"84'/0'/0'/0/0",j=V.split("/").slice(0,3).join("/"),O=V.split("/").slice(3),U=Number(O[0]??0),W=Number(O[1]??0),S=M.getWalletFormatFor(V),C=f(S),$,D;async function I(){let q=await _(),G=await k();if(!$)$=await q.getExtendedPubkey(`m/${j}`);let{DefaultWalletPolicy:J}=await import("ledger-bitcoin"),N=new J(C,`[${G}/${j}]${$}`);return{app:q,fpr:G,policy:N,xpub:$}}async function F(){if(!D){let{xpub:q}=await I(),J=B.HDKey.fromExtendedKey(q).derive(`m/${U}/${W}`);if(!J.publicKey)throw new M.SwapKitError("wallet_ledger_get_address_error",{message:`Cannot derive leaf pubkey for ${z}`});D=J.publicKey}return D}return{connect:async()=>{await _()},getAddress:async()=>{let{app:q,policy:G}=await I(),J=await q.getWalletAddress(G,null,U,W,!1);if(!J)throw new M.SwapKitError("wallet_ledger_get_address_error",{message:`Cannot get ${z} address from ledger derivation path: ${V}`});return J},getExtendedPublicKey:async(q=`m/${j}`)=>{return(await _()).getExtendedPubkey(q)},signTransaction:async(q)=>{let{app:G,policy:J,fpr:N}=await I(),L=Number.parseInt(N,16)>>>0,T=y(V),v=await F();for(let Y=0;Y<q.inputsLength;Y++)q.updateInput(Y,{bip32Derivation:[[v,{fingerprint:L,path:T}]]});let A=w.base64.encode(q.toPSBT(0)),x=await G.signPsbt(A,J,null);for(let[Y,E]of x)q.updateInput(Y,{partialSig:[[new Uint8Array(E.pubkey),new Uint8Array(E.signature)]]});return q.finalize(),q}}}},P=X({chain:"bitcoin"}),g=X({chain:"litecoin"});
|
|
2
|
-
|
|
3
|
-
//# debugId=EEF70E29FB42902064756E2164756E21
|
|
4
|
-
//# sourceMappingURL=chunk-m08x6an5.js.map
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/ledger/clients/utxo-psbt.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"import type Transport from \"@ledgerhq/hw-transport\";\nimport { base64 } from \"@scure/base\";\nimport { HDKey } from \"@scure/bip32\";\nimport { type DerivationPathArray, derivationPathToString, getWalletFormatFor, SwapKitError } from \"@swapkit/helpers\";\nimport type { Transaction } from \"@swapkit/utxo-signer\";\n\nimport { getLedgerTransport } from \"../helpers/getLedgerTransport\";\n\ntype SupportedCoin = \"bitcoin\" | \"litecoin\";\n\ntype DefaultDescriptorTemplate = \"wpkh(@0/**)\" | \"tr(@0/**)\" | \"sh(wpkh(@0/**))\" | \"pkh(@0/**)\";\n\nfunction templateForFormat(format: ReturnType<typeof getWalletFormatFor>): DefaultDescriptorTemplate {\n switch (format) {\n case \"bech32\":\n return \"wpkh(@0/**)\";\n case \"p2sh\":\n return \"sh(wpkh(@0/**))\";\n case \"legacy\":\n return \"pkh(@0/**)\";\n default:\n return \"wpkh(@0/**)\";\n }\n}\n\nfunction pathToString(path: DerivationPathArray | string): string {\n return typeof path === \"string\" ? path : derivationPathToString(path);\n}\n\nfunction pathToNumberArray(path: string): number[] {\n return path\n .replace(/^m\\//, \"\")\n .split(\"/\")\n .filter(Boolean)\n .map((p) => {\n const hardened = p.endsWith(\"'\");\n const num = Number.parseInt(hardened ? p.slice(0, -1) : p, 10);\n return hardened ? (num | 0x80000000) >>> 0 : num;\n });\n}\n\nconst BaseLedgerPsbtUTXO = ({ chain }: { chain: SupportedCoin }) => {\n return (derivationPathArray?: DerivationPathArray | string, injectedTransport?: Transport) => {\n // Per-call state — each BitcoinPsbtLedger/LitecoinPsbtLedger invocation has its own\n // AppClient and master fingerprint so different consumers (e.g. concurrent MCP\n // sessions on different devices) cannot inherit each other's ledger bindings or xpub.\n let appClient: import(\"ledger-bitcoin\").AppClient | undefined;\n let masterFingerprint: string | undefined;\n\n async function getAppClient() {\n if (!appClient) {\n const transport = injectedTransport ?? (await getLedgerTransport());\n const { AppClient } = await import(\"ledger-bitcoin\");\n appClient = new AppClient(transport);\n }\n return appClient;\n }\n\n async function getFingerprint() {\n if (!masterFingerprint) {\n const app = await getAppClient();\n masterFingerprint = await app.getMasterFingerprint();\n }\n return masterFingerprint;\n }\n\n // Single-address account: change == index == 0 by default.\n const derivationPath = derivationPathArray ? pathToString(derivationPathArray) : \"84'/0'/0'/0/0\";\n const accountPath = derivationPath.split(\"/\").slice(0, 3).join(\"/\");\n const leafSegments = derivationPath.split(\"/\").slice(3);\n const change = Number(leafSegments[0] ?? 0);\n const addressIndex = Number(leafSegments[1] ?? 0);\n const format = getWalletFormatFor(derivationPath);\n const template = templateForFormat(format);\n\n let cachedAccountXpub: string | undefined;\n let cachedLeafPubkey: Uint8Array | undefined;\n\n async function buildPolicy() {\n const app = await getAppClient();\n const fpr = await getFingerprint();\n if (!cachedAccountXpub) {\n cachedAccountXpub = await app.getExtendedPubkey(`m/${accountPath}`);\n }\n const { DefaultWalletPolicy } = await import(\"ledger-bitcoin\");\n const policy = new DefaultWalletPolicy(template, `[${fpr}/${accountPath}]${cachedAccountXpub}`);\n return { app, fpr, policy, xpub: cachedAccountXpub };\n }\n\n async function getLeafPubkey() {\n if (!cachedLeafPubkey) {\n const { xpub } = await buildPolicy();\n const accountKey = HDKey.fromExtendedKey(xpub);\n const leaf = accountKey.derive(`m/${change}/${addressIndex}`);\n if (!leaf.publicKey) {\n throw new SwapKitError(\"wallet_ledger_get_address_error\", {\n message: `Cannot derive leaf pubkey for ${chain}`,\n });\n }\n cachedLeafPubkey = leaf.publicKey;\n }\n return cachedLeafPubkey;\n }\n\n return {\n connect: async () => {\n await getAppClient();\n },\n getAddress: async () => {\n const { app, policy } = await buildPolicy();\n const address = await app.getWalletAddress(policy, null, change, addressIndex, false);\n if (!address) {\n throw new SwapKitError(\"wallet_ledger_get_address_error\", {\n message: `Cannot get ${chain} address from ledger derivation path: ${derivationPath}`,\n });\n }\n return address;\n },\n getExtendedPublicKey: async (path = `m/${accountPath}`) => {\n const app = await getAppClient();\n return app.getExtendedPubkey(path);\n },\n signTransaction: async (tx: Transaction): Promise<Transaction> => {\n const { app, policy, fpr } = await buildPolicy();\n const fingerprintBE = Number.parseInt(fpr, 16) >>> 0;\n const pathNumbers = pathToNumberArray(derivationPath);\n const leafPubkey = await getLeafPubkey();\n\n // Single-address account: every input is owned by the same key + path.\n for (let i = 0; i < tx.inputsLength; i++) {\n tx.updateInput(i, { bip32Derivation: [[leafPubkey, { fingerprint: fingerprintBE, path: pathNumbers }]] });\n }\n\n const psbtB64 = base64.encode(tx.toPSBT(0));\n const sigs = await app.signPsbt(psbtB64, policy, null);\n\n for (const [idx, partial] of sigs) {\n tx.updateInput(idx, { partialSig: [[new Uint8Array(partial.pubkey), new Uint8Array(partial.signature)]] });\n }\n\n tx.finalize();\n return tx;\n },\n };\n };\n};\n\nexport const BitcoinPsbtLedger = BaseLedgerPsbtUTXO({ chain: \"bitcoin\" });\nexport const LitecoinPsbtLedger = BaseLedgerPsbtUTXO({ chain: \"litecoin\" });\n"
|
|
6
|
-
],
|
|
7
|
-
"mappings": "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",
|
|
8
|
-
"debugId": "EEF70E29FB42902064756E2164756E21",
|
|
9
|
-
"names": []
|
|
10
|
-
}
|