@swapkit/wallet-hardware 4.9.14 → 4.9.15

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.
@@ -22,7 +22,7 @@ import {
22
22
  type UTXOToolboxes,
23
23
  type UTXOType,
24
24
  } from "@swapkit/toolboxes/utxo";
25
- import type { BTCNetwork, PCZT, Transaction, ZcashTransaction } from "@swapkit/utxo-signer";
25
+ import type { BTCNetwork, PCZT, Transaction, ZcashPSBT, ZcashTransaction } from "@swapkit/utxo-signer";
26
26
  import { BCHSigHash, NETWORKS, ZcashConsensusBranchId, ZcashVersionGroupId } from "@swapkit/utxo-signer";
27
27
  import { createWallet, getWalletSupportedChains, type HardwareExtendedPublicKeyInfo } from "@swapkit/wallet-core";
28
28
 
@@ -31,6 +31,7 @@ type TrezorCoreMode = "auto" | "iframe" | "popup" | "suite-desktop" | "suite-web
31
31
  type TrezorTransport = "BridgeTransport" | "WebUsbTransport" | "NodeUsbTransport";
32
32
  type ConnectTrezorOptions = { address?: string };
33
33
  type TrezorAccountRefTransaction = { details: Record<string, never>; hex: string; txid: string };
34
+ type ZcashSignableTransaction = PCZT | ZcashPSBT | ZcashTransaction;
34
35
  type TrezorExtendedPublicKeyInfo = {
35
36
  accountIndex: number;
36
37
  chainCode?: string;
@@ -275,7 +276,7 @@ function buildPCZTInputsForTrezor(
275
276
  inputs.push({
276
277
  address_n,
277
278
  amount: input.value.toString(),
278
- prev_hash: hexEncode.encode(new Uint8Array([...input.txid].reverse())),
279
+ prev_hash: hexEncode.encode(input.txid),
279
280
  prev_index: input.index,
280
281
  script_type: "SPENDADDRESS" as const,
281
282
  });
@@ -364,8 +365,8 @@ function buildZcashTxInputsForTrezor(
364
365
  const utxoInfo = utxoInputs[i];
365
366
  inputs.push({
366
367
  address_n,
367
- amount: utxoInfo?.value?.toString() || "0",
368
- prev_hash: input.txid ? hexEncode.encode(new Uint8Array([...input.txid].reverse())) : "",
368
+ amount: utxoInfo?.value?.toString() || input.value?.toString() || "0",
369
+ prev_hash: input.txid ? hexEncode.encode(input.txid) : "",
369
370
  prev_index: input.index ?? 0,
370
371
  script_type: "SPENDADDRESS" as const,
371
372
  });
@@ -467,7 +468,7 @@ function shouldUseTrezorSerializedSigner(chain: Chain) {
467
468
  }
468
469
 
469
470
  function buildTrezorRefTxs(chain: Chain, inputs: UTXOType[]): TrezorAccountRefTransaction[] | undefined {
470
- if (!shouldUseTrezorSerializedSigner(chain)) return undefined;
471
+ if (chain !== Chain.Dash) return undefined;
471
472
 
472
473
  const refs = new Map<string, TrezorAccountRefTransaction>();
473
474
 
@@ -537,158 +538,115 @@ async function getTrezorWallet<T extends Chain>({
537
538
  return payload.address;
538
539
  };
539
540
 
540
- const address = await getAddress();
541
-
542
- const signer = {
543
- getAddress: async () => address,
544
-
545
- signPCZT: async (pczt: PCZT): Promise<PCZT> => {
546
- const TrezorConnect = (await import("@trezor/connect-web")).default;
547
- const { hex: hexEncode } = await import("@scure/base");
548
- const address_n = hardenDerivationPath(derivationPath);
549
- const global = pczt.getGlobal();
550
-
551
- const inputs = buildPCZTInputsForTrezor(pczt, address_n, hexEncode);
552
- const outputs = await buildPCZTOutputsForTrezor(pczt, address_n, address, chain);
553
-
554
- const result = await TrezorConnect.signTransaction({
555
- branchId: global.consensusBranchId,
556
- coin: "zcash",
557
- expiry: global.expiryHeight,
558
- inputs,
559
- locktime: global.lockTime,
560
- outputs: outputs as any,
561
- overwintered: true,
562
- version: global.txVersion,
563
- versionGroupId: global.versionGroupId,
564
- });
565
-
566
- if (!result.success) {
567
- throw new SwapKitError({
568
- errorKey: "wallet_trezor_failed_to_sign_transaction",
569
- info: { chain, error: (result.payload as { error: string; code?: string }).error },
570
- });
571
- }
572
-
573
- return extractSignaturesFromSignedTx(result.payload.serializedTx, pczt);
574
- },
541
+ const address = providedAddress ?? (await getAddress());
575
542
 
576
- signTransaction: async (tx: ZcashTransaction, utxoInputs: UTXOType[]) => {
577
- const TrezorConnect = (await import("@trezor/connect-web")).default;
578
- const { hex: hexEncode } = await import("@scure/base");
579
- const address_n = hardenDerivationPath(derivationPath);
580
-
581
- const inputs = buildZcashTxInputsForTrezor(tx, utxoInputs, address_n, hexEncode);
582
- const outputs = buildZcashTxOutputsForTrezor(tx, address_n, address, chain);
583
-
584
- const result = await TrezorConnect.signTransaction({
585
- branchId: ZcashConsensusBranchId.NU6,
586
- coin: "zcash",
587
- expiry: 0,
588
- inputs,
589
- locktime: 0,
590
- outputs: outputs as any,
591
- overwintered: true,
592
- version: 4,
593
- versionGroupId: ZcashVersionGroupId.SAPLING,
594
- });
543
+ const signPCZTWithSerializedTx = async (pczt: PCZT) => {
544
+ const TrezorConnect = (await import("@trezor/connect-web")).default;
545
+ const { hex: hexEncode } = await import("@scure/base");
546
+ const address_n = hardenDerivationPath(derivationPath);
547
+ const global = pczt.getGlobal();
595
548
 
596
- if (result.success) {
597
- return result.payload.serializedTx;
598
- }
549
+ const inputs = buildPCZTInputsForTrezor(pczt, address_n, hexEncode);
550
+ const outputs = await buildPCZTOutputsForTrezor(pczt, address_n, address, chain);
551
+ const result = await TrezorConnect.signTransaction({
552
+ branchId: global.consensusBranchId,
553
+ coin: "zcash",
554
+ expiry: global.expiryHeight,
555
+ inputs,
556
+ locktime: global.lockTime,
557
+ outputs: outputs as any,
558
+ overwintered: true,
559
+ version: global.txVersion,
560
+ versionGroupId: global.versionGroupId,
561
+ });
599
562
 
563
+ if (!result.success) {
600
564
  throw new SwapKitError({
601
565
  errorKey: "wallet_trezor_failed_to_sign_transaction",
602
566
  info: { chain, error: (result.payload as { error: string; code?: string }).error },
603
567
  });
604
- },
568
+ }
569
+
570
+ return {
571
+ serializedTx: result.payload.serializedTx,
572
+ signedPczt: await extractSignaturesFromSignedTx(result.payload.serializedTx, pczt),
573
+ };
605
574
  };
606
575
 
607
- const toolbox = getUtxoToolbox(Chain.Zcash);
576
+ const parseSignedZcashTransaction = async (signedTxHex: string) => {
577
+ const { ZcashTransaction: ZcashTx } = await import("@swapkit/utxo-signer");
578
+ return ZcashTx.fromHex(signedTxHex, { allowUnknownOutputs: true });
579
+ };
608
580
 
609
- const transfer = async (params: GenericTransferParams) => {
610
- if (!(address && params.recipient)) {
611
- throw new SwapKitError({
612
- errorKey: "wallet_missing_params",
613
- info: { address, recipient: params.recipient, wallet: WalletOption.TREZOR },
614
- });
615
- }
581
+ const signZcashTransaction = async (tx: ZcashTransaction) => {
582
+ const TrezorConnect = (await import("@trezor/connect-web")).default;
583
+ const { hex: hexEncode } = await import("@scure/base");
584
+ const address_n = hardenDerivationPath(derivationPath);
616
585
 
617
- const feeRate = params.feeRate || (await toolbox.getFeeRates())[params.feeOptionKey || FeeOption.Fast];
586
+ const inputs = buildZcashTxInputsForTrezor(tx, [], address_n, hexEncode);
587
+ const outputs = buildZcashTxOutputsForTrezor(tx, address_n, address, chain);
618
588
 
619
- const { tx, inputs: txInputs } = await toolbox.createTransaction({
620
- ...params,
621
- feeRate,
622
- fetchTxHex: false,
623
- sender: address,
589
+ const result = await TrezorConnect.signTransaction({
590
+ branchId: ZcashConsensusBranchId.NU6,
591
+ coin: "zcash",
592
+ expiry: 0,
593
+ inputs,
594
+ locktime: 0,
595
+ outputs: outputs as any,
596
+ overwintered: true,
597
+ version: 4,
598
+ versionGroupId: ZcashVersionGroupId.SAPLING,
624
599
  });
625
600
 
626
- const txHex = await signer.signTransaction(tx, txInputs);
627
- const broadcastResult = await toolbox.broadcastTx(txHex);
601
+ if (result.success) {
602
+ return parseSignedZcashTransaction(result.payload.serializedTx);
603
+ }
628
604
 
629
- return broadcastResult;
605
+ throw new SwapKitError({
606
+ errorKey: "wallet_trezor_failed_to_sign_transaction",
607
+ info: { chain, error: (result.payload as { error: string; code?: string }).error },
608
+ });
630
609
  };
631
610
 
632
- const transferWithPCZT = async (params: GenericTransferParams) => {
633
- if (!(address && params.recipient)) {
634
- throw new SwapKitError({
635
- errorKey: "wallet_missing_params",
636
- info: { address, recipient: params.recipient, wallet: WalletOption.TREZOR },
637
- });
638
- }
639
-
640
- const { createPCZT, OutScript } = await import("@swapkit/utxo-signer");
641
- const { hex: hexEncode } = await import("@scure/base");
642
- const { getUtxoApi } = await import("@swapkit/toolboxes/utxo");
611
+ const signer = {
612
+ getAddress: async () => address,
643
613
 
644
- const feeRate = params.feeRate || (await toolbox.getFeeRates())[params.feeOptionKey || FeeOption.Fast];
614
+ signPCZT: async (pczt: PCZT): Promise<PCZT> => {
615
+ return (await signPCZTWithSerializedTx(pczt)).signedPczt;
616
+ },
645
617
 
646
- const utxos = await getUtxoApi(Chain.Zcash).getUtxos({ address });
618
+ signTransaction: async (tx: ZcashSignableTransaction): Promise<ZcashTransaction> => {
619
+ if ("toPCZT" in tx) {
620
+ const { serializedTx } = await signPCZTWithSerializedTx(tx.toPCZT());
621
+ return parseSignedZcashTransaction(serializedTx);
622
+ }
647
623
 
648
- const { tx, inputs: txInputs } = await toolbox.createTransaction({
649
- ...params,
650
- feeRate,
651
- fetchTxHex: false,
652
- sender: address,
653
- });
624
+ if ("getGlobal" in tx) {
625
+ const { serializedTx } = await signPCZTWithSerializedTx(tx);
626
+ return parseSignedZcashTransaction(serializedTx);
627
+ }
654
628
 
655
- const pczt = createPCZT();
629
+ return signZcashTransaction(tx);
630
+ },
631
+ };
656
632
 
657
- for (const utxoInput of txInputs) {
658
- const utxo = utxos.find((u) => u.hash === utxoInput.hash && u.index === utxoInput.index);
659
- const scriptPubkey = utxo?.witnessUtxo?.script
660
- ? new Uint8Array(utxo.witnessUtxo.script)
661
- : OutScript.encode({ hash: hexEncode.decode((utxoInput as any).address || ""), type: "pkh" });
633
+ const toolbox = getUtxoToolbox(Chain.Zcash, { signer });
662
634
 
663
- pczt.addInput({
664
- index: utxoInput.index,
665
- scriptPubkey,
666
- txid: hexEncode.decode(utxoInput.hash).reverse() as unknown as Uint8Array,
667
- value: BigInt(utxoInput.value),
668
- });
635
+ const signAndBroadcastTransaction = async (tx: ZcashSignableTransaction) => {
636
+ if ("toPCZT" in tx) {
637
+ const { serializedTx } = await signPCZTWithSerializedTx(tx.toPCZT());
638
+ return toolbox.broadcastTx(serializedTx);
669
639
  }
670
640
 
671
- for (let i = 0; i < tx.outputsLength; i++) {
672
- const output = tx.getOutput(i);
673
- pczt.addOutput({ scriptPubkey: output.script || new Uint8Array(), value: output.amount || 0n });
641
+ if ("getGlobal" in tx) {
642
+ const { serializedTx } = await signPCZTWithSerializedTx(tx);
643
+ return toolbox.broadcastTx(serializedTx);
674
644
  }
675
645
 
676
- const signedPczt = await signer.signPCZT(pczt);
677
- signedPczt.finalizeAllInputs();
678
- const finalTx = signedPczt.extract();
679
- const broadcastResult = await toolbox.broadcastTx(finalTx.toHex());
680
-
681
- return broadcastResult;
646
+ return toolbox.signAndBroadcastTransaction(tx);
682
647
  };
683
648
 
684
- return {
685
- ...toolbox,
686
- address,
687
- signPCZT: signer.signPCZT,
688
- signTransaction: signer.signTransaction,
689
- transfer,
690
- transferWithPCZT,
691
- };
649
+ return { ...toolbox, address, signAndBroadcastTransaction, signPCZT: signer.signPCZT };
692
650
  }
693
651
 
694
652
  case Chain.Bitcoin:
@@ -1019,7 +977,7 @@ async function getTrezorWallet<T extends Chain>({
1019
977
  const { tx, inputs: selectedInputs } = await toolbox.createTransaction({
1020
978
  assetValue: assetValue as any,
1021
979
  feeRate: txFeeRate,
1022
- fetchTxHex: true,
980
+ fetchTxHex: chain === Chain.Dash,
1023
981
  memo,
1024
982
  recipient,
1025
983
  sender: address,
@@ -1062,7 +1020,7 @@ async function getTrezorWallet<T extends Chain>({
1062
1020
  const { tx, inputs } = await createTxMethod({
1063
1021
  ...rest,
1064
1022
  feeRate,
1065
- fetchTxHex: true,
1023
+ fetchTxHex: chain === Chain.Dash,
1066
1024
  memo,
1067
1025
  recipient,
1068
1026
  sender: address,
@@ -1083,6 +1041,8 @@ async function getTrezorWallet<T extends Chain>({
1083
1041
  const signAndBroadcastTransaction = shouldUseTrezorSerializedSigner(chain)
1084
1042
  ? async (tx: Transaction) => baseToolbox.broadcastTx(await signSerializedTransaction(tx))
1085
1043
  : toolbox.signAndBroadcastTransaction;
1044
+ const walletSignTransaction = shouldUseTrezorPsbtSigner(chain) ? toolbox.signTransaction : signTransaction;
1045
+ const walletTransfer = shouldUseTrezorPsbtSigner(chain) ? toolbox.transfer : transfer;
1086
1046
 
1087
1047
  async function getExtendedPublicKeyInfo({ accountIndex }: { accountIndex?: number } = {}) {
1088
1048
  const TrezorConnect = (await import("@trezor/connect-web")).default;
@@ -1242,9 +1202,9 @@ async function getTrezorWallet<T extends Chain>({
1242
1202
  getExtendedPublicKey,
1243
1203
  getExtendedPublicKeyInfo,
1244
1204
  signAndBroadcastTransaction,
1245
- signTransaction,
1205
+ signTransaction: walletSignTransaction,
1246
1206
  signTransactionWithMultipleInputs,
1247
- transfer,
1207
+ transfer: walletTransfer,
1248
1208
  transferFromMultipleAddresses,
1249
1209
  };
1250
1210
  }
@@ -1259,14 +1219,14 @@ export async function getTrezorExtendedPublicKey(
1259
1219
  derivationPath?: DerivationPathArray,
1260
1220
  { accountIndex }: { accountIndex?: number } = {},
1261
1221
  ): Promise<HardwareExtendedPublicKeyInfo | undefined> {
1262
- if (![Chain.BitcoinCash, Chain.Bitcoin, Chain.Dash, Chain.Dogecoin, Chain.Litecoin].includes(chain)) {
1222
+ if (![Chain.BitcoinCash, Chain.Bitcoin, Chain.Dash, Chain.Dogecoin, Chain.Litecoin, Chain.Zcash].includes(chain)) {
1263
1223
  throw new SwapKitError({ errorKey: "wallet_chain_not_supported", info: { chain, wallet: WalletOption.TREZOR } });
1264
1224
  }
1265
1225
 
1266
1226
  const { TrezorConnect } = await initTrezorConnect();
1267
1227
  const utxoChain = chain as UTXOChain;
1268
1228
  const resolvedDerivationPath = derivationPath ?? (NetworkDerivationPath[chain] as DerivationPathArray);
1269
- const coin = chain.toLowerCase();
1229
+ const coin = chain === Chain.Zcash ? "zcash" : chain.toLowerCase();
1270
1230
 
1271
1231
  const resolvedAccountPath = getUTXOAccountPath({
1272
1232
  accountIndex,
@@ -1388,7 +1348,7 @@ export const trezorWallet = createWallet({
1388
1348
  [Chain.Optimism]: true,
1389
1349
  [Chain.Polygon]: true,
1390
1350
  [Chain.XLayer]: true,
1391
- // ZEC: pending PCZT/TrezorConnect validation
1351
+ [Chain.Zcash]: true,
1392
1352
  },
1393
1353
  getExtendedPublicKey: getTrezorExtendedPublicKey,
1394
1354
  name: "connectTrezor",