@swapkit/toolboxes 1.0.0-beta.3 → 1.0.0-beta.30

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.
Files changed (131) hide show
  1. package/dist/{chunk-fjfxga2v.js → chunk-5yxc1e69.js} +1 -1
  2. package/dist/{chunk-fjfxga2v.js.map → chunk-5yxc1e69.js.map} +1 -1
  3. package/dist/chunk-6f98phv2.js +4 -0
  4. package/dist/{chunk-0h4xdrwz.js.map → chunk-6f98phv2.js.map} +2 -2
  5. package/dist/{chunk-0f0249b1.js → chunk-9bqegm61.js} +1 -1
  6. package/dist/{chunk-p1kdg37m.js → chunk-s47y8512.js} +2 -2
  7. package/dist/{chunk-p1kdg37m.js.map → chunk-s47y8512.js.map} +1 -1
  8. package/dist/chunk-vtd17cje.js +3 -0
  9. package/dist/chunk-vtd17cje.js.map +10 -0
  10. package/dist/chunk-zcdeg6h9.js +4 -0
  11. package/dist/chunk-zcdeg6h9.js.map +10 -0
  12. package/dist/src/cosmos/index.cjs +3 -0
  13. package/dist/src/cosmos/index.cjs.map +16 -0
  14. package/dist/src/cosmos/index.js +3 -0
  15. package/dist/src/cosmos/index.js.map +16 -0
  16. package/dist/src/evm/index.cjs +3 -0
  17. package/dist/src/evm/index.cjs.map +18 -0
  18. package/dist/src/evm/index.js +3 -0
  19. package/dist/src/evm/index.js.map +18 -0
  20. package/dist/src/index.cjs +3 -0
  21. package/dist/src/index.cjs.map +10 -0
  22. package/dist/src/index.js +3 -0
  23. package/dist/src/index.js.map +10 -0
  24. package/dist/src/near/index.cjs +3 -0
  25. package/dist/src/near/index.cjs.map +13 -0
  26. package/dist/src/near/index.js +3 -0
  27. package/dist/src/near/index.js.map +13 -0
  28. package/dist/{radix → src/radix}/index.cjs +2 -2
  29. package/dist/src/radix/index.cjs.map +10 -0
  30. package/dist/src/radix/index.js +3 -0
  31. package/dist/src/radix/index.js.map +10 -0
  32. package/dist/src/ripple/index.cjs +3 -0
  33. package/dist/src/ripple/index.cjs.map +10 -0
  34. package/dist/src/ripple/index.js +3 -0
  35. package/dist/src/ripple/index.js.map +10 -0
  36. package/dist/src/solana/index.cjs +3 -0
  37. package/dist/src/solana/index.cjs.map +10 -0
  38. package/dist/src/solana/index.js +3 -0
  39. package/dist/src/solana/index.js.map +10 -0
  40. package/dist/src/substrate/index.cjs +3 -0
  41. package/dist/src/substrate/index.cjs.map +12 -0
  42. package/dist/src/substrate/index.js +3 -0
  43. package/dist/src/substrate/index.js.map +12 -0
  44. package/dist/src/tron/index.cjs +3 -0
  45. package/dist/src/tron/index.cjs.map +11 -0
  46. package/dist/src/tron/index.js +3 -0
  47. package/dist/src/tron/index.js.map +11 -0
  48. package/dist/src/utxo/index.cjs +5 -0
  49. package/dist/src/utxo/index.cjs.map +17 -0
  50. package/dist/src/utxo/index.js +5 -0
  51. package/dist/src/utxo/index.js.map +17 -0
  52. package/package.json +49 -37
  53. package/src/cosmos/thorchainUtils/addressFormat.ts +4 -1
  54. package/src/cosmos/thorchainUtils/messages.ts +2 -2
  55. package/src/cosmos/thorchainUtils/registry.ts +3 -3
  56. package/src/cosmos/toolbox/cosmos.ts +35 -16
  57. package/src/cosmos/toolbox/index.ts +2 -2
  58. package/src/cosmos/toolbox/thorchain.ts +11 -9
  59. package/src/cosmos/util.ts +76 -6
  60. package/src/evm/__tests__/address-validation.test.ts +86 -0
  61. package/src/evm/__tests__/ethereum.test.ts +1 -1
  62. package/src/evm/helpers.ts +4 -3
  63. package/src/evm/toolbox/baseEVMToolbox.ts +37 -25
  64. package/src/evm/toolbox/index.ts +2 -2
  65. package/src/evm/toolbox/op.ts +21 -7
  66. package/src/index.ts +118 -100
  67. package/src/near/__tests__/core.test.ts +80 -0
  68. package/src/near/helpers/contractFactory.ts +22 -0
  69. package/src/near/helpers/core.ts +89 -0
  70. package/src/near/helpers/gasEstimation.ts +110 -0
  71. package/src/near/helpers/index.ts +5 -0
  72. package/src/near/helpers/nep141.ts +110 -0
  73. package/src/near/index.ts +24 -0
  74. package/src/near/toolbox.ts +498 -0
  75. package/src/near/types/contract.ts +48 -0
  76. package/src/near/types/nep141.ts +66 -0
  77. package/src/near/types.ts +57 -0
  78. package/src/radix/index.ts +8 -2
  79. package/src/ripple/index.ts +15 -26
  80. package/src/solana/toolbox.ts +73 -2
  81. package/src/substrate/balance.ts +92 -0
  82. package/src/substrate/substrate.ts +7 -5
  83. package/src/tron/__tests__/toolbox.test.ts +147 -0
  84. package/src/tron/helpers/trc20.abi.ts +40 -0
  85. package/src/tron/index.ts +16 -0
  86. package/src/tron/toolbox.ts +336 -0
  87. package/src/tron/types.ts +31 -0
  88. package/src/utxo/__tests__/zcash-integration.test.ts +114 -0
  89. package/src/utxo/helpers/api.ts +66 -16
  90. package/src/utxo/helpers/bchaddrjs.ts +8 -8
  91. package/src/utxo/helpers/coinselect.ts +4 -2
  92. package/src/utxo/helpers/txSize.ts +4 -3
  93. package/src/utxo/index.ts +1 -0
  94. package/src/utxo/toolbox/bitcoinCash.ts +22 -14
  95. package/src/utxo/toolbox/index.ts +16 -4
  96. package/src/utxo/toolbox/utxo.ts +42 -27
  97. package/src/utxo/toolbox/zcash.ts +208 -0
  98. package/src/utxo/types.ts +2 -0
  99. package/dist/chunk-0h4xdrwz.js +0 -4
  100. package/dist/cosmos/index.cjs +0 -3
  101. package/dist/cosmos/index.cjs.map +0 -16
  102. package/dist/cosmos/index.js +0 -3
  103. package/dist/cosmos/index.js.map +0 -16
  104. package/dist/evm/index.cjs +0 -3
  105. package/dist/evm/index.cjs.map +0 -18
  106. package/dist/evm/index.js +0 -3
  107. package/dist/evm/index.js.map +0 -18
  108. package/dist/index.cjs +0 -3
  109. package/dist/index.cjs.map +0 -10
  110. package/dist/index.js +0 -3
  111. package/dist/index.js.map +0 -10
  112. package/dist/radix/index.cjs.map +0 -10
  113. package/dist/radix/index.js +0 -3
  114. package/dist/radix/index.js.map +0 -10
  115. package/dist/ripple/index.cjs +0 -3
  116. package/dist/ripple/index.cjs.map +0 -10
  117. package/dist/ripple/index.js +0 -3
  118. package/dist/ripple/index.js.map +0 -10
  119. package/dist/solana/index.cjs +0 -3
  120. package/dist/solana/index.cjs.map +0 -10
  121. package/dist/solana/index.js +0 -3
  122. package/dist/solana/index.js.map +0 -10
  123. package/dist/substrate/index.cjs +0 -3
  124. package/dist/substrate/index.cjs.map +0 -11
  125. package/dist/substrate/index.js +0 -3
  126. package/dist/substrate/index.js.map +0 -11
  127. package/dist/utxo/index.cjs +0 -3
  128. package/dist/utxo/index.cjs.map +0 -16
  129. package/dist/utxo/index.js +0 -3
  130. package/dist/utxo/index.js.map +0 -16
  131. /package/dist/{chunk-0f0249b1.js.map → chunk-9bqegm61.js.map} +0 -0
