@ledgerhq/coin-hedera 1.15.0-nightly.20251126023856 → 1.15.0-nightly.20251127023715

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
@@ -1,6 +1,12 @@
1
+ import BigNumber from "bignumber.js";
1
2
  import network from "@ledgerhq/live-network";
2
3
  import { apiClient } from "./api";
3
4
  import { getMockResponse } from "../test/fixtures/network.fixture";
5
+ import type {
6
+ HederaMirrorContractCallResult,
7
+ HederaMirrorNetworkFees,
8
+ HederaMirrorTransaction,
9
+ } from "../types";
4
10
 
5
11
  jest.mock("@ledgerhq/live-network");
6
12
  const mockedNetwork = jest.mocked(network);
@@ -10,7 +16,7 @@ describe("getAccountTransactions", () => {
10
16
  jest.resetAllMocks();
11
17
  });
12
18
 
13
- test("should include 'account.id', 'limit=100' and 'order=desc' query params", async () => {
19
+ it("should include 'account.id', 'limit=100' and 'order=desc' query params", async () => {
14
20
  mockedNetwork.mockResolvedValueOnce(
15
21
  getMockResponse({ transactions: [], links: { next: null } }),
16
22
  );
@@ -27,7 +33,7 @@ describe("getAccountTransactions", () => {
27
33
  expect(requestUrl).toContain("order=desc");
28
34
  });
29
35
 
30
- test("should keep fetching if fetchAllPages is set and links.next is present", async () => {
36
+ it("should keep fetching if fetchAllPages is set and links.next is present", async () => {
31
37
  mockedNetwork
32
38
  .mockResolvedValueOnce(
33
39
  getMockResponse({
@@ -72,7 +78,7 @@ describe("getAccountTransactions", () => {
72
78
  expect(mockedNetwork).toHaveBeenCalledTimes(5);
73
79
  });
74
80
 
75
- test("should paginate if fetchAllPages is not set", async () => {
81
+ it("should paginate if fetchAllPages is not set", async () => {
76
82
  mockedNetwork
77
83
  .mockResolvedValueOnce(
78
84
  getMockResponse({
@@ -192,3 +198,205 @@ describe("getAccountTokens", () => {
192
198
  expect(mockedNetwork).toHaveBeenCalledTimes(2);
193
199
  });
194
200
  });
201
+
202
+ describe("getNetworkFees", () => {
203
+ beforeEach(() => {
204
+ jest.clearAllMocks();
205
+ });
206
+
207
+ it("should call the correct endpoint and return network fees", async () => {
208
+ const mockedResults: HederaMirrorNetworkFees = {
209
+ fees: [{ gas: 39, transaction_type: "ContractCall" }],
210
+ timestamp: "1758733200.632122898",
211
+ };
212
+
213
+ mockedNetwork.mockResolvedValueOnce(getMockResponse(mockedResults));
214
+
215
+ const result = await apiClient.getNetworkFees();
216
+ const requestUrl = mockedNetwork.mock.calls[0][0].url;
217
+
218
+ expect(result).toEqual(mockedResults);
219
+ expect(requestUrl).toContain("/api/v1/network/fees");
220
+ expect(mockedNetwork).toHaveBeenCalledTimes(1);
221
+ });
222
+ });
223
+
224
+ describe("getContractCallResult", () => {
225
+ beforeEach(() => {
226
+ jest.clearAllMocks();
227
+ });
228
+
229
+ it("should call the correct endpoint and return results for contract call", async () => {
230
+ const mockedResults: HederaMirrorContractCallResult = {
231
+ contract_id: "0.0.4321",
232
+ block_gas_used: 100,
233
+ gas_consumed: 200,
234
+ gas_limit: 10000,
235
+ gas_used: 150,
236
+ timestamp: "xxxxxxxxx",
237
+ };
238
+
239
+ mockedNetwork.mockResolvedValueOnce(
240
+ getMockResponse({
241
+ contract_id: "0.0.4321",
242
+ block_gas_used: 100,
243
+ gas_consumed: 200,
244
+ gas_limit: 10000,
245
+ gas_used: 150,
246
+ timestamp: "xxxxxxxxx",
247
+ }),
248
+ );
249
+
250
+ const result = await apiClient.getContractCallResult(
251
+ "0xa9059cbb000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000186a0",
252
+ );
253
+ const requestUrl = mockedNetwork.mock.calls[0][0].url;
254
+
255
+ expect(result).toEqual(mockedResults);
256
+ expect(requestUrl).toContain("/api/v1/contracts/results");
257
+ expect(mockedNetwork).toHaveBeenCalledTimes(1);
258
+ });
259
+ });
260
+
261
+ describe("findTransactionByContractCall", () => {
262
+ beforeEach(() => {
263
+ jest.clearAllMocks();
264
+ });
265
+
266
+ it("should call the correct endpoint and return transaction details", async () => {
267
+ const mockedResults: HederaMirrorTransaction = {
268
+ transfers: [],
269
+ token_transfers: [],
270
+ charged_tx_fee: 100,
271
+ transaction_id: "xxxxxxxxxxxxxx",
272
+ transaction_hash: "xxxxxxxxxxxxx",
273
+ consensus_timestamp: "xxxxxxxxxxxxx",
274
+ result: "xxxxxxxxxxxxx",
275
+ entity_id: "0.0.1234",
276
+ name: "CONTRACTCALL",
277
+ };
278
+
279
+ mockedNetwork.mockResolvedValueOnce(
280
+ getMockResponse({
281
+ transactions: [mockedResults],
282
+ }),
283
+ );
284
+
285
+ const result = await apiClient.findTransactionByContractCall(
286
+ "xxxxxxxxxxxxxxxxxxxx",
287
+ "0.0.1234",
288
+ );
289
+ const requestUrl = mockedNetwork.mock.calls[0][0].url;
290
+
291
+ expect(result).toEqual(mockedResults);
292
+ expect(requestUrl).toContain("/api/v1/transactions?timestamp=");
293
+ expect(mockedNetwork).toHaveBeenCalledTimes(1);
294
+ });
295
+
296
+ it("should call the correct endpoint and return null for non existing contract calls", async () => {
297
+ mockedNetwork.mockResolvedValueOnce(
298
+ getMockResponse({
299
+ transactions: [
300
+ {
301
+ transfers: [],
302
+ token_transfers: [],
303
+ charged_tx_fee: 100,
304
+ transaction_hash: "xxxxxxxxxxxxx",
305
+ consensus_timestamp: "xxxxxxxxxxxxx",
306
+ result: "xxxxxxxxxxxxx",
307
+ entity_id: "0.0.1234",
308
+ name: "NOT_CONTRACTCALL",
309
+ },
310
+ {
311
+ transfers: [],
312
+ token_transfers: [],
313
+ charged_tx_fee: 100,
314
+ transaction_hash: "xxxxxxxxxxxxx",
315
+ consensus_timestamp: "xxxxxxxxxxxxx",
316
+ result: "xxxxxxxxxxxxx",
317
+ entity_id: "0.0.1111",
318
+ name: "CONTRACTCALL",
319
+ },
320
+ ],
321
+ }),
322
+ );
323
+
324
+ const result = await apiClient.findTransactionByContractCall(
325
+ "xxxxxxxxxxxxxxxxxxxx",
326
+ "0.0.1234",
327
+ );
328
+ const requestUrl = mockedNetwork.mock.calls[0][0].url;
329
+
330
+ expect(result).toEqual(null);
331
+ expect(requestUrl).toContain("/api/v1/transactions?timestamp=");
332
+ expect(mockedNetwork).toHaveBeenCalledTimes(1);
333
+ });
334
+
335
+ it("should call the correct endpoint and return null for empty transactions list", async () => {
336
+ mockedNetwork.mockResolvedValueOnce(
337
+ getMockResponse({
338
+ transactions: [],
339
+ }),
340
+ );
341
+
342
+ const result = await apiClient.findTransactionByContractCall(
343
+ "xxxxxxxxxxxxxxxxxxxx",
344
+ "0.0.1234",
345
+ );
346
+ const requestUrl = mockedNetwork.mock.calls[0][0].url;
347
+
348
+ expect(result).toEqual(null);
349
+ expect(requestUrl).toContain("/api/v1/transactions?timestamp=");
350
+ expect(mockedNetwork).toHaveBeenCalledTimes(1);
351
+ });
352
+ });
353
+
354
+ describe("getERC20Balance", () => {
355
+ beforeEach(() => {
356
+ jest.clearAllMocks();
357
+ });
358
+
359
+ it("should call the correct endpoint and return the contract balance", async () => {
360
+ mockedNetwork.mockResolvedValueOnce(
361
+ getMockResponse({
362
+ result: "1000000000",
363
+ }),
364
+ );
365
+
366
+ const result = await apiClient.getERC20Balance(
367
+ "0x0000000000000000000000000000000000000001",
368
+ "0x0000000000000000000000000000000000000002",
369
+ );
370
+ const requestUrl = mockedNetwork.mock.calls[0][0].url;
371
+
372
+ expect(result).toEqual(BigNumber("1000000000"));
373
+ expect(requestUrl).toContain("/api/v1/contracts/call");
374
+ expect(mockedNetwork).toHaveBeenCalledTimes(1);
375
+ });
376
+ });
377
+
378
+ describe("estimateContractCallGas", () => {
379
+ beforeEach(() => {
380
+ jest.clearAllMocks();
381
+ });
382
+
383
+ it("should call the correct endpoint and return estimated contract call gas", async () => {
384
+ mockedNetwork.mockResolvedValueOnce(
385
+ getMockResponse({
386
+ result: "1000000000",
387
+ }),
388
+ );
389
+
390
+ const result = await apiClient.estimateContractCallGas(
391
+ "0x0000000000000000000000000000000000000001",
392
+ "0x0000000000000000000000000000000000000002",
393
+ "0xa9059cbb000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000186a0",
394
+ BigInt(1000),
395
+ );
396
+ const requestUrl = mockedNetwork.mock.calls[0][0].url;
397
+
398
+ expect(result).toEqual(BigNumber("1000000000"));
399
+ expect(requestUrl).toContain("/api/v1/contracts/call");
400
+ expect(mockedNetwork).toHaveBeenCalledTimes(1);
401
+ });
402
+ });
@@ -1,3 +1,5 @@
1
+ import BigNumber from "bignumber.js";
2
+ import { encodeFunctionData, erc20Abi } from "viem";
1
3
  import network from "@ledgerhq/live-network";
2
4
  import type { LiveNetworkResponse } from "@ledgerhq/live-network/network";
3
5
  import { getEnv } from "@ledgerhq/live-env";
@@ -10,23 +12,21 @@ import type {
10
12
  HederaMirrorAccountsResponse,
11
13
  HederaMirrorToken,
12
14
  HederaMirrorTransaction,
15
+ HederaMirrorNetworkFees,
16
+ HederaMirrorContractCallResult,
17
+ HederaMirrorContractCallBalance,
18
+ HederaMirrorContractCallEstimate,
13
19
  } from "../types";
14
20
 
15
- const getMirrorApiUrl = (): string => getEnv("API_HEDERA_MIRROR");
16
-
17
- const fetch = <Result>(path: string) => {
18
- return network<Result>({
19
- method: "GET",
20
- url: `${getMirrorApiUrl()}${path}`,
21
- });
22
- };
21
+ const API_URL = getEnv("API_HEDERA_MIRROR");
23
22
 
24
23
  async function getAccountsForPublicKey(publicKey: string): Promise<HederaMirrorAccount[]> {
25
24
  let res;
26
25
  try {
27
- res = await fetch<HederaMirrorAccountsResponse>(
28
- `/api/v1/accounts?account.publicKey=${publicKey}&balance=true&limit=100`,
29
- );
26
+ res = await network<HederaMirrorAccountsResponse>({
27
+ method: "GET",
28
+ url: `${API_URL}/api/v1/accounts?account.publicKey=${publicKey}&balance=true&limit=100`,
29
+ });
30
30
  } catch (e: unknown) {
31
31
  if (e instanceof LedgerAPI4xx) return [];
32
32
  throw e;
@@ -39,7 +39,10 @@ async function getAccountsForPublicKey(publicKey: string): Promise<HederaMirrorA
39
39
 
40
40
  async function getAccount(address: string): Promise<HederaMirrorAccount> {
41
41
  try {
42
- const res = await fetch<HederaMirrorAccount>(`/api/v1/accounts/${address}`);
42
+ const res = await network<HederaMirrorAccount>({
43
+ method: "GET",
44
+ url: `${API_URL}/api/v1/accounts/${address}`,
45
+ });
43
46
  const account = res.data;
44
47
 
45
48
  return account;
@@ -83,16 +86,19 @@ async function getAccountTransactions({
83
86
  }
84
87
 
85
88
  let nextCursor: string | null = null;
86
- let nextUrl: string | null = `/api/v1/transactions?${params.toString()}`;
89
+ let nextPath: string | null = `/api/v1/transactions?${params.toString()}`;
87
90
 
88
91
  // WARNING: don't break the loop when `transactions` array is empty but `links.next` is present
89
92
  // the mirror node API enforces a 60-day max time range per query, even if `timestamp` param is set
90
93
  // see: https://hedera.com/blog/changes-to-the-hedera-operated-mirror-node
91
- while (nextUrl) {
92
- const res: LiveNetworkResponse<HederaMirrorTransactionsResponse> = await fetch(nextUrl);
94
+ while (nextPath) {
95
+ const res: LiveNetworkResponse<HederaMirrorTransactionsResponse> = await network({
96
+ method: "GET",
97
+ url: `${API_URL}${nextPath}`,
98
+ });
93
99
  const newTransactions = res.data.transactions;
94
100
  transactions.push(...newTransactions);
95
- nextUrl = res.data.links.next;
101
+ nextPath = res.data.links.next;
96
102
 
97
103
  // stop fetching if pagination mode is used and we reached the limit
98
104
  if (!fetchAllPages && transactions.length >= limit) {
@@ -106,7 +112,7 @@ async function getAccountTransactions({
106
112
  }
107
113
 
108
114
  // set the next cursor only if we have more transactions to fetch
109
- if (!fetchAllPages && nextUrl) {
115
+ if (!fetchAllPages && nextPath) {
110
116
  const lastTx = transactions.at(-1);
111
117
  nextCursor = lastTx?.consensus_timestamp ?? null;
112
118
  }
@@ -120,13 +126,16 @@ async function getAccountTokens(address: string): Promise<HederaMirrorToken[]> {
120
126
  limit: "100",
121
127
  });
122
128
 
123
- let nextUrl: string | null = `/api/v1/accounts/${address}/tokens?${params.toString()}`;
129
+ let nextPath: string | null = `/api/v1/accounts/${address}/tokens?${params.toString()}`;
124
130
 
125
- while (nextUrl) {
126
- const res: LiveNetworkResponse<HederaMirrorAccountTokensResponse> = await fetch(nextUrl);
131
+ while (nextPath) {
132
+ const res: LiveNetworkResponse<HederaMirrorAccountTokensResponse> = await network({
133
+ method: "GET",
134
+ url: `${API_URL}${nextPath}`,
135
+ });
127
136
  const newTokens = res.data.tokens;
128
137
  tokens.push(...newTokens);
129
- nextUrl = res.data.links.next;
138
+ nextPath = res.data.links.next;
130
139
  }
131
140
 
132
141
  return tokens;
@@ -138,9 +147,10 @@ async function getLatestTransaction(): Promise<HederaMirrorTransaction> {
138
147
  order: "desc",
139
148
  });
140
149
 
141
- const res = await fetch<HederaMirrorTransactionsResponse>(
142
- `/api/v1/transactions?${params.toString()}`,
143
- );
150
+ const res = await network<HederaMirrorTransactionsResponse>({
151
+ method: "GET",
152
+ url: `${API_URL}/api/v1/transactions?${params.toString()}`,
153
+ });
144
154
  const transaction = res.data.transactions[0];
145
155
 
146
156
  if (!transaction) {
@@ -150,10 +160,94 @@ async function getLatestTransaction(): Promise<HederaMirrorTransaction> {
150
160
  return transaction;
151
161
  }
152
162
 
163
+ async function getNetworkFees(): Promise<HederaMirrorNetworkFees> {
164
+ const res = await network<HederaMirrorNetworkFees>({
165
+ method: "GET",
166
+ url: `${API_URL}/api/v1/network/fees`,
167
+ });
168
+
169
+ return res.data;
170
+ }
171
+
172
+ async function getContractCallResult(
173
+ transactionHash: string,
174
+ ): Promise<HederaMirrorContractCallResult> {
175
+ const res = await network<HederaMirrorContractCallResult>({
176
+ method: "GET",
177
+ url: `${API_URL}/api/v1/contracts/results/${transactionHash}`,
178
+ });
179
+
180
+ return res.data;
181
+ }
182
+
183
+ async function findTransactionByContractCall(
184
+ timestamp: string,
185
+ contractId: string,
186
+ ): Promise<HederaMirrorTransaction | null> {
187
+ const res = await network<HederaMirrorTransactionsResponse>({
188
+ method: "GET",
189
+ url: `${API_URL}/api/v1/transactions?timestamp=${timestamp}`,
190
+ });
191
+ const transactions = res.data.transactions;
192
+
193
+ return transactions.find(el => el.name === "CONTRACTCALL" && el.entity_id === contractId) ?? null;
194
+ }
195
+
196
+ async function getERC20Balance(
197
+ accountEvmAddress: string,
198
+ contractEvmAddress: string,
199
+ ): Promise<BigNumber> {
200
+ const res = await network<HederaMirrorContractCallBalance>({
201
+ method: "POST",
202
+ url: `${API_URL}/api/v1/contracts/call`,
203
+ data: {
204
+ block: "latest",
205
+ to: contractEvmAddress,
206
+ data: encodeFunctionData({
207
+ abi: erc20Abi,
208
+ functionName: "balanceOf",
209
+ args: [accountEvmAddress as `0x${string}`],
210
+ }),
211
+ },
212
+ });
213
+
214
+ return new BigNumber(res.data.result);
215
+ }
216
+
217
+ async function estimateContractCallGas(
218
+ senderEvmAddress: string,
219
+ recipientEvmAddress: string,
220
+ contractEvmAddress: string,
221
+ amount: bigint,
222
+ ): Promise<BigNumber> {
223
+ const res = await network<HederaMirrorContractCallEstimate>({
224
+ method: "POST",
225
+ url: `${API_URL}/api/v1/contracts/call`,
226
+ data: {
227
+ block: "latest",
228
+ estimate: true,
229
+ from: senderEvmAddress,
230
+ to: contractEvmAddress,
231
+ data: encodeFunctionData({
232
+ abi: erc20Abi,
233
+ functionName: "transfer",
234
+ args: [recipientEvmAddress as `0x${string}`, amount],
235
+ }),
236
+ },
237
+ });
238
+
239
+ return new BigNumber(res.data.result);
240
+ }
241
+
153
242
  export const apiClient = {
154
243
  getAccountsForPublicKey,
155
244
  getAccount,
156
245
  getAccountTokens,
157
246
  getAccountTransactions,
158
247
  getLatestTransaction,
248
+ getNetworkFees,
249
+ getContractCallResult,
250
+ findTransactionByContractCall,
251
+ getERC20Balance,
252
+ estimateContractCallGas,
159
253
  };
@@ -2,6 +2,7 @@ import { Client, Transaction, TransactionResponse } from "@hashgraph/sdk";
2
2
  import { rpcClient } from "./rpc";
3
3
 
4
4
  const mockClient = {
5
+ close: jest.fn(),
5
6
  setMaxNodesPerTransaction: jest.fn().mockReturnThis(),
6
7
  setNetwork: jest.fn().mockReturnThis(),
7
8
  } as unknown as Client;
@@ -15,6 +15,7 @@ function getInstance(): Client {
15
15
 
16
16
  // for testing purposes only, used to reset singleton client instance
17
17
  function _resetInstance() {
18
+ _hederaClient?.close();
18
19
  _hederaClient = null;
19
20
  }
20
21
 
@@ -0,0 +1,188 @@
1
+ import { pad } from "viem";
2
+ import network from "@ledgerhq/live-network";
3
+ import { getMockedThirdwebTransaction } from "../test/fixtures/thirdweb.fixture";
4
+ import { getMockResponse } from "../test/fixtures/common.fixture";
5
+ import { thirdwebClient } from "./thirdweb";
6
+
7
+ jest.mock("@ledgerhq/live-network");
8
+ const mockedNetwork = jest.mocked(network);
9
+
10
+ const mockedERC20Transaction = getMockedThirdwebTransaction();
11
+ const mockedERC20TokenAddress1 = "0x0000000000000000000000000000000000000001";
12
+ const mockedERC20TokenAddress2 = "0x0000000000000000000000000000000000000002";
13
+ const mockedTopic0Address = pad("0x0000000000000000000000000000000000000000");
14
+ const mockedTopic1Address = pad("0x0000000000000000000000000000000000000003");
15
+
16
+ describe("fetchERC20Transactions", () => {
17
+ beforeEach(() => {
18
+ jest.clearAllMocks();
19
+ });
20
+
21
+ it("should include 'page', 'limit=1000' and filterTopic query params", async () => {
22
+ mockedNetwork.mockResolvedValueOnce(
23
+ getMockResponse({
24
+ result: {
25
+ events: [],
26
+ pagination: { limit: 1000, page: 1, totalCount: 0 },
27
+ },
28
+ }),
29
+ );
30
+
31
+ const params = {
32
+ limit: "1000",
33
+ page: "1",
34
+ filterTopic0: mockedTopic0Address,
35
+ filterTopic1: mockedTopic1Address,
36
+ };
37
+
38
+ await thirdwebClient.fetchERC20Transactions(mockedERC20TokenAddress1, params);
39
+
40
+ const requestUrl = mockedNetwork.mock.calls[0][0].url;
41
+ expect(requestUrl).toContain("page=1");
42
+ expect(requestUrl).toContain("limit=1000");
43
+ expect(requestUrl).toContain(`filterTopic0=${mockedTopic0Address}`);
44
+ expect(requestUrl).toContain(`filterTopic1=${mockedTopic1Address}`);
45
+ });
46
+
47
+ it("should fire only once and return single element", async () => {
48
+ mockedNetwork.mockResolvedValueOnce(
49
+ getMockResponse({
50
+ result: {
51
+ events: [mockedERC20Transaction],
52
+ pagination: { limit: 1000, page: 1, totalCount: 1 },
53
+ },
54
+ }),
55
+ );
56
+
57
+ const params = {
58
+ limit: "1000",
59
+ page: "1",
60
+ filterTopic0: mockedTopic0Address,
61
+ filterTopic1: mockedTopic1Address,
62
+ };
63
+ const result = await thirdwebClient.fetchERC20Transactions(mockedERC20TokenAddress1, params);
64
+
65
+ expect(result).toHaveLength(1);
66
+ expect(mockedNetwork).toHaveBeenCalledTimes(1);
67
+ });
68
+
69
+ it("should fire only once and return empty array", async () => {
70
+ mockedNetwork.mockResolvedValueOnce(
71
+ getMockResponse({
72
+ result: {
73
+ events: [],
74
+ pagination: { limit: 1000, page: 1, totalCount: 0 },
75
+ },
76
+ }),
77
+ );
78
+
79
+ const params = {
80
+ limit: "1000",
81
+ page: "1",
82
+ filterTopic0: mockedTopic0Address,
83
+ filterTopic1: mockedTopic1Address,
84
+ };
85
+ const result = await thirdwebClient.fetchERC20Transactions(mockedERC20TokenAddress1, params);
86
+
87
+ expect(result).toHaveLength(0);
88
+ expect(mockedNetwork).toHaveBeenCalledTimes(1);
89
+ });
90
+
91
+ it("should keep fetching until returned events are fewer than requested limit", async () => {
92
+ mockedNetwork
93
+ .mockResolvedValueOnce(
94
+ getMockResponse({
95
+ result: {
96
+ events: [mockedERC20Transaction, mockedERC20Transaction],
97
+ pagination: { limit: 2, page: 1, totalCount: 2 },
98
+ },
99
+ }),
100
+ )
101
+ .mockResolvedValueOnce(
102
+ getMockResponse({
103
+ result: {
104
+ events: [mockedERC20Transaction, mockedERC20Transaction],
105
+ pagination: { limit: 2, page: 2, totalCount: 2 },
106
+ },
107
+ }),
108
+ )
109
+ .mockResolvedValueOnce(
110
+ getMockResponse({
111
+ result: {
112
+ events: [mockedERC20Transaction],
113
+ pagination: { limit: 2, page: 3, totalCount: 2 },
114
+ },
115
+ }),
116
+ );
117
+
118
+ const params = {
119
+ limit: "2",
120
+ filterTopic0: mockedTopic0Address,
121
+ filterTopic1: mockedTopic1Address,
122
+ };
123
+ const result = await thirdwebClient.fetchERC20Transactions(mockedERC20TokenAddress1, params);
124
+
125
+ expect(result).toHaveLength(5);
126
+ expect(mockedNetwork).toHaveBeenCalledTimes(3);
127
+ });
128
+ });
129
+
130
+ describe("getERC20TransactionsForAccount", () => {
131
+ beforeEach(() => {
132
+ jest.clearAllMocks();
133
+ });
134
+
135
+ it("should return empty array without balance tokens list", async () => {
136
+ const result = await thirdwebClient.getERC20TransactionsForAccount({
137
+ address: "0.0.1234",
138
+ contractAddresses: [],
139
+ since: null,
140
+ });
141
+
142
+ expect(result).toHaveLength(0);
143
+ });
144
+
145
+ it("should return exactly 2 transactions (out & in)", async () => {
146
+ const mockFetcher = jest.fn().mockResolvedValue([mockedERC20Transaction]);
147
+
148
+ const result = await thirdwebClient.getERC20TransactionsForAccount({
149
+ address: "0.0.1234",
150
+ contractAddresses: [mockedERC20TokenAddress1],
151
+ since: null,
152
+ transactionFetcher: mockFetcher,
153
+ });
154
+
155
+ expect(result).toHaveLength(2);
156
+ expect(mockFetcher).toHaveBeenCalledTimes(2);
157
+ });
158
+
159
+ it("should return exactly 4 transactions total for 2 tokens (out & in)", async () => {
160
+ const mockFetcher = jest.fn().mockResolvedValue([mockedERC20Transaction]);
161
+
162
+ const result = await thirdwebClient.getERC20TransactionsForAccount({
163
+ address: "0.0.1234",
164
+ contractAddresses: [mockedERC20TokenAddress1, mockedERC20TokenAddress2],
165
+ since: null,
166
+ transactionFetcher: mockFetcher,
167
+ });
168
+
169
+ expect(result).toHaveLength(4);
170
+ expect(mockFetcher).toHaveBeenCalledTimes(4);
171
+ });
172
+
173
+ it("should return exactly 4 transactions for single token (out & in)", async () => {
174
+ const mockFetcher = jest
175
+ .fn()
176
+ .mockResolvedValue([mockedERC20Transaction, mockedERC20Transaction]);
177
+
178
+ const result = await thirdwebClient.getERC20TransactionsForAccount({
179
+ address: "0.0.1234",
180
+ contractAddresses: [mockedERC20TokenAddress1],
181
+ since: null,
182
+ transactionFetcher: mockFetcher,
183
+ });
184
+
185
+ expect(result).toHaveLength(4);
186
+ expect(mockFetcher).toHaveBeenCalledTimes(2);
187
+ });
188
+ });