@swapkit/toolboxes 1.0.0-beta.16 → 1.0.0-beta.18

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 (95) hide show
  1. package/dist/{chunk-p1kdg37m.js → chunk-38ztynv0.js} +1 -1
  2. package/dist/{chunk-p1kdg37m.js.map → chunk-38ztynv0.js.map} +1 -1
  3. package/dist/{chunk-fjfxga2v.js → chunk-5yxc1e69.js} +1 -1
  4. package/dist/{chunk-fjfxga2v.js.map → chunk-5yxc1e69.js.map} +1 -1
  5. package/dist/{chunk-0f0249b1.js → chunk-cv69ewns.js} +1 -1
  6. package/dist/chunk-vhc011em.js +4 -0
  7. package/dist/{chunk-0h4xdrwz.js.map → chunk-vhc011em.js.map} +2 -2
  8. package/dist/src/cosmos/index.cjs +3 -0
  9. package/dist/src/cosmos/index.cjs.map +16 -0
  10. package/dist/src/cosmos/index.js +3 -0
  11. package/dist/src/cosmos/index.js.map +16 -0
  12. package/dist/src/evm/index.cjs +3 -0
  13. package/dist/src/evm/index.cjs.map +18 -0
  14. package/dist/src/evm/index.js +3 -0
  15. package/dist/src/evm/index.js.map +18 -0
  16. package/dist/src/index.cjs +3 -0
  17. package/dist/src/index.cjs.map +10 -0
  18. package/dist/src/index.js +3 -0
  19. package/dist/src/index.js.map +10 -0
  20. package/dist/{radix → src/radix}/index.cjs +2 -2
  21. package/dist/src/radix/index.cjs.map +10 -0
  22. package/dist/src/radix/index.js +3 -0
  23. package/dist/src/radix/index.js.map +10 -0
  24. package/dist/src/ripple/index.js +3 -0
  25. package/dist/{ripple → src/ripple}/index.js.map +1 -1
  26. package/dist/src/solana/index.js +3 -0
  27. package/dist/{solana → src/solana}/index.js.map +1 -1
  28. package/dist/src/substrate/index.cjs +3 -0
  29. package/dist/src/substrate/index.cjs.map +11 -0
  30. package/dist/src/substrate/index.js +3 -0
  31. package/dist/src/substrate/index.js.map +11 -0
  32. package/dist/src/tron/index.cjs +3 -0
  33. package/dist/src/tron/index.cjs.map +11 -0
  34. package/dist/src/tron/index.js +3 -0
  35. package/dist/src/tron/index.js.map +11 -0
  36. package/dist/src/utxo/index.cjs +3 -0
  37. package/dist/src/utxo/index.cjs.map +16 -0
  38. package/dist/src/utxo/index.js +3 -0
  39. package/dist/src/utxo/index.js.map +16 -0
  40. package/package.json +24 -18
  41. package/src/cosmos/thorchainUtils/addressFormat.ts +4 -1
  42. package/src/cosmos/thorchainUtils/messages.ts +2 -2
  43. package/src/cosmos/toolbox/cosmos.ts +2 -4
  44. package/src/cosmos/toolbox/index.ts +2 -2
  45. package/src/cosmos/toolbox/thorchain.ts +3 -2
  46. package/src/cosmos/util.ts +10 -3
  47. package/src/evm/helpers.ts +4 -3
  48. package/src/evm/toolbox/baseEVMToolbox.ts +15 -12
  49. package/src/evm/toolbox/index.ts +2 -2
  50. package/src/evm/toolbox/op.ts +10 -2
  51. package/src/index.ts +104 -104
  52. package/src/radix/index.ts +8 -2
  53. package/src/substrate/substrate.ts +1 -1
  54. package/src/tron/helpers/trc20.abi.ts +40 -0
  55. package/src/tron/index.ts +16 -0
  56. package/src/tron/toolbox.ts +328 -0
  57. package/src/tron/types.ts +22 -0
  58. package/src/utxo/helpers/api.ts +25 -11
  59. package/src/utxo/helpers/bchaddrjs.ts +8 -8
  60. package/src/utxo/helpers/coinselect.ts +2 -2
  61. package/src/utxo/helpers/txSize.ts +2 -1
  62. package/src/utxo/toolbox/bitcoinCash.ts +14 -6
  63. package/src/utxo/toolbox/index.ts +2 -1
  64. package/src/utxo/toolbox/utxo.ts +23 -9
  65. package/dist/chunk-0h4xdrwz.js +0 -4
  66. package/dist/cosmos/index.cjs +0 -3
  67. package/dist/cosmos/index.cjs.map +0 -16
  68. package/dist/cosmos/index.js +0 -3
  69. package/dist/cosmos/index.js.map +0 -16
  70. package/dist/evm/index.cjs +0 -3
  71. package/dist/evm/index.cjs.map +0 -18
  72. package/dist/evm/index.js +0 -3
  73. package/dist/evm/index.js.map +0 -18
  74. package/dist/index.cjs +0 -3
  75. package/dist/index.cjs.map +0 -10
  76. package/dist/index.js +0 -3
  77. package/dist/index.js.map +0 -10
  78. package/dist/radix/index.cjs.map +0 -10
  79. package/dist/radix/index.js +0 -3
  80. package/dist/radix/index.js.map +0 -10
  81. package/dist/ripple/index.js +0 -3
  82. package/dist/solana/index.js +0 -3
  83. package/dist/substrate/index.cjs +0 -3
  84. package/dist/substrate/index.cjs.map +0 -11
  85. package/dist/substrate/index.js +0 -3
  86. package/dist/substrate/index.js.map +0 -11
  87. package/dist/utxo/index.cjs +0 -3
  88. package/dist/utxo/index.cjs.map +0 -16
  89. package/dist/utxo/index.js +0 -3
  90. package/dist/utxo/index.js.map +0 -16
  91. /package/dist/{chunk-0f0249b1.js.map → chunk-cv69ewns.js.map} +0 -0
  92. /package/dist/{ripple → src/ripple}/index.cjs +0 -0
  93. /package/dist/{ripple → src/ripple}/index.cjs.map +0 -0
  94. /package/dist/{solana → src/solana}/index.cjs +0 -0
  95. /package/dist/{solana → src/solana}/index.cjs.map +0 -0
