@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.
Files changed (280) hide show
  1. package/CHANGELOG.md +10 -8
  2. package/lib/api/index.d.ts.map +1 -1
  3. package/lib/api/index.js +6 -2
  4. package/lib/api/index.js.map +1 -1
  5. package/lib/bridge/broadcast.js +1 -1
  6. package/lib/bridge/broadcast.js.map +1 -1
  7. package/lib/bridge/buildOptimisticOperation.d.ts.map +1 -1
  8. package/lib/bridge/buildOptimisticOperation.js +80 -15
  9. package/lib/bridge/buildOptimisticOperation.js.map +1 -1
  10. package/lib/bridge/estimateMaxSpendable.js +5 -2
  11. package/lib/bridge/estimateMaxSpendable.js.map +1 -1
  12. package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
  13. package/lib/bridge/getTransactionStatus.js +70 -13
  14. package/lib/bridge/getTransactionStatus.js.map +1 -1
  15. package/lib/bridge/index.js +1 -1
  16. package/lib/bridge/index.js.map +1 -1
  17. package/lib/bridge/prepareTransaction.d.ts.map +1 -1
  18. package/lib/bridge/prepareTransaction.js +40 -7
  19. package/lib/bridge/prepareTransaction.js.map +1 -1
  20. package/lib/bridge/signOperation.d.ts.map +1 -1
  21. package/lib/bridge/signOperation.js +19 -2
  22. package/lib/bridge/signOperation.js.map +1 -1
  23. package/lib/bridge/synchronisation.d.ts +2 -0
  24. package/lib/bridge/synchronisation.d.ts.map +1 -1
  25. package/lib/bridge/synchronisation.js +101 -30
  26. package/lib/bridge/synchronisation.js.map +1 -1
  27. package/lib/bridge/utils.d.ts +35 -2
  28. package/lib/bridge/utils.d.ts.map +1 -1
  29. package/lib/bridge/utils.js +215 -16
  30. package/lib/bridge/utils.js.map +1 -1
  31. package/lib/constants.d.ts +22 -2
  32. package/lib/constants.d.ts.map +1 -1
  33. package/lib/constants.js +42 -2
  34. package/lib/constants.js.map +1 -1
  35. package/lib/deviceTransactionConfig.d.ts +1 -1
  36. package/lib/deviceTransactionConfig.d.ts.map +1 -1
  37. package/lib/deviceTransactionConfig.js +8 -0
  38. package/lib/deviceTransactionConfig.js.map +1 -1
  39. package/lib/errors.d.ts +3 -0
  40. package/lib/errors.d.ts.map +1 -1
  41. package/lib/errors.js +2 -1
  42. package/lib/errors.js.map +1 -1
  43. package/lib/logic/craftTransaction.d.ts +4 -4
  44. package/lib/logic/craftTransaction.d.ts.map +1 -1
  45. package/lib/logic/craftTransaction.js +46 -5
  46. package/lib/logic/craftTransaction.js.map +1 -1
  47. package/lib/logic/estimateFees.d.ts +2 -4
  48. package/lib/logic/estimateFees.d.ts.map +1 -1
  49. package/lib/logic/estimateFees.js +45 -5
  50. package/lib/logic/estimateFees.js.map +1 -1
  51. package/lib/logic/listOperations.d.ts.map +1 -1
  52. package/lib/logic/listOperations.js +7 -3
  53. package/lib/logic/listOperations.js.map +1 -1
  54. package/lib/logic/utils.d.ts +24 -2
  55. package/lib/logic/utils.d.ts.map +1 -1
  56. package/lib/logic/utils.js +66 -13
  57. package/lib/logic/utils.js.map +1 -1
  58. package/lib/network/api.d.ts +12 -1
  59. package/lib/network/api.d.ts.map +1 -1
  60. package/lib/network/api.js +91 -19
  61. package/lib/network/api.js.map +1 -1
  62. package/lib/network/rpc.d.ts.map +1 -1
  63. package/lib/network/rpc.js +1 -0
  64. package/lib/network/rpc.js.map +1 -1
  65. package/lib/network/thirdweb.d.ts +21 -0
  66. package/lib/network/thirdweb.d.ts.map +1 -0
  67. package/lib/network/thirdweb.js +72 -0
  68. package/lib/network/thirdweb.js.map +1 -0
  69. package/lib/network/utils.d.ts +4 -1
  70. package/lib/network/utils.d.ts.map +1 -1
  71. package/lib/network/utils.js +53 -1
  72. package/lib/network/utils.js.map +1 -1
  73. package/lib/test/bridgeDatasetTest.d.ts.map +1 -1
  74. package/lib/test/bridgeDatasetTest.js +4 -4
  75. package/lib/test/bridgeDatasetTest.js.map +1 -1
  76. package/lib/test/fixtures/account.fixture.js +1 -1
  77. package/lib/test/fixtures/account.fixture.js.map +1 -1
  78. package/lib/test/fixtures/common.fixture.d.ts +12 -0
  79. package/lib/test/fixtures/common.fixture.d.ts.map +1 -0
  80. package/lib/test/fixtures/common.fixture.js +66 -0
  81. package/lib/test/fixtures/common.fixture.js.map +1 -0
  82. package/lib/test/fixtures/currency.fixture.d.ts +3 -1
  83. package/lib/test/fixtures/currency.fixture.d.ts.map +1 -1
  84. package/lib/test/fixtures/currency.fixture.js +63 -16
  85. package/lib/test/fixtures/currency.fixture.js.map +1 -1
  86. package/lib/test/fixtures/mirror.fixture.d.ts +3 -1
  87. package/lib/test/fixtures/mirror.fixture.d.ts.map +1 -1
  88. package/lib/test/fixtures/mirror.fixture.js +12 -1
  89. package/lib/test/fixtures/mirror.fixture.js.map +1 -1
  90. package/lib/test/fixtures/thirdweb.fixture.d.ts +3 -0
  91. package/lib/test/fixtures/thirdweb.fixture.d.ts.map +1 -0
  92. package/lib/test/fixtures/thirdweb.fixture.js +34 -0
  93. package/lib/test/fixtures/thirdweb.fixture.js.map +1 -0
  94. package/lib/transaction.d.ts.map +1 -1
  95. package/lib/transaction.js +2 -0
  96. package/lib/transaction.js.map +1 -1
  97. package/lib/types/alpaca.d.ts +5 -1
  98. package/lib/types/alpaca.d.ts.map +1 -1
  99. package/lib/types/bridge.d.ts +6 -1
  100. package/lib/types/bridge.d.ts.map +1 -1
  101. package/lib/types/index.d.ts +2 -0
  102. package/lib/types/index.d.ts.map +1 -1
  103. package/lib/types/index.js +2 -0
  104. package/lib/types/index.js.map +1 -1
  105. package/lib/types/logic.d.ts +39 -0
  106. package/lib/types/logic.d.ts.map +1 -0
  107. package/lib/types/logic.js +3 -0
  108. package/lib/types/logic.js.map +1 -0
  109. package/lib/types/mirror.d.ts +29 -0
  110. package/lib/types/mirror.d.ts.map +1 -1
  111. package/lib/types/thirdweb.d.ts +34 -0
  112. package/lib/types/thirdweb.d.ts.map +1 -0
  113. package/lib/types/thirdweb.js +3 -0
  114. package/lib/types/thirdweb.js.map +1 -0
  115. package/lib-es/api/index.d.ts.map +1 -1
  116. package/lib-es/api/index.js +7 -3
  117. package/lib-es/api/index.js.map +1 -1
  118. package/lib-es/bridge/broadcast.js +2 -2
  119. package/lib-es/bridge/broadcast.js.map +1 -1
  120. package/lib-es/bridge/buildOptimisticOperation.d.ts.map +1 -1
  121. package/lib-es/bridge/buildOptimisticOperation.js +83 -18
  122. package/lib-es/bridge/buildOptimisticOperation.js.map +1 -1
  123. package/lib-es/bridge/estimateMaxSpendable.js +5 -2
  124. package/lib-es/bridge/estimateMaxSpendable.js.map +1 -1
  125. package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
  126. package/lib-es/bridge/getTransactionStatus.js +73 -16
  127. package/lib-es/bridge/getTransactionStatus.js.map +1 -1
  128. package/lib-es/bridge/index.js +2 -2
  129. package/lib-es/bridge/index.js.map +1 -1
  130. package/lib-es/bridge/prepareTransaction.d.ts.map +1 -1
  131. package/lib-es/bridge/prepareTransaction.js +42 -9
  132. package/lib-es/bridge/prepareTransaction.js.map +1 -1
  133. package/lib-es/bridge/signOperation.d.ts.map +1 -1
  134. package/lib-es/bridge/signOperation.js +21 -4
  135. package/lib-es/bridge/signOperation.js.map +1 -1
  136. package/lib-es/bridge/synchronisation.d.ts +2 -0
  137. package/lib-es/bridge/synchronisation.d.ts.map +1 -1
  138. package/lib-es/bridge/synchronisation.js +97 -27
  139. package/lib-es/bridge/synchronisation.js.map +1 -1
  140. package/lib-es/bridge/utils.d.ts +35 -2
  141. package/lib-es/bridge/utils.d.ts.map +1 -1
  142. package/lib-es/bridge/utils.js +211 -16
  143. package/lib-es/bridge/utils.js.map +1 -1
  144. package/lib-es/constants.d.ts +22 -2
  145. package/lib-es/constants.d.ts.map +1 -1
  146. package/lib-es/constants.js +38 -1
  147. package/lib-es/constants.js.map +1 -1
  148. package/lib-es/deviceTransactionConfig.d.ts +1 -1
  149. package/lib-es/deviceTransactionConfig.d.ts.map +1 -1
  150. package/lib-es/deviceTransactionConfig.js +8 -0
  151. package/lib-es/deviceTransactionConfig.js.map +1 -1
  152. package/lib-es/errors.d.ts +3 -0
  153. package/lib-es/errors.d.ts.map +1 -1
  154. package/lib-es/errors.js +1 -0
  155. package/lib-es/errors.js.map +1 -1
  156. package/lib-es/logic/craftTransaction.d.ts +4 -4
  157. package/lib-es/logic/craftTransaction.d.ts.map +1 -1
  158. package/lib-es/logic/craftTransaction.js +48 -7
  159. package/lib-es/logic/craftTransaction.js.map +1 -1
  160. package/lib-es/logic/estimateFees.d.ts +2 -4
  161. package/lib-es/logic/estimateFees.d.ts.map +1 -1
  162. package/lib-es/logic/estimateFees.js +47 -7
  163. package/lib-es/logic/estimateFees.js.map +1 -1
  164. package/lib-es/logic/listOperations.d.ts.map +1 -1
  165. package/lib-es/logic/listOperations.js +7 -3
  166. package/lib-es/logic/listOperations.js.map +1 -1
  167. package/lib-es/logic/utils.d.ts +24 -2
  168. package/lib-es/logic/utils.d.ts.map +1 -1
  169. package/lib-es/logic/utils.js +63 -13
  170. package/lib-es/logic/utils.js.map +1 -1
  171. package/lib-es/network/api.d.ts +12 -1
  172. package/lib-es/network/api.d.ts.map +1 -1
  173. package/lib-es/network/api.js +91 -19
  174. package/lib-es/network/api.js.map +1 -1
  175. package/lib-es/network/rpc.d.ts.map +1 -1
  176. package/lib-es/network/rpc.js +1 -0
  177. package/lib-es/network/rpc.js.map +1 -1
  178. package/lib-es/network/thirdweb.d.ts +21 -0
  179. package/lib-es/network/thirdweb.d.ts.map +1 -0
  180. package/lib-es/network/thirdweb.js +66 -0
  181. package/lib-es/network/thirdweb.js.map +1 -0
  182. package/lib-es/network/utils.d.ts +4 -1
  183. package/lib-es/network/utils.d.ts.map +1 -1
  184. package/lib-es/network/utils.js +49 -0
  185. package/lib-es/network/utils.js.map +1 -1
  186. package/lib-es/test/bridgeDatasetTest.d.ts.map +1 -1
  187. package/lib-es/test/bridgeDatasetTest.js +4 -4
  188. package/lib-es/test/bridgeDatasetTest.js.map +1 -1
  189. package/lib-es/test/fixtures/account.fixture.js +2 -2
  190. package/lib-es/test/fixtures/account.fixture.js.map +1 -1
  191. package/lib-es/test/fixtures/common.fixture.d.ts +12 -0
  192. package/lib-es/test/fixtures/common.fixture.d.ts.map +1 -0
  193. package/lib-es/test/fixtures/common.fixture.js +57 -0
  194. package/lib-es/test/fixtures/common.fixture.js.map +1 -0
  195. package/lib-es/test/fixtures/currency.fixture.d.ts +3 -1
  196. package/lib-es/test/fixtures/currency.fixture.d.ts.map +1 -1
  197. package/lib-es/test/fixtures/currency.fixture.js +59 -14
  198. package/lib-es/test/fixtures/currency.fixture.js.map +1 -1
  199. package/lib-es/test/fixtures/mirror.fixture.d.ts +3 -1
  200. package/lib-es/test/fixtures/mirror.fixture.d.ts.map +1 -1
  201. package/lib-es/test/fixtures/mirror.fixture.js +9 -0
  202. package/lib-es/test/fixtures/mirror.fixture.js.map +1 -1
  203. package/lib-es/test/fixtures/thirdweb.fixture.d.ts +3 -0
  204. package/lib-es/test/fixtures/thirdweb.fixture.d.ts.map +1 -0
  205. package/lib-es/test/fixtures/thirdweb.fixture.js +30 -0
  206. package/lib-es/test/fixtures/thirdweb.fixture.js.map +1 -0
  207. package/lib-es/transaction.d.ts.map +1 -1
  208. package/lib-es/transaction.js +2 -0
  209. package/lib-es/transaction.js.map +1 -1
  210. package/lib-es/types/alpaca.d.ts +5 -1
  211. package/lib-es/types/alpaca.d.ts.map +1 -1
  212. package/lib-es/types/bridge.d.ts +6 -1
  213. package/lib-es/types/bridge.d.ts.map +1 -1
  214. package/lib-es/types/index.d.ts +2 -0
  215. package/lib-es/types/index.d.ts.map +1 -1
  216. package/lib-es/types/index.js +2 -0
  217. package/lib-es/types/index.js.map +1 -1
  218. package/lib-es/types/logic.d.ts +39 -0
  219. package/lib-es/types/logic.d.ts.map +1 -0
  220. package/lib-es/types/logic.js +2 -0
  221. package/lib-es/types/logic.js.map +1 -0
  222. package/lib-es/types/mirror.d.ts +29 -0
  223. package/lib-es/types/mirror.d.ts.map +1 -1
  224. package/lib-es/types/thirdweb.d.ts +34 -0
  225. package/lib-es/types/thirdweb.d.ts.map +1 -0
  226. package/lib-es/types/thirdweb.js +2 -0
  227. package/lib-es/types/thirdweb.js.map +1 -0
  228. package/package.json +9 -8
  229. package/src/api/index.integ.test.ts +11 -8
  230. package/src/api/index.ts +10 -3
  231. package/src/bridge/broadcast.ts +2 -2
  232. package/src/bridge/buildOptimisticOperation.integration.test.ts +70 -19
  233. package/src/bridge/buildOptimisticOperation.ts +98 -20
  234. package/src/bridge/estimateMaxSpendable.ts +5 -5
  235. package/src/bridge/getTransactionStatus.test.ts +57 -12
  236. package/src/bridge/getTransactionStatus.ts +88 -15
  237. package/src/bridge/index.ts +2 -2
  238. package/src/bridge/js-estimateMaxSpendable.integration.test.ts +12 -9
  239. package/src/bridge/prepareTransaction.test.ts +3 -1
  240. package/src/bridge/prepareTransaction.ts +45 -10
  241. package/src/bridge/signOperation.ts +23 -5
  242. package/src/bridge/synchronisation.test.ts +67 -0
  243. package/src/bridge/synchronisation.ts +114 -34
  244. package/src/bridge/utils.integration.test.ts +486 -180
  245. package/src/bridge/utils.test.ts +404 -0
  246. package/src/bridge/utils.ts +330 -27
  247. package/src/constants.ts +47 -2
  248. package/src/deviceTransactionConfig.ts +10 -1
  249. package/src/errors.ts +3 -0
  250. package/src/logic/craftTransaction.test.ts +49 -9
  251. package/src/logic/craftTransaction.ts +76 -11
  252. package/src/logic/estimateFees.test.ts +180 -31
  253. package/src/logic/estimateFees.ts +68 -7
  254. package/src/logic/getAssetFromToken.test.ts +2 -2
  255. package/src/logic/getBalance.test.ts +18 -57
  256. package/src/logic/getTokenFromAsset.test.ts +2 -2
  257. package/src/logic/listOperations.ts +9 -5
  258. package/src/logic/utils.test.ts +157 -69
  259. package/src/logic/utils.ts +75 -13
  260. package/src/network/api.test.ts +211 -3
  261. package/src/network/api.ts +118 -24
  262. package/src/network/rpc.test.ts +1 -0
  263. package/src/network/rpc.ts +1 -0
  264. package/src/network/thirdweb.test.ts +188 -0
  265. package/src/network/thirdweb.ts +101 -0
  266. package/src/network/utils.test.ts +364 -164
  267. package/src/network/utils.ts +83 -1
  268. package/src/test/bridgeDatasetTest.ts +4 -5
  269. package/src/test/fixtures/account.fixture.ts +2 -2
  270. package/src/test/fixtures/common.fixture.ts +74 -0
  271. package/src/test/fixtures/currency.fixture.ts +66 -14
  272. package/src/test/fixtures/mirror.fixture.ts +23 -1
  273. package/src/test/fixtures/thirdweb.fixture.ts +33 -0
  274. package/src/transaction.ts +2 -0
  275. package/src/types/alpaca.ts +8 -1
  276. package/src/types/bridge.ts +6 -1
  277. package/src/types/index.ts +2 -0
  278. package/src/types/logic.ts +44 -0
  279. package/src/types/mirror.ts +35 -0
  280. package/src/types/thirdweb.ts +36 -0
