@shapeshiftoss/unchained-client 10.14.11 → 10.16.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.
Files changed (61) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/cosmossdk/thorchain/parser/__tests__/thorchain.test.js +1 -1
  3. package/dist/cjs/cosmossdk/thorchain/parser/rfox.js +1 -1
  4. package/dist/cjs/evm/arbitrum/parser/index.js +2 -2
  5. package/dist/cjs/evm/ethereum/parser/index.js +6 -0
  6. package/dist/cjs/generated/ethereum/apis/V1Api.d.ts +15 -0
  7. package/dist/cjs/generated/ethereum/apis/V1Api.js +24 -0
  8. package/dist/cjs/generated/mayachain/apis/V1Api.d.ts +1 -13
  9. package/dist/cjs/generated/mayachain/apis/V1Api.js +0 -27
  10. package/dist/cjs/generated/mayachain/models/index.d.ts +0 -2
  11. package/dist/cjs/generated/mayachain/models/index.js +0 -2
  12. package/dist/cjs/generated/thorchain/apis/V1Api.d.ts +1 -25
  13. package/dist/cjs/generated/thorchain/apis/V1Api.js +0 -54
  14. package/dist/cjs/generated/thorchain/models/index.d.ts +0 -3
  15. package/dist/cjs/generated/thorchain/models/index.js +0 -3
  16. package/dist/cjs/tron/__tests__/api.test.d.ts +1 -0
  17. package/dist/cjs/tron/__tests__/api.test.js +425 -0
  18. package/dist/cjs/tron/api.d.ts +42 -15
  19. package/dist/cjs/tron/api.js +222 -78
  20. package/dist/cjs/tron/types.d.ts +4 -0
  21. package/dist/esm/.tsbuildinfo +1 -1
  22. package/dist/esm/cosmossdk/thorchain/parser/__tests__/thorchain.test.js +2 -2
  23. package/dist/esm/cosmossdk/thorchain/parser/rfox.js +2 -2
  24. package/dist/esm/evm/arbitrum/parser/index.js +3 -3
  25. package/dist/esm/evm/ethereum/parser/index.js +7 -1
  26. package/dist/esm/generated/ethereum/apis/V1Api.d.ts +15 -0
  27. package/dist/esm/generated/ethereum/apis/V1Api.js +24 -0
  28. package/dist/esm/generated/mayachain/apis/V1Api.d.ts +1 -13
  29. package/dist/esm/generated/mayachain/apis/V1Api.js +1 -28
  30. package/dist/esm/generated/mayachain/models/index.d.ts +0 -2
  31. package/dist/esm/generated/mayachain/models/index.js +0 -2
  32. package/dist/esm/generated/thorchain/apis/V1Api.d.ts +1 -25
  33. package/dist/esm/generated/thorchain/apis/V1Api.js +1 -55
  34. package/dist/esm/generated/thorchain/models/index.d.ts +0 -3
  35. package/dist/esm/generated/thorchain/models/index.js +0 -3
  36. package/dist/esm/tron/__tests__/api.test.d.ts +1 -0
  37. package/dist/esm/tron/__tests__/api.test.js +423 -0
  38. package/dist/esm/tron/api.d.ts +42 -15
  39. package/dist/esm/tron/api.js +220 -77
  40. package/dist/esm/tron/types.d.ts +4 -0
  41. package/package.json +5 -5
  42. package/dist/cjs/generated/mayachain/models/AffiliateFee.d.ts +0 -67
  43. package/dist/cjs/generated/mayachain/models/AffiliateFee.js +0 -63
  44. package/dist/cjs/generated/mayachain/models/AffiliateFees.d.ts +0 -32
  45. package/dist/cjs/generated/mayachain/models/AffiliateFees.js +0 -52
  46. package/dist/cjs/generated/thorchain/models/AffiliateFee.d.ts +0 -67
  47. package/dist/cjs/generated/thorchain/models/AffiliateFee.js +0 -63
  48. package/dist/cjs/generated/thorchain/models/AffiliateFees.d.ts +0 -32
  49. package/dist/cjs/generated/thorchain/models/AffiliateFees.js +0 -52
  50. package/dist/cjs/generated/thorchain/models/AffiliateRevenue.d.ts +0 -45
  51. package/dist/cjs/generated/thorchain/models/AffiliateRevenue.js +0 -57
  52. package/dist/esm/generated/mayachain/models/AffiliateFee.d.ts +0 -67
  53. package/dist/esm/generated/mayachain/models/AffiliateFee.js +0 -56
  54. package/dist/esm/generated/mayachain/models/AffiliateFees.d.ts +0 -32
  55. package/dist/esm/generated/mayachain/models/AffiliateFees.js +0 -45
  56. package/dist/esm/generated/thorchain/models/AffiliateFee.d.ts +0 -67
  57. package/dist/esm/generated/thorchain/models/AffiliateFee.js +0 -56
  58. package/dist/esm/generated/thorchain/models/AffiliateFees.d.ts +0 -32
  59. package/dist/esm/generated/thorchain/models/AffiliateFees.js +0 -45
  60. package/dist/esm/generated/thorchain/models/AffiliateRevenue.d.ts +0 -45
  61. package/dist/esm/generated/thorchain/models/AffiliateRevenue.js +0 -50
