@ledgerhq/coin-hedera 1.15.0-nightly.20251126023856 → 1.15.0-nightly.20251126160702
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 +10 -8
- package/lib/api/index.d.ts.map +1 -1
- package/lib/api/index.js +6 -2
- package/lib/api/index.js.map +1 -1
- package/lib/bridge/broadcast.js +1 -1
- package/lib/bridge/broadcast.js.map +1 -1
- package/lib/bridge/buildOptimisticOperation.d.ts.map +1 -1
- package/lib/bridge/buildOptimisticOperation.js +80 -15
- package/lib/bridge/buildOptimisticOperation.js.map +1 -1
- package/lib/bridge/estimateMaxSpendable.js +5 -2
- package/lib/bridge/estimateMaxSpendable.js.map +1 -1
- package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib/bridge/getTransactionStatus.js +70 -13
- package/lib/bridge/getTransactionStatus.js.map +1 -1
- package/lib/bridge/index.js +1 -1
- package/lib/bridge/index.js.map +1 -1
- package/lib/bridge/prepareTransaction.d.ts.map +1 -1
- package/lib/bridge/prepareTransaction.js +40 -7
- package/lib/bridge/prepareTransaction.js.map +1 -1
- package/lib/bridge/signOperation.d.ts.map +1 -1
- package/lib/bridge/signOperation.js +19 -2
- package/lib/bridge/signOperation.js.map +1 -1
- package/lib/bridge/synchronisation.d.ts +2 -0
- package/lib/bridge/synchronisation.d.ts.map +1 -1
- package/lib/bridge/synchronisation.js +101 -30
- package/lib/bridge/synchronisation.js.map +1 -1
- package/lib/bridge/utils.d.ts +35 -2
- package/lib/bridge/utils.d.ts.map +1 -1
- package/lib/bridge/utils.js +215 -16
- package/lib/bridge/utils.js.map +1 -1
- package/lib/constants.d.ts +22 -2
- package/lib/constants.d.ts.map +1 -1
- package/lib/constants.js +42 -2
- package/lib/constants.js.map +1 -1
- package/lib/deviceTransactionConfig.d.ts +1 -1
- package/lib/deviceTransactionConfig.d.ts.map +1 -1
- package/lib/deviceTransactionConfig.js +8 -0
- package/lib/deviceTransactionConfig.js.map +1 -1
- package/lib/errors.d.ts +3 -0
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +2 -1
- package/lib/errors.js.map +1 -1
- package/lib/logic/craftTransaction.d.ts +4 -4
- package/lib/logic/craftTransaction.d.ts.map +1 -1
- package/lib/logic/craftTransaction.js +46 -5
- package/lib/logic/craftTransaction.js.map +1 -1
- package/lib/logic/estimateFees.d.ts +2 -4
- package/lib/logic/estimateFees.d.ts.map +1 -1
- package/lib/logic/estimateFees.js +45 -5
- package/lib/logic/estimateFees.js.map +1 -1
- package/lib/logic/listOperations.d.ts.map +1 -1
- package/lib/logic/listOperations.js +7 -3
- package/lib/logic/listOperations.js.map +1 -1
- package/lib/logic/utils.d.ts +24 -2
- package/lib/logic/utils.d.ts.map +1 -1
- package/lib/logic/utils.js +66 -13
- package/lib/logic/utils.js.map +1 -1
- package/lib/network/api.d.ts +12 -1
- package/lib/network/api.d.ts.map +1 -1
- package/lib/network/api.js +91 -19
- package/lib/network/api.js.map +1 -1
- package/lib/network/rpc.d.ts.map +1 -1
- package/lib/network/rpc.js +1 -0
- package/lib/network/rpc.js.map +1 -1
- package/lib/network/thirdweb.d.ts +21 -0
- package/lib/network/thirdweb.d.ts.map +1 -0
- package/lib/network/thirdweb.js +72 -0
- package/lib/network/thirdweb.js.map +1 -0
- package/lib/network/utils.d.ts +4 -1
- package/lib/network/utils.d.ts.map +1 -1
- package/lib/network/utils.js +53 -1
- package/lib/network/utils.js.map +1 -1
- package/lib/test/bridgeDatasetTest.d.ts.map +1 -1
- package/lib/test/bridgeDatasetTest.js +4 -4
- package/lib/test/bridgeDatasetTest.js.map +1 -1
- package/lib/test/fixtures/account.fixture.js +1 -1
- package/lib/test/fixtures/account.fixture.js.map +1 -1
- package/lib/test/fixtures/common.fixture.d.ts +12 -0
- package/lib/test/fixtures/common.fixture.d.ts.map +1 -0
- package/lib/test/fixtures/common.fixture.js +66 -0
- package/lib/test/fixtures/common.fixture.js.map +1 -0
- package/lib/test/fixtures/currency.fixture.d.ts +3 -1
- package/lib/test/fixtures/currency.fixture.d.ts.map +1 -1
- package/lib/test/fixtures/currency.fixture.js +63 -16
- package/lib/test/fixtures/currency.fixture.js.map +1 -1
- package/lib/test/fixtures/mirror.fixture.d.ts +3 -1
- package/lib/test/fixtures/mirror.fixture.d.ts.map +1 -1
- package/lib/test/fixtures/mirror.fixture.js +12 -1
- package/lib/test/fixtures/mirror.fixture.js.map +1 -1
- package/lib/test/fixtures/thirdweb.fixture.d.ts +3 -0
- package/lib/test/fixtures/thirdweb.fixture.d.ts.map +1 -0
- package/lib/test/fixtures/thirdweb.fixture.js +34 -0
- package/lib/test/fixtures/thirdweb.fixture.js.map +1 -0
- package/lib/transaction.d.ts.map +1 -1
- package/lib/transaction.js +2 -0
- package/lib/transaction.js.map +1 -1
- package/lib/types/alpaca.d.ts +5 -1
- package/lib/types/alpaca.d.ts.map +1 -1
- package/lib/types/bridge.d.ts +6 -1
- package/lib/types/bridge.d.ts.map +1 -1
- package/lib/types/index.d.ts +2 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +2 -0
- package/lib/types/index.js.map +1 -1
- package/lib/types/logic.d.ts +39 -0
- package/lib/types/logic.d.ts.map +1 -0
- package/lib/types/logic.js +3 -0
- package/lib/types/logic.js.map +1 -0
- package/lib/types/mirror.d.ts +29 -0
- package/lib/types/mirror.d.ts.map +1 -1
- package/lib/types/thirdweb.d.ts +34 -0
- package/lib/types/thirdweb.d.ts.map +1 -0
- package/lib/types/thirdweb.js +3 -0
- package/lib/types/thirdweb.js.map +1 -0
- package/lib-es/api/index.d.ts.map +1 -1
- package/lib-es/api/index.js +7 -3
- package/lib-es/api/index.js.map +1 -1
- package/lib-es/bridge/broadcast.js +2 -2
- package/lib-es/bridge/broadcast.js.map +1 -1
- package/lib-es/bridge/buildOptimisticOperation.d.ts.map +1 -1
- package/lib-es/bridge/buildOptimisticOperation.js +83 -18
- package/lib-es/bridge/buildOptimisticOperation.js.map +1 -1
- package/lib-es/bridge/estimateMaxSpendable.js +5 -2
- package/lib-es/bridge/estimateMaxSpendable.js.map +1 -1
- package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib-es/bridge/getTransactionStatus.js +73 -16
- package/lib-es/bridge/getTransactionStatus.js.map +1 -1
- package/lib-es/bridge/index.js +2 -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 +42 -9
- package/lib-es/bridge/prepareTransaction.js.map +1 -1
- package/lib-es/bridge/signOperation.d.ts.map +1 -1
- package/lib-es/bridge/signOperation.js +21 -4
- package/lib-es/bridge/signOperation.js.map +1 -1
- package/lib-es/bridge/synchronisation.d.ts +2 -0
- package/lib-es/bridge/synchronisation.d.ts.map +1 -1
- package/lib-es/bridge/synchronisation.js +97 -27
- package/lib-es/bridge/synchronisation.js.map +1 -1
- package/lib-es/bridge/utils.d.ts +35 -2
- package/lib-es/bridge/utils.d.ts.map +1 -1
- package/lib-es/bridge/utils.js +211 -16
- package/lib-es/bridge/utils.js.map +1 -1
- package/lib-es/constants.d.ts +22 -2
- package/lib-es/constants.d.ts.map +1 -1
- package/lib-es/constants.js +38 -1
- package/lib-es/constants.js.map +1 -1
- package/lib-es/deviceTransactionConfig.d.ts +1 -1
- package/lib-es/deviceTransactionConfig.d.ts.map +1 -1
- package/lib-es/deviceTransactionConfig.js +8 -0
- package/lib-es/deviceTransactionConfig.js.map +1 -1
- package/lib-es/errors.d.ts +3 -0
- package/lib-es/errors.d.ts.map +1 -1
- package/lib-es/errors.js +1 -0
- package/lib-es/errors.js.map +1 -1
- package/lib-es/logic/craftTransaction.d.ts +4 -4
- package/lib-es/logic/craftTransaction.d.ts.map +1 -1
- package/lib-es/logic/craftTransaction.js +48 -7
- package/lib-es/logic/craftTransaction.js.map +1 -1
- package/lib-es/logic/estimateFees.d.ts +2 -4
- package/lib-es/logic/estimateFees.d.ts.map +1 -1
- package/lib-es/logic/estimateFees.js +47 -7
- package/lib-es/logic/estimateFees.js.map +1 -1
- package/lib-es/logic/listOperations.d.ts.map +1 -1
- package/lib-es/logic/listOperations.js +7 -3
- package/lib-es/logic/listOperations.js.map +1 -1
- package/lib-es/logic/utils.d.ts +24 -2
- package/lib-es/logic/utils.d.ts.map +1 -1
- package/lib-es/logic/utils.js +63 -13
- package/lib-es/logic/utils.js.map +1 -1
- package/lib-es/network/api.d.ts +12 -1
- package/lib-es/network/api.d.ts.map +1 -1
- package/lib-es/network/api.js +91 -19
- package/lib-es/network/api.js.map +1 -1
- package/lib-es/network/rpc.d.ts.map +1 -1
- package/lib-es/network/rpc.js +1 -0
- package/lib-es/network/rpc.js.map +1 -1
- package/lib-es/network/thirdweb.d.ts +21 -0
- package/lib-es/network/thirdweb.d.ts.map +1 -0
- package/lib-es/network/thirdweb.js +66 -0
- package/lib-es/network/thirdweb.js.map +1 -0
- package/lib-es/network/utils.d.ts +4 -1
- package/lib-es/network/utils.d.ts.map +1 -1
- package/lib-es/network/utils.js +49 -0
- package/lib-es/network/utils.js.map +1 -1
- package/lib-es/test/bridgeDatasetTest.d.ts.map +1 -1
- package/lib-es/test/bridgeDatasetTest.js +4 -4
- package/lib-es/test/bridgeDatasetTest.js.map +1 -1
- package/lib-es/test/fixtures/account.fixture.js +2 -2
- package/lib-es/test/fixtures/account.fixture.js.map +1 -1
- package/lib-es/test/fixtures/common.fixture.d.ts +12 -0
- package/lib-es/test/fixtures/common.fixture.d.ts.map +1 -0
- package/lib-es/test/fixtures/common.fixture.js +57 -0
- package/lib-es/test/fixtures/common.fixture.js.map +1 -0
- package/lib-es/test/fixtures/currency.fixture.d.ts +3 -1
- package/lib-es/test/fixtures/currency.fixture.d.ts.map +1 -1
- package/lib-es/test/fixtures/currency.fixture.js +59 -14
- package/lib-es/test/fixtures/currency.fixture.js.map +1 -1
- package/lib-es/test/fixtures/mirror.fixture.d.ts +3 -1
- package/lib-es/test/fixtures/mirror.fixture.d.ts.map +1 -1
- package/lib-es/test/fixtures/mirror.fixture.js +9 -0
- package/lib-es/test/fixtures/mirror.fixture.js.map +1 -1
- package/lib-es/test/fixtures/thirdweb.fixture.d.ts +3 -0
- package/lib-es/test/fixtures/thirdweb.fixture.d.ts.map +1 -0
- package/lib-es/test/fixtures/thirdweb.fixture.js +30 -0
- package/lib-es/test/fixtures/thirdweb.fixture.js.map +1 -0
- package/lib-es/transaction.d.ts.map +1 -1
- package/lib-es/transaction.js +2 -0
- package/lib-es/transaction.js.map +1 -1
- package/lib-es/types/alpaca.d.ts +5 -1
- package/lib-es/types/alpaca.d.ts.map +1 -1
- package/lib-es/types/bridge.d.ts +6 -1
- package/lib-es/types/bridge.d.ts.map +1 -1
- package/lib-es/types/index.d.ts +2 -0
- package/lib-es/types/index.d.ts.map +1 -1
- package/lib-es/types/index.js +2 -0
- package/lib-es/types/index.js.map +1 -1
- package/lib-es/types/logic.d.ts +39 -0
- package/lib-es/types/logic.d.ts.map +1 -0
- package/lib-es/types/logic.js +2 -0
- package/lib-es/types/logic.js.map +1 -0
- package/lib-es/types/mirror.d.ts +29 -0
- package/lib-es/types/mirror.d.ts.map +1 -1
- package/lib-es/types/thirdweb.d.ts +34 -0
- package/lib-es/types/thirdweb.d.ts.map +1 -0
- package/lib-es/types/thirdweb.js +2 -0
- package/lib-es/types/thirdweb.js.map +1 -0
- package/package.json +9 -8
- package/src/api/index.integ.test.ts +11 -8
- package/src/api/index.ts +10 -3
- package/src/bridge/broadcast.ts +2 -2
- package/src/bridge/buildOptimisticOperation.integration.test.ts +70 -19
- package/src/bridge/buildOptimisticOperation.ts +98 -20
- package/src/bridge/estimateMaxSpendable.ts +5 -5
- package/src/bridge/getTransactionStatus.test.ts +57 -12
- package/src/bridge/getTransactionStatus.ts +88 -15
- package/src/bridge/index.ts +2 -2
- package/src/bridge/js-estimateMaxSpendable.integration.test.ts +12 -9
- package/src/bridge/prepareTransaction.test.ts +3 -1
- package/src/bridge/prepareTransaction.ts +45 -10
- package/src/bridge/signOperation.ts +23 -5
- package/src/bridge/synchronisation.test.ts +67 -0
- package/src/bridge/synchronisation.ts +114 -34
- package/src/bridge/utils.integration.test.ts +486 -180
- package/src/bridge/utils.test.ts +404 -0
- package/src/bridge/utils.ts +330 -27
- package/src/constants.ts +47 -2
- package/src/deviceTransactionConfig.ts +10 -1
- package/src/errors.ts +3 -0
- package/src/logic/craftTransaction.test.ts +49 -9
- package/src/logic/craftTransaction.ts +76 -11
- package/src/logic/estimateFees.test.ts +180 -31
- package/src/logic/estimateFees.ts +68 -7
- package/src/logic/getAssetFromToken.test.ts +2 -2
- package/src/logic/getBalance.test.ts +18 -57
- package/src/logic/getTokenFromAsset.test.ts +2 -2
- package/src/logic/listOperations.ts +9 -5
- package/src/logic/utils.test.ts +157 -69
- package/src/logic/utils.ts +75 -13
- package/src/network/api.test.ts +211 -3
- package/src/network/api.ts +118 -24
- package/src/network/rpc.test.ts +1 -0
- package/src/network/rpc.ts +1 -0
- package/src/network/thirdweb.test.ts +188 -0
- package/src/network/thirdweb.ts +101 -0
- package/src/network/utils.test.ts +364 -164
- package/src/network/utils.ts +83 -1
- package/src/test/bridgeDatasetTest.ts +4 -5
- package/src/test/fixtures/account.fixture.ts +2 -2
- package/src/test/fixtures/common.fixture.ts +74 -0
- package/src/test/fixtures/currency.fixture.ts +66 -14
- package/src/test/fixtures/mirror.fixture.ts +23 -1
- package/src/test/fixtures/thirdweb.fixture.ts +33 -0
- package/src/transaction.ts +2 -0
- package/src/types/alpaca.ts +8 -1
- package/src/types/bridge.ts +6 -1
- package/src/types/index.ts +2 -0
- package/src/types/logic.ts +44 -0
- package/src/types/mirror.ts +35 -0
- package/src/types/thirdweb.ts +36 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { getTokenFromAsset } from "./getTokenFromAsset";
|
|
2
|
-
import { getMockedCurrency,
|
|
2
|
+
import { getMockedCurrency, getMockedHTSTokenCurrency } from "../test/fixtures/currency.fixture";
|
|
3
3
|
import { setupMockCryptoAssetsStore } from "@ledgerhq/cryptoassets/cal-client/test-helpers";
|
|
4
4
|
|
|
5
5
|
setupMockCryptoAssetsStore();
|
|
6
6
|
|
|
7
7
|
describe("getTokenFromAsset", () => {
|
|
8
8
|
const mockCurrency = getMockedCurrency();
|
|
9
|
-
const mockToken =
|
|
9
|
+
const mockToken = getMockedHTSTokenCurrency();
|
|
10
10
|
|
|
11
11
|
beforeEach(() => {
|
|
12
12
|
jest.clearAllMocks();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import BigNumber from "bignumber.js";
|
|
2
|
-
import type { Operation } from "@ledgerhq/types-live";
|
|
2
|
+
import type { Operation, OperationType } from "@ledgerhq/types-live";
|
|
3
3
|
import type { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
|
|
4
4
|
import type { Pagination } from "@ledgerhq/coin-framework/api/types";
|
|
5
5
|
import { getCryptoAssetsStore } from "@ledgerhq/cryptoassets/state";
|
|
@@ -10,6 +10,11 @@ import { parseTransfers } from "../network/utils";
|
|
|
10
10
|
import type { HederaMirrorToken, HederaMirrorTransaction, HederaOperationExtra } from "../types";
|
|
11
11
|
import { base64ToUrlSafeBase64, getMemoFromBase64, getSyntheticBlock } from "./utils";
|
|
12
12
|
|
|
13
|
+
const txNameToCustomOperationType: Record<string, OperationType> = {
|
|
14
|
+
TOKENASSOCIATE: "ASSOCIATE_TOKEN",
|
|
15
|
+
CONTRACTCALL: "CONTRACT_CALL",
|
|
16
|
+
};
|
|
17
|
+
|
|
13
18
|
function getCommonOperationData(
|
|
14
19
|
rawTx: HederaMirrorTransaction,
|
|
15
20
|
useEncodedHash: boolean,
|
|
@@ -26,6 +31,7 @@ function getCommonOperationData(
|
|
|
26
31
|
const extra: HederaOperationExtra = {
|
|
27
32
|
pagingToken: rawTx.consensus_timestamp,
|
|
28
33
|
consensusTimestamp: rawTx.consensus_timestamp,
|
|
34
|
+
transactionId: rawTx.transaction_id,
|
|
29
35
|
...(memo && { memo }),
|
|
30
36
|
};
|
|
31
37
|
|
|
@@ -134,14 +140,12 @@ function processTransfers({
|
|
|
134
140
|
const { type, value, senders, recipients } = parseTransfers(transfers, address);
|
|
135
141
|
const { hash, fee, timestamp, blockHeight, blockHash, hasFailed } = commonData;
|
|
136
142
|
const extra = { ...commonData.extra };
|
|
137
|
-
|
|
143
|
+
const operationType = txNameToCustomOperationType[rawTx.name] ?? type;
|
|
138
144
|
|
|
139
145
|
// try to enrich ASSOCIATE_TOKEN operation with extra.associatedTokenId
|
|
140
146
|
// this value is used by custom OperationDetails components in Hedera family
|
|
141
147
|
// accounts or contracts must first associate with an HTS token before they can receive or send that token; without association, token transfers fail
|
|
142
|
-
if (
|
|
143
|
-
operationType = "ASSOCIATE_TOKEN";
|
|
144
|
-
|
|
148
|
+
if (operationType === "ASSOCIATE_TOKEN") {
|
|
145
149
|
const relatedMirrorToken = mirrorTokens.find(t => {
|
|
146
150
|
return t.created_timestamp === rawTx.consensus_timestamp;
|
|
147
151
|
});
|
package/src/logic/utils.test.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import BigNumber from "bignumber.js";
|
|
2
2
|
import { createHash } from "crypto";
|
|
3
|
-
import { Transaction } from "@hashgraph/sdk";
|
|
3
|
+
import { Transaction, TransactionId } from "@hashgraph/sdk";
|
|
4
4
|
import type { AssetInfo } from "@ledgerhq/coin-framework/api/types";
|
|
5
5
|
import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";
|
|
6
6
|
import { InvalidAddress } from "@ledgerhq/errors";
|
|
7
7
|
import { HEDERA_TRANSACTION_MODES, SYNTHETIC_BLOCK_WINDOW_SECONDS } from "../constants";
|
|
8
|
+
import { HederaRecipientInvalidChecksum } from "../errors";
|
|
8
9
|
import { apiClient } from "../network/api";
|
|
10
|
+
import { rpcClient } from "../network/rpc";
|
|
9
11
|
import { getMockedOperation } from "../test/fixtures/operation.fixture";
|
|
10
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
getMockedERC20TokenCurrency,
|
|
14
|
+
getMockedHTSTokenCurrency,
|
|
15
|
+
} from "../test/fixtures/currency.fixture";
|
|
11
16
|
import { getMockedAccount, getMockedTokenAccount } from "../test/fixtures/account.fixture";
|
|
12
17
|
import {
|
|
13
18
|
serializeSignature,
|
|
@@ -25,16 +30,22 @@ import {
|
|
|
25
30
|
checkAccountTokenAssociationStatus,
|
|
26
31
|
safeParseAccountId,
|
|
27
32
|
getSyntheticBlock,
|
|
33
|
+
fromEVMAddress,
|
|
34
|
+
toEVMAddress,
|
|
35
|
+
formatTransactionId,
|
|
28
36
|
} from "./utils";
|
|
29
|
-
import { HederaRecipientInvalidChecksum } from "../errors";
|
|
30
37
|
|
|
31
38
|
jest.mock("../network/api");
|
|
32
39
|
|
|
33
|
-
describe("utils", () => {
|
|
40
|
+
describe("logic utils", () => {
|
|
34
41
|
beforeEach(() => {
|
|
35
42
|
jest.clearAllMocks();
|
|
36
43
|
});
|
|
37
44
|
|
|
45
|
+
afterAll(() => {
|
|
46
|
+
rpcClient._resetInstance();
|
|
47
|
+
});
|
|
48
|
+
|
|
38
49
|
describe("signature serialization", () => {
|
|
39
50
|
it("should serialize a signature to base64", () => {
|
|
40
51
|
const signature = new Uint8Array([1, 2, 3, 4, 5]);
|
|
@@ -84,49 +95,49 @@ describe("utils", () => {
|
|
|
84
95
|
expect(deserialized).toBe(mockTransaction);
|
|
85
96
|
});
|
|
86
97
|
});
|
|
87
|
-
});
|
|
88
98
|
|
|
89
|
-
describe("getOperationValue", () => {
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
describe("getOperationValue", () => {
|
|
100
|
+
const nativeAsset: AssetInfo = { type: "native" };
|
|
101
|
+
const tokenAsset: AssetInfo = { type: "hts", assetReference: "0.0.1234" };
|
|
92
102
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
103
|
+
it("should return 0 for FEES operations", () => {
|
|
104
|
+
const operation = getMockedOperation({
|
|
105
|
+
type: "FEES",
|
|
106
|
+
value: BigNumber(0),
|
|
107
|
+
fee: BigNumber(100),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(getOperationValue({ asset: nativeAsset, operation })).toBe(BigInt(0));
|
|
111
|
+
expect(getOperationValue({ asset: tokenAsset, operation })).toBe(BigInt(0));
|
|
98
112
|
});
|
|
99
113
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
114
|
+
it("should substract fee from value for native OUT operations", () => {
|
|
115
|
+
const operation = getMockedOperation({
|
|
116
|
+
type: "OUT",
|
|
117
|
+
value: BigNumber(1000),
|
|
118
|
+
fee: BigNumber(100),
|
|
119
|
+
});
|
|
103
120
|
|
|
104
|
-
|
|
105
|
-
const operation = getMockedOperation({
|
|
106
|
-
type: "OUT",
|
|
107
|
-
value: BigNumber(1000),
|
|
108
|
-
fee: BigNumber(100),
|
|
121
|
+
expect(getOperationValue({ asset: nativeAsset, operation })).toBe(BigInt(900));
|
|
109
122
|
});
|
|
110
123
|
|
|
111
|
-
|
|
112
|
-
|
|
124
|
+
it("should return value for other operations", () => {
|
|
125
|
+
const operationOut = getMockedOperation({
|
|
126
|
+
type: "OUT",
|
|
127
|
+
value: BigNumber(500),
|
|
128
|
+
fee: BigNumber(20),
|
|
129
|
+
});
|
|
113
130
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
});
|
|
131
|
+
const operationIn = getMockedOperation({
|
|
132
|
+
type: "IN",
|
|
133
|
+
value: BigNumber(800),
|
|
134
|
+
fee: BigNumber(30),
|
|
135
|
+
});
|
|
120
136
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
fee: BigNumber(30),
|
|
137
|
+
expect(getOperationValue({ asset: tokenAsset, operation: operationOut })).toBe(BigInt(500));
|
|
138
|
+
expect(getOperationValue({ asset: tokenAsset, operation: operationIn })).toBe(BigInt(800));
|
|
139
|
+
expect(getOperationValue({ asset: nativeAsset, operation: operationIn })).toBe(BigInt(800));
|
|
125
140
|
});
|
|
126
|
-
|
|
127
|
-
expect(getOperationValue({ asset: tokenAsset, operation: operationOut })).toBe(BigInt(500));
|
|
128
|
-
expect(getOperationValue({ asset: tokenAsset, operation: operationIn })).toBe(BigInt(800));
|
|
129
|
-
expect(getOperationValue({ asset: nativeAsset, operation: operationIn })).toBe(BigInt(800));
|
|
130
141
|
});
|
|
131
142
|
|
|
132
143
|
describe("getMemoFromBase64", () => {
|
|
@@ -157,7 +168,7 @@ describe("getOperationValue", () => {
|
|
|
157
168
|
});
|
|
158
169
|
|
|
159
170
|
describe("getTransactionExplorer", () => {
|
|
160
|
-
|
|
171
|
+
it("Tx explorer URL is converted from hash to consensus timestamp", async () => {
|
|
161
172
|
const explorerView = getCryptoCurrencyById("hedera").explorerViews[0];
|
|
162
173
|
expect(explorerView).toEqual({
|
|
163
174
|
tx: expect.any(String),
|
|
@@ -172,7 +183,7 @@ describe("getOperationValue", () => {
|
|
|
172
183
|
expect(newUrl).toBe("https://hashscan.io/mainnet/transaction/1.2.3.4");
|
|
173
184
|
});
|
|
174
185
|
|
|
175
|
-
|
|
186
|
+
it("Tx explorer URL is based on transaction id if consensus timestamp is not available", async () => {
|
|
176
187
|
const explorerView = getCryptoCurrencyById("hedera").explorerViews[0];
|
|
177
188
|
expect(explorerView).toEqual({
|
|
178
189
|
tx: expect.any(String),
|
|
@@ -189,7 +200,7 @@ describe("getOperationValue", () => {
|
|
|
189
200
|
});
|
|
190
201
|
|
|
191
202
|
describe("isTokenAssociateTransaction", () => {
|
|
192
|
-
|
|
203
|
+
it("returns correct value based on tx.properties", () => {
|
|
193
204
|
expect(
|
|
194
205
|
isTokenAssociateTransaction({ mode: HEDERA_TRANSACTION_MODES.TokenAssociate } as any),
|
|
195
206
|
).toBe(true);
|
|
@@ -201,7 +212,7 @@ describe("getOperationValue", () => {
|
|
|
201
212
|
});
|
|
202
213
|
|
|
203
214
|
describe("isAutoTokenAssociationEnabled", () => {
|
|
204
|
-
|
|
215
|
+
it("returns value based on isAutoTokenAssociationEnabled flag", () => {
|
|
205
216
|
expect(
|
|
206
217
|
isAutoTokenAssociationEnabled({
|
|
207
218
|
hederaResources: { isAutoTokenAssociationEnabled: true },
|
|
@@ -219,16 +230,16 @@ describe("getOperationValue", () => {
|
|
|
219
230
|
});
|
|
220
231
|
|
|
221
232
|
describe("isTokenAssociationRequired", () => {
|
|
222
|
-
|
|
223
|
-
const mockedTokenCurrency =
|
|
233
|
+
it("should return false if token is already associated (token account exists)", () => {
|
|
234
|
+
const mockedTokenCurrency = getMockedHTSTokenCurrency();
|
|
224
235
|
const mockedTokenAccount = getMockedTokenAccount(mockedTokenCurrency);
|
|
225
236
|
const mockedAccount = getMockedAccount({ subAccounts: [mockedTokenAccount] });
|
|
226
237
|
|
|
227
238
|
expect(isTokenAssociationRequired(mockedAccount, mockedTokenCurrency)).toBe(false);
|
|
228
239
|
});
|
|
229
240
|
|
|
230
|
-
|
|
231
|
-
const mockedTokenCurrency =
|
|
241
|
+
it("should return false if auto token associations are enabled", () => {
|
|
242
|
+
const mockedTokenCurrency = getMockedHTSTokenCurrency();
|
|
232
243
|
const mockedAccount = getMockedAccount({
|
|
233
244
|
subAccounts: [],
|
|
234
245
|
hederaResources: {
|
|
@@ -240,21 +251,28 @@ describe("getOperationValue", () => {
|
|
|
240
251
|
expect(isTokenAssociationRequired(mockedAccount, mockedTokenCurrency)).toBe(false);
|
|
241
252
|
});
|
|
242
253
|
|
|
243
|
-
|
|
244
|
-
const mockedTokenCurrency =
|
|
254
|
+
it("should return true if token is not associated and auto associations are disabled", () => {
|
|
255
|
+
const mockedTokenCurrency = getMockedHTSTokenCurrency();
|
|
245
256
|
const mockedAccount = getMockedAccount({ subAccounts: [] });
|
|
246
257
|
|
|
247
258
|
expect(isTokenAssociationRequired(mockedAccount, mockedTokenCurrency)).toBe(true);
|
|
248
259
|
});
|
|
249
260
|
|
|
250
|
-
|
|
261
|
+
it("should return false for erc20 token", () => {
|
|
262
|
+
const mockedTokenCurrency = getMockedERC20TokenCurrency();
|
|
263
|
+
const mockedAccount = getMockedAccount({ subAccounts: [] });
|
|
264
|
+
|
|
265
|
+
expect(isTokenAssociationRequired(mockedAccount, mockedTokenCurrency)).toBe(false);
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("should return false if token is undefined", () => {
|
|
251
269
|
const mockedAccount = getMockedAccount({ subAccounts: [] });
|
|
252
270
|
|
|
253
271
|
expect(isTokenAssociationRequired(mockedAccount, undefined)).toBe(false);
|
|
254
272
|
});
|
|
255
273
|
|
|
256
|
-
|
|
257
|
-
const mockedTokenCurrency =
|
|
274
|
+
it("should return false for legacy accounts without subAccounts or hederaResources", () => {
|
|
275
|
+
const mockedTokenCurrency = getMockedHTSTokenCurrency();
|
|
258
276
|
const mockedAccount = getMockedAccount();
|
|
259
277
|
|
|
260
278
|
delete mockedAccount.subAccounts;
|
|
@@ -265,7 +283,7 @@ describe("getOperationValue", () => {
|
|
|
265
283
|
});
|
|
266
284
|
|
|
267
285
|
describe("isValidExtra", () => {
|
|
268
|
-
|
|
286
|
+
it("returns true for object and false for invalid types", () => {
|
|
269
287
|
expect(isValidExtra({ some: "value" })).toBe(true);
|
|
270
288
|
expect(isValidExtra(null)).toBe(false);
|
|
271
289
|
expect(isValidExtra(undefined)).toBe(false);
|
|
@@ -276,7 +294,7 @@ describe("getOperationValue", () => {
|
|
|
276
294
|
});
|
|
277
295
|
|
|
278
296
|
describe("sendRecipientCanNext", () => {
|
|
279
|
-
|
|
297
|
+
it("handles association warnings", () => {
|
|
280
298
|
expect(sendRecipientCanNext({ warnings: {} } as any)).toBe(true);
|
|
281
299
|
expect(sendRecipientCanNext({ warnings: { missingAssociation: new Error() } } as any)).toBe(
|
|
282
300
|
false,
|
|
@@ -289,15 +307,19 @@ describe("getOperationValue", () => {
|
|
|
289
307
|
|
|
290
308
|
describe("checkAccountTokenAssociationStatus", () => {
|
|
291
309
|
const accountId = "0.0.1234";
|
|
292
|
-
const
|
|
310
|
+
const htsToken = getMockedHTSTokenCurrency({ contractAddress: "0.0.1234", tokenType: "hts" });
|
|
311
|
+
const erc20Token = getMockedHTSTokenCurrency({
|
|
312
|
+
contractAddress: "0.0.4321",
|
|
313
|
+
tokenType: "erc20",
|
|
314
|
+
});
|
|
293
315
|
|
|
294
316
|
beforeEach(() => {
|
|
295
317
|
jest.clearAllMocks();
|
|
296
318
|
// reset LRU cache to make sure all tests receive correct mocks from mockedGetAccount
|
|
297
|
-
checkAccountTokenAssociationStatus.clear(`${accountId}-${
|
|
319
|
+
checkAccountTokenAssociationStatus.clear(`${accountId}-${htsToken.contractAddress}`);
|
|
298
320
|
});
|
|
299
321
|
|
|
300
|
-
|
|
322
|
+
it("returns true if max_automatic_token_associations === -1", async () => {
|
|
301
323
|
(apiClient.getAccount as jest.Mock).mockResolvedValueOnce({
|
|
302
324
|
account: accountId,
|
|
303
325
|
max_automatic_token_associations: -1,
|
|
@@ -308,41 +330,47 @@ describe("getOperationValue", () => {
|
|
|
308
330
|
},
|
|
309
331
|
});
|
|
310
332
|
|
|
311
|
-
const result = await checkAccountTokenAssociationStatus(accountId,
|
|
333
|
+
const result = await checkAccountTokenAssociationStatus(accountId, htsToken);
|
|
312
334
|
expect(result).toBe(true);
|
|
313
335
|
});
|
|
314
336
|
|
|
315
|
-
|
|
337
|
+
it("returns true if token is already associated", async () => {
|
|
316
338
|
(apiClient.getAccount as jest.Mock).mockResolvedValueOnce({
|
|
317
339
|
account: accountId,
|
|
318
340
|
max_automatic_token_associations: 0,
|
|
319
341
|
balance: {
|
|
320
342
|
balance: 1,
|
|
321
343
|
timestamp: "",
|
|
322
|
-
tokens: [{ token_id:
|
|
344
|
+
tokens: [{ token_id: htsToken.contractAddress, balance: 1 }],
|
|
323
345
|
},
|
|
324
346
|
});
|
|
325
347
|
|
|
326
|
-
const result = await checkAccountTokenAssociationStatus(accountId,
|
|
348
|
+
const result = await checkAccountTokenAssociationStatus(accountId, htsToken);
|
|
327
349
|
expect(result).toBe(true);
|
|
328
350
|
});
|
|
329
351
|
|
|
330
|
-
|
|
352
|
+
it("returns false if token is not associated", async () => {
|
|
331
353
|
(apiClient.getAccount as jest.Mock).mockResolvedValueOnce({
|
|
332
354
|
account: accountId,
|
|
333
355
|
max_automatic_token_associations: 0,
|
|
334
356
|
balance: {
|
|
335
357
|
balance: 1,
|
|
336
358
|
timestamp: "",
|
|
337
|
-
tokens: [{ token_id: "0.
|
|
359
|
+
tokens: [{ token_id: "0.1234", balance: 1 }],
|
|
338
360
|
},
|
|
339
361
|
});
|
|
340
362
|
|
|
341
|
-
const result = await checkAccountTokenAssociationStatus(accountId,
|
|
363
|
+
const result = await checkAccountTokenAssociationStatus(accountId, htsToken);
|
|
342
364
|
expect(result).toBe(false);
|
|
343
365
|
});
|
|
344
366
|
|
|
345
|
-
|
|
367
|
+
it("returns true for erc20 tokens", async () => {
|
|
368
|
+
const result = await checkAccountTokenAssociationStatus(accountId, erc20Token);
|
|
369
|
+
expect(apiClient.getAccount as jest.Mock).not.toHaveBeenCalled();
|
|
370
|
+
expect(result).toBe(true);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("supports addresses with checksum", async () => {
|
|
346
374
|
const addressWithChecksum = "0.0.9124531-xrxlv";
|
|
347
375
|
|
|
348
376
|
(apiClient.getAccount as jest.Mock).mockResolvedValueOnce({
|
|
@@ -351,18 +379,18 @@ describe("getOperationValue", () => {
|
|
|
351
379
|
balance: {
|
|
352
380
|
balance: 1,
|
|
353
381
|
timestamp: "",
|
|
354
|
-
tokens: [{ token_id:
|
|
382
|
+
tokens: [{ token_id: htsToken.contractAddress, balance: 1 }],
|
|
355
383
|
},
|
|
356
384
|
});
|
|
357
385
|
|
|
358
|
-
await checkAccountTokenAssociationStatus(addressWithChecksum,
|
|
386
|
+
await checkAccountTokenAssociationStatus(addressWithChecksum, htsToken);
|
|
359
387
|
expect(apiClient.getAccount).toHaveBeenCalledTimes(1);
|
|
360
388
|
expect(apiClient.getAccount).toHaveBeenCalledWith("0.0.9124531");
|
|
361
389
|
});
|
|
362
390
|
});
|
|
363
391
|
|
|
364
392
|
describe("safeParseAccountId", () => {
|
|
365
|
-
|
|
393
|
+
it("returns account id and no checksum for valid address without checksum", () => {
|
|
366
394
|
const [error, result] = safeParseAccountId("0.0.9124531");
|
|
367
395
|
|
|
368
396
|
expect(error).toBeNull();
|
|
@@ -370,7 +398,7 @@ describe("getOperationValue", () => {
|
|
|
370
398
|
expect(result?.checksum).toBeNull();
|
|
371
399
|
});
|
|
372
400
|
|
|
373
|
-
|
|
401
|
+
it("returns account id and checksum for valid address with correct checksum", () => {
|
|
374
402
|
const [error, result] = safeParseAccountId("0.0.9124531-xrxlv");
|
|
375
403
|
|
|
376
404
|
expect(error).toBeNull();
|
|
@@ -378,14 +406,14 @@ describe("getOperationValue", () => {
|
|
|
378
406
|
expect(result?.checksum).toBe("xrxlv");
|
|
379
407
|
});
|
|
380
408
|
|
|
381
|
-
|
|
409
|
+
it("returns error for valid address with incorrect checksum", () => {
|
|
382
410
|
const [error, accountId] = safeParseAccountId("0.0.9124531-invld");
|
|
383
411
|
|
|
384
412
|
expect(error).toBeInstanceOf(HederaRecipientInvalidChecksum);
|
|
385
413
|
expect(accountId).toBeNull();
|
|
386
414
|
});
|
|
387
415
|
|
|
388
|
-
|
|
416
|
+
it("returns error for invalid address format", () => {
|
|
389
417
|
const [error, accountId] = safeParseAccountId("not-a-valid-address");
|
|
390
418
|
|
|
391
419
|
expect(error).toBeInstanceOf(InvalidAddress);
|
|
@@ -429,4 +457,64 @@ describe("getOperationValue", () => {
|
|
|
429
457
|
expect(() => getSyntheticBlock("")).toThrow();
|
|
430
458
|
});
|
|
431
459
|
});
|
|
460
|
+
|
|
461
|
+
describe("formatTransactionId", () => {
|
|
462
|
+
it("converts SDK TransactionId format to mirror node format", () => {
|
|
463
|
+
const mockTransactionId = {
|
|
464
|
+
toString: () => "0.0.8835924@1759825731.231952875",
|
|
465
|
+
} as TransactionId;
|
|
466
|
+
|
|
467
|
+
const result = formatTransactionId(mockTransactionId);
|
|
468
|
+
expect(result).toBe("0.0.8835924-1759825731-231952875");
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
it("handles different account ID formats", () => {
|
|
472
|
+
const mockTransactionId = {
|
|
473
|
+
toString: () => "0.0.1@1234567890.987654321",
|
|
474
|
+
} as TransactionId;
|
|
475
|
+
|
|
476
|
+
const result = formatTransactionId(mockTransactionId);
|
|
477
|
+
expect(result).toBe("0.0.1-1234567890-987654321");
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
describe("toEVMAddress", () => {
|
|
482
|
+
it("returns correct EVM address for valid Hedera account ID", () => {
|
|
483
|
+
const evmAddress = toEVMAddress("0.0.12345");
|
|
484
|
+
expect(evmAddress).toBe("0x0000000000000000000000000000000000003039");
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it("returns null for invalid Hedera account ID", () => {
|
|
488
|
+
const evmAddress = toEVMAddress("invalid_account_id");
|
|
489
|
+
expect(evmAddress).toBeNull();
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
describe("fromEVMAddress", () => {
|
|
494
|
+
it("should convert a long-zero EVM address to Hedera account ID", () => {
|
|
495
|
+
const evmAddress = "0x00000000000000000000000000000000008b3ab3";
|
|
496
|
+
const result = fromEVMAddress(evmAddress);
|
|
497
|
+
expect(result).toBe("0.0.9124531");
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it("should return null for non-long-zero EVM address", () => {
|
|
501
|
+
const evmAddress = "0xae2e616828973ec543bbce40cf640c012c5a3805";
|
|
502
|
+
const result = fromEVMAddress(evmAddress, 0, 0);
|
|
503
|
+
expect(result).toBeNull();
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
it("should handle custom shard and realm values", () => {
|
|
507
|
+
const evmAddress = "0x0000000000000000000000000000000000000064";
|
|
508
|
+
const result = fromEVMAddress(evmAddress, 1, 2);
|
|
509
|
+
expect(result).toBe("1.2.100");
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
it("should return null for invalid EVM addresses", () => {
|
|
513
|
+
expect(fromEVMAddress("not-an-address")).toBeNull();
|
|
514
|
+
expect(fromEVMAddress("0xInvalid")).toBeNull();
|
|
515
|
+
expect(fromEVMAddress("")).toBeNull();
|
|
516
|
+
expect(fromEVMAddress("1234567890")).toBeNull();
|
|
517
|
+
expect(fromEVMAddress(undefined as unknown as string)).toBeNull();
|
|
518
|
+
});
|
|
519
|
+
});
|
|
432
520
|
});
|
package/src/logic/utils.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import BigNumber from "bignumber.js";
|
|
2
2
|
import { createHash } from "crypto";
|
|
3
3
|
import invariant from "invariant";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
AccountId,
|
|
6
|
+
EntityIdHelper,
|
|
7
|
+
Transaction as HederaSDKTransaction,
|
|
8
|
+
TransactionId,
|
|
9
|
+
} from "@hashgraph/sdk";
|
|
5
10
|
import { AssetInfo, TransactionIntent } from "@ledgerhq/coin-framework/api/types";
|
|
6
11
|
import { findCryptoCurrencyById } from "@ledgerhq/cryptoassets/currencies";
|
|
7
12
|
import { getFiatCurrencyByTicker } from "@ledgerhq/cryptoassets/fiats";
|
|
@@ -75,10 +80,14 @@ export const mapIntentToSDKOperation = (txIntent: TransactionIntent) => {
|
|
|
75
80
|
return HEDERA_OPERATION_TYPES.TokenAssociate;
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
if (txIntent.type === HEDERA_TRANSACTION_MODES.Send && txIntent.asset.type
|
|
83
|
+
if (txIntent.type === HEDERA_TRANSACTION_MODES.Send && txIntent.asset.type === "hts") {
|
|
79
84
|
return HEDERA_OPERATION_TYPES.TokenTransfer;
|
|
80
85
|
}
|
|
81
86
|
|
|
87
|
+
if (txIntent.type === HEDERA_TRANSACTION_MODES.Send && txIntent.asset.type === "erc20") {
|
|
88
|
+
return HEDERA_OPERATION_TYPES.ContractCall;
|
|
89
|
+
}
|
|
90
|
+
|
|
82
91
|
return HEDERA_OPERATION_TYPES.CryptoTransfer;
|
|
83
92
|
};
|
|
84
93
|
|
|
@@ -127,6 +136,10 @@ export const isTokenAssociationRequired = (
|
|
|
127
136
|
account: AccountLike,
|
|
128
137
|
token: TokenCurrency | null | undefined,
|
|
129
138
|
) => {
|
|
139
|
+
if (token?.tokenType !== "hts") {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
130
143
|
const subAccounts = !!account && "subAccounts" in account ? account.subAccounts ?? [] : [];
|
|
131
144
|
const isTokenAssociated = subAccounts.some(item => item.token.id === token?.id);
|
|
132
145
|
|
|
@@ -170,7 +183,11 @@ export const getCurrencyToUSDRate = makeLRUCache(
|
|
|
170
183
|
);
|
|
171
184
|
|
|
172
185
|
export const checkAccountTokenAssociationStatus = makeLRUCache(
|
|
173
|
-
async (address: string,
|
|
186
|
+
async (address: string, token: TokenCurrency) => {
|
|
187
|
+
if (token.tokenType !== "hts") {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
|
|
174
191
|
const [parsingError, parsingResult] = safeParseAccountId(address);
|
|
175
192
|
|
|
176
193
|
if (parsingError) {
|
|
@@ -185,13 +202,13 @@ export const checkAccountTokenAssociationStatus = makeLRUCache(
|
|
|
185
202
|
return true;
|
|
186
203
|
}
|
|
187
204
|
|
|
188
|
-
const isTokenAssociated = mirrorAccount.balance.tokens.some(
|
|
189
|
-
return
|
|
205
|
+
const isTokenAssociated = mirrorAccount.balance.tokens.some(t => {
|
|
206
|
+
return t.token_id === token.contractAddress;
|
|
190
207
|
});
|
|
191
208
|
|
|
192
209
|
return isTokenAssociated;
|
|
193
210
|
},
|
|
194
|
-
(accountId,
|
|
211
|
+
(accountId, token) => `${accountId}-${token.contractAddress}`,
|
|
195
212
|
seconds(30),
|
|
196
213
|
);
|
|
197
214
|
|
|
@@ -203,16 +220,11 @@ export const safeParseAccountId = (
|
|
|
203
220
|
|
|
204
221
|
try {
|
|
205
222
|
const accountId = AccountId.fromString(address);
|
|
223
|
+
const checksum = EntityIdHelper.fromString(address).checksum ?? null;
|
|
206
224
|
|
|
207
|
-
// verify checksum if present
|
|
208
|
-
// FIXME: migrate to EntityIdHelper methods once SDK is upgraded:
|
|
209
|
-
// https://github.com/hiero-ledger/hiero-sdk-js/blob/main/src/EntityIdHelper.js#L197
|
|
210
|
-
// https://github.com/hiero-ledger/hiero-sdk-js/blob/main/src/EntityIdHelper.js#L446
|
|
211
|
-
const checksum: string | null = address.split("-")[1] ?? null;
|
|
212
225
|
if (checksum) {
|
|
213
226
|
const client = rpcClient.getInstance();
|
|
214
|
-
const
|
|
215
|
-
const expectedChecksum = recipientWithChecksum.split("-")[1];
|
|
227
|
+
const expectedChecksum = accountId.toStringWithChecksum(client).split("-")[1];
|
|
216
228
|
|
|
217
229
|
if (checksum !== expectedChecksum) {
|
|
218
230
|
return [new HederaRecipientInvalidChecksum(), null];
|
|
@@ -253,3 +265,53 @@ export function getSyntheticBlock(
|
|
|
253
265
|
|
|
254
266
|
return { blockHeight, blockHash, blockTime };
|
|
255
267
|
}
|
|
268
|
+
|
|
269
|
+
export const formatTransactionId = (transactionId: TransactionId): string => {
|
|
270
|
+
const [accountId, timestamp] = transactionId.toString().split("@");
|
|
271
|
+
const [secs, nanos] = timestamp.split(".");
|
|
272
|
+
|
|
273
|
+
return `${accountId}-${secs}-${nanos}`;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Converts a Hedera account ID (e.g. "0.0.1234") into its corresponding EVM address in hexadecimal format.
|
|
278
|
+
* If the conversion fails, it returns null.
|
|
279
|
+
*
|
|
280
|
+
* @param address - Hedera account ID in the format `shard.realm.num`
|
|
281
|
+
* @returns the long-zero EVM address (`0x...`) or null if conversion fails
|
|
282
|
+
*/
|
|
283
|
+
export const toEVMAddress = (accountId: string) => {
|
|
284
|
+
try {
|
|
285
|
+
const evmAddress = "0x" + AccountId.fromString(accountId).toEvmAddress();
|
|
286
|
+
return evmAddress;
|
|
287
|
+
} catch {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Converts EVM address in hexadecimal format to its corresponding Hedera account ID.
|
|
294
|
+
* Only long-zero addresses can be mathematically converted back to account IDs.
|
|
295
|
+
* Non-long-zero addresses support would require mirror node call and is not needed for now
|
|
296
|
+
* Uses shard 0 and realm 0 by default for the conversion.
|
|
297
|
+
* If the conversion fails, it returns null.
|
|
298
|
+
*
|
|
299
|
+
* @param evmAddress - EVM address in hexadecimal format (should start with '0x')
|
|
300
|
+
* @param shard - Optional shard ID (defaults to 0)
|
|
301
|
+
* @param realm - Optional realm ID (defaults to 0)
|
|
302
|
+
* @returns Hedera account ID in the format `shard.realm.num` or null if conversion fails
|
|
303
|
+
*/
|
|
304
|
+
export const fromEVMAddress = (evmAddress: string, shard = 0, realm = 0): string | null => {
|
|
305
|
+
try {
|
|
306
|
+
const isLongZeroAddress = evmAddress.includes("0".repeat(20));
|
|
307
|
+
|
|
308
|
+
if (!isLongZeroAddress) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const accountId = AccountId.fromEvmAddress(shard, realm, evmAddress).toString();
|
|
313
|
+
return accountId;
|
|
314
|
+
} catch {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
};
|