@ledgerhq/live-common 34.42.2-nightly.0 → 34.43.0-nightly.2

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 (120) hide show
  1. package/lib/appSupportsQuitApp.d.ts +1 -0
  2. package/lib/appSupportsQuitApp.d.ts.map +1 -1
  3. package/lib/appSupportsQuitApp.js +1 -0
  4. package/lib/appSupportsQuitApp.js.map +1 -1
  5. package/lib/appSupportsQuitApp.test.js +28 -3
  6. package/lib/appSupportsQuitApp.test.js.map +1 -1
  7. package/lib/bridge/generic-alpaca/alpaca/index.d.ts +1 -1
  8. package/lib/bridge/generic-alpaca/alpaca/index.d.ts.map +1 -1
  9. package/lib/bridge/generic-alpaca/alpaca/index.js.map +1 -1
  10. package/lib/bridge/generic-alpaca/alpaca/network/network-alpaca.d.ts +2 -6
  11. package/lib/bridge/generic-alpaca/alpaca/network/network-alpaca.d.ts.map +1 -1
  12. package/lib/bridge/generic-alpaca/alpaca/network/network-alpaca.js.map +1 -1
  13. package/lib/bridge/generic-alpaca/signOperation.js.map +1 -1
  14. package/lib/bridge/generic-alpaca/utils.d.ts +2 -2
  15. package/lib/bridge/generic-alpaca/utils.d.ts.map +1 -1
  16. package/lib/bridge/generic-alpaca/utils.js +1 -1
  17. package/lib/bridge/generic-alpaca/utils.js.map +1 -1
  18. package/lib/e2e/index.d.ts +1 -0
  19. package/lib/e2e/index.d.ts.map +1 -1
  20. package/lib/e2e/speculos.d.ts.map +1 -1
  21. package/lib/e2e/speculos.js +45 -15
  22. package/lib/e2e/speculos.js.map +1 -1
  23. package/lib/e2e/speculosCI.d.ts.map +1 -1
  24. package/lib/e2e/speculosCI.js +22 -4
  25. package/lib/e2e/speculosCI.js.map +1 -1
  26. package/lib/env.react.d.ts +1 -1
  27. package/lib/env.react.d.ts.map +1 -1
  28. package/lib/families/solana/bridge/mock-data.d.ts.map +1 -1
  29. package/lib/families/solana/bridge/mock-data.js +46 -316
  30. package/lib/families/solana/bridge/mock-data.js.map +1 -1
  31. package/lib/families/solana/bridge/mock.js +2 -2
  32. package/lib/families/solana/bridge/mock.js.map +1 -1
  33. package/lib/modularDrawer/__mocks__/accounts.mock.d.ts +18 -0
  34. package/lib/modularDrawer/__mocks__/accounts.mock.d.ts.map +1 -0
  35. package/lib/modularDrawer/__mocks__/accounts.mock.js +48 -0
  36. package/lib/modularDrawer/__mocks__/accounts.mock.js.map +1 -0
  37. package/lib/utils/__tests__/composeHooks.test.d.ts +2 -0
  38. package/lib/utils/__tests__/composeHooks.test.d.ts.map +1 -0
  39. package/lib/utils/__tests__/composeHooks.test.js +35 -0
  40. package/lib/utils/__tests__/composeHooks.test.js.map +1 -0
  41. package/lib/utils/__tests__/getAccountTuplesForCurrency.test.d.ts +2 -0
  42. package/lib/utils/__tests__/getAccountTuplesForCurrency.test.d.ts.map +1 -0
  43. package/lib/utils/__tests__/getAccountTuplesForCurrency.test.js +104 -0
  44. package/lib/utils/__tests__/getAccountTuplesForCurrency.test.js.map +1 -0
  45. package/lib/utils/composeHooks.d.ts +11 -0
  46. package/lib/utils/composeHooks.d.ts.map +1 -0
  47. package/lib/utils/composeHooks.js +25 -0
  48. package/lib/utils/composeHooks.js.map +1 -0
  49. package/lib/utils/getAccountTuplesForCurrency.d.ts +8 -0
  50. package/lib/utils/getAccountTuplesForCurrency.d.ts.map +1 -0
  51. package/lib/utils/getAccountTuplesForCurrency.js +19 -0
  52. package/lib/utils/getAccountTuplesForCurrency.js.map +1 -0
  53. package/lib-es/appSupportsQuitApp.d.ts +1 -0
  54. package/lib-es/appSupportsQuitApp.d.ts.map +1 -1
  55. package/lib-es/appSupportsQuitApp.js +1 -0
  56. package/lib-es/appSupportsQuitApp.js.map +1 -1
  57. package/lib-es/appSupportsQuitApp.test.js +6 -1
  58. package/lib-es/appSupportsQuitApp.test.js.map +1 -1
  59. package/lib-es/bridge/generic-alpaca/alpaca/index.d.ts +1 -1
  60. package/lib-es/bridge/generic-alpaca/alpaca/index.d.ts.map +1 -1
  61. package/lib-es/bridge/generic-alpaca/alpaca/index.js.map +1 -1
  62. package/lib-es/bridge/generic-alpaca/alpaca/network/network-alpaca.d.ts +2 -6
  63. package/lib-es/bridge/generic-alpaca/alpaca/network/network-alpaca.d.ts.map +1 -1
  64. package/lib-es/bridge/generic-alpaca/alpaca/network/network-alpaca.js.map +1 -1
  65. package/lib-es/bridge/generic-alpaca/signOperation.js.map +1 -1
  66. package/lib-es/bridge/generic-alpaca/utils.d.ts +2 -2
  67. package/lib-es/bridge/generic-alpaca/utils.d.ts.map +1 -1
  68. package/lib-es/bridge/generic-alpaca/utils.js +1 -1
  69. package/lib-es/bridge/generic-alpaca/utils.js.map +1 -1
  70. package/lib-es/e2e/index.d.ts +1 -0
  71. package/lib-es/e2e/index.d.ts.map +1 -1
  72. package/lib-es/e2e/speculos.d.ts.map +1 -1
  73. package/lib-es/e2e/speculos.js +45 -15
  74. package/lib-es/e2e/speculos.js.map +1 -1
  75. package/lib-es/e2e/speculosCI.d.ts.map +1 -1
  76. package/lib-es/e2e/speculosCI.js +22 -4
  77. package/lib-es/e2e/speculosCI.js.map +1 -1
  78. package/lib-es/env.react.d.ts +1 -1
  79. package/lib-es/env.react.d.ts.map +1 -1
  80. package/lib-es/families/solana/bridge/mock-data.d.ts.map +1 -1
  81. package/lib-es/families/solana/bridge/mock-data.js +46 -316
  82. package/lib-es/families/solana/bridge/mock-data.js.map +1 -1
  83. package/lib-es/families/solana/bridge/mock.js +3 -3
  84. package/lib-es/families/solana/bridge/mock.js.map +1 -1
  85. package/lib-es/modularDrawer/__mocks__/accounts.mock.d.ts +18 -0
  86. package/lib-es/modularDrawer/__mocks__/accounts.mock.d.ts.map +1 -0
  87. package/lib-es/modularDrawer/__mocks__/accounts.mock.js +42 -0
  88. package/lib-es/modularDrawer/__mocks__/accounts.mock.js.map +1 -0
  89. package/lib-es/utils/__tests__/composeHooks.test.d.ts +2 -0
  90. package/lib-es/utils/__tests__/composeHooks.test.d.ts.map +1 -0
  91. package/lib-es/utils/__tests__/composeHooks.test.js +33 -0
  92. package/lib-es/utils/__tests__/composeHooks.test.js.map +1 -0
  93. package/lib-es/utils/__tests__/getAccountTuplesForCurrency.test.d.ts +2 -0
  94. package/lib-es/utils/__tests__/getAccountTuplesForCurrency.test.d.ts.map +1 -0
  95. package/lib-es/utils/__tests__/getAccountTuplesForCurrency.test.js +102 -0
  96. package/lib-es/utils/__tests__/getAccountTuplesForCurrency.test.js.map +1 -0
  97. package/lib-es/utils/composeHooks.d.ts +11 -0
  98. package/lib-es/utils/composeHooks.d.ts.map +1 -0
  99. package/lib-es/utils/composeHooks.js +21 -0
  100. package/lib-es/utils/composeHooks.js.map +1 -0
  101. package/lib-es/utils/getAccountTuplesForCurrency.d.ts +8 -0
  102. package/lib-es/utils/getAccountTuplesForCurrency.d.ts.map +1 -0
  103. package/lib-es/utils/getAccountTuplesForCurrency.js +15 -0
  104. package/lib-es/utils/getAccountTuplesForCurrency.js.map +1 -0
  105. package/package.json +46 -46
  106. package/src/appSupportsQuitApp.test.ts +7 -1
  107. package/src/appSupportsQuitApp.ts +1 -0
  108. package/src/bridge/generic-alpaca/alpaca/index.ts +3 -3
  109. package/src/bridge/generic-alpaca/alpaca/network/network-alpaca.ts +3 -8
  110. package/src/bridge/generic-alpaca/signOperation.ts +1 -1
  111. package/src/bridge/generic-alpaca/utils.ts +3 -10
  112. package/src/e2e/speculos.ts +77 -21
  113. package/src/e2e/speculosCI.ts +32 -10
  114. package/src/families/solana/bridge/mock-data.ts +52 -316
  115. package/src/families/solana/bridge/mock.ts +3 -3
  116. package/src/modularDrawer/__mocks__/accounts.mock.ts +43 -0
  117. package/src/utils/__tests__/composeHooks.test.ts +46 -0
  118. package/src/utils/__tests__/getAccountTuplesForCurrency.test.ts +138 -0
  119. package/src/utils/composeHooks.ts +26 -0
  120. package/src/utils/getAccountTuplesForCurrency.ts +34 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ledgerhq/live-common",
