@ledgerhq/coin-sui 0.9.0 → 0.10.0-nightly.1

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 (159) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.unimportedrc.json +1 -1
  3. package/CHANGELOG.md +26 -0
  4. package/lib/api/index.d.ts.map +1 -1
  5. package/lib/api/index.integration.test.js +21 -2
  6. package/lib/api/index.integration.test.js.map +1 -1
  7. package/lib/api/index.js +2 -0
  8. package/lib/api/index.js.map +1 -1
  9. package/lib/api/index.test.js +37 -0
  10. package/lib/api/index.test.js.map +1 -1
  11. package/lib/bridge/buildTransaction.d.ts +1 -3
  12. package/lib/bridge/buildTransaction.d.ts.map +1 -1
  13. package/lib/bridge/buildTransaction.integration.test.js +17 -35
  14. package/lib/bridge/buildTransaction.integration.test.js.map +1 -1
  15. package/lib/bridge/buildTransaction.js +10 -21
  16. package/lib/bridge/buildTransaction.js.map +1 -1
  17. package/lib/bridge/buildTransaction.test.js +33 -130
  18. package/lib/bridge/buildTransaction.test.js.map +1 -1
  19. package/lib/bridge/getFeesForTransaction.d.ts.map +1 -1
  20. package/lib/bridge/getFeesForTransaction.js +5 -1
  21. package/lib/bridge/getFeesForTransaction.js.map +1 -1
  22. package/lib/logic/craftTransaction.d.ts +2 -2
  23. package/lib/logic/craftTransaction.d.ts.map +1 -1
  24. package/lib/logic/craftTransaction.integration.test.d.ts +2 -0
  25. package/lib/logic/craftTransaction.integration.test.d.ts.map +1 -0
  26. package/lib/logic/craftTransaction.integration.test.js +56 -0
  27. package/lib/logic/craftTransaction.integration.test.js.map +1 -0
  28. package/lib/logic/craftTransaction.js +2 -2
  29. package/lib/logic/craftTransaction.js.map +1 -1
  30. package/lib/logic/estimateFees.d.ts +1 -1
  31. package/lib/logic/estimateFees.d.ts.map +1 -1
  32. package/lib/logic/estimateFees.integration.test.d.ts +2 -0
  33. package/lib/logic/estimateFees.integration.test.d.ts.map +1 -0
  34. package/lib/logic/estimateFees.integration.test.js +79 -0
  35. package/lib/logic/estimateFees.integration.test.js.map +1 -0
  36. package/lib/logic/estimateFees.js +7 -2
  37. package/lib/logic/estimateFees.js.map +1 -1
  38. package/lib/logic/getBalance.d.ts.map +1 -1
  39. package/lib/logic/getBalance.integration.test.d.ts +2 -0
  40. package/lib/logic/getBalance.integration.test.d.ts.map +1 -0
  41. package/lib/logic/getBalance.integration.test.js +56 -0
  42. package/lib/logic/getBalance.integration.test.js.map +1 -0
  43. package/lib/logic/getBalance.js +18 -7
  44. package/lib/logic/getBalance.js.map +1 -1
  45. package/lib/logic/getBalance.test.js +49 -7
  46. package/lib/logic/getBalance.test.js.map +1 -1
  47. package/lib/logic/index.d.ts +1 -0
  48. package/lib/logic/index.d.ts.map +1 -1
  49. package/lib/logic/index.js +4 -1
  50. package/lib/logic/index.js.map +1 -1
  51. package/lib/logic/staking.d.ts +4 -0
  52. package/lib/logic/staking.d.ts.map +1 -0
  53. package/lib/logic/staking.js +36 -0
  54. package/lib/logic/staking.js.map +1 -0
  55. package/lib/network/index.d.ts +6 -6
  56. package/lib/network/index.d.ts.map +1 -1
  57. package/lib/network/index.js +9 -3
  58. package/lib/network/index.js.map +1 -1
  59. package/lib/network/sdk.d.ts +27 -19
  60. package/lib/network/sdk.d.ts.map +1 -1
  61. package/lib/network/sdk.integration.test.js +21 -22
  62. package/lib/network/sdk.integration.test.js.map +1 -1
  63. package/lib/network/sdk.js +113 -68
  64. package/lib/network/sdk.js.map +1 -1
  65. package/lib/network/sdk.test.js +148 -65
  66. package/lib/network/sdk.test.js.map +1 -1
  67. package/lib/test/testUtils.d.ts +2 -0
  68. package/lib/test/testUtils.d.ts.map +1 -0
  69. package/lib/test/testUtils.js +35 -0
  70. package/lib/test/testUtils.js.map +1 -0
  71. package/lib-es/api/index.d.ts.map +1 -1
  72. package/lib-es/api/index.integration.test.js +21 -2
  73. package/lib-es/api/index.integration.test.js.map +1 -1
  74. package/lib-es/api/index.js +3 -1
  75. package/lib-es/api/index.js.map +1 -1
  76. package/lib-es/api/index.test.js +37 -0
  77. package/lib-es/api/index.test.js.map +1 -1
  78. package/lib-es/bridge/buildTransaction.d.ts +1 -3
  79. package/lib-es/bridge/buildTransaction.d.ts.map +1 -1
  80. package/lib-es/bridge/buildTransaction.integration.test.js +13 -31
  81. package/lib-es/bridge/buildTransaction.integration.test.js.map +1 -1
  82. package/lib-es/bridge/buildTransaction.js +9 -16
  83. package/lib-es/bridge/buildTransaction.js.map +1 -1
  84. package/lib-es/bridge/buildTransaction.test.js +34 -131
  85. package/lib-es/bridge/buildTransaction.test.js.map +1 -1
  86. package/lib-es/bridge/getFeesForTransaction.d.ts.map +1 -1
  87. package/lib-es/bridge/getFeesForTransaction.js +5 -1
  88. package/lib-es/bridge/getFeesForTransaction.js.map +1 -1
  89. package/lib-es/logic/craftTransaction.d.ts +2 -2
  90. package/lib-es/logic/craftTransaction.d.ts.map +1 -1
  91. package/lib-es/logic/craftTransaction.integration.test.d.ts +2 -0
  92. package/lib-es/logic/craftTransaction.integration.test.d.ts.map +1 -0
  93. package/lib-es/logic/craftTransaction.integration.test.js +51 -0
  94. package/lib-es/logic/craftTransaction.integration.test.js.map +1 -0
  95. package/lib-es/logic/craftTransaction.js +2 -2
  96. package/lib-es/logic/craftTransaction.js.map +1 -1
  97. package/lib-es/logic/estimateFees.d.ts +1 -1
  98. package/lib-es/logic/estimateFees.d.ts.map +1 -1
  99. package/lib-es/logic/estimateFees.integration.test.d.ts +2 -0
  100. package/lib-es/logic/estimateFees.integration.test.d.ts.map +1 -0
  101. package/lib-es/logic/estimateFees.integration.test.js +74 -0
  102. package/lib-es/logic/estimateFees.integration.test.js.map +1 -0
  103. package/lib-es/logic/estimateFees.js +7 -2
  104. package/lib-es/logic/estimateFees.js.map +1 -1
  105. package/lib-es/logic/getBalance.d.ts.map +1 -1
  106. package/lib-es/logic/getBalance.integration.test.d.ts +2 -0
  107. package/lib-es/logic/getBalance.integration.test.d.ts.map +1 -0
  108. package/lib-es/logic/getBalance.integration.test.js +51 -0
  109. package/lib-es/logic/getBalance.integration.test.js.map +1 -0
  110. package/lib-es/logic/getBalance.js +19 -8
  111. package/lib-es/logic/getBalance.js.map +1 -1
  112. package/lib-es/logic/getBalance.test.js +50 -8
  113. package/lib-es/logic/getBalance.test.js.map +1 -1
  114. package/lib-es/logic/index.d.ts +1 -0
  115. package/lib-es/logic/index.d.ts.map +1 -1
  116. package/lib-es/logic/index.js +1 -0
  117. package/lib-es/logic/index.js.map +1 -1
  118. package/lib-es/logic/staking.d.ts +4 -0
  119. package/lib-es/logic/staking.d.ts.map +1 -0
  120. package/lib-es/logic/staking.js +8 -0
  121. package/lib-es/logic/staking.js.map +1 -0
  122. package/lib-es/network/index.d.ts +6 -6
  123. package/lib-es/network/index.d.ts.map +1 -1
  124. package/lib-es/network/index.js +6 -3
  125. package/lib-es/network/index.js.map +1 -1
  126. package/lib-es/network/sdk.d.ts +27 -19
  127. package/lib-es/network/sdk.d.ts.map +1 -1
  128. package/lib-es/network/sdk.integration.test.js +22 -23
  129. package/lib-es/network/sdk.integration.test.js.map +1 -1
  130. package/lib-es/network/sdk.js +106 -64
  131. package/lib-es/network/sdk.js.map +1 -1
  132. package/lib-es/network/sdk.test.js +148 -65
  133. package/lib-es/network/sdk.test.js.map +1 -1
  134. package/lib-es/test/testUtils.d.ts +2 -0
  135. package/lib-es/test/testUtils.d.ts.map +1 -0
  136. package/lib-es/test/testUtils.js +31 -0
  137. package/lib-es/test/testUtils.js.map +1 -0
  138. package/package.json +7 -7
  139. package/src/api/index.integration.test.ts +24 -2
  140. package/src/api/index.test.ts +40 -0
  141. package/src/api/index.ts +4 -0
  142. package/src/bridge/buildTransaction.integration.test.ts +14 -39
  143. package/src/bridge/buildTransaction.test.ts +37 -160
  144. package/src/bridge/buildTransaction.ts +12 -21
  145. package/src/bridge/getFeesForTransaction.ts +6 -1
  146. package/src/logic/craftTransaction.integration.test.ts +63 -0
  147. package/src/logic/craftTransaction.ts +4 -4
  148. package/src/logic/estimateFees.integration.test.ts +89 -0
  149. package/src/logic/estimateFees.ts +7 -1
  150. package/src/logic/getBalance.integration.test.ts +66 -0
  151. package/src/logic/getBalance.test.ts +58 -8
  152. package/src/logic/getBalance.ts +24 -8
  153. package/src/logic/index.ts +1 -0
  154. package/src/logic/staking.ts +10 -0
  155. package/src/network/index.ts +12 -3
  156. package/src/network/sdk.integration.test.ts +25 -22
  157. package/src/network/sdk.test.ts +186 -77
  158. package/src/network/sdk.ts +149 -93
  159. package/src/test/testUtils.ts +38 -0
