@swapkit/wallet-hardware 4.9.14 → 4.9.16

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,140 @@ 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 signZcashTransactionWithSerializedTx = 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 {
603
+ serializedTx: result.payload.serializedTx,
604
+ signedTx: await parseSignedZcashTransaction(result.payload.serializedTx),
605
+ };
606
+ }
628
607
 
629
- return broadcastResult;
608
+ throw new SwapKitError({
609
+ errorKey: "wallet_trezor_failed_to_sign_transaction",
610
+ info: { chain, error: (result.payload as { error: string; code?: string }).error },
611
+ });
630
612
  };
631
613
 
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
- }
614
+ const signer = {
615
+ getAddress: async () => address,
639
616
 
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");
617
+ signPCZT: async (pczt: PCZT): Promise<PCZT> => {
618
+ return (await signPCZTWithSerializedTx(pczt)).signedPczt;
619
+ },
643
620
 
644
- const feeRate = params.feeRate || (await toolbox.getFeeRates())[params.feeOptionKey || FeeOption.Fast];
621
+ signTransaction: async (tx: ZcashSignableTransaction): Promise<ZcashTransaction> => {
622
+ if ("toPCZT" in tx) {
623
+ const { serializedTx } = await signPCZTWithSerializedTx(tx.toPCZT());
624
+ return parseSignedZcashTransaction(serializedTx);
625
+ }
645
626
 
646
- const utxos = await getUtxoApi(Chain.Zcash).getUtxos({ address });
627
+ if ("getGlobal" in tx) {
628
+ const { serializedTx } = await signPCZTWithSerializedTx(tx);
629
+ return parseSignedZcashTransaction(serializedTx);
630
+ }
647
631
 
648
- const { tx, inputs: txInputs } = await toolbox.createTransaction({
649
- ...params,
650
- feeRate,
651
- fetchTxHex: false,
652
- sender: address,
653
- });
632
+ return (await signZcashTransactionWithSerializedTx(tx)).signedTx;
633
+ },
634
+ };
654
635
 
655
- const pczt = createPCZT();
636
+ const toolbox = getUtxoToolbox(Chain.Zcash, { signer });
656
637
 
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" });
638
+ const signAndBroadcastTransaction = async (tx: ZcashSignableTransaction) => {
639
+ if ("toPCZT" in tx) {
640
+ const { serializedTx } = await signPCZTWithSerializedTx(tx.toPCZT());
641
+ return toolbox.broadcastTx(serializedTx);
642
+ }
662
643
 
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
- });
644
+ if ("getGlobal" in tx) {
645
+ const { serializedTx } = await signPCZTWithSerializedTx(tx);
646
+ return toolbox.broadcastTx(serializedTx);
669
647
  }
670
648
 
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 });
649
+ const { serializedTx } = await signZcashTransactionWithSerializedTx(tx);
650
+ return toolbox.broadcastTx(serializedTx);
651
+ };
652
+
653
+ const transfer = async ({ recipient, feeOptionKey, feeRate: paramFeeRate, ...rest }: GenericTransferParams) => {
654
+ if (!(address && recipient)) {
655
+ throw new SwapKitError({
656
+ errorKey: "wallet_missing_params",
657
+ info: { address, recipient, wallet: WalletOption.TREZOR },
658
+ });
674
659
  }
675
660
 
676
- const signedPczt = await signer.signPCZT(pczt);
677
- signedPczt.finalizeAllInputs();
678
- const finalTx = signedPczt.extract();
679
- const broadcastResult = await toolbox.broadcastTx(finalTx.toHex());
661
+ const feeRate = paramFeeRate || (await toolbox.getFeeRates())[feeOptionKey || FeeOption.Fast];
662
+ const { tx } = await toolbox.createTransaction({
663
+ ...rest,
664
+ feeRate,
665
+ fetchTxHex: false,
666
+ recipient,
667
+ sender: address,
668
+ });
669
+ const { serializedTx } = await signZcashTransactionWithSerializedTx(tx);
680
670
 
681
- return broadcastResult;
671
+ return toolbox.broadcastTx(serializedTx);
682
672
  };
683
673
 
684
- return {
685
- ...toolbox,
686
- address,
687
- signPCZT: signer.signPCZT,
688
- signTransaction: signer.signTransaction,
689
- transfer,
690
- transferWithPCZT,
691
- };
674
+ return { ...toolbox, address, signAndBroadcastTransaction, signPCZT: signer.signPCZT, transfer };
692
675
  }
