@ledgerhq/coin-hedera 1.15.0 → 1.16.0-nightly.20251210120335
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/CHANGELOG.md +24 -0
- package/lib/bridge/buildOptimisticOperation.d.ts.map +1 -1
- package/lib/bridge/buildOptimisticOperation.js +33 -0
- package/lib/bridge/buildOptimisticOperation.js.map +1 -1
- package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib/bridge/getTransactionStatus.js +54 -0
- package/lib/bridge/getTransactionStatus.js.map +1 -1
- package/lib/bridge/index.d.ts.map +1 -1
- package/lib/bridge/index.js +4 -2
- package/lib/bridge/index.js.map +1 -1
- package/lib/bridge/prepareTransaction.d.ts.map +1 -1
- package/lib/bridge/prepareTransaction.js +16 -0
- package/lib/bridge/prepareTransaction.js.map +1 -1
- package/lib/bridge/serialization.d.ts.map +1 -1
- package/lib/bridge/serialization.js +20 -0
- package/lib/bridge/serialization.js.map +1 -1
- package/lib/bridge/signOperation.d.ts +4 -4
- package/lib/bridge/signOperation.d.ts.map +1 -1
- package/lib/bridge/signOperation.js +10 -0
- package/lib/bridge/signOperation.js.map +1 -1
- package/lib/bridge/synchronisation.d.ts.map +1 -1
- package/lib/bridge/synchronisation.js +8 -0
- package/lib/bridge/synchronisation.js.map +1 -1
- package/lib/constants.d.ts +21 -1
- package/lib/constants.d.ts.map +1 -1
- package/lib/constants.js +22 -1
- package/lib/constants.js.map +1 -1
- package/lib/deviceTransactionConfig.d.ts.map +1 -1
- package/lib/deviceTransactionConfig.js +30 -0
- package/lib/deviceTransactionConfig.js.map +1 -1
- package/lib/errors.d.ts +9 -0
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +4 -1
- package/lib/errors.js.map +1 -1
- package/lib/logic/craftTransaction.d.ts +2 -2
- package/lib/logic/craftTransaction.d.ts.map +1 -1
- package/lib/logic/craftTransaction.js +42 -8
- package/lib/logic/craftTransaction.js.map +1 -1
- package/lib/logic/getBlock.d.ts.map +1 -1
- package/lib/logic/getBlock.js +1 -0
- package/lib/logic/getBlock.js.map +1 -1
- package/lib/logic/listOperations.d.ts.map +1 -1
- package/lib/logic/listOperations.js +39 -7
- package/lib/logic/listOperations.js.map +1 -1
- package/lib/logic/utils.d.ts +61 -3
- package/lib/logic/utils.d.ts.map +1 -1
- package/lib/logic/utils.js +117 -4
- package/lib/logic/utils.js.map +1 -1
- package/lib/network/api.d.ts +3 -1
- package/lib/network/api.d.ts.map +1 -1
- package/lib/network/api.js +19 -0
- package/lib/network/api.js.map +1 -1
- package/lib/preload-data.d.ts +7 -0
- package/lib/preload-data.d.ts.map +1 -0
- package/lib/preload-data.js +37 -0
- package/lib/preload-data.js.map +1 -0
- package/lib/preload.d.ts +8 -0
- package/lib/preload.d.ts.map +1 -0
- package/lib/preload.js +76 -0
- package/lib/preload.js.map +1 -0
- package/lib/test/fixtures/account.fixture.d.ts +1 -1
- package/lib/test/fixtures/account.fixture.d.ts.map +1 -1
- package/lib/test/fixtures/account.fixture.js +2 -0
- package/lib/test/fixtures/account.fixture.js.map +1 -1
- package/lib/transaction.d.ts.map +1 -1
- package/lib/transaction.js +34 -0
- package/lib/transaction.js.map +1 -1
- package/lib/types/alpaca.d.ts +3 -0
- package/lib/types/alpaca.d.ts.map +1 -1
- package/lib/types/bridge.d.ts +87 -3
- package/lib/types/bridge.d.ts.map +1 -1
- package/lib/types/logic.d.ts +5 -1
- package/lib/types/logic.d.ts.map +1 -1
- package/lib/types/mirror.d.ts +19 -0
- package/lib/types/mirror.d.ts.map +1 -1
- package/lib-es/bridge/buildOptimisticOperation.d.ts.map +1 -1
- package/lib-es/bridge/buildOptimisticOperation.js +34 -1
- package/lib-es/bridge/buildOptimisticOperation.js.map +1 -1
- package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib-es/bridge/getTransactionStatus.js +57 -3
- package/lib-es/bridge/getTransactionStatus.js.map +1 -1
- package/lib-es/bridge/index.d.ts.map +1 -1
- package/lib-es/bridge/index.js +4 -2
- package/lib-es/bridge/index.js.map +1 -1
- package/lib-es/bridge/prepareTransaction.d.ts.map +1 -1
- package/lib-es/bridge/prepareTransaction.js +15 -2
- package/lib-es/bridge/prepareTransaction.js.map +1 -1
- package/lib-es/bridge/serialization.d.ts.map +1 -1
- package/lib-es/bridge/serialization.js +17 -0
- package/lib-es/bridge/serialization.js.map +1 -1
- package/lib-es/bridge/signOperation.d.ts +4 -4
- package/lib-es/bridge/signOperation.d.ts.map +1 -1
- package/lib-es/bridge/signOperation.js +11 -1
- package/lib-es/bridge/signOperation.js.map +1 -1
- package/lib-es/bridge/synchronisation.d.ts.map +1 -1
- package/lib-es/bridge/synchronisation.js +8 -0
- package/lib-es/bridge/synchronisation.js.map +1 -1
- package/lib-es/constants.d.ts +21 -1
- package/lib-es/constants.d.ts.map +1 -1
- package/lib-es/constants.js +21 -0
- package/lib-es/constants.js.map +1 -1
- package/lib-es/deviceTransactionConfig.d.ts.map +1 -1
- package/lib-es/deviceTransactionConfig.js +31 -1
- package/lib-es/deviceTransactionConfig.js.map +1 -1
- package/lib-es/errors.d.ts +9 -0
- package/lib-es/errors.d.ts.map +1 -1
- package/lib-es/errors.js +3 -0
- package/lib-es/errors.js.map +1 -1
- package/lib-es/logic/craftTransaction.d.ts +2 -2
- package/lib-es/logic/craftTransaction.d.ts.map +1 -1
- package/lib-es/logic/craftTransaction.js +44 -10
- package/lib-es/logic/craftTransaction.js.map +1 -1
- package/lib-es/logic/getBlock.d.ts.map +1 -1
- package/lib-es/logic/getBlock.js +2 -1
- package/lib-es/logic/getBlock.js.map +1 -1
- package/lib-es/logic/listOperations.d.ts.map +1 -1
- package/lib-es/logic/listOperations.js +39 -7
- package/lib-es/logic/listOperations.js.map +1 -1
- package/lib-es/logic/utils.d.ts +61 -3
- package/lib-es/logic/utils.d.ts.map +1 -1
- package/lib-es/logic/utils.js +107 -4
- package/lib-es/logic/utils.js.map +1 -1
- package/lib-es/network/api.d.ts +3 -1
- package/lib-es/network/api.d.ts.map +1 -1
- package/lib-es/network/api.js +19 -0
- package/lib-es/network/api.js.map +1 -1
- package/lib-es/preload-data.d.ts +7 -0
- package/lib-es/preload-data.d.ts.map +1 -0
- package/lib-es/preload-data.js +31 -0
- package/lib-es/preload-data.js.map +1 -0
- package/lib-es/preload.d.ts +8 -0
- package/lib-es/preload.d.ts.map +1 -0
- package/lib-es/preload.js +67 -0
- package/lib-es/preload.js.map +1 -0
- package/lib-es/test/fixtures/account.fixture.d.ts +1 -1
- package/lib-es/test/fixtures/account.fixture.d.ts.map +1 -1
- package/lib-es/test/fixtures/account.fixture.js +2 -0
- package/lib-es/test/fixtures/account.fixture.js.map +1 -1
- package/lib-es/transaction.d.ts.map +1 -1
- package/lib-es/transaction.js +34 -0
- package/lib-es/transaction.js.map +1 -1
- package/lib-es/types/alpaca.d.ts +3 -0
- package/lib-es/types/alpaca.d.ts.map +1 -1
- package/lib-es/types/bridge.d.ts +87 -3
- package/lib-es/types/bridge.d.ts.map +1 -1
- package/lib-es/types/logic.d.ts +5 -1
- package/lib-es/types/logic.d.ts.map +1 -1
- package/lib-es/types/mirror.d.ts +19 -0
- package/lib-es/types/mirror.d.ts.map +1 -1
- package/package.json +13 -12
- package/src/api/index.integ.test.ts +11 -1
- package/src/bridge/buildOptimisticOperation.integration.test.ts +159 -4
- package/src/bridge/buildOptimisticOperation.ts +50 -2
- package/src/bridge/getTransactionStatus.test.ts +191 -21
- package/src/bridge/getTransactionStatus.ts +75 -1
- package/src/bridge/index.ts +4 -2
- package/src/bridge/prepareTransaction.test.ts +112 -8
- package/src/bridge/prepareTransaction.ts +20 -2
- package/src/bridge/serialization.ts +17 -0
- package/src/bridge/signOperation.ts +15 -5
- package/src/bridge/synchronisation.ts +9 -0
- package/src/bridge/utils.integration.test.ts +3 -10
- package/src/constants.ts +22 -0
- package/src/deviceTransactionConfig.test.ts +315 -0
- package/src/deviceTransactionConfig.ts +37 -1
- package/src/errors.ts +7 -0
- package/src/logic/craftTransaction.ts +70 -13
- package/src/logic/getBalance.test.ts +15 -16
- package/src/logic/getBlock.ts +2 -1
- package/src/logic/listOperations.test.ts +86 -29
- package/src/logic/listOperations.ts +46 -6
- package/src/logic/utils.test.ts +362 -8
- package/src/logic/utils.ts +158 -4
- package/src/network/api.test.ts +58 -6
- package/src/network/api.ts +25 -0
- package/src/network/thirdweb.test.ts +2 -2
- package/src/network/utils.test.ts +4 -6
- package/src/preload-data.ts +38 -0
- package/src/preload.test.ts +64 -0
- package/src/preload.ts +80 -0
- package/src/test/fixtures/account.fixture.ts +3 -1
- package/src/transaction.ts +42 -0
- package/src/types/alpaca.ts +4 -0
- package/src/types/bridge.ts +108 -3
- package/src/types/logic.ts +6 -1
- package/src/types/mirror.ts +21 -0
package/src/network/api.test.ts
CHANGED
|
@@ -72,7 +72,6 @@ describe("getAccountTransactions", () => {
|
|
|
72
72
|
fetchAllPages: true,
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
expect(result.transactions).toHaveLength(3);
|
|
76
75
|
expect(result.transactions.map(tx => tx.consensus_timestamp)).toEqual(["1", "3", "4"]);
|
|
77
76
|
expect(result.nextCursor).toBeNull();
|
|
78
77
|
expect(mockedNetwork).toHaveBeenCalledTimes(5);
|
|
@@ -118,7 +117,6 @@ describe("getAccountTransactions", () => {
|
|
|
118
117
|
fetchAllPages: false,
|
|
119
118
|
});
|
|
120
119
|
|
|
121
|
-
expect(result.transactions).toHaveLength(2);
|
|
122
120
|
expect(result.transactions.map(tx => tx.consensus_timestamp)).toEqual(["1", "3"]);
|
|
123
121
|
expect(result.nextCursor).toBe("3");
|
|
124
122
|
expect(mockedNetwork).toHaveBeenCalledTimes(3);
|
|
@@ -267,6 +265,7 @@ describe("findTransactionByContractCall", () => {
|
|
|
267
265
|
const mockedResults: HederaMirrorTransaction = {
|
|
268
266
|
transfers: [],
|
|
269
267
|
token_transfers: [],
|
|
268
|
+
staking_reward_transfers: [],
|
|
270
269
|
charged_tx_fee: 100,
|
|
271
270
|
transaction_id: "xxxxxxxxxxxxxx",
|
|
272
271
|
transaction_hash: "xxxxxxxxxxxxx",
|
|
@@ -300,6 +299,7 @@ describe("findTransactionByContractCall", () => {
|
|
|
300
299
|
{
|
|
301
300
|
transfers: [],
|
|
302
301
|
token_transfers: [],
|
|
302
|
+
staking_reward_transfers: [],
|
|
303
303
|
charged_tx_fee: 100,
|
|
304
304
|
transaction_hash: "xxxxxxxxxxxxx",
|
|
305
305
|
consensus_timestamp: "xxxxxxxxxxxxx",
|
|
@@ -310,6 +310,7 @@ describe("findTransactionByContractCall", () => {
|
|
|
310
310
|
{
|
|
311
311
|
transfers: [],
|
|
312
312
|
token_transfers: [],
|
|
313
|
+
staking_reward_transfers: [],
|
|
313
314
|
charged_tx_fee: 100,
|
|
314
315
|
transaction_hash: "xxxxxxxxxxxxx",
|
|
315
316
|
consensus_timestamp: "xxxxxxxxxxxxx",
|
|
@@ -317,7 +318,7 @@ describe("findTransactionByContractCall", () => {
|
|
|
317
318
|
entity_id: "0.0.1111",
|
|
318
319
|
name: "CONTRACTCALL",
|
|
319
320
|
},
|
|
320
|
-
],
|
|
321
|
+
] satisfies Partial<HederaMirrorTransaction>[],
|
|
321
322
|
}),
|
|
322
323
|
);
|
|
323
324
|
|
|
@@ -450,7 +451,6 @@ describe("getTransactionsByTimestampRange", () => {
|
|
|
450
451
|
"2000.000000000",
|
|
451
452
|
);
|
|
452
453
|
|
|
453
|
-
expect(result).toHaveLength(2);
|
|
454
454
|
expect(result.map(tx => tx.consensus_timestamp)).toEqual(["1500.123456789", "1750.987654321"]);
|
|
455
455
|
expect(mockedNetwork).toHaveBeenCalledTimes(1);
|
|
456
456
|
});
|
|
@@ -481,7 +481,6 @@ describe("getTransactionsByTimestampRange", () => {
|
|
|
481
481
|
"2000.000000000",
|
|
482
482
|
);
|
|
483
483
|
|
|
484
|
-
expect(result).toHaveLength(3);
|
|
485
484
|
expect(result.map(tx => tx.consensus_timestamp)).toEqual([
|
|
486
485
|
"1100.000000000",
|
|
487
486
|
"1200.000000000",
|
|
@@ -516,8 +515,61 @@ describe("getTransactionsByTimestampRange", () => {
|
|
|
516
515
|
"2000.000000000",
|
|
517
516
|
);
|
|
518
517
|
|
|
519
|
-
expect(result).toHaveLength(2);
|
|
520
518
|
expect(result.map(tx => tx.consensus_timestamp)).toEqual(["1100.000000000", "1300.000000000"]);
|
|
521
519
|
expect(mockedNetwork).toHaveBeenCalledTimes(3);
|
|
522
520
|
});
|
|
523
521
|
});
|
|
522
|
+
|
|
523
|
+
describe("getNodes", () => {
|
|
524
|
+
beforeEach(() => {
|
|
525
|
+
jest.resetAllMocks();
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it("should return all nodes if only one page is needed", async () => {
|
|
529
|
+
mockedNetwork.mockResolvedValueOnce(
|
|
530
|
+
getMockResponse({
|
|
531
|
+
nodes: [
|
|
532
|
+
{ node_id: 0, node_account_id: "0.0.3" },
|
|
533
|
+
{ node_id: 1, node_account_id: "0.0.4" },
|
|
534
|
+
],
|
|
535
|
+
links: { next: null },
|
|
536
|
+
}),
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
const result = await apiClient.getNodes();
|
|
540
|
+
const requestUrl = mockedNetwork.mock.calls[0][0].url;
|
|
541
|
+
|
|
542
|
+
expect(result.map(n => n.node_id)).toEqual([0, 1]);
|
|
543
|
+
expect(requestUrl).toContain("/api/v1/network/nodes");
|
|
544
|
+
expect(requestUrl).toContain("limit=100");
|
|
545
|
+
expect(requestUrl).toContain("order=desc");
|
|
546
|
+
expect(mockedNetwork).toHaveBeenCalledTimes(1);
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it("should keep fetching if links.next is present and new nodes are returned", async () => {
|
|
550
|
+
mockedNetwork
|
|
551
|
+
.mockResolvedValueOnce(
|
|
552
|
+
getMockResponse({
|
|
553
|
+
nodes: [{ node_id: 0, node_account_id: "0.0.3" }],
|
|
554
|
+
links: { next: "/next-1" },
|
|
555
|
+
}),
|
|
556
|
+
)
|
|
557
|
+
.mockResolvedValueOnce(
|
|
558
|
+
getMockResponse({
|
|
559
|
+
nodes: [{ node_id: 1, node_account_id: "0.0.4" }],
|
|
560
|
+
links: { next: "/next-2" },
|
|
561
|
+
}),
|
|
562
|
+
)
|
|
563
|
+
.mockResolvedValueOnce(
|
|
564
|
+
getMockResponse({
|
|
565
|
+
nodes: [{ node_id: 2, node_account_id: "0.0.5" }],
|
|
566
|
+
links: { next: null },
|
|
567
|
+
}),
|
|
568
|
+
);
|
|
569
|
+
|
|
570
|
+
const result = await apiClient.getNodes();
|
|
571
|
+
|
|
572
|
+
expect(result.map(n => n.node_id)).toEqual([0, 1, 2]);
|
|
573
|
+
expect(mockedNetwork).toHaveBeenCalledTimes(3);
|
|
574
|
+
});
|
|
575
|
+
});
|
package/src/network/api.ts
CHANGED
|
@@ -16,6 +16,8 @@ import type {
|
|
|
16
16
|
HederaMirrorContractCallResult,
|
|
17
17
|
HederaMirrorContractCallBalance,
|
|
18
18
|
HederaMirrorContractCallEstimate,
|
|
19
|
+
HederaMirrorNode,
|
|
20
|
+
HederaMirrorNodesResponse,
|
|
19
21
|
} from "../types";
|
|
20
22
|
|
|
21
23
|
const API_URL = getEnv("API_HEDERA_MIRROR");
|
|
@@ -267,6 +269,28 @@ async function getTransactionsByTimestampRange(
|
|
|
267
269
|
return transactions;
|
|
268
270
|
}
|
|
269
271
|
|
|
272
|
+
async function getNodes(): Promise<HederaMirrorNode[]> {
|
|
273
|
+
const nodes: HederaMirrorNode[] = [];
|
|
274
|
+
const params = new URLSearchParams({
|
|
275
|
+
order: "desc",
|
|
276
|
+
limit: "100",
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
let nextPath: string | null = `/api/v1/network/nodes?${params.toString()}`;
|
|
280
|
+
|
|
281
|
+
while (nextPath) {
|
|
282
|
+
const res: LiveNetworkResponse<HederaMirrorNodesResponse> = await network({
|
|
283
|
+
method: "GET",
|
|
284
|
+
url: `${API_URL}${nextPath}`,
|
|
285
|
+
});
|
|
286
|
+
const newNodes = res.data.nodes;
|
|
287
|
+
nodes.push(...newNodes);
|
|
288
|
+
nextPath = res.data.links.next;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return nodes;
|
|
292
|
+
}
|
|
293
|
+
|
|
270
294
|
export const apiClient = {
|
|
271
295
|
getAccountsForPublicKey,
|
|
272
296
|
getAccount,
|
|
@@ -279,4 +303,5 @@ export const apiClient = {
|
|
|
279
303
|
getERC20Balance,
|
|
280
304
|
estimateContractCallGas,
|
|
281
305
|
getTransactionsByTimestampRange,
|
|
306
|
+
getNodes,
|
|
282
307
|
};
|
|
@@ -84,7 +84,7 @@ describe("fetchERC20Transactions", () => {
|
|
|
84
84
|
};
|
|
85
85
|
const result = await thirdwebClient.fetchERC20Transactions(mockedERC20TokenAddress1, params);
|
|
86
86
|
|
|
87
|
-
expect(result).
|
|
87
|
+
expect(result).toEqual([]);
|
|
88
88
|
expect(mockedNetwork).toHaveBeenCalledTimes(1);
|
|
89
89
|
});
|
|
90
90
|
|
|
@@ -139,7 +139,7 @@ describe("getERC20TransactionsForAccount", () => {
|
|
|
139
139
|
since: null,
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
-
expect(result).
|
|
142
|
+
expect(result).toEqual([]);
|
|
143
143
|
});
|
|
144
144
|
|
|
145
145
|
it("should return exactly 2 transactions (out & in)", async () => {
|
|
@@ -207,8 +207,7 @@ describe("network utils", () => {
|
|
|
207
207
|
erc20Token.contractAddress,
|
|
208
208
|
);
|
|
209
209
|
|
|
210
|
-
expect(res).
|
|
211
|
-
expect(res).toMatchObject(mockedResponse);
|
|
210
|
+
expect(res).toEqual(mockedResponse);
|
|
212
211
|
});
|
|
213
212
|
|
|
214
213
|
it("returns empty array when there are no supported ERC20 tokens", async () => {
|
|
@@ -265,8 +264,7 @@ describe("network utils", () => {
|
|
|
265
264
|
|
|
266
265
|
const result = await getERC20Operations([mockThirdwebTransaction]);
|
|
267
266
|
|
|
268
|
-
expect(result).
|
|
269
|
-
expect(result).toMatchObject([
|
|
267
|
+
expect(result).toEqual([
|
|
270
268
|
{
|
|
271
269
|
thirdwebTransaction: mockThirdwebTransaction,
|
|
272
270
|
mirrorTransaction: mockMirrorTransaction,
|
|
@@ -299,7 +297,7 @@ describe("network utils", () => {
|
|
|
299
297
|
|
|
300
298
|
const result = await getERC20Operations(mockThirdwebTransactions);
|
|
301
299
|
|
|
302
|
-
expect(result).
|
|
300
|
+
expect(result).toEqual([]);
|
|
303
301
|
expect(apiClient.getContractCallResult).not.toHaveBeenCalled();
|
|
304
302
|
expect(apiClient.findTransactionByContractCall).not.toHaveBeenCalled();
|
|
305
303
|
});
|
|
@@ -328,7 +326,7 @@ describe("network utils", () => {
|
|
|
328
326
|
|
|
329
327
|
const result = await getERC20Operations([mockThirdwebTransactions]);
|
|
330
328
|
|
|
331
|
-
expect(result).
|
|
329
|
+
expect(result).toEqual([]);
|
|
332
330
|
});
|
|
333
331
|
});
|
|
334
332
|
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
2
|
+
import { BehaviorSubject, Observable } from "rxjs";
|
|
3
|
+
import type { HederaPreloadData } from "./types";
|
|
4
|
+
|
|
5
|
+
const initialData: HederaPreloadData = {
|
|
6
|
+
validators: [],
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const dataByCurrency = new Map<string, HederaPreloadData>([["hedera", initialData]]);
|
|
10
|
+
|
|
11
|
+
const dataUpdatesByCurrency = new Map([
|
|
12
|
+
["hedera", new BehaviorSubject<HederaPreloadData>(initialData)],
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
export function setHederaPreloadData(data: HederaPreloadData, currency: CryptoCurrency): void {
|
|
16
|
+
dataByCurrency.set(currency.id, data ?? initialData);
|
|
17
|
+
const subject = dataUpdatesByCurrency.get(currency.id);
|
|
18
|
+
if (subject === undefined) {
|
|
19
|
+
throw new Error(`unsupported currency ${currency.id}`);
|
|
20
|
+
}
|
|
21
|
+
subject.next(data);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getHederaPreloadData(currency: CryptoCurrency): Observable<HederaPreloadData> {
|
|
25
|
+
const subject = dataUpdatesByCurrency.get(currency.id);
|
|
26
|
+
if (subject === undefined) {
|
|
27
|
+
throw new Error(`unsupported currency ${currency.id}`);
|
|
28
|
+
}
|
|
29
|
+
return subject.asObservable();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function getCurrentHederaPreloadData(currency: CryptoCurrency): HederaPreloadData {
|
|
33
|
+
const data = dataByCurrency.get(currency.id);
|
|
34
|
+
if (data === undefined) {
|
|
35
|
+
throw new Error(`unsupported currency ${currency.id}`);
|
|
36
|
+
}
|
|
37
|
+
return data;
|
|
38
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets";
|
|
3
|
+
import { hydrate } from "./preload";
|
|
4
|
+
import { setHederaPreloadData } from "./preload-data";
|
|
5
|
+
|
|
6
|
+
jest.mock("./preload-data", () => ({
|
|
7
|
+
setHederaPreloadData: jest.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
describe("hydrate", () => {
|
|
11
|
+
const currency = getCryptoCurrencyById("hedera");
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.clearAllMocks();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
test.each([undefined, null, {}, []])(
|
|
18
|
+
"should hydrate empty validators list if data is corrupted (%p value)",
|
|
19
|
+
value => {
|
|
20
|
+
hydrate(value, currency);
|
|
21
|
+
expect(setHederaPreloadData).toHaveBeenCalledWith({ validators: [] }, currency);
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
it("should hydrate valid validators list", () => {
|
|
26
|
+
hydrate(
|
|
27
|
+
{
|
|
28
|
+
validators: [
|
|
29
|
+
{
|
|
30
|
+
nodeId: 1,
|
|
31
|
+
address: "0.0.1",
|
|
32
|
+
addressChecksum: "abcde",
|
|
33
|
+
name: "Ledger",
|
|
34
|
+
minStake: "1",
|
|
35
|
+
maxStake: "10",
|
|
36
|
+
activeStake: "5",
|
|
37
|
+
activeStakePercentage: "50",
|
|
38
|
+
overstaked: false,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
currency,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(setHederaPreloadData).toHaveBeenCalledWith(
|
|
46
|
+
{
|
|
47
|
+
validators: [
|
|
48
|
+
{
|
|
49
|
+
nodeId: 1,
|
|
50
|
+
address: "0.0.1",
|
|
51
|
+
addressChecksum: "abcde",
|
|
52
|
+
name: "Ledger",
|
|
53
|
+
minStake: new BigNumber(1),
|
|
54
|
+
maxStake: new BigNumber(10),
|
|
55
|
+
activeStake: new BigNumber(5),
|
|
56
|
+
activeStakePercentage: new BigNumber(50),
|
|
57
|
+
overstaked: false,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
currency,
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
});
|
package/src/preload.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
import { log } from "@ledgerhq/logs";
|
|
3
|
+
import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
4
|
+
import { extractCompanyFromNodeDescription, getChecksum, sortValidators } from "./logic/utils";
|
|
5
|
+
import { apiClient } from "./network/api";
|
|
6
|
+
import { setHederaPreloadData } from "./preload-data";
|
|
7
|
+
import type { HederaPreloadData, HederaValidator, HederaValidatorRaw } from "./types";
|
|
8
|
+
|
|
9
|
+
export const getPreloadStrategy = () => ({
|
|
10
|
+
preloadMaxAge: 15 * 60 * 1000, // 15 minutes
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export async function preload(currency: CryptoCurrency): Promise<HederaPreloadData> {
|
|
14
|
+
log("hedera/preload", "preloading hedera data...");
|
|
15
|
+
const nodes = await apiClient.getNodes();
|
|
16
|
+
|
|
17
|
+
const validators: HederaValidator[] = nodes.map(mirrorNode => {
|
|
18
|
+
const minStake = new BigNumber(mirrorNode.min_stake);
|
|
19
|
+
const maxStake = new BigNumber(mirrorNode.max_stake);
|
|
20
|
+
const activeStake = new BigNumber(mirrorNode.stake_rewarded);
|
|
21
|
+
const activeStakePercentage = maxStake.gt(0)
|
|
22
|
+
? activeStake.dividedBy(maxStake).multipliedBy(100).dp(0, BigNumber.ROUND_CEIL)
|
|
23
|
+
: new BigNumber(0);
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
nodeId: mirrorNode.node_id,
|
|
27
|
+
address: mirrorNode.node_account_id,
|
|
28
|
+
addressChecksum: getChecksum(mirrorNode.node_account_id),
|
|
29
|
+
name: extractCompanyFromNodeDescription(mirrorNode.description),
|
|
30
|
+
minStake,
|
|
31
|
+
maxStake,
|
|
32
|
+
activeStake,
|
|
33
|
+
activeStakePercentage,
|
|
34
|
+
overstaked: activeStake.gte(maxStake),
|
|
35
|
+
};
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const sortedValidators = sortValidators(validators);
|
|
39
|
+
const data: HederaPreloadData = {
|
|
40
|
+
validators: sortedValidators,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
setHederaPreloadData(data, currency);
|
|
44
|
+
|
|
45
|
+
return data;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function mapRawValidatorToValidator(validatorRaw: HederaValidatorRaw): HederaValidator {
|
|
49
|
+
return {
|
|
50
|
+
nodeId: validatorRaw.nodeId,
|
|
51
|
+
address: validatorRaw.address,
|
|
52
|
+
addressChecksum: validatorRaw.addressChecksum,
|
|
53
|
+
name: validatorRaw.name,
|
|
54
|
+
minStake: new BigNumber(validatorRaw.minStake),
|
|
55
|
+
maxStake: new BigNumber(validatorRaw.maxStake),
|
|
56
|
+
activeStake: new BigNumber(validatorRaw.activeStake),
|
|
57
|
+
activeStakePercentage: new BigNumber(validatorRaw.activeStakePercentage),
|
|
58
|
+
overstaked: validatorRaw.overstaked,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function fromHydratePreloadData(data: unknown): HederaPreloadData {
|
|
63
|
+
let validators: HederaValidator[] = [];
|
|
64
|
+
|
|
65
|
+
if (data && typeof data === "object" && "validators" in data) {
|
|
66
|
+
if (Array.isArray(data.validators)) {
|
|
67
|
+
validators = data.validators.map(mapRawValidatorToValidator);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
validators,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function hydrate(data: unknown, currency: CryptoCurrency): void {
|
|
77
|
+
const hydrated = fromHydratePreloadData(data);
|
|
78
|
+
log("hedera/preload", `hydrated ${hydrated.validators.length} hedera validators`);
|
|
79
|
+
setHederaPreloadData(hydrated, currency);
|
|
80
|
+
}
|
|
@@ -5,9 +5,9 @@ import type {
|
|
|
5
5
|
HederaResources,
|
|
6
6
|
HederaResourcesRaw,
|
|
7
7
|
} from "../../types";
|
|
8
|
+
import type { TokenCurrency } from "@ledgerhq/types-cryptoassets";
|
|
8
9
|
import type { TokenAccount } from "@ledgerhq/types-live";
|
|
9
10
|
import { getMockedCurrency, getMockedHTSTokenCurrency } from "./currency.fixture";
|
|
10
|
-
import { TokenCurrency } from "@ledgerhq/types-cryptoassets";
|
|
11
11
|
|
|
12
12
|
const defaultMockedCurrency = getMockedCurrency();
|
|
13
13
|
const defaultMockedTokenCurrency = getMockedHTSTokenCurrency();
|
|
@@ -19,11 +19,13 @@ const defaultTokenBalance = new BigNumber(10);
|
|
|
19
19
|
export const mockHederaResources: HederaResources = {
|
|
20
20
|
maxAutomaticTokenAssociations: 0,
|
|
21
21
|
isAutoTokenAssociationEnabled: false,
|
|
22
|
+
delegation: null,
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
export const mockHederaResourcesRaw: HederaResourcesRaw = {
|
|
25
26
|
maxAutomaticTokenAssociations: 0,
|
|
26
27
|
isAutoTokenAssociationEnabled: false,
|
|
28
|
+
delegation: null,
|
|
27
29
|
};
|
|
28
30
|
|
|
29
31
|
/**
|
package/src/transaction.ts
CHANGED
|
@@ -40,6 +40,27 @@ export function fromTransactionRaw(tr: TransactionRaw): Transaction {
|
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
if (
|
|
44
|
+
tr.mode === HEDERA_TRANSACTION_MODES.Delegate ||
|
|
45
|
+
tr.mode === HEDERA_TRANSACTION_MODES.Undelegate ||
|
|
46
|
+
tr.mode === HEDERA_TRANSACTION_MODES.Redelegate
|
|
47
|
+
) {
|
|
48
|
+
return {
|
|
49
|
+
...commonGeneric,
|
|
50
|
+
...commonHedera,
|
|
51
|
+
mode: tr.mode,
|
|
52
|
+
properties: tr.properties,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (tr.mode === HEDERA_TRANSACTION_MODES.ClaimRewards) {
|
|
57
|
+
return {
|
|
58
|
+
...commonGeneric,
|
|
59
|
+
...commonHedera,
|
|
60
|
+
mode: tr.mode,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
43
64
|
return {
|
|
44
65
|
...commonGeneric,
|
|
45
66
|
...commonHedera,
|
|
@@ -67,6 +88,27 @@ export function toTransactionRaw(t: Transaction): TransactionRaw {
|
|
|
67
88
|
};
|
|
68
89
|
}
|
|
69
90
|
|
|
91
|
+
if (
|
|
92
|
+
t.mode === HEDERA_TRANSACTION_MODES.Delegate ||
|
|
93
|
+
t.mode === HEDERA_TRANSACTION_MODES.Undelegate ||
|
|
94
|
+
t.mode === HEDERA_TRANSACTION_MODES.Redelegate
|
|
95
|
+
) {
|
|
96
|
+
return {
|
|
97
|
+
...commonGeneric,
|
|
98
|
+
...commonHedera,
|
|
99
|
+
mode: t.mode,
|
|
100
|
+
properties: t.properties,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (t.mode === HEDERA_TRANSACTION_MODES.ClaimRewards) {
|
|
105
|
+
return {
|
|
106
|
+
...commonGeneric,
|
|
107
|
+
...commonHedera,
|
|
108
|
+
mode: t.mode,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
70
112
|
return {
|
|
71
113
|
...commonGeneric,
|
|
72
114
|
...commonHedera,
|
package/src/types/alpaca.ts
CHANGED
package/src/types/bridge.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
TransactionStatusCommon,
|
|
10
10
|
TransactionStatusCommonRaw,
|
|
11
11
|
} from "@ledgerhq/types-live";
|
|
12
|
-
import { HEDERA_TRANSACTION_MODES } from "../constants";
|
|
12
|
+
import { HEDERA_DELEGATION_STATUS, HEDERA_TRANSACTION_MODES } from "../constants";
|
|
13
13
|
|
|
14
14
|
export type NetworkInfo = {
|
|
15
15
|
family: "hedera";
|
|
@@ -37,6 +37,28 @@ export type Transaction = TransactionCommon & {
|
|
|
37
37
|
token: TokenCurrency;
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
|
+
| {
|
|
41
|
+
mode: HEDERA_TRANSACTION_MODES.Delegate;
|
|
42
|
+
properties: {
|
|
43
|
+
stakingNodeId: number | null;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
| {
|
|
47
|
+
mode: HEDERA_TRANSACTION_MODES.Undelegate;
|
|
48
|
+
properties: {
|
|
49
|
+
stakingNodeId: number | null;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
| {
|
|
53
|
+
mode: HEDERA_TRANSACTION_MODES.Redelegate;
|
|
54
|
+
properties: {
|
|
55
|
+
stakingNodeId: number | null;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
| {
|
|
59
|
+
mode: HEDERA_TRANSACTION_MODES.ClaimRewards;
|
|
60
|
+
properties?: never;
|
|
61
|
+
}
|
|
40
62
|
);
|
|
41
63
|
|
|
42
64
|
export type TransactionRaw = TransactionCommonRaw & {
|
|
@@ -57,25 +79,77 @@ export type TransactionRaw = TransactionCommonRaw & {
|
|
|
57
79
|
token: TokenCurrency;
|
|
58
80
|
};
|
|
59
81
|
}
|
|
82
|
+
| {
|
|
83
|
+
mode: HEDERA_TRANSACTION_MODES.Delegate;
|
|
84
|
+
properties: {
|
|
85
|
+
stakingNodeId: number | null;
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
| {
|
|
89
|
+
mode: HEDERA_TRANSACTION_MODES.Undelegate;
|
|
90
|
+
properties: {
|
|
91
|
+
stakingNodeId: number | null;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
| {
|
|
95
|
+
mode: HEDERA_TRANSACTION_MODES.Redelegate;
|
|
96
|
+
properties: {
|
|
97
|
+
stakingNodeId: number | null;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
| {
|
|
101
|
+
mode: HEDERA_TRANSACTION_MODES.ClaimRewards;
|
|
102
|
+
properties?: never;
|
|
103
|
+
}
|
|
60
104
|
);
|
|
61
105
|
|
|
106
|
+
export type TransactionStatus = TransactionStatusCommon;
|
|
107
|
+
|
|
108
|
+
export type TransactionStatusRaw = TransactionStatusCommonRaw;
|
|
109
|
+
|
|
62
110
|
export type TransactionTokenAssociate = Extract<
|
|
63
111
|
Transaction,
|
|
64
112
|
{ mode: HEDERA_TRANSACTION_MODES.TokenAssociate }
|
|
65
113
|
>;
|
|
66
114
|
|
|
67
|
-
export type
|
|
115
|
+
export type TransactionStaking = Extract<
|
|
116
|
+
Transaction,
|
|
117
|
+
{
|
|
118
|
+
mode:
|
|
119
|
+
| HEDERA_TRANSACTION_MODES.Delegate
|
|
120
|
+
| HEDERA_TRANSACTION_MODES.Undelegate
|
|
121
|
+
| HEDERA_TRANSACTION_MODES.Redelegate
|
|
122
|
+
| HEDERA_TRANSACTION_MODES.ClaimRewards;
|
|
123
|
+
}
|
|
124
|
+
>;
|
|
68
125
|
|
|
69
|
-
export
|
|
126
|
+
export interface HederaDelegation {
|
|
127
|
+
nodeId: number;
|
|
128
|
+
delegated: BigNumber;
|
|
129
|
+
pendingReward: BigNumber;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export interface HederaEnrichedDelegation extends HederaDelegation {
|
|
133
|
+
status: HEDERA_DELEGATION_STATUS;
|
|
134
|
+
validator: HederaValidator;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
interface HederaDelegationRaw {
|
|
138
|
+
nodeId: number;
|
|
139
|
+
delegated: string;
|
|
140
|
+
pendingReward: string;
|
|
141
|
+
}
|
|
70
142
|
|
|
71
143
|
export interface HederaResources {
|
|
72
144
|
maxAutomaticTokenAssociations: number;
|
|
73
145
|
isAutoTokenAssociationEnabled: boolean;
|
|
146
|
+
delegation: HederaDelegation | null;
|
|
74
147
|
}
|
|
75
148
|
|
|
76
149
|
export interface HederaResourcesRaw {
|
|
77
150
|
maxAutomaticTokenAssociations: number;
|
|
78
151
|
isAutoTokenAssociationEnabled: boolean;
|
|
152
|
+
delegation: HederaDelegationRaw | null;
|
|
79
153
|
}
|
|
80
154
|
|
|
81
155
|
export type HederaAccount = Account & {
|
|
@@ -95,6 +169,37 @@ export type HederaOperationExtra = {
|
|
|
95
169
|
gasLimit?: number;
|
|
96
170
|
gasUsed?: number;
|
|
97
171
|
memo?: string | null;
|
|
172
|
+
targetStakingNodeId?: number | null;
|
|
173
|
+
previousStakingNodeId?: number | null;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export type HederaValidator = {
|
|
177
|
+
nodeId: number;
|
|
178
|
+
minStake: BigNumber;
|
|
179
|
+
maxStake: BigNumber;
|
|
180
|
+
activeStake: BigNumber;
|
|
181
|
+
activeStakePercentage: BigNumber;
|
|
182
|
+
address: string;
|
|
183
|
+
addressChecksum: string | null;
|
|
184
|
+
name: string;
|
|
185
|
+
overstaked: boolean;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
export type HederaValidatorRaw = {
|
|
189
|
+
nodeId: number;
|
|
190
|
+
minStake: string;
|
|
191
|
+
maxStake: string;
|
|
192
|
+
activeStake: string;
|
|
193
|
+
activeStakePercentage: string;
|
|
194
|
+
address: string;
|
|
195
|
+
addressChecksum: string | null;
|
|
196
|
+
name: string;
|
|
197
|
+
overstaked: boolean;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export type HederaPreloadData = {
|
|
201
|
+
validators: HederaValidator[];
|
|
202
|
+
associatedTokenId?: string;
|
|
98
203
|
};
|
|
99
204
|
|
|
100
205
|
export type HederaOperation = Operation<HederaOperationExtra>;
|
package/src/types/logic.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type EstimateFeesParams =
|
|
|
13
13
|
operationType: Exclude<HEDERA_OPERATION_TYPES, HEDERA_OPERATION_TYPES.ContractCall>;
|
|
14
14
|
}
|
|
15
15
|
| {
|
|
16
|
-
operationType:
|
|
16
|
+
operationType: HEDERA_OPERATION_TYPES.ContractCall;
|
|
17
17
|
txIntent: TransactionIntent;
|
|
18
18
|
};
|
|
19
19
|
|
|
@@ -42,3 +42,8 @@ export interface ERC20OperationFields {
|
|
|
42
42
|
standard: "erc20";
|
|
43
43
|
hasFailed: false;
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
export interface OperationDetailsExtraField {
|
|
47
|
+
key: string;
|
|
48
|
+
value: string | number;
|
|
49
|
+
}
|