@ledgerhq/coin-sui 0.11.0 → 0.12.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 (188) hide show
  1. package/.eslintrc.js +1 -0
  2. package/.turbo/turbo-build.log +1 -1
  3. package/.unimportedrc.json +1 -0
  4. package/CHANGELOG.md +21 -0
  5. package/index.d.ts +0 -1
  6. package/jest.config.js +1 -1
  7. package/lib/bridge/broadcast.d.ts.map +1 -1
  8. package/lib/bridge/broadcast.js +3 -0
  9. package/lib/bridge/broadcast.js.map +1 -1
  10. package/lib/bridge/buildOptimisticOperation.js +31 -0
  11. package/lib/bridge/buildOptimisticOperation.js.map +1 -1
  12. package/lib/bridge/buildTransaction.d.ts +1 -1
  13. package/lib/bridge/buildTransaction.d.ts.map +1 -1
  14. package/lib/bridge/buildTransaction.js +3 -1
  15. package/lib/bridge/buildTransaction.js.map +1 -1
  16. package/lib/bridge/buildTransaction.test.js +4 -0
  17. package/lib/bridge/buildTransaction.test.js.map +1 -1
  18. package/lib/bridge/estimateMaxSpendable.d.ts.map +1 -1
  19. package/lib/bridge/estimateMaxSpendable.js +15 -3
  20. package/lib/bridge/estimateMaxSpendable.js.map +1 -1
  21. package/lib/bridge/estimateMaxSpendable.test.js +27 -0
  22. package/lib/bridge/estimateMaxSpendable.test.js.map +1 -1
  23. package/lib/bridge/getFeesForTransaction.d.ts.map +1 -1
  24. package/lib/bridge/getFeesForTransaction.js +13 -1
  25. package/lib/bridge/getFeesForTransaction.js.map +1 -1
  26. package/lib/bridge/getOperationExtra.d.ts +2 -0
  27. package/lib/bridge/getOperationExtra.d.ts.map +1 -0
  28. package/lib/bridge/getOperationExtra.js +6 -0
  29. package/lib/bridge/getOperationExtra.js.map +1 -0
  30. package/lib/bridge/getTransactionStatus.d.ts.map +1 -1
  31. package/lib/bridge/getTransactionStatus.js +45 -20
  32. package/lib/bridge/getTransactionStatus.js.map +1 -1
  33. package/lib/bridge/preload.d.ts.map +1 -1
  34. package/lib/bridge/preload.js +5 -3
  35. package/lib/bridge/preload.js.map +1 -1
  36. package/lib/bridge/preload.test.js +13 -217
  37. package/lib/bridge/preload.test.js.map +1 -1
  38. package/lib/bridge/prepareTransaction.js +1 -1
  39. package/lib/bridge/prepareTransaction.js.map +1 -1
  40. package/lib/bridge/synchronisation.d.ts.map +1 -1
  41. package/lib/bridge/synchronisation.js +5 -2
  42. package/lib/bridge/synchronisation.js.map +1 -1
  43. package/lib/bridge/synchronisation.test.js +355 -7
  44. package/lib/bridge/synchronisation.test.js.map +1 -1
  45. package/lib/bridge/utils.d.ts +1 -1
  46. package/lib/bridge/utils.d.ts.map +1 -1
  47. package/lib/bridge/utils.js +22 -0
  48. package/lib/bridge/utils.js.map +1 -1
  49. package/lib/constants.d.ts +4 -0
  50. package/lib/constants.d.ts.map +1 -0
  51. package/lib/constants.js +7 -0
  52. package/lib/constants.js.map +1 -0
  53. package/lib/errors.d.ts +13 -0
  54. package/lib/errors.d.ts.map +1 -0
  55. package/lib/errors.js +21 -0
  56. package/lib/errors.js.map +1 -0
  57. package/lib/logic/craftTransaction.d.ts +5 -10
  58. package/lib/logic/craftTransaction.d.ts.map +1 -1
  59. package/lib/logic/craftTransaction.js +2 -1
  60. package/lib/logic/craftTransaction.js.map +1 -1
  61. package/lib/logic/estimateFees.d.ts +1 -1
  62. package/lib/logic/estimateFees.d.ts.map +1 -1
  63. package/lib/logic/estimateFees.js +14 -2
  64. package/lib/logic/estimateFees.js.map +1 -1
  65. package/lib/logic/index.d.ts +2 -1
  66. package/lib/logic/index.d.ts.map +1 -1
  67. package/lib/logic/index.js +3 -1
  68. package/lib/logic/index.js.map +1 -1
  69. package/lib/logic/stake.d.ts +3 -0
  70. package/lib/logic/stake.d.ts.map +1 -0
  71. package/lib/logic/stake.js +12 -0
  72. package/lib/logic/stake.js.map +1 -0
  73. package/lib/network/index.d.ts +5 -4
  74. package/lib/network/index.d.ts.map +1 -1
  75. package/lib/network/index.js +5 -3
  76. package/lib/network/index.js.map +1 -1
  77. package/lib/network/sdk.d.ts +5 -3
  78. package/lib/network/sdk.d.ts.map +1 -1
  79. package/lib/network/sdk.js +148 -26
  80. package/lib/network/sdk.js.map +1 -1
  81. package/lib/network/sdk.test.js +491 -6
  82. package/lib/network/sdk.test.js.map +1 -1
  83. package/lib/types/bridge.d.ts +33 -6
  84. package/lib/types/bridge.d.ts.map +1 -1
  85. package/lib-es/bridge/broadcast.d.ts.map +1 -1
  86. package/lib-es/bridge/broadcast.js +3 -0
  87. package/lib-es/bridge/broadcast.js.map +1 -1
  88. package/lib-es/bridge/buildOptimisticOperation.js +31 -0
  89. package/lib-es/bridge/buildOptimisticOperation.js.map +1 -1
  90. package/lib-es/bridge/buildTransaction.d.ts +1 -1
  91. package/lib-es/bridge/buildTransaction.d.ts.map +1 -1
  92. package/lib-es/bridge/buildTransaction.js +3 -1
  93. package/lib-es/bridge/buildTransaction.js.map +1 -1
  94. package/lib-es/bridge/buildTransaction.test.js +4 -0
  95. package/lib-es/bridge/buildTransaction.test.js.map +1 -1
  96. package/lib-es/bridge/estimateMaxSpendable.d.ts.map +1 -1
  97. package/lib-es/bridge/estimateMaxSpendable.js +15 -3
  98. package/lib-es/bridge/estimateMaxSpendable.js.map +1 -1
  99. package/lib-es/bridge/estimateMaxSpendable.test.js +27 -0
  100. package/lib-es/bridge/estimateMaxSpendable.test.js.map +1 -1
  101. package/lib-es/bridge/getFeesForTransaction.d.ts.map +1 -1
  102. package/lib-es/bridge/getFeesForTransaction.js +13 -1
  103. package/lib-es/bridge/getFeesForTransaction.js.map +1 -1
  104. package/lib-es/bridge/getOperationExtra.d.ts +2 -0
  105. package/lib-es/bridge/getOperationExtra.d.ts.map +1 -0
  106. package/lib-es/bridge/getOperationExtra.js +2 -0
  107. package/lib-es/bridge/getOperationExtra.js.map +1 -0
  108. package/lib-es/bridge/getTransactionStatus.d.ts.map +1 -1
  109. package/lib-es/bridge/getTransactionStatus.js +46 -21
  110. package/lib-es/bridge/getTransactionStatus.js.map +1 -1
  111. package/lib-es/bridge/preload.d.ts.map +1 -1
  112. package/lib-es/bridge/preload.js +5 -3
  113. package/lib-es/bridge/preload.js.map +1 -1
  114. package/lib-es/bridge/preload.test.js +14 -218
  115. package/lib-es/bridge/preload.test.js.map +1 -1
  116. package/lib-es/bridge/prepareTransaction.js +1 -1
  117. package/lib-es/bridge/prepareTransaction.js.map +1 -1
  118. package/lib-es/bridge/synchronisation.d.ts.map +1 -1
  119. package/lib-es/bridge/synchronisation.js +6 -3
  120. package/lib-es/bridge/synchronisation.js.map +1 -1
  121. package/lib-es/bridge/synchronisation.test.js +332 -7
  122. package/lib-es/bridge/synchronisation.test.js.map +1 -1
  123. package/lib-es/bridge/utils.d.ts +1 -1
  124. package/lib-es/bridge/utils.d.ts.map +1 -1
  125. package/lib-es/bridge/utils.js +22 -0
  126. package/lib-es/bridge/utils.js.map +1 -1
  127. package/lib-es/constants.d.ts +4 -0
  128. package/lib-es/constants.d.ts.map +1 -0
  129. package/lib-es/constants.js +4 -0
  130. package/lib-es/constants.js.map +1 -0
  131. package/lib-es/errors.d.ts +13 -0
  132. package/lib-es/errors.d.ts.map +1 -0
  133. package/lib-es/errors.js +18 -0
  134. package/lib-es/errors.js.map +1 -0
  135. package/lib-es/logic/craftTransaction.d.ts +5 -10
  136. package/lib-es/logic/craftTransaction.d.ts.map +1 -1
  137. package/lib-es/logic/craftTransaction.js +2 -1
  138. package/lib-es/logic/craftTransaction.js.map +1 -1
  139. package/lib-es/logic/estimateFees.d.ts +1 -1
  140. package/lib-es/logic/estimateFees.d.ts.map +1 -1
  141. package/lib-es/logic/estimateFees.js +14 -2
  142. package/lib-es/logic/estimateFees.js.map +1 -1
  143. package/lib-es/logic/index.d.ts +2 -1
  144. package/lib-es/logic/index.d.ts.map +1 -1
  145. package/lib-es/logic/index.js +1 -0
  146. package/lib-es/logic/index.js.map +1 -1
  147. package/lib-es/logic/stake.d.ts +3 -0
  148. package/lib-es/logic/stake.d.ts.map +1 -0
  149. package/lib-es/logic/stake.js +8 -0
  150. package/lib-es/logic/stake.js.map +1 -0
  151. package/lib-es/network/index.d.ts +5 -4
  152. package/lib-es/network/index.d.ts.map +1 -1
  153. package/lib-es/network/index.js +4 -3
  154. package/lib-es/network/index.js.map +1 -1
  155. package/lib-es/network/sdk.d.ts +5 -3
  156. package/lib-es/network/sdk.d.ts.map +1 -1
  157. package/lib-es/network/sdk.js +144 -25
  158. package/lib-es/network/sdk.js.map +1 -1
  159. package/lib-es/network/sdk.test.js +491 -6
  160. package/lib-es/network/sdk.test.js.map +1 -1
  161. package/lib-es/types/bridge.d.ts +33 -6
  162. package/lib-es/types/bridge.d.ts.map +1 -1
  163. package/package.json +20 -12
  164. package/src/bridge/broadcast.ts +3 -0
  165. package/src/bridge/buildOptimisticOperation.ts +47 -4
  166. package/src/bridge/buildTransaction.test.ts +4 -0
  167. package/src/bridge/buildTransaction.ts +3 -1
  168. package/src/bridge/estimateMaxSpendable.test.ts +33 -0
  169. package/src/bridge/estimateMaxSpendable.ts +17 -3
  170. package/src/bridge/getFeesForTransaction.ts +14 -1
  171. package/src/bridge/getOperationExtra.ts +1 -0
  172. package/src/bridge/getTransactionStatus.ts +53 -21
  173. package/src/bridge/preload.test.ts +13 -279
  174. package/src/bridge/preload.ts +5 -3
  175. package/src/bridge/prepareTransaction.ts +1 -1
  176. package/src/bridge/synchronisation.test.ts +389 -7
  177. package/src/bridge/synchronisation.ts +6 -3
  178. package/src/bridge/utils.ts +25 -1
  179. package/src/constants.ts +4 -0
  180. package/src/errors.ts +21 -0
  181. package/src/logic/craftTransaction.ts +6 -9
  182. package/src/logic/estimateFees.ts +16 -1
  183. package/src/logic/index.ts +2 -1
  184. package/src/logic/stake.ts +9 -0
  185. package/src/network/index.ts +6 -3
  186. package/src/network/sdk.test.ts +538 -10
  187. package/src/network/sdk.ts +179 -31
  188. package/src/types/bridge.ts +32 -6