@@ -0,0 +1,425 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const api_1 = require("../api");
5
+ const successResponse = {
6
+ result: { result: true },
7
+ energy_used: 64285,
8
+ energy_penalty: 49635,
9
+ constant_result: ['0000000000000000000000000000000000000000000000000000000000000000'],
10
+ transaction: { ret: [{}] },
11
+ };
12
+ // A reverted simulation still reports result.result: true - the failure is only visible on
13
+ // transaction.ret and the message
14
+ const revertResponse = {
15
+ result: { result: true, message: 'REVERT opcode executed' },
16
+ energy_used: 7896,
17
+ energy_penalty: 6079,
18
+ constant_result: [''],
19
+ transaction: { ret: [{ ret: 'FAILED' }] },
20
+ };
21
+ (0, vitest_1.describe)('TronApi', () => {
22
+ const api = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
23
+ (0, vitest_1.beforeEach)(() => {
24
+ vitest_1.vi.spyOn(api, 'getChainPrices').mockResolvedValue({
25
+ bandwidthPrice: 1000,
26
+ energyPrice: 100,
27
+ memoFee: 1_000_000,
28
+ });
29
+ vitest_1.vi.spyOn(api, 'getContractEnergyShare').mockResolvedValue({
30
+ callerPercent: 100,
31
+ originEnergyLimit: 0,
32
+ originEnergyAvailable: 0,
33
+ });
34
+ });
35
+ (0, vitest_1.afterEach)(() => {
36
+ vitest_1.vi.restoreAllMocks();
37
+ vitest_1.vi.unstubAllGlobals();
38
+ });
39
+ (0, vitest_1.describe)('estimateContractCallFee', () => {
40
+ const params = {
41
+ contractAddress: 'TAfbit1ENsRmtZbPQfYU3srURpfYuWYS7K',
42
+ from: 'TT2T17KZhoDu47i2E4FWxfG79zdkEWkU9N',
43
+ data: '0x2213bc0b',
44
+ };
45
+ (0, vitest_1.it)('prices a successful simulation at energy_used * energyPrice', async () => {
46
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn().mockResolvedValue({ ok: true, json: () => successResponse }));
47
+ (0, vitest_1.expect)(await api.estimateContractCallFee(params)).toBe('6428500');
48
+ });
49
+ (0, vitest_1.it)('prices the caller in full when the share lookup fails', async () => {
50
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn().mockResolvedValue({ ok: true, json: () => successResponse }));
51
+ vitest_1.vi.mocked(api.getContractEnergyShare).mockRejectedValue(new Error('429'));
52
+ (0, vitest_1.expect)(await api.estimateContractCallFee(params)).toBe('6428500');
53
+ });
54
+ (0, vitest_1.it)('fails instead when the caller requires the share', async () => {
55
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn().mockResolvedValue({ ok: true, json: () => successResponse }));
56
+ vitest_1.vi.mocked(api.getContractEnergyShare).mockRejectedValue(new Error('429'));
57
+ await (0, vitest_1.expect)(api.estimateContractCallFee({ ...params, requireEnergyShare: true })).rejects.toThrow('429');
58
+ });
59
+ (0, vitest_1.it)('bills the caller only their share when the deployer covers the rest', async () => {
60
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn().mockResolvedValue({ ok: true, json: () => successResponse }));
61
+ vitest_1.vi.mocked(api.getContractEnergyShare).mockResolvedValue({
62
+ callerPercent: 5,
63
+ originEnergyLimit: 10_000_000,
64
+ originEnergyAvailable: 5_000_000,
65
+ });
66
+ // 64285 - floor(64285 * 0.95) = 3215
67
+ (0, vitest_1.expect)(await api.estimateContractCallFee(params)).toBe('321500');
68
+ });
69
+ (0, vitest_1.it)('throws on a reverted simulation rather than trusting the partial energy', async () => {
70
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn().mockResolvedValue({ ok: true, json: () => revertResponse }));
71
+ await (0, vitest_1.expect)(api.estimateContractCallFee(params)).rejects.toThrow('REVERT opcode executed');
72
+ });
73
+ (0, vitest_1.it)('reports a failed request as such rather than as a revert', async () => {
74
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn().mockResolvedValue({ ok: false, status: 429, json: () => ({}) }));
75
+ await (0, vitest_1.expect)(api.estimateContractCallFee(params)).rejects.toThrow('request failed: 429');
76
+ });
77
+ });
78
+ (0, vitest_1.describe)('getCallerEnergy', () => {
79
+ (0, vitest_1.it)('charges the caller in full when the contract sets no deployer share', () => {
80
+ const share = {
81
+ callerPercent: 100,
82
+ originEnergyLimit: 0,
83
+ originEnergyAvailable: 0,
84
+ };
85
+ (0, vitest_1.expect)((0, api_1.getCallerEnergy)(901_682, share)).toBe(901_682);
86
+ });
87
+ (0, vitest_1.it)('charges the caller their percent when the deployer has the energy staked', () => {
88
+ const share = {
89
+ callerPercent: 5,
90
+ originEnergyLimit: 10_000_000,
91
+ originEnergyAvailable: 5_000_000,
92
+ };
93
+ (0, vitest_1.expect)((0, api_1.getCallerEnergy)(901_682, share)).toBe(45_085);
94
+ });
95
+ (0, vitest_1.it)('charges the caller in full when the deployer has nothing staked', () => {
96
+ const share = {
97
+ callerPercent: 30,
98
+ originEnergyLimit: 10_000_000,
99
+ originEnergyAvailable: 0,
100
+ };
101
+ (0, vitest_1.expect)((0, api_1.getCallerEnergy)(130_285, share)).toBe(130_285);
102
+ });
103
+ (0, vitest_1.it)('caps the deployer share at their per-call limit', () => {
104
+ const share = {
105
+ callerPercent: 0,
106
+ originEnergyLimit: 100_000,
107
+ originEnergyAvailable: 5_000_000,
108
+ };
109
+ (0, vitest_1.expect)((0, api_1.getCallerEnergy)(300_000, share)).toBe(200_000);
110
+ });
111
+ (0, vitest_1.it)('caps the deployer share at what they have left', () => {
112
+ const share = {
113
+ callerPercent: 0,
114
+ originEnergyLimit: 10_000_000,
115
+ originEnergyAvailable: 40_000,
116
+ };
117
+ (0, vitest_1.expect)((0, api_1.getCallerEnergy)(300_000, share)).toBe(260_000);
118
+ });
119
+ });
120
+ (0, vitest_1.describe)('getContractEnergyShare', () => {
121
+ const contractResponse = {
122
+ consume_user_resource_percent: 5,
123
+ origin_energy_limit: 10_000_000,
124
+ origin_address: 'TDeployer',
125
+ };
126
+ const resourceResponse = { EnergyLimit: 6_000_000, EnergyUsed: 1_000_000 };
127
+ const respond = (...bodies) => {
128
+ const fetchMock = vitest_1.vi.fn();
129
+ bodies.forEach(body => fetchMock.mockResolvedValueOnce({ ok: true, json: () => body }));
130
+ vitest_1.vi.stubGlobal('fetch', fetchMock);
131
+ return fetchMock;
132
+ };
133
+ // the contract record is cached per instance, so each case gets its own
134
+ const freshApi = () => new api_1.TronApi({ rpcUrl: 'https://tron.example' });
135
+ (0, vitest_1.it)('reads the caller percent, per-call limit and the unspent deployer energy', async () => {
136
+ respond(contractResponse, resourceResponse);
137
+ (0, vitest_1.expect)(await freshApi().getContractEnergyShare('TRouter')).toEqual({
138
+ callerPercent: 5,
139
+ originEnergyLimit: 10_000_000,
140
+ originEnergyAvailable: 5_000_000,
141
+ });
142
+ });
143
+ (0, vitest_1.it)('reads an omitted percent as the deployer paying everything', async () => {
144
+ respond({ origin_energy_limit: 10_000_000, origin_address: 'TDeployer' }, resourceResponse);
145
+ (0, vitest_1.expect)(await freshApi().getContractEnergyShare('TRouter')).toMatchObject({ callerPercent: 0 });
146
+ });
147
+ (0, vitest_1.it)('reads an omitted per-call limit as the creator default', async () => {
148
+ respond({ consume_user_resource_percent: 5, origin_address: 'TDeployer' }, resourceResponse);
149
+ (0, vitest_1.expect)(await freshApi().getContractEnergyShare('TRouter')).toMatchObject({
150
+ originEnergyLimit: 10_000_000,
151
+ });
152
+ });
153
+ (0, vitest_1.it)('reads a plain address as a call the caller pays in full without a resource lookup', async () => {
154
+ const fetchMock = respond({});
155
+ (0, vitest_1.expect)(await freshApi().getContractEnergyShare('TPlain')).toEqual({
156
+ callerPercent: 100,
157
+ originEnergyLimit: 0,
158
+ originEnergyAvailable: 0,
159
+ });
160
+ (0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(1);
161
+ });
162
+ (0, vitest_1.it)('re-reads an empty body rather than caching it as a plain address', async () => {
163
+ const fetchMock = respond({}, contractResponse, resourceResponse);
164
+ const api = freshApi();
165
+ await api.getContractEnergyShare('TRouter');
166
+ (0, vitest_1.expect)(await api.getContractEnergyShare('TRouter')).toMatchObject({ callerPercent: 5 });
167
+ (0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(3);
168
+ });
169
+ (0, vitest_1.it)('rejects a failed lookup rather than guessing the split', async () => {
170
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn().mockResolvedValue({ ok: false, status: 429 }));
171
+ await (0, vitest_1.expect)(freshApi().getContractEnergyShare('TRouter')).rejects.toThrow('429');
172
+ });
173
+ (0, vitest_1.it)('does not cache an error body the node returns with a 200', async () => {
174
+ const fetchMock = respond({ Error: 'class org.tron.core.exception' }, contractResponse, resourceResponse);
175
+ const api = freshApi();
176
+ await (0, vitest_1.expect)(api.getContractEnergyShare('TRouter')).rejects.toThrow('getcontract failed');
177
+ (0, vitest_1.expect)(await api.getContractEnergyShare('TRouter')).toMatchObject({ callerPercent: 5 });
178
+ (0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(3);
179
+ });
180
+ (0, vitest_1.it)('reads the contract once and the deployer energy once per refresh window', async () => {
181
+ vitest_1.vi.useFakeTimers();
182
+ const fetchMock = respond(contractResponse, resourceResponse, resourceResponse);
183
+ const api = freshApi();
184
+ await api.getContractEnergyShare('TRouter');
185
+ await api.getContractEnergyShare('TRouter');
186
+ (0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(2);
187
+ vitest_1.vi.advanceTimersByTime(16_000);
188
+ await api.getContractEnergyShare('TRouter');
189
+ (0, vitest_1.expect)(fetchMock).toHaveBeenCalledTimes(3);
190
+ const contractReads = fetchMock.mock.calls.filter(([url]) => String(url).endsWith('/wallet/getcontract'));
191
+ (0, vitest_1.expect)(contractReads).toHaveLength(1);
192
+ vitest_1.vi.useRealTimers();
193
+ });
194
+ (0, vitest_1.it)('re-reads the contract after a minute, since its split can change', async () => {
195
+ vitest_1.vi.useFakeTimers();
196
+ const fetchMock = respond(contractResponse, resourceResponse, contractResponse, resourceResponse);
197
+ const api = freshApi();
198
+ await api.getContractEnergyShare('TRouter');
199
+ vitest_1.vi.advanceTimersByTime(61_000);
200
+ await api.getContractEnergyShare('TRouter');
201
+ const contractReads = fetchMock.mock.calls.filter(([url]) => String(url).endsWith('/wallet/getcontract'));
202
+ (0, vitest_1.expect)(contractReads).toHaveLength(2);
203
+ vitest_1.vi.useRealTimers();
204
+ });
205
+ });
206
+ (0, vitest_1.describe)('getChainPrices', () => {
207
+ (0, vitest_1.it)('reads the energy, bandwidth and memo parameters', async () => {
208
+ vitest_1.vi.restoreAllMocks();
209
+ const tronWeb = api.getTronWeb();
210
+ vitest_1.vi.spyOn(tronWeb.trx, 'getChainParameters').mockResolvedValue([
211
+ { key: 'getTransactionFee', value: 1000 },
212
+ { key: 'getEnergyFee', value: 100 },
213
+ { key: 'getMemoFee', value: 1_000_000 },
214
+ ]);
215
+ (0, vitest_1.expect)(await api.getChainPrices()).toEqual({
216
+ bandwidthPrice: 1000,
217
+ energyPrice: 100,
218
+ memoFee: 1_000_000,
219
+ });
220
+ });
221
+ (0, vitest_1.it)('throws rather than defaulting when the node is unreachable', async () => {
222
+ vitest_1.vi.restoreAllMocks();
223
+ const tronWeb = api.getTronWeb();
224
+ vitest_1.vi.spyOn(tronWeb.trx, 'getChainParameters').mockRejectedValue(new Error('ECONNREFUSED'));
225
+ await (0, vitest_1.expect)(api.getChainPrices()).rejects.toThrow('ECONNREFUSED');
226
+ });
227
+ });
228
+ (0, vitest_1.describe)('getTrc20Balance', () => {
229
+ (0, vitest_1.it)('reads balanceOf(address) with a single constant call', async () => {
230
+ const tronWeb = api.getTronWeb();
231
+ const trigger = vitest_1.vi
232
+ .spyOn(tronWeb.transactionBuilder, 'triggerConstantContract')
233
+ .mockResolvedValue({
234
+ result: { result: true },
235
+ constant_result: ['00000000000000000000000000000000000000000000000000000000028e6fb0'],
236
+ });
237
+ const actual = await api.getTrc20Balance({
238
+ contractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
239
+ address: 'TE6oHVdTbcp1Q9XBYx5VzjWbZEg3t3Jrnc',
240
+ });
241
+ (0, vitest_1.expect)(actual).toBe('42889136');
242
+ (0, vitest_1.expect)(trigger).toHaveBeenCalledWith('TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', 'balanceOf(address)', {}, [{ type: 'address', value: 'TE6oHVdTbcp1Q9XBYx5VzjWbZEg3t3Jrnc' }], 'TE6oHVdTbcp1Q9XBYx5VzjWbZEg3t3Jrnc');
243
+ });
244
+ });
245
+ (0, vitest_1.describe)('getTrc20Decimals', () => {
246
+ const USDT = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
247
+ const eighteen = '0000000000000000000000000000000000000000000000000000000000000012';
248
+ (0, vitest_1.it)('reads decimals() with a single constant call', async () => {
249
+ const freshApi = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
250
+ const tronWeb = freshApi.getTronWeb();
251
+ const trigger = vitest_1.vi
252
+ .spyOn(tronWeb.transactionBuilder, 'triggerConstantContract')
253
+ .mockResolvedValue({ result: { result: true }, constant_result: [eighteen] });
254
+ (0, vitest_1.expect)(await freshApi.getTrc20Decimals({ contractAddress: USDT })).toBe(18);
255
+ (0, vitest_1.expect)(trigger).toHaveBeenCalledWith(USDT, 'decimals()', {}, [], USDT);
256
+ });
257
+ (0, vitest_1.it)('reads a reverted decimals() as unknown', async () => {
258
+ const freshApi = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
259
+ const tronWeb = freshApi.getTronWeb();
260
+ const trigger = vitest_1.vi
261
+ .spyOn(tronWeb.transactionBuilder, 'triggerConstantContract')
262
+ .mockResolvedValueOnce({
263
+ result: { result: true, message: 'REVERT opcode executed' },
264
+ constant_result: ['08c379a0' + '0'.repeat(56)],
265
+ transaction: { ret: [{ ret: 'FAILED' }] },
266
+ })
267
+ .mockResolvedValue({ result: { result: true }, constant_result: [eighteen] });
268
+ (0, vitest_1.expect)(await freshApi.getTrc20Decimals({ contractAddress: USDT })).toBeUndefined();
269
+ (0, vitest_1.expect)(await freshApi.getTrc20Decimals({ contractAddress: USDT })).toBe(18);
270
+ (0, vitest_1.expect)(trigger).toHaveBeenCalledTimes(2);
271
+ });
272
+ (0, vitest_1.it)('reads a value outside a uint8 as unknown', async () => {
273
+ const freshApi = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
274
+ const tronWeb = freshApi.getTronWeb();
275
+ vitest_1.vi.spyOn(tronWeb.transactionBuilder, 'triggerConstantContract').mockResolvedValue({
276
+ result: { result: true },
277
+ constant_result: ['0'.repeat(61) + '100'],
278
+ });
279
+ (0, vitest_1.expect)(await freshApi.getTrc20Decimals({ contractAddress: USDT })).toBeUndefined();
280
+ });
281
+ (0, vitest_1.it)('gives up on a call that hangs', async () => {
282
+ vitest_1.vi.useFakeTimers();
283
+ try {
284
+ const freshApi = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
285
+ const tronWeb = freshApi.getTronWeb();
286
+ vitest_1.vi.spyOn(tronWeb.transactionBuilder, 'triggerConstantContract').mockReturnValue(new Promise(() => undefined));
287
+ const pending = freshApi.getTrc20Decimals({ contractAddress: USDT });
288
+ await vitest_1.vi.runAllTimersAsync();
289
+ (0, vitest_1.expect)(await pending).toBeUndefined();
290
+ }
291
+ finally {
292
+ vitest_1.vi.useRealTimers();
293
+ }
294
+ });
295
+ (0, vitest_1.it)('reads a failed call as unknown', async () => {
296
+ const freshApi = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
297
+ const tronWeb = freshApi.getTronWeb();
298
+ const trigger = vitest_1.vi
299
+ .spyOn(tronWeb.transactionBuilder, 'triggerConstantContract')
300
+ .mockRejectedValueOnce(new Error('429'))
301
+ .mockResolvedValue({ result: { result: true }, constant_result: [eighteen] });
302
+ (0, vitest_1.expect)(await freshApi.getTrc20Decimals({ contractAddress: USDT })).toBeUndefined();
303
+ (0, vitest_1.expect)(await freshApi.getTrc20Decimals({ contractAddress: USDT })).toBe(18);
304
+ (0, vitest_1.expect)(trigger).toHaveBeenCalledTimes(2);
305
+ });
306
+ });
307
+ (0, vitest_1.describe)('getTrc10Precision', () => {
308
+ const respond = (body) => {
309
+ const fetchMock = vitest_1.vi.fn().mockResolvedValue({ ok: true, json: () => body });
310
+ vitest_1.vi.stubGlobal('fetch', fetchMock);
311
+ return fetchMock;
312
+ };
313
+ (0, vitest_1.it)('reads the precision of an issued token', async () => {
314
+ const freshApi = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
315
+ const fetchMock = respond({ success: true, data: [{ id: '1002000', precision: 6 }] });
316
+ (0, vitest_1.expect)(await freshApi.getTrc10Precision({ id: '1002000' })).toBe(6);
317
+ (0, vitest_1.expect)(fetchMock).toHaveBeenCalledWith('https://tron.example/v1/assets/1002000', vitest_1.expect.objectContaining({ headers: vitest_1.expect.anything() }));
318
+ });
319
+ (0, vitest_1.it)('reads a zero precision as issued', async () => {
320
+ const freshApi = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
321
+ respond({ success: true, data: [{ id: '1000001', precision: 0 }] });
322
+ (0, vitest_1.expect)(await freshApi.getTrc10Precision({ id: '1000001' })).toBe(0);
323
+ });
324
+ (0, vitest_1.it)('reads an unknown id as unknown', async () => {
325
+ const freshApi = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
326
+ respond({ success: false, error: 'A valid account address or asset id is required.' });
327
+ (0, vitest_1.expect)(await freshApi.getTrc10Precision({ id: '9999999' })).toBeUndefined();
328
+ });
329
+ (0, vitest_1.it)('reads an empty listing as unknown', async () => {
330
+ const freshApi = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
331
+ respond({ success: true, data: [] });
332
+ (0, vitest_1.expect)(await freshApi.getTrc10Precision({ id: '1009999' })).toBeUndefined();
333
+ });
334
+ (0, vitest_1.it)('gives up on a request that hangs', async () => {
335
+ vitest_1.vi.useFakeTimers();
336
+ try {
337
+ const freshApi = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
338
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn().mockReturnValue(new Promise(() => undefined)));
339
+ const pending = freshApi.getTrc10Precision({ id: '1002000' });
340
+ await vitest_1.vi.runAllTimersAsync();
341
+ (0, vitest_1.expect)(await pending).toBeUndefined();
342
+ }
343
+ finally {
344
+ vitest_1.vi.useRealTimers();
345
+ }
346
+ });
347
+ (0, vitest_1.it)('gives up on a response whose body stalls', async () => {
348
+ vitest_1.vi.useFakeTimers();
349
+ try {
350
+ const freshApi = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
351
+ const fetchMock = vitest_1.vi
352
+ .fn()
353
+ .mockResolvedValue({ ok: true, json: () => new Promise(() => undefined) });
354
+ vitest_1.vi.stubGlobal('fetch', fetchMock);
355
+ const pending = freshApi.getTrc10Precision({ id: '1002000' });
356
+ await vitest_1.vi.runAllTimersAsync();
357
+ (0, vitest_1.expect)(await pending).toBeUndefined();
358
+ (0, vitest_1.expect)(fetchMock).toHaveBeenCalledWith('https://tron.example/v1/assets/1002000', vitest_1.expect.objectContaining({ signal: vitest_1.expect.any(AbortSignal) }));
359
+ }
360
+ finally {
361
+ vitest_1.vi.useRealTimers();
362
+ }
363
+ });
364
+ (0, vitest_1.it)('reads a failed request as unknown', async () => {
365
+ const freshApi = new api_1.TronApi({ rpcUrl: 'https://tron.example' });
366
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn().mockRejectedValue(new Error('429')));
367
+ (0, vitest_1.expect)(await freshApi.getTrc10Precision({ id: '1002000' })).toBeUndefined();
368
+ });
369
+ });
370
+ (0, vitest_1.describe)('getTrc20Allowance', () => {
371
+ (0, vitest_1.it)('reads allowance(owner, spender) with a single constant call', async () => {
372
+ const tronWeb = api.getTronWeb();
373
+ const trigger = vitest_1.vi
374
+ .spyOn(tronWeb.transactionBuilder, 'triggerConstantContract')
375
+ .mockResolvedValue({
376
+ result: { result: true },
377
+ constant_result: ['000000000000000000000000000000000000000000000000000000000000007b'],
378
+ });
379
+ const actual = await api.getTrc20Allowance({
380
+ contractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
381
+ owner: 'TT2T17KZhoDu47i2E4FWxfG79zdkEWkU9N',
382
+ spender: 'TAfbit1ENsRmtZbPQfYU3srURpfYuWYS7K',
383
+ });
384
+ (0, vitest_1.expect)(actual).toBe('123');
385
+ (0, vitest_1.expect)(trigger).toHaveBeenCalledWith('TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', 'allowance(address,address)', {}, [
386
+ { type: 'address', value: 'TT2T17KZhoDu47i2E4FWxfG79zdkEWkU9N' },
387
+ { type: 'address', value: 'TAfbit1ENsRmtZbPQfYU3srURpfYuWYS7K' },
388
+ ], 'TT2T17KZhoDu47i2E4FWxfG79zdkEWkU9N');
389
+ });
390
+ });
391
+ (0, vitest_1.describe)('isAccountActivated', () => {
392
+ (0, vitest_1.it)('is true for an account the node knows', async () => {
393
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn().mockResolvedValue({ ok: true, json: () => ({ address: '41abc', balance: 1 }) }));
394
+ (0, vitest_1.expect)(await api.isAccountActivated('TT2T17KZhoDu47i2E4FWxfG79zdkEWkU9N')).toBe(true);
395
+ });
396
+ (0, vitest_1.it)('is false for a fresh address', async () => {
397
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn().mockResolvedValue({ ok: true, json: () => ({}) }));
398
+ (0, vitest_1.expect)(await api.isAccountActivated('TT2T17KZhoDu47i2E4FWxfG79zdkEWkU9N')).toBe(false);
399
+ });
400
+ (0, vitest_1.it)('throws on an error response rather than reading it as not activated', async () => {
401
+ vitest_1.vi.stubGlobal('fetch', vitest_1.vi.fn().mockResolvedValue({ ok: false, status: 429, json: () => ({ Error: 'throttled' }) }));
402
+ await (0, vitest_1.expect)(api.isAccountActivated('TT2T17KZhoDu47i2E4FWxfG79zdkEWkU9N')).rejects.toThrow('429');
403
+ });
404
+ });
405
+ (0, vitest_1.describe)('estimateTrc20TransferFee', () => {
406
+ const params = {
407
+ contractAddress: 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t',
408
+ from: 'TT2T17KZhoDu47i2E4FWxfG79zdkEWkU9N',
409
+ to: 'TT2T17KZhoDu47i2E4FWxfG79zdkEWkU9N',
410
+ amount: '1',
411
+ };
412
+ const stubTriggerConstantContract = (response) => {
413
+ const tronWeb = api.getTronWeb();
414
+ vitest_1.vi.spyOn(tronWeb.transactionBuilder, 'triggerConstantContract').mockResolvedValue(response);
415
+ };
416
+ (0, vitest_1.it)('prices a successful simulation at energy_used * energyPrice', async () => {
417
+ stubTriggerConstantContract(successResponse);
418
+ (0, vitest_1.expect)(await api.estimateTrc20TransferFee(params)).toBe('6428500');
419
+ });
420
+ (0, vitest_1.it)('throws on a reverted simulation rather than trusting the partial energy', async () => {
421
+ stubTriggerConstantContract(revertResponse);
422
+ await (0, vitest_1.expect)(api.estimateTrc20TransferFee(params)).rejects.toThrow('REVERT opcode executed');
423
+ });
424
+ });
425
+ });
@@ -3,10 +3,18 @@ export interface TronApiConfig {
3
3
  rpcUrl: string;
4
4
  apiKey?: string;
5
5
  }