@@ -1,6 +1,7 @@
1
1
  import { BigNumber } from "bignumber.js";
2
- import { buildTransaction, extractExtrinsicArg } from "./buildTransaction";
2
+ import { buildTransaction } from "./buildTransaction";
3
3
  import type { SuiAccount, Transaction } from "../types";
4
+ import { createFixtureAccount } from "../types/bridge.fixture";
4
5
 
5
6
  // Mock the craftTransaction function
6
7
  jest.mock("../logic", () => ({
@@ -64,7 +65,7 @@ describe("buildTransaction", () => {
64
65
  });
65
66
 
66
67
  describe("buildTransaction", () => {
67
- it("should call craftTransaction with correct parameters", async () => {
68
+ it("should call craftTransaction with correct parameters for native asset", async () => {
68
69
  // WHEN
69
70
  await buildTransaction(mockAccount, mockTransaction);
70
71
 
@@ -78,6 +79,40 @@ describe("buildTransaction", () => {
78
79
  });
79
80
  });
80
81
 
82
+ it("should call craftTransaction with correct parameters for token asset", async () => {
83
+ const account = createFixtureAccount({
84
+ id: "parentAccountId",
85
+ balance: BigNumber(0),
86
+ spendableBalance: BigNumber(0),
87
+ subAccounts: [
88
+ createFixtureAccount({
89
+ id: "subAccountId",
90
+ parentId: "parentAccountId",
91
+ type: "TokenAccount",
92
+ token: {
93
+ contractAddress: "0x3::usdt::USDT",
94
+ },
95
+ }),
96
+ ],
97
+ });
98
+
99
+ // WHEN
100
+ await buildTransaction(account, {
101
+ ...mockTransaction,
102
+ subAccountId: "subAccountId",
103
+ coinType: "0x3::usdt::USDT",
104
+ });
105
+
106
+ // THEN
107
+ expect(craftTransaction).toHaveBeenCalledWith({
108
+ sender: account.freshAddress,
109
+ recipient: mockTransaction.recipient,
110
+ type: mockTransaction.mode,
111
+ amount: BigInt(mockTransaction.amount!.toString()),
112
+ asset: { type: "token", assetReference: "0x3::usdt::USDT" },
113
+ });
114
+ });
115
+
81
116
  it("should return the result from craftTransaction", async () => {
82
117
  const expectedResult = {
83
118
  unsigned: new Uint8Array([1, 2, 3, 4, 5]),
@@ -207,164 +242,6 @@ describe("buildTransaction", () => {
207
242
  });
208
243
  });
209
244
 
210
- describe("extractExtrinsicArg", () => {
211
- it("should extract correct fields from transaction", () => {
212
- const transaction: Transaction = {
213
- ...mockTransaction,
214
- useAllAmount: true,
215
- };
216
-
217
- // WHEN
218
- const result = extractExtrinsicArg(transaction);
219
-
220
- // THEN
221
- expect(result).toEqual({
222
- mode: "send",
223
- coinType: "0x2::sui::SUI",
224
- amount: new BigNumber("100000000"),
225
- recipient: "0xabcdef1234567890",
226
- useAllAmount: true,
227
- });
228
- });
229
-
230
- it("should handle transaction without useAllAmount", () => {
231
- const transaction: Transaction = {
232
- ...mockTransaction,
233
- useAllAmount: false,
234
- };
235
-
236
- // WHEN
237
- const result = extractExtrinsicArg(transaction);
238
-
239
- // THEN
240
- expect(result).toEqual({
241
- mode: "send",
242
- coinType: "0x2::sui::SUI",
243
- amount: new BigNumber("100000000"),
244
- recipient: "0xabcdef1234567890",
245
- useAllAmount: false,
246
- });
247
- });
248
-
249
- it("should handle transaction with undefined useAllAmount", () => {
250
- const transaction = {
251
- ...mockTransaction,
252
- useAllAmount: undefined,
253
- } as any;
254
-
255
- // WHEN
256
- const result = extractExtrinsicArg(transaction);
257
-
258
- // THEN
259
- expect(result).toEqual({
260
- mode: "send",
261
- coinType: "0x2::sui::SUI",
262
- amount: new BigNumber("100000000"),
263
- recipient: "0xabcdef1234567890",
264
- useAllAmount: undefined,
265
- });
266
- });
267
-
268
- it("should handle different transaction modes", () => {
269
- const transaction: Transaction = {
270
- ...mockTransaction,
271
- mode: "send",
272
- };
273
-
274
- // WHEN
275
- const result = extractExtrinsicArg(transaction);
276
-
277
- // THEN
278
- expect(result.mode).toBe("send");
279
- });
280
-
281
- it("should handle different amounts", () => {
282
- const transaction: Transaction = {
283
- ...mockTransaction,
284
- amount: new BigNumber("500000000"),
285
- };
286
-
287
- // WHEN
288
- const result = extractExtrinsicArg(transaction);
289
-
290
- // THEN
291
- expect(result.amount).toEqual(new BigNumber("500000000"));
292
- });
293
-
294
- it("should handle different recipients", () => {
295
- const transaction: Transaction = {
296
- ...mockTransaction,
297
- recipient: "0x9876543210fedcba",
298
- };
299
-
300
- // WHEN
301
- const result = extractExtrinsicArg(transaction);
302
-
303
- // THEN
304
- expect(result.recipient).toBe("0x9876543210fedcba");
305
- });
306
-
307
- it("should not include other transaction fields", () => {
308
- const transaction: Transaction = {
309
- ...mockTransaction,
310
- fees: new BigNumber("1000000"),
311
- errors: { someError: new Error("test") },
312
- };
313
-
314
- // WHEN
315
- const result = extractExtrinsicArg(transaction);
316
-
317
- // THEN
318
- expect(result).not.toHaveProperty("fees");
319
- expect(result).not.toHaveProperty("errors");
320
- expect(result).not.toHaveProperty("family");
321
- expect(result).not.toHaveProperty("id");
322
- });
323
-
324
- it("should handle zero amount", () => {
325
- const transaction: Transaction = {
326
- ...mockTransaction,
327
- amount: new BigNumber("0"),
328
- };
329
-
330
- // WHEN
331
- const result = extractExtrinsicArg(transaction);
332
-
333
- // THEN
334
- expect(result.amount).toEqual(new BigNumber("0"));
335
- });
336
- });
337
-
338
- describe("Integration between extractExtrinsicArg and buildTransaction", () => {
339
- it("should work together correctly", async () => {
340
- const transaction: Transaction = {
341
- ...mockTransaction,
342
- useAllAmount: true,
343
- };
344
-
345
- // Extract extrinsic arg
346
- const extrinsicArg = extractExtrinsicArg(transaction);
347
-
348
- // Build transaction
349
- await buildTransaction(mockAccount, transaction);
350
-
351
- // Verify that the extracted arg contains the expected fields
352
- expect(extrinsicArg).toHaveProperty("mode", "send");
353
- expect(extrinsicArg).toHaveProperty("amount");
354
- expect(extrinsicArg).toHaveProperty("recipient");
355
- expect(extrinsicArg).toHaveProperty("useAllAmount", true);
356
-
357
- // Verify that buildTransaction was called with correct parameters
358
- expect(craftTransaction).toHaveBeenCalledWith({
359
- sender: mockAccount.freshAddress,
360
- recipient: transaction.recipient,
361
- type: transaction.mode,
362
- amount: BigInt(transaction.amount!.toString()),
363
- asset: { type: "native" },
364
- });
365
- });
366
- });
367
-
368
245
  describe("Edge Cases", () => {
369
246
  it("should handle empty recipient string", async () => {
370
247
  const transactionWithEmptyRecipient: Transaction = {
@@ -1,34 +1,25 @@
1
- import pick from "lodash/pick";
1
+ import { findSubAccountById } from "@ledgerhq/coin-framework/account/helpers";
2
2
  import type { SuiAccount, Transaction } from "../types";
3
- import { craftTransaction, type CreateExtrinsicArg } from "../logic";
4
- import { AssetInfo } from "@ledgerhq/coin-framework/api/types";
5
-
6
- export const extractExtrinsicArg = (transaction: Transaction): CreateExtrinsicArg =>
7
- pick(transaction, ["mode", "amount", "recipient", "useAllAmount", "coinType"]);
3
+ import { craftTransaction } from "../logic";
4
+ import { DEFAULT_COIN_TYPE, toSuiAsset } from "../network/sdk";
8
5
 
9
6
  /**
10
7
  * @param {Account} account
11
8
  * @param {Transaction} transaction
12
9
  */
13
10
  export const buildTransaction = async (
14
- { freshAddress }: SuiAccount,
15
- { recipient, mode, amount, coinType }: Transaction,
11
+ account: SuiAccount,
12
+ { amount, mode, recipient, subAccountId }: Transaction,
16
13
  ) => {
17
- const NATIVE_COIN_TYPE = "0x2::sui::SUI"; // <-- NOTE: unusure, double check
18
- let asset: AssetInfo = {
19
- type: "native",
20
- };
21
- if (coinType !== NATIVE_COIN_TYPE) {
22
- asset = {
23
- type: "token",
24
- assetReference: coinType,
25
- };
26
- }
14
+ const { freshAddress } = account;
15
+ const subAccount = findSubAccountById(account, subAccountId ?? "");
16
+ const asset = toSuiAsset(subAccount?.token.contractAddress ?? DEFAULT_COIN_TYPE);
17
+
27
18
  return craftTransaction({
28
- sender: freshAddress,
29
- recipient,
30
- type: mode,
31
19
  amount: BigInt(amount.toString()),
32
20
  asset,
21
+ recipient,
22
+ sender: freshAddress,
23
+ type: mode,
33
24
  });
34
25
  };
@@ -1,8 +1,10 @@
1
+ import { findSubAccountById } from "@ledgerhq/coin-framework/account/helpers";
1
2
  import { BigNumber } from "bignumber.js";
2
3
  import { getAbandonSeedAddress } from "@ledgerhq/cryptoassets";
3
4
  import type { SuiAccount, Transaction } from "../types";
4
5
  import { calculateAmount } from "./utils";
5
6
  import { estimateFees } from "../logic";
7
+ import { DEFAULT_COIN_TYPE, toSuiAsset } from "../network/sdk";
6
8
 
7
9
  /**
8
10
  * Fetch the transaction fees for a transaction
@@ -32,12 +34,15 @@ export default async function getEstimatedFees({
32
34
  }), // Remove fees if present since we are fetching fees
33
35
  };
34
36
 
37
+ const subAccount = findSubAccountById(account, transaction.subAccountId ?? "");
38
+ const asset = toSuiAsset(subAccount?.token.contractAddress ?? DEFAULT_COIN_TYPE);
39
+
35
40
  const fees = await estimateFees({
36
41
  recipient: getAbandonSeedAddress(account.currency.id),
37
42
  sender: account.freshAddress,
38
43
  amount: BigInt(t.amount.toString()),
39
44
  type: "send",
40
- asset: { type: "native" },
45
+ asset,
41
46
  });
42
47
  return new BigNumber(fees.toString());
43
48
  }
@@ -0,0 +1,63 @@
1
+ import type { TransactionIntent } from "@ledgerhq/coin-framework/api/index";
2
+ import { getFullnodeUrl } from "@mysten/sui/client";
3
+ import coinConfig from "../config";
4
+ import { extractCoinTypeFromUnsignedTx } from "../test/testUtils";
5
+ import { craftTransaction } from "./craftTransaction";
6
+
7
+ const SENDER = "0x33444cf803c690db96527cec67e3c9ab512596f4ba2d4eace43f0b4f716e0164";
8
+ const RECIPIENT = "0x33444cf803c690db96527cec67e3c9ab512596f4ba2d4eace43f0b4f716e0164";
9
+
10
+ describe("craftTransaction", () => {
11
+ beforeAll(() => {
12
+ coinConfig.setCoinConfig(() => ({
13
+ status: {
14
+ type: "active",
15
+ },
16
+ node: {
17
+ url: getFullnodeUrl("mainnet"),
18
+ },
19
+ }));
20
+ });
21
+
22
+ it("should craft a native SUI send transaction", async () => {
23
+ const transactionIntent: TransactionIntent = {
24
+ sender: SENDER,
25
+ recipient: RECIPIENT,
26
+ amount: BigInt(1000),
27
+ type: "send",
28
+ asset: { type: "native" },
29
+ };
30
+
31
+ const result = await craftTransaction(transactionIntent);
32
+
33
+ expect(result).toBeDefined();
34
+ expect(result.unsigned).toBeInstanceOf(Uint8Array);
35
+
36
+ const resultCoinTypes = await extractCoinTypeFromUnsignedTx(result.unsigned);
37
+ expect(resultCoinTypes).toEqual(expect.arrayContaining([expect.stringContaining("sui")]));
38
+ }, 15000);
39
+
40
+ it("should craft a token send transaction", async () => {
41
+ const coinType =
42
+ "0x375f70cf2ae4c00bf37117d0c85a2c71545e6ee05c4a5c7d282cd66a4504b068::usdt::USDT";
43
+
44
+ const transactionIntent: TransactionIntent = {
45
+ sender: SENDER,
46
+ recipient: RECIPIENT,
47
+ amount: BigInt(1000),
48
+ type: "send",
49
+ asset: {
50
+ type: "token",
51
+ assetReference: coinType,
52
+ },
53
+ };
54
+
55
+ const result = await craftTransaction(transactionIntent);
56
+
57
+ expect(result).toBeDefined();
58
+ expect(result.unsigned).toBeInstanceOf(Uint8Array);
59
+
60
+ const resultCoinTypes = await extractCoinTypeFromUnsignedTx(result.unsigned);
61
+ expect(resultCoinTypes).toEqual(expect.arrayContaining([expect.stringContaining("usdt")]));
62
+ }, 15000);
63
+ });
@@ -5,18 +5,18 @@ import suiAPI from "../network";
5
5
  import { DEFAULT_COIN_TYPE } from "../network/sdk";
6
6
 
7
7
  export type CreateExtrinsicArg = {
8
- mode: SuiTransactionMode;
9
8
  amount: BigNumber;
10
9
  coinType: string;
10
+ mode: SuiTransactionMode;
11
11
  recipient: string;
12
12
  useAllAmount?: boolean | undefined;
13
13
  };
14
14
 
15
15
  export async function craftTransaction({
16
- sender,
17
16
  amount,
18
- recipient,
19
17
  asset,
18
+ recipient,
19
+ sender,
20
20
  type,
21
21
  }: TransactionIntent): Promise<CoreTransaction> {
22
22
  let coinType = DEFAULT_COIN_TYPE;
@@ -25,9 +25,9 @@ export async function craftTransaction({
25
25
  }
26
26
  const unsigned = await suiAPI.createTransaction(sender, {
27
27
  amount: BigNumber(amount.toString()),
28
- recipient,
29
28
  coinType,
30
29
  mode: type as SuiTransactionMode,
30
+ recipient,
31
31
  });
32
32
 
33
33
  return { unsigned };
@@ -0,0 +1,89 @@
1
+ import type { TransactionIntent } from "@ledgerhq/coin-framework/api/index";
2
+ import { getFullnodeUrl } from "@mysten/sui/client";
3
+ import coinConfig from "../config";
4
+ import { estimateFees } from "./estimateFees";
5
+
6
+ const SENDER = "0x6e143fe0a8ca010a86580dafac44298e5b1b7d73efc345356a59a15f0d7824f0";
7
+ const RECIPIENT = "0x33444cf803c690db96527cec67e3c9ab512596f4ba2d4eace43f0b4f716e0164";
8
+
9
+ describe("estimateFees", () => {
10
+ beforeAll(() => {
11
+ coinConfig.setCoinConfig(() => ({
12
+ status: {
13
+ type: "active",
14
+ },
15
+ node: {
16
+ url: getFullnodeUrl("mainnet"),
17
+ },
18
+ }));
19
+ });
20
+
21
+ it("should estimate fees for native SUI transaction", async () => {
22
+ const transactionIntent: TransactionIntent = {
23
+ sender: SENDER,
24
+ recipient: RECIPIENT,
25
+ amount: BigInt(1000),
26
+ type: "send",
27
+ asset: { type: "native" },
28
+ };
29
+
30
+ const estimatedFees = await estimateFees(transactionIntent);
31
+
32
+ expect(typeof estimatedFees).toBe("bigint");
33
+ expect(estimatedFees).toBeGreaterThan(1000n);
34
+ expect(estimatedFees).toBeLessThan(10000000n);
35
+ }, 25000);
36
+
37
+ it("should estimate fees for token transaction", async () => {
38
+ const coinType =
39
+ "0x375f70cf2ae4c00bf37117d0c85a2c71545e6ee05c4a5c7d282cd66a4504b068::usdt::USDT";
40
+
41
+ const transactionIntent: TransactionIntent = {
42
+ sender: SENDER,
43
+ recipient: RECIPIENT,
44
+ amount: BigInt(1000),
45
+ type: "send",
46
+ asset: {
47
+ type: "token",
48
+ assetReference: coinType,
49
+ },
50
+ };
51
+
52
+ const estimatedFees = await estimateFees(transactionIntent);
53
+
54
+ expect(typeof estimatedFees).toBe("bigint");
55
+ expect(estimatedFees).toBeGreaterThan(1000n);
56
+ expect(estimatedFees).toBeLessThan(10000000n);
57
+ }, 25000);
58
+
59
+ it("should handle concurrent fee estimations", async () => {
60
+ const transactionIntent: TransactionIntent = {
61
+ sender: SENDER,
62
+ recipient: RECIPIENT,
63
+ amount: BigInt(1000),
64
+ type: "send",
65
+ asset: { type: "native" },
66
+ };
67
+
68
+ // Run multiple concurrent estimations
69
+ const promises = Array(5)
70
+ .fill(0)
71
+ .map(() => estimateFees(transactionIntent));
72
+ const results = await Promise.all(promises);
73
+
74
+ // All results should be valid
75
+ results.forEach(fees => {
76
+ expect(typeof fees).toBe("bigint");
77
+ expect(fees).toBeGreaterThan(1000n);
78
+ expect(fees).toBeLessThan(10000000n);
79
+ });
80
+
81
+ // Results should be similar (may have slight variations)
82
+ const values = results.map(r => Number(r));
83
+ const minValue = Math.min(...values);
84
+ const maxValue = Math.max(...values);
85
+
86
+ // Should not vary by more than 50% under concurrent load
87
+ expect((maxValue - minValue) / minValue).toBeLessThan(0.5);
88
+ }, 25000);
89
+ });
@@ -1,19 +1,25 @@
1
1
  import suiAPI from "../network";
2
2
  import { BigNumber } from "bignumber.js";
3
3
  import type { TransactionIntent } from "@ledgerhq/coin-framework/api/index";
4
+ import { DEFAULT_COIN_TYPE } from "../network/sdk";
4
5
 
5
6
  export async function estimateFees({
6
7
  recipient,
7
8
  amount,
8
9
  sender,
10
+ asset,
9
11
  }: TransactionIntent): Promise<bigint> {
12
+ let coinType = DEFAULT_COIN_TYPE;
13
+ if (asset.type === "token" && asset.assetReference) {
14
+ coinType = asset.assetReference;
15
+ }
10
16
  const { gasBudget } = await suiAPI.paymentInfo(sender, {
11
17
  mode: "send",
12
18
  family: "sui",
13
19
  recipient,
14
20
  amount: BigNumber(amount.toString()),
15
21
  errors: {},
16
- coinType: "0x2::sui::SUI",
22
+ coinType,
17
23
  });
18
24
  return BigInt(gasBudget);
19
25
  }
@@ -0,0 +1,66 @@
1
+ import { getFullnodeUrl } from "@mysten/sui/client";
2
+ import coinConfig from "../config";
3
+ import { getBalance } from "./getBalance";
4
+
5
+ const SENDER = "0x33444cf803c690db96527cec67e3c9ab512596f4ba2d4eace43f0b4f716e0164";
6
+
7
+ describe("getBalance", () => {
8
+ beforeAll(() => {
9
+ coinConfig.setCoinConfig(() => ({
10
+ status: {
11
+ type: "active",
12
+ },
13
+ node: {
14
+ url: getFullnodeUrl("testnet"),
15
+ },
16
+ }));
17
+ });
18
+
19
+ it("should fetch native SUI balance", async () => {
20
+ const balances = await getBalance(SENDER);
21
+
22
+ expect(balances.length).toBeGreaterThanOrEqual(1);
23
+ expect(balances[0]).toMatchObject({
24
+ asset: { type: "native" },
25
+ });
26
+
27
+ expect(typeof balances[0].value).toBe("bigint");
28
+ expect(balances[0].value).toBeGreaterThanOrEqual(0n);
29
+ }, 10000);
30
+
31
+ it("should fetch token balances", async () => {
32
+ const balances = await getBalance(SENDER);
33
+
34
+ expect(balances.length).toBeGreaterThanOrEqual(1);
35
+
36
+ const tokenBalances = balances.filter(balance => balance.asset.type === "token");
37
+ tokenBalances.forEach(balance => {
38
+ expect(balance.asset.type).toBe("token");
39
+ if (balance.asset.type === "token") {
40
+ expect(balance.asset.assetReference).toMatch(
41
+ /^0x[a-fA-F0-9]+::[a-zA-Z0-9_]+::[a-zA-Z0-9_]+$/,
42
+ );
43
+ }
44
+ expect(typeof balance.value).toBe("bigint");
45
+ expect(balance.value).toBeGreaterThanOrEqual(0n);
46
+ });
47
+ }, 15000);
48
+
49
+ it("should properly parse token asset reference", async () => {
50
+ const balances = await getBalance(SENDER);
51
+
52
+ const usdTokens = balances.filter(
53
+ balance =>
54
+ balance.asset.type === "token" &&
55
+ balance.asset.assetReference?.toLowerCase().includes("usd"),
56
+ );
57
+
58
+ usdTokens.forEach(balance => {
59
+ expect(balance.asset.type).toBe("token");
60
+ if (balance.asset.type === "token") {
61
+ expect(balance.asset.assetReference).toBeTruthy();
62
+ }
63
+ expect(typeof balance.value).toBe("bigint");
64
+ });
65
+ }, 15000);
66
+ });
@@ -1,24 +1,74 @@
1
1
  import { getBalance } from "./getBalance";
2
- import { getAccount } from "../network";
2
+ import { getAllBalancesCached, getStakes } from "../network";
3
+ import type { Stake } from "@ledgerhq/coin-framework/api/types";
3
4
 
4
- // Mock the getAccount function
5
5
  jest.mock("../network", () => ({
6
- getAccount: jest.fn().mockResolvedValue({ balance: 1000000000 }),
6
+ getAllBalancesCached: jest.fn().mockResolvedValue([
7
+ { coinType: "0x2::sui::SUI", totalBalance: 1000000000 },
8
+ { coinType: "0x3::usdt::USDT", totalBalance: 500000000 },
9
+ ]),
10
+ getStakes: jest.fn().mockResolvedValue([]),
7
11
  }));
8
12
 
13
+ const mockedGetStakes = jest.mocked(getStakes);
14
+
9
15
  describe("getBalance", () => {
10
16
  beforeEach(() => {
11
17
  jest.clearAllMocks();
12
18
  });
13
19
 
14
- it("should return the correct balance as bigint", async () => {
15
- // Mock the getAccount response
16
- const mockBalance = { balance: 1000000000 };
20
+ it("should return the correct native SUI balance", async () => {
21
+ const address = "0x123";
22
+ const result = await getBalance(address);
17
23
 
24
+ expect(result[0]).toMatchObject({
25
+ value: BigInt(1000000000),
26
+ asset: { type: "native" },
27
+ });
28
+ });
29
+
30
+ it("should return the correct USDT token balance", async () => {
18
31
  const address = "0x123";
19
32
  const result = await getBalance(address);
20
33
 
21
- expect(getAccount).toHaveBeenCalledWith(address);
22
- expect(result[0].value).toBe(BigInt(mockBalance.balance.toString()));
34
+ expect(result[1]).toMatchObject({
35
+ value: BigInt(500000000),
36
+ asset: { type: "token", assetReference: "0x3::usdt::USDT" },
37
+ });
38
+ });
39
+
40
+ it("should return staking balances when stakes are available", async () => {
41
+ const mockStakes: Stake[] = [
42
+ {
43
+ uid: "stake_1",
44
+ address: "0x123",
45
+ delegate: "0xvalidator1",
46
+ state: "active",
47
+ asset: { type: "native" },
48
+ amount: BigInt(2000000000),
49
+ },
50
+ ];
51
+
52
+ mockedGetStakes.mockResolvedValueOnce(mockStakes);
53
+
54
+ const address = "0x123";
55
+ const result = await getBalance(address);
56
+
57
+ expect(getAllBalancesCached).toHaveBeenCalledWith(address);
58
+ expect(getStakes).toHaveBeenCalledWith(address);
59
+ expect(result).toHaveLength(3);
60
+
61
+ expect(result[2]).toMatchObject({
62
+ value: BigInt(2000000000),
63
+ asset: { type: "native" },
64
+ stake: {
65
+ uid: "stake_1",
66
+ address: "0x123",
67
+ delegate: "0xvalidator1",
68
+ state: "active",
69
+ asset: { type: "native" },
70
+ amount: BigInt(2000000000),
71
+ },
72
+ });
23
73
  });
24
74
  });