@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.
- package/dist/{chunk-p1kdg37m.js → chunk-38ztynv0.js} +1 -1
- package/dist/{chunk-p1kdg37m.js.map → chunk-38ztynv0.js.map} +1 -1
- package/dist/{chunk-fjfxga2v.js → chunk-5yxc1e69.js} +1 -1
- package/dist/{chunk-fjfxga2v.js.map → chunk-5yxc1e69.js.map} +1 -1
- package/dist/{chunk-0f0249b1.js → chunk-cv69ewns.js} +1 -1
- package/dist/chunk-vhc011em.js +4 -0
- package/dist/{chunk-0h4xdrwz.js.map → chunk-vhc011em.js.map} +2 -2
- package/dist/src/cosmos/index.cjs +3 -0
- package/dist/src/cosmos/index.cjs.map +16 -0
- package/dist/src/cosmos/index.js +3 -0
- package/dist/src/cosmos/index.js.map +16 -0
- package/dist/src/evm/index.cjs +3 -0
- package/dist/src/evm/index.cjs.map +18 -0
- package/dist/src/evm/index.js +3 -0
- package/dist/src/evm/index.js.map +18 -0
- package/dist/src/index.cjs +3 -0
- package/dist/src/index.cjs.map +10 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +10 -0
- package/dist/{radix → src/radix}/index.cjs +2 -2
- package/dist/src/radix/index.cjs.map +10 -0
- package/dist/src/radix/index.js +3 -0
- package/dist/src/radix/index.js.map +10 -0
- package/dist/src/ripple/index.js +3 -0
- package/dist/{ripple → src/ripple}/index.js.map +1 -1
- package/dist/src/solana/index.js +3 -0
- package/dist/{solana → src/solana}/index.js.map +1 -1
- package/dist/src/substrate/index.cjs +3 -0
- package/dist/src/substrate/index.cjs.map +11 -0
- package/dist/src/substrate/index.js +3 -0
- package/dist/src/substrate/index.js.map +11 -0
- package/dist/src/tron/index.cjs +3 -0
- package/dist/src/tron/index.cjs.map +11 -0
- package/dist/src/tron/index.js +3 -0
- package/dist/src/tron/index.js.map +11 -0
- package/dist/src/utxo/index.cjs +3 -0
- package/dist/src/utxo/index.cjs.map +16 -0
- package/dist/src/utxo/index.js +3 -0
- package/dist/src/utxo/index.js.map +16 -0
- package/package.json +24 -18
- package/src/cosmos/thorchainUtils/addressFormat.ts +4 -1
- package/src/cosmos/thorchainUtils/messages.ts +2 -2
- package/src/cosmos/toolbox/cosmos.ts +2 -4
- package/src/cosmos/toolbox/index.ts +2 -2
- package/src/cosmos/toolbox/thorchain.ts +3 -2
- package/src/cosmos/util.ts +10 -3
- package/src/evm/helpers.ts +4 -3
- package/src/evm/toolbox/baseEVMToolbox.ts +15 -12
- package/src/evm/toolbox/index.ts +2 -2
- package/src/evm/toolbox/op.ts +10 -2
- package/src/index.ts +104 -104
- package/src/radix/index.ts +8 -2
- package/src/substrate/substrate.ts +1 -1
- package/src/tron/helpers/trc20.abi.ts +40 -0
- package/src/tron/index.ts +16 -0
- package/src/tron/toolbox.ts +328 -0
- package/src/tron/types.ts +22 -0
- package/src/utxo/helpers/api.ts +25 -11
- package/src/utxo/helpers/bchaddrjs.ts +8 -8
- package/src/utxo/helpers/coinselect.ts +2 -2
- package/src/utxo/helpers/txSize.ts +2 -1
- package/src/utxo/toolbox/bitcoinCash.ts +14 -6
- package/src/utxo/toolbox/index.ts +2 -1
- package/src/utxo/toolbox/utxo.ts +23 -9
- package/dist/chunk-0h4xdrwz.js +0 -4
- package/dist/cosmos/index.cjs +0 -3
- package/dist/cosmos/index.cjs.map +0 -16
- package/dist/cosmos/index.js +0 -3
- package/dist/cosmos/index.js.map +0 -16
- package/dist/evm/index.cjs +0 -3
- package/dist/evm/index.cjs.map +0 -18
- package/dist/evm/index.js +0 -3
- package/dist/evm/index.js.map +0 -18
- package/dist/index.cjs +0 -3
- package/dist/index.cjs.map +0 -10
- package/dist/index.js +0 -3
- package/dist/index.js.map +0 -10
- package/dist/radix/index.cjs.map +0 -10
- package/dist/radix/index.js +0 -3
- package/dist/radix/index.js.map +0 -10
- package/dist/ripple/index.js +0 -3
- package/dist/solana/index.js +0 -3
- package/dist/substrate/index.cjs +0 -3
- package/dist/substrate/index.cjs.map +0 -11
- package/dist/substrate/index.js +0 -3
- package/dist/substrate/index.js.map +0 -11
- package/dist/utxo/index.cjs +0 -3
- package/dist/utxo/index.cjs.map +0 -16
- package/dist/utxo/index.js +0 -3
- package/dist/utxo/index.js.map +0 -16
- /package/dist/{chunk-0f0249b1.js.map → chunk-cv69ewns.js.map} +0 -0
- /package/dist/{ripple → src/ripple}/index.cjs +0 -0
- /package/dist/{ripple → src/ripple}/index.cjs.map +0 -0
- /package/dist/{solana → src/solana}/index.cjs +0 -0
- /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
|
+
}
|
package/src/utxo/helpers/api.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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)
|
|
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
|
|
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-
|
|
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
|
|
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
|
|
119
|
+
throw new SwapKitError("toolbox_utxo_invalid_address", { address });
|
|
120
120
|
}
|
|
121
121
|
} catch (_error) {
|
|
122
|
-
throw new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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))
|
|
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))
|
|
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
|
|
208
|
-
if (!recipient)
|
|
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))
|
|
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))
|
|
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
|
|
69
|
+
throw new SwapKitError("toolbox_utxo_not_supported", { chain });
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
|