@shapeshiftoss/unchained-client 10.15.0 → 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.
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/generated/ethereum/apis/V1Api.d.ts +15 -0
- package/dist/cjs/generated/ethereum/apis/V1Api.js +24 -0
- package/dist/cjs/tron/__tests__/api.test.js +125 -0
- package/dist/cjs/tron/api.d.ts +7 -0
- package/dist/cjs/tron/api.js +39 -3
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/generated/ethereum/apis/V1Api.d.ts +15 -0
- package/dist/esm/generated/ethereum/apis/V1Api.js +24 -0
- package/dist/esm/tron/__tests__/api.test.js +125 -0
- package/dist/esm/tron/api.d.ts +7 -0
- package/dist/esm/tron/api.js +39 -3
- package/package.json +3 -3
|
@@ -20,6 +20,9 @@ export interface EstimateGasRequest {
|
|
|
20
20
|
export interface GetAccountRequest {
|
|
21
21
|
pubkey: string;
|
|
22
22
|
}
|
|
23
|
+
export interface GetRfoxStakingDurationRequest {
|
|
24
|
+
address: string;
|
|
25
|
+
}
|
|
23
26
|
export interface GetTransactionRequest {
|
|
24
27
|
txid: string;
|
|
25
28
|
}
|
|
@@ -77,6 +80,18 @@ export declare class V1Api extends runtime.BaseAPI {
|
|
|
77
80
|
* Get information about the running coinstack
|
|
78
81
|
*/
|
|
79
82
|
getInfo(initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<BaseInfo>;
|
|
83
|
+
/**
|
|
84
|
+
* Get rFOX staking duration by contract address
|
|
85
|
+
*/
|
|
86
|
+
getRfoxStakingDurationRaw(requestParameters: GetRfoxStakingDurationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<runtime.ApiResponse<{
|
|
87
|
+
[key: string]: number;
|
|
88
|
+
}>>;
|
|
89
|
+
/**
|
|
90
|
+
* Get rFOX staking duration by contract address
|
|
91
|
+
*/
|
|
92
|
+
getRfoxStakingDuration(requestParameters: GetRfoxStakingDurationRequest, initOverrides?: RequestInit | runtime.InitOverrideFunction): Promise<{
|
|
93
|
+
[key: string]: number;
|
|
94
|
+
}>;
|
|
80
95
|
/**
|
|
81
96
|
* Get transaction details
|
|
82
97
|
*/
|
|
@@ -162,6 +162,30 @@ class V1Api extends runtime.BaseAPI {
|
|
|
162
162
|
const response = await this.getInfoRaw(initOverrides);
|
|
163
163
|
return await response.value();
|
|
164
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Get rFOX staking duration by contract address
|
|
167
|
+
*/
|
|
168
|
+
async getRfoxStakingDurationRaw(requestParameters, initOverrides) {
|
|
169
|
+
if (requestParameters.address === null || requestParameters.address === undefined) {
|
|
170
|
+
throw new runtime.RequiredError('address', 'Required parameter requestParameters.address was null or undefined when calling getRfoxStakingDuration.');
|
|
171
|
+
}
|
|
172
|
+
const queryParameters = {};
|
|
173
|
+
const headerParameters = {};
|
|
174
|
+
const response = await this.request({
|
|
175
|
+
path: `/api/v1/rfox/staking-duration/{address}`.replace(`{${"address"}}`, encodeURIComponent(String(requestParameters.address))),
|
|
176
|
+
method: 'GET',
|
|
177
|
+
headers: headerParameters,
|
|
178
|
+
query: queryParameters,
|
|
179
|
+
}, initOverrides);
|
|
180
|
+
return new runtime.JSONApiResponse(response);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get rFOX staking duration by contract address
|
|
184
|
+
*/
|
|
185
|
+
async getRfoxStakingDuration(requestParameters, initOverrides) {
|
|
186
|
+
const response = await this.getRfoxStakingDurationRaw(requestParameters, initOverrides);
|
|
187
|
+
return await response.value();
|
|
188
|
+
}
|
|
165
189
|
/**
|
|
166
190
|
* Get transaction details
|
|
167
191
|
*/
|
|
@@ -242,6 +242,131 @@ const revertResponse = {
|
|
|
242
242
|
(0, vitest_1.expect)(trigger).toHaveBeenCalledWith('TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t', 'balanceOf(address)', {}, [{ type: 'address', value: 'TE6oHVdTbcp1Q9XBYx5VzjWbZEg3t3Jrnc' }], 'TE6oHVdTbcp1Q9XBYx5VzjWbZEg3t3Jrnc');
|
|
243
243
|
});
|
|
244
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
|
+
});
|
|
245
370
|
(0, vitest_1.describe)('getTrc20Allowance', () => {
|
|
246
371
|
(0, vitest_1.it)('reads allowance(owner, spender) with a single constant call', async () => {
|
|
247
372
|
const tronWeb = api.getTronWeb();
|
package/dist/cjs/tron/api.d.ts
CHANGED
|
@@ -32,6 +32,12 @@ export declare class TronApi {
|
|
|
32
32
|
balance: string;
|
|
33
33
|
}[];
|
|
34
34
|
}>;
|
|
35
|
+
getTrc20Decimals(params: {
|
|
36
|
+
contractAddress: string;
|
|
37
|
+
}): Promise<number | undefined>;
|
|
38
|
+
getTrc10Precision(params: {
|
|
39
|
+
id: string;
|
|
40
|
+
}): Promise<number | undefined>;
|
|
35
41
|
getTrc20Balance(params: {
|
|
36
42
|
contractAddress: string;
|
|
37
43
|
address: string;
|
|
@@ -85,5 +91,6 @@ export declare class TronApi {
|
|
|
85
91
|
callValue?: string;
|
|
86
92
|
requireEnergyShare?: boolean;
|
|
87
93
|
}): Promise<string>;
|
|
94
|
+
private isReverted;
|
|
88
95
|
private getSimulatedEnergy;
|
|
89
96
|
}
|
package/dist/cjs/tron/api.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TronApi = exports.getCallerEnergy = void 0;
|
|
4
|
+
const utils_1 = require("@shapeshiftoss/utils");
|
|
4
5
|
const tronweb_1 = require("tronweb");
|
|
6
|
+
const PRECISION_READ_TIMEOUT_MS = 10_000;
|
|
5
7
|
const TRON_CALLER_PAYS_ALL = {
|
|
6
8
|
callerPercent: 100,
|
|
7
9
|
originEnergyLimit: 0,
|
|
@@ -144,6 +146,36 @@ class TronApi {
|
|
|
144
146
|
tokens,
|
|
145
147
|
};
|
|
146
148
|
}
|
|
149
|
+
async getTrc20Decimals(params) {
|
|
150
|
+
try {
|
|
151
|
+
const result = await (0, utils_1.timeout)(this.getTronWeb().transactionBuilder.triggerConstantContract(params.contractAddress, 'decimals()', {}, [], params.contractAddress), PRECISION_READ_TIMEOUT_MS, undefined);
|
|
152
|
+
if (!result)
|
|
153
|
+
return;
|
|
154
|
+
const [decimalsHex] = result.constant_result ?? [];
|
|
155
|
+
if (this.isReverted(result) || !decimalsHex)
|
|
156
|
+
return;
|
|
157
|
+
// decimals() is a uint8; anything larger is a contract answering something else
|
|
158
|
+
const decimals = Number(BigInt(`0x${decimalsHex}`));
|
|
159
|
+
return decimals > 255 ? undefined : decimals;
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
console.error(`[tron] failed to read decimals of ${params.contractAddress}`, err);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async getTrc10Precision(params) {
|
|
167
|
+
try {
|
|
168
|
+
const data = await (0, utils_1.timeout)(fetch(`${this.rpcUrl}/v1/assets/${params.id}`, {
|
|
169
|
+
headers: this.tronGridHeaders,
|
|
170
|
+
signal: AbortSignal.timeout(PRECISION_READ_TIMEOUT_MS),
|
|
171
|
+
}).then(response => response.json()), PRECISION_READ_TIMEOUT_MS, undefined);
|
|
172
|
+
return data?.data?.[0]?.precision;
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
console.error(`[tron] failed to read precision of trc10 ${params.id}`, err);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
147
179
|
async getTrc20Balance(params) {
|
|
148
180
|
const result = await this.getTronWeb().transactionBuilder.triggerConstantContract(params.contractAddress, 'balanceOf(address)', {}, [{ type: 'address', value: params.address }], params.address);
|
|
149
181
|
const [balance] = result.constant_result ?? [];
|
|
@@ -378,10 +410,14 @@ class TronApi {
|
|
|
378
410
|
const energy = this.getSimulatedEnergy(result, 'contract call');
|
|
379
411
|
return String((0, exports.getCallerEnergy)(energy, share) * energyPrice);
|
|
380
412
|
}
|
|
381
|
-
// A revert still reports result.result: true
|
|
413
|
+
// A revert still reports result.result: true - the failure is only visible on the transaction's ret
|
|
414
|
+
isReverted(result) {
|
|
415
|
+
const failed = result.transaction?.ret?.some(ret => ret.ret === 'FAILED');
|
|
416
|
+
return result.result?.result !== true || Boolean(failed);
|
|
417
|
+
}
|
|
418
|
+
// Trusting a revert's partial energy would underestimate
|
|
382
419
|
getSimulatedEnergy(result, label) {
|
|
383
|
-
|
|
384
|
-
if (result.result?.result !== true || isReverted || !result.energy_used) {
|
|
420
|
+
if (this.isReverted(result) || !result.energy_used) {
|
|
385
421
|
throw new Error(`[tron] ${label} simulation failed: ${result.result?.message ?? 'unknown error'}`);
|
|
386
422
|
}
|
|
387
423
|
return result.energy_used;
|