@ledgerhq/coin-celo 1.3.0 → 1.3.1-nightly.0

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 (187) hide show
  1. package/.unimportedrc.json +3 -6
  2. package/CHANGELOG.md +8 -0
  3. package/lib/__tests__/bridge/buildOptimisticOperation.test.d.ts +2 -0
  4. package/lib/__tests__/bridge/buildOptimisticOperation.test.d.ts.map +1 -0
  5. package/lib/__tests__/bridge/buildOptimisticOperation.test.js +23 -0
  6. package/lib/__tests__/bridge/buildOptimisticOperation.test.js.map +1 -0
  7. package/lib/__tests__/bridge/buildTransaction.test.d.ts +2 -0
  8. package/lib/__tests__/bridge/buildTransaction.test.d.ts.map +1 -0
  9. package/lib/__tests__/bridge/buildTransaction.test.js +412 -0
  10. package/lib/__tests__/bridge/buildTransaction.test.js.map +1 -0
  11. package/lib/__tests__/bridge/createTransaction.test.d.ts +2 -0
  12. package/lib/__tests__/bridge/createTransaction.test.d.ts.map +1 -0
  13. package/lib/__tests__/bridge/createTransaction.test.js +22 -0
  14. package/lib/__tests__/bridge/createTransaction.test.js.map +1 -0
  15. package/lib/__tests__/bridge/deviceTransactionConfig.test.d.ts +2 -0
  16. package/lib/__tests__/bridge/deviceTransactionConfig.test.d.ts.map +1 -0
  17. package/lib/__tests__/bridge/deviceTransactionConfig.test.js +21 -0
  18. package/lib/__tests__/bridge/deviceTransactionConfig.test.js.map +1 -0
  19. package/lib/__tests__/bridge/estimateMaxSpendable.test.d.ts +2 -0
  20. package/lib/__tests__/bridge/estimateMaxSpendable.test.d.ts.map +1 -0
  21. package/lib/__tests__/bridge/estimateMaxSpendable.test.js +108 -0
  22. package/lib/__tests__/bridge/estimateMaxSpendable.test.js.map +1 -0
  23. package/lib/__tests__/bridge/getFeesForTransaction.test.d.ts +2 -0
  24. package/lib/__tests__/bridge/getFeesForTransaction.test.d.ts.map +1 -0
  25. package/lib/__tests__/bridge/getFeesForTransaction.test.js +211 -0
  26. package/lib/__tests__/bridge/getFeesForTransaction.test.js.map +1 -0
  27. package/lib/__tests__/bridge/getTransactionStatus.test.d.ts +2 -0
  28. package/lib/__tests__/bridge/getTransactionStatus.test.d.ts.map +1 -0
  29. package/lib/__tests__/bridge/getTransactionStatus.test.js +214 -0
  30. package/lib/__tests__/bridge/getTransactionStatus.test.js.map +1 -0
  31. package/lib/__tests__/bridge/logic.test.d.ts +2 -0
  32. package/lib/__tests__/bridge/logic.test.d.ts.map +1 -0
  33. package/lib/__tests__/bridge/logic.test.js +490 -0
  34. package/lib/__tests__/bridge/logic.test.js.map +1 -0
  35. package/lib/__tests__/bridge/prepareTransaction.test.d.ts +2 -0
  36. package/lib/__tests__/bridge/prepareTransaction.test.d.ts.map +1 -0
  37. package/lib/__tests__/bridge/prepareTransaction.test.js +239 -0
  38. package/lib/__tests__/bridge/prepareTransaction.test.js.map +1 -0
  39. package/lib/bridge/buildOptimisticOperation.d.ts.map +1 -1
  40. package/lib/bridge/buildOptimisticOperation.js +5 -2
  41. package/lib/bridge/buildOptimisticOperation.js.map +1 -1
  42. package/lib/bridge/buildTransaction.d.ts.map +1 -1
  43. package/lib/bridge/buildTransaction.js +29 -6
  44. package/lib/bridge/buildTransaction.js.map +1 -1
  45. package/lib/bridge/estimateMaxSpendable.d.ts.map +1 -1
  46. package/lib/bridge/estimateMaxSpendable.js +9 -1
  47. package/lib/bridge/estimateMaxSpendable.js.map +1 -1
  48. package/lib/bridge/fixtures.d.ts +36 -0
  49. package/lib/bridge/fixtures.d.ts.map +1 -0
  50. package/lib/bridge/fixtures.js +98 -0
  51. package/lib/bridge/fixtures.js.map +1 -0
  52. package/lib/bridge/getFeesForTransaction.d.ts.map +1 -1
  53. package/lib/bridge/getFeesForTransaction.js +32 -5
  54. package/lib/bridge/getFeesForTransaction.js.map +1 -1
  55. package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
  56. package/lib/bridge/getTransactionStatus.js +18 -4
  57. package/lib/bridge/getTransactionStatus.js.map +1 -1
  58. package/lib/bridge/index.d.ts.map +1 -1
  59. package/lib/bridge/index.js.map +1 -1
  60. package/lib/bridge/prepareTransaction.d.ts.map +1 -1
  61. package/lib/bridge/prepareTransaction.js +33 -4
  62. package/lib/bridge/prepareTransaction.js.map +1 -1
  63. package/lib/bridge/signOperation.d.ts.map +1 -1
  64. package/lib/bridge/signOperation.js +11 -3
  65. package/lib/bridge/signOperation.js.map +1 -1
  66. package/lib/bridge/synchronisation.d.ts.map +1 -1
  67. package/lib/bridge/synchronisation.js +7 -3
  68. package/lib/bridge/synchronisation.js.map +1 -1
  69. package/lib/config.d.ts.map +1 -1
  70. package/lib/config.js +8 -0
  71. package/lib/config.js.map +1 -1
  72. package/lib/constants.d.ts +6 -0
  73. package/lib/constants.d.ts.map +1 -0
  74. package/lib/constants.js +15 -0
  75. package/lib/constants.js.map +1 -0
  76. package/lib/types/types.d.ts +1 -0
  77. package/lib/types/types.d.ts.map +1 -1
  78. package/lib/types/types.js.map +1 -1
  79. package/lib-es/__tests__/bridge/buildOptimisticOperation.test.d.ts +2 -0
  80. package/lib-es/__tests__/bridge/buildOptimisticOperation.test.d.ts.map +1 -0
  81. package/lib-es/__tests__/bridge/buildOptimisticOperation.test.js +18 -0
  82. package/lib-es/__tests__/bridge/buildOptimisticOperation.test.js.map +1 -0
  83. package/lib-es/__tests__/bridge/buildTransaction.test.d.ts +2 -0
  84. package/lib-es/__tests__/bridge/buildTransaction.test.d.ts.map +1 -0
  85. package/lib-es/__tests__/bridge/buildTransaction.test.js +407 -0
  86. package/lib-es/__tests__/bridge/buildTransaction.test.js.map +1 -0
  87. package/lib-es/__tests__/bridge/createTransaction.test.d.ts +2 -0
  88. package/lib-es/__tests__/bridge/createTransaction.test.d.ts.map +1 -0
  89. package/lib-es/__tests__/bridge/createTransaction.test.js +17 -0
  90. package/lib-es/__tests__/bridge/createTransaction.test.js.map +1 -0
  91. package/lib-es/__tests__/bridge/deviceTransactionConfig.test.d.ts +2 -0
  92. package/lib-es/__tests__/bridge/deviceTransactionConfig.test.d.ts.map +1 -0
  93. package/lib-es/__tests__/bridge/deviceTransactionConfig.test.js +16 -0
  94. package/lib-es/__tests__/bridge/deviceTransactionConfig.test.js.map +1 -0
  95. package/lib-es/__tests__/bridge/estimateMaxSpendable.test.d.ts +2 -0
  96. package/lib-es/__tests__/bridge/estimateMaxSpendable.test.d.ts.map +1 -0
  97. package/lib-es/__tests__/bridge/estimateMaxSpendable.test.js +103 -0
  98. package/lib-es/__tests__/bridge/estimateMaxSpendable.test.js.map +1 -0
  99. package/lib-es/__tests__/bridge/getFeesForTransaction.test.d.ts +2 -0
  100. package/lib-es/__tests__/bridge/getFeesForTransaction.test.d.ts.map +1 -0
  101. package/lib-es/__tests__/bridge/getFeesForTransaction.test.js +206 -0
  102. package/lib-es/__tests__/bridge/getFeesForTransaction.test.js.map +1 -0
  103. package/lib-es/__tests__/bridge/getTransactionStatus.test.d.ts +2 -0
  104. package/lib-es/__tests__/bridge/getTransactionStatus.test.d.ts.map +1 -0
  105. package/lib-es/__tests__/bridge/getTransactionStatus.test.js +209 -0
  106. package/lib-es/__tests__/bridge/getTransactionStatus.test.js.map +1 -0
  107. package/lib-es/__tests__/bridge/logic.test.d.ts +2 -0
  108. package/lib-es/__tests__/bridge/logic.test.d.ts.map +1 -0
  109. package/lib-es/__tests__/bridge/logic.test.js +485 -0
  110. package/lib-es/__tests__/bridge/logic.test.js.map +1 -0
  111. package/lib-es/__tests__/bridge/prepareTransaction.test.d.ts +2 -0
  112. package/lib-es/__tests__/bridge/prepareTransaction.test.d.ts.map +1 -0
  113. package/lib-es/__tests__/bridge/prepareTransaction.test.js +234 -0
  114. package/lib-es/__tests__/bridge/prepareTransaction.test.js.map +1 -0
  115. package/lib-es/bridge/buildOptimisticOperation.d.ts.map +1 -1
  116. package/lib-es/bridge/buildOptimisticOperation.js +5 -2
  117. package/lib-es/bridge/buildOptimisticOperation.js.map +1 -1
  118. package/lib-es/bridge/buildTransaction.d.ts.map +1 -1
  119. package/lib-es/bridge/buildTransaction.js +29 -6
  120. package/lib-es/bridge/buildTransaction.js.map +1 -1
  121. package/lib-es/bridge/estimateMaxSpendable.d.ts.map +1 -1
  122. package/lib-es/bridge/estimateMaxSpendable.js +10 -2
  123. package/lib-es/bridge/estimateMaxSpendable.js.map +1 -1
  124. package/lib-es/bridge/fixtures.d.ts +36 -0
  125. package/lib-es/bridge/fixtures.d.ts.map +1 -0
  126. package/lib-es/bridge/fixtures.js +89 -0
  127. package/lib-es/bridge/fixtures.js.map +1 -0
  128. package/lib-es/bridge/getFeesForTransaction.d.ts.map +1 -1
  129. package/lib-es/bridge/getFeesForTransaction.js +29 -5
  130. package/lib-es/bridge/getFeesForTransaction.js.map +1 -1
  131. package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
  132. package/lib-es/bridge/getTransactionStatus.js +18 -4
  133. package/lib-es/bridge/getTransactionStatus.js.map +1 -1
  134. package/lib-es/bridge/index.d.ts.map +1 -1
  135. package/lib-es/bridge/index.js.map +1 -1
  136. package/lib-es/bridge/prepareTransaction.d.ts.map +1 -1
  137. package/lib-es/bridge/prepareTransaction.js +33 -4
  138. package/lib-es/bridge/prepareTransaction.js.map +1 -1
  139. package/lib-es/bridge/signOperation.d.ts.map +1 -1
  140. package/lib-es/bridge/signOperation.js +11 -3
  141. package/lib-es/bridge/signOperation.js.map +1 -1
  142. package/lib-es/bridge/synchronisation.d.ts.map +1 -1
  143. package/lib-es/bridge/synchronisation.js +7 -3
  144. package/lib-es/bridge/synchronisation.js.map +1 -1
  145. package/lib-es/config.d.ts.map +1 -1
  146. package/lib-es/config.js +8 -0
  147. package/lib-es/config.js.map +1 -1
  148. package/lib-es/constants.d.ts +6 -0
  149. package/lib-es/constants.d.ts.map +1 -0
  150. package/lib-es/constants.js +11 -0
  151. package/lib-es/constants.js.map +1 -0
  152. package/lib-es/types/types.d.ts +1 -0
  153. package/lib-es/types/types.d.ts.map +1 -1
  154. package/lib-es/types/types.js.map +1 -1
  155. package/package.json +9 -8
  156. package/src/__tests__/bridge/buildOptimisticOperation.test.ts +29 -0
  157. package/src/__tests__/bridge/buildTransaction.test.ts +502 -0
  158. package/src/__tests__/bridge/createTransaction.test.ts +17 -0
  159. package/src/__tests__/bridge/deviceTransactionConfig.test.ts +16 -0
  160. package/src/__tests__/bridge/estimateMaxSpendable.test.ts +112 -0
  161. package/src/__tests__/bridge/getFeesForTransaction.test.ts +233 -0
  162. package/src/__tests__/bridge/getTransactionStatus.test.ts +261 -0
  163. package/src/__tests__/bridge/logic.test.ts +538 -0
  164. package/src/__tests__/bridge/prepareTransaction.test.ts +261 -0
  165. package/src/bridge/buildOptimisticOperation.ts +6 -2
  166. package/src/bridge/buildTransaction.ts +38 -7
  167. package/src/bridge/estimateMaxSpendable.ts +18 -2
  168. package/src/bridge/fixtures.ts +103 -0
  169. package/src/bridge/getFeesForTransaction.ts +39 -5
  170. package/src/bridge/getTransactionStatus.ts +21 -4
  171. package/src/bridge/index.ts +0 -1
  172. package/src/bridge/prepareTransaction.ts +39 -6
  173. package/src/bridge/signOperation.ts +19 -4
  174. package/src/bridge/synchronisation.ts +10 -4
  175. package/src/config.ts +8 -0
  176. package/src/constants.ts +15 -0
  177. package/src/logic.ts +1 -1
  178. package/src/types/types.ts +1 -0
  179. package/lib/test/please-add-coverage.test.d.ts +0 -2
  180. package/lib/test/please-add-coverage.test.d.ts.map +0 -1
  181. package/lib/test/please-add-coverage.test.js +0 -6
  182. package/lib/test/please-add-coverage.test.js.map +0 -1
  183. package/lib-es/test/please-add-coverage.test.d.ts +0 -1
  184. package/lib-es/test/please-add-coverage.test.d.ts.map +0 -1
  185. package/lib-es/test/please-add-coverage.test.js +0 -5
  186. package/lib-es/test/please-add-coverage.test.js.map +0 -1
  187. package/src/test/please-add-coverage.test.ts +0 -3