@@ -1,5 +1,6 @@
1
+ import { SwapKitError } from "@swapkit/helpers";
1
2
  import base58check from "bs58check";
2
- // @ts-expect-error
3
+ // @ts-ignore
3
4
  import cashaddr from "cashaddrjs";
4
5
 
5
6
  enum Format {
@@ -83,7 +84,7 @@ function decodeAddress(address: string) {
83
84
  } catch (_error) {
84
85
  // Try to decode as bitpay if cashaddr decoding fails.
85
86
  }
86
- throw new Error("Received an invalid Bitcoin Cash address as input.");
87
+ throw new SwapKitError("toolbox_utxo_invalid_address", { address });
87
88
  }
88
89
 
89
90
  function decodeBase58Address(address: string) {
@@ -91,8 +92,7 @@ function decodeBase58Address(address: string) {
91
92
  const payload = base58check.decode(address);
92
93
 
93
94
  // BASE_58_CHECK_PAYLOAD_LENGTH
94
- if (payload.length !== 21)
95
- throw new Error("Received an invalid Bitcoin Cash address as input.");
95
+ if (payload.length !== 21) throw new SwapKitError("toolbox_utxo_invalid_address", { address });
96
96
  const versionByte = payload[0];
97
97
  const hash = Array.prototype.slice.call(payload, 1);
98
98
 
@@ -116,10 +116,10 @@ function decodeBase58Address(address: string) {
116
116
  return { hash, format: Format.Bitpay, network: UtxoNetwork.Mainnet, type: Type.P2SH };
117
117
 
118
118
  default:
119
- throw new Error("Received an invalid Bitcoin Cash address as input.");
119
+ throw new SwapKitError("toolbox_utxo_invalid_address", { address });
120
120
  }
121
121
  } catch (_error) {
122
- throw new Error("Received an invalid Bitcoin Cash address as input.");
122
+ throw new SwapKitError("toolbox_utxo_invalid_address", { address });
123
123
  }
124
124
  }
125
125
 
@@ -141,7 +141,7 @@ function decodeCashAddress(address: string) {
141
141
  }