3
3
  "description": "Common ground for the Ledger Live apps",
4
- "version": "34.42.2-nightly.0",
4
+ "version": "34.43.0-nightly.2",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/LedgerHQ/ledger-live.git"
@@ -162,73 +162,73 @@
162
162
  "xstate": "^5.19.2",
163
163
  "yargs": "^17.0.0",
164
164
  "zod": "^3.22.4",
165
- "@ledgerhq/coin-algorand": "^0.9.15-nightly.0",
166
- "@ledgerhq/coin-aptos": "^2.6.1-nightly.0",
167
- "@ledgerhq/coin-bitcoin": "^0.18.1-nightly.0",
168
- "@ledgerhq/coin-cardano": "^0.10.1-nightly.0",
169
- "@ledgerhq/coin-casper": "^2.0.6-nightly.0",
170
- "@ledgerhq/coin-celo": "^1.1.13-nightly.0",
171
- "@ledgerhq/coin-cosmos": "^0.16.4-nightly.0",
172
- "@ledgerhq/coin-evm": "^2.24.1-nightly.0",
173
- "@ledgerhq/coin-filecoin": "^1.9.15-nightly.0",
174
- "@ledgerhq/coin-framework": "^5.7.1-nightly.0",
175
- "@ledgerhq/coin-hedera": "^1.9.1-nightly.0",
176
- "@ledgerhq/coin-icon": "^0.11.2-nightly.0",
177
- "@ledgerhq/coin-internet_computer": "^1.7.15-nightly.0",
178
- "@ledgerhq/coin-mina": "^1.1.14-nightly.0",
179
- "@ledgerhq/coin-multiversx": "^0.4.15-nightly.0",
180
- "@ledgerhq/coin-near": "^0.11.15-nightly.0",
181
- "@ledgerhq/coin-polkadot": "^6.4.1-nightly.0",
182
- "@ledgerhq/coin-solana": "^0.28.1-nightly.0",
183
- "@ledgerhq/coin-stacks": "^0.8.15-nightly.0",
184
- "@ledgerhq/coin-stellar": "^5.3.3-nightly.0",
185
- "@ledgerhq/coin-sui": "^0.7.1-nightly.0",
186
- "@ledgerhq/coin-tezos": "^5.6.2-nightly.0",
187
- "@ledgerhq/coin-ton": "^0.13.4-nightly.0",
188
- "@ledgerhq/coin-tron": "^4.4.3-nightly.0",
189
- "@ledgerhq/coin-vechain": "^2.7.15-nightly.0",
190
- "@ledgerhq/coin-xrp": "^6.4.2-nightly.0",
165
+ "@ledgerhq/coin-algorand": "^0.9.15-nightly.2",
166
+ "@ledgerhq/coin-aptos": "^2.7.0-nightly.2",
167
+ "@ledgerhq/coin-bitcoin": "^0.18.1-nightly.2",
168
+ "@ledgerhq/coin-cardano": "^0.10.1-nightly.2",
169
+ "@ledgerhq/coin-casper": "^2.0.6-nightly.2",
170
+ "@ledgerhq/coin-celo": "^1.1.13-nightly.2",
171
+ "@ledgerhq/coin-cosmos": "^0.16.4-nightly.2",
172
+ "@ledgerhq/coin-filecoin": "^1.9.15-nightly.2",
173
+ "@ledgerhq/coin-evm": "^2.25.0-nightly.2",
174
+ "@ledgerhq/coin-framework": "^5.8.0-nightly.2",
175
+ "@ledgerhq/coin-icon": "^0.11.2-nightly.2",
176
+ "@ledgerhq/coin-hedera": "^1.9.1-nightly.2",
177
+ "@ledgerhq/coin-internet_computer": "^1.7.15-nightly.2",
178
+ "@ledgerhq/coin-mina": "^1.1.14-nightly.2",
179
+ "@ledgerhq/coin-multiversx": "^0.4.15-nightly.2",
180
+ "@ledgerhq/coin-near": "^0.11.15-nightly.2",
181
+ "@ledgerhq/coin-polkadot": "^6.5.0-nightly.2",
182
+ "@ledgerhq/coin-solana": "^0.29.0-nightly.2",
183
+ "@ledgerhq/coin-stacks": "^0.8.15-nightly.2",
184
+ "@ledgerhq/coin-stellar": "^5.4.0-nightly.2",
185
+ "@ledgerhq/coin-tezos": "^5.7.0-nightly.2",
186
+ "@ledgerhq/coin-sui": "^0.8.0-nightly.2",
187
+ "@ledgerhq/coin-ton": "^0.13.4-nightly.2",
188
+ "@ledgerhq/coin-tron": "^4.5.0-nightly.2",
191
189
  "@ledgerhq/crypto-icons-ui": "^1.16.0",