@@ -0,0 +1,261 @@
1
+ import BigNumber from "bignumber.js";
2
+ import {
3
+ accountFixture,
4
+ accountWithTokenAccountFixture,
5
+ tokenTransactionFixture,
6
+ transactionFixture,
7
+ } from "../../bridge/fixtures";
8
+ import prepareTransaction from "../../bridge/prepareTransaction";
9
+
10
+ const chainIdMock = jest.fn();
11
+ const nonceMock = jest.fn();
12
+
13
+ jest.mock("../../network/sdk", () => {
14
+ return {
15
+ celoKit: jest.fn(() => ({
16
+ contracts: {
17
+ getLockedGold: jest.fn(async () => ({
18
+ address: "address",
19
+ lock: jest.fn(() => ({
20
+ txo: {
21
+ encodeABI: jest.fn(() => "0x6C6F636B5F64617461"), // lock_data
22
+ estimateGas: jest.fn(async () => 2),
23
+ },
24
+ })),
25
+ unlock: jest.fn(() => ({
26
+ txo: {
27
+ encodeABI: jest.fn(() => "0x756E6C6F636B5F64617461"), // unlock_data
28
+ estimateGas: jest.fn(async () => 3),
29
+ },
30
+ })),
31
+ withdraw: jest.fn(() => ({
32
+ txo: {
33
+ encodeABI: jest.fn(() => "0x77697468647261775F64617461"), // withdraw_data
34
+ estimateGas: jest.fn(async () => 3),
35
+ },
36
+ })),
37
+ vote: jest.fn(() => ({
38
+ txo: {
39
+ encodeABI: jest.fn(() => "0x766F74655F64617461"), // vote_data
40
+ estimateGas: jest.fn(async () => 3),
41
+ },
42
+ })),
43
+ getAccountNonvotingLockedGold: jest.fn(() => BigNumber(0)),
44
+ })),
45
+ getGoldToken: jest.fn(async () => ({
46
+ address: "gold_token_address",
47
+ transfer: jest.fn(() => ({
48
+ txo: {
49
+ encodeABI: jest.fn(() => "0x73656E645F64617461"), // send_data
50
+ },
51
+ })),
52
+ })),
53
+ getStableToken: jest.fn(async () => ({
54
+ address: "stable_token_address",
55
+ transfer: jest.fn(() => ({
56
+ txo: {
57
+ encodeABI: jest.fn(() => "0x73656E645F746F6B656E5F64617461"), // stable_token_data
58
+ },
59
+ })),
60
+ })),
61
+ getErc20: jest.fn(async () => ({
62
+ address: "erc20_token_address",
63
+ transfer: jest.fn(() => ({
64
+ txo: {
65
+ encodeABI: jest.fn(() => "0x73656E645F65726332305F746F6B656E5F64617461"), // send_erc20_token_data
66
+ },
67
+ })),
68
+ })),
69
+ },
70
+ connection: {
71
+ chainId: chainIdMock,
72
+ nonce: nonceMock,
73
+ estimateGasWithInflationFactor: jest.fn().mockReturnValue(3),
74
+ gasPrice: jest.fn(async () => BigNumber(2)),
75
+ web3: { eth: { getBlock: jest.fn().mockResolvedValue({ baseFeePerGas: 10 }) } },
76
+ getMaxPriorityFeePerGas: jest.fn().mockResolvedValue(1),
77
+ },
78
+ })),
79
+ };
80
+ });
81
+
82
+ describe("prepareTransaction", () => {
83
+ it("should return the transaction if the address is invalid", async () => {
84
+ const transaction = await prepareTransaction(accountFixture, transactionFixture);
85
+ expect(transaction).toMatchObject({
86
+ amount: BigNumber(10),
87
+ recipient: "recipient",
88
+ useAllAmount: false,
89
+ family: "celo",
90
+ mode: "send",
91
+ index: 0,
92
+ fees: null,
93
+ });
94
+ });
95
+
96
+ it("should return the transaction if it doesn't have a recipient", async () => {
97
+ const transaction = await prepareTransaction(accountFixture, {
98
+ ...transactionFixture,
99
+ recipient: "",
100
+ });
101
+ expect(transaction).toMatchObject({
102
+ amount: BigNumber(10),
103
+ recipient: "",
104
+ useAllAmount: false,
105
+ family: "celo",
106
+ mode: "send",
107
+ index: 0,
108
+ fees: null,
109
+ });
110
+ });
111
+
112
+ it("should return the vote transaction if it doesn't have a recipient", async () => {
113
+ const transaction = await prepareTransaction(accountFixture, {
114
+ ...transactionFixture,
115
+ recipient: "",
116
+ mode: "vote",
117
+ });
118
+ expect(transaction).toMatchObject({
119
+ amount: BigNumber(10),
120
+ recipient: "",
121
+ useAllAmount: false,
122
+ family: "celo",
123
+ mode: "vote",
124
+ index: 0,
125
+ fees: null,
126
+ });
127
+ });
128
+
129
+ it("should return the vote transaction if the amount is less than or equal to zero", async () => {
130
+ const transaction = await prepareTransaction(accountFixture, {
131
+ ...transactionFixture,
132
+ recipient: "0x79D5A290D7ba4b99322d91b577589e8d0BF87072",
133
+ mode: "vote",
134
+ amount: BigNumber(0),
135
+ });
136
+ expect(transaction).toMatchObject({
137
+ amount: BigNumber(0),
138
+ recipient: "0x79D5A290D7ba4b99322d91b577589e8d0BF87072",
139
+ useAllAmount: false,
140
+ family: "celo",
141
+ mode: "vote",
142
+ index: 0,
143
+ fees: null,
144
+ });
145
+ });
146
+
147
+ it("should return the prepared transaction", async () => {
148
+ const transaction = await prepareTransaction(
149
+ { ...accountFixture, balance: BigNumber(222222), spendableBalance: BigNumber(222222) },
150
+ {
151
+ ...transactionFixture,
152
+ recipient: "0x79D5A290D7ba4b99322d91b577589e8d0BF87072",
153
+ amount: BigNumber(22),
154
+ },
155
+ );
156
+ expect(transaction).toMatchObject({
157
+ amount: BigNumber(22),
158
+ recipient: "0x79D5A290D7ba4b99322d91b577589e8d0BF87072",
159
+ useAllAmount: false,
160
+ family: "celo",
161
+ mode: "send",
162
+ index: 0,
163
+ fees: BigNumber(24),
164
+ });
165
+ });
166
+
167
+ it("should return the prepared stable token transaction", async () => {
168
+ const transaction = await prepareTransaction(
169
+ {
170
+ ...accountWithTokenAccountFixture,
171
+ balance: BigNumber(222222),
172
+ spendableBalance: BigNumber(222222),
173
+ subAccounts: [
174
+ {
175
+ ...accountWithTokenAccountFixture.subAccounts[0],
176
+ token: {
177
+ ...accountWithTokenAccountFixture.subAccounts[0].token,
178
+ id: "cEUR",
179
+ },
180
+ },
181
+ ],
182
+ },
183
+ {
184
+ ...tokenTransactionFixture,
185
+ recipient: "0x79D5A290D7ba4b99322d91b577589e8d0BF87072",
186
+ amount: BigNumber(22),
187
+ },
188
+ );
189
+ expect(transaction).toMatchObject({
190
+ amount: BigNumber(22),
191
+ recipient: "0x79D5A290D7ba4b99322d91b577589e8d0BF87072",
192
+ useAllAmount: false,
193
+ family: "celo",
194
+ mode: "send",
195
+ index: 0,
196
+ fees: BigNumber(24),
197
+ subAccountId: "subAccountId",
198
+ maxFeePerGas: "1000000010",
199
+ maxPriorityFeePerGas: 1,
200
+ data: Buffer.from("0x73656E645F746F6B656E5F64617461"),
201
+ });
202
+ });
203
+
204
+ it("should return the prepared token transaction", async () => {
205
+ const transaction = await prepareTransaction(
206
+ {
207
+ ...accountWithTokenAccountFixture,
208
+ balance: BigNumber(222222),
209
+ spendableBalance: BigNumber(222222),
210
+ },
211
+ {
212
+ ...tokenTransactionFixture,
213
+ recipient: "0x79D5A290D7ba4b99322d91b577589e8d0BF87072",
214
+ amount: BigNumber(22),
215
+ },
216
+ );
217
+ expect(transaction).toMatchObject({
218
+ amount: BigNumber(22),
219
+ recipient: "0x79D5A290D7ba4b99322d91b577589e8d0BF87072",
220
+ useAllAmount: false,
221
+ family: "celo",
222
+ mode: "send",
223
+ index: 0,
224
+ fees: BigNumber(24),
225
+ subAccountId: "subAccountId",
226
+ maxFeePerGas: "1000000010",
227
+ maxPriorityFeePerGas: 1,
228
+ data: Buffer.from("0x73656E645F65726332305F746F6B656E5F64617461"),
229
+ });
230
+ });
231
+
232
+ it("should return the prepared token transaction with useAllAmount", async () => {
233
+ const transaction = await prepareTransaction(
234
+ {
235
+ ...accountWithTokenAccountFixture,
236
+ balance: BigNumber(222222),
237
+ spendableBalance: BigNumber(222222),
238
+ },
239
+ {
240
+ ...tokenTransactionFixture,
241
+ recipient: "0x79D5A290D7ba4b99322d91b577589e8d0BF87072",
242
+ amount: BigNumber(22),
243
+ useAllAmount: true,
244
+ },
245
+ );
246
+
247
+ expect(transaction).toMatchObject({
248
+ amount: BigNumber(212),
249
+ recipient: "0x79D5A290D7ba4b99322d91b577589e8d0BF87072",
250
+ useAllAmount: true,
251
+ family: "celo",
252
+ mode: "send",
253
+ index: 0,
254
+ fees: BigNumber(24),
255
+ subAccountId: "subAccountId",
256
+ maxFeePerGas: "1000000010",
257
+ maxPriorityFeePerGas: 1,
258
+ data: Buffer.from("0x73656E645F65726332305F746F6B656E5F64617461"),
259
+ });
260
+ });
261
+ });
@@ -2,6 +2,7 @@ import BigNumber from "bignumber.js";
2
2
  import { OperationType } from "@ledgerhq/types-live";
