@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
@@ -7,20 +7,26 @@ import {
7
7
  } from "@ledgerhq/errors";
8
8
  import { HEDERA_TRANSACTION_MODES } from "../constants";
9
9
  import {
10
- HederaRecipientInvalidChecksum,
11
10
  HederaInsufficientFundsForAssociation,
11
+ HederaRecipientEvmAddressVerificationRequired,
12
+ HederaRecipientInvalidChecksum,
12
13
  HederaRecipientTokenAssociationRequired,
13
14
  HederaRecipientTokenAssociationUnverified,
14
15
  } from "../errors";
15
16
  import { getTransactionStatus } from "./getTransactionStatus";
16
17
  import * as estimateFees from "../logic/estimateFees";
17
18
  import * as logicUtils from "../logic/utils";
19
+ import { rpcClient } from "../network/rpc";
18
20
  import { getMockedAccount, getMockedTokenAccount } from "../test/fixtures/account.fixture";
19
- import { getMockedTokenCurrency } from "../test/fixtures/currency.fixture";
21
+ import {
22
+ getMockedERC20TokenCurrency,
23
+ getMockedHTSTokenCurrency,
24
+ } from "../test/fixtures/currency.fixture";
20
25
  import { getMockedTransaction } from "../test/fixtures/transaction.fixture";
26
+ import type { EstimateFeesResult } from "../types";
21
27
 