192
- "@ledgerhq/cryptoassets": "^13.22.0",
193
- "@ledgerhq/device-core": "^0.5.4-nightly.0",
190
+ "@ledgerhq/cryptoassets": "^13.23.0-nightly.1",
191
+ "@ledgerhq/coin-xrp": "^6.5.0-nightly.2",
192
+ "@ledgerhq/device-core": "^0.5.4-nightly.1",
194
193
  "@ledgerhq/devices": "8.4.8",
195
194
  "@ledgerhq/errors": "^6.23.0",
195
+ "@ledgerhq/coin-vechain": "^2.8.0-nightly.2",
196
196
  "@ledgerhq/hw-app-algorand": "^6.31.4",
197
197
  "@ledgerhq/hw-app-aptos": "^6.34.4",
198
+ "@ledgerhq/hw-app-celo": "^6.33.11-nightly.1",
198
199
  "@ledgerhq/hw-app-btc": "^10.10.0",
199
- "@ledgerhq/hw-app-celo": "^6.33.11-nightly.0",
200
- "@ledgerhq/hw-app-cosmos": "^6.32.4",
201
- "@ledgerhq/hw-app-eth": "^6.45.13-nightly.0",
202
200
  "@ledgerhq/hw-app-exchange": "^0.10.4",
201
+ "@ledgerhq/hw-app-cosmos": "^6.32.4",
203
202
  "@ledgerhq/hw-app-hedera": "^1.2.4",