3
3
  import { CeloAccount, CeloOperation, CeloOperationMode, Transaction } from "../types";
4
4
  import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
5
+ import { findSubAccountById } from "@ledgerhq/coin-framework/account/index";
5
6
 
6
7
  const MODE_TO_TYPE: { [key in CeloOperationMode | "default"]: OperationType } = {
7
8
  send: "OUT",
@@ -22,9 +23,12 @@ export const buildOptimisticOperation = (
22
23
  ): CeloOperation => {
23
24
  const type = MODE_TO_TYPE[transaction.mode] ?? MODE_TO_TYPE.default;
24
25
 
26
+ const tokenAccount = findSubAccountById(account, transaction.subAccountId || "");
27
+ const isTokenTransaction = tokenAccount?.type === "TokenAccount";
28
+
25
29
  const value =
26
30
  type === "OUT" || type === "LOCK"
27
- ? new BigNumber(transaction.amount).plus(fee)
31
+ ? new BigNumber(transaction.amount).plus(isTokenTransaction ? 0 : fee)
28
32
  : new BigNumber(transaction.amount);
29
33
 
30
34
  const operation: CeloOperation = {
@@ -37,7 +41,7 @@ export const buildOptimisticOperation = (
37
41
  blockHeight: null,
38
42
  senders: [account.freshAddress],
39
43
  recipients: [transaction.recipient].filter(Boolean),
40
- accountId: account.id,
44
+ accountId: isTokenTransaction ? tokenAccount.id : account.id,
41
45
  date: new Date(),
42
46
  extra: {
43
47
  celoOperationValue: new BigNumber(transaction.amount),
@@ -3,12 +3,21 @@ import { CeloTx } from "@celo/connect";
3
3
  import { celoKit } from "../network/sdk";
4
4
  import { BigNumber } from "bignumber.js";
5
5
  import { getPendingStakingOperationAmounts, getVote } from "../logic";
6
+ import { findSubAccountById } from "@ledgerhq/coin-framework/account/index";
7
+ import {
8
+ CELO_STABLE_TOKENS,
9
+ getStableTokenEnum,
10
+ MAX_FEES_THRESHOLD_MULTIPLIER,
11
+ MAX_PRIORITY_FEE_PER_GAS,
12
+ } from "../constants";
6
13
 
7
14
  const buildTransaction = async (account: CeloAccount, transaction: Transaction) => {
8
15
  const kit = celoKit();
9
16
 
10
- const value = transactionValue(account, transaction);
17
+ const tokenAccount = findSubAccountById(account, transaction.subAccountId || "");
18
+ const isTokenTransaction = tokenAccount?.type === "TokenAccount";
11
19
 
20
+ let value = transactionValue(account, transaction);
12
21
  let celoTransaction: CeloTx;
13
22
 
14
23
  if (transaction.mode === "lock") {
@@ -105,23 +114,45 @@ const buildTransaction = async (account: CeloAccount, transaction: Transaction)
105
114
  data: accounts.createAccount().txo.encodeABI(),
106
115
  gas: await accounts.createAccount().txo.estimateGas({ from: account.freshAddress }),
107
116
  };
108
- } else {
109
- // Send
117
+ } else if (isTokenTransaction) {
118
+ value = transaction.useAllAmount ? tokenAccount.balance : transaction.amount;
119
+
120
+ const block = await kit.connection.web3.eth.getBlock("latest");
121
+ const baseFee = BigInt(block.baseFeePerGas || MAX_PRIORITY_FEE_PER_GAS);
122
+ const maxFeePerGas = baseFee + MAX_PRIORITY_FEE_PER_GAS;
123
+
124
+ let token;
125
+ if (CELO_STABLE_TOKENS.includes(tokenAccount.token.id)) {
126
+ token = await kit.contracts.getStableToken(getStableTokenEnum(tokenAccount.token.id));
127
+ } else {
128
+ token = await kit.contracts.getErc20(tokenAccount.token.contractAddress);
129
+ }
110
130
 
111
131
  celoTransaction = {
112
132
  from: account.freshAddress,
113
133
  to: transaction.recipient,
134
+ data: token.transfer(transaction.recipient, value.toFixed()).txo.encodeABI(),
135
+ maxFeePerGas: maxFeePerGas.toString(),
136
+ maxPriorityFeePerGas: await kit.connection.getMaxPriorityFeePerGas(),
114
137
  value: value.toFixed(),
115
138
  };
116
- const gas = await kit.connection.estimateGasWithInflationFactor(celoTransaction);
117
-
139
+ } else {
140
+ // Send
118
141
  celoTransaction = {
119
- ...celoTransaction,
120
- gas,
142
+ from: account.freshAddress,
143
+ to: transaction.recipient,
144
+ value: value.toFixed(),
121
145
  };
122
146
  }
147
+
148
+ const gas = (
149
+ (await kit.connection.estimateGasWithInflationFactor(celoTransaction)) *
150
+ MAX_FEES_THRESHOLD_MULTIPLIER
151
+ ).toFixed();
152
+
123
153
  const tx: CeloTx = {
124
154
  ...celoTransaction,
155
+ gas,
125
156
  chainId: await kit.connection.chainId(),
126
157
  nonce: await kit.connection.nonce(account.freshAddress),
127
158
  };
@@ -3,20 +3,36 @@ import { CeloAccount, Transaction } from "../types";
3
3
  import getTransactionStatus from "./getTransactionStatus";
4
4
  import prepareTransaction from "./prepareTransaction";
5
5
  import createTransaction from "./createTransaction";
6
- import { getMainAccount } from "@ledgerhq/coin-framework/account/helpers";
6
+ import {
7
+ findSubAccountById,
8
+ getMainAccount,
9
+ isTokenAccount,
10
+ } from "@ledgerhq/coin-framework/account/helpers";
11
+ import BigNumber from "bignumber.js";
7
12
 
8
13
  export const estimateMaxSpendable: AccountBridge<
9
14
  Transaction,
10
15
  CeloAccount
11
16
  >["estimateMaxSpendable"] = async ({ account, parentAccount, transaction }) => {
12
17
  const mainAccount = getMainAccount(account, parentAccount);
18
+
19
+ const tokenAccount = findSubAccountById(mainAccount, transaction?.subAccountId ?? "");
20
+ const fromTokenAccount = tokenAccount && isTokenAccount(tokenAccount);
21
+
13
22
  const t = await prepareTransaction(mainAccount, {
14
23
  ...createTransaction(account),
15
24
  ...transaction,
16
25
  useAllAmount: true,
17
26
  });
27
+
18
28
  const { amount } = await getTransactionStatus(mainAccount, t);
19
- return amount;
29
+ const fees = t.fees ?? BigNumber(0);
30
+
31
+ return fromTokenAccount
32
+ ? tokenAccount.spendableBalance
33
+ : account.spendableBalance.gt(fees)
34
+ ? amount.minus(fees)
35
+ : new BigNumber(0);
20
36
  };
21
37
 
22
38
  export default estimateMaxSpendable;
@@ -0,0 +1,103 @@
1
+ import { CeloAccount, Transaction } from "../types";
2
+ import { emptyHistoryCache } from "@ledgerhq/coin-framework/account/index";
3
+ import { getCryptoCurrencyById } from "@ledgerhq/cryptoassets/lib/currencies";
4
+ import BigNumber from "bignumber.js";
5
+ import { faker } from "@faker-js/faker";
6
+ import { TokenCurrency } from "@ledgerhq/types-cryptoassets";
7
+ import { TokenAccount } from "@ledgerhq/types-live";
8
+
9
+ const currency = getCryptoCurrencyById("celo");
10
+
11
+ const accountFixture: CeloAccount = {
12
+ type: "Account",
13
+ id: faker.string.uuid(),
14
+ seedIdentifier: faker.string.uuid(),
15
+ derivationMode: "",
16
+ index: faker.number.int(),
17
+ freshAddress: "address",
18
+ freshAddressPath: "derivationPath",
19
+ used: true,
20
+ balance: new BigNumber(0),
21
+ spendableBalance: new BigNumber(0),
22
+ creationDate: faker.date.past(),
23
+ blockHeight: faker.number.int({ min: 100_000, max: 200_000 }),
24
+ currency,
25
+ operationsCount: 0,
26
+ operations: [],
27
+ pendingOperations: [],
28
+ lastSyncDate: new Date(),
29
+ balanceHistoryCache: emptyHistoryCache,
30
+ swapHistory: [],
31
+ celoResources: {
32
+ registrationStatus: false,
33
+ lockedBalance: BigNumber(0),
34
+ nonvotingLockedBalance: BigNumber(0),
35
+ pendingWithdrawals: null,
36
+ votes: null,
37
+ electionAddress: null,
38
+ lockedGoldAddress: null,
39
+ maxNumGroupsVotedFor: BigNumber(0),
40
+ },
41
+ };
42
+
43
+ const subAccounts = [
44
+ {
45
+ id: "subAccountId",
46
+ type: "TokenAccount",
47
+ parentId: accountFixture.id,
48
+ token: {
49
+ type: "TokenCurrency",
50
+ id: "celoToken",
51
+ contractAddress: "contract_address",
52
+ parentCurrency: currency,
53
+ } as TokenCurrency,
54
+ balance: BigNumber(212),
55
+ spendableBalance: BigNumber(212),
56
+ creationDate: faker.date.past(),
57
+ operationsCount: 0,
58
+ operations: [],
59
+ pendingOperations: [],
60
+ balanceHistoryCache: {
61
+ HOUR: {
62
+ latestDate: null,
63
+ balances: [],
64
+ },
65
+ DAY: {
66
+ latestDate: null,
67
+ balances: [],
68
+ },
69
+ WEEK: {
70
+ latestDate: null,
71
+ balances: [],
72
+ },
73
+ },
74
+ swapHistory: [],
75
+ },
76
+ ] as TokenAccount[];
77
+
78
+ const accountWithTokenAccountFixture = {
79
+ ...accountFixture,
80
+ subAccounts,
81
+ };
82
+
83
+ const transactionFixture: Transaction = {
84
+ amount: new BigNumber(10),
85
+ recipient: "recipient",
86
+ useAllAmount: false,
87
+ family: "celo",
88
+ mode: "send",
89
+ index: 0,
90
+ fees: null,
91
+ };
92
+
93
+ const tokenTransactionFixture: Transaction = {
94
+ ...transactionFixture,
95
+ subAccountId: "subAccountId",
96
+ };
97
+
98
+ export {
99
+ accountFixture,
100
+ accountWithTokenAccountFixture,
101
+ transactionFixture,
102
+ tokenTransactionFixture,
103
+ };
@@ -1,7 +1,15 @@
1
+ import { findSubAccountById } from "@ledgerhq/coin-framework/account/index";
1
2
  import { BigNumber } from "bignumber.js";
2
3
  import type { CeloAccount, Transaction } from "../types";
3
4
  import { celoKit } from "../network/sdk";
4
5
  import { getPendingStakingOperationAmounts, getVote } from "../logic";
6
+ import buildTransaction from "./buildTransaction";
7
+ import {
8
+ CELO_STABLE_TOKENS,
9
+ getStableTokenEnum,
10
+ MAX_FEES_THRESHOLD_MULTIPLIER,
11
+ MAX_PRIORITY_FEE_PER_GAS,
12
+ } from "../constants";
5
13
 
6
14
  const getFeesForTransaction = async ({
7
15
  account,
@@ -28,6 +36,9 @@ const getFeesForTransaction = async ({
28
36
  // Deduct pending lock operations from the spendable balance
29
37
  const totalSpendableBalance = account.spendableBalance.minus(pendingOperationAmounts.lock);
30
38
 
39
+ const tokenAccount = findSubAccountById(account, transaction.subAccountId || "");
40
+ const isTokenTransaction = tokenAccount?.type === "TokenAccount";
41
+
31
42
  if ((transaction.mode === "unlock" || transaction.mode === "vote") && account.celoResources) {
32
43
  value = transaction.useAllAmount
33
44
  ? totalNonVotingLockedBalance
@@ -96,16 +107,39 @@ const getFeesForTransaction = async ({
96
107
  const accounts = await kit.contracts.getAccounts();
97
108
 
98
109
  gas = await accounts.createAccount().txo.estimateGas({ from: account.freshAddress });
99
- } else {
100
- const celoToken = await kit.contracts.getGoldToken();
110
+ } else if (isTokenTransaction) {
111
+ value = transaction.useAllAmount ? tokenAccount.balance : transaction.amount;
112
+
113
+ const block = await kit.connection.web3.eth.getBlock("latest");
114
+ const baseFee = BigInt(block.baseFeePerGas || MAX_PRIORITY_FEE_PER_GAS);
115
+ const maxFeePerGas = baseFee + MAX_PRIORITY_FEE_PER_GAS;
116
+
117
+ let token;
118
+ if (CELO_STABLE_TOKENS.includes(tokenAccount.token.id)) {
119
+ token = await kit.contracts.getStableToken(getStableTokenEnum(tokenAccount.token.id));
120
+ } else {
121
+ token = await kit.contracts.getErc20(tokenAccount.token.contractAddress);
122
+ }
101
123
 
102
124
  const celoTransaction = {
103
125
  from: account.freshAddress,
104
- to: celoToken.address,
105
- data: celoToken.transfer(transaction.recipient, value.toFixed()).txo.encodeABI(),
126
+ to: transaction.recipient,
127
+ data: token.transfer(transaction.recipient, value.toFixed()).txo.encodeABI(),
128
+ maxFeePerGas: maxFeePerGas.toString(),
129
+ maxPriorityFeePerGas: await kit.connection.getMaxPriorityFeePerGas(),
130
+ value: value.toFixed(),
106
131
  };
107
132
 
108
- gas = await kit.connection.estimateGasWithInflationFactor(celoTransaction);
133
+ gas = Number(
134
+ (
135
+ (await kit.connection.estimateGasWithInflationFactor(celoTransaction)) *
136
+ MAX_FEES_THRESHOLD_MULTIPLIER
137
+ ).toFixed(),
138
+ );
139
+ } else {
140
+ // Send
141
+ const tx = await buildTransaction(account, transaction);
142
+ gas = tx.gas ? Number(tx.gas) : 0;
109
143
  }
110
144
 
111
145
  const gasPrice = new BigNumber(await kit.connection.gasPrice());
@@ -13,6 +13,7 @@ import { getPendingStakingOperationAmounts, getVote } from "../logic";
13
13
  import { CeloAccount, Transaction, TransactionStatus } from "../types";
14
14
  import { CeloAllFundsWarning } from "../errors";
15
15
  import { celoKit } from "../network/sdk";
16
+ import { findSubAccountById } from "@ledgerhq/coin-framework/account/index";
16
17
 
17
18
  const kit = celoKit();
18
19
 
@@ -45,10 +46,14 @@ export const getTransactionStatus: AccountBridge<
45
46
  const totalNonVotingLockedBalance = nonvotingLockedGoldBalance.minus(
46
47
  pendingOperationAmounts.vote,
47
48
  );
49
+
48
50
  // Deduct pending lock operations from the spendable balance
49
51
  const totalSpendableBalance = account.spendableBalance.minus(pendingOperationAmounts.lock);
50
52
  const estimatedFees = transaction.fees || new BigNumber(0);
51
53
 
54
+ const tokenAccount = findSubAccountById(account, transaction.subAccountId || "");
55
+ const isTokenTransaction = tokenAccount?.type === "TokenAccount";
56
+
52
57
  let amount: BigNumber = new BigNumber(0);
53
58
  if (useAllAmount && (transaction.mode === "unlock" || transaction.mode === "vote")) {
54
59
  amount = totalNonVotingLockedBalance ?? new BigNumber(0);
@@ -56,7 +61,9 @@ export const getTransactionStatus: AccountBridge<
56
61
  const revoke = getVote(account, transaction.recipient, transaction.index);
57
62
  if (revoke?.amount) amount = revoke.amount;
58
63
  } else if (useAllAmount) {
59
- amount = totalSpendableBalance.minus(estimatedFees);
64
+ amount = isTokenTransaction
65
+ ? tokenAccount.spendableBalance
66
+ : totalSpendableBalance.minus(estimatedFees);
60
67
  } else {
61
68
  amount = new BigNumber(transaction.amount);
62
69
  }
@@ -73,7 +80,7 @@ export const getTransactionStatus: AccountBridge<
73
80
  }
74
81
 
75
82
  if (!["register", "withdraw", "activate"].includes(transaction.mode)) {
76
- if (!errors.amount && amount.lte(0) && !useAllAmount) {
83
+ if (amount.lte(0) && !useAllAmount) {
77
84
  errors.amount = new AmountRequired();
78
85
  }
79
86
  }
@@ -99,13 +106,23 @@ export const getTransactionStatus: AccountBridge<
99
106
  }
100
107
 
101
108
  if (transaction.mode === "send") {
102
- if (!transaction.recipient) {
109
+ if (!transaction.recipient && !errors.recipient) {
103
110
  errors.recipient = new RecipientRequired();
104
- } else if (!isValidAddress(transaction.recipient)) {
111
+ } else if (!isValidAddress(transaction.recipient) && !errors.recipient) {
105
112
  errors.recipient = new InvalidAddress("", {
106
113
  currencyName: account.currency.name,
107
114
  });
108
115
  }
116
+
117
+ if (isTokenTransaction) {
118
+ return {
119
+ errors,
120
+ warnings,
121
+ estimatedFees,
122
+ amount,
123
+ totalSpent: amount,
124
+ };
125
+ }
109
126
  }
110
127
 
111
128
  return {
@@ -21,7 +21,6 @@ import resolver from "../signer/hw-getAddress";
21
21
  import type { CeloAccount, Transaction, TransactionStatus } from "../types";
22
22
  import { broadcast } from "./broadcast";
23
23
 
24
- import { EvmSigner } from "@ledgerhq/coin-evm/types/signer";
25
24
  import getAddressWrapper from "@ledgerhq/coin-framework/bridge/getAddressWrapper";
26
25
  import { getAccountShape } from "./synchronisation";
27
26
  import { buildSignOperation } from "./signOperation";