@@ -0,0 +1,101 @@
1
+ import { pad } from "viem";
2
+ import { getEnv } from "@ledgerhq/live-env";
3
+ import network from "@ledgerhq/live-network";
4
+ import { HEDERA_MAINNET_CHAIN_ID, ERC20_TRANSFER_EVENT_TOPIC } from "../constants";
5
+ import { toEVMAddress } from "../logic/utils";
6
+ import type { HederaThirdwebTransaction, HederaThirdwebContractEventsResponse } from "../types";
7
+
8
+ interface FetchOptions extends Record<string, string> {
9
+ filterBlockTimestampGte?: string;
10
+ filterTopic0: string;
11
+ filterTopic1?: string;
12
+ filterTopic2?: string;
13
+ limit: string;
14
+ }
15
+
16
+ const API_URL = getEnv("API_HEDERA_THIRDWEB_URL");
17
+
18
+ async function fetchERC20Transactions(
19
+ contractAddress: string,
20
+ options: FetchOptions,
21
+ ): Promise<HederaThirdwebTransaction[]> {
22
+ const transactions: HederaThirdwebTransaction[] = [];
23
+ const params = new URLSearchParams(options);
24
+ let page = 1;
25
+ let hasMorePages = true;
26
+
27
+ while (hasMorePages) {
28
+ params.set("page", page.toString());
29
+ const response = await network<HederaThirdwebContractEventsResponse>({
30
+ method: "GET",
31
+ url: `${API_URL}/v1/contracts/${HEDERA_MAINNET_CHAIN_ID}/${contractAddress}/events?${params.toString()}`,
32
+ });
33
+ const newTransactions = response.data.result.events;
34
+ transactions.push(...newTransactions);
35
+
36
+ // stop if we received fewer items than the limit or no items
37
+ if (newTransactions.length < Number(options.limit) || newTransactions.length === 0) {
38
+ hasMorePages = false;
39
+ } else {
40
+ page++;
41
+ }
42
+ }
43
+
44
+ return transactions;
45
+ }
46
+
47
+ async function getERC20TransactionsForAccount({
48
+ address,
49
+ contractAddresses,
50
+ transactionFetcher = fetchERC20Transactions,
51
+ since,
52
+ }: {
53
+ address: string;
54
+ contractAddresses: string[];
55
+ since?: string | null;
56
+ transactionFetcher?: typeof fetchERC20Transactions; // optional dependency injection for testing
57
+ }): Promise<HederaThirdwebTransaction[]> {
58
+ const allTransactions: HederaThirdwebTransaction[] = [];
59
+ const evmAddress = toEVMAddress(address);
60
+
61
+ if (contractAddresses.length === 0) {
62
+ return allTransactions;
63
+ }
64
+
65
+ if (!evmAddress) {
66
+ return allTransactions;
67
+ }
68
+
69
+ const baseParams = {
70
+ limit: "1000",
71
+ filterTopic0: ERC20_TRANSFER_EVENT_TOPIC,
72
+ ...(since && { filterBlockTimestampGte: since }),
73
+ } as const;
74
+
75
+ for (const contractAddress of contractAddresses) {
76
+ const outTransactionOptions: FetchOptions = {
77
+ ...baseParams,
78
+ filterTopic1: pad(evmAddress as `0x${string}`).toString(),
79
+ };
80
+
81
+ const inTransactionOptions: FetchOptions = {
82
+ ...baseParams,
83
+ filterTopic2: pad(evmAddress as `0x${string}`).toString(),
84
+ };
85
+
86
+ const outgoingTxs = await transactionFetcher(contractAddress, outTransactionOptions);
87
+ const incomingTxs = await transactionFetcher(contractAddress, inTransactionOptions);
88
+
89
+ allTransactions.push(...outgoingTxs, ...incomingTxs);
90
+ }
91
+
92
+ return allTransactions;
93
+ }
94
+
95
+ // Thirdweb API is used in addition to mirror node because:
96
+ // - mirror node has a 1-week range limitation for ERC20 events queries
97
+ // - mirror node has rate limits that we could exceed with ERC20 integration
98
+ export const thirdwebClient = {
99
+ fetchERC20Transactions,
100
+ getERC20TransactionsForAccount,
101
+ };
@@ -1,175 +1,375 @@
1
1
  import BigNumber from "bignumber.js";