204
203
  "@ledgerhq/hw-app-icon": "^1.3.4",
205
204
  "@ledgerhq/hw-app-multiversx": "^6.24.4",
206
205
  "@ledgerhq/hw-app-near": "^6.31.4",
207
206
  "@ledgerhq/hw-app-polkadot": "^6.34.4",
208
207
  "@ledgerhq/hw-app-str": "^7.2.4",
209
- "@ledgerhq/hw-app-sui": "^1.2.0",
210
- "@ledgerhq/hw-app-tezos": "^6.31.4",
211
208
  "@ledgerhq/hw-app-trx": "^6.31.4",
212
- "@ledgerhq/hw-app-vet": "^0.5.10",
209
+ "@ledgerhq/hw-app-tezos": "^6.31.4",
210
+ "@ledgerhq/hw-app-vet": "^0.5.11-nightly.1",
211
+ "@ledgerhq/hw-app-sui": "^1.2.0",
213
212
  "@ledgerhq/hw-app-xrp": "^6.32.2",
214
213
  "@ledgerhq/hw-transport": "^6.31.8",
215
214
  "@ledgerhq/hw-transport-mocker": "^6.29.8",
216
- "@ledgerhq/ledger-cal-service": "^1.2.1",
215
+ "@ledgerhq/ledger-cal-service": "^1.2.2-nightly.0",
217
216
  "@ledgerhq/live-config": "^3.1.0",
