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