@ledgerhq/coin-hedera 1.10.1 → 1.11.0-nightly.2
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/.eslintrc.js +1 -0
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +37 -12
- package/lib/api/mirror.d.ts +3 -20
- package/lib/api/mirror.d.ts.map +1 -1
- package/lib/api/mirror.js +32 -90
- package/lib/api/mirror.js.map +1 -1
- package/lib/api/mirror.test.js +59 -4
- package/lib/api/mirror.test.js.map +1 -1
- package/lib/api/network.d.ts +3 -3
- package/lib/api/network.d.ts.map +1 -1
- package/lib/api/network.js +46 -3
- package/lib/api/network.js.map +1 -1
- package/lib/api/types.d.ts +44 -0
- package/lib/api/types.d.ts.map +1 -0
- package/lib/api/types.js +3 -0
- package/lib/api/types.js.map +1 -0
- package/lib/api/utils.d.ts +8 -0
- package/lib/api/utils.d.ts.map +1 -0
- package/lib/api/utils.js +132 -0
- package/lib/api/utils.js.map +1 -0
- package/lib/bridge/broadcast.d.ts.map +1 -1
- package/lib/bridge/broadcast.js +2 -0
- package/lib/bridge/broadcast.js.map +1 -1
- package/lib/bridge/buildOptimisticOperation.d.ts +2 -2
- package/lib/bridge/buildOptimisticOperation.d.ts.map +1 -1
- package/lib/bridge/buildOptimisticOperation.integration.test.d.ts +2 -0
- package/lib/bridge/buildOptimisticOperation.integration.test.d.ts.map +1 -0
- package/lib/bridge/buildOptimisticOperation.integration.test.js +82 -0
- package/lib/bridge/buildOptimisticOperation.integration.test.js.map +1 -0
- package/lib/bridge/buildOptimisticOperation.js +87 -5
- package/lib/bridge/buildOptimisticOperation.js.map +1 -1
- package/lib/bridge/estimateMaxSpendable.d.ts.map +1 -1
- package/lib/bridge/estimateMaxSpendable.js +8 -2
- package/lib/bridge/estimateMaxSpendable.js.map +1 -1
- package/lib/bridge/getTransactionStatus.d.ts +3 -3
- package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib/bridge/getTransactionStatus.js +116 -23
- package/lib/bridge/getTransactionStatus.js.map +1 -1
- package/lib/bridge/getTransactionStatus.test.d.ts +2 -0
- package/lib/bridge/getTransactionStatus.test.d.ts.map +1 -0
- package/lib/bridge/getTransactionStatus.test.js +176 -0
- package/lib/bridge/getTransactionStatus.test.js.map +1 -0
- package/lib/bridge/index.d.ts +4 -4
- package/lib/bridge/index.d.ts.map +1 -1
- package/lib/bridge/index.js +9 -6
- package/lib/bridge/index.js.map +1 -1
- package/lib/bridge/js-estimateMaxSpendable.integration.test.js +28 -44
- package/lib/bridge/js-estimateMaxSpendable.integration.test.js.map +1 -1
- package/lib/bridge/js-transaction.test.js +10 -49
- package/lib/bridge/js-transaction.test.js.map +1 -1
- package/lib/bridge/prepareTransaction.d.ts +0 -1
- package/lib/bridge/prepareTransaction.d.ts.map +1 -1
- package/lib/bridge/prepareTransaction.js +0 -1
- package/lib/bridge/prepareTransaction.js.map +1 -1
- package/lib/bridge/serialization.d.ts +7 -0
- package/lib/bridge/serialization.d.ts.map +1 -0
- package/lib/bridge/serialization.js +36 -0
- package/lib/bridge/serialization.js.map +1 -0
- package/lib/bridge/serialization.test.d.ts +2 -0
- package/lib/bridge/serialization.test.d.ts.map +1 -0
- package/lib/bridge/serialization.test.js +27 -0
- package/lib/bridge/serialization.test.js.map +1 -0
- package/lib/bridge/synchronisation.d.ts +3 -3
- package/lib/bridge/synchronisation.d.ts.map +1 -1
- package/lib/bridge/synchronisation.js +37 -15
- package/lib/bridge/synchronisation.js.map +1 -1
- package/lib/bridge/transaction.test.js +18 -59
- package/lib/bridge/transaction.test.js.map +1 -1
- package/lib/bridge/utils.d.ts +22 -8
- package/lib/bridge/utils.d.ts.map +1 -1
- package/lib/bridge/utils.integration.test.js +415 -73
- package/lib/bridge/utils.integration.test.js.map +1 -1
- package/lib/bridge/utils.js +300 -15
- package/lib/bridge/utils.js.map +1 -1
- package/lib/constants.d.ts +32 -0
- package/lib/constants.d.ts.map +1 -0
- package/lib/constants.js +37 -0
- package/lib/constants.js.map +1 -0
- package/lib/deviceTransactionConfig.d.ts.map +1 -1
- package/lib/deviceTransactionConfig.js +17 -15
- package/lib/deviceTransactionConfig.js.map +1 -1
- package/lib/logic.d.ts +9 -3
- package/lib/logic.d.ts.map +1 -1
- package/lib/logic.js +31 -3
- package/lib/logic.js.map +1 -1
- package/lib/logic.test.js +103 -50
- package/lib/logic.test.js.map +1 -1
- package/lib/test/fixtures/account.fixture.d.ts +19 -0
- package/lib/test/fixtures/account.fixture.d.ts.map +1 -0
- package/lib/test/fixtures/account.fixture.js +116 -0
- package/lib/test/fixtures/account.fixture.js.map +1 -0
- package/lib/test/fixtures/currency.fixture.d.ts +5 -0
- package/lib/test/fixtures/currency.fixture.d.ts.map +1 -0
- package/lib/test/fixtures/currency.fixture.js +67 -0
- package/lib/test/fixtures/currency.fixture.js.map +1 -0
- package/lib/test/fixtures/mirror.fixture.d.ts +3 -0
- package/lib/test/fixtures/mirror.fixture.d.ts.map +1 -0
- package/lib/test/fixtures/mirror.fixture.js +17 -0
- package/lib/test/fixtures/mirror.fixture.js.map +1 -0
- package/lib/test/fixtures/operation.fixture.d.ts +3 -0
- package/lib/test/fixtures/operation.fixture.d.ts.map +1 -0
- package/lib/test/fixtures/operation.fixture.js +26 -0
- package/lib/test/fixtures/operation.fixture.js.map +1 -0
- package/lib/test/fixtures/transaction.fixture.d.ts +4 -0
- package/lib/test/fixtures/transaction.fixture.d.ts.map +1 -0
- package/lib/test/fixtures/transaction.fixture.js +28 -0
- package/lib/test/fixtures/transaction.fixture.js.map +1 -0
- package/lib/types/bridge.d.ts +25 -1
- package/lib/types/bridge.d.ts.map +1 -1
- package/lib-es/api/mirror.d.ts +3 -20
- package/lib-es/api/mirror.d.ts.map +1 -1
- package/lib-es/api/mirror.js +29 -88
- package/lib-es/api/mirror.js.map +1 -1
- package/lib-es/api/mirror.test.js +60 -5
- package/lib-es/api/mirror.test.js.map +1 -1
- package/lib-es/api/network.d.ts +3 -3
- package/lib-es/api/network.d.ts.map +1 -1
- package/lib-es/api/network.js +44 -4
- package/lib-es/api/network.js.map +1 -1
- package/lib-es/api/types.d.ts +44 -0
- package/lib-es/api/types.d.ts.map +1 -0
- package/lib-es/api/types.js +2 -0
- package/lib-es/api/types.js.map +1 -0
- package/lib-es/api/utils.d.ts +8 -0
- package/lib-es/api/utils.d.ts.map +1 -0
- package/lib-es/api/utils.js +124 -0
- package/lib-es/api/utils.js.map +1 -0
- package/lib-es/bridge/broadcast.d.ts.map +1 -1
- package/lib-es/bridge/broadcast.js +2 -0
- package/lib-es/bridge/broadcast.js.map +1 -1
- package/lib-es/bridge/buildOptimisticOperation.d.ts +2 -2
- package/lib-es/bridge/buildOptimisticOperation.d.ts.map +1 -1
- package/lib-es/bridge/buildOptimisticOperation.integration.test.d.ts +2 -0
- package/lib-es/bridge/buildOptimisticOperation.integration.test.d.ts.map +1 -0
- package/lib-es/bridge/buildOptimisticOperation.integration.test.js +77 -0
- package/lib-es/bridge/buildOptimisticOperation.integration.test.js.map +1 -0
- package/lib-es/bridge/buildOptimisticOperation.js +84 -5
- package/lib-es/bridge/buildOptimisticOperation.js.map +1 -1
- package/lib-es/bridge/estimateMaxSpendable.d.ts.map +1 -1
- package/lib-es/bridge/estimateMaxSpendable.js +8 -2
- package/lib-es/bridge/estimateMaxSpendable.js.map +1 -1
- package/lib-es/bridge/getTransactionStatus.d.ts +3 -3
- package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
- package/lib-es/bridge/getTransactionStatus.js +114 -24
- package/lib-es/bridge/getTransactionStatus.js.map +1 -1
- package/lib-es/bridge/getTransactionStatus.test.d.ts +2 -0
- package/lib-es/bridge/getTransactionStatus.test.d.ts.map +1 -0
- package/lib-es/bridge/getTransactionStatus.test.js +148 -0
- package/lib-es/bridge/getTransactionStatus.test.js.map +1 -0
- package/lib-es/bridge/index.d.ts +4 -4
- package/lib-es/bridge/index.d.ts.map +1 -1
- package/lib-es/bridge/index.js +9 -6
- package/lib-es/bridge/index.js.map +1 -1
- package/lib-es/bridge/js-estimateMaxSpendable.integration.test.js +28 -44
- package/lib-es/bridge/js-estimateMaxSpendable.integration.test.js.map +1 -1
- package/lib-es/bridge/js-transaction.test.js +10 -49
- package/lib-es/bridge/js-transaction.test.js.map +1 -1
- package/lib-es/bridge/prepareTransaction.d.ts +0 -1
- package/lib-es/bridge/prepareTransaction.d.ts.map +1 -1
- package/lib-es/bridge/prepareTransaction.js +0 -1
- package/lib-es/bridge/prepareTransaction.js.map +1 -1
- package/lib-es/bridge/serialization.d.ts +7 -0
- package/lib-es/bridge/serialization.d.ts.map +1 -0
- package/lib-es/bridge/serialization.js +29 -0
- package/lib-es/bridge/serialization.js.map +1 -0
- package/lib-es/bridge/serialization.test.d.ts +2 -0
- package/lib-es/bridge/serialization.test.d.ts.map +1 -0
- package/lib-es/bridge/serialization.test.js +25 -0
- package/lib-es/bridge/serialization.test.js.map +1 -0
- package/lib-es/bridge/synchronisation.d.ts +3 -3
- package/lib-es/bridge/synchronisation.d.ts.map +1 -1
- package/lib-es/bridge/synchronisation.js +39 -17
- package/lib-es/bridge/synchronisation.js.map +1 -1
- package/lib-es/bridge/transaction.test.js +18 -59
- package/lib-es/bridge/transaction.test.js.map +1 -1
- package/lib-es/bridge/utils.d.ts +22 -8
- package/lib-es/bridge/utils.d.ts.map +1 -1
- package/lib-es/bridge/utils.integration.test.js +416 -74
- package/lib-es/bridge/utils.integration.test.js.map +1 -1
- package/lib-es/bridge/utils.js +295 -15
- package/lib-es/bridge/utils.js.map +1 -1
- package/lib-es/constants.d.ts +32 -0
- package/lib-es/constants.d.ts.map +1 -0
- package/lib-es/constants.js +34 -0
- package/lib-es/constants.js.map +1 -0
- package/lib-es/deviceTransactionConfig.d.ts.map +1 -1
- package/lib-es/deviceTransactionConfig.js +17 -15
- package/lib-es/deviceTransactionConfig.js.map +1 -1
- package/lib-es/logic.d.ts +9 -3
- package/lib-es/logic.d.ts.map +1 -1
- package/lib-es/logic.js +26 -3
- package/lib-es/logic.js.map +1 -1
- package/lib-es/logic.test.js +104 -51
- package/lib-es/logic.test.js.map +1 -1
- package/lib-es/test/fixtures/account.fixture.d.ts +19 -0
- package/lib-es/test/fixtures/account.fixture.d.ts.map +1 -0
- package/lib-es/test/fixtures/account.fixture.js +107 -0
- package/lib-es/test/fixtures/account.fixture.js.map +1 -0
- package/lib-es/test/fixtures/currency.fixture.d.ts +5 -0
- package/lib-es/test/fixtures/currency.fixture.d.ts.map +1 -0
- package/lib-es/test/fixtures/currency.fixture.js +58 -0
- package/lib-es/test/fixtures/currency.fixture.js.map +1 -0
- package/lib-es/test/fixtures/mirror.fixture.d.ts +3 -0
- package/lib-es/test/fixtures/mirror.fixture.d.ts.map +1 -0
- package/lib-es/test/fixtures/mirror.fixture.js +13 -0
- package/lib-es/test/fixtures/mirror.fixture.js.map +1 -0
- package/lib-es/test/fixtures/operation.fixture.d.ts +3 -0
- package/lib-es/test/fixtures/operation.fixture.d.ts.map +1 -0
- package/lib-es/test/fixtures/operation.fixture.js +19 -0
- package/lib-es/test/fixtures/operation.fixture.js.map +1 -0
- package/lib-es/test/fixtures/transaction.fixture.d.ts +4 -0
- package/lib-es/test/fixtures/transaction.fixture.d.ts.map +1 -0
- package/lib-es/test/fixtures/transaction.fixture.js +20 -0
- package/lib-es/test/fixtures/transaction.fixture.js.map +1 -0
- package/lib-es/types/bridge.d.ts +25 -1
- package/lib-es/types/bridge.d.ts.map +1 -1
- package/package.json +12 -10
- package/src/api/mirror.test.ts +79 -5
- package/src/api/mirror.ts +30 -111
- package/src/api/network.ts +71 -4
- package/src/api/types.ts +48 -0
- package/src/api/utils.ts +150 -0
- package/src/bridge/broadcast.ts +2 -0
- package/src/bridge/buildOptimisticOperation.integration.test.ts +88 -0
- package/src/bridge/buildOptimisticOperation.ts +118 -7
- package/src/bridge/estimateMaxSpendable.ts +8 -2
- package/src/bridge/getTransactionStatus.test.ts +200 -0
- package/src/bridge/getTransactionStatus.ts +166 -32
- package/src/bridge/index.ts +13 -10
- package/src/bridge/js-estimateMaxSpendable.integration.test.ts +37 -46
- package/src/bridge/js-transaction.test.ts +13 -54
- package/src/bridge/prepareTransaction.ts +1 -2
- package/src/bridge/serialization.test.ts +39 -0
- package/src/bridge/serialization.ts +43 -0
- package/src/bridge/synchronisation.ts +65 -27
- package/src/bridge/transaction.test.ts +22 -64
- package/src/bridge/utils.integration.test.ts +525 -76
- package/src/bridge/utils.ts +423 -24
- package/src/constants.ts +35 -0
- package/src/deviceTransactionConfig.ts +16 -15
- package/src/logic.test.ts +147 -57
- package/src/logic.ts +58 -7
- package/src/test/fixtures/account.fixture.ts +123 -0
- package/src/test/fixtures/currency.fixture.ts +66 -0
- package/src/test/fixtures/mirror.fixture.ts +14 -0
- package/src/test/fixtures/operation.fixture.ts +20 -0
- package/src/test/fixtures/transaction.fixture.ts +22 -0
- package/src/types/bridge.ts +33 -0
package/src/api/mirror.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import network from "@ledgerhq/live-network/network";
|
|
2
|
-
import { getAccountTransactions } from "./mirror";
|
|
2
|
+
import { getAccount, getAccountTokens, getAccountTransactions } from "./mirror";
|
|
3
3
|
|
|
4
4
|
jest.mock("@ledgerhq/live-network/network");
|
|
5
5
|
const mockedNetwork = jest.mocked(network);
|
|
@@ -26,10 +26,10 @@ describe("getAccountTransactions", () => {
|
|
|
26
26
|
|
|
27
27
|
await getAccountTransactions("0.0.1234", null);
|
|
28
28
|
|
|
29
|
-
const
|
|
30
|
-
expect(
|
|
31
|
-
expect(
|
|
32
|
-
expect(
|
|
29
|
+
const requestUrl = mockedNetwork.mock.calls[0][0].url;
|
|
30
|
+
expect(requestUrl).toContain("account.id=0.0.1234");
|
|
31
|
+
expect(requestUrl).toContain("limit=100");
|
|
32
|
+
expect(requestUrl).toContain("order=desc");
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
test("should keep fetching if links.next is present", async () => {
|
|
@@ -72,3 +72,77 @@ describe("getAccountTransactions", () => {
|
|
|
72
72
|
expect(mockedNetwork).toHaveBeenCalledTimes(5);
|
|
73
73
|
});
|
|
74
74
|
});
|
|
75
|
+
|
|
76
|
+
describe("getAccount", () => {
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
jest.clearAllMocks();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("should call the correct endpoint and return account data", async () => {
|
|
82
|
+
mockedNetwork.mockResolvedValueOnce(
|
|
83
|
+
makeMockResponse({
|
|
84
|
+
account: "0.0.1234",
|
|
85
|
+
max_automatic_token_associations: 0,
|
|
86
|
+
balance: {
|
|
87
|
+
balance: 1000,
|
|
88
|
+
timestamp: "1749047764.000113442",
|
|
89
|
+
tokens: [],
|
|
90
|
+
},
|
|
91
|
+
}),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const result = await getAccount("0.0.1234");
|
|
95
|
+
const requestUrl = mockedNetwork.mock.calls[0][0].url;
|
|
96
|
+
|
|
97
|
+
expect(result.account).toEqual("0.0.1234");
|
|
98
|
+
expect(requestUrl).toContain("/api/v1/accounts/0.0.1234");
|
|
99
|
+
expect(mockedNetwork).toHaveBeenCalledTimes(1);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("getAccountTokens", () => {
|
|
104
|
+
beforeEach(() => {
|
|
105
|
+
jest.clearAllMocks();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should return all tokens if only one page is needed", async () => {
|
|
109
|
+
mockedNetwork.mockResolvedValueOnce(
|
|
110
|
+
makeMockResponse({
|
|
111
|
+
tokens: [
|
|
112
|
+
{ token_id: "0.0.1001", balance: 10 },
|
|
113
|
+
{ token_id: "0.0.1002", balance: 20 },
|
|
114
|
+
],
|
|
115
|
+
links: { next: null },
|
|
116
|
+
}),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const result = await getAccountTokens("0.0.1234");
|
|
120
|
+
const requestUrl = mockedNetwork.mock.calls[0][0].url;
|
|
121
|
+
|
|
122
|
+
expect(result.map(t => t.token_id)).toEqual(["0.0.1001", "0.0.1002"]);
|
|
123
|
+
expect(requestUrl).toContain("/api/v1/accounts/0.0.1234/tokens");
|
|
124
|
+
expect(requestUrl).toContain("limit=100");
|
|
125
|
+
expect(mockedNetwork).toHaveBeenCalledTimes(1);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should keep fetching if links.next is present and new tokens are returned", async () => {
|
|
129
|
+
mockedNetwork
|
|
130
|
+
.mockResolvedValueOnce(
|
|
131
|
+
makeMockResponse({
|
|
132
|
+
tokens: [{ token_id: "0.0.1001", balance: 10 }],
|
|
133
|
+
links: { next: "/next-1" },
|
|
134
|
+
}),
|
|
135
|
+
)
|
|
136
|
+
.mockResolvedValueOnce(
|
|
137
|
+
makeMockResponse({
|
|
138
|
+
tokens: [{ token_id: "0.0.1002", balance: 20 }],
|
|
139
|
+
links: { next: null },
|
|
140
|
+
}),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const result = await getAccountTokens("0.0.1234");
|
|
144
|
+
|
|
145
|
+
expect(result.map(t => t.token_id)).toEqual(["0.0.1001", "0.0.1002"]);
|
|
146
|
+
expect(mockedNetwork).toHaveBeenCalledTimes(2);
|
|
147
|
+
});
|
|
148
|
+
});
|
package/src/api/mirror.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import { AccountId } from "@hashgraph/sdk";
|
|
2
1
|
import network from "@ledgerhq/live-network/network";
|
|
3
|
-
import { Operation, OperationType } from "@ledgerhq/types-live";
|
|
4
|
-
import BigNumber from "bignumber.js";
|
|
5
2
|
import { getEnv } from "@ledgerhq/live-env";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { HederaOperationExtra } from "../types";
|
|
3
|
+
import type { HederaMirrorAccount, HederaMirrorToken, HederaMirrorTransaction } from "./types";
|
|
4
|
+
import { HederaAddAccountError } from "../errors";
|
|
5
|
+
import { LedgerAPI4xx } from "@ledgerhq/errors";
|
|
10
6
|
|
|
11
7
|
const getMirrorApiUrl = (): string => getEnv("API_HEDERA_MIRROR");
|
|
12
8
|
|
|
@@ -17,45 +13,33 @@ const fetch = (path: string) => {
|
|
|
17
13
|
});
|
|
18
14
|
};
|
|
19
15
|
|
|
20
|
-
interface HederaMirrorAccount {
|
|
21
|
-
accountId: AccountId;
|
|
22
|
-
balance: BigNumber;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
16
|
export async function getAccountsForPublicKey(publicKey: string): Promise<HederaMirrorAccount[]> {
|
|
26
17
|
let r;
|
|
27
18
|
try {
|
|
28
|
-
r = await fetch(`/api/v1/accounts?account.publicKey=${publicKey}&balance=
|
|
19
|
+
r = await fetch(`/api/v1/accounts?account.publicKey=${publicKey}&balance=true&limit=100`);
|
|
29
20
|
} catch (e: any) {
|
|
30
21
|
if (e.name === "LedgerAPI4xx") return [];
|
|
31
22
|
throw e;
|
|
32
23
|
}
|
|
33
|
-
const rawAccounts = r.data.accounts;
|
|
34
|
-
const accounts: HederaMirrorAccount[] = [];
|
|
35
24
|
|
|
36
|
-
|
|
37
|
-
const accountBalance = await getAccountBalance(raw.account);
|
|
38
|
-
|
|
39
|
-
accounts.push({
|
|
40
|
-
accountId: AccountId.fromString(raw.account),
|
|
41
|
-
balance: accountBalance.balance,
|
|
42
|
-
});
|
|
43
|
-
}
|
|
25
|
+
const accounts = r.data.accounts as HederaMirrorAccount[];
|
|
44
26
|
|
|
45
27
|
return accounts;
|
|
46
28
|
}
|
|
47
29
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
30
|
+
export async function getAccount(address: string): Promise<HederaMirrorAccount> {
|
|
31
|
+
try {
|
|
32
|
+
const res = await fetch(`/api/v1/accounts/${address}`);
|
|
33
|
+
const account = res.data as HederaMirrorAccount;
|
|
34
|
+
|
|
35
|
+
return account;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (error instanceof LedgerAPI4xx && "status" in error && error.status === 404) {
|
|
38
|
+
throw new HederaAddAccountError();
|
|
39
|
+
}
|
|
52
40
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
charged_tx_fee: string;
|
|
56
|
-
transaction_hash: string;
|
|
57
|
-
consensus_timestamp: string;
|
|
58
|
-
transaction_id: string;
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
59
43
|
}
|
|
60
44
|
|
|
61
45
|
export async function getAccountTransactions(
|
|
@@ -88,85 +72,20 @@ export async function getAccountTransactions(
|
|
|
88
72
|
return transactions;
|
|
89
73
|
}
|
|
90
74
|
|
|
91
|
-
export async function
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
)
|
|
96
|
-
const rawOperations = await getAccountTransactions(address, latestOperationTimestamp);
|
|
97
|
-
const operations: Operation[] = [];
|
|
98
|
-
|
|
99
|
-
for (const raw of rawOperations) {
|
|
100
|
-
const { consensus_timestamp, transaction_id } = raw;
|
|
101
|
-
const timestamp = new Date(parseInt(consensus_timestamp.split(".")[0], 10) * 1000);
|
|
102
|
-
const senders: string[] = [];
|
|
103
|
-
const recipients: string[] = [];
|
|
104
|
-
const fee = new BigNumber(raw.charged_tx_fee);
|
|
105
|
-
let value = new BigNumber(0);
|
|
106
|
-
let type: OperationType = "NONE";
|
|
107
|
-
|
|
108
|
-
for (let i = raw.transfers.length - 1; i >= 0; i--) {
|
|
109
|
-
const transfer = raw.transfers[i];
|
|
110
|
-
const amount = new BigNumber(transfer.amount);
|
|
111
|
-
const account = AccountId.fromString(transfer.account);
|
|
112
|
-
|
|
113
|
-
if (transfer.account === address) {
|
|
114
|
-
if (amount.isNegative()) {
|
|
115
|
-
value = amount.abs();
|
|
116
|
-
type = "OUT";
|
|
117
|
-
} else {
|
|
118
|
-
value = amount;
|
|
119
|
-
type = "IN";
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (amount.isNegative()) {
|
|
124
|
-
senders.push(transfer.account);
|
|
125
|
-
} else {
|
|
126
|
-
if (account.shard.eq(0) && account.realm.eq(0)) {
|
|
127
|
-
if (account.num.lt(100)) {
|
|
128
|
-
// account is a node, only add to list if we have none
|
|
129
|
-
if (recipients.length === 0) {
|
|
130
|
-
recipients.push(transfer.account);
|
|
131
|
-
}
|
|
132
|
-
} else if (account.num.lt(1000)) {
|
|
133
|
-
// account is a system account that is not a node
|
|
134
|
-
// do NOT add
|
|
135
|
-
} else {
|
|
136
|
-
recipients.push(transfer.account);
|
|
137
|
-
}
|
|
138
|
-
} else {
|
|
139
|
-
recipients.push(transfer.account);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
75
|
+
export async function getAccountTokens(address: string): Promise<HederaMirrorToken[]> {
|
|
76
|
+
const tokens: HederaMirrorToken[] = [];
|
|
77
|
+
const params = new URLSearchParams({
|
|
78
|
+
limit: "100",
|
|
79
|
+
});
|
|
143
80
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
value,
|
|
152
|
-
date: timestamp,
|
|
153
|
-
// NOTE: there are no "blocks" in hedera
|
|
154
|
-
// Set a value just so that it's considered confirmed according to isConfirmedOperation
|
|
155
|
-
blockHeight: 5,
|
|
156
|
-
blockHash: null,
|
|
157
|
-
extra: {
|
|
158
|
-
consensusTimestamp: consensus_timestamp,
|
|
159
|
-
transactionId: transaction_id,
|
|
160
|
-
} satisfies HederaOperationExtra,
|
|
161
|
-
fee,
|
|
162
|
-
hash,
|
|
163
|
-
recipients,
|
|
164
|
-
senders,
|
|
165
|
-
accountId: ledgerAccountId,
|
|
166
|
-
id: encodeOperationId(ledgerAccountId, hash, type),
|
|
167
|
-
type,
|
|
168
|
-
});
|
|
81
|
+
let nextUrl = `/api/v1/accounts/${address}/tokens?${params.toString()}`;
|
|
82
|
+
|
|
83
|
+
while (nextUrl) {
|
|
84
|
+
const res = await fetch(nextUrl);
|
|
85
|
+
const newTokens = res.data.tokens as HederaMirrorToken[];
|
|
86
|
+
tokens.push(...newTokens);
|
|
87
|
+
nextUrl = res.data.links.next;
|
|
169
88
|
}
|
|
170
89
|
|
|
171
|
-
return
|
|
90
|
+
return tokens;
|
|
172
91
|
}
|
package/src/api/network.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import BigNumber from "bignumber.js";
|
|
2
|
+
import invariant from "invariant";
|
|
2
3
|
import type { Transaction as HederaTransaction, TransactionResponse } from "@hashgraph/sdk";
|
|
3
4
|
import {
|
|
4
5
|
Client,
|
|
@@ -8,27 +9,33 @@ import {
|
|
|
8
9
|
TransactionId,
|
|
9
10
|
AccountBalanceQuery,
|
|
10
11
|
HbarUnit,
|
|
12
|
+
TokenAssociateTransaction,
|
|
11
13
|
} from "@hashgraph/sdk";
|
|
12
|
-
import { Account } from "@ledgerhq/types-live";
|
|
14
|
+
import type { Account, TokenAccount } from "@ledgerhq/types-live";
|
|
15
|
+
import { findSubAccountById, isTokenAccount } from "@ledgerhq/coin-framework/account/helpers";
|
|
13
16
|
import { HederaAddAccountError } from "../errors";
|
|
14
17
|
import { Transaction } from "../types";
|
|
18
|
+
import { isTokenAssociateTransaction } from "../logic";
|
|
15
19
|
|
|
16
20
|
export function broadcastTransaction(transaction: HederaTransaction): Promise<TransactionResponse> {
|
|
17
21
|
return transaction.execute(getClient());
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
// https://github.com/LedgerHQ/ledger-live/pull/72/commits/1e942687d4301660e43e0c4b5419fcfa2733b290
|
|
25
|
+
const nodeAccountIds: AccountId[] = [new AccountId(3)];
|
|
26
|
+
|
|
27
|
+
async function buildUnsignedCoinTransaction({
|
|
21
28
|
account,
|
|
22
29
|
transaction,
|
|
23
30
|
}: {
|
|
24
31
|
account: Account;
|
|
25
32
|
transaction: Transaction;
|
|
26
33
|
}): Promise<TransferTransaction> {
|
|
27
|
-
const hbarAmount = Hbar.fromTinybars(transaction.amount);
|
|
28
34
|
const accountId = account.freshAddress;
|
|
35
|
+
const hbarAmount = Hbar.fromTinybars(transaction.amount);
|
|
29
36
|
|
|
30
37
|
return new TransferTransaction()
|
|
31
|
-
.setNodeAccountIds(
|
|
38
|
+
.setNodeAccountIds(nodeAccountIds)
|
|
32
39
|
.setTransactionId(TransactionId.generate(accountId))
|
|
33
40
|
.setTransactionMemo(transaction.memo ?? "")
|
|
34
41
|
.addHbarTransfer(accountId, hbarAmount.negated())
|
|
@@ -36,6 +43,66 @@ export async function buildUnsignedTransaction({
|
|
|
36
43
|
.freeze();
|
|
37
44
|
}
|
|
38
45
|
|
|
46
|
+
async function buildUnsignedTokenTransaction({
|
|
47
|
+
account,
|
|
48
|
+
tokenAccount,
|
|
49
|
+
transaction,
|
|
50
|
+
}: {
|
|
51
|
+
account: Account;
|
|
52
|
+
tokenAccount: TokenAccount;
|
|
53
|
+
transaction: Transaction;
|
|
54
|
+
}): Promise<TransferTransaction> {
|
|
55
|
+
const accountId = account.freshAddress;
|
|
56
|
+
const tokenId = tokenAccount.token.contractAddress;
|
|
57
|
+
|
|
58
|
+
return new TransferTransaction()
|
|
59
|
+
.setNodeAccountIds(nodeAccountIds)
|
|
60
|
+
.setTransactionId(TransactionId.generate(accountId))
|
|
61
|
+
.setTransactionMemo(transaction.memo ?? "")
|
|
62
|
+
.addTokenTransfer(tokenId, accountId, transaction.amount.negated().toNumber())
|
|
63
|
+
.addTokenTransfer(tokenId, transaction.recipient, transaction.amount.toNumber())
|
|
64
|
+
.freeze();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function buildTokenAssociateTransaction({
|
|
68
|
+
account,
|
|
69
|
+
transaction,
|
|
70
|
+
}: {
|
|
71
|
+
account: Account;
|
|
72
|
+
transaction: Transaction;
|
|
73
|
+
}): Promise<TokenAssociateTransaction> {
|
|
74
|
+
invariant(isTokenAssociateTransaction(transaction), "invalid transaction properties");
|
|
75
|
+
|
|
76
|
+
const accountId = account.freshAddress;
|
|
77
|
+
|
|
78
|
+
return new TokenAssociateTransaction()
|
|
79
|
+
.setNodeAccountIds(nodeAccountIds)
|
|
80
|
+
.setTransactionId(TransactionId.generate(accountId))
|
|
81
|
+
.setTransactionMemo(transaction.memo ?? "")
|
|
82
|
+
.setAccountId(accountId)
|
|
83
|
+
.setTokenIds([transaction.properties.token.contractAddress])
|
|
84
|
+
.freeze();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function buildUnsignedTransaction({
|
|
88
|
+
account,
|
|
89
|
+
transaction,
|
|
90
|
+
}: {
|
|
91
|
+
account: Account;
|
|
92
|
+
transaction: Transaction;
|
|
93
|
+
}): Promise<TransferTransaction | TokenAssociateTransaction> {
|
|
94
|
+
const subAccount = findSubAccountById(account, transaction?.subAccountId || "");
|
|
95
|
+
const isTokenTransaction = isTokenAccount(subAccount);
|
|
96
|
+
|
|
97
|
+
if (isTokenAssociateTransaction(transaction)) {
|
|
98
|
+
return buildTokenAssociateTransaction({ account, transaction });
|
|
99
|
+
} else if (isTokenTransaction) {
|
|
100
|
+
return buildUnsignedTokenTransaction({ account, tokenAccount: subAccount, transaction });
|
|
101
|
+
} else {
|
|
102
|
+
return buildUnsignedCoinTransaction({ account, transaction });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
39
106
|
export interface AccountBalance {
|
|
40
107
|
balance: BigNumber;
|
|
41
108
|
}
|
package/src/api/types.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
type FreezeStatus = "NOT_APPLICABLE" | "FROZEN" | "UNFROZEN";
|
|
2
|
+
|
|
3
|
+
type KycStatus = "NOT_APPLICABLE" | "GRANTED" | "REVOKED";
|
|
4
|
+
|
|
5
|
+
export interface HederaMirrorCoinTransfer {
|
|
6
|
+
account: string;
|
|
7
|
+
amount: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface HederaMirrorTokenTransfer {
|
|
11
|
+
token_id: string;
|
|
12
|
+
account: string;
|
|
13
|
+
amount: number;
|
|
14
|
+
is_approval?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface HederaMirrorTransaction {
|
|
18
|
+
transfers: HederaMirrorCoinTransfer[];
|
|
19
|
+
token_transfers: HederaMirrorTokenTransfer[];
|
|
20
|
+
charged_tx_fee: string;
|
|
21
|
+
transaction_hash: string;
|
|
22
|
+
consensus_timestamp: string;
|
|
23
|
+
result: string;
|
|
24
|
+
name: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface HederaMirrorToken {
|
|
28
|
+
automatic_association: boolean;
|
|
29
|
+
balance: number;
|
|
30
|
+
created_timestamp: string;
|
|
31
|
+
decimals: number;
|
|
32
|
+
token_id: string;
|
|
33
|
+
freeze_status: FreezeStatus;
|
|
34
|
+
kyc_status: KycStatus;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface HederaMirrorAccount {
|
|
38
|
+
account: string;
|
|
39
|
+
max_automatic_token_associations: number;
|
|
40
|
+
balance: {
|
|
41
|
+
balance: number;
|
|
42
|
+
timestamp: string;
|
|
43
|
+
tokens: {
|
|
44
|
+
token_id: string;
|
|
45
|
+
balance: number;
|
|
46
|
+
}[];
|
|
47
|
+
};
|
|
48
|
+
}
|
package/src/api/utils.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
import { AccountId } from "@hashgraph/sdk";
|
|
3
|
+
import type { Operation, OperationType } from "@ledgerhq/types-live";
|
|
4
|
+
import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
|
|
5
|
+
import { encodeTokenAccountId } from "@ledgerhq/coin-framework/account";
|
|
6
|
+
import { findTokenByAddressInCurrency } from "@ledgerhq/cryptoassets";
|
|
7
|
+
import type { HederaMirrorTokenTransfer, HederaMirrorCoinTransfer } from "./types";
|
|
8
|
+
import { getAccountTransactions } from "./mirror";
|
|
9
|
+
import { base64ToUrlSafeBase64 } from "../bridge/utils";
|
|
10
|
+
|
|
11
|
+
function isValidRecipient(accountId: AccountId, recipients: string[]): boolean {
|
|
12
|
+
if (accountId.shard.eq(0) && accountId.realm.eq(0)) {
|
|
13
|
+
// account is a node, only add to list if we have none
|
|
14
|
+
if (accountId.num.lt(100)) {
|
|
15
|
+
return recipients.length === 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// account is a system account that is not a node, do NOT add
|
|
19
|
+
if (accountId.num.lt(1000)) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function parseTransfers(
|
|
28
|
+
mirrorTransfers: (HederaMirrorCoinTransfer | HederaMirrorTokenTransfer)[],
|
|
29
|
+
address: string,
|
|
30
|
+
): Pick<Operation, "type" | "value" | "senders" | "recipients"> {
|
|
31
|
+
let value = new BigNumber(0);
|
|
32
|
+
let type: OperationType = "NONE";
|
|
33
|
+
|
|
34
|
+
const senders: string[] = [];
|
|
35
|
+
const recipients: string[] = [];
|
|
36
|
+
|
|
37
|
+
for (const transfer of mirrorTransfers) {
|
|
38
|
+
const amount = new BigNumber(transfer.amount);
|
|
39
|
+
const accountId = AccountId.fromString(transfer.account);
|
|
40
|
+
|
|
41
|
+
if (transfer.account === address) {
|
|
42
|
+
value = amount.abs();
|
|
43
|
+
type = amount.isNegative() ? "OUT" : "IN";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (amount.isNegative()) {
|
|
47
|
+
senders.push(transfer.account);
|
|
48
|
+
} else if (isValidRecipient(accountId, recipients)) {
|
|
49
|
+
recipients.push(transfer.account);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// NOTE: earlier addresses are the "fee" addresses
|
|
54
|
+
senders.reverse();
|
|
55
|
+
recipients.reverse();
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
type,
|
|
59
|
+
value,
|
|
60
|
+
senders,
|
|
61
|
+
recipients,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function getOperationsForAccount(
|
|
66
|
+
ledgerAccountId: string,
|
|
67
|
+
address: string,
|
|
68
|
+
latestOperationTimestamp: string | null,
|
|
69
|
+
): Promise<{
|
|
70
|
+
coinOperations: Operation[];
|
|
71
|
+
tokenOperations: Operation[];
|
|
72
|
+
}> {
|
|
73
|
+
const mirrorTransactions = await getAccountTransactions(address, latestOperationTimestamp);
|
|
74
|
+
const coinOperations: Operation[] = [];
|
|
75
|
+
const tokenOperations: Operation[] = [];
|
|
76
|
+
|
|
77
|
+
for (const rawTx of mirrorTransactions) {
|
|
78
|
+
const timestamp = new Date(parseInt(rawTx.consensus_timestamp.split(".")[0], 10) * 1000);
|
|
79
|
+
const hash = base64ToUrlSafeBase64(rawTx.transaction_hash);
|
|
80
|
+
const fee = new BigNumber(rawTx.charged_tx_fee);
|
|
81
|
+
const tokenTransfers = rawTx.token_transfers ?? [];
|
|
82
|
+
const transfers = rawTx.transfers ?? [];
|
|
83
|
+
const hasFailed = rawTx.result !== "SUCCESS";
|
|
84
|
+
|
|
85
|
+
if (tokenTransfers.length > 0) {
|
|
86
|
+
const tokenId = rawTx.token_transfers[0].token_id;
|
|
87
|
+
const token = findTokenByAddressInCurrency(tokenId, "hedera");
|
|
88
|
+
if (!token) continue;
|
|
89
|
+
|
|
90
|
+
const encodedTokenId = encodeTokenAccountId(ledgerAccountId, token);
|
|
91
|
+
const { type, value, senders, recipients } = parseTransfers(rawTx.token_transfers, address);
|
|
92
|
+
|
|
93
|
+
// add main FEES coin operation for send token transfer
|
|
94
|
+
if (type === "OUT") {
|
|
95
|
+
coinOperations.push({
|
|
96
|
+
id: encodeOperationId(ledgerAccountId, hash, "FEES"),
|
|
97
|
+
accountId: ledgerAccountId,
|
|
98
|
+
type: "FEES",
|
|
99
|
+
value: fee,
|
|
100
|
+
recipients,
|
|
101
|
+
senders,
|
|
102
|
+
hash,
|
|
103
|
+
fee,
|
|
104
|
+
date: timestamp,
|
|
105
|
+
blockHeight: 5,
|
|
106
|
+
blockHash: null,
|
|
107
|
+
hasFailed,
|
|
108
|
+
extra: { consensusTimestamp: rawTx.consensus_timestamp },
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
tokenOperations.push({
|
|
113
|
+
id: encodeOperationId(encodedTokenId, hash, type),
|
|
114
|
+
accountId: encodedTokenId,
|
|
115
|
+
type,
|
|
116
|
+
value,
|
|
117
|
+
recipients,
|
|
118
|
+
senders,
|
|
119
|
+
hash,
|
|
120
|
+
fee,
|
|
121
|
+
date: timestamp,
|
|
122
|
+
blockHeight: 5,
|
|
123
|
+
blockHash: null,
|
|
124
|
+
hasFailed,
|
|
125
|
+
extra: { consensusTimestamp: rawTx.consensus_timestamp },
|
|
126
|
+
});
|
|
127
|
+
} else if (transfers.length > 0) {
|
|
128
|
+
const { type, value, senders, recipients } = parseTransfers(rawTx.transfers, address);
|
|
129
|
+
const operationType = rawTx.name === "TOKENASSOCIATE" ? "ASSOCIATE_TOKEN" : type;
|
|
130
|
+
|
|
131
|
+
coinOperations.push({
|
|
132
|
+
id: encodeOperationId(ledgerAccountId, hash, operationType),
|
|
133
|
+
accountId: ledgerAccountId,
|
|
134
|
+
type: operationType,
|
|
135
|
+
value,
|
|
136
|
+
recipients,
|
|
137
|
+
senders,
|
|
138
|
+
hash,
|
|
139
|
+
fee,
|
|
140
|
+
date: timestamp,
|
|
141
|
+
blockHeight: 5,
|
|
142
|
+
blockHash: null,
|
|
143
|
+
hasFailed,
|
|
144
|
+
extra: { consensusTimestamp: rawTx.consensus_timestamp },
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return { coinOperations, tokenOperations };
|
|
150
|
+
}
|
package/src/bridge/broadcast.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { patchOperationWithHash } from "@ledgerhq/coin-framework/operation";
|
|
|
4
4
|
import { base64ToUrlSafeBase64, patchOperationWithExtra } from "./utils";
|
|
5
5
|
import { HederaOperationExtra, Transaction } from "../types";
|
|
6
6
|
import { broadcastTransaction } from "../api/network";
|
|
7
|
+
import { isValidExtra } from "../logic";
|
|
7
8
|
|
|
8
9
|
export const broadcast: AccountBridge<Transaction>["broadcast"] = async ({ signedOperation }) => {
|
|
9
10
|
const { signature, operation } = signedOperation;
|
|
@@ -16,6 +17,7 @@ export const broadcast: AccountBridge<Transaction>["broadcast"] = async ({ signe
|
|
|
16
17
|
const base64Hash = Buffer.from(response.transactionHash).toString("base64");
|
|
17
18
|
const base64HashUrlSafe = base64ToUrlSafeBase64(base64Hash);
|
|
18
19
|
const extra: HederaOperationExtra = {
|
|
20
|
+
...(isValidExtra(operation.extra) ? operation.extra : {}),
|
|
19
21
|
transactionId: response.transactionId.toString(),
|
|
20
22
|
};
|
|
21
23
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import BigNumber from "bignumber.js";
|
|
2
|
+
import { getMockedAccount, getMockedTokenAccount } from "../test/fixtures/account.fixture";
|
|
3
|
+
import { getMockedTokenCurrency } from "../test/fixtures/currency.fixture";
|
|
4
|
+
import { getMockedTransaction } from "../test/fixtures/transaction.fixture";
|
|
5
|
+
import { buildOptimisticOperation } from "./buildOptimisticOperation";
|
|
6
|
+
import { getEstimatedFees } from "./utils";
|
|
7
|
+
import { HEDERA_OPERATION_TYPES, HEDERA_TRANSACTION_KINDS } from "../constants";
|
|
8
|
+
|
|
9
|
+
describe("buildOptimisticOperation", () => {
|
|
10
|
+
let estimatedFees: Record<"crypto" | "associate", BigNumber>;
|
|
11
|
+
|
|
12
|
+
beforeAll(async () => {
|
|
13
|
+
const mockedAccount = getMockedAccount();
|
|
14
|
+
const [crypto, associate] = await Promise.all([
|
|
15
|
+
getEstimatedFees(mockedAccount, HEDERA_OPERATION_TYPES.CryptoTransfer),
|
|
16
|
+
getEstimatedFees(mockedAccount, HEDERA_OPERATION_TYPES.TokenAssociate),
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
estimatedFees = { crypto, associate };
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("builds optimistic operation for token association", async () => {
|
|
23
|
+
const mockedAccount = getMockedAccount();
|
|
24
|
+
const mockedToken = getMockedTokenCurrency();
|
|
25
|
+
const mockedTransaction = getMockedTransaction({
|
|
26
|
+
amount: new BigNumber(0),
|
|
27
|
+
recipient: "0.0.1234",
|
|
28
|
+
properties: {
|
|
29
|
+
name: HEDERA_TRANSACTION_KINDS.TokenAssociate.name,
|
|
30
|
+
token: mockedToken,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const op = await buildOptimisticOperation({
|
|
35
|
+
account: mockedAccount,
|
|
36
|
+
transaction: mockedTransaction,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(op.type).toBe("ASSOCIATE_TOKEN");
|
|
40
|
+
expect(op.extra).toEqual({ associatedTokenId: mockedToken.contractAddress });
|
|
41
|
+
expect(op.fee).toEqual(estimatedFees.associate);
|
|
42
|
+
expect(op.senders).toContain(mockedAccount.freshAddress.toString());
|
|
43
|
+
expect(op.recipients).toContain("0.0.1234");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("builds optimistic operation for coin", async () => {
|
|
47
|
+
const mockedAccount = getMockedAccount();
|
|
48
|
+
const mockedTransaction = getMockedTransaction({
|
|
49
|
+
amount: new BigNumber(123),
|
|
50
|
+
recipient: "0.0.5678",
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const op = await buildOptimisticOperation({
|
|
54
|
+
account: mockedAccount,
|
|
55
|
+
transaction: mockedTransaction,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
expect(op.type).toBe("OUT");
|
|
59
|
+
expect(op.fee).toEqual(estimatedFees.crypto);
|
|
60
|
+
expect(op.value).toEqual(new BigNumber(123));
|
|
61
|
+
expect(op.senders).toContain(mockedAccount.freshAddress.toString());
|
|
62
|
+
expect(op.recipients).toContain("0.0.5678");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("builds optimistic operation for token", async () => {
|
|
66
|
+
const mockedTokenCurrency = getMockedTokenCurrency();
|
|
67
|
+
const tokenAccount = getMockedTokenAccount(mockedTokenCurrency);
|
|
68
|
+
const parentAccount = getMockedAccount({ subAccounts: [tokenAccount] });
|
|
69
|
+
const mockedTransaction = getMockedTransaction({
|
|
70
|
+
subAccountId: tokenAccount.id,
|
|
71
|
+
amount: new BigNumber(123),
|
|
72
|
+
recipient: "0.0.9999",
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const op = await buildOptimisticOperation({
|
|
76
|
+
account: parentAccount,
|
|
77
|
+
transaction: mockedTransaction,
|
|
78
|
+
});
|
|
79
|
+
const subOp = op.subOperations![0];
|
|
80
|
+
|
|
81
|
+
expect(op.type).toBe("FEES");
|
|
82
|
+
expect(op.subOperations).toHaveLength(1);
|
|
83
|
+
expect(subOp.type).toBe("OUT");
|
|
84
|
+
expect(subOp.value).toEqual(new BigNumber(123));
|
|
85
|
+
expect(subOp.accountId).toBe(tokenAccount.id);
|
|
86
|
+
expect(subOp.recipients).toContain("0.0.9999");
|
|
87
|
+
});
|
|
88
|
+
});
|