@@ -4,6 +4,7 @@ import { getMainAccount } from "@ledgerhq/coin-framework/account/index";
4
4
  import type { SuiAccount, Transaction } from "../types";
5
5
  import getFeesForTransaction from "./getFeesForTransaction";
6
6
  import createTransaction from "./createTransaction";
7
+ import { ONE_SUI } from "../constants";
7
8
 
8
9
  /**
9
10
  * Returns the maximum possible amount for transaction
@@ -30,17 +31,30 @@ export const estimateMaxSpendable: AccountBridge<Transaction>["estimateMaxSpenda
30
31
 
31
32
  let spendableBalance = account.spendableBalance;
32
33
 
33
- if (account.type == "Account") {
34
+ if (account.type === "Account") {
34
35
  const fees = await getFeesForTransaction({
35
36
  account: mainAccount,
36
37
  transaction: estimatedTransaction,
37
38
  });
38
39
  if (fees) {
39
- spendableBalance = BigNumber.max(spendableBalance.minus(fees), 0);
40
+ spendableBalance = spendableBalance.minus(fees);
40
41
  }
41
42
  }
42
43
 
43
- return spendableBalance;
44
+ // Apply delegation-specific constraints
45
+ if (transaction?.mode === "delegate") {
46
+ // Reserve 0.1 SUI for future gas fees as recommended
47
+ const gasReserve = new BigNumber(ONE_SUI).div(10);
48
+ spendableBalance = spendableBalance.minus(gasReserve);
49
+
50
+ // Ensure we don't go below 1 SUI minimum for delegation
51
+ const oneSui = new BigNumber(ONE_SUI);
52
+ if (spendableBalance.lt(oneSui)) {
53
+ return new BigNumber(0);
54
+ }
55
+ }
56
+
57
+ return BigNumber.max(spendableBalance, 0);
44
58
  } catch (e) {
45
59
  return new BigNumber(0);
46
60
  }
@@ -37,11 +37,24 @@ export default async function getEstimatedFees({
37
37
  const subAccount = findSubAccountById(account, transaction.subAccountId ?? "");
38
38
  const asset = toSuiAsset(subAccount?.token.contractAddress ?? DEFAULT_COIN_TYPE);
39
39
 
40
+ let transactionType: "send" | "delegate" | "undelegate";
41
+ switch (transaction.mode) {
42
+ case "delegate":
43
+ transactionType = "delegate";
44
+ break;
45
+ case "undelegate":
46
+ transactionType = "undelegate";
47
+ break;
48
+ default:
49
+ transactionType = "send";
50
+ break;
51
+ }
52
+
40
53
  const fees = await estimateFees({
41
54
  recipient: getAbandonSeedAddress(account.currency.id),
42
55
  sender: account.freshAddress,
43
56
  amount: BigInt(t.amount.toString()),
44
- type: "send",
57
+ type: transactionType,
45
58
  asset,
46
59
  });
47
60
  return new BigNumber(fees.toString());
@@ -0,0 +1 @@
1
+ export { getOperationExtra } from "../network/sdk";
@@ -3,17 +3,24 @@ import {
3
3
  NotEnoughBalance,
4
4
  NotEnoughBalanceInParentAccount,
5
5
  RecipientRequired,
6
- InvalidAddress,
7
6
  InvalidAddressBecauseDestinationIsAlsoSource,
8
7
  AmountRequired,
9
8
  FeeNotLoaded,
10
9
  FeeTooHigh,
10
+ InvalidAddress,
11
11
  } from "@ledgerhq/errors";
12
12
  import { AccountBridge } from "@ledgerhq/types-live";
13
13
  import { findSubAccountById } from "@ledgerhq/coin-framework/account/index";
14
14
  import { isValidSuiAddress } from "@mysten/sui/utils";
15
15
  import type { SuiAccount, Transaction, TransactionStatus } from "../types";
16
16
  import { ensureAddressFormat } from "../utils";
17
+ import {
18
+ OneSuiMinForStake,
19
+ OneSuiMinForUnstake,
20
+ OneSuiMinForUnstakeToBeLeft,
21
+ SomeSuiForUnstake,
22
+ } from "../errors";
23
+ import { ONE_SUI } from "../constants";
17
24
 
18
25
  /**
19
26
  * Get the status of a transaction.
@@ -39,36 +46,61 @@ export const getTransactionStatus: AccountBridge<
39
46
  accountBalance = subAccount?.balance ?? new BigNumber(0);
40
47
  }
41
48
 
42
- if (amount.lte(0)) {
49
+ if (amount.lte(0) && !transaction.useAllAmount) {
43
50
  errors.amount = new AmountRequired();
44
51
  } else if (estimatedFees.times(10).gt(amount) && !transaction.subAccountId) {
45
52
  warnings.feeTooHigh = new FeeTooHigh();
46
53
  }
47
54
 
48
- if (!transaction.recipient) {
49
- errors.recipient = new RecipientRequired();
50
- } else if (!isValidSuiAddress(transaction.recipient)) {
51
- errors.recipient = new InvalidAddress(undefined, {
52
- currencyName: account.currency.name,
53
- });
55
+ if (!transaction.fees) {
56
+ errors.fees = new FeeNotLoaded();
54
57
  }
55
58
 
56
- if (ensureAddressFormat(account.freshAddress) === ensureAddressFormat(transaction.recipient)) {
57
- errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource();
58
- }
59
+ if (transaction.mode === "undelegate") {
60
+ const stakes = account.suiResources?.stakes?.flatMap(({ stakes }) => stakes) ?? [];
61
+ const stake = stakes.find(s => s.stakedSuiId === transaction.stakedSuiId);
62
+ if (stake) {
63
+ if (!transaction.useAllAmount && amount.lt(ONE_SUI)) {
64
+ errors.amount = new OneSuiMinForUnstake();
65
+ }
66
+ const stakeLeft = BigNumber(stake?.principal).minus(amount);
67
+ if (!transaction.useAllAmount && stakeLeft.lt(ONE_SUI) && stakeLeft.gt(0)) {
68
+ errors.amount = new OneSuiMinForUnstakeToBeLeft();
69
+ }
70
+ }
71
+ } else {
72
+ if (!transaction.recipient) {
73
+ errors.recipient = new RecipientRequired();
74
+ } else if (!isValidSuiAddress(transaction.recipient)) {
75
+ errors.recipient = new InvalidAddress(undefined, {
76
+ currencyName: account.currency.name,
77
+ });
78
+ }
59
79
 
60
- if (transaction.subAccountId && estimatedFees.gt(account.balance)) {
61
- errors.amount = new NotEnoughBalanceInParentAccount();
62
- }
80
+ if (ensureAddressFormat(account.freshAddress) === ensureAddressFormat(transaction.recipient)) {
81
+ errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource();
82
+ }
63
83
 
64
- if (totalSpent.eq(0) && transaction.useAllAmount) {
65
- errors.amount = new NotEnoughBalance();
66
- }
67
- if (totalSpent.gt(accountBalance)) {
68
- errors.amount = new NotEnoughBalance();
84
+ if (totalSpent.eq(0) && transaction.useAllAmount) {
85
+ errors.amount = new NotEnoughBalance();
86
+ }
87
+
88
+ if (transaction.subAccountId && estimatedFees.gt(account.balance)) {
89
+ errors.amount = new NotEnoughBalanceInParentAccount();
90
+ }
91
+
92
+ if (totalSpent.gt(accountBalance)) {
93
+ errors.amount = new NotEnoughBalance();
94
+ }
69
95
  }
70
- if (!transaction.fees) {
71
- errors.fees = new FeeNotLoaded();
96
+ if (transaction.mode === "delegate") {
97
+ if (amount.lt(new BigNumber(ONE_SUI))) {
98
+ errors.amount = new OneSuiMinForStake();
99
+ }
100
+
101
+ // 0.1 SUI
102
+ if (account.balance.minus(transaction.amount).lt(ONE_SUI / 10))
103
+ warnings.amount = new SomeSuiForUnstake();
72
104
  }
73
105
 
74
106
  return {
@@ -5,8 +5,6 @@ import {
5
5
  setSuiPreloadData,
6
6
  getSuiPreloadDataUpdates,
7
7
  getPreloadStrategy,
8
- preload,
9
- hydrate,
10
8
  } from "./preload";
11
9
  import type { SuiPreloadData } from "../types";
12
10
 
@@ -18,17 +16,12 @@ jest.mock("@ledgerhq/logs", () => ({
18
16
  describe("Sui Preload Functions", () => {
19
17
  beforeEach(() => {
20
18
  // Reset the preload data before each test
21
- setSuiPreloadData({});
19
+ setSuiPreloadData({ validators: [] });
22
20
  });
23
21
 
24
22
  describe("getCurrentSuiPreloadData", () => {
25
- it("should return empty object by default", () => {
26
- const data = getCurrentSuiPreloadData();
27
- expect(data).toEqual({});
28
- });
29
-
30
23
  it("should return the current preloaded data", () => {
31
- const testData: SuiPreloadData = { testKey: "testValue" };
24
+ const testData: SuiPreloadData = { validators: [] };
32
25
  setSuiPreloadData(testData);
33
26
 
34
27
  const data = getCurrentSuiPreloadData();
@@ -36,46 +29,6 @@ describe("Sui Preload Functions", () => {
36
29
  });
37
30
  });
38
31
 
39
- describe("setSuiPreloadData", () => {
40
- it("should set the preload data", () => {
41
- const testData: SuiPreloadData = { key1: "value1", key2: "value2" };
42
-
43
- setSuiPreloadData(testData);
44
-
45
- const currentData = getCurrentSuiPreloadData();
46
- expect(currentData).toEqual(testData);
47
- });
48
-
49
- it("should not update if data is the same reference", () => {
50
- const testData: SuiPreloadData = { test: "data" };
51
-
52
- // Set data first time
53
- setSuiPreloadData(testData);
54
- const firstCall = getCurrentSuiPreloadData();
55
-
56
- // Set the same data reference
57
- setSuiPreloadData(testData);
58
- const secondCall = getCurrentSuiPreloadData();
59
-
60
- expect(firstCall).toBe(secondCall);
61
- expect(firstCall).toEqual(testData);
62
- });
63
-
64
- it("should update if data has different content", () => {
65
- const firstData: SuiPreloadData = { test: "first" };
66
- const secondData: SuiPreloadData = { test: "second" };
67
-
68
- setSuiPreloadData(firstData);
69
- const firstCall = getCurrentSuiPreloadData();
70
-
71
- setSuiPreloadData(secondData);
72
- const secondCall = getCurrentSuiPreloadData();
73
-
74
- expect(firstCall).not.toBe(secondCall);
75
- expect(secondCall).toEqual(secondData);
76
- });
77
- });
78
-
79
32
  describe("getSuiPreloadDataUpdates", () => {
80
33
  it("should return an Observable", () => {
81
34
  const updates = getSuiPreloadDataUpdates();
@@ -83,7 +36,7 @@ describe("Sui Preload Functions", () => {
83
36
  });
84
37
 
85
38
  it("should emit updates when data is set", done => {
86
- const testData: SuiPreloadData = { update: "test" };
39
+ const testData: SuiPreloadData = { validators: [] };
87
40
  const updates = getSuiPreloadDataUpdates();
88
41
 
89
42
  updates.pipe(take(1)).subscribe(data => {
@@ -94,240 +47,21 @@ describe("Sui Preload Functions", () => {
94
47
  setSuiPreloadData(testData);
95
48
  });
96
49
 
97
- it("should emit multiple updates", done => {
98
- const firstData: SuiPreloadData = { first: "data" };
99
- const secondData: SuiPreloadData = { second: "data" };
100
- const updates = getSuiPreloadDataUpdates();
101
-
102
- let callCount = 0;
103
- updates.pipe(take(2)).subscribe(data => {
104
- callCount++;
105
- if (callCount === 1) {
106
- expect(data).toEqual(firstData);
107
- } else if (callCount === 2) {
108
- expect(data).toEqual(secondData);
109
- done();
110
- }
111
- });
112
-
113
- setSuiPreloadData(firstData);
114
- setSuiPreloadData(secondData);
115
- });
116
-
117
- it("should not emit when same data reference is set", done => {
118
- const testData: SuiPreloadData = { test: "data" };
119
- const updates = getSuiPreloadDataUpdates();
120
-
121
- let callCount = 0;
122
- updates.pipe(take(1)).subscribe(data => {
123
- callCount++;
124
- expect(data).toEqual(testData);
125
- // Wait a bit to ensure no additional emissions
126
- setTimeout(() => {
127
- expect(callCount).toBe(1);
128
- done();
129
- }, 10);
130
- });
131
-
132
- setSuiPreloadData(testData);
133
- setSuiPreloadData(testData); // Same reference, should not emit
134
- });
135
- });
136
-
137
- describe("getPreloadStrategy", () => {
138
- it("should return preload strategy with correct max age", () => {
139
- const strategy = getPreloadStrategy();
140
-
141
- expect(strategy).toHaveProperty("preloadMaxAge");
142
- expect(strategy.preloadMaxAge).toBe(30 * 60 * 1000); // 30 minutes in milliseconds
143
- });
144
-
145
- it("should return consistent strategy object", () => {
146
- const strategy1 = getPreloadStrategy();
147
- const strategy2 = getPreloadStrategy();
148
-
149
- expect(strategy1).toEqual(strategy2);
150
- expect(strategy1.preloadMaxAge).toBe(30 * 60 * 1000);
151
- });
152
- });
153
-
154
- describe("preload", () => {
155
- it("should return empty object", async () => {
156
- const result = await preload();
157
- expect(result).toEqual({});
158
- });
159
-
160
- it("should log preload message", async () => {
161
- // eslint-disable-next-line @typescript-eslint/no-var-requires
162
- const { log } = require("@ledgerhq/logs");
163
-
164
- await preload();
165
-
166
- expect(log).toHaveBeenCalledWith("sui/preload", "preloading sui data...");
167
- });
168
-
169
- it("should always return the same result", async () => {
170
- const result1 = await preload();
171
- const result2 = await preload();
172
-
173
- expect(result1).toEqual(result2);
174
- expect(result1).toEqual({});
175
- });
176
- });
177
-
178
- describe("hydrate", () => {
179
- it("should set preload data with hydrated data", () => {
180
- const testData: SuiPreloadData = { hydrate: "test" };
181
-
182
- hydrate(testData);
183
-
184
- const currentData = getCurrentSuiPreloadData();
185
- expect(currentData).toEqual({}); // fromHydratePreloadData returns empty object
186
- });
187
-
188
- it("should emit update when hydrated", done => {
189
- const testData: SuiPreloadData = { hydrate: "test" };
190
- const updates = getSuiPreloadDataUpdates();
191
-
192
- updates.pipe(take(1)).subscribe(data => {
193
- expect(data).toEqual({}); // fromHydratePreloadData returns empty object
194
- done();
195
- });
196
-
197
- hydrate(testData);
198
- });
199
-
200
- it("should handle empty data", () => {
201
- const emptyData: SuiPreloadData = {};
202
-
203
- hydrate(emptyData);
204
-
205
- const currentData = getCurrentSuiPreloadData();
206
- expect(currentData).toEqual({});
207
- });
208
-
209
- it("should handle complex data structures", () => {
210
- const complexData: SuiPreloadData = {
211
- nested: { key: "value" },
212
- array: [1, 2, 3],
213
- boolean: true,
214
- number: 42,
215
- };
216
-
217
- hydrate(complexData);
218
-
219
- const currentData = getCurrentSuiPreloadData();
220
- expect(currentData).toEqual({}); // fromHydratePreloadData returns empty object
221
- });
222
- });
223
-
224
- describe("Integration Tests", () => {
225
- it("should maintain data consistency across multiple operations", () => {
226
- const testData: SuiPreloadData = { integration: "test" };
227
-
228
- // Set data
229
- setSuiPreloadData(testData);
230
- expect(getCurrentSuiPreloadData()).toEqual(testData);
231
-
232
- // Hydrate should override
233
- hydrate({ different: "data" });
234
- expect(getCurrentSuiPreloadData()).toEqual({});
235
-
236
- // Set again
237
- setSuiPreloadData(testData);
238
- expect(getCurrentSuiPreloadData()).toEqual(testData);
239
- });
240
-
241
- it("should handle rapid successive updates", done => {
242
- const updates = getSuiPreloadDataUpdates();
243
- const results: SuiPreloadData[] = [];
50
+ describe("getPreloadStrategy", () => {
51
+ it("should return preload strategy with correct max age", () => {
52
+ const strategy = getPreloadStrategy();
244
53
 
245
- updates.pipe(take(3)).subscribe(data => {
246
- results.push(data);
247
- if (results.length === 3) {
248
- expect(results).toEqual([{ first: "update" }, { second: "update" }, { third: "update" }]);
249
- done();
250
- }
54
+ expect(strategy).toHaveProperty("preloadMaxAge");
55
+ expect(strategy.preloadMaxAge).toBe(30 * 60 * 1000); // 30 minutes in milliseconds
251
56
  });
252
57
 
253
- setSuiPreloadData({ first: "update" });
254
- setSuiPreloadData({ second: "update" });
255
- setSuiPreloadData({ third: "update" });
256
- });
257
-
258
- it("should work with preload strategy", async () => {
259
- const strategy = getPreloadStrategy();
260
- const preloadResult = await preload();
261
-
262
- expect(strategy.preloadMaxAge).toBe(30 * 60 * 1000);
263
- expect(preloadResult).toEqual({});
264
-
265
- // Preload should not affect current data
266
- expect(getCurrentSuiPreloadData()).toEqual({});
267
- });
268
- });
269
-
270
- describe("Edge Cases", () => {
271
- it("should handle null data gracefully", () => {
272
- // TypeScript would prevent this, but testing runtime behavior
273
- const nullData = null as any;
274
-
275
- expect(() => {
276
- setSuiPreloadData(nullData);
277
- }).not.toThrow();
278
- });
279
-
280
- it("should handle undefined data gracefully", () => {
281
- // TypeScript would prevent this, but testing runtime behavior
282
- const undefinedData = undefined as any;
283
-
284
- expect(() => {
285
- setSuiPreloadData(undefinedData);
286
- }).not.toThrow();
287
- });
288
-
289
- it("should handle multiple subscribers", done => {
290
- const updates1 = getSuiPreloadDataUpdates();
291
- const updates2 = getSuiPreloadDataUpdates();
292
-
293
- let subscriber1Called = false;
294
- let subscriber2Called = false;
58
+ it("should return consistent strategy object", () => {
59
+ const strategy1 = getPreloadStrategy();
60
+ const strategy2 = getPreloadStrategy();
295
61
 
296
- updates1.pipe(take(1)).subscribe(() => {
297
- subscriber1Called = true;
298
- if (subscriber1Called && subscriber2Called) {
299
- done();
300
- }
62
+ expect(strategy1).toEqual(strategy2);
63
+ expect(strategy1.preloadMaxAge).toBe(30 * 60 * 1000);
301
64
  });
302
-
303
- updates2.pipe(take(1)).subscribe(() => {
304
- subscriber2Called = true;
305
- if (subscriber1Called && subscriber2Called) {
306
- done();
307
- }
308
- });
309
-
310
- setSuiPreloadData({ multi: "subscriber" });
311
- });
312
-
313
- it("should handle unsubscribe scenarios", done => {
314
- const updates = getSuiPreloadDataUpdates();
315
- let callCount = 0;
316
-
317
- const subscription = updates.subscribe(() => {
318
- callCount++;
319
- });
320
-
321
- setSuiPreloadData({ first: "update" });
322
-
323
- subscription.unsubscribe();
324
-
325
- setSuiPreloadData({ second: "update" });
326
-
327
- setTimeout(() => {
328
- expect(callCount).toBe(1); // Should only receive first update
329
- done();
330
- }, 10);
331
65
  });
332
66
  });
333
67
  });
@@ -2,13 +2,14 @@ import { Observable, Subject } from "rxjs";
2
2
  import { log } from "@ledgerhq/logs";
3
3
 
4
4
  import type { SuiPreloadData } from "../types";
5
+ import { getValidators } from "../network/sdk";
5
6
 
6
7
  const PRELOAD_MAX_AGE = 30 * 60 * 1000; // 30 minutes
7
8
 
8
- let currentPreloadedData: SuiPreloadData = {};
9
+ let currentPreloadedData: SuiPreloadData = { validators: [] };
9
10
 
10
11
  function fromHydratePreloadData(_data: SuiPreloadData): SuiPreloadData {
11
- return {};
12
+ return { validators: _data.validators };
12
13
  }
13
14
 
14
15
  const updates = new Subject<SuiPreloadData>();
@@ -34,9 +35,10 @@ export const getPreloadStrategy = () => ({
34
35
  });
35
36
 
36
37
  export const preload = async (): Promise<SuiPreloadData> => {
38
+ const validators = await getValidators();
37
39
  log("sui/preload", "preloading sui data...");
38
40
 
39
- return {};
41
+ return { validators };
40
42
  };
41
43
 
42
44
  export const hydrate = (data: SuiPreloadData) => {
@@ -35,7 +35,7 @@ export const prepareTransaction: AccountBridge<
35
35
  fees = BigNumber(0);
36
36
  }
37
37
 
38
- let mode: SuiTransactionMode = "send";
38
+ let mode: SuiTransactionMode = transaction.mode ?? "send";
39
39
  let coinType = DEFAULT_COIN_TYPE;
40
40
  if (transaction?.subAccountId) {
41
41
  mode = "token.send";