22
28
  describe("getTransactionStatus", () => {
23
- const mockedEstimatedFee = new BigNumber(1);
29
+ const mockedEstimatedFee: EstimateFeesResult = { tinybars: new BigNumber(1) };
24
30
  const mockedUsdRate = new BigNumber(1);
25
31
  const validRecipientAddress = "0.0.1234567";
26
32
  const validRecipientAddressWithChecksum = "0.0.1234567-ylkls";
@@ -32,6 +38,10 @@ describe("getTransactionStatus", () => {
32
38
  jest.spyOn(estimateFees, "estimateFees").mockResolvedValueOnce(mockedEstimatedFee);
33
39
  });
34
40
 
41
+ afterAll(() => {
42
+ rpcClient._resetInstance();
43
+ });
44
+
35
45
  test("coin transfer with valid recipient and sufficient balance completes successfully", async () => {
36
46
  const mockedAccount = getMockedAccount({ balance: new BigNumber(1000) });
37
47
  const mockedTransaction = getMockedTransaction({
@@ -47,10 +57,10 @@ describe("getTransactionStatus", () => {
47
57
  expect(result.totalSpent.isGreaterThan(100)).toBe(true);
48
58
  });
49
59
 
50
- test("token transfer with valid recipient and sufficient balance completes successfully", async () => {
60
+ test("hts token transfer with valid recipient and sufficient balance completes successfully", async () => {
51
61
  jest.spyOn(logicUtils, "checkAccountTokenAssociationStatus").mockResolvedValueOnce(true);
52
62
 
53
- const tokenCurrency = getMockedTokenCurrency();
63
+ const tokenCurrency = getMockedHTSTokenCurrency();
54
64
  const tokenAccount = getMockedTokenAccount(tokenCurrency, { balance: new BigNumber(500) });
55
65
  const account = getMockedAccount({ balance: new BigNumber(1000), subAccounts: [tokenAccount] });
56
66
  const transaction = getMockedTransaction({
@@ -66,8 +76,27 @@ describe("getTransactionStatus", () => {
66
76
  expect(result.amount).toEqual(new BigNumber(200));
67
77
  });
68
78
 
79
+ test("erc20 token transfer with valid recipient and sufficient balance completes successfully", async () => {
80
+ const tokenCurrency = getMockedERC20TokenCurrency();
81
+ const tokenAccount = getMockedTokenAccount(tokenCurrency, { balance: new BigNumber(500) });
82
+ const account = getMockedAccount({ balance: new BigNumber(1000), subAccounts: [tokenAccount] });
83
+ const transaction = getMockedTransaction({
84
+ subAccountId: tokenAccount.id,
85
+ recipient: validRecipientAddress,
86
+ amount: new BigNumber(200),
87
+ });
88
+
89
+ const result = await getTransactionStatus(account, transaction);
90
+
91
+ expect(result.errors).toEqual({});
92
+ expect(result.warnings).toMatchObject({
93
+ unverifiedEvmAddress: expect.any(Error),
94
+ });
95
+ expect(result.amount).toEqual(new BigNumber(200));
96
+ });
97
+
69
98
  test("token associate transaction with sufficient USD worth completes successfully", async () => {
70
- const mockedTokenCurrency = getMockedTokenCurrency();
99
+ const mockedTokenCurrency = getMockedHTSTokenCurrency();
71
100
  const mockedAccount = getMockedAccount();
72
101
  const mockedTransaction = getMockedTransaction({
73
102
  mode: HEDERA_TRANSACTION_MODES.TokenAssociate,
@@ -81,8 +110,8 @@ describe("getTransactionStatus", () => {
81
110
  expect(result.amount).toEqual(new BigNumber(0));
82
111
  expect(result.errors).toEqual({});
83
112
  expect(result.warnings).toEqual({});
84
- expect(result.totalSpent).toEqual(mockedEstimatedFee);
85
- expect(result.estimatedFees).toEqual(mockedEstimatedFee);
113
+ expect(result.totalSpent).toEqual(mockedEstimatedFee.tinybars);
114
+ expect(result.estimatedFees).toEqual(mockedEstimatedFee.tinybars);
86
115
  });
87
116
 
88
117
  test("recipient with checksum is supported", async () => {
@@ -137,7 +166,7 @@ describe("getTransactionStatus", () => {
137
166
  });
138
167
 
139
168
  test("adds error if USD balance is too low for token association", async () => {
140
- const mockedTokenCurrency = getMockedTokenCurrency();
169
+ const mockedTokenCurrency = getMockedHTSTokenCurrency();
141
170
  const mockedAccount = getMockedAccount({ balance: new BigNumber(0) });
142
171
  const mockedTransaction = getMockedTransaction({
143
172
  mode: HEDERA_TRANSACTION_MODES.TokenAssociate,
@@ -156,7 +185,7 @@ describe("getTransactionStatus", () => {
156
185
  test("adds warning during token transfer if recipient has no token associated", async () => {
157
186
  jest.spyOn(logicUtils, "checkAccountTokenAssociationStatus").mockResolvedValueOnce(false);
158
187
 
159
- const mockedTokenCurrency = getMockedTokenCurrency();
188
+ const mockedTokenCurrency = getMockedHTSTokenCurrency();
160
189
  const mockedTokenAccount = getMockedTokenAccount(mockedTokenCurrency);
161
190
  const mockedAccount = getMockedAccount({ subAccounts: [mockedTokenAccount] });
162
191
  const mockedTransaction = getMockedTransaction({
@@ -171,12 +200,28 @@ describe("getTransactionStatus", () => {
171
200
  );
172
201
  });
173
202
 
203
+ test("adds evm address verification warning during ERC20 token transfer", async () => {
204
+ const mockedTokenCurrency = getMockedERC20TokenCurrency();
205
+ const mockedTokenAccount = getMockedTokenAccount(mockedTokenCurrency);
206
+ const mockedAccount = getMockedAccount({ subAccounts: [mockedTokenAccount] });
207
+ const mockedTransaction = getMockedTransaction({
208
+ subAccountId: mockedTokenAccount.id,
209
+ recipient: validRecipientAddress,
210
+ });
211
+
212
+ const result = await getTransactionStatus(mockedAccount, mockedTransaction);
213
+
214
+ expect(result.warnings.unverifiedEvmAddress).toBeInstanceOf(
215
+ HederaRecipientEvmAddressVerificationRequired,
216
+ );
217
+ });
218
+
174
219
  test("adds warning if token association status can't be verified", async () => {
175
220
  jest
176
221
  .spyOn(logicUtils, "checkAccountTokenAssociationStatus")
177
222
  .mockRejectedValueOnce(new HederaRecipientTokenAssociationUnverified());
178
223
 
179
- const mockedTokenCurrency = getMockedTokenCurrency();
224
+ const mockedTokenCurrency = getMockedHTSTokenCurrency();
180
225
  const mockedTokenAccount = getMockedTokenAccount(mockedTokenCurrency);
181
226
  const mockedAccount = getMockedAccount({ subAccounts: [mockedTokenAccount] });
182
227
  const mockedTransaction = getMockedTransaction({
@@ -192,7 +237,7 @@ describe("getTransactionStatus", () => {
192
237
  });
193
238
 
194
239
  test("adds error during token transfer with insufficient balance", async () => {
195
- const mockedTokenCurrency = getMockedTokenCurrency();
240
+ const mockedTokenCurrency = getMockedHTSTokenCurrency();
196
241
  const mockedTokenAccount = getMockedTokenAccount(mockedTokenCurrency, {
197
242
  balance: new BigNumber(0),
198
243
  });
@@ -6,13 +6,14 @@ import {
6
6
  RecipientRequired,
7
7
  } from "@ledgerhq/errors";
8
8
  import type { Account, AccountBridge, TokenAccount } from "@ledgerhq/types-live";
9
- import { findSubAccountById, isTokenAccount } from "@ledgerhq/coin-framework/account";
9
+ import { findSubAccountById } from "@ledgerhq/coin-framework/account";
10
10
  import { getEnv } from "@ledgerhq/live-env";
11
- import { HEDERA_OPERATION_TYPES } from "../constants";
11
+ import { HEDERA_OPERATION_TYPES, HEDERA_TRANSACTION_MODES } from "../constants";
12
12
  import {
13
13
  HederaInsufficientFundsForAssociation,
14
14
  HederaRecipientTokenAssociationRequired,
15
15
  HederaRecipientTokenAssociationUnverified,
16
+ HederaRecipientEvmAddressVerificationRequired,
16
17
  } from "../errors";
17
18
  import { estimateFees } from "../logic/estimateFees";
18
19
  import {
@@ -57,11 +58,14 @@ async function handleTokenAssociateTransaction(
57
58
 
58
59
  const [usdRate, estimatedFees] = await Promise.all([
59
60
  getCurrencyToUSDRate(account.currency),
60
- estimateFees(account.currency, HEDERA_OPERATION_TYPES.TokenAssociate),
61
+ estimateFees({
62
+ currency: account.currency,
63
+ operationType: HEDERA_OPERATION_TYPES.TokenAssociate,
64
+ }),
61
65
  ]);
62
66
 
63
67
  const amount = BigNumber(0);
64
- const totalSpent = amount.plus(estimatedFees);
68
+ const totalSpent = amount.plus(estimatedFees.tinybars);
65
69
  const isAssociationFlow = isTokenAssociationRequired(account, transaction.properties.token);
66
70
 
67
71
  if (isAssociationFlow) {
@@ -79,22 +83,26 @@ async function handleTokenAssociateTransaction(
79
83
  return {
80
84
  amount,
81
85
  totalSpent,
82
- estimatedFees,
86
+ estimatedFees: estimatedFees.tinybars,
83
87
  errors,
84
88
  warnings,
85
89
  };
86
90
  }
87
91
 
88
- async function handleTokenTransaction(
92
+ async function handleHTSTokenTransaction(
89
93
  account: Account,
90
94
  subAccount: TokenAccount,
91
95
  transaction: Transaction,
92
96
  ): Promise<TransactionStatus> {
93
97
  const errors: Errors = {};
94
98
  const warnings: Warnings = {};
99
+
95
100
  const [calculatedAmount, estimatedFees] = await Promise.all([
96
101
  calculateAmount({ transaction, account }),
97
- estimateFees(account.currency, HEDERA_OPERATION_TYPES.TokenTransfer),
102
+ estimateFees({
103
+ currency: account.currency,
104
+ operationType: HEDERA_OPERATION_TYPES.TokenTransfer,
105
+ }),
98
106
  ]);
99
107
 
100
108
  const recipientError = validateRecipient(account, transaction.recipient);
@@ -107,7 +115,7 @@ async function handleTokenTransaction(
107
115
  try {
108
116
  const hasRecipientTokenAssociated = await checkAccountTokenAssociationStatus(
109
117
  transaction.recipient,
110
- subAccount.token.contractAddress,
118
+ subAccount.token,
111
119
  );
112
120
 
113
121
  if (!hasRecipientTokenAssociated) {
@@ -126,14 +134,70 @@ async function handleTokenTransaction(
126
134
  errors.amount = new NotEnoughBalance();
127
135
  }
128
136
 
129
- if (account.balance.isLessThan(estimatedFees)) {
137
+ if (account.balance.isLessThan(estimatedFees.tinybars)) {
138
+ errors.amount = new NotEnoughBalance();
139
+ }
140
+
141
+ return {
142
+ amount: calculatedAmount.amount,
143
+ totalSpent: calculatedAmount.totalSpent,
144
+ estimatedFees: estimatedFees.tinybars,
145
+ errors,
146
+ warnings,
147
+ };
148
+ }
149
+
150
+ async function handleERC20TokenTransaction(
151
+ account: Account,
152
+ subAccount: TokenAccount,
153
+ transaction: Transaction,
154
+ ): Promise<TransactionStatus> {
155
+ const errors: Errors = {};
156
+ const warnings: Warnings = {
157
+ unverifiedEvmAddress: new HederaRecipientEvmAddressVerificationRequired(),
158
+ };
159
+
160
+ const [calculatedAmount, estimatedFees] = await Promise.all([
161
+ calculateAmount({ transaction, account }),
162
+ estimateFees({
163
+ operationType: HEDERA_OPERATION_TYPES.ContractCall,
164
+ txIntent: {
165
+ intentType: "transaction",
166
+ type: HEDERA_TRANSACTION_MODES.Send,
167
+ asset: {
168
+ type: "erc20",
169
+ assetReference: subAccount.token.contractAddress,
170
+ assetOwner: account.freshAddress,
171
+ },
172
+ amount: BigInt(transaction.amount.toString()),
173
+ sender: account.freshAddress,
174
+ recipient: transaction.recipient,
175
+ },
176
+ }),
177
+ ]);
178
+
179
+ const recipientError = validateRecipient(account, transaction.recipient);
180
+
181
+ if (recipientError) {
182
+ errors.recipient = recipientError;
183
+ }
184
+
185
+ if (transaction.amount.eq(0)) {
186
+ errors.amount = new AmountRequired();
187
+ }
188
+
189
+ if (subAccount.balance.isLessThan(calculatedAmount.totalSpent)) {
190
+ errors.amount = new NotEnoughBalance();
191
+ }
192
+
193
+ if (account.balance.isLessThan(estimatedFees.tinybars)) {
130
194
  errors.amount = new NotEnoughBalance();
131
195
  }
132
196
 
133
197
  return {
134
198
  amount: calculatedAmount.amount,
135
199
  totalSpent: calculatedAmount.totalSpent,
136
- estimatedFees,
200
+ estimatedFees: estimatedFees.tinybars,
137
201
  errors,
138
202
  warnings,
139
203
  };
@@ -145,9 +209,13 @@ async function handleCoinTransaction(
145
209
  ): Promise<TransactionStatus> {
146
210
  const errors: Errors = {};
147
211
  const warnings: Warnings = {};
212
+
148
213
  const [calculatedAmount, estimatedFees] = await Promise.all([
149
214
  calculateAmount({ transaction, account }),
150
- estimateFees(account.currency, HEDERA_OPERATION_TYPES.CryptoTransfer),
215
+ estimateFees({
216
+ currency: account.currency,
217
+ operationType: HEDERA_OPERATION_TYPES.CryptoTransfer,
218
+ }),
151
219
  ]);
152
220
 
153
221
  const recipientError = validateRecipient(account, transaction.recipient);
@@ -167,7 +235,7 @@ async function handleCoinTransaction(
167
235
  return {
168
236
  amount: calculatedAmount.amount,
169
237
  totalSpent: calculatedAmount.totalSpent,
170
- estimatedFees,
238
+ estimatedFees: estimatedFees.tinybars,
171
239
  errors,
172
240
  warnings,
173
241
  };
@@ -179,12 +247,17 @@ export const getTransactionStatus: AccountBridge<
179
247
  TransactionStatus
180
248
  >["getTransactionStatus"] = async (account, transaction) => {
181
249
  const subAccount = findSubAccountById(account, transaction?.subAccountId || "");
182
- const isTokenTransaction = isTokenAccount(subAccount);
250
+ const isHTSTokenTransaction =
251
+ transaction.mode === HEDERA_TRANSACTION_MODES.Send && subAccount?.token.tokenType === "hts";
252
+ const isERC20TokenTransaction =
253
+ transaction.mode === HEDERA_TRANSACTION_MODES.Send && subAccount?.token.tokenType === "erc20";
183
254
 
184
255
  if (isTokenAssociateTransaction(transaction)) {
185
256
  return handleTokenAssociateTransaction(account, transaction);
186
- } else if (isTokenTransaction) {
187
- return handleTokenTransaction(account, subAccount, transaction);
257
+ } else if (isHTSTokenTransaction) {
258
+ return handleHTSTokenTransaction(account, subAccount, transaction);
259
+ } else if (isERC20TokenTransaction) {
260
+ return handleERC20TokenTransaction(account, subAccount, transaction);
188
261
  } else {
189
262
  return handleCoinTransaction(account, transaction);
190
263
  }
@@ -14,7 +14,7 @@ import { getTransactionStatus } from "./getTransactionStatus";
14
14
  import { prepareTransaction } from "./prepareTransaction";
15
15
  import { receive } from "./receive";
16
16
  import { buildSignOperation } from "./signOperation";
17
- import { getAccountShape, buildIterateResult } from "./synchronisation";
17
+ import { getAccountShape, buildIterateResult, postSync } from "./synchronisation";
18
18
  import { assignFromAccountRaw, assignToAccountRaw } from "./serialization";
19
19
  import resolver from "../signer/index";
20
20
  import type { Transaction, TransactionStatus, HederaSigner, HederaAccount } from "../types";
@@ -35,7 +35,7 @@ function buildCurrencyBridge(signerContext: SignerContext<HederaSigner>): Curren
35
35
  };
36
36
  }
37
37
 
38
- const sync = makeSync({ getAccountShape });
38
+ const sync = makeSync({ getAccountShape, postSync, shouldMergeOps: false });
39
39
 
40
40
  function buildAccountBridge(
41
41
  signerContext: SignerContext<HederaSigner>,
@@ -3,21 +3,22 @@ import { createBridges } from ".";
3
3
  import { HEDERA_OPERATION_TYPES } from "../constants";
4
4
  import { estimateFees } from "../logic/estimateFees";
5
5
  import { getMockedAccount, getMockedTokenAccount } from "../test/fixtures/account.fixture";
6
- import { getMockedTokenCurrency } from "../test/fixtures/currency.fixture";
6
+ import { getMockedHTSTokenCurrency } from "../test/fixtures/currency.fixture";
7
+ import type { EstimateFeesResult } from "../types";
7
8
 
8
9
  describe("js-estimateMaxSpendable", () => {
9
10
  let bridge: ReturnType<typeof createBridges>;
10
- let estimatedFees: Record<"crypto", BigNumber>;
11
+ let estimatedFees: Record<"crypto", EstimateFeesResult>;
11
12
 
12
13
  beforeAll(async () => {
13
14
  const signer = jest.fn();
14
15
  bridge = createBridges(signer);
15
16
 
16
17
  const mockedAccount = getMockedAccount();
17
- const crypto = await estimateFees(
18
- mockedAccount.currency,
19
- HEDERA_OPERATION_TYPES.CryptoTransfer,
20
- );
18
+ const crypto = await estimateFees({
19
+ currency: mockedAccount.currency,
20
+ operationType: HEDERA_OPERATION_TYPES.CryptoTransfer,
21
+ });
21
22
 
22
23
  estimatedFees = { crypto };
23
24
  });
@@ -29,11 +30,13 @@ describe("js-estimateMaxSpendable", () => {
29
30
  account: mockedAccount,
30
31
  });
31
32
 
32
- expect(result).toEqual(mockedAccount.balance.minus(estimatedFees.crypto));
33
+ const expected = mockedAccount.balance.minus(estimatedFees.crypto.tinybars);
34
+
35
+ expect(result).toEqual(expected);
33
36
  });
34
37
 
35
38
  test("estimateMaxSpendable returns 0 if balance < estimated fees", async () => {
36
- const mockedAccount = getMockedAccount({ balance: estimatedFees.crypto.minus(1) });
39
+ const mockedAccount = getMockedAccount({ balance: estimatedFees.crypto.tinybars.minus(1) });
37
40
 
38
41
  const result = await bridge.accountBridge.estimateMaxSpendable({
39
42
  account: mockedAccount,
@@ -43,7 +46,7 @@ describe("js-estimateMaxSpendable", () => {
43
46
  });
44
47
 
45
48
  test("estimateMaxSpendable returns token balance for token account", async () => {
46
- const mockedTokenCurrency = getMockedTokenCurrency();
49
+ const mockedTokenCurrency = getMockedHTSTokenCurrency();
47
50
  const mockedTokenAccount = getMockedTokenAccount(mockedTokenCurrency);
48
51
  const mockedAccount = getMockedAccount({ subAccounts: [mockedTokenAccount] });
49
52
 
@@ -4,17 +4,19 @@ import { prepareTransaction } from "./prepareTransaction";
4
4
  import { getMockedAccount } from "../test/fixtures/account.fixture";
5
5
  import { getMockedTransaction } from "../test/fixtures/transaction.fixture";
6
6
  import * as utils from "./utils";
7
+ import type { EstimateFeesResult } from "../types";
7
8
 
8
9
  jest.mock("../logic/estimateFees");
9
10
 
10
11
  describe("prepareTransaction", () => {
11
12
  const mockAccount = getMockedAccount();
12
13
  const mockTx = getMockedTransaction();
14
+ const mockFeeEstimation: EstimateFeesResult = { tinybars: new BigNumber(10) };
13
15
 
14
16
  beforeEach(() => {
15
17
  jest.clearAllMocks();
16
18
 
17
- (estimateFees as jest.Mock).mockResolvedValue(Promise.resolve(new BigNumber(10)));
19
+ (estimateFees as jest.Mock).mockResolvedValue(mockFeeEstimation);
18
20
  jest
19
21
  .spyOn(utils, "calculateAmount")
20
22
  .mockResolvedValue(
@@ -1,9 +1,9 @@
1
- import { findSubAccountById, isTokenAccount } from "@ledgerhq/coin-framework/account/helpers";
1
+ import { findSubAccountById } from "@ledgerhq/coin-framework/account/helpers";
2
2
  import type { AccountBridge } from "@ledgerhq/types-live";
3
- import { HEDERA_OPERATION_TYPES } from "../constants";
3
+ import { HEDERA_OPERATION_TYPES, HEDERA_TRANSACTION_MODES } from "../constants";
4
4
  import { estimateFees } from "../logic/estimateFees";
5
5
  import { isTokenAssociateTransaction } from "../logic/utils";
6
- import type { Transaction } from "../types";
6
+ import type { EstimateFeesParams, Transaction } from "../types";
7
7
  import { calculateAmount } from "./utils";
8
8
 
9
9
  /**
@@ -18,31 +18,66 @@ export const prepareTransaction: AccountBridge<Transaction>["prepareTransaction"
18
18
  account,
19
19
  transaction,
20
20
  ): Promise<Transaction> => {
21
- const subAccount = findSubAccountById(account, transaction?.subAccountId || "");
22
- const isTokenTransaction = isTokenAccount(subAccount);
21
+ let estimateFeesParams: EstimateFeesParams;
23
22
  let operationType: HEDERA_OPERATION_TYPES;
23
+ const subAccount = findSubAccountById(account, transaction?.subAccountId || "");
24
+ const isHTSTokenTransaction =
25
+ transaction.mode === HEDERA_TRANSACTION_MODES.Send && subAccount?.token.tokenType === "hts";
26
+ const isERC20TokenTransaction =
27
+ transaction.mode === HEDERA_TRANSACTION_MODES.Send && subAccount?.token.tokenType === "erc20";
24
28
 
25
29
  if (isTokenAssociateTransaction(transaction)) {
26
30
  operationType = HEDERA_OPERATION_TYPES.TokenAssociate;
27
- } else if (isTokenTransaction) {
31
+ } else if (isHTSTokenTransaction) {
28
32
  operationType = HEDERA_OPERATION_TYPES.TokenTransfer;
33
+ } else if (isERC20TokenTransaction) {
34
+ operationType = HEDERA_OPERATION_TYPES.ContractCall;
29
35
  } else {
30
36
  operationType = HEDERA_OPERATION_TYPES.CryptoTransfer;
31
37
  }
32
38
 
39
+ // build different estimation params for ERC20 ContractCall transactions
40
+ if (operationType === HEDERA_OPERATION_TYPES.ContractCall) {
41
+ estimateFeesParams = {
42
+ operationType,
43
+ txIntent: {
44
+ intentType: "transaction",
45
+ type: HEDERA_TRANSACTION_MODES.Send,
46
+ asset: {
47
+ type: "erc20",
48
+ assetReference: subAccount?.token.contractAddress ?? "",
49
+ assetOwner: account.freshAddress,
50
+ },
51
+ amount: BigInt(transaction.amount.toString()),
52
+ sender: account.freshAddress,
53
+ recipient: transaction.recipient,
54
+ },
55
+ };
56
+ } else {
57
+ estimateFeesParams = {
58
+ currency: account.currency,
59
+ operationType,
60
+ };
61
+ }
62
+
33
63
  // explicitly calculate transaction amount to account for `useAllAmount` flag (send max flow)
34
64
  // i.e. if `useAllAmount` has been toggled to true, this is where it will update the transaction to reflect that action
35
- const [{ amount }, estimatedFees] = await Promise.all([
65
+ const [calculatedAmount, estimatedFees] = await Promise.all([
36
66
  calculateAmount({ account, transaction }),
37
- estimateFees(account.currency, operationType),
67
+ estimateFees(estimateFeesParams),
38
68
  ]);
39
69
 
70
+ transaction.amount = calculatedAmount.amount;
71
+
40
72
  // `maxFee` must be explicitly set to avoid the @hashgraph/sdk default fallback
41
73
  // this ensures device app validation passes (e.g. during swap flow)
42
74
  // it's applied via `tx.setMaxTransactionFee` when building the transaction
43
- transaction.maxFee = estimatedFees;
75
+ transaction.maxFee = estimatedFees.tinybars;
44
76
 
45
- transaction.amount = amount;
77
+ // ERC20 transactions should have gas limit set (tinybars fee is calculated based on gas)
78
+ if (isERC20TokenTransaction && estimatedFees.gas) {
79
+ transaction.gasLimit = estimatedFees.gas;
80
+ }
46
81
 
47
82
  return transaction;
48
83
  };
@@ -2,9 +2,9 @@ import { Observable } from "rxjs";
2
2
  import { Account, AccountBridge } from "@ledgerhq/types-live";
3
3
  import { AssetInfo, FeeEstimation } from "@ledgerhq/coin-framework/api/types";
4
4
  import { SignerContext } from "@ledgerhq/coin-framework/signer";
5
- import { findSubAccountById, isTokenAccount } from "@ledgerhq/coin-framework/account/helpers";
5
+ import { findSubAccountById } from "@ledgerhq/coin-framework/account/helpers";
6
6
  import { buildOptimisticOperation } from "./buildOptimisticOperation";
7
- import { HEDERA_TRANSACTION_MODES } from "../constants";
7
+ import { DEFAULT_GAS_LIMIT, HEDERA_TRANSACTION_MODES } from "../constants";
8
8
  import { combine } from "../logic/combine";
9
9
  import { craftTransaction } from "../logic/craftTransaction";
10
10
  import {
@@ -13,7 +13,7 @@ import {
13
13
  getHederaTransactionBodyBytes,
14
14
  isTokenAssociateTransaction,
15
15
  } from "../logic/utils";
16
- import type { Transaction, HederaSigner } from "../types";
16
+ import type { Transaction, HederaSigner, HederaTxData } from "../types";
17
17
 
18
18
  export const buildSignOperation =
19
19
  (
@@ -29,10 +29,16 @@ export const buildSignOperation =
29
29
 
30
30
  let type: Transaction["mode"];
31
31
  let asset: AssetInfo;
32
+ let data: HederaTxData | undefined;
32
33
  const accountAddress = account.freshAddress;
33
34
  const accountPublicKey = account.seedIdentifier;
34
35
  const subAccount = findSubAccountById(account, transaction.subAccountId || "");
35
- const isTokenTransaction = isTokenAccount(subAccount);
36
+ const isHTSTokenTransaction =
37
+ transaction.mode === HEDERA_TRANSACTION_MODES.Send &&
38
+ subAccount?.token.tokenType === "hts";
39
+ const isERC20TokenTransaction =
40
+ transaction.mode === HEDERA_TRANSACTION_MODES.Send &&
41
+ subAccount?.token.tokenType === "erc20";
36
42
 
37
43
  if (isTokenAssociateTransaction(transaction)) {
38
44
  type = HEDERA_TRANSACTION_MODES.TokenAssociate;
@@ -40,13 +46,24 @@ export const buildSignOperation =
40
46
  type: transaction.properties.token.tokenType,
41
47
  assetReference: transaction.properties.token.contractAddress,
42
48
  };
43
- } else if (isTokenTransaction) {
49
+ } else if (isHTSTokenTransaction) {
44
50
  type = HEDERA_TRANSACTION_MODES.Send;
45
51
  asset = {
46
52
  type: subAccount.token.tokenType,
47
53
  assetReference: subAccount.token.contractAddress,
48
54
  assetOwner: accountAddress,
49
55
  };
56
+ } else if (isERC20TokenTransaction) {
57
+ type = HEDERA_TRANSACTION_MODES.Send;
58
+ asset = {
59
+ type: subAccount.token.tokenType,
60
+ assetReference: subAccount.token.contractAddress,
61
+ assetOwner: accountAddress,
62
+ };
63
+ data = {
64
+ type: "erc20",
65
+ gasLimit: BigInt((transaction.gasLimit ?? DEFAULT_GAS_LIMIT).toString()),
66
+ };
50
67
  } else {
51
68
  type = HEDERA_TRANSACTION_MODES.Send;
52
69
  asset = {
@@ -72,6 +89,7 @@ export const buildSignOperation =
72
89
  type: "string",
73
90
  value: transaction.memo ?? "",
74
91
  },
92
+ ...(data && { data }),
75
93
  },
76
94
  customFees,
77
95
  );
@@ -0,0 +1,67 @@
1
+ import type { Account } from "@ledgerhq/types-live";
2
+ import { postSync } from "./synchronisation";
3
+ import { getMockedAccount, getMockedTokenAccount } from "../test/fixtures/account.fixture";
4
+ import { getMockedOperation } from "../test/fixtures/operation.fixture";
5
+ import { getMockedHTSTokenCurrency } from "../test/fixtures/currency.fixture";
6
+
7
+ describe("postSync", () => {
8
+ it("should remove pending operations that match confirmed ERC20 operations", () => {
9
+ const confirmedERC20Ops = [
10
+ getMockedOperation({ hash: "hash1", standard: "erc20" }),
11
+ getMockedOperation({ hash: "hash2", standard: "erc20" }),
12
+ ];
13
+
14
+ const initialAccount = {} as Account;
15
+ const syncedAccount = getMockedAccount({
16
+ operations: [...confirmedERC20Ops, getMockedOperation({ hash: "otherHash" })],
17
+ pendingOperations: [
18
+ getMockedOperation({ hash: "hash1" }),
19
+ getMockedOperation({ hash: "hash2" }),
20
+ getMockedOperation({ hash: "hash3" }),
21
+ ],
22
+ });
23
+
24
+ const result = postSync(initialAccount, syncedAccount);
25
+
26
+ expect(result.pendingOperations).toHaveLength(1);
27
+ expect(result.pendingOperations).toMatchObject([{ hash: "hash3" }]);
28
+ });
29
+
30
+ it("should filter pending operations from subaccounts", () => {
31
+ const mockToken1 = getMockedHTSTokenCurrency();
32
+
33
+ const confirmedERC20Ops = [
34
+ getMockedOperation({ hash: "hash1", standard: "erc20" }),
35
+ getMockedOperation({ hash: "hash2", standard: "erc20" }),
36
+ ];
37
+
38
+ const subAccounts = [
39
+ getMockedTokenAccount(mockToken1, {
40
+ pendingOperations: [
41
+ getMockedOperation({ hash: "hash1" }),
42
+ getMockedOperation({ hash: "hash4" }),
43
+ ],
44
+ }),
45
+ getMockedTokenAccount(mockToken1, {
46
+ pendingOperations: [
47
+ getMockedOperation({ hash: "hash2" }),
48
+ getMockedOperation({ hash: "hash5" }),
49
+ ],
50
+ }),
51
+ ];
52
+
53
+ const initialAccount = {} as Account;
54
+ const syncedAccount = getMockedAccount({
55
+ operations: [...confirmedERC20Ops, getMockedOperation({ hash: "otherHash" })],
56
+ subAccounts,
57
+ });
58
+
59
+ const result = postSync(initialAccount, syncedAccount);
60
+
61
+ expect(result.subAccounts).toHaveLength(2);
62
+ expect(result.subAccounts).toMatchObject([
63
+ { pendingOperations: [{ hash: "hash4" }] },
64
+ { pendingOperations: [{ hash: "hash5" }] },
65
+ ]);
66
+ });
67
+ });