142
142
  }
143
143
 
144
- throw new Error("Received an invalid Bitcoin Cash address as input.");
144
+ throw new SwapKitError("toolbox_utxo_invalid_address", { address });
145
145
  }
146
146
 
147
147
  function decodeCashAddressWithPrefix(address: string): DecodedType {
@@ -155,7 +155,7 @@ function decodeCashAddressWithPrefix(address: string): DecodedType {
155
155
  type: type === "P2PKH" ? Type.P2PKH : Type.P2SH,
156
156
  };
157
157
  } catch (_error) {
158
- throw new Error("Received an invalid Bitcoin Cash address as input.");
158
+ throw new SwapKitError("toolbox_utxo_invalid_address", { address });
159
159
  }
160
160
  }
161
161
 
@@ -1,4 +1,4 @@
1
- import { Chain, type UTXOChain } from "@swapkit/helpers";
1
+ import { Chain, SwapKitError, type UTXOChain } from "@swapkit/helpers";
2
2
 
3
3
  import {
4
4
  TX_OVERHEAD,
@@ -20,8 +20,10 @@ export const getDustThreshold = (chain: UTXOChain) => {
20
20
  return 5500;
21
21
  case Chain.Dogecoin:
22
22
  return 100000;
23
+ case Chain.Zcash:
24
+ return 546;
23
25
  default:
24
- throw new Error("Invalid Chain");
26
+ throw new SwapKitError("toolbox_utxo_not_supported", { chain });
25
27
  }
26
28
  };
27
29
 