2
- import type { HederaMirrorCoinTransfer, HederaMirrorTokenTransfer } from "../types";
3
- import { parseTransfers } from "./utils";
4
-
5
- const createMirrorCoinTransfer = (account: string, amount: number): HederaMirrorCoinTransfer => ({
6
- account,
7
- amount,
8
- });
9
-
10
- const createMirrorTokenTransfer = (
11
- account: string,
12
- amount: number,
13
- tokenId: string,
14
- ): HederaMirrorTokenTransfer => ({
15
- token_id: tokenId,
16
- account,
17
- amount,
18
- });
19
-
20
- describe("parseTransfers", () => {
21
- it("should correctly identify an incoming transfer", () => {
22
- const userAddress = "0.0.1234";
23
- const transfers = [
24
- createMirrorCoinTransfer("0.0.5678", -100),
25
- createMirrorCoinTransfer(userAddress, 100),
26
- ];
27
-
28
- const result = parseTransfers(transfers, userAddress);
29
-
30
- expect(result.type).toBe("IN");
31
- expect(result.value).toEqual(new BigNumber(100));
32
- expect(result.senders).toEqual(["0.0.5678"]);
33
- expect(result.recipients).toEqual([userAddress]);
2
+ import { setupMockCryptoAssetsStore } from "@ledgerhq/cryptoassets/cal-client/test-helpers";
3
+ import { apiClient } from "./api";
4
+ import { getMockedAccount } from "../test/fixtures/account.fixture";
5
+ import { getMockedERC20TokenCurrency } from "../test/fixtures/currency.fixture";
6
+ import {
7
+ createMirrorCoinTransfer,
8
+ createMirrorTokenTransfer,
9
+ } from "../test/fixtures/mirror.fixture";
10
+ import { getMockedThirdwebTransaction } from "../test/fixtures/thirdweb.fixture";
11
+ import type { HederaMirrorCoinTransfer } from "../types";
12
+ import {
13
+ getERC20BalancesForAccount,
14
+ getERC20Operations,
15
+ parseThirdwebTransactionParams,
16
+ parseTransfers,
17
+ } from "./utils";
18
+
19
+ jest.mock("./api");
20
+
21
+ describe("network utils", () => {
22
+ beforeEach(() => {
23
+ jest.clearAllMocks();
34
24
  });
35
25
 
36
- it("should correctly identify an outgoing transfer", () => {
37
- const userAddress = "0.0.1234";
38
- const transfers = [
39
- createMirrorCoinTransfer(userAddress, -100),
40
- createMirrorCoinTransfer("0.0.5678", 100),
41
- ];
42
-
43
- const result = parseTransfers(transfers, userAddress);
44
-
45
- expect(result.type).toBe("OUT");
46
- expect(result.value).toEqual(new BigNumber(100));
47
- expect(result.senders).toEqual([userAddress]);
48
- expect(result.recipients).toEqual(["0.0.5678"]);
26
+ describe("parseTransfers", () => {
27
+ it("should correctly identify an incoming transfer", () => {
28
+ const userAddress = "0.0.1234";
29
+ const transfers = [
30
+ createMirrorCoinTransfer("0.0.5678", -100),
31
+ createMirrorCoinTransfer(userAddress, 100),
32
+ ];
33
+
34
+ const result = parseTransfers(transfers, userAddress);
35
+
36
+ expect(result.type).toBe("IN");
37
+ expect(result.value).toEqual(new BigNumber(100));
38
+ expect(result.senders).toEqual(["0.0.5678"]);
39
+ expect(result.recipients).toEqual([userAddress]);
40
+ });
41
+
42
+ it("should correctly identify an outgoing transfer", () => {
43
+ const userAddress = "0.0.1234";
44
+ const transfers = [
45
+ createMirrorCoinTransfer(userAddress, -100),
46
+ createMirrorCoinTransfer("0.0.5678", 100),
47
+ ];
48
+
49
+ const result = parseTransfers(transfers, userAddress);
50
+
51
+ expect(result.type).toBe("OUT");
52
+ expect(result.value).toEqual(new BigNumber(100));
53
+ expect(result.senders).toEqual([userAddress]);
54
+ expect(result.recipients).toEqual(["0.0.5678"]);
55
+ });
56
+
57
+ it("should handle multiple senders and recipients", () => {
58
+ const userAddress = "0.0.1234";
59
+ const transfers = [
60
+ createMirrorCoinTransfer("0.0.5678", -50),
61
+ createMirrorCoinTransfer(userAddress, -50),
62
+ createMirrorCoinTransfer("0.0.9999", 100),
63
+ ];
64
+
65
+ const result = parseTransfers(transfers, userAddress);
66
+
67
+ expect(result.type).toBe("OUT");
68
+ expect(result.value).toEqual(new BigNumber(50));
69
+ expect(result.senders).toEqual(["0.0.1234", "0.0.5678"]);
70
+ expect(result.recipients).toEqual(["0.0.9999"]);
71
+ });
72
+
73
+ it("should correctly process token transfers", () => {
74
+ const userAddress = "0.0.1234";
75
+ const tokenId = "0.0.7777";
76
+ const transfers = [
77
+ createMirrorTokenTransfer(userAddress, -10, tokenId),
78
+ createMirrorTokenTransfer("0.0.5678", 10, tokenId),
79
+ ];
80
+
81
+ const result = parseTransfers(transfers, userAddress);
82
+
83
+ expect(result.type).toBe("OUT");
84
+ expect(result.value).toEqual(new BigNumber(10));
85
+ expect(result.senders).toEqual([userAddress]);
86
+ expect(result.recipients).toEqual(["0.0.5678"]);
87
+ });
88
+
89
+ it("should exclude system accounts that are not nodes from recipients", () => {
90
+ const userAddress = "0.0.1234";
91
+ const systemAccount = "0.0.500";
92
+ const transfers = [
93
+ createMirrorCoinTransfer(userAddress, -100),
94
+ createMirrorCoinTransfer(systemAccount, 100),
95
+ ];
96
+
97
+ const result = parseTransfers(transfers, userAddress);
98
+
99
+ expect(result.type).toBe("OUT");
100
+ expect(result.value).toEqual(new BigNumber(100));
101
+ expect(result.senders).toEqual([userAddress]);
102
+ expect(result.recipients).toEqual([]);
103
+ });
104
+
105
+ it("should include node accounts as recipients only if no other recipients", () => {
106
+ const userAddress = "0.0.1234";
107
+ const nodeAccount = "0.0.3";
108
+ const transfers = [
109
+ createMirrorCoinTransfer(userAddress, -100),
110
+ createMirrorCoinTransfer(nodeAccount, 100),
111
+ ];
112
+
113
+ const result = parseTransfers(transfers, userAddress);
114
+
115
+ expect(result.type).toBe("OUT");
116
+ expect(result.value).toEqual(new BigNumber(100));
117
+ expect(result.senders).toEqual([userAddress]);
118
+ expect(result.recipients).toEqual([nodeAccount]);
119
+ });
120
+
121
+ it("should exclude node accounts if there are other recipients", () => {
122
+ const userAddress = "0.0.1234";
123
+ const normalAccount = "0.0.5678";
124
+ const nodeAccount = "0.0.3";
125
+ const transfers = [
126
+ createMirrorCoinTransfer(userAddress, -100),
127
+ createMirrorCoinTransfer(normalAccount, 50),
128
+ createMirrorCoinTransfer(nodeAccount, 50),
129
+ ];
130
+
131
+ const result = parseTransfers(transfers, userAddress);
132
+
133
+ expect(result.type).toBe("OUT");
134
+ expect(result.value).toEqual(new BigNumber(100));
135
+ expect(result.senders).toEqual([userAddress]);
136
+ expect(result.recipients).toEqual([normalAccount]);
137
+ });
138
+
139
+ it("should handle transactions where user is not involved", () => {
140
+ const userAddress = "0.0.1234";
141
+ const transfers = [
142
+ createMirrorCoinTransfer("0.0.5678", -100),
143
+ createMirrorCoinTransfer("0.0.9999", 100),
144
+ ];
145
+
146
+ const result = parseTransfers(transfers, userAddress);
147
+
148
+ expect(result.type).toBe("NONE");
149
+ expect(result.value).toEqual(new BigNumber(0));
150
+ expect(result.senders).toEqual(["0.0.5678"]);
151
+ expect(result.recipients).toEqual(["0.0.9999"]);
152
+ });
153
+
154
+ it("should handle empty transfers array", () => {
155
+ const userAddress = "0.0.1234";
156
+ const transfers: HederaMirrorCoinTransfer[] = [];
157
+
158
+ const result = parseTransfers(transfers, userAddress);
159
+
160
+ expect(result.type).toBe("NONE");
161
+ expect(result.value).toEqual(new BigNumber(0));
162
+ expect(result.senders).toEqual([]);
163
+ expect(result.recipients).toEqual([]);
164
+ });
165
+
166
+ it("should reverse the order of senders and recipients", () => {
167
+ const userAddress = "0.0.1234";
168
+ const transfers = [
169
+ createMirrorCoinTransfer("0.0.900", -5),
170
+ createMirrorCoinTransfer("0.0.5678", -95),
171
+ createMirrorCoinTransfer(userAddress, 100),
172
+ ];
173
+
174
+ const result = parseTransfers(transfers, userAddress);
175
+
176
+ expect(result.type).toBe("IN");
177
+ expect(result.value).toEqual(new BigNumber(100));
178
+ expect(result.senders).toEqual(["0.0.5678", "0.0.900"]);
179
+ expect(result.recipients).toEqual([userAddress]);
180
+ });
49
181
  });
50
182
 
51
- it("should handle multiple senders and recipients", () => {
52
- const userAddress = "0.0.1234";
53
- const transfers = [
54
- createMirrorCoinTransfer("0.0.5678", -50),
55
- createMirrorCoinTransfer(userAddress, -50),
56
- createMirrorCoinTransfer("0.0.9999", 100),
57
- ];
58
-
59
- const result = parseTransfers(transfers, userAddress);
60
-
61
- expect(result.type).toBe("OUT");
62
- expect(result.value).toEqual(new BigNumber(50));
63
- expect(result.senders).toEqual(["0.0.1234", "0.0.5678"]);
64
- expect(result.recipients).toEqual(["0.0.9999"]);
65
- });
66
-
67
- it("should correctly process token transfers", () => {
68
- const userAddress = "0.0.1234";
69
- const tokenId = "0.0.7777";
70
- const transfers = [
71
- createMirrorTokenTransfer(userAddress, -10, tokenId),
72
- createMirrorTokenTransfer("0.0.5678", 10, tokenId),
73
- ];
74
-
75
- const result = parseTransfers(transfers, userAddress);
76
-
77
- expect(result.type).toBe("OUT");
78
- expect(result.value).toEqual(new BigNumber(10));
79
- expect(result.senders).toEqual([userAddress]);
80
- expect(result.recipients).toEqual(["0.0.5678"]);
183
+ describe("getERC20BalancesForAccount", () => {
184
+ it("returns balances only for supported ERC20 tokens and calls apiClient.getERC20Balance accordingly", async () => {
185
+ const mockAccount = getMockedAccount();
186
+ const mockedSupportedTokenIds = ["0/erc20/0x0", "0/erc20/0x1", "0/erc20/0x2"];
187
+ const erc20Token = getMockedERC20TokenCurrency();
188
+
189
+ const mockedResponse = Array.from({ length: mockedSupportedTokenIds.length }, () => ({
190
+ token: erc20Token,
191
+ balance: new BigNumber(123),
192
+ }));
193
+
194
+ (apiClient.getERC20Balance as jest.Mock).mockResolvedValue(new BigNumber(123));
195
+ setupMockCryptoAssetsStore({
196
+ findTokenById: jest.fn().mockReturnValue(erc20Token),
197
+ });
198
+
199
+ const res = await getERC20BalancesForAccount(
200
+ mockAccount.freshAddress,
201
+ mockedSupportedTokenIds,
202
+ );
203
+
204
+ expect(apiClient.getERC20Balance).toHaveBeenCalledTimes(mockedSupportedTokenIds.length);
205
+ expect(apiClient.getERC20Balance).toHaveBeenCalledWith(
206
+ mockAccount.freshAddress,
207
+ erc20Token.contractAddress,
208
+ );
209
+
210
+ expect(res).toHaveLength(mockedSupportedTokenIds.length);
211
+ expect(res).toMatchObject(mockedResponse);
212
+ });
213
+
214
+ it("returns empty array when there are no supported ERC20 tokens", async () => {
215
+ const supportedTokenIds: string[] = [];
216
+ const res = await getERC20BalancesForAccount("0xaccount", supportedTokenIds);
217
+
218
+ expect(res).toEqual([]);
219
+ expect(apiClient.getERC20Balance).not.toHaveBeenCalled();
220
+ });
81
221
  });
82
222
 
83
- it("should exclude system accounts that are not nodes from recipients", () => {
84
- const userAddress = "0.0.1234";
85
- const systemAccount = "0.0.500";
86
- const transfers = [
87
- createMirrorCoinTransfer(userAddress, -100),
88
- createMirrorCoinTransfer(systemAccount, 100),
89
- ];
90
-
91
- const result = parseTransfers(transfers, userAddress);
92
-
93
- expect(result.type).toBe("OUT");
94
- expect(result.value).toEqual(new BigNumber(100));
95
- expect(result.senders).toEqual([userAddress]);
96
- expect(result.recipients).toEqual([]);
223
+ describe("getERC20Operations", () => {
224
+ beforeEach(() => {
225
+ jest.clearAllMocks();
226
+ });
227
+
228
+ it("should fetch and combine data from thirdweb and mirror node", async () => {
229
+ const mockTokenERC20 = getMockedERC20TokenCurrency();
230
+ const mockThirdwebTransaction = getMockedThirdwebTransaction({
231
+ transactionHash: "0xTXHASH1",
232
+ address: mockTokenERC20.contractAddress,
233
+ decoded: {
234
+ name: "Transfer",
235
+ signature: "Transfer(address,address,uint256)",
236
+ params: {
237
+ from: "0x1234",
238
+ to: "0x5678",
239
+ value: "1000000",
240
+ },
241
+ },
242
+ });
243
+ const mockContractCallResult = {
244
+ timestamp: "1234567890.000000000",
245
+ contract_id: mockTokenERC20.contractAddress,
246
+ gas_consumed: 50000,
247
+ gas_limit: 100000,
248
+ gas_used: 50000,
249
+ };
250
+ const mockMirrorTransaction = {
251
+ consensus_timestamp: mockContractCallResult.timestamp,
252
+ transaction_hash: "BASE64HASH",
253
+ transaction_id: "0.0.123@1234567890.000",
254
+ charged_tx_fee: 100000,
255
+ memo_base64: "",
256
+ };
257
+
258
+ (apiClient.getContractCallResult as jest.Mock).mockResolvedValue(mockContractCallResult);
259
+ (apiClient.findTransactionByContractCall as jest.Mock).mockResolvedValue(
260
+ mockMirrorTransaction,
261
+ );
262
+ setupMockCryptoAssetsStore({
263
+ findTokenByAddressInCurrency: jest.fn().mockReturnValue(mockTokenERC20),
264
+ });
265
+
266
+ const result = await getERC20Operations([mockThirdwebTransaction]);
267
+
268
+ expect(result).toHaveLength(1);
269
+ expect(result).toMatchObject([
270
+ {
271
+ thirdwebTransaction: mockThirdwebTransaction,
272
+ mirrorTransaction: mockMirrorTransaction,
273
+ contractCallResult: mockContractCallResult,
274
+ token: mockTokenERC20,
275
+ },
276
+ ]);
277
+ expect(apiClient.getContractCallResult).toHaveBeenCalledTimes(1);
278
+ expect(apiClient.getContractCallResult).toHaveBeenCalledWith(
279
+ mockThirdwebTransaction.transactionHash,
280
+ );
281
+ expect(apiClient.findTransactionByContractCall).toHaveBeenCalledTimes(1);
282
+ expect(apiClient.findTransactionByContractCall).toHaveBeenCalledWith(
283
+ mockContractCallResult.timestamp,
284
+ mockTokenERC20.contractAddress,
285
+ );
286
+ });
287
+
288
+ it("should skip transactions for tokens not found in currency list", async () => {
289
+ const mockThirdwebTransactions = [
290
+ getMockedThirdwebTransaction({
291
+ transactionHash: "0xTXHASH1",
292
+ address: "unknown",
293
+ }),
294
+ ];
295
+
296
+ setupMockCryptoAssetsStore({
297
+ findTokenByAddressInCurrency: jest.fn().mockReturnValue(undefined),
298
+ });
299
+
300
+ const result = await getERC20Operations(mockThirdwebTransactions);
301
+
302
+ expect(result).toHaveLength(0);
303
+ expect(apiClient.getContractCallResult).not.toHaveBeenCalled();
304
+ expect(apiClient.findTransactionByContractCall).not.toHaveBeenCalled();
305
+ });
306
+
307
+ it("should skip transactions when mirror transaction is not found", async () => {
308
+ const mockTokenERC20 = getMockedERC20TokenCurrency();
309
+ const mockThirdwebTransactions = getMockedThirdwebTransaction({
310
+ transactionHash: "0xTXHASH1",
311
+ address: mockTokenERC20.contractAddress,
312
+ });
313
+ const mockContractCallResult = {
314
+ timestamp: "1234567890.000000000",
315
+ contract_id: mockTokenERC20.contractAddress,
316
+ gas_consumed: 50000,
317
+ gas_limit: 100000,
318
+ gas_used: 50000,
319
+ };
320
+
321
+ (apiClient.getContractCallResult as jest.Mock).mockResolvedValue(
322
+ mockContractCallResult as any,
323
+ );
324
+ (apiClient.findTransactionByContractCall as jest.Mock).mockResolvedValue(null);
325
+ setupMockCryptoAssetsStore({
326
+ findTokenByAddressInCurrency: jest.fn().mockReturnValue(mockTokenERC20),
327
+ });
328
+
329
+ const result = await getERC20Operations([mockThirdwebTransactions]);
330
+
331
+ expect(result).toHaveLength(0);
332
+ });
97
333
  });
98
334
 
99
- it("should include node accounts as recipients only if no other recipients", () => {
100
- const userAddress = "0.0.1234";
101
- const nodeAccount = "0.0.3";
102
- const transfers = [
103
- createMirrorCoinTransfer(userAddress, -100),
104
- createMirrorCoinTransfer(nodeAccount, 100),
105
- ];
106
-
107
- const result = parseTransfers(transfers, userAddress);
108
-
109
- expect(result.type).toBe("OUT");
110
- expect(result.value).toEqual(new BigNumber(100));
111
- expect(result.senders).toEqual([userAddress]);
112
- expect(result.recipients).toEqual([nodeAccount]);
113
- });
114
-
115
- it("should exclude node accounts if there are other recipients", () => {
116
- const userAddress = "0.0.1234";
117
- const normalAccount = "0.0.5678";
118
- const nodeAccount = "0.0.3";
119
- const transfers = [
120
- createMirrorCoinTransfer(userAddress, -100),
121
- createMirrorCoinTransfer(normalAccount, 50),
122
- createMirrorCoinTransfer(nodeAccount, 50),
123
- ];
124
-
125
- const result = parseTransfers(transfers, userAddress);
126
-
127
- expect(result.type).toBe("OUT");
128
- expect(result.value).toEqual(new BigNumber(100));
129
- expect(result.senders).toEqual([userAddress]);
130
- expect(result.recipients).toEqual([normalAccount]);
131
- });
132
-
133
- it("should handle transactions where user is not involved", () => {
134
- const userAddress = "0.0.1234";
135
- const transfers = [
136
- createMirrorCoinTransfer("0.0.5678", -100),
137
- createMirrorCoinTransfer("0.0.9999", 100),
138
- ];
139
-
140
- const result = parseTransfers(transfers, userAddress);
141
-
142
- expect(result.type).toBe("NONE");
143
- expect(result.value).toEqual(new BigNumber(0));
144
- expect(result.senders).toEqual(["0.0.5678"]);
145
- expect(result.recipients).toEqual(["0.0.9999"]);
146
- });
147
-
148
- it("should handle empty transfers array", () => {
149
- const userAddress = "0.0.1234";
150
- const transfers: HederaMirrorCoinTransfer[] = [];
151
-
152
- const result = parseTransfers(transfers, userAddress);
153
-
154
- expect(result.type).toBe("NONE");
155
- expect(result.value).toEqual(new BigNumber(0));
156
- expect(result.senders).toEqual([]);
157
- expect(result.recipients).toEqual([]);
158
- });
159
-
160
- it("should reverse the order of senders and recipients", () => {
161
- const userAddress = "0.0.1234";
162
- const transfers = [
163
- createMirrorCoinTransfer("0.0.900", -5),
164
- createMirrorCoinTransfer("0.0.5678", -95),
165
- createMirrorCoinTransfer(userAddress, 100),
166
- ];
167
-
168
- const result = parseTransfers(transfers, userAddress);
169
-
170
- expect(result.type).toBe("IN");
171
- expect(result.value).toEqual(new BigNumber(100));
172
- expect(result.senders).toEqual(["0.0.5678", "0.0.900"]);
173
- expect(result.recipients).toEqual([userAddress]);
335
+ describe("parseThirdwebTransactionParams", () => {
336
+ it("should parse valid transaction params", () => {
337
+ const mockTransaction = getMockedThirdwebTransaction({
338
+ decoded: {
339
+ name: "",
340
+ signature: "",
341
+ params: {
342
+ from: "0x1234",
343
+ to: "0x5678",
344
+ value: "1000000",
345
+ },
346
+ },
347
+ });
348
+
349
+ const result = parseThirdwebTransactionParams(mockTransaction);
350
+
351
+ expect(result).toEqual({
352
+ from: mockTransaction.decoded.params.from,
353
+ to: mockTransaction.decoded.params.to,
354
+ value: mockTransaction.decoded.params.value,
355
+ });
356
+ });
357
+
358
+ it("should return null if params are invalid", () => {
359
+ const mockTransaction = getMockedThirdwebTransaction({
360
+ decoded: {
361
+ name: "",
362
+ signature: "",
363
+ params: {
364
+ from: "0x1234",
365
+ to: 123,
366
+ },
367
+ },
368
+ });
369
+
370
+ const result = parseThirdwebTransactionParams(mockTransaction);
371
+
372
+ expect(result).toBeNull();
373
+ });
174
374
  });
175
375
  });