@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/logic/utils.test.ts
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
1
|
import BigNumber from "bignumber.js";
|
|
2
2
|
import { createHash } from "crypto";
|
|
3
|
-
import { Transaction, TransactionId } from "@hashgraph/sdk";
|
|
4
|
-
import type { AssetInfo } from "@ledgerhq/coin-framework/api/types";
|
|
3
|
+
import { Transaction as SDKTransaction, TransactionId } from "@hashgraph/sdk";
|
|
4
|
+
import type { AssetInfo, TransactionIntent } from "@ledgerhq/coin-framework/api/types";
|
|
5
5
|
import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";
|
|
6
6
|
import { InvalidAddress } from "@ledgerhq/errors";
|
|
7
|
-
import {
|
|
7
|
+
import { getEnv, setEnv } from "@ledgerhq/live-env";
|
|
8
|
+
import {
|
|
9
|
+
HEDERA_OPERATION_TYPES,
|
|
10
|
+
HEDERA_TRANSACTION_MODES,
|
|
11
|
+
SYNTHETIC_BLOCK_WINDOW_SECONDS,
|
|
12
|
+
} from "../constants";
|
|
8
13
|
import { HederaRecipientInvalidChecksum } from "../errors";
|
|
9
14
|
import { apiClient } from "../network/api";
|
|
10
15
|
import { rpcClient } from "../network/rpc";
|
|
16
|
+
import * as preloadData from "../preload-data";
|
|
11
17
|
import { getMockedOperation } from "../test/fixtures/operation.fixture";
|
|
12
18
|
import {
|
|
13
19
|
getMockedERC20TokenCurrency,
|
|
14
20
|
getMockedHTSTokenCurrency,
|
|
15
21
|
} from "../test/fixtures/currency.fixture";
|
|
16
22
|
import { getMockedAccount, getMockedTokenAccount } from "../test/fixtures/account.fixture";
|
|
23
|
+
import type {
|
|
24
|
+
HederaAccount,
|
|
25
|
+
HederaMemo,
|
|
26
|
+
HederaPreloadData,
|
|
27
|
+
HederaTxData,
|
|
28
|
+
HederaValidator,
|
|
29
|
+
Transaction,
|
|
30
|
+
} from "../types";
|
|
17
31
|
import {
|
|
18
32
|
serializeSignature,
|
|
19
33
|
deserializeSignature,
|
|
@@ -35,15 +49,36 @@ import {
|
|
|
35
49
|
formatTransactionId,
|
|
36
50
|
getTimestampRangeFromBlockHeight,
|
|
37
51
|
getBlockHash,
|
|
52
|
+
isStakingTransaction,
|
|
53
|
+
extractCompanyFromNodeDescription,
|
|
54
|
+
sortValidators,
|
|
55
|
+
getValidatorFromAccount,
|
|
56
|
+
getDefaultValidator,
|
|
57
|
+
getDelegationStatus,
|
|
58
|
+
filterValidatorBySearchTerm,
|
|
59
|
+
hasSpecificIntentData,
|
|
60
|
+
getChecksum,
|
|
61
|
+
mapIntentToSDKOperation,
|
|
62
|
+
getOperationDetailsExtraFields,
|
|
38
63
|
} from "./utils";
|
|
39
64
|
|
|
40
65
|
jest.mock("../network/api");
|
|
41
66
|
|
|
42
67
|
describe("logic utils", () => {
|
|
68
|
+
let oldStakingLedgerNodeIdEnv: number;
|
|
69
|
+
|
|
43
70
|
beforeEach(() => {
|
|
44
71
|
jest.clearAllMocks();
|
|
45
72
|
});
|
|
46
73
|
|
|
74
|
+
afterEach(() => {
|
|
75
|
+
setEnv("HEDERA_STAKING_LEDGER_NODE_ID", oldStakingLedgerNodeIdEnv);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
beforeAll(() => {
|
|
79
|
+
oldStakingLedgerNodeIdEnv = getEnv("HEDERA_STAKING_LEDGER_NODE_ID");
|
|
80
|
+
});
|
|
81
|
+
|
|
47
82
|
afterAll(() => {
|
|
48
83
|
rpcClient._resetInstance();
|
|
49
84
|
});
|
|
@@ -66,7 +101,7 @@ describe("logic utils", () => {
|
|
|
66
101
|
|
|
67
102
|
describe("transaction serialization", () => {
|
|
68
103
|
beforeEach(() => {
|
|
69
|
-
jest.spyOn(
|
|
104
|
+
jest.spyOn(SDKTransaction, "fromBytes");
|
|
70
105
|
});
|
|
71
106
|
|
|
72
107
|
afterEach(() => {
|
|
@@ -76,7 +111,7 @@ describe("logic utils", () => {
|
|
|
76
111
|
it("should serialize a transaction to hex", () => {
|
|
77
112
|
const mockTransaction = {
|
|
78
113
|
toBytes: jest.fn().mockReturnValue(Buffer.from([10, 20, 30, 40, 50])),
|
|
79
|
-
} as unknown as
|
|
114
|
+
} as unknown as SDKTransaction;
|
|
80
115
|
|
|
81
116
|
const serialized = serializeTransaction(mockTransaction);
|
|
82
117
|
|
|
@@ -86,14 +121,14 @@ describe("logic utils", () => {
|
|
|
86
121
|
|
|
87
122
|
it("should deserialize a hex string to a Transaction", () => {
|
|
88
123
|
const mockTransaction = { id: "mock-transaction-id" };
|
|
89
|
-
(
|
|
124
|
+
(SDKTransaction.fromBytes as jest.Mock).mockReturnValue(mockTransaction);
|
|
90
125
|
|
|
91
126
|
const hexTransaction = "0a141e2832";
|
|
92
127
|
const deserialized = deserializeTransaction(hexTransaction);
|
|
93
128
|
|
|
94
129
|
const hexTransactionBuffer = Buffer.from([10, 20, 30, 40, 50]);
|
|
95
|
-
expect(
|
|
96
|
-
expect(
|
|
130
|
+
expect(SDKTransaction.fromBytes).toHaveBeenCalledTimes(1);
|
|
131
|
+
expect(SDKTransaction.fromBytes).toHaveBeenCalledWith(hexTransactionBuffer);
|
|
97
132
|
expect(deserialized).toBe(mockTransaction);
|
|
98
133
|
});
|
|
99
134
|
});
|
|
@@ -142,6 +177,75 @@ describe("logic utils", () => {
|
|
|
142
177
|
});
|
|
143
178
|
});
|
|
144
179
|
|
|
180
|
+
describe("mapIntentToSDKOperation", () => {
|
|
181
|
+
it("should return TokenAssociate for TokenAssociate intent", () => {
|
|
182
|
+
const txIntent = {
|
|
183
|
+
type: HEDERA_TRANSACTION_MODES.TokenAssociate,
|
|
184
|
+
} as TransactionIntent;
|
|
185
|
+
|
|
186
|
+
expect(mapIntentToSDKOperation(txIntent)).toBe(HEDERA_OPERATION_TYPES.TokenAssociate);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("should return TokenTransfer for Send intent with HTS asset", () => {
|
|
190
|
+
const txIntent = {
|
|
191
|
+
type: HEDERA_TRANSACTION_MODES.Send,
|
|
192
|
+
asset: { type: "hts", assetReference: "0.0.1234" },
|
|
193
|
+
} as TransactionIntent;
|
|
194
|
+
|
|
195
|
+
expect(mapIntentToSDKOperation(txIntent)).toBe(HEDERA_OPERATION_TYPES.TokenTransfer);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("should return ContractCall for Send intent with ERC20 asset", () => {
|
|
199
|
+
const txIntent = {
|
|
200
|
+
type: HEDERA_TRANSACTION_MODES.Send,
|
|
201
|
+
asset: { type: "erc20", assetReference: "0x1234" },
|
|
202
|
+
} as TransactionIntent;
|
|
203
|
+
|
|
204
|
+
expect(mapIntentToSDKOperation(txIntent)).toBe(HEDERA_OPERATION_TYPES.ContractCall);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("should return CryptoUpdate for Delegate intent", () => {
|
|
208
|
+
const txIntent = {
|
|
209
|
+
type: HEDERA_TRANSACTION_MODES.Delegate,
|
|
210
|
+
} as TransactionIntent;
|
|
211
|
+
|
|
212
|
+
expect(mapIntentToSDKOperation(txIntent)).toBe(HEDERA_OPERATION_TYPES.CryptoUpdate);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("should return CryptoUpdate for Undelegate intent", () => {
|
|
216
|
+
const txIntent = {
|
|
217
|
+
type: HEDERA_TRANSACTION_MODES.Undelegate,
|
|
218
|
+
} as TransactionIntent;
|
|
219
|
+
|
|
220
|
+
expect(mapIntentToSDKOperation(txIntent)).toBe(HEDERA_OPERATION_TYPES.CryptoUpdate);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("should return CryptoUpdate for Redelegate intent", () => {
|
|
224
|
+
const txIntent = {
|
|
225
|
+
type: HEDERA_TRANSACTION_MODES.Redelegate,
|
|
226
|
+
} as TransactionIntent;
|
|
227
|
+
|
|
228
|
+
expect(mapIntentToSDKOperation(txIntent)).toBe(HEDERA_OPERATION_TYPES.CryptoUpdate);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("should return CryptoTransfer for Send intent with native asset", () => {
|
|
232
|
+
const txIntent = {
|
|
233
|
+
type: HEDERA_TRANSACTION_MODES.Send,
|
|
234
|
+
asset: { type: "native" },
|
|
235
|
+
} as TransactionIntent;
|
|
236
|
+
|
|
237
|
+
expect(mapIntentToSDKOperation(txIntent)).toBe(HEDERA_OPERATION_TYPES.CryptoTransfer);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it("should return CryptoTransfer for other intent types", () => {
|
|
241
|
+
const txIntent = {
|
|
242
|
+
type: HEDERA_TRANSACTION_MODES.ClaimRewards,
|
|
243
|
+
} as TransactionIntent;
|
|
244
|
+
|
|
245
|
+
expect(mapIntentToSDKOperation(txIntent)).toBe(HEDERA_OPERATION_TYPES.CryptoTransfer);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|
|
145
249
|
describe("getMemoFromBase64", () => {
|
|
146
250
|
it("decodes a simple base64 string", () => {
|
|
147
251
|
expect(getMemoFromBase64("YnJkZw==")).toBe("brdg");
|
|
@@ -247,6 +351,7 @@ describe("logic utils", () => {
|
|
|
247
351
|
hederaResources: {
|
|
248
352
|
maxAutomaticTokenAssociations: -1,
|
|
249
353
|
isAutoTokenAssociationEnabled: true,
|
|
354
|
+
delegation: null,
|
|
250
355
|
},
|
|
251
356
|
});
|
|
252
357
|
|
|
@@ -391,6 +496,22 @@ describe("logic utils", () => {
|
|
|
391
496
|
});
|
|
392
497
|
});
|
|
393
498
|
|
|
499
|
+
describe("getChecksum", () => {
|
|
500
|
+
it("should return correct checksum for valid account ID", () => {
|
|
501
|
+
const accountId = "0.0.9124531-xrxlv";
|
|
502
|
+
const checksum = getChecksum(accountId);
|
|
503
|
+
|
|
504
|
+
expect(checksum).toBe("xrxlv");
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
it("should return null for invalid account ID", () => {
|
|
508
|
+
const accountId = "invalid-account-id";
|
|
509
|
+
const checksum = getChecksum(accountId);
|
|
510
|
+
|
|
511
|
+
expect(checksum).toBeNull();
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
|
|
394
515
|
describe("safeParseAccountId", () => {
|
|
395
516
|
it("returns account id and no checksum for valid address without checksum", () => {
|
|
396
517
|
const [error, result] = safeParseAccountId("0.0.9124531");
|
|
@@ -597,4 +718,237 @@ describe("logic utils", () => {
|
|
|
597
718
|
expect(result.end).toMatch(/^\d+\.000000000$/);
|
|
598
719
|
});
|
|
599
720
|
});
|
|
721
|
+
|
|
722
|
+
describe("isStakingTransaction", () => {
|
|
723
|
+
it("returns correct value based on tx.mode", () => {
|
|
724
|
+
const stakingDelegateTx = { mode: HEDERA_TRANSACTION_MODES.Delegate } as Transaction;
|
|
725
|
+
const stakingUndelegateTx = { mode: HEDERA_TRANSACTION_MODES.Undelegate } as Transaction;
|
|
726
|
+
const stakingRedelegateTx = { mode: HEDERA_TRANSACTION_MODES.Redelegate } as Transaction;
|
|
727
|
+
const stakingClaimRewardsTx = { mode: HEDERA_TRANSACTION_MODES.ClaimRewards } as Transaction;
|
|
728
|
+
const transferTx = { recipient: "", amount: new BigNumber(1) } as Transaction;
|
|
729
|
+
const emptyTx = {} as Transaction;
|
|
730
|
+
|
|
731
|
+
expect(isStakingTransaction(stakingDelegateTx)).toBe(true);
|
|
732
|
+
expect(isStakingTransaction(stakingUndelegateTx)).toBe(true);
|
|
733
|
+
expect(isStakingTransaction(stakingRedelegateTx)).toBe(true);
|
|
734
|
+
expect(isStakingTransaction(stakingClaimRewardsTx)).toBe(true);
|
|
735
|
+
expect(isStakingTransaction(transferTx)).toBe(false);
|
|
736
|
+
expect(isStakingTransaction(emptyTx)).toBe(false);
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
it("returns false for undefined or null transactions", () => {
|
|
740
|
+
expect(isStakingTransaction(undefined as unknown as Transaction)).toBe(false);
|
|
741
|
+
expect(isStakingTransaction(null as unknown as Transaction)).toBe(false);
|
|
742
|
+
});
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
describe("extractCompanyFromNodeDescription", () => {
|
|
746
|
+
it("extracts company name from description", () => {
|
|
747
|
+
expect(extractCompanyFromNodeDescription("Hosted by Ledger | Paris, France")).toBe("Ledger");
|
|
748
|
+
expect(extractCompanyFromNodeDescription("Hosted by LG | Seoul, South Korea")).toBe("LG");
|
|
749
|
+
expect(extractCompanyFromNodeDescription("TestCompany | something else")).toBe("TestCompany");
|
|
750
|
+
expect(extractCompanyFromNodeDescription("NoSeparator ")).toBe("NoSeparator");
|
|
751
|
+
});
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
describe("sortValidators", () => {
|
|
755
|
+
it("sorts validators by active stake DESC, Ledger node first if set", () => {
|
|
756
|
+
setEnv("HEDERA_STAKING_LEDGER_NODE_ID", 2);
|
|
757
|
+
|
|
758
|
+
const validators = [
|
|
759
|
+
{ nodeId: 3, activeStake: new BigNumber(1000) },
|
|
760
|
+
{ nodeId: 2, activeStake: new BigNumber(2000) },
|
|
761
|
+
{ nodeId: 1, activeStake: new BigNumber(3000) },
|
|
762
|
+
] as HederaValidator[];
|
|
763
|
+
|
|
764
|
+
const sorted = sortValidators(validators);
|
|
765
|
+
|
|
766
|
+
expect(sorted[0].nodeId).toBe(2);
|
|
767
|
+
expect(sorted[1].nodeId).toBe(1);
|
|
768
|
+
expect(sorted[2].nodeId).toBe(3);
|
|
769
|
+
});
|
|
770
|
+
});
|
|
771
|
+
|
|
772
|
+
describe("getValidatorFromAccount", () => {
|
|
773
|
+
const mockValidator = { nodeId: 1 };
|
|
774
|
+
const mockPreload = { validators: [mockValidator] } as HederaPreloadData;
|
|
775
|
+
|
|
776
|
+
beforeEach(() => {
|
|
777
|
+
jest.clearAllMocks();
|
|
778
|
+
|
|
779
|
+
jest.spyOn(preloadData, "getCurrentHederaPreloadData").mockReturnValueOnce(mockPreload);
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
it("returns validator matching delegation nodeId", () => {
|
|
783
|
+
const mockAccount = {
|
|
784
|
+
currency: "hedera",
|
|
785
|
+
hederaResources: { delegation: { nodeId: 1 } },
|
|
786
|
+
} as unknown as HederaAccount;
|
|
787
|
+
|
|
788
|
+
expect(getValidatorFromAccount(mockAccount)).toEqual(mockValidator);
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
it("returns null if no delegation", () => {
|
|
792
|
+
const mockAccount = {
|
|
793
|
+
currency: "hedera",
|
|
794
|
+
hederaResources: {},
|
|
795
|
+
} as unknown as HederaAccount;
|
|
796
|
+
|
|
797
|
+
expect(getValidatorFromAccount(mockAccount)).toBeNull();
|
|
798
|
+
});
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
describe("getDefaultValidator", () => {
|
|
802
|
+
const mockValidators = [
|
|
803
|
+
{ nodeId: 1, activeStake: new BigNumber(2000) },
|
|
804
|
+
{ nodeId: 2, activeStake: new BigNumber(1000) },
|
|
805
|
+
{ nodeId: 3, activeStake: new BigNumber(10000) },
|
|
806
|
+
] as HederaValidator[];
|
|
807
|
+
|
|
808
|
+
it("returns Ledger validator if present", () => {
|
|
809
|
+
setEnv("HEDERA_STAKING_LEDGER_NODE_ID", 2);
|
|
810
|
+
expect(getDefaultValidator(mockValidators)?.nodeId).toBe(2);
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
it("returns null if no Ledger validator is present", () => {
|
|
814
|
+
expect(getDefaultValidator([])).toBeNull();
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
describe("getDelegationStatus", () => {
|
|
819
|
+
const mockValidator = { address: "0.0.3", overstaked: false } as HederaValidator;
|
|
820
|
+
const mockOverstakedValidator = { address: "0.0.3", overstaked: true } as HederaValidator;
|
|
821
|
+
|
|
822
|
+
it("returns inactive if validator or validator's address is missing", () => {
|
|
823
|
+
expect(getDelegationStatus(null)).toBe("inactive");
|
|
824
|
+
expect(getDelegationStatus({ address: "" } as any)).toBe("inactive");
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
it("returns overstaked if validator.overstaked is true", () => {
|
|
828
|
+
expect(getDelegationStatus(mockOverstakedValidator)).toBe("overstaked");
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
it("returns active otherwise", () => {
|
|
832
|
+
expect(getDelegationStatus(mockValidator)).toBe("active");
|
|
833
|
+
});
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
describe("filterValidatorBySearchTerm", () => {
|
|
837
|
+
const mockValidator: HederaValidator = {
|
|
838
|
+
nodeId: 123,
|
|
839
|
+
name: "Validator Test",
|
|
840
|
+
address: "0.0.456",
|
|
841
|
+
addressChecksum: "abcde",
|
|
842
|
+
minStake: new BigNumber(0),
|
|
843
|
+
maxStake: new BigNumber(0),
|
|
844
|
+
activeStake: new BigNumber(0),
|
|
845
|
+
activeStakePercentage: new BigNumber(0),
|
|
846
|
+
overstaked: false,
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
it("should match by nodeId", () => {
|
|
850
|
+
expect(filterValidatorBySearchTerm(mockValidator, "123")).toBe(true);
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
it("should match by name with case insensitivity", () => {
|
|
854
|
+
expect(filterValidatorBySearchTerm(mockValidator, "validator")).toBe(true);
|
|
855
|
+
expect(filterValidatorBySearchTerm(mockValidator, "VALIDATOR")).toBe(true);
|
|
856
|
+
expect(filterValidatorBySearchTerm(mockValidator, "test")).toBe(true);
|
|
857
|
+
expect(filterValidatorBySearchTerm(mockValidator, "unknown")).toBe(false);
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
it("should match by address", () => {
|
|
861
|
+
expect(filterValidatorBySearchTerm(mockValidator, "0.0.456")).toBe(true);
|
|
862
|
+
expect(filterValidatorBySearchTerm(mockValidator, "456")).toBe(true);
|
|
863
|
+
expect(filterValidatorBySearchTerm(mockValidator, "789")).toBe(false);
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
it("should match by address with checksum", () => {
|
|
867
|
+
expect(filterValidatorBySearchTerm(mockValidator, "0.0.456-abcde")).toBe(true);
|
|
868
|
+
expect(filterValidatorBySearchTerm(mockValidator, "abcde")).toBe(true);
|
|
869
|
+
expect(filterValidatorBySearchTerm(mockValidator, "ABC")).toBe(true);
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
it("should handle validator without checksum", () => {
|
|
873
|
+
const validatorWithoutChecksum = { ...mockValidator, addressChecksum: null };
|
|
874
|
+
expect(filterValidatorBySearchTerm(validatorWithoutChecksum, "0.0.456")).toBe(true);
|
|
875
|
+
expect(filterValidatorBySearchTerm(validatorWithoutChecksum, "abcde")).toBe(false);
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
it("should handle empty search term", () => {
|
|
879
|
+
expect(filterValidatorBySearchTerm(mockValidator, "")).toBe(true);
|
|
880
|
+
});
|
|
881
|
+
|
|
882
|
+
it("should handle partial matches", () => {
|
|
883
|
+
expect(filterValidatorBySearchTerm(mockValidator, "valid")).toBe(true);
|
|
884
|
+
expect(filterValidatorBySearchTerm(mockValidator, "0.0")).toBe(true);
|
|
885
|
+
expect(filterValidatorBySearchTerm(mockValidator, "12")).toBe(true);
|
|
886
|
+
});
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
describe("hasSpecificIntentData", () => {
|
|
890
|
+
it("should return true when txIntent has data matching expected type", () => {
|
|
891
|
+
const stakingTxIntent = {
|
|
892
|
+
data: { type: "staking" as const },
|
|
893
|
+
} as TransactionIntent<HederaMemo, HederaTxData>;
|
|
894
|
+
const erc20TxIntent = {
|
|
895
|
+
data: { type: "erc20" as const },
|
|
896
|
+
} as TransactionIntent<HederaMemo, HederaTxData>;
|
|
897
|
+
|
|
898
|
+
expect(hasSpecificIntentData(stakingTxIntent, "staking")).toBe(true);
|
|
899
|
+
expect(hasSpecificIntentData(erc20TxIntent, "erc20")).toBe(true);
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
it("should return false when txIntent has invalid data", () => {
|
|
903
|
+
const txIntentNoData = {} as TransactionIntent<HederaMemo, HederaTxData>;
|
|
904
|
+
const txIntentUnknown = {
|
|
905
|
+
data: { type: "unknown" as const },
|
|
906
|
+
} as unknown as TransactionIntent<HederaMemo, HederaTxData>;
|
|
907
|
+
|
|
908
|
+
expect(hasSpecificIntentData(txIntentUnknown, "erc20")).toBe(false);
|
|
909
|
+
expect(hasSpecificIntentData(txIntentNoData, "erc20")).toBe(false);
|
|
910
|
+
});
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
describe("getOperationDetailsExtraFields", () => {
|
|
914
|
+
it("should return empty array when no fields are present", () => {
|
|
915
|
+
const result = getOperationDetailsExtraFields({});
|
|
916
|
+
|
|
917
|
+
expect(result).toEqual([]);
|
|
918
|
+
});
|
|
919
|
+
|
|
920
|
+
it("should handle zero values correctly", () => {
|
|
921
|
+
const result = getOperationDetailsExtraFields({
|
|
922
|
+
gasConsumed: 0,
|
|
923
|
+
targetStakingNodeId: 0,
|
|
924
|
+
});
|
|
925
|
+
|
|
926
|
+
expect(result).toEqual([
|
|
927
|
+
{ key: "targetStakingNodeId", value: "0" },
|
|
928
|
+
{ key: "gasConsumed", value: "0" },
|
|
929
|
+
]);
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
it("should return all fields when all are present", () => {
|
|
933
|
+
const result = getOperationDetailsExtraFields({
|
|
934
|
+
memo: "complete",
|
|
935
|
+
associatedTokenId: "123",
|
|
936
|
+
targetStakingNodeId: 5,
|
|
937
|
+
previousStakingNodeId: 3,
|
|
938
|
+
gasConsumed: 1000,
|
|
939
|
+
gasUsed: 950,
|
|
940
|
+
gasLimit: 2000,
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
expect(result).toEqual([
|
|
944
|
+
{ key: "memo", value: "complete" },
|
|
945
|
+
{ key: "associatedTokenId", value: "123" },
|
|
946
|
+
{ key: "targetStakingNodeId", value: "5" },
|
|
947
|
+
{ key: "previousStakingNodeId", value: "3" },
|
|
948
|
+
{ key: "gasConsumed", value: "1000" },
|
|
949
|
+
{ key: "gasUsed", value: "950" },
|
|
950
|
+
{ key: "gasLimit", value: "2000" },
|
|
951
|
+
]);
|
|
952
|
+
});
|
|
953
|
+
});
|
|
600
954
|
});
|
package/src/logic/utils.ts
CHANGED
|
@@ -12,10 +12,12 @@ import { findCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";
|
|
|
12
12
|
import { getFiatCurrencyByTicker } from "@ledgerhq/cryptoassets/fiats";
|
|
13
13
|
import cvsApi from "@ledgerhq/live-countervalues/api/index";
|
|
14
14
|
import { InvalidAddress } from "@ledgerhq/errors";
|
|
15
|
+
import { getEnv } from "@ledgerhq/live-env";
|
|
15
16
|
import { makeLRUCache, seconds } from "@ledgerhq/live-network/cache";
|
|
16
17
|
import type { Currency, ExplorerView, TokenCurrency } from "@ledgerhq/types-cryptoassets";
|
|
17
18
|
import type { AccountLike, Operation as LiveOperation } from "@ledgerhq/types-live";
|
|
18
19
|
import {
|
|
20
|
+
HEDERA_DELEGATION_STATUS,
|
|
19
21
|
HEDERA_OPERATION_TYPES,
|
|
20
22
|
HEDERA_TRANSACTION_MODES,
|
|
21
23
|
SYNTHETIC_BLOCK_WINDOW_SECONDS,
|
|
@@ -23,13 +25,19 @@ import {
|
|
|
23
25
|
import { apiClient } from "../network/api";
|
|
24
26
|
import type {
|
|
25
27
|
HederaAccount,
|
|
28
|
+
HederaMemo,
|
|
26
29
|
HederaOperationExtra,
|
|
30
|
+
HederaTxData,
|
|
31
|
+
HederaValidator,
|
|
32
|
+
OperationDetailsExtraField,
|
|
27
33
|
Transaction,
|
|
34
|
+
TransactionStaking,
|
|
28
35
|
TransactionStatus,
|
|
29
36
|
TransactionTokenAssociate,
|
|
30
37
|
} from "../types";
|
|
31
38
|
import { rpcClient } from "../network/rpc";
|
|
32
39
|
import { HederaRecipientInvalidChecksum } from "../errors";
|
|
40
|
+
import { getCurrentHederaPreloadData } from "../preload-data";
|
|
33
41
|
|
|
34
42
|
export const serializeSignature = (signature: Uint8Array) => {
|
|
35
43
|
return Buffer.from(signature).toString("base64");
|
|
@@ -88,6 +96,14 @@ export const mapIntentToSDKOperation = (txIntent: TransactionIntent) => {
|
|
|
88
96
|
return HEDERA_OPERATION_TYPES.ContractCall;
|
|
89
97
|
}
|
|
90
98
|
|
|
99
|
+
if (
|
|
100
|
+
txIntent.type === HEDERA_TRANSACTION_MODES.Delegate ||
|
|
101
|
+
txIntent.type === HEDERA_TRANSACTION_MODES.Undelegate ||
|
|
102
|
+
txIntent.type === HEDERA_TRANSACTION_MODES.Redelegate
|
|
103
|
+
) {
|
|
104
|
+
return HEDERA_OPERATION_TYPES.CryptoUpdate;
|
|
105
|
+
}
|
|
106
|
+
|
|
91
107
|
return HEDERA_OPERATION_TYPES.CryptoTransfer;
|
|
92
108
|
};
|
|
93
109
|
|
|
@@ -122,8 +138,10 @@ export const getTransactionExplorer = (
|
|
|
122
138
|
);
|
|
123
139
|
};
|
|
124
140
|
|
|
125
|
-
export const isTokenAssociateTransaction = (
|
|
126
|
-
|
|
141
|
+
export const isTokenAssociateTransaction = (
|
|
142
|
+
tx: Transaction | null | undefined,
|
|
143
|
+
): tx is TransactionTokenAssociate => {
|
|
144
|
+
return tx?.mode === HEDERA_TRANSACTION_MODES.TokenAssociate;
|
|
127
145
|
};
|
|
128
146
|
|
|
129
147
|
export const isAutoTokenAssociationEnabled = (account: AccountLike) => {
|
|
@@ -150,6 +168,42 @@ export const isValidExtra = (extra: unknown): extra is HederaOperationExtra => {
|
|
|
150
168
|
return !!extra && typeof extra === "object" && !Array.isArray(extra);
|
|
151
169
|
};
|
|
152
170
|
|
|
171
|
+
export const getOperationDetailsExtraFields = (
|
|
172
|
+
extra: HederaOperationExtra,
|
|
173
|
+
): OperationDetailsExtraField[] => {
|
|
174
|
+
const fields: OperationDetailsExtraField[] = [];
|
|
175
|
+
|
|
176
|
+
if (typeof extra.memo === "string") {
|
|
177
|
+
fields.push({ key: "memo", value: extra.memo });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (typeof extra.associatedTokenId === "string") {
|
|
181
|
+
fields.push({ key: "associatedTokenId", value: extra.associatedTokenId });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (typeof extra.targetStakingNodeId === "number") {
|
|
185
|
+
fields.push({ key: "targetStakingNodeId", value: extra.targetStakingNodeId.toString() });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (typeof extra.previousStakingNodeId === "number") {
|
|
189
|
+
fields.push({ key: "previousStakingNodeId", value: extra.previousStakingNodeId.toString() });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (typeof extra.gasConsumed === "number") {
|
|
193
|
+
fields.push({ key: "gasConsumed", value: extra.gasConsumed.toString() });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (typeof extra.gasUsed === "number") {
|
|
197
|
+
fields.push({ key: "gasUsed", value: extra.gasUsed.toString() });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (typeof extra.gasLimit === "number") {
|
|
201
|
+
fields.push({ key: "gasLimit", value: extra.gasLimit.toString() });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return fields;
|
|
205
|
+
};
|
|
206
|
+
|
|
153
207
|
// disables the "Continue" button in the Send modal's Recipient step during token transfers if:
|
|
154
208
|
// - the recipient is not associated with the token
|
|
155
209
|
// - the association status can't be verified
|
|
@@ -212,6 +266,15 @@ export const checkAccountTokenAssociationStatus = makeLRUCache(
|
|
|
212
266
|
seconds(30),
|
|
213
267
|
);
|
|
214
268
|
|
|
269
|
+
export const getChecksum = (accountId: string): string | null => {
|
|
270
|
+
try {
|
|
271
|
+
const entityId = EntityIdHelper.fromString(accountId);
|
|
272
|
+
return entityId.checksum ?? null;
|
|
273
|
+
} catch {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
215
278
|
export const safeParseAccountId = (
|
|
216
279
|
address: string,
|
|
217
280
|
): [Error, null] | [null, { accountId: string; checksum: string | null }] => {
|
|
@@ -220,7 +283,7 @@ export const safeParseAccountId = (
|
|
|
220
283
|
|
|
221
284
|
try {
|
|
222
285
|
const accountId = AccountId.fromString(address);
|
|
223
|
-
const checksum =
|
|
286
|
+
const checksum = getChecksum(address);
|
|
224
287
|
|
|
225
288
|
if (checksum) {
|
|
226
289
|
const client = rpcClient.getInstance();
|
|
@@ -237,7 +300,7 @@ export const safeParseAccountId = (
|
|
|
237
300
|
};
|
|
238
301
|
|
|
239
302
|
return [null, result];
|
|
240
|
-
} catch
|
|
303
|
+
} catch {
|
|
241
304
|
return [new InvalidAddress("", { currencyName }), null];
|
|
242
305
|
}
|
|
243
306
|
};
|
|
@@ -339,3 +402,94 @@ export const fromEVMAddress = (evmAddress: string, shard = 0, realm = 0): string
|
|
|
339
402
|
return null;
|
|
340
403
|
}
|
|
341
404
|
};
|
|
405
|
+
|
|
406
|
+
export const extractCompanyFromNodeDescription = (description: string): string => {
|
|
407
|
+
return description
|
|
408
|
+
.split("|")[0]
|
|
409
|
+
.replace(/hosted by/i, "")
|
|
410
|
+
.replace(/hosted for/i, "")
|
|
411
|
+
.trim();
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
export const sortValidators = (validators: HederaValidator[]): HederaValidator[] => {
|
|
415
|
+
const ledgerNodeId = getEnv("HEDERA_STAKING_LEDGER_NODE_ID");
|
|
416
|
+
|
|
417
|
+
// sort validators by active stake in DESC order, with Ledger node first if it exists
|
|
418
|
+
return [...validators].sort((a, b) => {
|
|
419
|
+
if (typeof ledgerNodeId === "number") {
|
|
420
|
+
if (a.nodeId === ledgerNodeId) return -1;
|
|
421
|
+
if (b.nodeId === ledgerNodeId) return 1;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return b.activeStake.toNumber() - a.activeStake.toNumber();
|
|
425
|
+
});
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
export const filterValidatorBySearchTerm = (
|
|
429
|
+
validator: HederaValidator,
|
|
430
|
+
search: string,
|
|
431
|
+
): boolean => {
|
|
432
|
+
const lowercaseSearch = search.toLowerCase();
|
|
433
|
+
const addressWithChecksum = validator.addressChecksum
|
|
434
|
+
? `${validator.address}-${validator.addressChecksum}`
|
|
435
|
+
: validator.address;
|
|
436
|
+
|
|
437
|
+
return (
|
|
438
|
+
validator.nodeId.toString().includes(lowercaseSearch) ||
|
|
439
|
+
validator.name.toLowerCase().includes(lowercaseSearch) ||
|
|
440
|
+
addressWithChecksum.toLowerCase().includes(lowercaseSearch)
|
|
441
|
+
);
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
export const getValidatorFromAccount = (account: HederaAccount): HederaValidator | null => {
|
|
445
|
+
const { delegation } = account.hederaResources ?? {};
|
|
446
|
+
|
|
447
|
+
if (!delegation) {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const validators = getCurrentHederaPreloadData(account.currency);
|
|
452
|
+
const validator = validators.validators.find(v => v.nodeId === delegation.nodeId) ?? null;
|
|
453
|
+
|
|
454
|
+
return validator;
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
export const getDefaultValidator = (validators: HederaValidator[]): HederaValidator | null => {
|
|
458
|
+
const ledgerNodeId = getEnv("HEDERA_STAKING_LEDGER_NODE_ID");
|
|
459
|
+
|
|
460
|
+
return validators.find(v => v.nodeId === ledgerNodeId) ?? null;
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
export const getDelegationStatus = (
|
|
464
|
+
validator: HederaValidator | null,
|
|
465
|
+
): HEDERA_DELEGATION_STATUS => {
|
|
466
|
+
if (!validator?.address) {
|
|
467
|
+
return HEDERA_DELEGATION_STATUS.Inactive;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (validator.overstaked) {
|
|
471
|
+
return HEDERA_DELEGATION_STATUS.Overstaked;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return HEDERA_DELEGATION_STATUS.Active;
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
export const isStakingTransaction = (
|
|
478
|
+
tx: Transaction | null | undefined,
|
|
479
|
+
): tx is TransactionStaking => {
|
|
480
|
+
if (!tx) return false;
|
|
481
|
+
|
|
482
|
+
return (
|
|
483
|
+
tx.mode === HEDERA_TRANSACTION_MODES.Delegate ||
|
|
484
|
+
tx.mode === HEDERA_TRANSACTION_MODES.Undelegate ||
|
|
485
|
+
tx.mode === HEDERA_TRANSACTION_MODES.Redelegate ||
|
|
486
|
+
tx.mode === HEDERA_TRANSACTION_MODES.ClaimRewards
|
|
487
|
+
);
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
export const hasSpecificIntentData = <Type extends "staking" | "erc20">(
|
|
491
|
+
txIntent: TransactionIntent<HederaMemo, HederaTxData>,
|
|
492
|
+
expectedType: Type,
|
|
493
|
+
): txIntent is Extract<TransactionIntent<HederaMemo, HederaTxData>, { data: { type: Type } }> => {
|
|
494
|
+
return "data" in txIntent && txIntent.data.type === expectedType;
|
|
495
|
+
};
|