@@ -1,3 +1,5 @@
1
+ import { SwapKitError } from "@swapkit/helpers";
2
+ import { opcodes, script } from "bitcoinjs-lib";
1
3
  import type {
2
4
  TargetOutput,
3
5
  UTXOCalculateTxSizeParams,
@@ -16,8 +18,7 @@ export const OP_RETURN_OVERHEAD = 1 + 8 + 1; //10
16
18
  const TX_INPUT_BASE = 32 + 4 + 1 + 4; // 41
17
19
  const TX_INPUT_PUBKEYHASH = 107;
18
20
 
19
- export async function compileMemo(memo: string) {
20
- const { script, opcodes } = await import("bitcoinjs-lib");
21
+ export function compileMemo(memo: string) {
21
22
  const data = Buffer.from(memo, "utf8"); // converts MEMO to buffer
22
23
  return script.compile([opcodes.OP_RETURN as number, data]); // Compile OP_RETURN script
23
24
  }
@@ -60,7 +61,7 @@ export const getScriptTypeForAddress = (address: string) => {
60
61
  ) {
61
62
  return UTXOScriptType.P2PKH;
62
63
  }
63
- throw new Error("Invalid address");
64
+ throw new SwapKitError("toolbox_utxo_invalid_address", { address });
64
65
  };
65
66
 
66
67
  export const calculateTxSize = ({ inputs, outputs, feeRate }: UTXOCalculateTxSizeParams) => {
package/src/utxo/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./toolbox";
2
2
  export * from "./toolbox/bitcoinCash";
3
3
  export * from "./toolbox/utxo";
4
+ export * from "./toolbox/zcash";
4
5
  export * from "./helpers";
5
6
  export * from "./types";
@@ -1,12 +1,20 @@
1
+ import {
2
+ Transaction,
3
+ TransactionBuilder,
4
+ address as bchAddress,
5
+ // @ts-ignore
6
+ } from "@psf/bitcoincashjs-lib";
1
7
  import {
2
8
  Chain,
3
9
  type ChainSigner,
4
10
  type DerivationPathArray,
5
11
  FeeOption,
6
12
  NetworkDerivationPath,
13
+ SwapKitError,
7
14
  derivationPathToString,
8
15
  updateDerivationPath,
9
16
  } from "@swapkit/helpers";
17
+ import { Psbt } from "bitcoinjs-lib";
10
18
  import type { UtxoToolboxParams } from ".";
11
19
  import {
12
20
  accumulative,
@@ -130,13 +138,8 @@ async function createTransaction({
130
138
  feeRate,
131
139
  sender,
132
140
  }: UTXOBuildTxParams) {
133
- const {
134
- Transaction,
135
- TransactionBuilder,
136
- address: bchAddress,
137
- // @ts-ignore
138
- } = await import("@psf/bitcoincashjs-lib");
139
- if (!bchValidateAddress(recipient)) throw new Error("Invalid address");
141
+ if (!bchValidateAddress(recipient))
142
+ throw new SwapKitError("toolbox_utxo_invalid_address", { address: recipient });
140
143
  const utxos = await getUtxoApi(chain).scanUTXOs({
141
144
  address: stripToCashAddress(sender),
142
145
  fetchTxHex: true,
@@ -158,9 +161,10 @@ async function createTransaction({
158
161
  });
159
162
 
160
163
  // .inputs and .outputs will be undefined if no solution was found
161
- if (!(inputs && outputs)) throw new Error("Balance insufficient for transaction");
164
+ if (!(inputs && outputs))
165
+ throw new SwapKitError("toolbox_utxo_insufficient_balance", { sender, assetValue });
162
166
  const getNetwork = await getUtxoNetwork();
163
- const builder = new TransactionBuilder(getNetwork(chain));
167
+ const builder = new TransactionBuilder(getNetwork(chain)) as TransactionBuilderType;
164
168
 
165
169
  await Promise.all(
166
170
  inputs.map(async (utxo: UTXOType) => {
@@ -203,8 +207,11 @@ function transfer({
203
207
  ...rest
204
208
  }: UTXOTransferParams) {
205
209
  const from = await signer?.getAddress();
206
- if (!(signer && from)) throw new Error("Signer must provider address");
207
- if (!recipient) throw new Error("Recipient address must be provided");
210
+ if (!(signer && from)) throw new SwapKitError("toolbox_utxo_no_signer");
211
+ if (!recipient)
212
+ throw new SwapKitError("toolbox_utxo_invalid_params", {
213
+ error: "Recipient address must be provided",
214
+ });
208
215
 
209
216
  const feeRate = rest.feeRate || (await getFeeRates())[feeOptionKey];
210
217
 
@@ -226,9 +233,9 @@ function transfer({
226
233
 
227
234
  // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: TODO: refactor
228
235
  async function buildTx({ assetValue, recipient, memo, feeRate, sender }: UTXOBuildTxParams) {
229
- const { Psbt } = await import("bitcoinjs-lib");
230
236
  const recipientCashAddress = toCashAddress(recipient);
231
- if (!bchValidateAddress(recipientCashAddress)) throw new Error("Invalid address");
237
+ if (!bchValidateAddress(recipientCashAddress))
238
+ throw new SwapKitError("toolbox_utxo_invalid_address", { address: recipientCashAddress });
232
239
 
233
240
  const utxos = await getUtxoApi(chain).scanUTXOs({
234
241
  address: stripToCashAddress(sender),
@@ -259,7 +266,8 @@ async function buildTx({ assetValue, recipient, memo, feeRate, sender }: UTXOBui
259
266
  });
260
267
 
261
268
  // .inputs and .outputs will be undefined if no solution was found
262
- if (!(inputs && outputs)) throw new Error("Balance insufficient for transaction");
269
+ if (!(inputs && outputs))
270
+ throw new SwapKitError("toolbox_utxo_insufficient_balance", { sender, assetValue });
263
271
  const getNetwork = await getUtxoNetwork();
264
272
  const psbt = new Psbt({ network: getNetwork(chain) }); // Network-specific
265
273
 
@@ -2,6 +2,7 @@ import {
2
2
  Chain,
3
3
  type ChainSigner,
4
4
  type DerivationPathArray,
5
+ SwapKitError,
5
6
  type UTXOChain,
6
7
  } from "@swapkit/helpers";
7
8
  import type { Psbt } from "bitcoinjs-lib";
@@ -9,11 +10,13 @@ import type { Psbt } from "bitcoinjs-lib";
9
10
  import type { TransactionBuilderType, TransactionType, UTXOType } from "../types";
10
11
  import { createBCHToolbox } from "./bitcoinCash";
11
12
  import { createUTXOToolbox } from "./utxo";
13
+ import { createZcashToolbox } from "./zcash";
12
14
 
13
15
  type BCHToolbox = Awaited<ReturnType<typeof createBCHToolbox>>;
14
16
  type CommonUTXOToolbox = Awaited<
15
- ReturnType<typeof createUTXOToolbox<Exclude<UTXOChain, Chain.BitcoinCash>>>
17
+ ReturnType<typeof createUTXOToolbox<Exclude<UTXOChain, Chain.BitcoinCash | Chain.Zcash>>>
16
18
  >;
19
+ type ZcashToolbox = Awaited<ReturnType<typeof createZcashToolbox>>;
17
20
 
18
21
  export type UTXOToolboxes = {
19
22
  [Chain.BitcoinCash]: BCHToolbox;
@@ -21,6 +24,7 @@ export type UTXOToolboxes = {
21
24
  [Chain.Dogecoin]: CommonUTXOToolbox;
22
25
  [Chain.Litecoin]: CommonUTXOToolbox;
23
26
  [Chain.Dash]: CommonUTXOToolbox;
27
+ [Chain.Zcash]: ZcashToolbox;
24
28
  };
25
29
 
26
30
  export type UTXOWallets = {
@@ -35,6 +39,9 @@ export type UtxoToolboxParams = {
35
39
  [Chain.Dogecoin]: { signer: ChainSigner<Psbt, Psbt> };
36
40
  [Chain.Litecoin]: { signer: ChainSigner<Psbt, Psbt> };
37
41
  [Chain.Dash]: { signer: ChainSigner<Psbt, Psbt> };
42
+ [Chain.Zcash]: {
43
+ signer?: ChainSigner<Psbt, Psbt>;
44
+ };
38
45
  };
39
46
 
40
47
  export async function getUtxoToolbox<T extends keyof UTXOToolboxes>(
@@ -53,19 +60,24 @@ export async function getUtxoToolbox<T extends keyof UTXOToolboxes>(
53
60
  return toolbox as UTXOToolboxes[T];
54
61
  }
55
62
 
63
+ case Chain.Zcash: {
64
+ const toolbox = await createZcashToolbox(params as UtxoToolboxParams[Chain.Zcash]);
65
+ return toolbox as UTXOToolboxes[T];
66
+ }
67
+
56
68
  case Chain.Bitcoin:
57
69
  case Chain.Dogecoin:
58
70
  case Chain.Litecoin:
59
71
  case Chain.Dash: {
60
72
  const toolbox = await createUTXOToolbox({
61
73
  chain,
62
- ...(params as UtxoToolboxParams[Exclude<T, Chain.BitcoinCash>]),
74
+ ...(params as UtxoToolboxParams[Exclude<T, Chain.BitcoinCash | Chain.Zcash>]),
63
75
  });
64
- return toolbox as UTXOToolboxes[Exclude<T, Chain.BitcoinCash>];
76
+ return toolbox as UTXOToolboxes[Exclude<T, Chain.BitcoinCash | Chain.Zcash>];
65
77
  }
66
78
 
67
79
  default:
68
- throw new Error(`Chain ${chain} is not supported`);
80
+ throw new SwapKitError("toolbox_utxo_not_supported", { chain });
69
81
  }
70
82
  }
71
83
 
@@ -6,13 +6,16 @@ import {
6
6
  type DerivationPathArray,
7
7
  FeeOption,
8
8
  NetworkDerivationPath,
9
+ SwapKitError,
9
10
  SwapKitNumber,
10
11
  type UTXOChain,
12
+ applyFeeMultiplier,
11
13
  derivationPathToString,
12
14
  updateDerivationPath,
13
15
  } from "@swapkit/helpers";
14
- import type { Psbt } from "bitcoinjs-lib";
16
+ import { Psbt, address as btcLibAddress, initEccLib, payments } from "bitcoinjs-lib";
15
17
  import type { ECPairInterface } from "ecpair";
18
+ import ECPairFactory from "ecpair";
16
19
  import type { UtxoToolboxParams } from ".";
17
20
  import { getBalance } from "../../utils";
18
21
  import {
@@ -34,9 +37,16 @@ import type {
34
37
  } from "../types";
35
38
  import { bchValidateAddress } from "./bitcoinCash";
36
39
 
40
+ import secp256k1 from "@bitcoinerlab/secp256k1";
41
+ // @ts-ignore
42
+ import { ECPair, HDNode } from "@psf/bitcoincashjs-lib";
43
+ import { HDKey } from "@scure/bip32";
44
+ import { mnemonicToSeedSync } from "@scure/bip39";
45
+ import { validateZcashAddress } from "./zcash";
46
+
37
47
  export const nonSegwitChains = [Chain.Dash, Chain.Dogecoin];
38
48
 
39
- async function addInputsAndOutputs({
49
+ function addInputsAndOutputs({
40
50
  inputs,
41
51
  outputs,
42
52
  chain,
@@ -62,9 +72,6 @@ async function addInputsAndOutputs({
62
72
  psbt.addInput({ hash: utxo.hash, index: utxo.index, ...witnessInfo, ...nonWitnessInfo });
63
73
  }
64
74
 
65
- const { initEccLib } = await import("bitcoinjs-lib");
66
- const secp256k1 = await import("@bitcoinerlab/secp256k1");
67
-
68
75
  for (const output of outputs) {
69
76
  const address = "address" in output && output.address ? output.address : sender;
70
77
  const hasOutputScript = output.script;
@@ -103,8 +110,6 @@ async function createTransaction({
103
110
  inputs: UTXOType[];
104
111
  }> {
105
112
  const chain = assetValue.chain as UTXOChain;
106
-
107
- const { Psbt } = await import("bitcoinjs-lib");
108
113
  const compiledMemo = memo ? await compileMemo(memo) : null;
109
114
 
110
115
  const inputsAndOutputs = await getInputsAndTargetOutputs({
@@ -118,7 +123,8 @@ async function createTransaction({
118
123
  const { inputs, outputs } = accumulative({ ...inputsAndOutputs, feeRate, chain });
119
124
 
120
125
  // .inputs and .outputs will be undefined if no solution was found
121
- if (!(inputs && outputs)) throw new Error("Insufficient Balance for transaction");
126
+ if (!(inputs && outputs))
127
+ throw new SwapKitError("toolbox_utxo_insufficient_balance", { sender, assetValue });
122
128
  const getNetwork = await getUtxoNetwork();
123
129
  const psbt = new Psbt({ network: getNetwork(chain) });
124
130
 
@@ -141,8 +147,6 @@ async function createTransaction({
141
147
  }
142
148
 
143
149
  export async function getUTXOAddressValidator() {
144
- const secp256k1 = await import("@bitcoinerlab/secp256k1");
145
- const { initEccLib, address: btcLibAddress } = await import("bitcoinjs-lib");
146
150
  const getNetwork = await getUtxoNetwork();
147
151
 
148
152
  return function validateAddress({ address, chain }: { address: string; chain: UTXOChain }) {
@@ -150,6 +154,10 @@ export async function getUTXOAddressValidator() {
150
154
  return bchValidateAddress(address);
151
155
  }
152
156
 
157
+ if (chain === Chain.Zcash) {
158
+ return validateZcashAddress(address);
159
+ }
160
+
153
161
  try {
154
162
  initEccLib(secp256k1);
155
163
  btcLibAddress.toOutputScript(address, getNetwork(chain));
@@ -348,6 +356,7 @@ type CreateKeysForPathReturnType = {
348
356
  [Chain.Dash]: ECPairInterface;
349
357
  [Chain.Dogecoin]: ECPairInterface;
350
358
  [Chain.Litecoin]: ECPairInterface;
359
+ [Chain.Zcash]: ECPairInterface;
351
360
  };
352
361
 
353
362
  export async function getCreateKeysForPath<T extends keyof CreateKeysForPathReturnType>(
@@ -359,13 +368,7 @@ export async function getCreateKeysForPath<T extends keyof CreateKeysForPathRetu
359
368
  derivationPath?: string;
360
369
  }) => CreateKeysForPathReturnType[T]
361
370
  > {
362
- const { ECPairFactory } = await import("ecpair");
363
- const secp256k1 = await import("@bitcoinerlab/secp256k1");
364
- const { HDKey } = await import("@scure/bip32");
365
- const { mnemonicToSeedSync } = await import("@scure/bip39");
366
371
  const getNetwork = await getUtxoNetwork();
367
- // @ts-ignore
368
- const { HDNode, ECPair } = await import("@psf/bitcoincashjs-lib");
369
372
 
370
373
  switch (chain) {
371
374
  case Chain.BitcoinCash: {
@@ -379,7 +382,8 @@ export async function getCreateKeysForPath<T extends keyof CreateKeysForPathRetu
379
382
  if (wif) {
380
383
  return ECPair.fromWIF(wif, network) as BchECPair;
381
384
  }
382
- if (!phrase) throw new Error("No phrase provided");
385
+ if (!phrase)
386
+ throw new SwapKitError("toolbox_utxo_invalid_params", { error: "No phrase provided" });
383
387
 
384
388
  const masterHDNode = HDNode.fromSeedBuffer(
385
389
  Buffer.from(mnemonicToSeedSync(phrase)),
@@ -397,13 +401,17 @@ export async function getCreateKeysForPath<T extends keyof CreateKeysForPathRetu
397
401
  case Chain.Bitcoin:
398
402
  case Chain.Dogecoin:
399
403
  case Chain.Litecoin:
404
+ case Chain.Zcash:
400
405
  case Chain.Dash: {
401
406
  return function createKeysForPath({
402
407
  phrase,
403
408
  wif,
404
409
  derivationPath,
405
410
  }: { phrase?: string; wif?: string; derivationPath: string }) {
406
- if (!(wif || phrase)) throw new Error("Either phrase or wif must be provided");
411
+ if (!(wif || phrase))
412
+ throw new SwapKitError("toolbox_utxo_invalid_params", {
413
+ error: "Either phrase or wif must be provided",
414
+ });
407
415
 
408
416
  const factory = ECPairFactory(secp256k1);
409
417
  const network = getNetwork(chain);
@@ -412,7 +420,10 @@ export async function getCreateKeysForPath<T extends keyof CreateKeysForPathRetu
412
420
 
413
421
  const seed = mnemonicToSeedSync(phrase as string);
414
422
  const master = HDKey.fromMasterSeed(seed, network).derive(derivationPath);
415
- if (!master.privateKey) throw new Error("Could not get private key from phrase");
423
+ if (!master.privateKey)
424
+ throw new SwapKitError("toolbox_utxo_invalid_params", {
425
+ error: "Could not get private key from phrase",
426
+ });
416
427
 
417
428
  return factory.fromPrivateKey(Buffer.from(master.privateKey), { network });
418
429
  } as (params: {
@@ -422,20 +433,21 @@ export async function getCreateKeysForPath<T extends keyof CreateKeysForPathRetu
422
433
  }) => CreateKeysForPathReturnType[T];
423
434
  }
424
435
  default:
425
- throw new Error(`Chain ${chain} is not supported`);
436
+ throw new SwapKitError("toolbox_utxo_not_supported", { chain });
426
437
  }
427
438
  }
428
439
 
429
440
  export async function addressFromKeysGetter(chain: UTXOChain) {
430
- const { payments } = await import("bitcoinjs-lib");
431
441
  const getNetwork = await getUtxoNetwork();
432
442
 
433
443
  return function getAddressFromKeys(keys: ECPairInterface | BchECPair) {
434
- if (!keys) throw new Error("Keys must be provided");
444
+ if (!keys)
445
+ throw new SwapKitError("toolbox_utxo_invalid_params", { error: "Keys must be provided" });
435
446
 
436
447
  const method = nonSegwitChains.includes(chain) ? payments.p2pkh : payments.p2wpkh;
437
448
  const { address } = method({ pubkey: keys.publicKey as Buffer, network: getNetwork(chain) });
438
- if (!address) throw new Error("Address not defined");
449
+ if (!address)
450
+ throw new SwapKitError("toolbox_utxo_invalid_address", { error: "Address not defined" });
439
451
 
440
452
  return address;
441
453
  };
@@ -453,8 +465,11 @@ function transfer(signer?: ChainSigner<Psbt, Psbt>) {
453
465
 
454
466
  const chain = assetValue.chain as UTXOChain;
455
467
 
456
- if (!(signer && from)) throw new Error("From address must be provided");
457
- if (!recipient) throw new Error("Recipient address must be provided");
468
+ if (!(signer && from)) throw new SwapKitError("toolbox_utxo_no_signer");
469
+ if (!recipient)
470
+ throw new SwapKitError("toolbox_utxo_invalid_params", {
471
+ error: "Recipient address must be provided",
472
+ });
458
473
  const txFeeRate = feeRate || (await getFeeRates(chain))[feeOptionKey || FeeOption.Fast];
459
474
 
460
475
  const { psbt } = await createTransaction({
@@ -476,8 +491,8 @@ async function getFeeRates(chain: UTXOChain) {
476
491
 
477
492
  return {
478
493
  [FeeOption.Average]: suggestedFeeRate,
479
- [FeeOption.Fast]: suggestedFeeRate * 1.5,
480
- [FeeOption.Fastest]: suggestedFeeRate * 2.0,
494
+ [FeeOption.Fast]: applyFeeMultiplier(suggestedFeeRate, FeeOption.Fast),
495
+ [FeeOption.Fastest]: applyFeeMultiplier(suggestedFeeRate, FeeOption.Fastest),
481
496
  };
482
497
  }
483
498
 
@@ -0,0 +1,208 @@
1
+ import secp256k1 from "@bitcoinerlab/secp256k1";
2
+ import { HDKey } from "@scure/bip32";
3
+ import { mnemonicToSeedSync } from "@scure/bip39";
4
+ import {
5
+ Chain,
6
+ type ChainSigner,
7
+ type DerivationPathArray,
8
+ FeeOption,
9
+ NetworkDerivationPath,
10
+ SKConfig,
11
+ SwapKitError,
12
+ derivationPathToString,
13
+ updateDerivationPath,
14
+ } from "@swapkit/helpers";
15
+ import type { Psbt } from "bitcoinjs-lib";
16
+ import { hash160 } from "bitcoinjs-lib/src/crypto";
17
+ import bs58check from "bs58check";
18
+ import ECPairFactory from "ecpair";
19
+ import { P, match } from "ts-pattern";
20
+ import { getUtxoNetwork } from "../helpers";
21
+ import type { UTXOBuildTxParams, UTXOTransferParams } from "../types";
22
+ import { createUTXOToolbox } from "./utxo";
23
+
24
+ const chain = Chain.Zcash;
25
+ const network = getUtxoNetwork()(chain);
26
+
27
+ /**
28
+ * Custom Zcash address generation that handles 2-byte prefixes
29
+ *
30
+ * Zcash transparent addresses use 2-byte version prefixes, unlike Bitcoin's single byte.
31
+ * This is incompatible with bitcoinjs-lib's payment methods which expect UInt8 values.
32
+ *
33
+ * @param publicKey - The public key buffer to generate address from
34
+ * @param isTestnet - Whether to generate testnet or mainnet address
35
+ * @returns A valid Zcash transparent address (t1... for mainnet, tm... for testnet)
36
+ */
37
+ function generateZcashAddress(publicKey: Buffer, isTestnet = false): string {
38
+ // Hash the public key using RIPEMD160(SHA256(pubkey))
39
+ const publicKeyHash = hash160(publicKey);
40
+
41
+ // Zcash uses 2-byte prefixes for transparent addresses
42
+ // These prefixes ensure addresses start with expected characters when base58 encoded
43
+ const prefix = isTestnet
44
+ ? Buffer.from([0x1c, 0xba]) // testnet prefix (results in tm... addresses)
45
+ : Buffer.from([0x1c, 0xb8]); // mainnet prefix (results in t1... addresses)
46
+
47
+ // Combine prefix + hash (22 bytes total: 2 byte prefix + 20 byte hash)
48
+ const payload = Buffer.concat([prefix, publicKeyHash]);
49
+
50
+ // Encode with base58check for final address
51
+ return bs58check.encode(payload);
52
+ }
53
+
54
+ export function validateZcashAddress(address: string) {
55
+ try {
56
+ // Shielded addresses are not supported
57
+ if (address.startsWith("z")) {
58
+ console.warn(
59
+ "Shielded Zcash addresses (z-addresses) are not supported. Use transparent addresses (t1/t3) only.",
60
+ );
61
+ return false;
62
+ }
63
+
64
+ const isMainnet = address.startsWith("t1");
65
+ const isTestnet = address.startsWith("t3");
66
+
67
+ if (!(isMainnet || isTestnet)) {
68
+ return false;
69
+ }
70
+
71
+ // Verify network matches address type
72
+ const { isStagenet } = SKConfig.get("envs");
73
+ if ((isMainnet && isStagenet) || (isTestnet && !isStagenet)) {
74
+ return false;
75
+ }
76
+
77
+ return validateBase58Check(address, network);
78
+ } catch {
79
+ return false;
80
+ }
81
+ }
82
+
83
+ function validateBase58Check(
84
+ address: string,
85
+ network: ReturnType<ReturnType<typeof getUtxoNetwork>>,
86
+ ) {
87
+ try {
88
+ const decoded = bs58check.decode(address);
89
+
90
+ if (decoded.length < 21) {
91
+ return false;
92
+ }
93
+
94
+ const version = decoded[0];
95
+ return version === network.pubKeyHash || version === network.scriptHash;
96
+ } catch {
97
+ return false;
98
+ }
99
+ }
100
+
101
+ const ECPair = ECPairFactory(secp256k1);
102
+
103
+ type ZcashSigner = ChainSigner<Psbt, Psbt>;
104
+
105
+ async function createZcashSignerFromPhrase({
106
+ phrase,
107
+ derivationPathString,
108
+ }: {
109
+ phrase: string;
110
+ derivationPathString: string;
111
+ }) {
112
+ const seed = mnemonicToSeedSync(phrase);
113
+ const root = HDKey.fromMasterSeed(seed);
114
+
115
+ const node = root.derive(derivationPathString);
116
+
117
+ if (!node.privateKey) {
118
+ throw new Error("Unable to derive private key");
119
+ }
120
+
121
+ const keyPair = ECPair.fromPrivateKey(Buffer.from(node.privateKey), { network });
122
+
123
+ const { isStagenet } = SKConfig.get("envs");
124
+ const address = generateZcashAddress(keyPair.publicKey, isStagenet);
125
+
126
+ return {
127
+ getAddress() {
128
+ return Promise.resolve(address);
129
+ },
130
+
131
+ signTransaction(psbt: Psbt) {
132
+ for (let i = 0; i < psbt.inputCount; i++) {
133
+ psbt.signInput(i, keyPair);
134
+ }
135
+ return Promise.resolve(psbt);
136
+ },
137
+ };
138
+ }
139
+
140
+ export async function createZcashToolbox(
141
+ toolboxParams:
142
+ | {
143
+ signer?: ZcashSigner;
144
+ }
145
+ | {
146
+ phrase?: string;
147
+ derivationPath?: DerivationPathArray;
148
+ index?: number;
149
+ },
150
+ ) {
151
+ const signer = await match(toolboxParams)
152
+ .with({ signer: P.not(P.nullish) }, ({ signer }) => Promise.resolve(signer))
153
+ .with({ phrase: P.string }, ({ phrase, derivationPath, index = 0 }) => {
154
+ // Handle derivation path processing at toolbox level
155
+ const baseDerivationPath = derivationPath ||
156
+ NetworkDerivationPath[Chain.Zcash] || [44, 133, 0, 0, 0];
157
+ const updatedDerivationPath = updateDerivationPath(baseDerivationPath, { index });
158
+ const derivationPathString = derivationPathToString(updatedDerivationPath);
159
+
160
+ return createZcashSignerFromPhrase({ phrase, derivationPathString });
161
+ })
162
+ .otherwise(() => Promise.resolve(undefined));
163
+
164
+ const { getFeeRates, broadcastTx, ...toolbox } = await createUTXOToolbox({
165
+ chain: Chain.Zcash,
166
+ signer,
167
+ });
168
+
169
+ function getAddressFromKeys(keys: { getAddress: () => string }) {
170
+ return keys.getAddress();
171
+ }
172
+
173
+ async function transfer({
174
+ recipient,
175
+ assetValue,
176
+ feeOptionKey = FeeOption.Fast,
177
+ ...rest
178
+ }: UTXOTransferParams) {
179
+ const from = await signer?.getAddress();
180
+ if (!(signer && from)) throw new SwapKitError("toolbox_utxo_no_signer");
181
+
182
+ const feeRate = rest.feeRate || (await getFeeRates())[feeOptionKey];
183
+
184
+ const buildTxParams: UTXOBuildTxParams = {
185
+ ...rest,
186
+ assetValue,
187
+ feeRate,
188
+ recipient,
189
+ sender: from,
190
+ };
191
+
192
+ const { psbt } = await toolbox.createTransaction(buildTxParams);
193
+ const signedPsbt = await signer.signTransaction(psbt);
194
+ signedPsbt.finalizeAllInputs();
195
+ const txHex = signedPsbt.extractTransaction().toHex();
196
+
197
+ return broadcastTx(txHex);
198
+ }
199
+
200
+ return {
201
+ ...toolbox,
202
+ broadcastTx,
203
+ getFeeRates,
204
+ transfer,
205
+ getAddressFromKeys,
206
+ validateAddress: validateZcashAddress,
207
+ };
208
+ }
package/src/utxo/types.ts CHANGED
@@ -52,4 +52,6 @@ export type TransactionBuilderType = {
52
52
  signatureAlgorithm?: string,
53
53
  ): void;
54
54
  build(): TransactionType;
55
+ addOutput(addressOrScriptBuffer: string | Buffer, value: number): void;
56
+ addInput(txHash: string | Buffer, vout: number, sequence?: number, prevOutScript?: Buffer): void;
55
57
  };
@@ -1,4 +0,0 @@
1
- import{AssetValue as c,BaseDecimal as p}from"@swapkit/helpers";import{SwapKitApi as u}from"@swapkit/helpers/api";var l=typeof process!=="undefined"&&process.pid?process.pid.toString(36):"",a=0;function g(){function t(){let e=Date.now(),n=a||e;return a=n,e>a?e:n+1}return l+t().toString(36)}function w(t){return async function e(n,i=!0){return(await u.getChainBalance({chain:t,address:n,scamFilter:i})).map(({identifier:r,value:o,decimal:s})=>{return new c({decimal:s||p[t],value:o,identifier:r})})}}
2
- export{g as a,w as b};
3
-
4
- //# debugId=7D1F4061F0771C8264756E2164756E21