@xchainjs/xchain-tron 2.0.0

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/lib/index.js ADDED
@@ -0,0 +1,1045 @@
1
+ 'use strict';
2
+
3
+ var bip32 = require('@scure/bip32');
4
+ var bip39 = require('@scure/bip39');
5
+ var TW = require('tronweb');
6
+ var xchainClient = require('@xchainjs/xchain-client');
7
+ var xchainUtil = require('@xchainjs/xchain-util');
8
+
9
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
10
+
11
+ var TW__default = /*#__PURE__*/_interopDefault(TW);
12
+
13
+ /******************************************************************************
14
+ Copyright (c) Microsoft Corporation.
15
+
16
+ Permission to use, copy, modify, and/or distribute this software for any
17
+ purpose with or without fee is hereby granted.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
20
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
21
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
22
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
23
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
24
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
25
+ PERFORMANCE OF THIS SOFTWARE.
26
+ ***************************************************************************** */
27
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
28
+
29
+
30
+ function __awaiter(thisArg, _arguments, P, generator) {
31
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
32
+ return new (P || (P = Promise))(function (resolve, reject) {
33
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
34
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
35
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
36
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
37
+ });
38
+ }
39
+
40
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
41
+ var e = new Error(message);
42
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
43
+ };
44
+
45
+ const TRX_DECIMAL = 6;
46
+ const TRON_DERIVATION_PATH = "m/44'/195'/0'/0/";
47
+ /**
48
+ * Chain identifier for Tron mainnet
49
+ */
50
+ const TRONChain = 'TRON';
51
+ const AssetTRX = {
52
+ chain: TRONChain,
53
+ symbol: 'TRX',
54
+ ticker: 'TRX',
55
+ type: xchainUtil.AssetType.NATIVE,
56
+ };
57
+ const AssetTRONUSDT = {
58
+ chain: TRONChain,
59
+ symbol: 'USDT-TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
60
+ ticker: 'USDT',
61
+ type: xchainUtil.AssetType.TOKEN,
62
+ };
63
+ // Explorer providers for Tron
64
+ const TRON_MAINNET_EXPLORER = new xchainClient.ExplorerProvider('https://tronscan.org/', 'https://tronscan.org/#/address/%%ADDRESS%%', 'https://tronscan.org/#/transaction/%%TX_ID%%');
65
+ const TRON_TESTNET_EXPLORER = new xchainClient.ExplorerProvider('https://nile.tronscan.org/', 'https://nile.tronscan.org/accounts/%%ADDRESS%%', 'https://nile.tronscan.org/transactions/%%TX_ID%%');
66
+ const TRON_DEFAULT_RPC = 'https://tron-rpc.publicnode.com';
67
+ const tronExplorerProviders = {
68
+ [xchainClient.Network.Mainnet]: TRON_MAINNET_EXPLORER,
69
+ [xchainClient.Network.Stagenet]: TRON_MAINNET_EXPLORER,
70
+ [xchainClient.Network.Testnet]: TRON_TESTNET_EXPLORER,
71
+ };
72
+ const TRX_TRANSFER_BANDWIDTH = 268; // Bandwidth consumed by a TRX transfer
73
+ const TRC20_TRANSFER_ENERGY = 13000; // Average energy consumed by TRC20 transfer
74
+ const TRC20_TRANSFER_BANDWIDTH = 345; // Bandwidth consumed by TRC20 transfer
75
+ const TRON_USDT_CONTRACT = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
76
+ const MAX_APPROVAL = '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
77
+ const TRX_FEE_LIMIT = 100000000;
78
+
79
+ const validateAddress = (address) => {
80
+ return TW.TronWeb.isAddress(address);
81
+ };
82
+ const getTRC20AssetContractAddress = (asset) => {
83
+ try {
84
+ return asset.symbol.slice(asset.ticker.length + 1);
85
+ }
86
+ catch (_err) {
87
+ return null;
88
+ }
89
+ };
90
+
91
+ var trc20ABI = [
92
+ {
93
+ constant: true,
94
+ inputs: [
95
+ {
96
+ name: "_owner",
97
+ type: "address"
98
+ }
99
+ ],
100
+ name: "balanceOf",
101
+ outputs: [
102
+ {
103
+ name: "balance",
104
+ type: "uint256"
105
+ }
106
+ ],
107
+ stateMutability: "view",
108
+ type: "function"
109
+ },
110
+ {
111
+ constant: false,
112
+ inputs: [
113
+ {
114
+ name: "_to",
115
+ type: "address"
116
+ },
117
+ {
118
+ name: "_value",
119
+ type: "uint256"
120
+ }
121
+ ],
122
+ name: "transfer",
123
+ outputs: [
124
+ {
125
+ name: "success",
126
+ type: "bool"
127
+ }
128
+ ],
129
+ stateMutability: "nonpayable",
130
+ type: "function"
131
+ },
132
+ {
133
+ constant: true,
134
+ inputs: [
135
+ ],
136
+ name: "decimals",
137
+ outputs: [
138
+ {
139
+ name: "",
140
+ type: "uint8"
141
+ }
142
+ ],
143
+ stateMutability: "view",
144
+ type: "function"
145
+ },
146
+ {
147
+ constant: true,
148
+ inputs: [
149
+ ],
150
+ name: "symbol",
151
+ outputs: [
152
+ {
153
+ name: "",
154
+ type: "string"
155
+ }
156
+ ],
157
+ stateMutability: "view",
158
+ type: "function"
159
+ },
160
+ {
161
+ constant: true,
162
+ inputs: [
163
+ ],
164
+ name: "name",
165
+ outputs: [
166
+ {
167
+ name: "",
168
+ type: "string"
169
+ }
170
+ ],
171
+ stateMutability: "view",
172
+ type: "function"
173
+ },
174
+ {
175
+ constant: true,
176
+ inputs: [
177
+ {
178
+ name: "_owner",
179
+ type: "address"
180
+ },
181
+ {
182
+ name: "_spender",
183
+ type: "address"
184
+ }
185
+ ],
186
+ name: "allowance",
187
+ outputs: [
188
+ {
189
+ name: "remaining",
190
+ type: "uint256"
191
+ }
192
+ ],
193
+ stateMutability: "view",
194
+ type: "function"
195
+ },
196
+ {
197
+ constant: false,
198
+ inputs: [
199
+ {
200
+ name: "_spender",
201
+ type: "address"
202
+ },
203
+ {
204
+ name: "_value",
205
+ type: "uint256"
206
+ }
207
+ ],
208
+ name: "approve",
209
+ outputs: [
210
+ {
211
+ name: "success",
212
+ type: "bool"
213
+ }
214
+ ],
215
+ stateMutability: "nonpayable",
216
+ type: "function"
217
+ },
218
+ {
219
+ constant: true,
220
+ inputs: [
221
+ ],
222
+ name: "totalSupply",
223
+ outputs: [
224
+ {
225
+ name: "",
226
+ type: "uint256"
227
+ }
228
+ ],
229
+ stateMutability: "view",
230
+ type: "function"
231
+ },
232
+ {
233
+ constant: false,
234
+ inputs: [
235
+ {
236
+ name: "_from",
237
+ type: "address"
238
+ },
239
+ {
240
+ name: "_to",
241
+ type: "address"
242
+ },
243
+ {
244
+ name: "_value",
245
+ type: "uint256"
246
+ }
247
+ ],
248
+ name: "transferFrom",
249
+ outputs: [
250
+ {
251
+ name: "success",
252
+ type: "bool"
253
+ }
254
+ ],
255
+ stateMutability: "nonpayable",
256
+ type: "function"
257
+ },
258
+ {
259
+ anonymous: false,
260
+ inputs: [
261
+ {
262
+ indexed: true,
263
+ name: "_from",
264
+ type: "address"
265
+ },
266
+ {
267
+ indexed: true,
268
+ name: "_to",
269
+ type: "address"
270
+ },
271
+ {
272
+ indexed: false,
273
+ name: "_value",
274
+ type: "uint256"
275
+ }
276
+ ],
277
+ name: "Transfer",
278
+ type: "event"
279
+ },
280
+ {
281
+ anonymous: false,
282
+ inputs: [
283
+ {
284
+ indexed: true,
285
+ name: "_owner",
286
+ type: "address"
287
+ },
288
+ {
289
+ indexed: true,
290
+ name: "_spender",
291
+ type: "address"
292
+ },
293
+ {
294
+ indexed: false,
295
+ name: "_value",
296
+ type: "uint256"
297
+ }
298
+ ],
299
+ name: "Approval",
300
+ type: "event"
301
+ }
302
+ ];
303
+
304
+ const TRONGRID_API_BASE_URL = 'https://api.trongrid.io';
305
+ class TronGrid {
306
+ constructor() {
307
+ this.tronWeb = new TW.TronWeb({ fullHost: TRON_DEFAULT_RPC });
308
+ }
309
+ getAccount(address) {
310
+ return __awaiter(this, void 0, void 0, function* () {
311
+ try {
312
+ const response = yield fetch(`${TRONGRID_API_BASE_URL}/v1/accounts/${address}`);
313
+ if (!response.ok) {
314
+ throw new Error(`TronGrid API error: ${response.status} ${response.statusText}`);
315
+ }
316
+ const data = (yield response.json());
317
+ if (!(data.success && data.data) || data.data.length === 0) {
318
+ throw new Error('Invalid response from TronGrid API');
319
+ }
320
+ // Convert search address to hex format for comparison
321
+ let searchAddressHex;
322
+ try {
323
+ // If address is base58, convert to hex
324
+ searchAddressHex = TW.TronWeb.address.toHex(address).toLowerCase();
325
+ }
326
+ catch (_a) {
327
+ // If conversion fails, assume it's already hex
328
+ searchAddressHex = address.toLowerCase();
329
+ }
330
+ // Find the account that matches the requested address
331
+ const account = data.data.find((acc) => {
332
+ return acc.address.toLowerCase() === searchAddressHex;
333
+ });
334
+ if (!account) {
335
+ return;
336
+ }
337
+ return account;
338
+ }
339
+ catch (error) {
340
+ throw error;
341
+ }
342
+ });
343
+ }
344
+ getTransactions(params) {
345
+ return __awaiter(this, void 0, void 0, function* () {
346
+ const { address, offset, limit } = params;
347
+ const url = `https://api.trongrid.io/v1/accounts/${address}/transactions?limit=${limit}&start=${offset}`;
348
+ const res = yield fetch(url);
349
+ const { data } = yield res.json();
350
+ const txs = [];
351
+ for (const tx of data) {
352
+ const contract = tx.raw_data.contract[0];
353
+ // Case 1: Native TRX transfer
354
+ if (contract.type === 'TransferContract') {
355
+ const amount = xchainUtil.baseAmount(contract.parameter.value.amount, TRX_DECIMAL);
356
+ const from = [{ from: TW.TronWeb.address.fromHex(contract.parameter.value.owner_address), amount }];
357
+ const to = [{ to: TW.TronWeb.address.fromHex(contract.parameter.value.to_address), amount }];
358
+ txs.push({
359
+ asset: AssetTRX,
360
+ type: xchainClient.TxType.Transfer,
361
+ from,
362
+ to,
363
+ date: new Date(tx.block_timestamp),
364
+ hash: tx.txID,
365
+ });
366
+ continue;
367
+ }
368
+ // Case 2: TRC20 transfer (TriggerSmartContract)
369
+ if (contract.type === 'TriggerSmartContract') {
370
+ const { contract_address, data: inputData, owner_address } = contract.parameter.value;
371
+ // `inputData` starts with 4-byte method selector (transfer(address,uint256))
372
+ // and then encoded params
373
+ try {
374
+ const decoded = TW__default.default.utils.abi.decodeParams(['_to', '_value'], // parameter names
375
+ ['address', 'uint256'], // parameter types
376
+ inputData, // full input data (with selector)
377
+ true);
378
+ const toAddress = decoded._to;
379
+ const fromAddress = TW.TronWeb.address.fromHex(owner_address);
380
+ const rawValue = decoded._value.toString();
381
+ // Get token contract instance
382
+ const contractHex = TW.TronWeb.address.fromHex(contract_address);
383
+ const tokenContract = yield this.tronWeb.contract().at(contractHex);
384
+ // Call symbol() and decimals()
385
+ const symbol = yield tokenContract.symbol().call();
386
+ const decimals = yield tokenContract.decimals().call();
387
+ const amount = xchainUtil.baseAmount(rawValue, decimals);
388
+ const from = [{ from: fromAddress, amount }];
389
+ const to = [{ to: toAddress, amount }];
390
+ const asset = {
391
+ chain: 'TRON',
392
+ symbol: `${symbol}-${contract_address}`,
393
+ ticker: symbol,
394
+ type: xchainUtil.AssetType.TOKEN,
395
+ };
396
+ txs.push({
397
+ asset,
398
+ type: xchainClient.TxType.Transfer,
399
+ from,
400
+ to,
401
+ date: new Date(tx.block_timestamp),
402
+ hash: tx.txID,
403
+ });
404
+ }
405
+ catch (_e) {
406
+ // Not an ERC20 transfer (could be another contract call)
407
+ continue;
408
+ }
409
+ }
410
+ }
411
+ return {
412
+ total: txs.length,
413
+ txs,
414
+ };
415
+ });
416
+ }
417
+ getTransactionData(txId) {
418
+ return __awaiter(this, void 0, void 0, function* () {
419
+ var _a, _b, _c;
420
+ const url = `${TRONGRID_API_BASE_URL}/walletsolidity/gettransactionbyid`;
421
+ const res = yield fetch(url, {
422
+ method: 'POST',
423
+ headers: { 'Content-Type': 'application/json' },
424
+ body: JSON.stringify({ value: txId }),
425
+ });
426
+ const tx = yield res.json();
427
+ if (!tx || !((_b = (_a = tx.raw_data) === null || _a === void 0 ? void 0 : _a.contract) === null || _b === void 0 ? void 0 : _b.length)) {
428
+ throw new Error('Transaction not found');
429
+ }
430
+ const contract = tx.raw_data.contract[0];
431
+ // Case 1: Native TRX transfer
432
+ if (contract.type === 'TransferContract') {
433
+ const amount = xchainUtil.baseAmount(contract.parameter.value.amount, TRX_DECIMAL);
434
+ const from = [{ from: TW.TronWeb.address.fromHex(contract.parameter.value.owner_address), amount }];
435
+ const to = [{ to: TW.TronWeb.address.fromHex(contract.parameter.value.to_address), amount }];
436
+ return {
437
+ asset: AssetTRX,
438
+ type: xchainClient.TxType.Transfer,
439
+ from,
440
+ to,
441
+ date: new Date((_c = tx.raw_data.timestamp) !== null && _c !== void 0 ? _c : Date.now()),
442
+ hash: tx.txID,
443
+ };
444
+ }
445
+ // Case 2: TRC20 transfer (must decode ABI input manually)
446
+ if (contract.type === 'TriggerSmartContract') {
447
+ const { owner_address, contract_address, data: inputData } = contract.parameter.value;
448
+ // ERC20 transfer selector = a9059cbb
449
+ if (inputData.startsWith('a9059cbb')) {
450
+ const decoded = TW__default.default.utils.abi.decodeParams(['_to', '_value'], // parameter names
451
+ ['address', 'uint256'], // parameter types
452
+ inputData, // full input data (with selector)
453
+ true);
454
+ const toAddress = TW.TronWeb.address.fromHex(decoded._to);
455
+ const fromAddress = TW.TronWeb.address.fromHex(owner_address);
456
+ const rawValue = decoded._value.toString();
457
+ // should set owner_address
458
+ this.tronWeb.setAddress(fromAddress);
459
+ // Get token contract instance
460
+ const contract = TW.TronWeb.address.fromHex(contract_address);
461
+ const tokenContract = yield this.tronWeb.contract().at(contract);
462
+ // Call symbol() and decimals()
463
+ const symbol = yield tokenContract.symbol().call();
464
+ const decimals = yield tokenContract.decimals().call();
465
+ const amount = xchainUtil.baseAmount(rawValue, decimals);
466
+ const from = [{ from: fromAddress, amount }];
467
+ const to = [{ to: toAddress, amount }];
468
+ const asset = {
469
+ chain: 'TRON',
470
+ symbol: `${symbol}-${contract_address}`,
471
+ ticker: symbol,
472
+ type: xchainUtil.AssetType.TOKEN,
473
+ };
474
+ return {
475
+ asset,
476
+ type: xchainClient.TxType.Transfer,
477
+ from,
478
+ to,
479
+ date: new Date(tx.raw_data.timestamp),
480
+ hash: tx.txID,
481
+ };
482
+ }
483
+ }
484
+ throw new Error(`Unsupported or non-transfer transaction: ${contract.type}`);
485
+ });
486
+ }
487
+ }
488
+
489
+ // Default parameters for the Tron client
490
+ const defaultTRONParams = {
491
+ network: xchainClient.Network.Mainnet,
492
+ phrase: '',
493
+ explorerProviders: tronExplorerProviders,
494
+ rootDerivationPaths: {
495
+ [xchainClient.Network.Mainnet]: TRON_DERIVATION_PATH,
496
+ [xchainClient.Network.Testnet]: TRON_DERIVATION_PATH,
497
+ [xchainClient.Network.Stagenet]: TRON_DERIVATION_PATH,
498
+ },
499
+ };
500
+ class Client extends xchainClient.BaseXChainClient {
501
+ constructor(params = defaultTRONParams) {
502
+ const clientParams = Object.assign(Object.assign({}, defaultTRONParams), params);
503
+ super(TRONChain, clientParams);
504
+ /**
505
+ * Get token balance and info directly from contract
506
+ */
507
+ this.fetchTokenMetadata = (_a) => __awaiter(this, [_a], void 0, function* ({ contractAddress }) {
508
+ const contract = this.tronWeb.contract(trc20ABI, contractAddress);
509
+ const [symbolRaw, decimalsRaw] = yield Promise.all([
510
+ contract
511
+ .symbol()
512
+ .call()
513
+ .catch(() => 'UNKNOWN'),
514
+ contract
515
+ .decimals()
516
+ .call()
517
+ .catch(() => '18'),
518
+ ]);
519
+ return { decimals: Number(decimalsRaw !== null && decimalsRaw !== void 0 ? decimalsRaw : 18), symbol: symbolRaw !== null && symbolRaw !== void 0 ? symbolRaw : 'UNKNOWN' };
520
+ });
521
+ /**
522
+ * Get token balance and info directly from contract
523
+ */
524
+ this.fetchTokenBalance = (_a) => __awaiter(this, [_a], void 0, function* ({ contractAddress, address }) {
525
+ var _b;
526
+ try {
527
+ const contract = this.tronWeb.contract(trc20ABI, contractAddress);
528
+ if (!((_b = contract.methods) === null || _b === void 0 ? void 0 : _b.balanceOf)) {
529
+ return BigInt(0);
530
+ }
531
+ const [balance] = yield contract.methods.balanceOf(address).call();
532
+ return balance ? (typeof balance === 'bigint' ? balance : BigInt(balance)) : BigInt(0);
533
+ }
534
+ catch (err) {
535
+ console.warn(`balanceOf() failed for ${contractAddress}:`, err);
536
+ return BigInt(0);
537
+ }
538
+ });
539
+ /**
540
+ * Get current chain parameters including resource prices
541
+ */
542
+ this.getChainParameters = () => __awaiter(this, void 0, void 0, function* () {
543
+ try {
544
+ const parameters = yield this.tronWeb.trx.getChainParameters();
545
+ const paramMap = {};
546
+ for (const param of parameters) {
547
+ paramMap[param.key] = param.value;
548
+ }
549
+ return {
550
+ bandwidthFee: paramMap.getTransactionFee || 1000, // SUN per bandwidth unit
551
+ createAccountFee: paramMap.getCreateAccountFee || 100000, // 0.1 TRX in SUN
552
+ energyFee: paramMap.getEnergyFee || 420, // SUN per energy unit
553
+ };
554
+ }
555
+ catch (_a) {
556
+ // Return default values if unable to fetch
557
+ return { bandwidthFee: 1000, createAccountFee: 100000, energyFee: 420 };
558
+ }
559
+ });
560
+ /**
561
+ * Check if an address exists on the blockchain
562
+ */
563
+ this.accountExists = (address) => __awaiter(this, void 0, void 0, function* () {
564
+ try {
565
+ const account = yield this.tronWeb.trx.getAccount(address);
566
+ return account && Object.keys(account).length > 0;
567
+ }
568
+ catch (_a) {
569
+ return false;
570
+ }
571
+ });
572
+ /**
573
+ * Get account resources (bandwidth and energy)
574
+ */
575
+ this.getAccountResources = (address) => __awaiter(this, void 0, void 0, function* () {
576
+ var _a, _b;
577
+ try {
578
+ const resources = yield this.tronWeb.trx.getAccountResources(address);
579
+ return {
580
+ bandwidth: {
581
+ free: ((_a = resources === null || resources === void 0 ? void 0 : resources.freeNetLimit) !== null && _a !== void 0 ? _a : 0) - ((_b = resources === null || resources === void 0 ? void 0 : resources.freeNetUsed) !== null && _b !== void 0 ? _b : 0),
582
+ total: resources.NetLimit || 0,
583
+ used: resources.NetUsed || 0,
584
+ },
585
+ energy: { total: resources.EnergyLimit || 0, used: resources.EnergyUsed || 0 },
586
+ };
587
+ }
588
+ catch (_c) {
589
+ // Return default structure if unable to fetch
590
+ return {
591
+ bandwidth: { free: 600, total: 0, used: 0 }, // 600 free bandwidth daily
592
+ energy: { total: 0, used: 0 },
593
+ };
594
+ }
595
+ });
596
+ this.createTransaction = (params) => __awaiter(this, void 0, void 0, function* () {
597
+ var _a, _b;
598
+ const { asset, amount: baseValue, recipient, memo, walletIndex } = params;
599
+ if (!asset)
600
+ throw Error('Asset not provided');
601
+ const sender = this.getAddress(walletIndex);
602
+ const amount = baseValue.amount().toString();
603
+ if (asset.type === xchainUtil.AssetType.NATIVE) {
604
+ const transaction = yield this.tronWeb.transactionBuilder.sendTrx(recipient, Number(amount), sender);
605
+ if (memo) {
606
+ return this.tronWeb.transactionBuilder.addUpdateData(transaction, memo, 'utf8');
607
+ }
608
+ return transaction;
609
+ }
610
+ const contractAddress = getTRC20AssetContractAddress(asset);
611
+ if (!contractAddress) {
612
+ throw new Error('TRC20 Asset Contract Address is not valid');
613
+ }
614
+ // Build TRC20 transfer transaction
615
+ const functionSelector = 'transfer(address,uint256)';
616
+ const parameter = [
617
+ { type: 'address', value: recipient },
618
+ { type: 'uint256', value: amount },
619
+ ];
620
+ const options = { callValue: 0, feeLimit: TRX_FEE_LIMIT };
621
+ const res = yield this.tronWeb.transactionBuilder.triggerSmartContract(contractAddress, functionSelector, options, parameter, sender);
622
+ // Some nodes don’t throw; they return result=false + message
623
+ if (!(((_a = res === null || res === void 0 ? void 0 : res.result) === null || _a === void 0 ? void 0 : _a.result) && (res === null || res === void 0 ? void 0 : res.transaction))) {
624
+ throw new Error((_b = res === null || res === void 0 ? void 0 : res.result) === null || _b === void 0 ? void 0 : _b.message);
625
+ }
626
+ // Attach memo if requested
627
+ const tx = memo
628
+ ? yield this.tronWeb.transactionBuilder.addUpdateData(res.transaction, memo, 'utf8')
629
+ : res.transaction;
630
+ return tx;
631
+ });
632
+ /**
633
+ * Check the current allowance for a spender on a token
634
+ */
635
+ this.getApprovedAmount = (_a) => __awaiter(this, [_a], void 0, function* ({ contractAddress, spenderAddress, from }) {
636
+ var _b;
637
+ this.tronWeb.setAddress(from);
638
+ const contract = this.tronWeb.contract(trc20ABI, contractAddress);
639
+ if (!((_b = contract.methods) === null || _b === void 0 ? void 0 : _b.allowance)) {
640
+ throw new Error('invalid contract');
641
+ }
642
+ const [allowance] = yield contract.methods.allowance(from, spenderAddress).call();
643
+ return allowance ? (typeof allowance === 'bigint' ? allowance : BigInt(allowance)) : BigInt(0);
644
+ });
645
+ this.explorerProviders = clientParams.explorerProviders;
646
+ this.tronWeb = new TW.TronWeb({ fullHost: TRON_DEFAULT_RPC });
647
+ this.tronGrid = new TronGrid();
648
+ }
649
+ /**
650
+ * Get TRX asset info.
651
+ * @returns {AssetInfo} TRX asset information.
652
+ */
653
+ getAssetInfo() {
654
+ const assetInfo = {
655
+ asset: AssetTRX,
656
+ decimal: TRX_DECIMAL,
657
+ };
658
+ return assetInfo;
659
+ }
660
+ /**
661
+ * Get the explorer URL.
662
+ *
663
+ * @returns {string} The explorer URL.
664
+ */
665
+ getExplorerUrl() {
666
+ return this.explorerProviders[this.getNetwork()].getExplorerUrl();
667
+ }
668
+ /**
669
+ * Get the explorer url for the given address.
670
+ *
671
+ * @param {Address} address
672
+ * @returns {string} The explorer url for the given address.
673
+ */
674
+ getExplorerAddressUrl(address) {
675
+ return this.explorerProviders[this.getNetwork()].getExplorerAddressUrl(address);
676
+ }
677
+ /**
678
+ * Get the explorer url for the given transaction id.
679
+ *
680
+ * @param {string} txID
681
+ * @returns {string} The explorer url for the given transaction id.
682
+ */
683
+ getExplorerTxUrl(txID) {
684
+ return this.explorerProviders[this.getNetwork()].getExplorerTxUrl(txID);
685
+ }
686
+ /**
687
+ * Validate the given Tron address.
688
+ * @param {string} address Tron address to validate.
689
+ * @returns {boolean} `true` if the address is valid, `false` otherwise.
690
+ */
691
+ validateAddress(address) {
692
+ return validateAddress(address);
693
+ }
694
+ /**
695
+ * Retrieves the balance of a given address.
696
+ * @param {Address} address - The address to retrieve the balance for.
697
+ * @param {TokenAsset[]} assets - Assets to retrieve the balance for (optional).
698
+ * @returns {Promise<Balance[]>} An array containing the balance of the address.
699
+ */
700
+ getBalance(address) {
701
+ return __awaiter(this, void 0, void 0, function* () {
702
+ const ZeroBalance = [
703
+ {
704
+ asset: AssetTRX,
705
+ amount: xchainUtil.baseAmount(0, TRX_DECIMAL),
706
+ },
707
+ ];
708
+ // Try get balance from TronGrid
709
+ try {
710
+ const accountData = yield this.tronGrid.getAccount(address);
711
+ if (!accountData)
712
+ return ZeroBalance;
713
+ const balances = [];
714
+ // Add TRX balance
715
+ balances.push({
716
+ asset: AssetTRX,
717
+ amount: xchainUtil.baseAmount(accountData.balance, TRX_DECIMAL),
718
+ });
719
+ // Add TRC20 balances
720
+ for (const token of accountData.trc20) {
721
+ const entries = Object.entries(token);
722
+ if (entries.length !== 1)
723
+ continue;
724
+ const [contractAddress, balance] = entries[0];
725
+ if (!(contractAddress && balance))
726
+ continue;
727
+ const tokenMetaData = yield this.fetchTokenMetadata({ contractAddress });
728
+ if (!tokenMetaData)
729
+ continue;
730
+ balances.push({
731
+ asset: {
732
+ chain: TRONChain,
733
+ symbol: `${tokenMetaData.symbol}-${contractAddress}`,
734
+ ticker: tokenMetaData.symbol,
735
+ type: xchainUtil.AssetType.TOKEN,
736
+ },
737
+ amount: xchainUtil.baseAmount(balance || 0, tokenMetaData.decimals),
738
+ });
739
+ }
740
+ return balances;
741
+ }
742
+ catch (_error) {
743
+ // Fallback: get TRX and USDT Balance from TronWeb
744
+ const balances = [];
745
+ const trxBalanceInSun = yield this.tronWeb.trx.getBalance(address);
746
+ if (trxBalanceInSun && Number(trxBalanceInSun) > 0) {
747
+ balances.push({
748
+ asset: AssetTRX,
749
+ amount: xchainUtil.baseAmount(trxBalanceInSun, TRX_DECIMAL),
750
+ });
751
+ }
752
+ const usdtBalance = yield this.fetchTokenBalance({ address, contractAddress: TRON_USDT_CONTRACT });
753
+ if (usdtBalance) {
754
+ balances.push({
755
+ asset: {
756
+ chain: TRONChain,
757
+ symbol: `USDT-${TRON_USDT_CONTRACT}`,
758
+ ticker: 'USDT',
759
+ type: xchainUtil.AssetType.TOKEN,
760
+ },
761
+ amount: xchainUtil.baseAmount(usdtBalance.toString(), 6),
762
+ });
763
+ }
764
+ return balances;
765
+ }
766
+ });
767
+ }
768
+ /**
769
+ * Get transaction fees.
770
+ * @param {TxParams} params - Tx param
771
+ * @returns {Fees} The average, fast, and fastest fees.
772
+ */
773
+ getFees(params) {
774
+ return __awaiter(this, void 0, void 0, function* () {
775
+ if (!params)
776
+ throw new Error('Params need to be passed');
777
+ const { asset, recipient, walletIndex } = params;
778
+ const isNative = (asset === null || asset === void 0 ? void 0 : asset.type) === xchainUtil.AssetType.NATIVE;
779
+ let feeAmount;
780
+ // Get sender address
781
+ const senderAddress = this.getAddress(walletIndex);
782
+ if (!senderAddress) {
783
+ // If no signer, return default fee
784
+ feeAmount = isNative ? xchainUtil.baseAmount(0.1 * Math.pow(10, 6), TRX_DECIMAL) : xchainUtil.baseAmount(15 * Math.pow(10, 6), TRX_DECIMAL);
785
+ }
786
+ else {
787
+ // Get chain parameters for current resource prices
788
+ const chainParams = yield this.getChainParameters();
789
+ // Check if recipient account exists (new accounts require activation fee)
790
+ const recipientExists = yield this.accountExists(recipient);
791
+ const activationFee = recipientExists ? 0 : chainParams.createAccountFee;
792
+ // Get account resources
793
+ const resources = yield this.getAccountResources(senderAddress);
794
+ if (isNative) {
795
+ // Calculate bandwidth needed for TRX transfer
796
+ const bandwidthNeeded = TRX_TRANSFER_BANDWIDTH;
797
+ const availableBandwidth = resources.bandwidth.free + (resources.bandwidth.total - resources.bandwidth.used);
798
+ let bandwidthFee = 0;
799
+ if (bandwidthNeeded > availableBandwidth) {
800
+ // Need to burn TRX for bandwidth
801
+ const bandwidthToBuy = bandwidthNeeded - availableBandwidth;
802
+ bandwidthFee = bandwidthToBuy * chainParams.bandwidthFee;
803
+ }
804
+ // Total fee in SUN
805
+ const totalFeeSun = activationFee + bandwidthFee;
806
+ feeAmount = xchainUtil.baseAmount(totalFeeSun, TRX_DECIMAL);
807
+ }
808
+ else {
809
+ // TRC20 Transfer - needs both bandwidth and energy
810
+ const bandwidthNeeded = TRC20_TRANSFER_BANDWIDTH;
811
+ const energyNeeded = TRC20_TRANSFER_ENERGY;
812
+ const availableBandwidth = resources.bandwidth.free + (resources.bandwidth.total - resources.bandwidth.used);
813
+ const availableEnergy = resources.energy.total - resources.energy.used;
814
+ let bandwidthFee = 0;
815
+ if (bandwidthNeeded > availableBandwidth) {
816
+ const bandwidthToBuy = bandwidthNeeded - availableBandwidth;
817
+ bandwidthFee = bandwidthToBuy * chainParams.bandwidthFee;
818
+ }
819
+ let energyFee = 0;
820
+ if (energyNeeded > availableEnergy) {
821
+ const energyToBuy = energyNeeded - availableEnergy;
822
+ energyFee = energyToBuy * chainParams.energyFee;
823
+ }
824
+ // Total fee in SUN
825
+ const totalFeeSun = activationFee + bandwidthFee + energyFee;
826
+ feeAmount = xchainUtil.baseAmount(totalFeeSun, TRX_DECIMAL);
827
+ }
828
+ }
829
+ // Tron has Fixed Fee model, unlike ETH or BTC
830
+ return {
831
+ average: feeAmount,
832
+ fast: feeAmount,
833
+ fastest: feeAmount,
834
+ type: xchainClient.FeeType.FlatFee,
835
+ };
836
+ });
837
+ }
838
+ /**
839
+ * Transfer TRON Asset
840
+ * @param {TxParams} params The transfer options.
841
+ * @returns {TxHash} The transaction hash.
842
+ */
843
+ transfer(params) {
844
+ return __awaiter(this, void 0, void 0, function* () {
845
+ const { asset, amount: baseValue, recipient, memo, walletIndex } = params;
846
+ if (!asset)
847
+ throw Error('Asset not provided');
848
+ const amount = baseValue.amount().toString();
849
+ const sender = this.getAddress(walletIndex);
850
+ const isNative = asset.type === xchainUtil.AssetType.NATIVE;
851
+ if (isNative) {
852
+ const transaction = yield this.tronWeb.transactionBuilder.sendTrx(recipient, Number(amount), sender);
853
+ if (memo) {
854
+ const transactionWithMemo = yield this.tronWeb.transactionBuilder.addUpdateData(transaction, memo, 'utf8');
855
+ const signedTx = yield this.signTransaction(transactionWithMemo, walletIndex);
856
+ const { txid } = yield this.tronWeb.trx.sendRawTransaction(signedTx);
857
+ return txid;
858
+ }
859
+ const signedTx = yield this.signTransaction(transaction, walletIndex);
860
+ const { txid } = yield this.tronWeb.trx.sendRawTransaction(signedTx);
861
+ return txid;
862
+ }
863
+ // TRC20 Token Transfer - always use createTransaction + sign pattern
864
+ const transaction = yield this.createTransaction(params);
865
+ const signedTx = yield this.signTransaction(transaction, walletIndex);
866
+ const { txid } = yield this.tronWeb.trx.sendRawTransaction(signedTx);
867
+ if (!txid) {
868
+ throw new Error('TRON Transfer falied');
869
+ }
870
+ return txid;
871
+ });
872
+ }
873
+ /**
874
+ * Check TRC20 allowance.
875
+ * @param {Address} contractAddress The contract address.
876
+ * @param {Address} spenderAddress The spender address.
877
+ * @param {BaseAmount} amount The amount to check if it's allowed to spend or not (optional).
878
+ * @param {number} walletIndex (optional) HD wallet index
879
+ * @param {IsApprovedParams} params - Parameters for checking allowance.
880
+ * @returns {boolean} `true` if the allowance is approved, `false` otherwise.
881
+ */
882
+ isApproved(_a) {
883
+ return __awaiter(this, arguments, void 0, function* ({ contractAddress, spenderAddress, amount, walletIndex }) {
884
+ const from = this.getAddress(walletIndex);
885
+ const allowance = yield this.getApprovedAmount({ contractAddress, from, spenderAddress });
886
+ if (!amount) {
887
+ // If no amount specified, check if there's any approval
888
+ return allowance > BigInt(0);
889
+ }
890
+ return allowance >= BigInt(amount.amount().toString());
891
+ });
892
+ }
893
+ /**
894
+ * Approves an allowance for spending tokens.
895
+ *
896
+ * @param {ApproveParams} params - Parameters for approving an allowance.
897
+ * @param {Address} contractAddress The contract address.
898
+ * @param {Address} spenderAddress The spender address.
899
+ * @param {BaseAmount} amount The amount of token. By default, it will be unlimited token allowance. (optional)
900
+ * @param {number} walletIndex (optional) HD wallet index
901
+ * @returns {TransactionResponse} The result of the approval transaction.
902
+ * @throws Error If gas estimation fails.
903
+ */
904
+ approve(_a) {
905
+ return __awaiter(this, arguments, void 0, function* ({ contractAddress, spenderAddress, amount, walletIndex = 0 }) {
906
+ const fromAddress = this.getAddress(walletIndex);
907
+ const approvalAmount = amount !== undefined ? amount.amount().toString() : MAX_APPROVAL;
908
+ // Build approve transaction using triggerSmartContract
909
+ const functionSelector = 'approve(address,uint256)';
910
+ const parameter = [
911
+ { type: 'address', value: spenderAddress },
912
+ { type: 'uint256', value: approvalAmount },
913
+ ];
914
+ const feeLimit = TRX_FEE_LIMIT;
915
+ const options = { callValue: 0, feeLimit };
916
+ const { transaction } = yield this.tronWeb.transactionBuilder.triggerSmartContract(contractAddress, functionSelector, options, parameter, fromAddress);
917
+ if (!transaction) {
918
+ throw new Error('Failed to build approve transaction');
919
+ }
920
+ const signedTx = yield this.signTransaction(transaction, walletIndex);
921
+ const { txid } = yield this.tronWeb.trx.sendRawTransaction(signedTx);
922
+ if (!txid) {
923
+ throw new Error('TRC20 Approve Failed');
924
+ }
925
+ return txid;
926
+ });
927
+ }
928
+ broadcastTransaction(signedTx) {
929
+ return __awaiter(this, void 0, void 0, function* () {
930
+ const { txid } = yield this.tronWeb.trx.sendRawTransaction(signedTx);
931
+ return txid;
932
+ });
933
+ }
934
+ getTransactions(params) {
935
+ return __awaiter(this, void 0, void 0, function* () {
936
+ const address = (params === null || params === void 0 ? void 0 : params.address) || this.getAddress();
937
+ const offset = (params === null || params === void 0 ? void 0 : params.offset) || 0;
938
+ const limit = (params === null || params === void 0 ? void 0 : params.limit) || 10;
939
+ return this.tronGrid.getTransactions({ address, limit, offset });
940
+ });
941
+ }
942
+ /**
943
+ * Get the transaction details of a given transaction ID.
944
+ *
945
+ * @param {string} txId The transaction ID.
946
+ * @returns {Tx} The transaction details.
947
+ */
948
+ getTransactionData(txId) {
949
+ return __awaiter(this, void 0, void 0, function* () {
950
+ return this.tronGrid.getTransactionData(txId);
951
+ });
952
+ }
953
+ broadcastTx(_signedTx) {
954
+ return __awaiter(this, void 0, void 0, function* () {
955
+ throw Error('Error: not supported');
956
+ });
957
+ }
958
+ prepareTx(_) {
959
+ return __awaiter(this, void 0, void 0, function* () {
960
+ throw new Error('Error: raw tx string not supported');
961
+ });
962
+ }
963
+ getFeesWithRates() {
964
+ return __awaiter(this, void 0, void 0, function* () {
965
+ throw Error('Error: not supported');
966
+ });
967
+ }
968
+ getFeeRates() {
969
+ return __awaiter(this, void 0, void 0, function* () {
970
+ throw Error('Error: not supported');
971
+ });
972
+ }
973
+ }
974
+
975
+ class ClientKeystore extends Client {
976
+ constructor(params = defaultTRONParams) {
977
+ const clientParams = Object.assign(Object.assign({}, defaultTRONParams), params);
978
+ super(clientParams);
979
+ }
980
+ getSigner(walletIndex = 0) {
981
+ if (!this.phrase)
982
+ throw new Error('Phrase must be provided');
983
+ const seed = bip39.mnemonicToSeedSync(this.phrase);
984
+ const hdKey = bip32.HDKey.fromMasterSeed(seed);
985
+ const derived = hdKey.derive(this.getFullDerivationPath(walletIndex));
986
+ if (!derived.privateKey) {
987
+ throw new Error('No Tron Signer');
988
+ }
989
+ const privateKeyHex = Buffer.from(derived.privateKey).toString('hex');
990
+ const isolatedTronWeb = new TW.TronWeb({
991
+ fullHost: TRON_DEFAULT_RPC,
992
+ privateKey: privateKeyHex,
993
+ });
994
+ const address = isolatedTronWeb.address.fromPrivateKey(privateKeyHex);
995
+ return {
996
+ getAddress: () => (typeof address === 'string' ? address : ''),
997
+ signTransaction: (transaction) => __awaiter(this, void 0, void 0, function* () {
998
+ // Use isolated instance - no shared state
999
+ const signedTx = yield isolatedTronWeb.trx.sign(transaction);
1000
+ return signedTx;
1001
+ }),
1002
+ };
1003
+ }
1004
+ /**
1005
+ * Get the current address synchronously.
1006
+ */
1007
+ getAddress(walletIndex = 0) {
1008
+ return this.getSigner(walletIndex).getAddress();
1009
+ }
1010
+ /**
1011
+ * Get the current address asynchronously.
1012
+ *
1013
+ * @param {number} index The index of the address. Default 0
1014
+ * @returns {Address} The TRON address related to the index provided.
1015
+ * @throws {"Phrase must be provided"} Thrown if the phrase has not been set before.
1016
+ */
1017
+ getAddressAsync() {
1018
+ return __awaiter(this, arguments, void 0, function* (walletIndex = 0) {
1019
+ return this.getAddress(walletIndex);
1020
+ });
1021
+ }
1022
+ signTransaction(transaction_1) {
1023
+ return __awaiter(this, arguments, void 0, function* (transaction, walletIndex = 0) {
1024
+ return this.getSigner(walletIndex).signTransaction(transaction);
1025
+ });
1026
+ }
1027
+ }
1028
+
1029
+ exports.AssetTRONUSDT = AssetTRONUSDT;
1030
+ exports.AssetTRX = AssetTRX;
1031
+ exports.Client = ClientKeystore;
1032
+ exports.MAX_APPROVAL = MAX_APPROVAL;
1033
+ exports.TRC20_TRANSFER_BANDWIDTH = TRC20_TRANSFER_BANDWIDTH;
1034
+ exports.TRC20_TRANSFER_ENERGY = TRC20_TRANSFER_ENERGY;
1035
+ exports.TRONChain = TRONChain;
1036
+ exports.TRON_DEFAULT_RPC = TRON_DEFAULT_RPC;
1037
+ exports.TRON_DERIVATION_PATH = TRON_DERIVATION_PATH;
1038
+ exports.TRON_USDT_CONTRACT = TRON_USDT_CONTRACT;
1039
+ exports.TRX_DECIMAL = TRX_DECIMAL;
1040
+ exports.TRX_FEE_LIMIT = TRX_FEE_LIMIT;
1041
+ exports.TRX_TRANSFER_BANDWIDTH = TRX_TRANSFER_BANDWIDTH;
1042
+ exports.defaultTRONParams = defaultTRONParams;
1043
+ exports.tronExplorerProviders = tronExplorerProviders;
1044
+ exports.validateAddress = validateAddress;
1045
+ //# sourceMappingURL=index.js.map