@@ -0,0 +1,328 @@
1
+ import {
2
+ AssetValue,
3
+ Chain,
4
+ NetworkDerivationPath,
5
+ SKConfig,
6
+ SwapKitError,
7
+ derivationPathToString,
8
+ updateDerivationPath,
9
+ warnOnce,
10
+ } from "@swapkit/helpers";
11
+ import { TronWeb } from "tronweb";
12
+ import { P, match } from "ts-pattern";
13
+
14
+ import { trc20ABI } from "./helpers/trc20.abi.js";
15
+ import type {
16
+ TronSignedTransaction,
17
+ TronSigner,
18
+ TronToolboxOptions,
19
+ TronTransaction,
20
+ TronTransferParams,
21
+ } from "./types.js";
22
+
23
+ export async function getTronAddressValidator() {
24
+ const { TronWeb } = await import("tronweb");
25
+ return (address: string) => TronWeb.isAddress(address);
26
+ }
27
+
28
+ export async function getTronPrivateKeyFromMnemonic({
29
+ phrase,
30
+ derivationPath: customPath,
31
+ index,
32
+ }: {
33
+ phrase: string;
34
+ derivationPath?: string;
35
+ index?: number;
36
+ }) {
37
+ const derivationPathToUse =
38
+ customPath ||
39
+ derivationPathToString(
40
+ updateDerivationPath(NetworkDerivationPath[Chain.Tron], { index: index || 0 }),
41
+ );
42
+
43
+ const { HDKey } = await import("@scure/bip32");
44
+ const { mnemonicToSeedSync } = await import("@scure/bip39");
45
+
46
+ const seed = mnemonicToSeedSync(phrase);
47
+ const hdKey = HDKey.fromMasterSeed(seed);
48
+ const derived = hdKey.derive(derivationPathToUse);
49
+
50
+ if (!derived.privateKey) {
51
+ throw new SwapKitError("toolbox_tron_no_signer");
52
+ }
53
+
54
+ return Buffer.from(derived.privateKey).toString("hex");
55
+ }
56
+
57
+ async function createKeysForPath({
58
+ phrase,
59
+ derivationPath,
60
+ }: {
61
+ phrase: string;
62
+ derivationPath: string;
63
+ }): Promise<TronSigner> {
64
+ const { HDKey } = await import("@scure/bip32");
65
+ const { mnemonicToSeedSync } = await import("@scure/bip39");
66
+
67
+ const seed = mnemonicToSeedSync(phrase);
68
+ const hdKey = HDKey.fromMasterSeed(seed);
69
+ const derived = hdKey.derive(derivationPath);
70
+
71
+ if (!derived.privateKey) {
72
+ throw new SwapKitError("toolbox_tron_no_signer");
73
+ }
74
+
75
+ // Convert private key to hex string for TronWeb
76
+ const privateKeyHex = Buffer.from(derived.privateKey).toString("hex");
77
+
78
+ // Create TronWeb instance with the derived private key
79
+ const tronWebWithKey = new TronWeb({
80
+ fullHost: SKConfig.get("rpcUrls")[Chain.Tron],
81
+ privateKey: privateKeyHex,
82
+ });
83
+
84
+ const address = tronWebWithKey.address.fromPrivateKey(privateKeyHex);
85
+
86
+ return {
87
+ getAddress: () => Promise.resolve(typeof address === "string" ? address : ""),
88
+ signTransaction: async (transaction: TronTransaction) => {
89
+ const signedTx = await tronWebWithKey.trx.sign(transaction, privateKeyHex);
90
+ return signedTx;
91
+ },
92
+ };
93
+ }
94
+
95
+ export const createTronToolbox = async (options: TronToolboxOptions = {}) => {
96
+ // Always get configuration from SKConfig
97
+ const rpcUrl = SKConfig.get("rpcUrls")[Chain.Tron];
98
+ // Note: TRON API key support can be added to SKConfig apiKeys when needed
99
+ const headers = undefined; // No API key needed for basic TronGrid access
100
+
101
+ const tronWeb = new TronWeb({
102
+ fullHost: rpcUrl,
103
+ headers,
104
+ });
105
+
106
+ // Handle derivation path and index
107
+ const index = "index" in options ? options.index || 0 : 0;
108
+ const derivationPath = derivationPathToString(
109
+ "derivationPath" in options && options.derivationPath
110
+ ? options.derivationPath
111
+ : updateDerivationPath(NetworkDerivationPath[Chain.Tron], { index }),
112
+ );
113
+
114
+ // Create signer based on options using pattern matching
115
+ const signer: TronSigner | undefined = await match(options)
116
+ .with({ phrase: P.string }, async ({ phrase }) => createKeysForPath({ phrase, derivationPath }))
117
+ .with({ signer: P.any }, ({ signer }) => Promise.resolve(signer as TronSigner))
118
+ .otherwise(() => Promise.resolve(undefined));
119
+
120
+ const getAddress = async () => {
121
+ if (!signer) throw new SwapKitError("toolbox_tron_no_signer");
122
+ return await signer.getAddress();
123
+ };
124
+
125
+ const validateAddress = (address: string) => {
126
+ return tronWeb.isAddress(address);
127
+ };
128
+
129
+ const getContractAddress = (assetValue: AssetValue) => {
130
+ // Use asset.address for TRC20 contracts instead of parsing string
131
+ return assetValue.address;
132
+ };
133
+
134
+ const calculateFeeLimit = () => {
135
+ return 100_000_000; // 100 TRX in SUN
136
+ };
137
+
138
+ const getBalance = async (address: string, scamFilter = true) => {
139
+ const { getBalance: getBalanceFromApi } = await import("../utils.js");
140
+
141
+ try {
142
+ // Use SwapKit API for comprehensive balance fetching (includes TRX + TRC20 tokens)
143
+ const apiBalances = await getBalanceFromApi(Chain.Tron)(address, scamFilter);
144
+
145
+ // If API returns balances, use those
146
+ if (apiBalances.length > 0) {
147
+ return apiBalances;
148
+ }
149
+
150
+ // Fallback to on-chain TRX balance if API fails or returns empty
151
+ const trxBalanceInSun = await tronWeb.trx.getBalance(address);
152
+ return [
153
+ AssetValue.from({
154
+ chain: Chain.Tron,
155
+ value: trxBalanceInSun,
156
+ fromBaseDecimal: 6, // TRX has 6 decimals
157
+ }),
158
+ ];
159
+ } catch (error) {
160
+ warnOnce(
161
+ true,
162
+ `Failed to get Tron balance for ${address}: ${error instanceof Error ? error.message : error}`,
163
+ );
164
+
165
+ // Final fallback: try to get just the native TRX balance
166
+ try {
167
+ const trxBalanceInSun = await tronWeb.trx.getBalance(address);
168
+ return [
169
+ AssetValue.from({
170
+ chain: Chain.Tron,
171
+ value: trxBalanceInSun,
172
+ fromBaseDecimal: 6,
173
+ }),
174
+ ];
175
+ } catch (fallbackError) {
176
+ warnOnce(
177
+ true,
178
+ `Failed to get native TRX balance for ${address}: ${fallbackError instanceof Error ? fallbackError.message : fallbackError}`,
179
+ );
180
+ return [];
181
+ }
182
+ }
183
+ };
184
+
185
+ const transfer = async ({ recipient, assetValue, memo }: TronTransferParams) => {
186
+ if (!signer) throw new SwapKitError("toolbox_tron_no_signer");
187
+
188
+ const from = await getAddress();
189
+ const isNative = assetValue.isGasAsset;
190
+
191
+ if (isNative) {
192
+ // Native TRX Transfer (amount in SUN - base units)
193
+ const transaction = await tronWeb.transactionBuilder.sendTrx(
194
+ recipient,
195
+ assetValue.getBaseValue("number"),
196
+ from,
197
+ );
198
+
199
+ // Add memo if provided
200
+ if (memo) {
201
+ const transactionWithMemo = await tronWeb.transactionBuilder.addUpdateData(
202
+ transaction,
203
+ memo,
204
+ "utf8",
205
+ );
206
+ const signedTx = await signer.signTransaction(transactionWithMemo);
207
+ const { txid } = await tronWeb.trx.sendRawTransaction(signedTx);
208
+ return txid;
209
+ }
210
+
211
+ const signedTx = await signer.signTransaction(transaction);
212
+ const { txid } = await tronWeb.trx.sendRawTransaction(signedTx);
213
+ return txid;
214
+ }
215
+
216
+ // TRC20 Token Transfer
217
+ const contractAddress = getContractAddress(assetValue);
218
+ if (!contractAddress) {
219
+ throw new SwapKitError("toolbox_tron_invalid_token_identifier", {
220
+ identifier: assetValue.toString(),
221
+ });
222
+ }
223
+
224
+ const feeLimit = calculateFeeLimit();
225
+ const contract = await tronWeb.contract(trc20ABI, contractAddress);
226
+
227
+ if (!contract.methods?.transfer) {
228
+ throw new SwapKitError("toolbox_tron_token_transfer_failed");
229
+ }
230
+
231
+ const txid = await contract.methods
232
+ .transfer(recipient, assetValue.getBaseValue("string"))
233
+ .send({
234
+ from,
235
+ feeLimit,
236
+ callValue: 0,
237
+ });
238
+
239
+ if (!txid) {
240
+ throw new SwapKitError("toolbox_tron_token_transfer_failed");
241
+ }
242
+
243
+ return txid;
244
+ };
245
+
246
+ const estimateTransactionFee = ({ assetValue }: TronTransferParams) => {
247
+ const isNative = assetValue.isGasAsset;
248
+
249
+ if (isNative) {
250
+ // Native TRX transfers typically consume bandwidth, which is free up to daily limit
251
+ // Return a minimal fee estimation for bandwidth cost
252
+ return AssetValue.from({ chain: Chain.Tron, value: 1 }); // 1 TRX
253
+ }
254
+
255
+ // TRC20 transfers consume energy, estimate higher fee
256
+ return AssetValue.from({ chain: Chain.Tron, value: 10 }); // 10 TRX
257
+ };
258
+
259
+ const createTransaction = async (params: TronTransferParams) => {
260
+ if (!signer) throw new SwapKitError("toolbox_tron_no_signer");
261
+
262
+ const { recipient, assetValue, memo } = params;
263
+ const from = await getAddress();
264
+ const isNative = assetValue.isGasAsset;
265
+
266
+ if (isNative) {
267
+ const transaction = await tronWeb.transactionBuilder.sendTrx(
268
+ recipient,
269
+ assetValue.getBaseValue("number"),
270
+ from,
271
+ );
272
+
273
+ if (memo) {
274
+ return tronWeb.transactionBuilder.addUpdateData(transaction, memo, "utf8");
275
+ }
276
+
277
+ return transaction;
278
+ }
279
+
280
+ // For TRC20, we would need to build the transaction manually
281
+ // This is a simplified version - in practice, you'd build the contract call transaction
282
+ const contractAddress = getContractAddress(assetValue);
283
+ if (!contractAddress) {
284
+ throw new SwapKitError("toolbox_tron_invalid_token_identifier", {
285
+ identifier: assetValue.toString(),
286
+ });
287
+ }
288
+
289
+ // Build TRC20 transfer transaction
290
+ const functionSelector = "transfer(address,uint256)";
291
+ const parameter = [
292
+ { type: "address", value: recipient },
293
+ { type: "uint256", value: assetValue.getBaseValue("string") },
294
+ ];
295
+
296
+ const result = await tronWeb.transactionBuilder.triggerSmartContract(
297
+ contractAddress,
298
+ functionSelector,
299
+ {},
300
+ parameter,
301
+ from,
302
+ );
303
+
304
+ return result.transaction;
305
+ };
306
+
307
+ const signTransaction = async (transaction: TronTransaction) => {
308
+ if (!signer) throw new SwapKitError("toolbox_tron_no_signer");
309
+ return await signer.signTransaction(transaction);
310
+ };
311
+
312
+ const broadcastTransaction = async (signedTx: TronSignedTransaction) => {
313
+ const { txid } = await tronWeb.trx.sendRawTransaction(signedTx);
314
+ return txid;
315
+ };
316
+
317
+ return {
318
+ tronWeb,
319
+ getAddress,
320
+ validateAddress,
321
+ getBalance,
322
+ transfer,
323
+ estimateTransactionFee,
324
+ createTransaction,
325
+ signTransaction,
326
+ broadcastTransaction,
327
+ };
328
+ };
@@ -0,0 +1,22 @@
1
+ import type { DerivationPathArray, GenericTransferParams } from "@swapkit/helpers";
2
+ import type { Contract, Types } from "tronweb";
3
+
4
+ // Re-export TronWeb types for convenience
5
+ export type TronTransaction = Types.Transaction;
6
+ export type TronContract = Contract;
7
+ export type TronSignedTransaction = Types.SignedTransaction;
8
+
9
+ // Signer interface compatible with TronWeb and wallet implementations
10
+ export interface TronSigner {
11
+ getAddress(): Promise<string>;
12
+ signTransaction(transaction: TronTransaction): Promise<TronSignedTransaction>;
13
+ }
14
+
15
+ export type TronToolboxOptions =
16
+ | { signer?: TronSigner }
17
+ | { phrase?: string; derivationPath?: DerivationPathArray; index?: number }
18
+ | {};
19
+
20
+ export interface TronTransferParams extends GenericTransferParams {
21
+ // No additional fields needed - all inherited from GenericTransferParams
22
+ }
@@ -1,4 +1,11 @@
1
- import { Chain, RequestClient, SKConfig, type UTXOChain, warnOnce } from "@swapkit/helpers";
1
+ import {
2
+ Chain,
3
+ RequestClient,
4
+ SKConfig,
5
+ SwapKitError,
6
+ type UTXOChain,
7
+ warnOnce,
8
+ } from "@swapkit/helpers";
2
9
  import { networks } from "bitcoinjs-lib";
