@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
|
@@ -4,20 +4,20 @@ import { setupCalClientStore } from "@ledgerhq/cryptoassets/cal-client/test-help
|
|
|
4
4
|
import { getCryptoAssetsStore } from "@ledgerhq/cryptoassets/state";
|
|
5
5
|
import { HEDERA_OPERATION_TYPES, HEDERA_TRANSACTION_MODES } from "../constants";
|
|
6
6
|
import { estimateFees } from "../logic/estimateFees";
|
|
7
|
+
import { toEVMAddress } from "../logic/utils";
|
|
8
|
+
import { apiClient } from "../network/api";
|
|
7
9
|
import { getMockedAccount, getMockedTokenAccount } from "../test/fixtures/account.fixture";
|
|
8
|
-
import { getMockedTokenCurrency, getTokenCurrencyFromCAL } from "../test/fixtures/currency.fixture";
|
|
9
|
-
import { getMockedTransaction } from "../test/fixtures/transaction.fixture";
|
|
10
|
-
import { getMockedOperation } from "../test/fixtures/operation.fixture";
|
|
11
|
-
import { getMockedMirrorToken } from "../test/fixtures/mirror.fixture";
|
|
12
|
-
import type { HederaOperationExtra } from "../types";
|
|
13
10
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
} from "
|
|
11
|
+
getMockedHTSTokenCurrency,
|
|
12
|
+
getTokenCurrencyFromCAL,
|
|
13
|
+
getTokenCurrencyFromCALByType,
|
|
14
|
+
} from "../test/fixtures/currency.fixture";
|
|
15
|
+
import { getMockedMirrorToken } from "../test/fixtures/mirror.fixture";
|
|
16
|
+
import { getMockedOperation } from "../test/fixtures/operation.fixture";
|
|
17
|
+
import { getMockedThirdwebTransaction } from "../test/fixtures/thirdweb.fixture";
|
|
18
|
+
import { getMockedTransaction } from "../test/fixtures/transaction.fixture";
|
|
19
|
+
import type { EstimateFeesResult } from "../types";
|
|
20
|
+
import { calculateAmount, getSubAccounts, integrateERC20Operations } from "./utils";
|
|
21
21
|
|
|
22
22
|
describe("utils", () => {
|
|
23
23
|
beforeAll(() => {
|
|
@@ -26,24 +26,30 @@ describe("utils", () => {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
describe("calculateAmount", () => {
|
|
29
|
-
let estimatedFees: Record<"crypto" | "associate",
|
|
29
|
+
let estimatedFees: Record<"crypto" | "associate", EstimateFeesResult>;
|
|
30
30
|
|
|
31
31
|
beforeAll(async () => {
|
|
32
32
|
const mockedAccount = getMockedAccount();
|
|
33
33
|
const [crypto, associate] = await Promise.all([
|
|
34
|
-
estimateFees(
|
|
35
|
-
|
|
34
|
+
estimateFees({
|
|
35
|
+
currency: mockedAccount.currency,
|
|
36
|
+
operationType: HEDERA_OPERATION_TYPES.CryptoTransfer,
|
|
37
|
+
}),
|
|
38
|
+
estimateFees({
|
|
39
|
+
currency: mockedAccount.currency,
|
|
40
|
+
operationType: HEDERA_OPERATION_TYPES.TokenAssociate,
|
|
41
|
+
}),
|
|
36
42
|
]);
|
|
37
43
|
|
|
38
44
|
estimatedFees = { crypto, associate };
|
|
39
45
|
});
|
|
40
46
|
|
|
41
|
-
|
|
47
|
+
it("HBAR transfer, useAllAmount = true", async () => {
|
|
42
48
|
const mockedAccount = getMockedAccount();
|
|
43
49
|
const mockedTransaction = getMockedTransaction({ useAllAmount: true });
|
|
44
50
|
|
|
45
|
-
const amount = mockedAccount.balance.minus(estimatedFees.crypto);
|
|
46
|
-
const totalSpent = amount.plus(estimatedFees.crypto);
|
|
51
|
+
const amount = mockedAccount.balance.minus(estimatedFees.crypto.tinybars);
|
|
52
|
+
const totalSpent = amount.plus(estimatedFees.crypto.tinybars);
|
|
47
53
|
|
|
48
54
|
const result = await calculateAmount({
|
|
49
55
|
account: mockedAccount,
|
|
@@ -53,7 +59,7 @@ describe("utils", () => {
|
|
|
53
59
|
expect(result).toEqual({ amount, totalSpent });
|
|
54
60
|
});
|
|
55
61
|
|
|
56
|
-
|
|
62
|
+
it("HBAR transfer, useAllAmount = false", async () => {
|
|
57
63
|
const mockedAccount = getMockedAccount();
|
|
58
64
|
const mockedTransaction = getMockedTransaction({
|
|
59
65
|
useAllAmount: false,
|
|
@@ -61,7 +67,7 @@ describe("utils", () => {
|
|
|
61
67
|
});
|
|
62
68
|
|
|
63
69
|
const amount = mockedTransaction.amount;
|
|
64
|
-
const totalSpent = amount.plus(estimatedFees.crypto);
|
|
70
|
+
const totalSpent = amount.plus(estimatedFees.crypto.tinybars);
|
|
65
71
|
|
|
66
72
|
const result = await calculateAmount({
|
|
67
73
|
account: mockedAccount,
|
|
@@ -71,8 +77,8 @@ describe("utils", () => {
|
|
|
71
77
|
expect(result).toEqual({ amount, totalSpent });
|
|
72
78
|
});
|
|
73
79
|
|
|
74
|
-
|
|
75
|
-
const mockedTokenCurrency =
|
|
80
|
+
it("token transfer, useAllAmount = true", async () => {
|
|
81
|
+
const mockedTokenCurrency = getMockedHTSTokenCurrency();
|
|
76
82
|
const mockedTokenAccount = getMockedTokenAccount(mockedTokenCurrency);
|
|
77
83
|
const mockedAccount = getMockedAccount({ subAccounts: [mockedTokenAccount] });
|
|
78
84
|
const mockedTransaction = getMockedTransaction({
|
|
@@ -91,8 +97,8 @@ describe("utils", () => {
|
|
|
91
97
|
expect(result).toEqual({ amount, totalSpent });
|
|
92
98
|
});
|
|
93
99
|
|
|
94
|
-
|
|
95
|
-
const mockedTokenCurrency =
|
|
100
|
+
it("token transfer, useAllAmount = false", async () => {
|
|
101
|
+
const mockedTokenCurrency = getMockedHTSTokenCurrency();
|
|
96
102
|
const mockedTokenAccount = getMockedTokenAccount(mockedTokenCurrency);
|
|
97
103
|
const mockedAccount = getMockedAccount({ subAccounts: [mockedTokenAccount] });
|
|
98
104
|
const mockedTransaction = getMockedTransaction({
|
|
@@ -112,8 +118,8 @@ describe("utils", () => {
|
|
|
112
118
|
expect(result).toEqual({ amount, totalSpent });
|
|
113
119
|
});
|
|
114
120
|
|
|
115
|
-
|
|
116
|
-
const mockedTokenCurrency =
|
|
121
|
+
it("token associate operation uses TokenAssociate fee", async () => {
|
|
122
|
+
const mockedTokenCurrency = getMockedHTSTokenCurrency();
|
|
117
123
|
const mockedTokenAccount = getMockedTokenAccount(mockedTokenCurrency);
|
|
118
124
|
const mockedAccount = getMockedAccount({ subAccounts: [mockedTokenAccount] });
|
|
119
125
|
const mockedTransaction = getMockedTransaction({
|
|
@@ -126,7 +132,7 @@ describe("utils", () => {
|
|
|
126
132
|
});
|
|
127
133
|
|
|
128
134
|
const amount = mockedTransaction.amount;
|
|
129
|
-
const totalSpent = amount.plus(estimatedFees.associate);
|
|
135
|
+
const totalSpent = amount.plus(estimatedFees.associate.tinybars);
|
|
130
136
|
|
|
131
137
|
const result = await calculateAmount({
|
|
132
138
|
account: mockedAccount,
|
|
@@ -138,7 +144,7 @@ describe("utils", () => {
|
|
|
138
144
|
});
|
|
139
145
|
|
|
140
146
|
describe("getSubAccounts", () => {
|
|
141
|
-
|
|
147
|
+
it("returns sub account based on operations and mirror tokens", async () => {
|
|
142
148
|
const firstTokenCurrencyFromCAL = getTokenCurrencyFromCAL(0);
|
|
143
149
|
const secondTokenCurrencyFromCAL = getTokenCurrencyFromCAL(1);
|
|
144
150
|
const mockedAccount = getMockedAccount();
|
|
@@ -172,215 +178,515 @@ describe("utils", () => {
|
|
|
172
178
|
accountId: encodeTokenAccountId(mockedAccount.id, secondTokenFromCAL),
|
|
173
179
|
});
|
|
174
180
|
|
|
175
|
-
const result = await getSubAccounts(
|
|
176
|
-
mockedAccount.id,
|
|
177
|
-
[mockedOperation1, mockedOperation2],
|
|
178
|
-
[
|
|
179
|
-
|
|
181
|
+
const result = await getSubAccounts({
|
|
182
|
+
ledgerAccountId: mockedAccount.id,
|
|
183
|
+
latestHTSTokenOperations: [mockedOperation1, mockedOperation2],
|
|
184
|
+
latestERC20TokenOperations: [],
|
|
185
|
+
mirrorTokens: [mockedMirrorToken1, mockedMirrorToken2],
|
|
186
|
+
erc20Tokens: [],
|
|
187
|
+
});
|
|
180
188
|
const uniqueSubAccountIds = new Set(result.map(sa => sa.id));
|
|
181
189
|
|
|
182
|
-
expect(result).toHaveLength(2);
|
|
183
|
-
// Compare contract address instead of full object since CAL format may differ
|
|
184
|
-
expect(result[0].token.contractAddress).toBe(firstTokenCurrencyFromCAL.contractAddress);
|
|
185
|
-
expect(result[1].token.contractAddress).toBe(secondTokenCurrencyFromCAL.contractAddress);
|
|
186
|
-
expect(result[0].balance).toEqual(new BigNumber(10));
|
|
187
|
-
expect(result[1].balance).toEqual(new BigNumber(0));
|
|
188
|
-
expect(result[0].operations).toEqual([mockedOperation1]);
|
|
189
|
-
expect(result[1].operations).toEqual([mockedOperation2]);
|
|
190
190
|
expect(uniqueSubAccountIds.size).toBe(result.length);
|
|
191
|
+
expect(result).toHaveLength(2);
|
|
192
|
+
expect(result).toMatchObject([
|
|
193
|
+
{
|
|
194
|
+
token: firstTokenCurrencyFromCAL,
|
|
195
|
+
balance: new BigNumber(10),
|
|
196
|
+
operations: [mockedOperation1],
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
token: secondTokenCurrencyFromCAL,
|
|
200
|
+
balance: new BigNumber(0),
|
|
201
|
+
operations: [mockedOperation2],
|
|
202
|
+
},
|
|
203
|
+
]);
|
|
191
204
|
});
|
|
192
205
|
|
|
193
|
-
|
|
194
|
-
const mockedTokenCurrency =
|
|
206
|
+
it("ignores operation if token is not listed in CAL", async () => {
|
|
207
|
+
const mockedTokenCurrency = getMockedHTSTokenCurrency();
|
|
195
208
|
const mockedAccount = getMockedAccount();
|
|
196
209
|
const mockedOperation = getMockedOperation({
|
|
197
210
|
accountId: encodeTokenAccountId(mockedAccount.id, mockedTokenCurrency),
|
|
198
211
|
});
|
|
199
212
|
|
|
200
|
-
const result = await getSubAccounts(
|
|
213
|
+
const result = await getSubAccounts({
|
|
214
|
+
ledgerAccountId: mockedAccount.id,
|
|
215
|
+
latestHTSTokenOperations: [mockedOperation],
|
|
216
|
+
latestERC20TokenOperations: [],
|
|
217
|
+
mirrorTokens: [],
|
|
218
|
+
erc20Tokens: [],
|
|
219
|
+
});
|
|
201
220
|
|
|
202
221
|
expect(result).toHaveLength(0);
|
|
203
222
|
});
|
|
204
223
|
|
|
205
|
-
|
|
224
|
+
it("returns sub account for mirror token with no operations yet (e.g. right after association)", async () => {
|
|
206
225
|
const tokenCurrencyFromCAL = getTokenCurrencyFromCAL(0);
|
|
207
226
|
const mockedAccount = getMockedAccount();
|
|
208
|
-
const
|
|
227
|
+
const mockedTokenHTS = getMockedMirrorToken({
|
|
209
228
|
token_id: tokenCurrencyFromCAL.contractAddress,
|
|
210
229
|
balance: 42,
|
|
211
230
|
});
|
|
212
231
|
|
|
213
|
-
const result = await getSubAccounts(
|
|
232
|
+
const result = await getSubAccounts({
|
|
233
|
+
ledgerAccountId: mockedAccount.id,
|
|
234
|
+
latestHTSTokenOperations: [],
|
|
235
|
+
latestERC20TokenOperations: [],
|
|
236
|
+
mirrorTokens: [mockedTokenHTS],
|
|
237
|
+
erc20Tokens: [],
|
|
238
|
+
});
|
|
214
239
|
|
|
215
240
|
expect(result).toHaveLength(1);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
241
|
+
expect(result).toMatchObject([
|
|
242
|
+
{
|
|
243
|
+
token: tokenCurrencyFromCAL,
|
|
244
|
+
operations: [],
|
|
245
|
+
balance: new BigNumber(42),
|
|
246
|
+
},
|
|
247
|
+
]);
|
|
220
248
|
});
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
describe("prepareOperations", () => {
|
|
224
|
-
test("links token operation to existing coin operation with matching hash", async () => {
|
|
225
|
-
const tokenCurrencyFromCAL = getTokenCurrencyFromCAL(0);
|
|
226
|
-
// Fetch actual token from CAL to ensure we use the correct format
|
|
227
|
-
const tokenFromCAL = await getCryptoAssetsStore().findTokenByAddressInCurrency(
|
|
228
|
-
tokenCurrencyFromCAL.contractAddress,
|
|
229
|
-
"hedera",
|
|
230
|
-
);
|
|
231
249
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
250
|
+
it("returns sub account for erc20 token with no operations yet", async () => {
|
|
251
|
+
const tokenCurrencyFromCAL = getTokenCurrencyFromCALByType("erc20");
|
|
252
|
+
const mockedAccount = getMockedAccount();
|
|
235
253
|
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
254
|
+
const result = await getSubAccounts({
|
|
255
|
+
ledgerAccountId: mockedAccount.id,
|
|
256
|
+
latestHTSTokenOperations: [],
|
|
257
|
+
latestERC20TokenOperations: [],
|
|
258
|
+
mirrorTokens: [],
|
|
259
|
+
erc20Tokens: [{ balance: new BigNumber(42), token: tokenCurrencyFromCAL }],
|
|
241
260
|
});
|
|
242
261
|
|
|
243
|
-
const result = await prepareOperations([mockedCoinOperation], [mockedTokenOperation]);
|
|
244
|
-
|
|
245
262
|
expect(result).toHaveLength(1);
|
|
246
|
-
expect(result
|
|
263
|
+
expect(result).toMatchObject([
|
|
264
|
+
{
|
|
265
|
+
token: tokenCurrencyFromCAL,
|
|
266
|
+
operations: [],
|
|
267
|
+
balance: new BigNumber(42),
|
|
268
|
+
},
|
|
269
|
+
]);
|
|
247
270
|
});
|
|
271
|
+
});
|
|
248
272
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
"hedera",
|
|
255
|
-
);
|
|
273
|
+
describe("integrateERC20Operations", () => {
|
|
274
|
+
const address = "0.0.12345";
|
|
275
|
+
const evmAddress = toEVMAddress(address);
|
|
276
|
+
const ledgerAccountId = `js:2:hedera:${address}:`;
|
|
277
|
+
const tokenCurrency = getTokenCurrencyFromCALByType("erc20");
|
|
256
278
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
279
|
+
afterEach(() => {
|
|
280
|
+
jest.restoreAllMocks();
|
|
281
|
+
});
|
|
260
282
|
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
283
|
+
it("creates new operation for erc20 in transfer", async () => {
|
|
284
|
+
const mockGetContractCallResult = jest.spyOn(apiClient, "getContractCallResult");
|
|
285
|
+
const mockFindTransactionByContractCall = jest.spyOn(
|
|
286
|
+
apiClient,
|
|
287
|
+
"findTransactionByContractCall",
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
const incomingTxConsensusTimestamp = `1705836000.000000000`;
|
|
291
|
+
const incomingTxHash = "incoming_erc20";
|
|
292
|
+
const incomingTxValue = "3000000";
|
|
293
|
+
const incomingTxFrom = "0xSENDER";
|
|
294
|
+
const incomingTxTo = evmAddress;
|
|
295
|
+
const incomingERC20Transaction = getMockedThirdwebTransaction({
|
|
296
|
+
transactionHash: incomingTxHash,
|
|
297
|
+
address: tokenCurrency.contractAddress,
|
|
298
|
+
blockHash: "0xINCOMING_BLOCK",
|
|
299
|
+
blockNumber: 12345,
|
|
300
|
+
decoded: {
|
|
301
|
+
name: "Transfer",
|
|
302
|
+
signature: "Transfer(address,address,uint256)",
|
|
303
|
+
params: {
|
|
304
|
+
from: incomingTxFrom,
|
|
305
|
+
to: incomingTxTo,
|
|
306
|
+
value: incomingTxValue,
|
|
307
|
+
},
|
|
308
|
+
},
|
|
309
|
+
});
|
|
310
|
+
const oldMirrorOperations = [
|
|
311
|
+
getMockedOperation({
|
|
312
|
+
hash: "normal_tx",
|
|
313
|
+
type: "IN",
|
|
314
|
+
date: new Date("2024-01-20T10:00:00Z"),
|
|
315
|
+
}),
|
|
316
|
+
];
|
|
317
|
+
|
|
318
|
+
mockGetContractCallResult.mockResolvedValue({
|
|
319
|
+
timestamp: incomingTxConsensusTimestamp,
|
|
320
|
+
contract_id: tokenCurrency.contractAddress,
|
|
321
|
+
} as any);
|
|
322
|
+
|
|
323
|
+
mockFindTransactionByContractCall.mockResolvedValue({
|
|
324
|
+
transaction_hash: incomingTxHash,
|
|
325
|
+
consensus_timestamp: incomingTxConsensusTimestamp,
|
|
326
|
+
} as any);
|
|
327
|
+
|
|
328
|
+
const { updatedOperations, newERC20TokenOperations } = await integrateERC20Operations({
|
|
329
|
+
ledgerAccountId,
|
|
330
|
+
address,
|
|
331
|
+
allOperations: oldMirrorOperations,
|
|
332
|
+
latestERC20Transactions: [incomingERC20Transaction],
|
|
333
|
+
pendingOperationHashes: new Set(),
|
|
334
|
+
erc20OperationHashes: new Set(),
|
|
265
335
|
});
|
|
266
336
|
|
|
267
|
-
const
|
|
268
|
-
const noneOp = result.find(op => op.type === "NONE");
|
|
337
|
+
const incomingOp = updatedOperations.find(op => op.hash === incomingTxHash);
|
|
269
338
|
|
|
270
|
-
expect(
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
339
|
+
expect(incomingOp).toMatchObject({
|
|
340
|
+
type: "NONE",
|
|
341
|
+
hash: incomingTxHash,
|
|
342
|
+
blockHash: incomingERC20Transaction.blockHash,
|
|
343
|
+
});
|
|
344
|
+
expect(incomingOp?.subOperations).toHaveLength(1);
|
|
345
|
+
expect(incomingOp?.subOperations).toMatchObject([
|
|
346
|
+
{
|
|
347
|
+
type: "IN",
|
|
348
|
+
hash: incomingTxHash,
|
|
349
|
+
blockHash: incomingERC20Transaction.blockHash,
|
|
350
|
+
standard: "erc20",
|
|
351
|
+
value: new BigNumber(incomingTxValue),
|
|
352
|
+
senders: [incomingTxFrom],
|
|
353
|
+
recipients: [address],
|
|
354
|
+
},
|
|
355
|
+
]);
|
|
356
|
+
expect(newERC20TokenOperations).toHaveLength(1);
|
|
357
|
+
expect(newERC20TokenOperations).toMatchObject([incomingOp?.subOperations?.[0]]);
|
|
358
|
+
expect(updatedOperations).toHaveLength(oldMirrorOperations.length + 1);
|
|
274
359
|
});
|
|
275
|
-
});
|
|
276
360
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const initialAccount = undefined;
|
|
284
|
-
const newSubAccounts = [mockedTokenAccount1, mockedTokenAccount2];
|
|
361
|
+
it("creates new operation for erc20 out transfer (not made by user)", async () => {
|
|
362
|
+
const mockGetContractCallResult = jest.spyOn(apiClient, "getContractCallResult");
|
|
363
|
+
const mockFindTransactionByContractCall = jest.spyOn(
|
|
364
|
+
apiClient,
|
|
365
|
+
"findTransactionByContractCall",
|
|
366
|
+
);
|
|
285
367
|
|
|
286
|
-
const
|
|
368
|
+
const allowanceTxConsensusTimestamp = "1705922400.000000000";
|
|
369
|
+
const allowanceTxHash = "transfer_by_allowance";
|
|
370
|
+
const allowanceTxValue = "2000000";
|
|
371
|
+
const allowanceTxFrom = evmAddress;
|
|
372
|
+
const allowanceTxTo = "0xRECIPIENT";
|
|
373
|
+
|
|
374
|
+
const oldMirrorOperations = [
|
|
375
|
+
getMockedOperation({
|
|
376
|
+
hash: "normal_tx",
|
|
377
|
+
type: "OUT",
|
|
378
|
+
date: new Date("2024-01-20T10:00:00Z"),
|
|
379
|
+
}),
|
|
380
|
+
];
|
|
381
|
+
|
|
382
|
+
const allowanceERC20Transaction = getMockedThirdwebTransaction({
|
|
383
|
+
transactionHash: allowanceTxHash,
|
|
384
|
+
address: tokenCurrency.contractAddress,
|
|
385
|
+
blockHash: "0xALLOWANCE_BLOCK",
|
|
386
|
+
blockNumber: 12346,
|
|
387
|
+
decoded: {
|
|
388
|
+
name: "Transfer",
|
|
389
|
+
signature: "Transfer(address,address,uint256)",
|
|
390
|
+
params: {
|
|
391
|
+
from: allowanceTxFrom,
|
|
392
|
+
to: allowanceTxTo,
|
|
393
|
+
value: allowanceTxValue,
|
|
394
|
+
},
|
|
395
|
+
},
|
|
396
|
+
});
|
|
287
397
|
|
|
288
|
-
|
|
289
|
-
|
|
398
|
+
mockGetContractCallResult.mockResolvedValue({
|
|
399
|
+
timestamp: allowanceTxConsensusTimestamp,
|
|
400
|
+
contract_id: tokenCurrency.contractAddress,
|
|
401
|
+
} as any);
|
|
402
|
+
|
|
403
|
+
mockFindTransactionByContractCall.mockResolvedValue({
|
|
404
|
+
transaction_hash: allowanceTxHash,
|
|
405
|
+
consensus_timestamp: allowanceTxConsensusTimestamp,
|
|
406
|
+
} as any);
|
|
407
|
+
|
|
408
|
+
const { updatedOperations, newERC20TokenOperations } = await integrateERC20Operations({
|
|
409
|
+
ledgerAccountId,
|
|
410
|
+
address,
|
|
411
|
+
allOperations: oldMirrorOperations,
|
|
412
|
+
latestERC20Transactions: [allowanceERC20Transaction],
|
|
413
|
+
pendingOperationHashes: new Set(),
|
|
414
|
+
erc20OperationHashes: new Set(),
|
|
415
|
+
});
|
|
290
416
|
|
|
291
|
-
|
|
292
|
-
const mockedTokenCurrency = getMockedTokenCurrency();
|
|
293
|
-
const existingOperation = getMockedOperation({ id: "op1" });
|
|
294
|
-
const newOperation = getMockedOperation({ id: "op2" });
|
|
295
|
-
const newPendingOperation = getMockedOperation({ id: "op3" });
|
|
296
|
-
const existingTokenAccount = getMockedTokenAccount(mockedTokenCurrency, {
|
|
297
|
-
id: "tokenaccount",
|
|
298
|
-
balance: new BigNumber(1000),
|
|
299
|
-
creationDate: new Date(),
|
|
300
|
-
operations: [existingOperation],
|
|
301
|
-
pendingOperations: [],
|
|
302
|
-
});
|
|
303
|
-
const updatedTokenAccount = getMockedTokenAccount(mockedTokenCurrency, {
|
|
304
|
-
id: "tokenaccount",
|
|
305
|
-
balance: new BigNumber(2000),
|
|
306
|
-
creationDate: new Date(Date.now() - 24 * 60 * 60 * 1000),
|
|
307
|
-
operations: [newOperation],
|
|
308
|
-
pendingOperations: [newPendingOperation],
|
|
309
|
-
});
|
|
310
|
-
const mockedAccount = getMockedAccount({ subAccounts: [existingTokenAccount] });
|
|
311
|
-
|
|
312
|
-
const result = mergeSubAccounts(mockedAccount, [updatedTokenAccount]);
|
|
313
|
-
const merged = result[0];
|
|
417
|
+
const allowanceOp = updatedOperations.find(op => op.hash === allowanceTxHash);
|
|
314
418
|
|
|
315
|
-
expect(
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
419
|
+
expect(allowanceOp).toMatchObject({
|
|
420
|
+
type: "FEES",
|
|
421
|
+
hash: allowanceTxHash,
|
|
422
|
+
blockHash: allowanceERC20Transaction.blockHash,
|
|
423
|
+
standard: "erc20",
|
|
424
|
+
});
|
|
425
|
+
expect(allowanceOp?.subOperations).toHaveLength(1);
|
|
426
|
+
expect(allowanceOp?.subOperations).toMatchObject([
|
|
427
|
+
{
|
|
428
|
+
type: "OUT",
|
|
429
|
+
hash: allowanceTxHash,
|
|
430
|
+
blockHash: allowanceERC20Transaction.blockHash,
|
|
431
|
+
standard: "erc20",
|
|
432
|
+
value: new BigNumber(allowanceTxValue),
|
|
433
|
+
senders: [address],
|
|
434
|
+
recipients: [allowanceTxTo],
|
|
435
|
+
},
|
|
436
|
+
]);
|
|
437
|
+
expect(newERC20TokenOperations).toHaveLength(1);
|
|
438
|
+
expect(newERC20TokenOperations).toMatchObject([allowanceOp?.subOperations?.[0]]);
|
|
439
|
+
expect(updatedOperations).toHaveLength(oldMirrorOperations.length + 1);
|
|
321
440
|
});
|
|
322
441
|
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const result = mergeSubAccounts(mockedAccount, [existingTokenAccount, newTokenAccount]);
|
|
331
|
-
|
|
332
|
-
expect(result.map(sa => sa.id)).toEqual(["ta1", "ta2"]);
|
|
333
|
-
});
|
|
334
|
-
});
|
|
442
|
+
it("avoids duplicated CONTRACT_CALL operation if confirmed erc20 operation exists", async () => {
|
|
443
|
+
const mockGetContractCallResult = jest.spyOn(apiClient, "getContractCallResult");
|
|
444
|
+
const mockFindTransactionByContractCall = jest.spyOn(
|
|
445
|
+
apiClient,
|
|
446
|
+
"findTransactionByContractCall",
|
|
447
|
+
);
|
|
335
448
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
const
|
|
449
|
+
const duplicateTxConsensusTimestamp = "1705836000.000000000";
|
|
450
|
+
const duplicateTxHash = "duplicate_tx";
|
|
451
|
+
|
|
452
|
+
const operationsWithDuplicate = [
|
|
453
|
+
getMockedOperation({
|
|
454
|
+
hash: duplicateTxHash,
|
|
455
|
+
type: "FEES",
|
|
456
|
+
standard: "erc20",
|
|
457
|
+
date: new Date("2024-01-20T10:00:00Z"),
|
|
458
|
+
blockHash: "0xBLOCK",
|
|
459
|
+
subOperations: [
|
|
460
|
+
getMockedOperation({
|
|
461
|
+
type: "OUT",
|
|
462
|
+
standard: "erc20",
|
|
463
|
+
hash: duplicateTxHash,
|
|
464
|
+
accountId: encodeTokenAccountId(ledgerAccountId, tokenCurrency),
|
|
465
|
+
}),
|
|
466
|
+
],
|
|
467
|
+
}),
|
|
468
|
+
getMockedOperation({
|
|
469
|
+
hash: duplicateTxHash,
|
|
470
|
+
type: "CONTRACT_CALL",
|
|
471
|
+
date: new Date("2024-01-20T10:00:00Z"),
|
|
472
|
+
}),
|
|
473
|
+
getMockedOperation({
|
|
474
|
+
hash: "unique_tx",
|
|
475
|
+
type: "OUT",
|
|
476
|
+
date: new Date("2024-01-19T10:00:00Z"),
|
|
477
|
+
}),
|
|
478
|
+
];
|
|
479
|
+
|
|
480
|
+
const duplicateERC20Transaction = getMockedThirdwebTransaction({
|
|
481
|
+
transactionHash: duplicateTxHash,
|
|
482
|
+
address: tokenCurrency.contractAddress,
|
|
483
|
+
blockHash: "0xBLOCK",
|
|
484
|
+
decoded: {
|
|
485
|
+
name: "Transfer",
|
|
486
|
+
signature: "Transfer(address,address,uint256)",
|
|
487
|
+
params: {
|
|
488
|
+
from: evmAddress,
|
|
489
|
+
to: "0xRECIPIENT",
|
|
490
|
+
value: "1000000",
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
});
|
|
340
494
|
|
|
341
|
-
|
|
342
|
-
|
|
495
|
+
mockGetContractCallResult.mockResolvedValue({
|
|
496
|
+
timestamp: duplicateTxConsensusTimestamp,
|
|
497
|
+
contract_id: tokenCurrency.contractAddress,
|
|
498
|
+
} as any);
|
|
499
|
+
|
|
500
|
+
mockFindTransactionByContractCall.mockResolvedValue({
|
|
501
|
+
transaction_hash: duplicateTxHash,
|
|
502
|
+
consensus_timestamp: duplicateTxConsensusTimestamp,
|
|
503
|
+
} as any);
|
|
504
|
+
|
|
505
|
+
const { updatedOperations } = await integrateERC20Operations({
|
|
506
|
+
ledgerAccountId,
|
|
507
|
+
address,
|
|
508
|
+
allOperations: operationsWithDuplicate,
|
|
509
|
+
latestERC20Transactions: [duplicateERC20Transaction],
|
|
510
|
+
pendingOperationHashes: new Set(),
|
|
511
|
+
erc20OperationHashes: new Set([duplicateTxHash]),
|
|
512
|
+
});
|
|
343
513
|
|
|
344
|
-
const
|
|
514
|
+
const duplicatedContractCalls = updatedOperations.filter(
|
|
515
|
+
op => op.type === "CONTRACT_CALL" && op.hash === duplicateTxHash,
|
|
516
|
+
);
|
|
517
|
+
const feesOps = updatedOperations.filter(
|
|
518
|
+
op => op.type === "FEES" && op.hash === duplicateTxHash,
|
|
519
|
+
);
|
|
345
520
|
|
|
346
|
-
expect(
|
|
347
|
-
expect(
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
});
|
|
521
|
+
expect(updatedOperations).toHaveLength(2);
|
|
522
|
+
expect(duplicatedContractCalls).toHaveLength(0);
|
|
523
|
+
expect(feesOps).toHaveLength(1);
|
|
524
|
+
expect(feesOps).toMatchObject([{ blockHash: "0xBLOCK" }]);
|
|
351
525
|
});
|
|
352
526
|
|
|
353
|
-
|
|
354
|
-
const
|
|
355
|
-
|
|
527
|
+
it("avoids duplicated CONTRACT_CALL operation if erc20 operation is pending", async () => {
|
|
528
|
+
const pendingTxHash = "pending_erc20";
|
|
529
|
+
|
|
530
|
+
const operationsWithPending = [
|
|
531
|
+
getMockedOperation({
|
|
532
|
+
hash: pendingTxHash,
|
|
533
|
+
type: "CONTRACT_CALL",
|
|
534
|
+
date: new Date("2024-01-20T10:00:00Z"),
|
|
535
|
+
}),
|
|
536
|
+
getMockedOperation({
|
|
537
|
+
hash: "confirmed_tx",
|
|
538
|
+
type: "OUT",
|
|
539
|
+
date: new Date("2024-01-19T10:00:00Z"),
|
|
540
|
+
}),
|
|
541
|
+
];
|
|
542
|
+
|
|
543
|
+
const { updatedOperations } = await integrateERC20Operations({
|
|
544
|
+
ledgerAccountId,
|
|
545
|
+
address,
|
|
546
|
+
allOperations: operationsWithPending,
|
|
547
|
+
latestERC20Transactions: [],
|
|
548
|
+
pendingOperationHashes: new Set([pendingTxHash]),
|
|
549
|
+
erc20OperationHashes: new Set(),
|
|
550
|
+
});
|
|
356
551
|
|
|
357
|
-
const
|
|
358
|
-
const mockedPendingOperation = getMockedOperation({ hash: "op1", extra: pendingExtra });
|
|
552
|
+
const pendingOp = updatedOperations.find(op => op.hash === pendingTxHash);
|
|
359
553
|
|
|
360
|
-
|
|
361
|
-
expect(
|
|
362
|
-
expect(
|
|
554
|
+
expect(pendingOp).toBeUndefined();
|
|
555
|
+
expect(updatedOperations).toHaveLength(1);
|
|
556
|
+
expect(updatedOperations[0].hash).toBe("confirmed_tx");
|
|
363
557
|
});
|
|
364
|
-
});
|
|
365
558
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
559
|
+
/**
|
|
560
|
+
* Timeline:
|
|
561
|
+
* - Tuesday: Normal transactions
|
|
562
|
+
* - Wednesday: ERC20 transfer (Mirror + Thirdweb in sync)
|
|
563
|
+
* - Thursday: Normal transaction
|
|
564
|
+
* - Friday: ERC20 transfer (Thirdweb stuck - no event)
|
|
565
|
+
* - Saturday: Normal transaction
|
|
566
|
+
*
|
|
567
|
+
* SYNC 1 (Friday):
|
|
568
|
+
* - Mirror Node shows CONTRACT_CALL without blockHash
|
|
569
|
+
* - Thirdweb has no event yet (indexer stuck)
|
|
570
|
+
* - Operation remains as CONTRACT_CALL (not enriched)
|
|
571
|
+
*
|
|
572
|
+
* SYNC 2 (Saturday):
|
|
573
|
+
* - Thirdweb catches up and returns Friday's event
|
|
574
|
+
* - CONTRACT_CALL should get patched to FEES with ERC20 sub-operation
|
|
575
|
+
*/
|
|
576
|
+
it("handles delayed thirdweb indexer", async () => {
|
|
577
|
+
const mockGetContractCallResult = jest.spyOn(apiClient, "getContractCallResult");
|
|
578
|
+
const mockFindTransactionByContractCall = jest.spyOn(
|
|
579
|
+
apiClient,
|
|
580
|
+
"findTransactionByContractCall",
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
const fridayTxConsensusTimestamp = `1705678200.000000000`;
|
|
584
|
+
|
|
585
|
+
// sync 1 from Friday: thirdweb hasn't indexed yet
|
|
586
|
+
const fridaySyncOperations = [
|
|
587
|
+
getMockedOperation({
|
|
588
|
+
hash: "saturday_tx",
|
|
589
|
+
type: "OUT",
|
|
590
|
+
date: new Date("2024-01-20T10:00:00Z"),
|
|
591
|
+
}),
|
|
592
|
+
getMockedOperation({
|
|
593
|
+
hash: "friday_erc20",
|
|
594
|
+
type: "CONTRACT_CALL",
|
|
595
|
+
date: new Date("2024-01-19T15:30:00Z"),
|
|
596
|
+
}),
|
|
597
|
+
getMockedOperation({
|
|
598
|
+
hash: "thursday_tx",
|
|
599
|
+
type: "OUT",
|
|
600
|
+
date: new Date("2024-01-18T12:00:00Z"),
|
|
601
|
+
}),
|
|
602
|
+
getMockedOperation({
|
|
603
|
+
hash: "wednesday_erc20",
|
|
604
|
+
type: "FEES",
|
|
605
|
+
date: new Date("2024-01-17T09:00:00Z"),
|
|
606
|
+
standard: "erc20",
|
|
607
|
+
blockHash: "0xWEDNESDAY_BLOCK",
|
|
608
|
+
subOperations: [
|
|
609
|
+
getMockedOperation({
|
|
610
|
+
type: "OUT",
|
|
611
|
+
standard: "erc20",
|
|
612
|
+
hash: "wednesday_erc20",
|
|
613
|
+
accountId: encodeTokenAccountId(ledgerAccountId, tokenCurrency),
|
|
614
|
+
}),
|
|
615
|
+
],
|
|
616
|
+
}),
|
|
617
|
+
getMockedOperation({
|
|
618
|
+
hash: "tuesday_tx",
|
|
619
|
+
type: "OUT",
|
|
620
|
+
date: new Date("2024-01-16T08:00:00Z"),
|
|
621
|
+
}),
|
|
622
|
+
];
|
|
623
|
+
|
|
624
|
+
// thirdweb catches up with Friday's event
|
|
625
|
+
const lateERC20Transaction = getMockedThirdwebTransaction({
|
|
626
|
+
transactionHash: "friday_erc20",
|
|
627
|
+
address: tokenCurrency.contractAddress,
|
|
628
|
+
decoded: {
|
|
629
|
+
name: "Transfer",
|
|
630
|
+
signature: "Transfer(address,address,uint256)",
|
|
631
|
+
params: {
|
|
632
|
+
from: evmAddress,
|
|
633
|
+
to: "0xRECIPIENT",
|
|
634
|
+
value: "5000000",
|
|
635
|
+
},
|
|
636
|
+
},
|
|
372
637
|
});
|
|
373
638
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
};
|
|
639
|
+
mockGetContractCallResult.mockResolvedValue({
|
|
640
|
+
timestamp: fridayTxConsensusTimestamp,
|
|
641
|
+
contract_id: tokenCurrency.contractAddress,
|
|
642
|
+
} as any);
|
|
643
|
+
|
|
644
|
+
mockFindTransactionByContractCall.mockResolvedValue({
|
|
645
|
+
transaction_hash: lateERC20Transaction.transactionHash,
|
|
646
|
+
consensus_timestamp: fridayTxConsensusTimestamp,
|
|
647
|
+
} as any);
|
|
648
|
+
|
|
649
|
+
const { updatedOperations, newERC20TokenOperations } = await integrateERC20Operations({
|
|
650
|
+
ledgerAccountId,
|
|
651
|
+
address,
|
|
652
|
+
allOperations: fridaySyncOperations,
|
|
653
|
+
latestERC20Transactions: [lateERC20Transaction],
|
|
654
|
+
pendingOperationHashes: new Set(),
|
|
655
|
+
erc20OperationHashes: new Set(["wednesday_erc20"]),
|
|
656
|
+
});
|
|
378
657
|
|
|
379
|
-
|
|
658
|
+
// check if friday operation got patched
|
|
659
|
+
const wednesdayOp = updatedOperations.find(op => op.hash === "wednesday_erc20");
|
|
660
|
+
const fridayOp = updatedOperations.find(
|
|
661
|
+
op => op.hash === lateERC20Transaction.transactionHash,
|
|
662
|
+
);
|
|
380
663
|
|
|
381
|
-
expect(
|
|
382
|
-
|
|
383
|
-
|
|
664
|
+
expect(fridayOp).toMatchObject({
|
|
665
|
+
type: "FEES",
|
|
666
|
+
standard: "erc20",
|
|
667
|
+
hash: lateERC20Transaction.transactionHash,
|
|
668
|
+
subOperations: [
|
|
669
|
+
{
|
|
670
|
+
type: "OUT",
|
|
671
|
+
standard: "erc20",
|
|
672
|
+
},
|
|
673
|
+
],
|
|
674
|
+
});
|
|
675
|
+
expect(newERC20TokenOperations).toHaveLength(1);
|
|
676
|
+
expect(newERC20TokenOperations).toMatchObject([
|
|
677
|
+
{
|
|
678
|
+
type: "OUT",
|
|
679
|
+
hash: lateERC20Transaction.transactionHash,
|
|
680
|
+
accountId: encodeTokenAccountId(ledgerAccountId, tokenCurrency),
|
|
681
|
+
},
|
|
682
|
+
]);
|
|
683
|
+
expect(wednesdayOp).toMatchObject({
|
|
684
|
+
type: "FEES",
|
|
685
|
+
blockHash: "0xWEDNESDAY_BLOCK",
|
|
686
|
+
});
|
|
687
|
+
expect(updatedOperations[0]).toMatchObject({ hash: "saturday_tx" });
|
|
688
|
+
expect(updatedOperations.at(-1)).toMatchObject({ hash: "tuesday_tx" });
|
|
689
|
+
expect(updatedOperations).toHaveLength(fridaySyncOperations.length);
|
|
384
690
|
});
|
|
385
691
|
});
|
|
386
692
|
});
|