@ledgerhq/coin-hedera 1.15.0 → 1.16.0-nightly.20251210114107
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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
1
2
|
import type { AccountRaw, Account } from "@ledgerhq/types-live";
|
|
2
3
|
import type {
|
|
3
4
|
HederaAccount,
|
|
@@ -8,19 +9,35 @@ import type {
|
|
|
8
9
|
|
|
9
10
|
export function toHederaResourcesRaw(resources: HederaResources): HederaResourcesRaw {
|
|
10
11
|
const { maxAutomaticTokenAssociations, isAutoTokenAssociationEnabled } = resources;
|
|
12
|
+
const delegation = resources.delegation
|
|
13
|
+
? {
|
|
14
|
+
nodeId: resources.delegation.nodeId,
|
|
15
|
+
delegated: resources.delegation.delegated.toString(),
|
|
16
|
+
pendingReward: resources.delegation.pendingReward.toString(),
|
|
17
|
+
}
|
|
18
|
+
: null;
|
|
11
19
|
|
|
12
20
|
return {
|
|
13
21
|
maxAutomaticTokenAssociations,
|
|
14
22
|
isAutoTokenAssociationEnabled,
|
|
23
|
+
delegation,
|
|
15
24
|
};
|
|
16
25
|
}
|
|
17
26
|
|
|
18
27
|
export function fromHederaResourcesRaw(rawResources: HederaResourcesRaw): HederaResources {
|
|
19
28
|
const { maxAutomaticTokenAssociations, isAutoTokenAssociationEnabled } = rawResources;
|
|
29
|
+
const delegation = rawResources.delegation
|
|
30
|
+
? {
|
|
31
|
+
nodeId: rawResources.delegation.nodeId,
|
|
32
|
+
delegated: new BigNumber(rawResources.delegation.delegated),
|
|
33
|
+
pendingReward: new BigNumber(rawResources.delegation.pendingReward),
|
|
34
|
+
}
|
|
35
|
+
: null;
|
|
20
36
|
|
|
21
37
|
return {
|
|
22
38
|
maxAutomaticTokenAssociations,
|
|
23
39
|
isAutoTokenAssociationEnabled,
|
|
40
|
+
delegation,
|
|
24
41
|
};
|
|
25
42
|
}
|
|
26
43
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Observable } from "rxjs";
|
|
2
|
-
import {
|
|
3
|
-
import { AssetInfo, FeeEstimation } from "@ledgerhq/coin-framework/api/types";
|
|
4
|
-
import { SignerContext } from "@ledgerhq/coin-framework/signer";
|
|
2
|
+
import type { AccountBridge } from "@ledgerhq/types-live";
|
|
3
|
+
import type { AssetInfo, FeeEstimation } from "@ledgerhq/coin-framework/api/types";
|
|
4
|
+
import type { SignerContext } from "@ledgerhq/coin-framework/signer";
|
|
5
5
|
import { findSubAccountById } from "@ledgerhq/coin-framework/account/helpers";
|
|
6
6
|
import { buildOptimisticOperation } from "./buildOptimisticOperation";
|
|
7
7
|
import { DEFAULT_GAS_LIMIT, HEDERA_TRANSACTION_MODES } from "../constants";
|
|
@@ -12,13 +12,14 @@ import {
|
|
|
12
12
|
serializeTransaction,
|
|
13
13
|
getHederaTransactionBodyBytes,
|
|
14
14
|
isTokenAssociateTransaction,
|
|
15
|
+
isStakingTransaction,
|
|
15
16
|
} from "../logic/utils";
|
|
16
|
-
import type { Transaction, HederaSigner, HederaTxData } from "../types";
|
|
17
|
+
import type { Transaction, HederaSigner, HederaTxData, HederaAccount } from "../types";
|
|
17
18
|
|
|
18
19
|
export const buildSignOperation =
|
|
19
20
|
(
|
|
20
21
|
signerContext: SignerContext<HederaSigner>,
|
|
21
|
-
): AccountBridge<Transaction,
|
|
22
|
+
): AccountBridge<Transaction, HederaAccount>["signOperation"] =>
|
|
22
23
|
({ account, transaction, deviceId }) =>
|
|
23
24
|
new Observable(o => {
|
|
24
25
|
void (async function () {
|
|
@@ -64,6 +65,15 @@ export const buildSignOperation =
|
|
|
64
65
|
type: "erc20",
|
|
65
66
|
gasLimit: BigInt((transaction.gasLimit ?? DEFAULT_GAS_LIMIT).toString()),
|
|
66
67
|
};
|
|
68
|
+
} else if (isStakingTransaction(transaction)) {
|
|
69
|
+
type = transaction.mode;
|
|
70
|
+
asset = {
|
|
71
|
+
type: "native",
|
|
72
|
+
};
|
|
73
|
+
data = {
|
|
74
|
+
type: "staking",
|
|
75
|
+
stakingNodeId: transaction.properties?.stakingNodeId,
|
|
76
|
+
};
|
|
67
77
|
} else {
|
|
68
78
|
type = HEDERA_TRANSACTION_MODES.Send;
|
|
69
79
|
asset = {
|
|
@@ -107,6 +107,14 @@ export const getAccountShape: GetAccountShape<HederaAccount> = async (
|
|
|
107
107
|
const operations = shouldSyncFromScratch
|
|
108
108
|
? enrichedNewOperations
|
|
109
109
|
: mergeOps(oldOperations, enrichedNewOperations);
|
|
110
|
+
const delegation =
|
|
111
|
+
typeof mirrorAccount.staked_node_id === "number"
|
|
112
|
+
? {
|
|
113
|
+
nodeId: mirrorAccount.staked_node_id,
|
|
114
|
+
delegated: accountBalance,
|
|
115
|
+
pendingReward: new BigNumber(mirrorAccount.pending_reward),
|
|
116
|
+
}
|
|
117
|
+
: null;
|
|
110
118
|
|
|
111
119
|
// how ERC20 operations are handled:
|
|
112
120
|
// - mirror node doesn't include "IN" erc20 token transactions
|
|
@@ -159,6 +167,7 @@ export const getAccountShape: GetAccountShape<HederaAccount> = async (
|
|
|
159
167
|
hederaResources: {
|
|
160
168
|
maxAutomaticTokenAssociations: mirrorAccount.max_automatic_token_associations,
|
|
161
169
|
isAutoTokenAssociationEnabled: mirrorAccount.max_automatic_token_associations === -1,
|
|
170
|
+
delegation,
|
|
162
171
|
},
|
|
163
172
|
};
|
|
164
173
|
};
|
|
@@ -218,7 +218,7 @@ describe("utils", () => {
|
|
|
218
218
|
erc20Tokens: [],
|
|
219
219
|
});
|
|
220
220
|
|
|
221
|
-
expect(result).
|
|
221
|
+
expect(result).toEqual([]);
|
|
222
222
|
});
|
|
223
223
|
|
|
224
224
|
it("returns sub account for mirror token with no operations yet (e.g. right after association)", async () => {
|
|
@@ -237,7 +237,6 @@ describe("utils", () => {
|
|
|
237
237
|
erc20Tokens: [],
|
|
238
238
|
});
|
|
239
239
|
|
|
240
|
-
expect(result).toHaveLength(1);
|
|
241
240
|
expect(result).toMatchObject([
|
|
242
241
|
{
|
|
243
242
|
token: tokenCurrencyFromCAL,
|
|
@@ -259,7 +258,6 @@ describe("utils", () => {
|
|
|
259
258
|
erc20Tokens: [{ balance: new BigNumber(42), token: tokenCurrencyFromCAL }],
|
|
260
259
|
});
|
|
261
260
|
|
|
262
|
-
expect(result).toHaveLength(1);
|
|
263
261
|
expect(result).toMatchObject([
|
|
264
262
|
{
|
|
265
263
|
token: tokenCurrencyFromCAL,
|
|
@@ -341,7 +339,6 @@ describe("utils", () => {
|
|
|
341
339
|
hash: incomingTxHash,
|
|
342
340
|
blockHash: incomingERC20Transaction.blockHash,
|
|
343
341
|
});
|
|
344
|
-
expect(incomingOp?.subOperations).toHaveLength(1);
|
|
345
342
|
expect(incomingOp?.subOperations).toMatchObject([
|
|
346
343
|
{
|
|
347
344
|
type: "IN",
|
|
@@ -353,7 +350,6 @@ describe("utils", () => {
|
|
|
353
350
|
recipients: [address],
|
|
354
351
|
},
|
|
355
352
|
]);
|
|
356
|
-
expect(newERC20TokenOperations).toHaveLength(1);
|
|
357
353
|
expect(newERC20TokenOperations).toMatchObject([incomingOp?.subOperations?.[0]]);
|
|
358
354
|
expect(updatedOperations).toHaveLength(oldMirrorOperations.length + 1);
|
|
359
355
|
});
|
|
@@ -422,7 +418,6 @@ describe("utils", () => {
|
|
|
422
418
|
blockHash: allowanceERC20Transaction.blockHash,
|
|
423
419
|
standard: "erc20",
|
|
424
420
|
});
|
|
425
|
-
expect(allowanceOp?.subOperations).toHaveLength(1);
|
|
426
421
|
expect(allowanceOp?.subOperations).toMatchObject([
|
|
427
422
|
{
|
|
428
423
|
type: "OUT",
|
|
@@ -434,7 +429,6 @@ describe("utils", () => {
|
|
|
434
429
|
recipients: [allowanceTxTo],
|
|
435
430
|
},
|
|
436
431
|
]);
|
|
437
|
-
expect(newERC20TokenOperations).toHaveLength(1);
|
|
438
432
|
expect(newERC20TokenOperations).toMatchObject([allowanceOp?.subOperations?.[0]]);
|
|
439
433
|
expect(updatedOperations).toHaveLength(oldMirrorOperations.length + 1);
|
|
440
434
|
});
|
|
@@ -519,7 +513,7 @@ describe("utils", () => {
|
|
|
519
513
|
);
|
|
520
514
|
|
|
521
515
|
expect(updatedOperations).toHaveLength(2);
|
|
522
|
-
expect(duplicatedContractCalls).
|
|
516
|
+
expect(duplicatedContractCalls).toEqual([]);
|
|
523
517
|
expect(feesOps).toHaveLength(1);
|
|
524
518
|
expect(feesOps).toMatchObject([{ blockHash: "0xBLOCK" }]);
|
|
525
519
|
});
|
|
@@ -553,7 +547,7 @@ describe("utils", () => {
|
|
|
553
547
|
|
|
554
548
|
expect(pendingOp).toBeUndefined();
|
|
555
549
|
expect(updatedOperations).toHaveLength(1);
|
|
556
|
-
expect(updatedOperations
|
|
550
|
+
expect(updatedOperations).toMatchObject([{ hash: "confirmed_tx" }]);
|
|
557
551
|
});
|
|
558
552
|
|
|
559
553
|
/**
|
|
@@ -672,7 +666,6 @@ describe("utils", () => {
|
|
|
672
666
|
},
|
|
673
667
|
],
|
|
674
668
|
});
|
|
675
|
-
expect(newERC20TokenOperations).toHaveLength(1);
|
|
676
669
|
expect(newERC20TokenOperations).toMatchObject([
|
|
677
670
|
{
|
|
678
671
|
type: "OUT",
|
package/src/constants.ts
CHANGED
|
@@ -7,12 +7,17 @@ import BigNumber from "bignumber.js";
|
|
|
7
7
|
export enum HEDERA_TRANSACTION_MODES {
|
|
8
8
|
Send = "send",
|
|
9
9
|
TokenAssociate = "token-associate",
|
|
10
|
+
Delegate = "delegate",
|
|
11
|
+
Undelegate = "undelegate",
|
|
12
|
+
Redelegate = "redelegate",
|
|
13
|
+
ClaimRewards = "claim-rewards",
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
/**
|
|
13
17
|
* Enum representing the supported Hedera operation types for fee estimation
|
|
14
18
|
*/
|
|
15
19
|
export enum HEDERA_OPERATION_TYPES {
|
|
20
|
+
CryptoUpdate = "CryptoUpdate",
|
|
16
21
|
CryptoTransfer = "CryptoTransfer",
|
|
17
22
|
TokenTransfer = "TokenTransfer",
|
|
18
23
|
TokenAssociate = "TokenAssociate",
|
|
@@ -39,6 +44,15 @@ export const DEFAULT_GAS_PRICE_TINYBARS = new BigNumber(100);
|
|
|
39
44
|
|
|
40
45
|
export const HEDERA_MAINNET_CHAIN_ID = 295;
|
|
41
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Enum representing the delegation status of a Hedera account
|
|
49
|
+
*/
|
|
50
|
+
export enum HEDERA_DELEGATION_STATUS {
|
|
51
|
+
Inactive = "inactive",
|
|
52
|
+
Overstaked = "overstaked",
|
|
53
|
+
Active = "active",
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
/**
|
|
43
57
|
* https://docs.hedera.com/hedera/networks/mainnet/fees
|
|
44
58
|
*
|
|
@@ -49,6 +63,7 @@ export const HEDERA_MAINNET_CHAIN_ID = 295;
|
|
|
49
63
|
* has sufficient balance to cover the cost of a transaction (e.g. token association).
|
|
50
64
|
*/
|
|
51
65
|
export const BASE_USD_FEE_BY_OPERATION_TYPE = {
|
|
66
|
+
[HEDERA_OPERATION_TYPES.CryptoUpdate]: 0.00022 * 10 ** TINYBAR_SCALE,
|
|
52
67
|
[HEDERA_OPERATION_TYPES.CryptoTransfer]: 0.0001 * 10 ** TINYBAR_SCALE,
|
|
53
68
|
[HEDERA_OPERATION_TYPES.TokenTransfer]: 0.001 * 10 ** TINYBAR_SCALE,
|
|
54
69
|
[HEDERA_OPERATION_TYPES.TokenAssociate]: 0.05 * 10 ** TINYBAR_SCALE,
|
|
@@ -84,3 +99,10 @@ export const SUPPORTED_ERC20_TOKENS = [
|
|
|
84
99
|
tokenId: "0.0.10047837",
|
|
85
100
|
},
|
|
86
101
|
];
|
|
102
|
+
|
|
103
|
+
export const MAP_STAKING_MODE_TO_MEMO = {
|
|
104
|
+
[HEDERA_TRANSACTION_MODES.ClaimRewards]: "Collect Staking Rewards",
|
|
105
|
+
[HEDERA_TRANSACTION_MODES.Delegate]: "Stake",
|
|
106
|
+
[HEDERA_TRANSACTION_MODES.Undelegate]: "Unstake",
|
|
107
|
+
[HEDERA_TRANSACTION_MODES.Redelegate]: "Restake",
|
|
108
|
+
} as const;
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
import type { Account } from "@ledgerhq/types-live";
|
|
3
|
+
import { HEDERA_TRANSACTION_MODES } from "./constants";
|
|
4
|
+
import getDeviceTransactionConfig from "./deviceTransactionConfig";
|
|
5
|
+
import type { Transaction, TransactionStatus } from "./types";
|
|
6
|
+
|
|
7
|
+
describe("getDeviceTransactionConfig", () => {
|
|
8
|
+
const mockAccount = {
|
|
9
|
+
id: "mock-account-id",
|
|
10
|
+
currency: { id: "hedera" },
|
|
11
|
+
} as Account;
|
|
12
|
+
|
|
13
|
+
const createMockStatus = (estimatedFees: BigNumber): TransactionStatus => ({
|
|
14
|
+
errors: {},
|
|
15
|
+
warnings: {},
|
|
16
|
+
estimatedFees,
|
|
17
|
+
amount: new BigNumber(0),
|
|
18
|
+
totalSpent: new BigNumber(0),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("staking transactions", () => {
|
|
22
|
+
it("should return correct fields for ClaimRewards transaction", async () => {
|
|
23
|
+
const transaction = {
|
|
24
|
+
family: "hedera",
|
|
25
|
+
mode: HEDERA_TRANSACTION_MODES.ClaimRewards,
|
|
26
|
+
amount: new BigNumber(0),
|
|
27
|
+
recipient: "",
|
|
28
|
+
memo: "Claiming rewards",
|
|
29
|
+
} as Transaction;
|
|
30
|
+
|
|
31
|
+
const status = createMockStatus(new BigNumber(100000));
|
|
32
|
+
|
|
33
|
+
const fields = await getDeviceTransactionConfig({
|
|
34
|
+
account: mockAccount,
|
|
35
|
+
transaction,
|
|
36
|
+
status,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(fields).toEqual([
|
|
40
|
+
{
|
|
41
|
+
type: "text",
|
|
42
|
+
label: "Method",
|
|
43
|
+
value: "Claim Rewards",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
label: "Fees",
|
|
47
|
+
type: "fees",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
type: "text",
|
|
51
|
+
label: "Memo",
|
|
52
|
+
value: "Claiming rewards",
|
|
53
|
+
},
|
|
54
|
+
]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should return correct fields for Delegate transaction", async () => {
|
|
58
|
+
const transaction = {
|
|
59
|
+
family: "hedera",
|
|
60
|
+
mode: HEDERA_TRANSACTION_MODES.Delegate,
|
|
61
|
+
amount: new BigNumber(0),
|
|
62
|
+
recipient: "",
|
|
63
|
+
properties: {
|
|
64
|
+
stakingNodeId: 10,
|
|
65
|
+
},
|
|
66
|
+
} as Transaction;
|
|
67
|
+
|
|
68
|
+
const status = createMockStatus(new BigNumber(100000));
|
|
69
|
+
|
|
70
|
+
const fields = await getDeviceTransactionConfig({
|
|
71
|
+
account: mockAccount,
|
|
72
|
+
transaction,
|
|
73
|
+
status,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
expect(fields).toEqual([
|
|
77
|
+
{
|
|
78
|
+
type: "text",
|
|
79
|
+
label: "Method",
|
|
80
|
+
value: "Update Account",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
label: "Fees",
|
|
84
|
+
type: "fees",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: "text",
|
|
88
|
+
label: "Staked Node ID",
|
|
89
|
+
value: "10",
|
|
90
|
+
},
|
|
91
|
+
]);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("should not include staking node ID if not provided", async () => {
|
|
95
|
+
const transaction = {
|
|
96
|
+
family: "hedera",
|
|
97
|
+
mode: HEDERA_TRANSACTION_MODES.Undelegate,
|
|
98
|
+
amount: new BigNumber(0),
|
|
99
|
+
recipient: "",
|
|
100
|
+
properties: {},
|
|
101
|
+
} as Transaction;
|
|
102
|
+
|
|
103
|
+
const status = createMockStatus(new BigNumber(100000));
|
|
104
|
+
|
|
105
|
+
const fields = await getDeviceTransactionConfig({
|
|
106
|
+
account: mockAccount,
|
|
107
|
+
transaction,
|
|
108
|
+
status,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
expect(fields).toEqual([
|
|
112
|
+
{
|
|
113
|
+
type: "text",
|
|
114
|
+
label: "Method",
|
|
115
|
+
value: "Update Account",
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
label: "Fees",
|
|
119
|
+
type: "fees",
|
|
120
|
+
},
|
|
121
|
+
]);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("token associate transactions", () => {
|
|
126
|
+
it("should return correct fields for TokenAssociate transaction", async () => {
|
|
127
|
+
const transaction = {
|
|
128
|
+
family: "hedera",
|
|
129
|
+
mode: HEDERA_TRANSACTION_MODES.TokenAssociate,
|
|
130
|
+
amount: new BigNumber(0),
|
|
131
|
+
recipient: "",
|
|
132
|
+
subAccountId: "token-account-id",
|
|
133
|
+
memo: "Associating token",
|
|
134
|
+
} as Transaction;
|
|
135
|
+
|
|
136
|
+
const status = createMockStatus(new BigNumber(50000));
|
|
137
|
+
|
|
138
|
+
const fields = await getDeviceTransactionConfig({
|
|
139
|
+
account: mockAccount,
|
|
140
|
+
transaction,
|
|
141
|
+
status,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(fields).toEqual([
|
|
145
|
+
{
|
|
146
|
+
type: "text",
|
|
147
|
+
label: "Method",
|
|
148
|
+
value: "Associate Token",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: "fees",
|
|
152
|
+
label: "Fees",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
type: "text",
|
|
156
|
+
label: "Memo",
|
|
157
|
+
value: "Associating token",
|
|
158
|
+
},
|
|
159
|
+
]);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("should not include fees if they are zero", async () => {
|
|
163
|
+
const transaction = {
|
|
164
|
+
family: "hedera",
|
|
165
|
+
mode: HEDERA_TRANSACTION_MODES.TokenAssociate,
|
|
166
|
+
amount: new BigNumber(0),
|
|
167
|
+
recipient: "",
|
|
168
|
+
subAccountId: "token-account-id",
|
|
169
|
+
} as Transaction;
|
|
170
|
+
|
|
171
|
+
const status = createMockStatus(new BigNumber(0));
|
|
172
|
+
|
|
173
|
+
const fields = await getDeviceTransactionConfig({
|
|
174
|
+
account: mockAccount,
|
|
175
|
+
transaction,
|
|
176
|
+
status,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
expect(fields).toEqual([
|
|
180
|
+
{
|
|
181
|
+
type: "text",
|
|
182
|
+
label: "Method",
|
|
183
|
+
value: "Associate Token",
|
|
184
|
+
},
|
|
185
|
+
]);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe("regular transfer transactions", () => {
|
|
190
|
+
it("should return correct fields for regular Send transaction", async () => {
|
|
191
|
+
const transaction = {
|
|
192
|
+
family: "hedera",
|
|
193
|
+
mode: HEDERA_TRANSACTION_MODES.Send,
|
|
194
|
+
amount: new BigNumber(1000000),
|
|
195
|
+
recipient: "0.0.12345",
|
|
196
|
+
useAllAmount: false,
|
|
197
|
+
memo: "Payment",
|
|
198
|
+
} as Transaction;
|
|
199
|
+
|
|
200
|
+
const status = createMockStatus(new BigNumber(100000));
|
|
201
|
+
|
|
202
|
+
const fields = await getDeviceTransactionConfig({
|
|
203
|
+
account: mockAccount,
|
|
204
|
+
transaction,
|
|
205
|
+
status,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
expect(fields).toEqual([
|
|
209
|
+
{
|
|
210
|
+
type: "text",
|
|
211
|
+
label: "Method",
|
|
212
|
+
value: "Transfer",
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
type: "amount",
|
|
216
|
+
label: "Amount",
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
type: "fees",
|
|
220
|
+
label: "Fees",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
type: "text",
|
|
224
|
+
label: "Memo",
|
|
225
|
+
value: "Payment",
|
|
226
|
+
},
|
|
227
|
+
]);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("should show 'Transfer All' method when useAllAmount is true", async () => {
|
|
231
|
+
const transaction = {
|
|
232
|
+
family: "hedera",
|
|
233
|
+
mode: HEDERA_TRANSACTION_MODES.Send,
|
|
234
|
+
amount: new BigNumber(0),
|
|
235
|
+
recipient: "0.0.12345",
|
|
236
|
+
useAllAmount: true,
|
|
237
|
+
} as Transaction;
|
|
238
|
+
|
|
239
|
+
const status = createMockStatus(new BigNumber(100000));
|
|
240
|
+
|
|
241
|
+
const fields = await getDeviceTransactionConfig({
|
|
242
|
+
account: mockAccount,
|
|
243
|
+
transaction,
|
|
244
|
+
status,
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
expect(fields[0]).toEqual({
|
|
248
|
+
type: "text",
|
|
249
|
+
label: "Method",
|
|
250
|
+
value: "Transfer All",
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it("should include gas limit for Send transactions with gasLimit", async () => {
|
|
255
|
+
const transaction = {
|
|
256
|
+
family: "hedera",
|
|
257
|
+
mode: HEDERA_TRANSACTION_MODES.Send,
|
|
258
|
+
amount: new BigNumber(1000000),
|
|
259
|
+
recipient: "0.0.12345",
|
|
260
|
+
useAllAmount: false,
|
|
261
|
+
gasLimit: new BigNumber(300000),
|
|
262
|
+
} as Transaction;
|
|
263
|
+
|
|
264
|
+
const status = createMockStatus(new BigNumber(100000));
|
|
265
|
+
|
|
266
|
+
const fields = await getDeviceTransactionConfig({
|
|
267
|
+
account: mockAccount,
|
|
268
|
+
transaction,
|
|
269
|
+
status,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
expect(fields).toEqual([
|
|
273
|
+
{
|
|
274
|
+
type: "text",
|
|
275
|
+
label: "Method",
|
|
276
|
+
value: "Transfer",
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
type: "amount",
|
|
280
|
+
label: "Amount",
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
type: "fees",
|
|
284
|
+
label: "Fees",
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
type: "text",
|
|
288
|
+
label: "Gas Limit",
|
|
289
|
+
value: "300000",
|
|
290
|
+
},
|
|
291
|
+
]);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("should not include memo if not provided", async () => {
|
|
295
|
+
const transaction = {
|
|
296
|
+
family: "hedera",
|
|
297
|
+
mode: HEDERA_TRANSACTION_MODES.Send,
|
|
298
|
+
amount: new BigNumber(1000000),
|
|
299
|
+
recipient: "0.0.12345",
|
|
300
|
+
useAllAmount: false,
|
|
301
|
+
} as Transaction;
|
|
302
|
+
|
|
303
|
+
const status = createMockStatus(new BigNumber(100000));
|
|
304
|
+
|
|
305
|
+
const fields = await getDeviceTransactionConfig({
|
|
306
|
+
account: mockAccount,
|
|
307
|
+
transaction,
|
|
308
|
+
status,
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
expect(fields).toHaveLength(3);
|
|
312
|
+
expect(fields.some(f => f.label === "Memo")).toBe(false);
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CommonDeviceTransactionField as DeviceTransactionField } from "@ledgerhq/coin-framework/transaction/common";
|
|
2
2
|
import type { AccountLike, Account } from "@ledgerhq/types-live";
|
|
3
3
|
import { HEDERA_TRANSACTION_MODES } from "./constants";
|
|
4
|
-
import { isTokenAssociateTransaction } from "./logic/utils";
|
|
4
|
+
import { isTokenAssociateTransaction, isStakingTransaction } from "./logic/utils";
|
|
5
5
|
import type { Transaction, TransactionStatus } from "./types";
|
|
6
6
|
|
|
7
7
|
async function getDeviceTransactionConfig({
|
|
@@ -15,6 +15,42 @@ async function getDeviceTransactionConfig({
|
|
|
15
15
|
}): Promise<Array<DeviceTransactionField>> {
|
|
16
16
|
const fields: Array<DeviceTransactionField> = [];
|
|
17
17
|
|
|
18
|
+
if (isStakingTransaction(transaction)) {
|
|
19
|
+
fields.push({
|
|
20
|
+
type: "text",
|
|
21
|
+
label: "Method",
|
|
22
|
+
value:
|
|
23
|
+
transaction.mode === HEDERA_TRANSACTION_MODES.ClaimRewards
|
|
24
|
+
? "Claim Rewards"
|
|
25
|
+
: "Update Account",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (!estimatedFees.isZero()) {
|
|
29
|
+
fields.push({
|
|
30
|
+
type: "fees",
|
|
31
|
+
label: "Fees",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (typeof transaction.properties?.stakingNodeId === "number") {
|
|
36
|
+
fields.push({
|
|
37
|
+
type: "text",
|
|
38
|
+
label: "Staked Node ID",
|
|
39
|
+
value: transaction.properties.stakingNodeId.toString(),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (transaction.memo) {
|
|
44
|
+
fields.push({
|
|
45
|
+
type: "text",
|
|
46
|
+
label: "Memo",
|
|
47
|
+
value: transaction.memo,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return fields;
|
|
52
|
+
}
|
|
53
|
+
|
|
18
54
|
const method = (() => {
|
|
19
55
|
if (isTokenAssociateTransaction(transaction)) return "Associate Token";
|
|
20
56
|
else if (transaction.useAllAmount) return "Transfer All";
|
package/src/errors.ts
CHANGED
|
@@ -16,3 +16,10 @@ export const HederaRecipientTokenAssociationUnverified = createCustomErrorClass(
|
|
|
16
16
|
export const HederaRecipientEvmAddressVerificationRequired = createCustomErrorClass(
|
|
17
17
|
"HederaRecipientEvmAddressVerificationRequired",
|
|
18
18
|
);
|
|
19
|
+
export const HederaRedundantStakingNodeIdError = createCustomErrorClass(
|
|
20
|
+
"HederaRedundantStakingNodeIdError",
|
|
21
|
+
);
|
|
22
|
+
export const HederaInvalidStakingNodeIdError = createCustomErrorClass(
|
|
23
|
+
"HederaInvalidStakingNodeIdError",
|
|
24
|
+
);
|
|
25
|
+
export const HederaNoStakingRewardsError = createCustomErrorClass("HederaNoStakingRewardsError");
|