3
10
  import { uniqid } from "../../utils";
4
11
 
@@ -28,11 +35,13 @@ async function broadcastUTXOTx({ chain, txHash }: { chain: Chain; txHash: string
28
35
  }>(rpcUrl, { headers: { "Content-Type": "application/json" }, body });
29
36
 
30
37
  if (response.error) {
31
- throw new Error(`failed to broadcast a transaction: ${response.error?.message}`);
38
+ throw new SwapKitError("toolbox_utxo_broadcast_failed", { error: response.error?.message });
32
39
  }
33
40
 
34
41
  if (response.result.includes('"code":-26')) {
35
- throw new Error("Invalid transaction: the transaction amount was too low");
42
+ throw new SwapKitError("toolbox_utxo_invalid_transaction", {
43
+ error: "Transaction amount was too low",
44
+ });
36
45
  }
37
46
 
38
47
  return response.result;
@@ -93,7 +102,8 @@ async function getSuggestedTxFee(chain: Chain) {
93
102
  async function blockchairRequest<T>(url: string, apiKey?: string): Promise<T> {
94
103
  try {
95
104
  const response = await RequestClient.get<BlockchairResponse<T>>(url);
96
- if (!response || response.context.code !== 200) throw new Error(`failed to query ${url}`);
105
+ if (!response || response.context.code !== 200)
106
+ throw new SwapKitError("toolbox_utxo_api_error", { error: `Failed to query ${url}` });
97
107
 
98
108
  return response.data as T;
99
109
  } catch (error) {
@@ -102,14 +112,16 @@ async function blockchairRequest<T>(url: string, apiKey?: string): Promise<T> {
102
112
  `${url}${apiKey ? `&key=${apiKey}` : ""}`,
103
113
  );
104
114
 
105
- if (!response || response.context.code !== 200) throw new Error(`failed to query ${url}`);
115
+ if (!response || response.context.code !== 200)
116
+ throw new SwapKitError("toolbox_utxo_api_error", { error: `Failed to query ${url}` });
106
117
 
107
118
  return response.data as T;
108
119
  }
109
120
  }
110
121
 
111
122
  async function getAddressData({ address, chain, apiKey }: BlockchairParams<{ address?: string }>) {
112
- if (!address) throw new Error("address is required");
123
+ if (!address)
124
+ throw new SwapKitError("toolbox_utxo_invalid_params", { error: "Address is required" });
113
125
 
114
126
  try {
115
127
  const response = await blockchairRequest<BlockchairAddressResponse>(
@@ -134,7 +146,8 @@ async function getUnconfirmedBalance({
134
146
  }
135
147
 
136
148
  async function getRawTx({ chain, apiKey, txHash }: BlockchairParams<{ txHash?: string }>) {
137
- if (!txHash) throw new Error("txHash is required");
149
+ if (!txHash)
150
+ throw new SwapKitError("toolbox_utxo_invalid_params", { error: "TxHash is required" });
138
151
 
139
152
  try {
140
153
  const rawTxResponse = await blockchairRequest<BlockchairRawTransactionResponse>(
@@ -143,7 +156,7 @@ async function getRawTx({ chain, apiKey, txHash }: BlockchairParams<{ txHash?: s
143
156
  );
144
157
  return rawTxResponse?.[txHash]?.raw_transaction || "";
145
158
  } catch (error) {
146
- console.error(error);
159
+ console.error("Failed to fetch raw transaction:", error);
147
160
  return "";
148
161
  }
149
162
  }
@@ -181,7 +194,8 @@ async function getUnspentUtxos({
181
194
  offset = 0,
182
195
  limit = 100,
183
196
  }: BlockchairFetchUnspentUtxoParams): Promise<Awaited<ReturnType<typeof fetchUnspentUtxoBatch>>> {
184
- if (!address) throw new Error("address is required");
197
+ if (!address)
198
+ throw new SwapKitError("toolbox_utxo_invalid_params", { error: "Address is required" });
185
199
 
186
200
  try {
187
201
  const txs = await fetchUnspentUtxoBatch({ chain, address, apiKey, offset, limit });
@@ -198,7 +212,7 @@ async function getUnspentUtxos({
198
212
 
199
213
  return [...txs, ...nextBatch];
200
214
  } catch (error) {
201
- console.error(error);
215
+ console.error("Failed to fetch unspent UTXOs:", error);
202
216
  return [];
203
217
  }
204
218
  }
@@ -282,7 +296,7 @@ export function getUtxoNetwork() {
282
296
  return coininfo.dogecoin.main.toBitcoinJS();
283
297
  }
284
298
  default:
285
- throw new Error("Invalid chain");
299
+ throw new SwapKitError("toolbox_utxo_not_supported", { chain });
286
300
  }
287
301
  };
288
302
  }
@@ -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,
@@ -21,7 +21,7 @@ export const getDustThreshold = (chain: UTXOChain) => {
21
21
  case Chain.Dogecoin:
22
22
  return 100000;
23
23
  default:
24
- throw new Error("Invalid Chain");
24
+ throw new SwapKitError("toolbox_utxo_not_supported", { chain });
25
25
  }
26
26
  };
27
27
 
@@ -1,3 +1,4 @@
1
+ import { SwapKitError } from "@swapkit/helpers";
1
2
  import { opcodes, script } from "bitcoinjs-lib";
2
3
  import type {
3
4
  TargetOutput,
@@ -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) => {
@@ -10,6 +10,7 @@ import {
10
10
  type DerivationPathArray,
11
11
  FeeOption,
12
12
  NetworkDerivationPath,
13
+ SwapKitError,
13
14
  derivationPathToString,
14
15
  updateDerivationPath,
15
16
  } from "@swapkit/helpers";
@@ -137,7 +138,8 @@ async function createTransaction({
137
138
  feeRate,
138
139
  sender,
139
140
  }: UTXOBuildTxParams) {
140
- if (!bchValidateAddress(recipient)) throw new Error("Invalid address");
141
+ if (!bchValidateAddress(recipient))
142
+ throw new SwapKitError("toolbox_utxo_invalid_address", { address: recipient });
141
143
  const utxos = await getUtxoApi(chain).scanUTXOs({
142
144
  address: stripToCashAddress(sender),
143
145
  fetchTxHex: true,
@@ -159,7 +161,8 @@ async function createTransaction({
159
161
  });
160
162
 
161
163
  // .inputs and .outputs will be undefined if no solution was found
162
- if (!(inputs && outputs)) throw new Error("Balance insufficient for transaction");
164
+ if (!(inputs && outputs))
165
+ throw new SwapKitError("toolbox_utxo_insufficient_balance", { sender, assetValue });
163
166
  const getNetwork = await getUtxoNetwork();
164
167
  const builder = new TransactionBuilder(getNetwork(chain)) as TransactionBuilderType;
165
168
 
@@ -204,8 +207,11 @@ function transfer({
204
207
  ...rest
205
208
  }: UTXOTransferParams) {
206
209
  const from = await signer?.getAddress();
207
- if (!(signer && from)) throw new Error("Signer must provider address");
208
- 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
+ });
209
215
 
210
216
  const feeRate = rest.feeRate || (await getFeeRates())[feeOptionKey];
211
217
 
@@ -228,7 +234,8 @@ function transfer({
228
234
  // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: TODO: refactor
229
235
  async function buildTx({ assetValue, recipient, memo, feeRate, sender }: UTXOBuildTxParams) {
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";
@@ -65,7 +66,7 @@ export async function getUtxoToolbox<T extends keyof UTXOToolboxes>(
65
66
  }
66
67
 
67
68
  default:
68
- throw new Error(`Chain ${chain} is not supported`);
69
+ throw new SwapKitError("toolbox_utxo_not_supported", { chain });
69
70
  }
70
71
  }
71
72