693
676
 
694
677
  case Chain.Bitcoin:
@@ -1019,7 +1002,7 @@ async function getTrezorWallet<T extends Chain>({
1019
1002
  const { tx, inputs: selectedInputs } = await toolbox.createTransaction({
1020
1003
  assetValue: assetValue as any,
1021
1004
  feeRate: txFeeRate,
1022
- fetchTxHex: true,
1005
+ fetchTxHex: chain === Chain.Dash,
1023
1006
  memo,
1024
1007
  recipient,
1025
1008
  sender: address,
@@ -1062,7 +1045,7 @@ async function getTrezorWallet<T extends Chain>({
1062
1045
  const { tx, inputs } = await createTxMethod({
1063
1046
  ...rest,
1064
1047
  feeRate,
1065
- fetchTxHex: true,
1048
+ fetchTxHex: chain === Chain.Dash,
1066
1049
  memo,
1067
1050
  recipient,
1068
1051
  sender: address,
@@ -1083,6 +1066,8 @@ async function getTrezorWallet<T extends Chain>({
1083
1066
  const signAndBroadcastTransaction = shouldUseTrezorSerializedSigner(chain)
1084
1067
  ? async (tx: Transaction) => baseToolbox.broadcastTx(await signSerializedTransaction(tx))
1085
1068
  : toolbox.signAndBroadcastTransaction;
1069
+ const walletSignTransaction = shouldUseTrezorPsbtSigner(chain) ? toolbox.signTransaction : signTransaction;
1070
+ const walletTransfer = shouldUseTrezorPsbtSigner(chain) ? toolbox.transfer : transfer;
1086
1071
 
1087
1072
  async function getExtendedPublicKeyInfo({ accountIndex }: { accountIndex?: number } = {}) {
1088
1073
  const TrezorConnect = (await import("@trezor/connect-web")).default;
@@ -1242,9 +1227,9 @@ async function getTrezorWallet<T extends Chain>({
1242
1227
  getExtendedPublicKey,
1243
1228
  getExtendedPublicKeyInfo,
1244
1229
  signAndBroadcastTransaction,
1245
- signTransaction,
1230
+ signTransaction: walletSignTransaction,
1246
1231
  signTransactionWithMultipleInputs,
1247
- transfer,
1232
+ transfer: walletTransfer,
1248
1233
  transferFromMultipleAddresses,
1249
1234
  };
1250
1235
  }
@@ -1259,14 +1244,14 @@ export async function getTrezorExtendedPublicKey(
1259
1244
  derivationPath?: DerivationPathArray,
1260
1245
  { accountIndex }: { accountIndex?: number } = {},
1261
1246
  ): Promise<HardwareExtendedPublicKeyInfo | undefined> {
1262
- if (![Chain.BitcoinCash, Chain.Bitcoin, Chain.Dash, Chain.Dogecoin, Chain.Litecoin].includes(chain)) {
1247
+ if (![Chain.BitcoinCash, Chain.Bitcoin, Chain.Dash, Chain.Dogecoin, Chain.Litecoin, Chain.Zcash].includes(chain)) {
1263
1248
  throw new SwapKitError({ errorKey: "wallet_chain_not_supported", info: { chain, wallet: WalletOption.TREZOR } });
1264
1249
  }
1265
1250
 
1266
1251
  const { TrezorConnect } = await initTrezorConnect();
1267
1252
  const utxoChain = chain as UTXOChain;
1268
1253
  const resolvedDerivationPath = derivationPath ?? (NetworkDerivationPath[chain] as DerivationPathArray);
1269
- const coin = chain.toLowerCase();
1254
+ const coin = chain === Chain.Zcash ? "zcash" : chain.toLowerCase();
1270
1255
 
1271
1256
  const resolvedAccountPath = getUTXOAccountPath({
1272
1257
  accountIndex,
@@ -1388,7 +1373,7 @@ export const trezorWallet = createWallet({
1388
1373
  [Chain.Optimism]: true,
1389
1374
  [Chain.Polygon]: true,
1390
1375
  [Chain.XLayer]: true,
1391
- // ZEC: pending PCZT/TrezorConnect validation
1376
+ [Chain.Zcash]: true,
1392
1377
  },
1393
1378
  getExtendedPublicKey: getTrezorExtendedPublicKey,
1394
1379
  name: "connectTrezor",