218
- "@ledgerhq/live-countervalues": "^0.5.15-nightly.0",
219
- "@ledgerhq/live-countervalues-react": "^0.2.44-nightly.0",
217
+ "@ledgerhq/hw-app-eth": "^6.45.13-nightly.1",
218
+ "@ledgerhq/live-countervalues": "^0.5.15-nightly.2",
219
+ "@ledgerhq/live-countervalues-react": "^0.2.44-nightly.2",
220
220
  "@ledgerhq/live-dmk-shared": "^0.11.1",
221
- "@ledgerhq/live-env": "^2.12.0",
221
+ "@ledgerhq/live-network": "^2.0.14-nightly.0",
222
222
  "@ledgerhq/live-hooks": "0.1.0",
223
- "@ledgerhq/live-network": "^2.0.13",
224
- "@ledgerhq/live-nft": "^0.8.15-nightly.0",
223
+ "@ledgerhq/live-nft": "^0.8.15-nightly.2",
225
224
  "@ledgerhq/live-promise": "^0.1.1",
226
- "@ledgerhq/live-signer-evm": "^0.6.2-nightly.0",
227
- "@ledgerhq/live-signer-solana": "^0.5.0-nightly.0",
228
- "@ledgerhq/live-wallet": "^0.11.6-nightly.0",
225
+ "@ledgerhq/live-env": "^2.13.0-nightly.0",
226
+ "@ledgerhq/live-signer-evm": "^0.6.2-nightly.2",
227
+ "@ledgerhq/live-signer-solana": "^0.5.0-nightly.2",
229
228
  "@ledgerhq/logs": "^6.13.0",
230
- "@ledgerhq/speculos-transport": "^0.2.6",
229
+ "@ledgerhq/live-wallet": "^0.11.6-nightly.2",
231
230
  "@ledgerhq/wallet-api-acre-module": "^0.5.0",
231
+ "@ledgerhq/speculos-transport": "^0.2.7-nightly.0",
232
232
  "@ledgerhq/wallet-api-exchange-module": "^0.14.0"
233
233
  },