6
+ export type TronContractEnergyShare = {
7
+ callerPercent: number;
8
+ originEnergyLimit: number;
9
+ originEnergyAvailable: number;
10
+ };
11
+ export declare const getCallerEnergy: (energyUsed: number, share: TronContractEnergyShare) => number;
6
12
  export declare class TronApi {
7
13
  private readonly rpcUrl;
8
14
  private readonly apiKey;
9
15
  private tronWeb;
16
+ private readonly contracts;
17
+ private readonly originEnergy;
10
18
  private requestQueue;
11
19
  private readonly minRequestInterval;
12
20
  constructor(config: TronApiConfig);
@@ -24,10 +32,22 @@ export declare class TronApi {
24
32
  balance: string;
25
33
  }[];
26
34
  }>;
27
- getTRC20Balance(params: {
35
+ getTrc20Decimals(params: {
36
+ contractAddress: string;
37
+ }): Promise<number | undefined>;
38
+ getTrc10Precision(params: {
39
+ id: string;
40
+ }): Promise<number | undefined>;
41
+ getTrc20Balance(params: {
42
+ contractAddress: string;
28
43
  address: string;
44
+ }): Promise<string>;
45
+ getTrc20Allowance(params: {
29
46
  contractAddress: string;
47
+ owner: string;
48
+ spender: string;
30
49
  }): Promise<string>;
50
+ isAccountActivated(address: string): Promise<boolean>;
31
51
  getTxHistory(_params: {
32
52
  pubkey: string;
33
53
  pageSize?: number;
@@ -47,23 +67,30 @@ export declare class TronApi {
47
67
  hex: string;
48
68
  };
49
69
  }): Promise<string>;
50
- private getChainPrices;
51
- estimateFees(params: {
52
- estimateFeesBody: {
53
- serializedTx: string;
54
- };
55
- }): Promise<string>;
56
- estimateTRC20TransferFee(params: {
70
+ getChainPrices(): Promise<{
71
+ bandwidthPrice: number;
72
+ energyPrice: number;
73
+ memoFee: number;
74
+ }>;
75
+ getContractEnergyShare(contractAddress: string): Promise<TronContractEnergyShare>;
76
+ private getContract;
77
+ private getPricingContext;
78
+ private getOriginEnergyAvailable;
79
+ private post;
80
+ estimateTrc20TransferFee(params: {
57
81
  contractAddress: string;
58
82
  from: string;
59
83
  to: string;
60
84
  amount: string;
85
+ requireEnergyShare?: boolean;
61
86
  }): Promise<string>;
62
- getPriorityFees(): Promise<{
63
- baseFee: string;
64
- fast: string;
65
- average: string;
66
- slow: string;
67
- estimatedBandwidth: string;
68
- }>;
87
+ estimateContractCallFee(params: {
88
+ contractAddress: string;
89
+ from: string;
90
+ data: string;
91
+ callValue?: string;
92
+ requireEnergyShare?: boolean;
93
+ }): Promise<string>;
94
+ private isReverted;
95
+ private getSimulatedEnergy;
69
96
  }