@ledgerhq/coin-hedera 1.16.0-nightly.20251211024123 → 1.16.0-nightly.20251213023821
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 +14 -10
- package/lib/api/index.d.ts.map +1 -1
- package/lib/api/index.js +4 -3
- package/lib/api/index.js.map +1 -1
- package/lib/bridge/buildOptimisticOperation.d.ts.map +1 -1
- package/lib/bridge/buildOptimisticOperation.js +14 -43
- package/lib/bridge/buildOptimisticOperation.js.map +1 -1
- package/lib/bridge/index.d.ts.map +1 -1
- package/lib/bridge/index.js +2 -0
- package/lib/bridge/index.js.map +1 -1
- package/lib/bridge/validateAddress.d.ts +3 -0
- package/lib/bridge/validateAddress.d.ts.map +1 -0
- package/lib/bridge/validateAddress.js +10 -0
- package/lib/bridge/validateAddress.js.map +1 -0
- package/lib/constants.d.ts +11 -6
- package/lib/constants.d.ts.map +1 -1
- package/lib/constants.js +20 -1
- package/lib/constants.js.map +1 -1
- package/lib/deviceTransactionConfig.d.ts.map +1 -1
- package/lib/deviceTransactionConfig.js +1 -3
- package/lib/deviceTransactionConfig.js.map +1 -1
- package/lib/logic/getBalance.d.ts.map +1 -1
- package/lib/logic/getBalance.js +21 -4
- package/lib/logic/getBalance.js.map +1 -1
- package/lib/logic/getBlock.d.ts.map +1 -1
- package/lib/logic/getBlock.js +39 -2
- package/lib/logic/getBlock.js.map +1 -1
- package/lib/logic/getValidators.d.ts +3 -0
- package/lib/logic/getValidators.d.ts.map +1 -0
- package/lib/logic/getValidators.js +24 -0
- package/lib/logic/getValidators.js.map +1 -0
- package/lib/logic/index.d.ts +1 -0
- package/lib/logic/index.d.ts.map +1 -1
- package/lib/logic/index.js +3 -1
- package/lib/logic/index.js.map +1 -1
- package/lib/logic/listOperations.d.ts.map +1 -1
- package/lib/logic/listOperations.js +16 -2
- package/lib/logic/listOperations.js.map +1 -1
- package/lib/logic/utils.d.ts +17 -1
- package/lib/logic/utils.d.ts.map +1 -1
- package/lib/logic/utils.js +54 -1
- package/lib/logic/utils.js.map +1 -1
- package/lib/network/api.d.ts +22 -2
- package/lib/network/api.d.ts.map +1 -1
- package/lib/network/api.js +49 -14
- package/lib/network/api.js.map +1 -1
- package/lib/preload.js +2 -2
- package/lib/preload.js.map +1 -1
- package/lib/test/fixtures/account.fixture.d.ts +8 -0
- package/lib/test/fixtures/account.fixture.d.ts.map +1 -1
- package/lib/test/fixtures/account.fixture.js +8 -0
- package/lib/test/fixtures/account.fixture.js.map +1 -1
- package/lib/test/fixtures/mirror.fixture.d.ts +2 -1
- package/lib/test/fixtures/mirror.fixture.d.ts.map +1 -1
- package/lib/test/fixtures/mirror.fixture.js +16 -1
- package/lib/test/fixtures/mirror.fixture.js.map +1 -1
- package/lib/types/bridge.d.ts +1 -0
- package/lib/types/bridge.d.ts.map +1 -1
- package/lib/types/logic.d.ts +6 -0
- package/lib/types/logic.d.ts.map +1 -1
- package/lib-es/api/index.d.ts.map +1 -1
- package/lib-es/api/index.js +5 -4
- package/lib-es/api/index.js.map +1 -1
- package/lib-es/bridge/buildOptimisticOperation.d.ts.map +1 -1
- package/lib-es/bridge/buildOptimisticOperation.js +15 -44
- package/lib-es/bridge/buildOptimisticOperation.js.map +1 -1
- package/lib-es/bridge/index.d.ts.map +1 -1
- package/lib-es/bridge/index.js +2 -0
- package/lib-es/bridge/index.js.map +1 -1
- package/lib-es/bridge/validateAddress.d.ts +3 -0
- package/lib-es/bridge/validateAddress.d.ts.map +1 -0
- package/lib-es/bridge/validateAddress.js +6 -0
- package/lib-es/bridge/validateAddress.js.map +1 -0
- package/lib-es/constants.d.ts +11 -6
- package/lib-es/constants.d.ts.map +1 -1
- package/lib-es/constants.js +19 -0
- package/lib-es/constants.js.map +1 -1
- package/lib-es/deviceTransactionConfig.d.ts.map +1 -1
- package/lib-es/deviceTransactionConfig.js +2 -4
- package/lib-es/deviceTransactionConfig.js.map +1 -1
- package/lib-es/logic/getBalance.d.ts.map +1 -1
- package/lib-es/logic/getBalance.js +21 -4
- package/lib-es/logic/getBalance.js.map +1 -1
- package/lib-es/logic/getBlock.d.ts.map +1 -1
- package/lib-es/logic/getBlock.js +40 -3
- package/lib-es/logic/getBlock.js.map +1 -1
- package/lib-es/logic/getValidators.d.ts +3 -0
- package/lib-es/logic/getValidators.d.ts.map +1 -0
- package/lib-es/logic/getValidators.js +20 -0
- package/lib-es/logic/getValidators.js.map +1 -0
- package/lib-es/logic/index.d.ts +1 -0
- package/lib-es/logic/index.d.ts.map +1 -1
- package/lib-es/logic/index.js +1 -0
- package/lib-es/logic/index.js.map +1 -1
- package/lib-es/logic/listOperations.d.ts.map +1 -1
- package/lib-es/logic/listOperations.js +17 -3
- package/lib-es/logic/listOperations.js.map +1 -1
- package/lib-es/logic/utils.d.ts +17 -1
- package/lib-es/logic/utils.d.ts.map +1 -1
- package/lib-es/logic/utils.js +52 -1
- package/lib-es/logic/utils.js.map +1 -1
- package/lib-es/network/api.d.ts +22 -2
- package/lib-es/network/api.d.ts.map +1 -1
- package/lib-es/network/api.js +49 -14
- package/lib-es/network/api.js.map +1 -1
- package/lib-es/preload.js +2 -2
- package/lib-es/preload.js.map +1 -1
- package/lib-es/test/fixtures/account.fixture.d.ts +8 -0
- package/lib-es/test/fixtures/account.fixture.d.ts.map +1 -1
- package/lib-es/test/fixtures/account.fixture.js +8 -0
- package/lib-es/test/fixtures/account.fixture.js.map +1 -1
- package/lib-es/test/fixtures/mirror.fixture.d.ts +2 -1
- package/lib-es/test/fixtures/mirror.fixture.d.ts.map +1 -1
- package/lib-es/test/fixtures/mirror.fixture.js +14 -0
- package/lib-es/test/fixtures/mirror.fixture.js.map +1 -1
- package/lib-es/types/bridge.d.ts +1 -0
- package/lib-es/types/bridge.d.ts.map +1 -1
- package/lib-es/types/logic.d.ts +6 -0
- package/lib-es/types/logic.d.ts.map +1 -1
- package/package.json +10 -10
- package/src/api/index.integ.test.ts +226 -1
- package/src/api/index.test.ts +5 -2
- package/src/api/index.ts +5 -5
- package/src/bridge/{buildOptimisticOperation.integration.test.ts → buildOptimisticOperation.test.ts} +23 -68
- package/src/bridge/buildOptimisticOperation.ts +16 -45
- package/src/bridge/index.ts +2 -0
- package/src/bridge/validateAddress.test.ts +31 -0
- package/src/bridge/validateAddress.ts +10 -0
- package/src/constants.ts +23 -1
- package/src/deviceTransactionConfig.test.ts +59 -43
- package/src/deviceTransactionConfig.ts +2 -5
- package/src/logic/getBalance.test.ts +50 -0
- package/src/logic/getBalance.ts +21 -4
- package/src/logic/getBlock.test.ts +283 -1
- package/src/logic/getBlock.ts +57 -6
- package/src/logic/getValidators.test.ts +50 -0
- package/src/logic/getValidators.ts +22 -0
- package/src/logic/index.ts +1 -0
- package/src/logic/listOperations.ts +33 -3
- package/src/logic/utils.test.ts +113 -0
- package/src/logic/utils.ts +67 -1
- package/src/network/api.test.ts +55 -9
- package/src/network/api.ts +66 -14
- package/src/preload.ts +2 -2
- package/src/test/fixtures/account.fixture.ts +8 -0
- package/src/test/fixtures/mirror.fixture.ts +18 -0
- package/src/types/bridge.ts +1 -0
- package/src/types/logic.ts +7 -0
- package/tsconfig.json +17 -12
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import BigNumber from "bignumber.js";
|
|
2
|
-
import type { Account } from "@ledgerhq/types-live";
|
|
3
2
|
import { HEDERA_TRANSACTION_MODES } from "./constants";
|
|
4
3
|
import getDeviceTransactionConfig from "./deviceTransactionConfig";
|
|
5
|
-
import type {
|
|
4
|
+
import type { TransactionStatus } from "./types";
|
|
5
|
+
import { getMockedAccount } from "./test/fixtures/account.fixture";
|
|
6
|
+
import { getMockedTransaction } from "./test/fixtures/transaction.fixture";
|
|
6
7
|
|
|
7
8
|
describe("getDeviceTransactionConfig", () => {
|
|
8
|
-
const mockAccount = {
|
|
9
|
-
id: "mock-account-id",
|
|
10
|
-
currency: { id: "hedera" },
|
|
11
|
-
} as Account;
|
|
9
|
+
const mockAccount = getMockedAccount({ id: "mock-account-id" });
|
|
12
10
|
|
|
13
11
|
const createMockStatus = (estimatedFees: BigNumber): TransactionStatus => ({
|
|
14
12
|
errors: {},
|
|
@@ -20,13 +18,11 @@ describe("getDeviceTransactionConfig", () => {
|
|
|
20
18
|
|
|
21
19
|
describe("staking transactions", () => {
|
|
22
20
|
it("should return correct fields for ClaimRewards transaction", async () => {
|
|
23
|
-
const transaction = {
|
|
24
|
-
family: "hedera",
|
|
21
|
+
const transaction = getMockedTransaction({
|
|
25
22
|
mode: HEDERA_TRANSACTION_MODES.ClaimRewards,
|
|
26
23
|
amount: new BigNumber(0),
|
|
27
|
-
recipient: "",
|
|
28
24
|
memo: "Claiming rewards",
|
|
29
|
-
}
|
|
25
|
+
});
|
|
30
26
|
|
|
31
27
|
const status = createMockStatus(new BigNumber(100000));
|
|
32
28
|
|
|
@@ -55,15 +51,48 @@ describe("getDeviceTransactionConfig", () => {
|
|
|
55
51
|
});
|
|
56
52
|
|
|
57
53
|
it("should return correct fields for Delegate transaction", async () => {
|
|
58
|
-
const transaction = {
|
|
59
|
-
family: "hedera",
|
|
54
|
+
const transaction = getMockedTransaction({
|
|
60
55
|
mode: HEDERA_TRANSACTION_MODES.Delegate,
|
|
61
56
|
amount: new BigNumber(0),
|
|
62
|
-
recipient: "",
|
|
63
57
|
properties: {
|
|
64
58
|
stakingNodeId: 10,
|
|
65
59
|
},
|
|
66
|
-
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const status = createMockStatus(new BigNumber(100000));
|
|
63
|
+
|
|
64
|
+
const fields = await getDeviceTransactionConfig({
|
|
65
|
+
account: mockAccount,
|
|
66
|
+
transaction,
|
|
67
|
+
status,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(fields).toEqual([
|
|
71
|
+
{
|
|
72
|
+
type: "text",
|
|
73
|
+
label: "Method",
|
|
74
|
+
value: "Delegate",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
label: "Fees",
|
|
78
|
+
type: "fees",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
type: "text",
|
|
82
|
+
label: "Staked Node ID",
|
|
83
|
+
value: "10",
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should return correct fields for Redelegate transaction", async () => {
|
|
89
|
+
const transaction = getMockedTransaction({
|
|
90
|
+
mode: HEDERA_TRANSACTION_MODES.Redelegate,
|
|
91
|
+
amount: new BigNumber(0),
|
|
92
|
+
properties: {
|
|
93
|
+
stakingNodeId: 10,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
67
96
|
|
|
68
97
|
const status = createMockStatus(new BigNumber(100000));
|
|
69
98
|
|
|
@@ -77,7 +106,7 @@ describe("getDeviceTransactionConfig", () => {
|
|
|
77
106
|
{
|
|
78
107
|
type: "text",
|
|
79
108
|
label: "Method",
|
|
80
|
-
value: "
|
|
109
|
+
value: "Redelegate",
|
|
81
110
|
},
|
|
82
111
|
{
|
|
83
112
|
label: "Fees",
|
|
@@ -92,13 +121,10 @@ describe("getDeviceTransactionConfig", () => {
|
|
|
92
121
|
});
|
|
93
122
|
|
|
94
123
|
it("should not include staking node ID if not provided", async () => {
|
|
95
|
-
const transaction = {
|
|
96
|
-
family: "hedera",
|
|
124
|
+
const transaction = getMockedTransaction({
|
|
97
125
|
mode: HEDERA_TRANSACTION_MODES.Undelegate,
|
|
98
126
|
amount: new BigNumber(0),
|
|
99
|
-
|
|
100
|
-
properties: {},
|
|
101
|
-
} as Transaction;
|
|
127
|
+
});
|
|
102
128
|
|
|
103
129
|
const status = createMockStatus(new BigNumber(100000));
|
|
104
130
|
|
|
@@ -112,7 +138,7 @@ describe("getDeviceTransactionConfig", () => {
|
|
|
112
138
|
{
|
|
113
139
|
type: "text",
|
|
114
140
|
label: "Method",
|
|
115
|
-
value: "
|
|
141
|
+
value: "Undelegate",
|
|
116
142
|
},
|
|
117
143
|
{
|
|
118
144
|
label: "Fees",
|
|
@@ -124,14 +150,11 @@ describe("getDeviceTransactionConfig", () => {
|
|
|
124
150
|
|
|
125
151
|
describe("token associate transactions", () => {
|
|
126
152
|
it("should return correct fields for TokenAssociate transaction", async () => {
|
|
127
|
-
const transaction = {
|
|
128
|
-
family: "hedera",
|
|
153
|
+
const transaction = getMockedTransaction({
|
|
129
154
|
mode: HEDERA_TRANSACTION_MODES.TokenAssociate,
|
|
130
|
-
amount: new BigNumber(0),
|
|
131
|
-
recipient: "",
|
|
132
155
|
subAccountId: "token-account-id",
|
|
133
156
|
memo: "Associating token",
|
|
134
|
-
}
|
|
157
|
+
});
|
|
135
158
|
|
|
136
159
|
const status = createMockStatus(new BigNumber(50000));
|
|
137
160
|
|
|
@@ -160,13 +183,10 @@ describe("getDeviceTransactionConfig", () => {
|
|
|
160
183
|
});
|
|
161
184
|
|
|
162
185
|
it("should not include fees if they are zero", async () => {
|
|
163
|
-
const transaction = {
|
|
164
|
-
family: "hedera",
|
|
186
|
+
const transaction = getMockedTransaction({
|
|
165
187
|
mode: HEDERA_TRANSACTION_MODES.TokenAssociate,
|
|
166
|
-
amount: new BigNumber(0),
|
|
167
|
-
recipient: "",
|
|
168
188
|
subAccountId: "token-account-id",
|
|
169
|
-
}
|
|
189
|
+
});
|
|
170
190
|
|
|
171
191
|
const status = createMockStatus(new BigNumber(0));
|
|
172
192
|
|
|
@@ -188,14 +208,13 @@ describe("getDeviceTransactionConfig", () => {
|
|
|
188
208
|
|
|
189
209
|
describe("regular transfer transactions", () => {
|
|
190
210
|
it("should return correct fields for regular Send transaction", async () => {
|
|
191
|
-
const transaction = {
|
|
192
|
-
family: "hedera",
|
|
211
|
+
const transaction = getMockedTransaction({
|
|
193
212
|
mode: HEDERA_TRANSACTION_MODES.Send,
|
|
194
213
|
amount: new BigNumber(1000000),
|
|
195
214
|
recipient: "0.0.12345",
|
|
196
215
|
useAllAmount: false,
|
|
197
216
|
memo: "Payment",
|
|
198
|
-
}
|
|
217
|
+
});
|
|
199
218
|
|
|
200
219
|
const status = createMockStatus(new BigNumber(100000));
|
|
201
220
|
|
|
@@ -228,13 +247,12 @@ describe("getDeviceTransactionConfig", () => {
|
|
|
228
247
|
});
|
|
229
248
|
|
|
230
249
|
it("should show 'Transfer All' method when useAllAmount is true", async () => {
|
|
231
|
-
const transaction = {
|
|
232
|
-
family: "hedera",
|
|
250
|
+
const transaction = getMockedTransaction({
|
|
233
251
|
mode: HEDERA_TRANSACTION_MODES.Send,
|
|
234
252
|
amount: new BigNumber(0),
|
|
235
253
|
recipient: "0.0.12345",
|
|
236
254
|
useAllAmount: true,
|
|
237
|
-
}
|
|
255
|
+
});
|
|
238
256
|
|
|
239
257
|
const status = createMockStatus(new BigNumber(100000));
|
|
240
258
|
|
|
@@ -252,14 +270,13 @@ describe("getDeviceTransactionConfig", () => {
|
|
|
252
270
|
});
|
|
253
271
|
|
|
254
272
|
it("should include gas limit for Send transactions with gasLimit", async () => {
|
|
255
|
-
const transaction = {
|
|
256
|
-
family: "hedera",
|
|
273
|
+
const transaction = getMockedTransaction({
|
|
257
274
|
mode: HEDERA_TRANSACTION_MODES.Send,
|
|
258
275
|
amount: new BigNumber(1000000),
|
|
259
276
|
recipient: "0.0.12345",
|
|
260
277
|
useAllAmount: false,
|
|
261
278
|
gasLimit: new BigNumber(300000),
|
|
262
|
-
}
|
|
279
|
+
});
|
|
263
280
|
|
|
264
281
|
const status = createMockStatus(new BigNumber(100000));
|
|
265
282
|
|
|
@@ -292,13 +309,12 @@ describe("getDeviceTransactionConfig", () => {
|
|
|
292
309
|
});
|
|
293
310
|
|
|
294
311
|
it("should not include memo if not provided", async () => {
|
|
295
|
-
const transaction = {
|
|
296
|
-
family: "hedera",
|
|
312
|
+
const transaction = getMockedTransaction({
|
|
297
313
|
mode: HEDERA_TRANSACTION_MODES.Send,
|
|
298
314
|
amount: new BigNumber(1000000),
|
|
299
315
|
recipient: "0.0.12345",
|
|
300
316
|
useAllAmount: false,
|
|
301
|
-
}
|
|
317
|
+
});
|
|
302
318
|
|
|
303
319
|
const status = createMockStatus(new BigNumber(100000));
|
|
304
320
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CommonDeviceTransactionField as DeviceTransactionField } from "@ledgerhq/coin-framework/transaction/common";
|
|
2
2
|
import type { AccountLike, Account } from "@ledgerhq/types-live";
|
|
3
|
-
import { HEDERA_TRANSACTION_MODES } from "./constants";
|
|
3
|
+
import { HEDERA_TRANSACTION_MODES, MAP_STAKING_MODE_TO_METHOD } from "./constants";
|
|
4
4
|
import { isTokenAssociateTransaction, isStakingTransaction } from "./logic/utils";
|
|
5
5
|
import type { Transaction, TransactionStatus } from "./types";
|
|
6
6
|
|
|
@@ -19,10 +19,7 @@ async function getDeviceTransactionConfig({
|
|
|
19
19
|
fields.push({
|
|
20
20
|
type: "text",
|
|
21
21
|
label: "Method",
|
|
22
|
-
value:
|
|
23
|
-
transaction.mode === HEDERA_TRANSACTION_MODES.ClaimRewards
|
|
24
|
-
? "Claim Rewards"
|
|
25
|
-
: "Update Account",
|
|
22
|
+
value: MAP_STAKING_MODE_TO_METHOD[transaction.mode],
|
|
26
23
|
});
|
|
27
24
|
|
|
28
25
|
if (!estimatedFees.isZero()) {
|
|
@@ -22,6 +22,7 @@ describe("getBalance", () => {
|
|
|
22
22
|
|
|
23
23
|
(apiClient.getAccount as jest.Mock).mockResolvedValue(mockMirrorAccount);
|
|
24
24
|
(apiClient.getAccountTokens as jest.Mock).mockResolvedValue([]);
|
|
25
|
+
(apiClient.getNodes as jest.Mock).mockResolvedValue({ nodes: [] });
|
|
25
26
|
|
|
26
27
|
const result = await getBalance(mockCurrency, address);
|
|
27
28
|
|
|
@@ -70,6 +71,7 @@ describe("getBalance", () => {
|
|
|
70
71
|
|
|
71
72
|
(apiClient.getAccount as jest.Mock).mockResolvedValue(mockMirrorAccount);
|
|
72
73
|
(apiClient.getAccountTokens as jest.Mock).mockResolvedValue(mockMirrorTokens);
|
|
74
|
+
(apiClient.getNodes as jest.Mock).mockResolvedValue({ nodes: [] });
|
|
73
75
|
|
|
74
76
|
const result = await getBalance(mockCurrency, address);
|
|
75
77
|
|
|
@@ -99,6 +101,51 @@ describe("getBalance", () => {
|
|
|
99
101
|
);
|
|
100
102
|
});
|
|
101
103
|
|
|
104
|
+
it("should return stake", async () => {
|
|
105
|
+
const address = "0.0.12345";
|
|
106
|
+
const mockCurrency = getMockedCurrency();
|
|
107
|
+
const mockMirrorAccount = {
|
|
108
|
+
account: address,
|
|
109
|
+
staked_node_id: 5,
|
|
110
|
+
balance: {
|
|
111
|
+
balance: 100,
|
|
112
|
+
},
|
|
113
|
+
pending_reward: 100,
|
|
114
|
+
};
|
|
115
|
+
const mockMirrorNode = {
|
|
116
|
+
node_id: 5,
|
|
117
|
+
node_account_id: "0.0.5",
|
|
118
|
+
description: "Hosted for Wipro | Amsterdam, Netherlands",
|
|
119
|
+
max_stake: 45000000000000000,
|
|
120
|
+
stake: 45000000000000000,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
(apiClient.getAccount as jest.Mock).mockResolvedValue(mockMirrorAccount);
|
|
124
|
+
(apiClient.getAccountTokens as jest.Mock).mockResolvedValue([]);
|
|
125
|
+
(apiClient.getNodes as jest.Mock).mockResolvedValue({ nodes: [mockMirrorNode] });
|
|
126
|
+
|
|
127
|
+
const result = await getBalance(mockCurrency, address);
|
|
128
|
+
|
|
129
|
+
expect(apiClient.getAccount).toHaveBeenCalledTimes(1);
|
|
130
|
+
expect(apiClient.getAccount).toHaveBeenCalledWith(address);
|
|
131
|
+
expect(apiClient.getNodes).toHaveBeenCalledTimes(1);
|
|
132
|
+
expect(result).toHaveLength(1);
|
|
133
|
+
expect(result[0]).toMatchObject({
|
|
134
|
+
asset: { type: "native" },
|
|
135
|
+
value: BigInt(mockMirrorAccount.balance.balance),
|
|
136
|
+
stake: {
|
|
137
|
+
uid: address,
|
|
138
|
+
address,
|
|
139
|
+
asset: { type: "native" },
|
|
140
|
+
state: "active",
|
|
141
|
+
amount: BigInt(mockMirrorAccount.balance.balance + mockMirrorAccount.pending_reward),
|
|
142
|
+
amountDeposited: BigInt(mockMirrorAccount.balance.balance),
|
|
143
|
+
amountRewarded: BigInt(mockMirrorAccount.pending_reward),
|
|
144
|
+
delegate: mockMirrorNode.node_account_id,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
102
149
|
it("should skip tokens not found in CAL", async () => {
|
|
103
150
|
const address = "0.0.12345";
|
|
104
151
|
const mockCurrency = getMockedCurrency();
|
|
@@ -145,6 +192,7 @@ describe("getBalance", () => {
|
|
|
145
192
|
|
|
146
193
|
(apiClient.getAccount as jest.Mock).mockResolvedValue(mockMirrorAccount);
|
|
147
194
|
(apiClient.getAccountTokens as jest.Mock).mockResolvedValue(mockMirrorTokens);
|
|
195
|
+
(apiClient.getNodes as jest.Mock).mockResolvedValue({ nodes: [] });
|
|
148
196
|
|
|
149
197
|
const result = await getBalance(mockCurrency, address);
|
|
150
198
|
|
|
@@ -173,6 +221,7 @@ describe("getBalance", () => {
|
|
|
173
221
|
|
|
174
222
|
(apiClient.getAccount as jest.Mock).mockRejectedValue(error);
|
|
175
223
|
(apiClient.getAccountTokens as jest.Mock).mockResolvedValue([]);
|
|
224
|
+
(apiClient.getNodes as jest.Mock).mockResolvedValue({ nodes: [] });
|
|
176
225
|
|
|
177
226
|
await expect(getBalance(mockCurrency, address)).rejects.toThrow(error);
|
|
178
227
|
});
|
|
@@ -189,6 +238,7 @@ describe("getBalance", () => {
|
|
|
189
238
|
|
|
190
239
|
(apiClient.getAccount as jest.Mock).mockResolvedValue(mockMirrorAccount);
|
|
191
240
|
(apiClient.getAccountTokens as jest.Mock).mockRejectedValue(error);
|
|
241
|
+
(apiClient.getNodes as jest.Mock).mockResolvedValue({ nodes: [] });
|
|
192
242
|
|
|
193
243
|
await expect(getBalance(mockCurrency, address)).rejects.toThrow(error);
|
|
194
244
|
});
|
package/src/logic/getBalance.ts
CHANGED
|
@@ -4,15 +4,32 @@ import { getCryptoAssetsStore } from "@ledgerhq/cryptoassets/state";
|
|
|
4
4
|
import { apiClient } from "../network/api";
|
|
5
5
|
|
|
6
6
|
export async function getBalance(currency: CryptoCurrency, address: string): Promise<Balance[]> {
|
|
7
|
-
const [mirrorAccount, mirrorTokens] = await Promise.all([
|
|
7
|
+
const [mirrorAccount, mirrorTokens, mirrorNodes] = await Promise.all([
|
|
8
8
|
apiClient.getAccount(address),
|
|
9
9
|
apiClient.getAccountTokens(address),
|
|
10
|
+
apiClient.getNodes({ fetchAllPages: true }),
|
|
10
11
|
]);
|
|
11
12
|
|
|
12
|
-
const
|
|
13
|
+
const validator = mirrorNodes.nodes.find(v => v.node_id === mirrorAccount.staked_node_id);
|
|
14
|
+
const balances: Balance[] = [
|
|
13
15
|
{
|
|
14
16
|
asset: { type: "native" },
|
|
15
17
|
value: BigInt(mirrorAccount.balance.balance),
|
|
18
|
+
...(validator && {
|
|
19
|
+
stake: {
|
|
20
|
+
uid: address,
|
|
21
|
+
address,
|
|
22
|
+
asset: { type: "native" },
|
|
23
|
+
state: "active",
|
|
24
|
+
amount: BigInt(mirrorAccount.balance.balance) + BigInt(mirrorAccount.pending_reward),
|
|
25
|
+
amountDeposited: BigInt(mirrorAccount.balance.balance),
|
|
26
|
+
amountRewarded: BigInt(mirrorAccount.pending_reward),
|
|
27
|
+
delegate: validator.node_account_id,
|
|
28
|
+
details: {
|
|
29
|
+
overstaked: BigInt(validator.stake) >= BigInt(validator.max_stake),
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
16
33
|
},
|
|
17
34
|
];
|
|
18
35
|
|
|
@@ -26,7 +43,7 @@ export async function getBalance(currency: CryptoCurrency, address: string): Pro
|
|
|
26
43
|
continue;
|
|
27
44
|
}
|
|
28
45
|
|
|
29
|
-
|
|
46
|
+
balances.push({
|
|
30
47
|
value: BigInt(mirrorToken.balance),
|
|
31
48
|
asset: {
|
|
32
49
|
type: calToken.tokenType,
|
|
@@ -38,5 +55,5 @@ export async function getBalance(currency: CryptoCurrency, address: string): Pro
|
|
|
38
55
|
});
|
|
39
56
|
}
|
|
40
57
|
|
|
41
|
-
return
|
|
58
|
+
return balances;
|
|
42
59
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { HEDERA_TRANSACTION_NAMES } from "../constants";
|
|
1
2
|
import { getBlock } from "./getBlock";
|
|
2
3
|
import { getBlockInfo } from "./getBlockInfo";
|
|
3
4
|
import { apiClient } from "../network/api";
|
|
4
|
-
import {
|
|
5
|
+
import type { StakingAnalysis } from "../types";
|
|
6
|
+
import { analyzeStakingOperation, getTimestampRangeFromBlockHeight } from "./utils";
|
|
5
7
|
|
|
6
8
|
jest.mock("./getBlockInfo");
|
|
7
9
|
jest.mock("../network/api");
|
|
@@ -23,6 +25,7 @@ describe("getBlock", () => {
|
|
|
23
25
|
jest.clearAllMocks();
|
|
24
26
|
(getBlockInfo as jest.Mock).mockResolvedValue(mockBlockInfo);
|
|
25
27
|
(getTimestampRangeFromBlockHeight as jest.Mock).mockReturnValue(mockTimestampRange);
|
|
28
|
+
(analyzeStakingOperation as jest.Mock).mockResolvedValue(null);
|
|
26
29
|
});
|
|
27
30
|
|
|
28
31
|
it("should return empty block when no transactions exist", async () => {
|
|
@@ -57,6 +60,7 @@ describe("getBlock", () => {
|
|
|
57
60
|
name: "CRYPTOTRANSFER",
|
|
58
61
|
result: "SUCCESS",
|
|
59
62
|
charged_tx_fee: 100000,
|
|
63
|
+
staking_reward_transfers: [],
|
|
60
64
|
transfers: [],
|
|
61
65
|
token_transfers: [],
|
|
62
66
|
};
|
|
@@ -75,6 +79,7 @@ describe("getBlock", () => {
|
|
|
75
79
|
name: "CRYPTOTRANSFER",
|
|
76
80
|
result: "SUCCESS",
|
|
77
81
|
charged_tx_fee: 67179,
|
|
82
|
+
staking_reward_transfers: [],
|
|
78
83
|
transfers: [
|
|
79
84
|
{
|
|
80
85
|
account: "0.0.999",
|
|
@@ -98,4 +103,281 @@ describe("getBlock", () => {
|
|
|
98
103
|
amount: BigInt(-567179 + 67179),
|
|
99
104
|
});
|
|
100
105
|
});
|
|
106
|
+
|
|
107
|
+
it("should handle token transfers", async () => {
|
|
108
|
+
const mockTx = {
|
|
109
|
+
transaction_id: "0.0.999-1234567890-000000000",
|
|
110
|
+
transaction_hash: "hash",
|
|
111
|
+
name: "CRYPTOTRANSFER",
|
|
112
|
+
result: "SUCCESS",
|
|
113
|
+
charged_tx_fee: 100000,
|
|
114
|
+
staking_reward_transfers: [],
|
|
115
|
+
transfers: [],
|
|
116
|
+
token_transfers: [
|
|
117
|
+
{
|
|
118
|
+
token_id: "0.0.12345",
|
|
119
|
+
account: "0.0.999",
|
|
120
|
+
amount: -1000,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
token_id: "0.0.12345",
|
|
124
|
+
account: "0.0.1001",
|
|
125
|
+
amount: 1000,
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
(apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValue([mockTx]);
|
|
131
|
+
|
|
132
|
+
const result = await getBlock(100);
|
|
133
|
+
|
|
134
|
+
expect(result.transactions[0].operations).toEqual([
|
|
135
|
+
{
|
|
136
|
+
type: "transfer",
|
|
137
|
+
address: "0.0.999",
|
|
138
|
+
asset: {
|
|
139
|
+
type: "hts",
|
|
140
|
+
assetReference: "0.0.12345",
|
|
141
|
+
},
|
|
142
|
+
amount: BigInt(-1000),
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
type: "transfer",
|
|
146
|
+
address: "0.0.1001",
|
|
147
|
+
asset: {
|
|
148
|
+
type: "hts",
|
|
149
|
+
assetReference: "0.0.12345",
|
|
150
|
+
},
|
|
151
|
+
amount: BigInt(1000),
|
|
152
|
+
},
|
|
153
|
+
]);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("should mark failed transactions", async () => {
|
|
157
|
+
const mockTx = {
|
|
158
|
+
transaction_id: "0.0.999-1234567890-000000000",
|
|
159
|
+
transaction_hash: "hash",
|
|
160
|
+
name: "CRYPTOTRANSFER",
|
|
161
|
+
result: "INSUFFICIENT_ACCOUNT_BALANCE",
|
|
162
|
+
charged_tx_fee: 100000,
|
|
163
|
+
staking_reward_transfers: [],
|
|
164
|
+
transfers: [],
|
|
165
|
+
token_transfers: [],
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
(apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValue([mockTx]);
|
|
169
|
+
|
|
170
|
+
const result = await getBlock(100);
|
|
171
|
+
|
|
172
|
+
expect(result.transactions[0].failed).toBe(true);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("should analyze CRYPTOUPDATEACCOUNT transactions for staking", async () => {
|
|
176
|
+
const mockTx = {
|
|
177
|
+
transaction_id: "0.0.999-1234567890-000000000",
|
|
178
|
+
transaction_hash: "hash_update",
|
|
179
|
+
name: HEDERA_TRANSACTION_NAMES.UpdateAccount,
|
|
180
|
+
result: "SUCCESS",
|
|
181
|
+
charged_tx_fee: 22000,
|
|
182
|
+
consensus_timestamp: "1704067210.123456789",
|
|
183
|
+
staking_reward_transfers: [],
|
|
184
|
+
transfers: [],
|
|
185
|
+
token_transfers: [],
|
|
186
|
+
};
|
|
187
|
+
const mockStakingAnalysis: StakingAnalysis = {
|
|
188
|
+
operationType: "DELEGATE",
|
|
189
|
+
targetStakingNodeId: 5,
|
|
190
|
+
previousStakingNodeId: null,
|
|
191
|
+
stakedAmount: BigInt(100),
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
(apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValue([mockTx]);
|
|
195
|
+
(analyzeStakingOperation as jest.Mock).mockResolvedValue(mockStakingAnalysis);
|
|
196
|
+
|
|
197
|
+
const result = await getBlock(100);
|
|
198
|
+
|
|
199
|
+
expect(analyzeStakingOperation).toHaveBeenCalledTimes(1);
|
|
200
|
+
expect(analyzeStakingOperation).toHaveBeenCalledWith("0.0.999", mockTx);
|
|
201
|
+
expect(result.transactions[0].operations).toHaveLength(1);
|
|
202
|
+
expect(result.transactions[0].operations[0]).toEqual({
|
|
203
|
+
type: "other",
|
|
204
|
+
operationType: mockStakingAnalysis.operationType,
|
|
205
|
+
stakedNodeId: mockStakingAnalysis.targetStakingNodeId,
|
|
206
|
+
previousStakedNodeId: mockStakingAnalysis.previousStakingNodeId,
|
|
207
|
+
stakedAmount: mockStakingAnalysis.stakedAmount,
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it("should handle UNDELEGATE staking operation", async () => {
|
|
212
|
+
const mockTx = {
|
|
213
|
+
transaction_id: "0.0.999-1234567890-000000000",
|
|
214
|
+
transaction_hash: "hash_undelegate",
|
|
215
|
+
name: HEDERA_TRANSACTION_NAMES.UpdateAccount,
|
|
216
|
+
result: "SUCCESS",
|
|
217
|
+
charged_tx_fee: 22000,
|
|
218
|
+
consensus_timestamp: "1704067210.123456789",
|
|
219
|
+
staking_reward_transfers: [],
|
|
220
|
+
transfers: [],
|
|
221
|
+
token_transfers: [],
|
|
222
|
+
};
|
|
223
|
+
const mockStakingAnalysis: StakingAnalysis = {
|
|
224
|
+
operationType: "UNDELEGATE",
|
|
225
|
+
targetStakingNodeId: null,
|
|
226
|
+
previousStakingNodeId: 3,
|
|
227
|
+
stakedAmount: BigInt(100),
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
(apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValue([mockTx]);
|
|
231
|
+
(analyzeStakingOperation as jest.Mock).mockResolvedValue(mockStakingAnalysis);
|
|
232
|
+
|
|
233
|
+
const result = await getBlock(100);
|
|
234
|
+
|
|
235
|
+
expect(result.transactions[0].operations[0]).toEqual({
|
|
236
|
+
type: "other",
|
|
237
|
+
operationType: mockStakingAnalysis.operationType,
|
|
238
|
+
stakedNodeId: mockStakingAnalysis.targetStakingNodeId,
|
|
239
|
+
previousStakedNodeId: mockStakingAnalysis.previousStakingNodeId,
|
|
240
|
+
stakedAmount: mockStakingAnalysis.stakedAmount,
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it("should handle REDELEGATE staking operation", async () => {
|
|
245
|
+
const mockTx = {
|
|
246
|
+
transaction_id: "0.0.999-1234567890-000000000",
|
|
247
|
+
transaction_hash: "hash_redelegate",
|
|
248
|
+
name: HEDERA_TRANSACTION_NAMES.UpdateAccount,
|
|
249
|
+
result: "SUCCESS",
|
|
250
|
+
charged_tx_fee: 22000,
|
|
251
|
+
consensus_timestamp: "1704067210.123456789",
|
|
252
|
+
staking_reward_transfers: [],
|
|
253
|
+
transfers: [],
|
|
254
|
+
token_transfers: [],
|
|
255
|
+
};
|
|
256
|
+
const mockStakingAnalysis: StakingAnalysis = {
|
|
257
|
+
operationType: "REDELEGATE",
|
|
258
|
+
targetStakingNodeId: 10,
|
|
259
|
+
previousStakingNodeId: 5,
|
|
260
|
+
stakedAmount: BigInt(100),
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
(apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValue([mockTx]);
|
|
264
|
+
(analyzeStakingOperation as jest.Mock).mockResolvedValue(mockStakingAnalysis);
|
|
265
|
+
|
|
266
|
+
const result = await getBlock(100);
|
|
267
|
+
|
|
268
|
+
expect(result.transactions[0].operations).toEqual([
|
|
269
|
+
{
|
|
270
|
+
type: "other",
|
|
271
|
+
operationType: mockStakingAnalysis.operationType,
|
|
272
|
+
stakedNodeId: mockStakingAnalysis.targetStakingNodeId,
|
|
273
|
+
previousStakedNodeId: mockStakingAnalysis.previousStakingNodeId,
|
|
274
|
+
stakedAmount: mockStakingAnalysis.stakedAmount,
|
|
275
|
+
},
|
|
276
|
+
]);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("should create CLAIM_REWARDS operations for staking reward transfers", async () => {
|
|
280
|
+
const mockTx = {
|
|
281
|
+
transaction_id: "0.0.999-1234567890-000000000",
|
|
282
|
+
transaction_hash: "hash",
|
|
283
|
+
name: "CRYPTOTRANSFER",
|
|
284
|
+
result: "SUCCESS",
|
|
285
|
+
charged_tx_fee: 100000,
|
|
286
|
+
staking_reward_transfers: [
|
|
287
|
+
{
|
|
288
|
+
account: "0.0.999",
|
|
289
|
+
amount: 100000,
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
account: "0.0.1001",
|
|
293
|
+
amount: 200000,
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
transfers: [
|
|
297
|
+
{
|
|
298
|
+
account: "0.0.999",
|
|
299
|
+
amount: -600000,
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
account: "0.0.1001",
|
|
303
|
+
amount: 500000,
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
token_transfers: [],
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
(apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValue([mockTx]);
|
|
310
|
+
|
|
311
|
+
const result = await getBlock(100);
|
|
312
|
+
|
|
313
|
+
expect(result.transactions[0].operations).toEqual([
|
|
314
|
+
{
|
|
315
|
+
type: "transfer",
|
|
316
|
+
address: "0.0.999",
|
|
317
|
+
asset: { type: "native" },
|
|
318
|
+
amount: BigInt(-500000),
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
type: "transfer",
|
|
322
|
+
address: "0.0.1001",
|
|
323
|
+
asset: { type: "native" },
|
|
324
|
+
amount: BigInt(500000),
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
type: "transfer",
|
|
328
|
+
address: "0.0.999",
|
|
329
|
+
asset: { type: "native" },
|
|
330
|
+
amount: BigInt(100000),
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
type: "transfer",
|
|
334
|
+
address: "0.0.1001",
|
|
335
|
+
asset: { type: "native" },
|
|
336
|
+
amount: BigInt(200000),
|
|
337
|
+
},
|
|
338
|
+
]);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("should handle CRYPTOUPDATEACCOUNT if it's not related to staking", async () => {
|
|
342
|
+
const mockTx = {
|
|
343
|
+
transaction_id: "0.0.999-1234567890-000000000",
|
|
344
|
+
transaction_hash: "hash_regular_update",
|
|
345
|
+
name: HEDERA_TRANSACTION_NAMES.UpdateAccount,
|
|
346
|
+
result: "SUCCESS",
|
|
347
|
+
charged_tx_fee: 22000,
|
|
348
|
+
consensus_timestamp: "1704067210.123456789",
|
|
349
|
+
staking_reward_transfers: [],
|
|
350
|
+
transfers: [
|
|
351
|
+
{
|
|
352
|
+
account: "0.0.999",
|
|
353
|
+
amount: -23000,
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
account: "0.0.1000",
|
|
357
|
+
amount: 1000,
|
|
358
|
+
},
|
|
359
|
+
],
|
|
360
|
+
token_transfers: [],
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
(apiClient.getTransactionsByTimestampRange as jest.Mock).mockResolvedValue([mockTx]);
|
|
364
|
+
(analyzeStakingOperation as jest.Mock).mockResolvedValue(null);
|
|
365
|
+
|
|
366
|
+
const result = await getBlock(100);
|
|
367
|
+
|
|
368
|
+
expect(result.transactions[0].operations).toEqual([
|
|
369
|
+
{
|
|
370
|
+
type: "transfer",
|
|
371
|
+
address: "0.0.999",
|
|
372
|
+
asset: { type: "native" },
|
|
373
|
+
amount: BigInt(-1000),
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
type: "transfer",
|
|
377
|
+
address: "0.0.1000",
|
|
378
|
+
asset: { type: "native" },
|
|
379
|
+
amount: BigInt(1000),
|
|
380
|
+
},
|
|
381
|
+
]);
|
|
382
|
+
});
|
|
101
383
|
});
|