234
234
  "devDependencies": {
@@ -277,7 +277,7 @@
277
277
  "undici": "6.19.2",
278
278
  "uuid": "^8.3.2",
279
279
  "ws": "7",
280
- "@ledgerhq/device-react": "^0.2.39-nightly.0",
280
+ "@ledgerhq/device-react": "^0.2.39-nightly.1",
281
281
  "@ledgerhq/types-cryptoassets": "^7.24.0-nightly.0",
282
282
  "@ledgerhq/types-devices": "^6.25.3",
283
283
  "@ledgerhq/types-live": "^6.78.1-nightly.0"
@@ -1,4 +1,5 @@
1
- import appSupportsQuitApp from "./appSupportsQuitApp";
1
+ import appSupportsQuitApp, { minAppVersion } from "./appSupportsQuitApp";
2
+
2
3
  test("appSupportsQuitApp - Apps that are listed fail if version is lt", () => {
3
4
  expect(
4
5
  appSupportsQuitApp({
@@ -26,3 +27,8 @@ test("appSupportsQuitApp - Apps that are not listed pass the test", () => {
26
27
  }),
27
28
  ).toBeTruthy();
28
29
  });
30
+ test("minAppVersion - Sonic and Ethereum have the same min app version", () => {
31
+ expect(minAppVersion["Sonic"]).toBeDefined();
32
+ expect(minAppVersion["Ethereum"]).toBeDefined();
33
+ expect(minAppVersion["Sonic"]).toBe(minAppVersion["Ethereum"]);
34
+ });
@@ -122,6 +122,7 @@ export const minAppVersion = {
122
122
  "SSH/PGP Agent": "0.0.7",
123
123
  Sia: "0.4.2",
124
124
  Solana: "1.0.1",
125
+ Sonic: "1.4.0",
125
126
  Stacks: "0.0.0",
126
127
  Stellar: "3.2.2",
127
128
  Stratis: "1.5.0",
@@ -5,16 +5,16 @@ import { getNetworkAlpacaApi } from "./network/network-alpaca";
5
5
  import { Api } from "@ledgerhq/coin-framework/api/types";
6
6
  import { XrpCoinConfig } from "@ledgerhq/coin-xrp/config";
7
7
 
8
- export function getAlpacaApi(network: string, kind: "local" | "remote"): Api<any, any> {
8
+ export function getAlpacaApi(network: string, kind: "local" | "remote"): Api<any> {
9
9
  if (kind === "local") {
10
10
  switch (network) {
11
11
  case "ripple":
12
12
  case "xrp":
13
13
  return createXrpApi(
14
14
  getCurrencyConfiguration<XrpCoinConfig>(getCryptoCurrencyById("ripple")),
15
- ) as Api<any, any>;
15
+ ) as Api<any>;
16
16
  // as unknown as Api<any>; // FIXME: createXrpApi returns a strongly typed Api<XrpSender>, fix Api<any> to allow it
17
17
  }
18
18
  }
19
- return getNetworkAlpacaApi(network) satisfies Partial<Api<any, any>> as Api<any, any>;
19
+ return getNetworkAlpacaApi(network) satisfies Partial<Api<any>> as Api<any>;
20
20
  }
@@ -107,14 +107,9 @@ const buildValidateIntent = (networkFamily: string) =>
107
107
  return data;
108
108
  };
109
109
 
110
- // FIXME: shouldn't hardcode
111
- type AssetInfo = {
112
- type: "native"; // or "token" if applicable
113
- };
114
-
115
110
  const buildGetBalance = (networkFamily: string) =>
116
- async function getBalance(address: string): Promise<Balance<AssetInfo>[]> {
117
- const { data } = await network<Balance<AssetInfo>, unknown>({
111
+ async function getBalance(address: string): Promise<Balance[]> {
112
+ const { data } = await network<Balance, unknown>({
118
113
  method: "GET",
119
114
  url: `${ALPACA_URL}/${networkFamily}/account/${address}/balance`,
120
115
  });
@@ -191,7 +186,7 @@ export const getNetworkAlpacaApi = (networkFamily: string) =>
191
186
  listOperations: buildListOperations(networkFamily),
192
187
  lastBlock: buildLastBlock(networkFamily),
193
188
  craftTransaction: buildCraftTransaction(networkFamily),
194
- getBlock(_height): Promise<Block<any>> {
189
+ getBlock(_height): Promise<Block> {
195
190
  throw new Error("getBlock is not supported");
196
191
  },
197
192
  getBlockInfo(_height: number): Promise<BlockInfo> {
@@ -40,7 +40,7 @@ export const genericSignOperation =
40
40
  const transactionIntent = transactionToIntent(account, transaction);
41
41
  transactionIntent.senderPublicKey = publicKey;
42
42
  // NOTE: is setting the memo here instead of transactionToIntent sensible?
43
- const txWithMemo = transactionIntent as TransactionIntent<any, MapMemo<string, string>>;
43
+ const txWithMemo = transactionIntent as TransactionIntent<MapMemo<string, string>>;
44
44
  if (transaction["tag"]) {
45
45
  const txMemo = String(transaction["tag"]);
46
46
  txWithMemo.memo = {
@@ -1,17 +1,10 @@
1
1
  import { encodeOperationId } from "@ledgerhq/coin-framework/operation";
2
2
  import { Account, Operation, OperationType, TransactionCommon } from "@ledgerhq/types-live";
3
- import {
4
- Operation as CoreOperation,
5
- Asset,
6
- TransactionIntent,
7
- } from "@ledgerhq/coin-framework/api/types";
3
+ import { Operation as CoreOperation, TransactionIntent } from "@ledgerhq/coin-framework/api/types";
8
4
  import BigNumber from "bignumber.js";
9
5
  import { fromBigNumberToBigInt } from "@ledgerhq/coin-framework/utils";
10
6
 
11
- export function adaptCoreOperationToLiveOperation(
12
- accountId: string,
13
- op: CoreOperation<Asset>,
14
- ): Operation {
7
+ export function adaptCoreOperationToLiveOperation(accountId: string, op: CoreOperation): Operation {
15
8
  return {
16
9
  id: encodeOperationId(accountId, op.tx.hash, op.type),
17
10
  hash: op.tx.hash,
@@ -38,7 +31,7 @@ export function transactionToIntent(
38
31
  sender: account.freshAddress,
39
32
  recipient: transaction.recipient,
40
33
  amount: fromBigNumberToBigInt(transaction.amount, BigInt(0)),
41
- asset: null,
34
+ asset: { type: "native" },
42
35
  };
43
36
  }
44
37
 
@@ -11,7 +11,7 @@ import { createSpeculosDeviceCI, releaseSpeculosDeviceCI } from "./speculosCI";
11
11
  import type { AppCandidate } from "@ledgerhq/coin-framework/bot/types";
12
12
  import { DeviceModelId } from "@ledgerhq/devices";
13
13
  import { CryptoCurrency } from "@ledgerhq/types-cryptoassets";
14
- import axios from "axios";
14
+ import axios, { AxiosError, AxiosResponse } from "axios";
15
15
  import { getEnv } from "@ledgerhq/live-env";
16
16
  import { getCryptoCurrencyById } from "../currencies";
17
17
  import { DeviceLabels } from "../e2e/enum/DeviceLabels";
@@ -415,13 +415,59 @@ interface ResponseData {
415
415
  events: Event[];
416
416
  }
417
417
 
418
+ function getSpeculosAddress(): string {
419
+ const speculosAddress = process.env.SPECULOS_ADDRESS;
420
+ return speculosAddress || "http://127.0.0.1";
421
+ }
422
+
423
+ async function retryAxiosRequest<T>(
424
+ requestFn: () => Promise<AxiosResponse<T>>,
425
+ maxRetries: number = 5,
426
+ baseDelay: number = 1000,
427
+ retryableStatusCodes: number[] = [500, 502, 503, 504],
428
+ ): Promise<AxiosResponse<T>> {
429
+ let lastError: AxiosError | Error;
430
+
431
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
432
+ try {
433
+ return await requestFn();
434
+ } catch (error) {
435
+ lastError = error as AxiosError | Error;
436
+
437
+ const isRetryable =
438
+ axios.isAxiosError(error) &&
439
+ error.response &&
440
+ retryableStatusCodes.includes(error.response.status);
441
+
442
+ const isNetworkError = axios.isAxiosError(error) && !error.response;
443
+
444
+ if ((isRetryable || isNetworkError) && attempt < maxRetries) {
445
+ const delay = baseDelay * (attempt + 1);
446
+ console.warn(
447
+ `Axios request failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms...`,
448
+ {
449
+ status: axios.isAxiosError(error) ? error.response?.status : "network error",
450
+ message: error.message,
451
+ },
452
+ );
453
+ await new Promise(resolve => setTimeout(resolve, delay));
454
+ continue;
455
+ }
456
+
457
+ throw lastError;
458
+ }
459
+ }
460
+
461
+ throw lastError!;
462
+ }
463
+
418
464
  export async function waitFor(text: string, maxAttempts = 15): Promise<string[]> {
419
465
  const port = getEnv("SPECULOS_API_PORT");
420
- const address = process.env.SPECULOS_ADDRESS || "http://127.0.0.1";
466
+ const address = getSpeculosAddress();
421
467
  const url = `${address}:${port}/events?stream=false&currentscreenonly=true`;
422
468
 
423
469
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
424
- const { data } = await axios.get<ResponseData>(url);
470
+ const { data } = await retryAxiosRequest(() => axios.get<ResponseData>(url));
425
471
  const texts = data.events.map(event => event.text);
426
472
 
427
473
  if (texts?.some(t => t?.toLowerCase().includes(text.toLowerCase()))) {
@@ -436,10 +482,12 @@ export async function waitFor(text: string, maxAttempts = 15): Promise<string[]>
436
482
 
437
483
  export async function pressBoth() {
438
484
  const speculosApiPort = getEnv("SPECULOS_API_PORT");
439
- const speculosAddress = process.env.SPECULOS_ADDRESS || "http://127.0.0.1";
440
- await axios.post(`${speculosAddress}:${speculosApiPort}/button/both`, {
441
- action: "press-and-release",
442
- });
485
+ const speculosAddress = getSpeculosAddress();
486
+ await retryAxiosRequest(() =>
487
+ axios.post(`${speculosAddress}:${speculosApiPort}/button/both`, {
488
+ action: "press-and-release",
489
+ }),
490
+ );
443
491
  }
444
492
 
445
493
  export async function pressUntilTextFound(
@@ -465,27 +513,33 @@ export async function pressUntilTextFound(
465
513
  }
466
514
 
467
515
  async function fetchCurrentScreenTexts(speculosApiPort: number): Promise<string> {
468
- const speculosAddress = process.env.SPECULOS_ADDRESS || "http://127.0.0.1";
469
- const response = await axios.get<ResponseData>(
470
- `${speculosAddress}:${speculosApiPort}/events?stream=false&currentscreenonly=true`,
516
+ const speculosAddress = getSpeculosAddress();
517
+ const response = await retryAxiosRequest(() =>
518
+ axios.get<ResponseData>(
519
+ `${speculosAddress}:${speculosApiPort}/events?stream=false&currentscreenonly=true`,
520
+ ),
471
521
  );
472
522
  return response.data.events.map(event => event.text).join("");
473
523
  }
474
524
 
475
525
  async function fetchAllEvents(speculosApiPort: number): Promise<string[]> {
476
- const speculosAddress = process.env.SPECULOS_ADDRESS || "http://127.0.0.1";
477
- const response = await axios.get<ResponseData>(
478
- `${speculosAddress}:${speculosApiPort}/events?stream=false&currentscreenonly=false`,
526
+ const speculosAddress = getSpeculosAddress();
527
+ const response = await retryAxiosRequest(() =>
528
+ axios.get<ResponseData>(
529
+ `${speculosAddress}:${speculosApiPort}/events?stream=false&currentscreenonly=false`,
530
+ ),
479
531
  );
480
532
  return response.data.events.map(event => event.text);
481
533
  }
482
534
 
483
535
  export async function pressRightButton(): Promise<void> {
484
536
  const speculosApiPort = getEnv("SPECULOS_API_PORT");
485
- const speculosAddress = process.env.SPECULOS_ADDRESS || "http://127.0.0.1";
486
- await axios.post(`${speculosAddress}:${speculosApiPort}/button/right`, {
487
- action: "press-and-release",
488
- });
537
+ const speculosAddress = getSpeculosAddress();
538
+ await retryAxiosRequest(() =>
539
+ axios.post(`${speculosAddress}:${speculosApiPort}/button/right`, {
540
+ action: "press-and-release",
541
+ }),
542
+ );
489
543
  }
490
544
 
491
545
  export function containsSubstringInEvent(targetString: string, events: string[]): boolean {
@@ -503,12 +557,14 @@ export function containsSubstringInEvent(targetString: string, events: string[])
503
557
  }
504
558
 
505
559
  export async function takeScreenshot(port?: number): Promise<Buffer | undefined> {
506
- const speculosAddress = process.env.SPECULOS_ADDRESS || "http://127.0.0.1";
560
+ const speculosAddress = getSpeculosAddress();
507
561
  const speculosApiPort = port ?? getEnv("SPECULOS_API_PORT");
508
562
  try {
509
- const response = await axios.get(`${speculosAddress}:${speculosApiPort}/screenshot`, {
510
- responseType: "arraybuffer",
511
- });
563
+ const response = await retryAxiosRequest(() =>
564
+ axios.get(`${speculosAddress}:${speculosApiPort}/screenshot`, {
565
+ responseType: "arraybuffer",
566
+ }),
567
+ );
512
568
  return response.data;
513
569
  } catch (error) {
514
570
  console.error("Error downloading speculos screenshot:", error);
@@ -61,22 +61,44 @@ async function githubApiRequest<T = unknown>({
61
61
  function waitForSpeculosReady(url: string, { interval = 2000, timeout = 300_000 } = {}) {
62
62
  return new Promise((resolve, reject) => {
63
63
  const startTime = Date.now();
64
+ let currentRequest: ReturnType<typeof https.get> | null = null;
65
+
66
+ function cleanup() {
67
+ if (currentRequest) {
68
+ currentRequest.destroy();
69
+ currentRequest = null;
70
+ }
71
+ }
64
72
 
65
73
  function check() {
66
- https
67
- .get(url, res => {
68
- if (res.statusCode && res.statusCode >= 200 && res.statusCode < 400) {
69
- process.env.SPECULOS_ADDRESS = url;
70
- resolve(true);
71
- } else {
72
- retry();
73
- }
74
- })
75
- .on("error", retry);
74
+ cleanup();
75
+
76
+ currentRequest = https.get(url, { timeout: 10000 }, res => {
77
+ if (res.statusCode && res.statusCode >= 200 && res.statusCode < 400) {
78
+ process.env.SPECULOS_ADDRESS = url;
79
+ cleanup();
80
+ console.warn(`Speculos is ready at ${url}`);
81
+ resolve(true);
82
+ } else {
83
+ console.warn(`Speculos not ready yet, status: ${res.statusCode}`);
84
+ retry();
85
+ }
86
+ });
87
+
88
+ currentRequest.on("error", error => {
89
+ console.error(`Request error: ${error.message}`);
90
+ retry();
91
+ });
92
+
93
+ currentRequest.on("timeout", () => {
94
+ console.error("Request timeout");
95
+ retry();
96
+ });
76
97
  }
77
98
 
78
99
  function retry() {
79
100
  if (Date.now() - startTime >= timeout) {
101
+ cleanup();
80
102
  reject(new Error(`Timeout: ${url} did not become available within ${timeout}ms`));
81
103
  } else {
82
104
  setTimeout(check, interval);