@ledgerhq/coin-tron 4.0.0-next.0 → 4.1.0-next.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +26 -0
- package/lib/api/index.d.ts.map +1 -1
- package/lib/api/index.js +6 -1
- package/lib/api/index.js.map +1 -1
- package/lib/api/index.test.js +7 -2
- package/lib/api/index.test.js.map +1 -1
- package/lib/logic/listOperations.d.ts +7 -1
- package/lib/logic/listOperations.d.ts.map +1 -1
- package/lib/logic/listOperations.integ.test.js +56 -4
- package/lib/logic/listOperations.integ.test.js.map +1 -1
- package/lib/logic/listOperations.js +20 -7
- package/lib/logic/listOperations.js.map +1 -1
- package/lib/logic/listOperations.unit.test.js +5 -3
- package/lib/logic/listOperations.unit.test.js.map +1 -1
- package/lib/network/format.js +2 -2
- package/lib/network/format.js.map +1 -1
- package/lib/network/index.d.ts +5 -1
- package/lib/network/index.d.ts.map +1 -1
- package/lib/network/index.js +56 -5
- package/lib/network/index.js.map +1 -1
- package/lib/network/index.test.js +93 -28
- package/lib/network/index.test.js.map +1 -1
- package/lib/network/types.fixture.d.ts +384 -48
- package/lib/network/types.fixture.d.ts.map +1 -1
- package/lib/network/types.fixture.js.map +1 -1
- package/lib-es/api/index.d.ts.map +1 -1
- package/lib-es/api/index.js +6 -1
- package/lib-es/api/index.js.map +1 -1
- package/lib-es/api/index.test.js +7 -2
- package/lib-es/api/index.test.js.map +1 -1
- package/lib-es/logic/listOperations.d.ts +7 -1
- package/lib-es/logic/listOperations.d.ts.map +1 -1
- package/lib-es/logic/listOperations.integ.test.js +57 -5
- package/lib-es/logic/listOperations.integ.test.js.map +1 -1
- package/lib-es/logic/listOperations.js +19 -7
- package/lib-es/logic/listOperations.js.map +1 -1
- package/lib-es/logic/listOperations.unit.test.js +6 -4
- package/lib-es/logic/listOperations.unit.test.js.map +1 -1
- package/lib-es/network/format.js +2 -2
- package/lib-es/network/format.js.map +1 -1
- package/lib-es/network/index.d.ts +5 -1
- package/lib-es/network/index.d.ts.map +1 -1
- package/lib-es/network/index.js +56 -5
- package/lib-es/network/index.js.map +1 -1
- package/lib-es/network/index.test.js +93 -28
- package/lib-es/network/index.test.js.map +1 -1
- package/lib-es/network/types.fixture.d.ts +384 -48
- package/lib-es/network/types.fixture.d.ts.map +1 -1
- package/lib-es/network/types.fixture.js.map +1 -1
- package/package.json +4 -4
- package/src/api/index.test.ts +7 -2
- package/src/api/index.ts +7 -1
- package/src/logic/listOperations.integ.test.ts +74 -5
- package/src/logic/listOperations.ts +31 -7
- package/src/logic/listOperations.unit.test.ts +8 -4
- package/src/network/format.ts +2 -2
- package/src/network/index.test.ts +120 -27
- package/src/network/index.ts +80 -9
- package/src/network/types.fixture.ts +1 -1
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
import { HttpResponse, http } from "msw";
|
|
2
|
-
import { setupServer } from "msw/node";
|
|
2
|
+
import { setupServer, SetupServerApi } from "msw/node";
|
|
3
3
|
import { TRANSACTION_DETAIL_FIXTURE, TRANSACTION_FIXTURE, TRC20_FIXTURE } from "./types.fixture";
|
|
4
4
|
import coinConfig from "../config";
|
|
5
5
|
import { defaultFetchParams, fetchTronAccountTxs } from ".";
|
|
6
|
+
import { assert } from "console";
|
|
6
7
|
|
|
7
8
|
const TRON_BASE_URL_TEST = "https://httpbin.org";
|
|
8
9
|
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
const defaultGetTransactionsH = http.get(
|
|
11
|
+
`${TRON_BASE_URL_TEST}/v1/accounts/:addr/transactions`,
|
|
12
|
+
() => HttpResponse.json(TRANSACTION_FIXTURE),
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const defaultGetTrc20TransactionsH = http.get(
|
|
16
|
+
`${TRON_BASE_URL_TEST}/v1/accounts/:addr/transactions/trc20`,
|
|
17
|
+
() => HttpResponse.json(TRC20_FIXTURE),
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const defaultGetTxInfo = http.get(
|
|
21
|
+
`${TRON_BASE_URL_TEST}/wallet/gettransactioninfobyid`,
|
|
22
|
+
({ request }) => {
|
|
21
23
|
const url = new URL(request.url);
|
|
22
24
|
const value = url.searchParams.get("value") ?? "UNKNOWN";
|
|
23
25
|
return HttpResponse.json(TRANSACTION_DETAIL_FIXTURE(value));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
+
},
|
|
27
|
+
);
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
describe("fetchTronAccountTxs", () => {
|
|
30
|
-
beforeAll(() => {
|
|
29
|
+
function doBeforeAll(server: SetupServerApi): () => void {
|
|
30
|
+
return () => {
|
|
31
31
|
coinConfig.setCoinConfig(() => ({
|
|
32
32
|
status: {
|
|
33
33
|
type: "active",
|
|
@@ -37,16 +37,26 @@ describe("fetchTronAccountTxs", () => {
|
|
|
37
37
|
},
|
|
38
38
|
}));
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
}
|
|
40
|
+
server.listen();
|
|
41
|
+
};
|
|
42
|
+
}
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
function doBeforeEach(server: SetupServerApi): () => void {
|
|
45
|
+
return () => server.resetHandlers();
|
|
46
|
+
}
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
function doAfterAll(server: SetupServerApi): () => void {
|
|
49
|
+
return () => server.close();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
describe("fetchTronAccountTxs", () => {
|
|
53
|
+
const handlers = [defaultGetTransactionsH, defaultGetTrc20TransactionsH, defaultGetTxInfo];
|
|
54
|
+
|
|
55
|
+
const mockServer = setupServer(...handlers);
|
|
56
|
+
|
|
57
|
+
beforeAll(doBeforeAll(mockServer));
|
|
58
|
+
beforeEach(doBeforeEach(mockServer));
|
|
59
|
+
afterAll(doAfterAll(mockServer));
|
|
50
60
|
|
|
51
61
|
it("convert correctly operations from the blockchain", async () => {
|
|
52
62
|
// WHEN
|
|
@@ -64,3 +74,86 @@ describe("fetchTronAccountTxs", () => {
|
|
|
64
74
|
expect(tx!.to).toEqual("TAVrrARNdnjHgCGMQYeQV7hv4PSu7mVsMj");
|
|
65
75
|
});
|
|
66
76
|
});
|
|
77
|
+
|
|
78
|
+
describe("fetchTronAccountTxs with invalid TRC20 (see LIVE-18992)", () => {
|
|
79
|
+
const tx1Hash = "1237889e91c0ebbe389436c341865df09921f8f0c029d9286102372cbaadc585";
|
|
80
|
+
const tx2Hash = "154164dd04482ae78f930033d0ad95730b8b19fde171a33c3920d18c228426ab";
|
|
81
|
+
let counterGetTrc20 = 0;
|
|
82
|
+
const invalidTrc20Handler = http.get(
|
|
83
|
+
`${TRON_BASE_URL_TEST}/v1/accounts/:addr/transactions/trc20`,
|
|
84
|
+
() => {
|
|
85
|
+
const ret: any = JSON.parse(JSON.stringify(TRC20_FIXTURE));
|
|
86
|
+
switch (counterGetTrc20) {
|
|
87
|
+
case 0: {
|
|
88
|
+
const tx1 = ret.data[0];
|
|
89
|
+
assert(tx1.transaction_id == tx1Hash);
|
|
90
|
+
ret.data[0].detail.ret = undefined;
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
case 1: {
|
|
94
|
+
const tx2 = ret.data[1];
|
|
95
|
+
assert(tx2.transaction_id == tx2Hash);
|
|
96
|
+
ret.data[1].detail.ret = undefined;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
default:
|
|
100
|
+
// the 3rd call should not happen
|
|
101
|
+
// because merging the 1st and 2nd results is enough to have a full set, perfectly well formed
|
|
102
|
+
throw "results should be merged after 2 calls";
|
|
103
|
+
}
|
|
104
|
+
counterGetTrc20++;
|
|
105
|
+
return HttpResponse.json(ret);
|
|
106
|
+
},
|
|
107
|
+
);
|
|
108
|
+
const handlers = [defaultGetTransactionsH, invalidTrc20Handler, defaultGetTxInfo];
|
|
109
|
+
const mockServer = setupServer(...handlers);
|
|
110
|
+
|
|
111
|
+
beforeAll(doBeforeAll(mockServer));
|
|
112
|
+
beforeEach(doBeforeEach(mockServer));
|
|
113
|
+
afterAll(doAfterAll(mockServer));
|
|
114
|
+
|
|
115
|
+
it("retry several times until result is correct", async () => {
|
|
116
|
+
// WHEN
|
|
117
|
+
const results = await fetchTronAccountTxs("ADDRESS", () => true, {}, defaultFetchParams);
|
|
118
|
+
|
|
119
|
+
// THEN
|
|
120
|
+
const tx1 = results.find(tx => tx.txID === tx1Hash);
|
|
121
|
+
expect(tx1).toBeDefined();
|
|
122
|
+
const tx2 = results.find(tx => tx.txID === tx2Hash);
|
|
123
|
+
expect(tx2).toBeDefined();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe("fetchTronAccountTxs with invalid TRC20 (see LIVE-18992): after 3 tries it throws an exception", () => {
|
|
128
|
+
const tx1Hash = "1237889e91c0ebbe389436c341865df09921f8f0c029d9286102372cbaadc585";
|
|
129
|
+
const alwaysInvalidTrc20Handler = http.get(
|
|
130
|
+
`${TRON_BASE_URL_TEST}/v1/accounts/:addr/transactions/trc20`,
|
|
131
|
+
() => {
|
|
132
|
+
const ret: any = JSON.parse(JSON.stringify(TRC20_FIXTURE));
|
|
133
|
+
const tx1 = ret.data[0];
|
|
134
|
+
assert(tx1.transaction_id == tx1Hash);
|
|
135
|
+
ret.data[0].detail.ret = undefined;
|
|
136
|
+
return HttpResponse.json(ret);
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const handlers = [defaultGetTransactionsH, alwaysInvalidTrc20Handler, defaultGetTxInfo];
|
|
141
|
+
const mockServer = setupServer(...handlers);
|
|
142
|
+
|
|
143
|
+
beforeAll(doBeforeAll(mockServer));
|
|
144
|
+
beforeEach(doBeforeEach(mockServer));
|
|
145
|
+
afterAll(doAfterAll(mockServer));
|
|
146
|
+
|
|
147
|
+
it("after several retry, it gives up on retry", async () => {
|
|
148
|
+
try {
|
|
149
|
+
await fetchTronAccountTxs("ADDRESS", () => true, {}, defaultFetchParams);
|
|
150
|
+
} catch (e) {
|
|
151
|
+
expect(e).toBeDefined();
|
|
152
|
+
expect((e as Error).message).toBe(
|
|
153
|
+
"getTrc20TxsWithRetry: couldn't fetch trc20 transactions after several attempts",
|
|
154
|
+
);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
fail("should have thrown an error");
|
|
158
|
+
});
|
|
159
|
+
});
|
package/src/network/index.ts
CHANGED
|
@@ -483,13 +483,18 @@ export type FetchTxsStopPredicate = (
|
|
|
483
483
|
) => boolean;
|
|
484
484
|
|
|
485
485
|
export type FetchParams = {
|
|
486
|
-
|
|
486
|
+
/** The maximum number of transactions to fetch per call. */
|
|
487
|
+
limitPerCall: number;
|
|
488
|
+
/** Hint about the number of transactions to be fetched in total (hint to optimize `limitPerCall`) */
|
|
489
|
+
hintGlobalLimit?: number;
|
|
487
490
|
minTimestamp: number;
|
|
491
|
+
order: "asc" | "desc";
|
|
488
492
|
};
|
|
489
493
|
|
|
490
494
|
export const defaultFetchParams: FetchParams = {
|
|
491
|
-
|
|
495
|
+
limitPerCall: 100,
|
|
492
496
|
minTimestamp: 0,
|
|
497
|
+
order: "desc",
|
|
493
498
|
} as const;
|
|
494
499
|
|
|
495
500
|
export async function fetchTronAccountTxs(
|
|
@@ -498,12 +503,15 @@ export async function fetchTronAccountTxs(
|
|
|
498
503
|
cacheTransactionInfoById: Record<string, TronTransactionInfo>,
|
|
499
504
|
params: FetchParams,
|
|
500
505
|
): Promise<TrongridTxInfo[]> {
|
|
501
|
-
const
|
|
506
|
+
const adjustedLimitPerCall = params.hintGlobalLimit
|
|
507
|
+
? Math.min(params.limitPerCall, params.hintGlobalLimit)
|
|
508
|
+
: params.limitPerCall;
|
|
509
|
+
const queryParams = `limit=${adjustedLimitPerCall}&min_timestamp=${params.minTimestamp}&order_by=block_timestamp,${params.order}`;
|
|
502
510
|
const nativeTxs = (
|
|
503
511
|
await getAllTransactions<
|
|
504
512
|
(TransactionTronAPI & { detail?: TronTransactionInfo }) | MalformedTransactionTronAPI
|
|
505
513
|
>(
|
|
506
|
-
`${getBaseApiUrl()}/v1/accounts/${addr}/transactions?${
|
|
514
|
+
`${getBaseApiUrl()}/v1/accounts/${addr}/transactions?${queryParams}`,
|
|
507
515
|
shouldFetchMoreTxs,
|
|
508
516
|
getTransactions(cacheTransactionInfoById),
|
|
509
517
|
)
|
|
@@ -528,14 +536,77 @@ export async function fetchTronAccountTxs(
|
|
|
528
536
|
|
|
529
537
|
// we need to fetch and filter trc20 transactions from another endpoint
|
|
530
538
|
// doc https://developers.tron.network/reference/get-trc20-transaction-info-by-account-address
|
|
531
|
-
|
|
532
|
-
const
|
|
539
|
+
|
|
540
|
+
const callTrc20Endpoint = async () =>
|
|
533
541
|
await getAllTransactions<Trc20API>(
|
|
534
|
-
`${getBaseApiUrl()}/v1/accounts/${addr}/transactions/trc20?${
|
|
542
|
+
`${getBaseApiUrl()}/v1/accounts/${addr}/transactions/trc20?${queryParams}&get_detail=true`,
|
|
535
543
|
shouldFetchMoreTxs,
|
|
536
544
|
getTrc20,
|
|
537
|
-
)
|
|
538
|
-
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
type Acc = {
|
|
548
|
+
txs: Trc20API[];
|
|
549
|
+
invalids: number[];
|
|
550
|
+
};
|
|
551
|
+
|
|
552
|
+
function isValid(tx: Trc20API): boolean {
|
|
553
|
+
const ret = tx?.detail?.ret;
|
|
554
|
+
return Array.isArray(ret) && ret.length > 0;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function getInvalidTxIndexes(txs: Trc20API[]): number[] {
|
|
558
|
+
const invalids: number[] = [];
|
|
559
|
+
for (let i = 0; i < txs.length; i++) {
|
|
560
|
+
if (!isValid(txs[i])) {
|
|
561
|
+
invalids.push(i);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
txs.filter(tx => !isValid(tx)).map((tx, index) => index);
|
|
565
|
+
return invalids;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
function assert(predicate: boolean, message: string) {
|
|
569
|
+
if (!predicate) {
|
|
570
|
+
throw new Error(message);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Merge the two results
|
|
575
|
+
function mergeAccs(acc1: Acc, acc2: Acc): Acc {
|
|
576
|
+
assert(acc1.txs.length == acc2.txs.length, "accs should have the same length");
|
|
577
|
+
const accRet: Acc = { txs: acc1.txs, invalids: [] };
|
|
578
|
+
acc1.invalids.forEach(invalidIndex => {
|
|
579
|
+
acc2.invalids.includes(invalidIndex)
|
|
580
|
+
? accRet.invalids.push(invalidIndex)
|
|
581
|
+
: (accRet.txs[invalidIndex] = acc2.txs[invalidIndex]);
|
|
582
|
+
});
|
|
583
|
+
return accRet;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// see LIVE-18992 for an explanation to why we need this
|
|
587
|
+
async function getTrc20TxsWithRetry(acc: Acc | null, times: number): Promise<Trc20API[]> {
|
|
588
|
+
assert(
|
|
589
|
+
times > 0,
|
|
590
|
+
"getTrc20TxsWithRetry: couldn't fetch trc20 transactions after several attempts",
|
|
591
|
+
);
|
|
592
|
+
const ret = await callTrc20Endpoint();
|
|
593
|
+
const thisAcc: Acc = {
|
|
594
|
+
txs: ret,
|
|
595
|
+
invalids: getInvalidTxIndexes(ret),
|
|
596
|
+
};
|
|
597
|
+
const newAcc = acc ? mergeAccs(acc, thisAcc) : thisAcc;
|
|
598
|
+
if (newAcc.invalids.length == 0) {
|
|
599
|
+
return newAcc.txs;
|
|
600
|
+
} else {
|
|
601
|
+
log(
|
|
602
|
+
"coin-tron",
|
|
603
|
+
`getTrc20TxsWithRetry: got ${newAcc.invalids.length} invalid trc20 transactions, retrying...`,
|
|
604
|
+
);
|
|
605
|
+
return await getTrc20TxsWithRetry(newAcc, times - 1);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
const trc20Txs = (await getTrc20TxsWithRetry(null, 3)).map(formatTrongridTrc20TxResponse);
|
|
539
610
|
|
|
540
611
|
const txInfos: TrongridTxInfo[] = compact(nativeTxs.concat(trc20Txs)).sort(
|
|
541
612
|
(a, b) => b.date.getTime() - a.date.getTime(),
|