@ledgerhq/live-common 21.21.0 → 21.21.4

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 (55) hide show
  1. package/lib/api/Ethereum.d.ts.map +1 -1
  2. package/lib/api/Ethereum.js +2 -1
  3. package/lib/api/Ethereum.js.map +1 -1
  4. package/lib/apps/support.js +1 -1
  5. package/lib/bot/engine.d.ts.map +1 -1
  6. package/lib/bot/engine.js +2 -1
  7. package/lib/bot/engine.js.map +1 -1
  8. package/lib/bot/formatters.d.ts +1 -0
  9. package/lib/bot/formatters.d.ts.map +1 -1
  10. package/lib/bot/formatters.js +13 -2
  11. package/lib/bot/formatters.js.map +1 -1
  12. package/lib/bot/index.js +4 -4
  13. package/lib/bot/index.js.map +1 -1
  14. package/lib/env.d.ts.map +1 -1
  15. package/lib/env.js +1 -2
  16. package/lib/env.js.map +1 -1
  17. package/lib/families/ethereum/signOperation.d.ts.map +1 -1
  18. package/lib/families/ethereum/signOperation.js +2 -2
  19. package/lib/families/ethereum/signOperation.js.map +1 -1
  20. package/lib/families/polkadot/api/sidecar.types.d.ts +2 -1
  21. package/lib/families/polkadot/api/sidecar.types.d.ts.map +1 -1
  22. package/lib/families/solana/errors.d.ts +0 -3
  23. package/lib/families/solana/errors.d.ts.map +1 -1
  24. package/lib/families/solana/errors.js +1 -2
  25. package/lib/families/solana/errors.js.map +1 -1
  26. package/lib/families/solana/js-getTransactionStatus.d.ts +1 -1
  27. package/lib/families/solana/js-getTransactionStatus.d.ts.map +1 -1
  28. package/lib/families/solana/js-getTransactionStatus.js +51 -12
  29. package/lib/families/solana/js-getTransactionStatus.js.map +1 -1
  30. package/lib/families/solana/js-prepareTransaction.d.ts.map +1 -1
  31. package/lib/families/solana/js-prepareTransaction.js +8 -4
  32. package/lib/families/solana/js-prepareTransaction.js.map +1 -1
  33. package/lib/families/solana/js-signOperation.d.ts.map +1 -1
  34. package/lib/families/solana/js-signOperation.js +19 -9
  35. package/lib/families/solana/js-signOperation.js.map +1 -1
  36. package/lib/families/solana/js-synchronization.js +9 -6
  37. package/lib/families/solana/js-synchronization.js.map +1 -1
  38. package/lib/families/solana/test-dataset.js +6 -6
  39. package/lib/families/solana/test-dataset.js.map +1 -1
  40. package/package.json +16 -16
  41. package/src/__tests__/__snapshots__/all.libcore.ts.snap +9 -13
  42. package/src/api/Ethereum.ts +3 -1
  43. package/src/apps/support.ts +1 -1
  44. package/src/bot/engine.ts +3 -1
  45. package/src/bot/formatters.ts +11 -1
  46. package/src/bot/index.ts +5 -5
  47. package/src/env.ts +1 -2
  48. package/src/families/ethereum/signOperation.ts +2 -2
  49. package/src/families/polkadot/api/sidecar.types.ts +3 -1
  50. package/src/families/solana/errors.ts +0 -4
  51. package/src/families/solana/js-getTransactionStatus.ts +65 -12
  52. package/src/families/solana/js-prepareTransaction.ts +7 -4
  53. package/src/families/solana/js-signOperation.ts +21 -9
  54. package/src/families/solana/js-synchronization.ts +11 -6
  55. package/src/families/solana/test-dataset.ts +6 -6
@@ -210,7 +210,9 @@ export const apiForCurrency = (currency: CryptoCurrency): API => {
210
210
  async getNFTMetadata(input) {
211
211
  const { data }: { data: NFTMetadataResponse[] } = await network({
212
212
  method: "POST",
213
- url: getEnv("NFT_ETH_METADATA_SERVICE"),
213
+ url:
214
+ getEnv("NFT_ETH_METADATA_SERVICE") +
215
+ "/v1/ethereum/1/contracts/tokens/infos",
214
216
  data: input,
215
217
  });
216
218
 
@@ -29,7 +29,7 @@ export function shouldUpgrade(
29
29
  const appVersionsRequired = {
30
30
  Cosmos: ">= 2.14",
31
31
  Algorand: ">= 1.2.9",
32
- Polkadot: ">= 8.9100.0",
32
+ Polkadot: ">= 9.9140.0",
33
33
  Elrond: ">= 1.0.11",
34
34
  };
35
35
  export function mustUpgrade(
package/src/bot/engine.ts CHANGED
@@ -39,6 +39,7 @@ import {
39
39
  formatReportForConsole,
40
40
  formatTime,
41
41
  formatAppCandidate,
42
+ formatError,
42
43
  } from "./formatters";
43
44
  import type {
44
45
  AppSpec,
@@ -510,7 +511,8 @@ export async function runOnAccount<T extends Transaction>({
510
511
  `spec ${spec.name}/${account.name}/${optimisticOperation.hash} confirmed`
511
512
  );
512
513
  } catch (error: any) {
513
- log("mutation-error", spec.name + ": " + String(error));
514
+ console.error(error);
515
+ log("mutation-error", spec.name + ": " + formatError(error));
514
516
  report.error = error;
515
517
  }
516
518
 
@@ -18,6 +18,16 @@ const formatDt = (from, to) => (from && to ? formatTime(to - from) : "?");
18
18
  export function formatAppCandidate(appCandidate: AppCandidate) {
19
19
  return `${appCandidate.appName} ${appCandidate.appVersion} on ${appCandidate.model} ${appCandidate.firmware}`;
20
20
  }
21
+
22
+ export function formatError(e: any) {
23
+ if (!e || typeof e !== "object" || e instanceof Error) return String(e);
24
+ try {
25
+ return "raw object: " + JSON.stringify(e).slice(0, 400);
26
+ } catch (_e) {
27
+ return String(e);
28
+ }
29
+ }
30
+
21
31
  export function formatReportForConsole<T extends Transaction>({
22
32
  syncAllAccountsTime,
23
33
  appCandidate,
@@ -137,7 +147,7 @@ export function formatReportForConsole<T extends Transaction>({
137
147
  }
138
148
 
139
149
  if (error) {
140
- str += `⚠️ ${String(error)}\n`;
150
+ str += `⚠️ ${formatError(error)}\n`;
141
151
  }
142
152
 
143
153
  return str;
package/src/bot/index.ts CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  } from "../currencies";
19
19
  import { isAccountEmpty } from "../account";
20
20
  import { runWithAppSpec } from "./engine";
21
- import { formatReportForConsole } from "./formatters";
21
+ import { formatReportForConsole, formatError } from "./formatters";
22
22
  import {
23
23
  initialState,
24
24
  calculate,
@@ -230,7 +230,7 @@ export async function bot({ currency, family, mutation }: Arg = {}) {
230
230
  let subtitle = "";
231
231
 
232
232
  if (countervaluesError) {
233
- subtitle += `> ${String(countervaluesError)}`;
233
+ subtitle += `> ${formatError(countervaluesError)}`;
234
234
  }
235
235
 
236
236
  let slackBody = "";
@@ -254,8 +254,8 @@ export async function bot({ currency, family, mutation }: Arg = {}) {
254
254
  body += `<summary>${specFatals.length} critical spec errors</summary>\n\n`;
255
255
  specFatals.forEach(({ spec, fatalError }) => {
256
256
  body += `**Spec ${spec.name} failed!**\n`;
257
- body += "```\n" + String(fatalError) + "\n```\n\n";
258
- slackBody += `❌ *Spec ${spec.name}*: \`${String(fatalError)}\`\n`;
257
+ body += "```\n" + formatError(fatalError) + "\n```\n\n";
258
+ slackBody += `❌ *Spec ${spec.name}*: \`${formatError(fatalError)}\`\n`;
259
259
  });
260
260
  body += "</details>\n\n";
261
261
  }
@@ -268,7 +268,7 @@ export async function bot({ currency, family, mutation }: Arg = {}) {
268
268
  "```\n" +
269
269
  formatReportForConsole(c) +
270
270
  "\n" +
271
- String(c.error) +
271
+ formatError(c.error) +
272
272
  "\n```\n\n";
273
273
  });
274
274
  body += "</details>\n\n";
package/src/env.ts CHANGED
@@ -387,8 +387,7 @@ const envDefinitions = {
387
387
  desc: "set the currencies where NFT is active",
388
388
  },
389
389
  NFT_ETH_METADATA_SERVICE: {
390
- // FIXME LL-8001
391
- def: "https://nft.staging.aws.ledger.fr/v1/ethereum/1/contracts/tokens/infos",
390
+ def: "https://nft.api.live.ledger.com",
392
391
  parser: stringParser,
393
392
  desc: "service uri used to get the metadata of an nft",
394
393
  },
@@ -14,6 +14,7 @@ import { apiForCurrency } from "../../api/Ethereum";
14
14
  import { withDevice } from "../../hw/deviceAccess";
15
15
  import { modes } from "./modules";
16
16
  import { isNFTActive } from "../../nft";
17
+ import { getEnv } from "../../env";
17
18
  export const signOperation = ({
18
19
  account,
19
20
  deviceId,
@@ -63,9 +64,8 @@ export const signOperation = ({
63
64
  const eth = new Eth(transport);
64
65
  if (isNFTActive(account.currency)) {
65
66
  eth.setLoadConfig({
66
- // FIXME drop this after LL-8001
67
67
  nftExplorerBaseURL:
68
- "https://nft.staging.aws.ledger.fr/v1/ethereum",
68
+ getEnv("NFT_ETH_METADATA_SERVICE") + "/v1/ethereum",
69
69
  });
70
70
  }
71
71
  // FIXME this part is still required for compound to correctly display info on the device
@@ -1,3 +1,5 @@
1
+ import type { HexString } from "@polkadot/util/types";
2
+
1
3
  interface IAt {
2
4
  hash: string;
3
5
  height: string;
@@ -137,7 +139,7 @@ export interface SidecarTransactionMaterial {
137
139
  specName: string;
138
140
  specVersion: string;
139
141
  txVersion: string;
140
- metadata?: string;
142
+ metadata?: HexString;
141
143
  }
142
144
  export interface SidecarTransactionBroadcast {
143
145
  hash: string;
@@ -4,10 +4,6 @@ export const SolanaAccountNotFunded = createCustomErrorClass(
4
4
  "SolanaAccountNotFunded"
5
5
  );
6
6
 
7
- export const SolanaNotEnoughBalanceToPayFees = createCustomErrorClass(
8
- "SolanaNotEnoughBalanceToPayFees"
9
- );
10
-
11
7
  export const SolanaRecipientAssociatedTokenAccountWillBeFunded =
12
8
  createCustomErrorClass("SolanaAssociatedTokenAccountWillBeFunded");
13
9
 
@@ -1,10 +1,10 @@
1
1
  import { BigNumber } from "bignumber.js";
2
2
  import type { Account, TransactionStatus } from "../../types";
3
- import type { Command, Transaction } from "./types";
3
+ import type { Command, Transaction, TransactionModel } from "./types";
4
4
  import { assertUnreachable } from "./utils";
5
5
 
6
6
  const getTransactionStatus = async (
7
- _: Account,
7
+ account: Account,
8
8
  tx: Transaction
9
9
  ): Promise<TransactionStatus> => {
10
10
  const txFees = new BigNumber(tx.feeCalculator?.lamportsPerSignature ?? 0);
@@ -12,28 +12,37 @@ const getTransactionStatus = async (
12
12
  const { commandDescriptor } = tx.model;
13
13
 
14
14
  if (commandDescriptor === undefined) {
15
+ const amount = getAmountForModel(account, tx, txFees);
16
+ const totalSpent = getTotalSpentForModel(tx.model, amount, txFees);
15
17
  return {
16
- amount: new BigNumber(tx.amount),
18
+ amount,
17
19
  errors: {},
18
20
  warnings: {},
19
21
  estimatedFees: txFees,
20
- totalSpent: new BigNumber(tx.amount),
22
+ totalSpent,
21
23
  };
22
24
  }
23
25
  switch (commandDescriptor.status) {
24
- case "invalid":
26
+ case "invalid": {
27
+ const amount = getAmountForModel(account, tx, txFees);
28
+ const totalSpent = getTotalSpentForModel(tx.model, amount, txFees);
25
29
  return {
26
- amount: new BigNumber(tx.amount),
30
+ amount,
27
31
  errors: commandDescriptor.errors,
28
32
  warnings: commandDescriptor.warnings ?? {},
29
33
  estimatedFees: txFees,
30
- totalSpent: new BigNumber(0),
34
+ totalSpent,
31
35
  };
36
+ }
32
37
  case "valid": {
33
38
  const { command } = commandDescriptor;
34
39
  const estimatedFees = txFees.plus(commandDescriptor.fees ?? 0);
35
- const amount = getAmount(tx, command);
36
- const totalSpent = getTotalSpent(command, amount, estimatedFees);
40
+ const amount = getAmountForCommand(command);
41
+ const totalSpent = getTotalSpentForCommand(
42
+ command,
43
+ amount,
44
+ estimatedFees
45
+ );
37
46
 
38
47
  return {
39
48
  amount,
@@ -48,17 +57,61 @@ const getTransactionStatus = async (
48
57
  }
49
58
  };
50
59
 
51
- function getAmount(tx: Transaction, command: Command) {
60
+ function getAmountForModel(
61
+ account: Account,
62
+ tx: Transaction,
63
+ estimatedFees: BigNumber
64
+ ) {
65
+ const { model } = tx;
66
+ switch (model.kind) {
67
+ case "transfer": {
68
+ if (tx.amount.lte(0)) {
69
+ return tx.amount;
70
+ }
71
+ const amount = tx.useAllAmount
72
+ ? account.balance.minus(estimatedFees)
73
+ : BigNumber.max(tx.amount, 0);
74
+ return amount.gte(0) ? amount : account.balance;
75
+ }
76
+ case "token.transfer":
77
+ return tx.useAllAmount ? account.balance : tx.amount;
78
+ case "token.createATA":
79
+ return new BigNumber(0);
80
+ default:
81
+ return assertUnreachable(model);
82
+ }
83
+ }
84
+
85
+ function getTotalSpentForModel(
86
+ model: TransactionModel,
87
+ amount: BigNumber,
88
+ estimatedFees: BigNumber
89
+ ) {
90
+ switch (model.kind) {
91
+ case "transfer":
92
+ return amount.plus(estimatedFees);
93
+ case "token.transfer":
94
+ return amount;
95
+ case "token.createATA":
96
+ return estimatedFees;
97
+ default:
98
+ return assertUnreachable(model);
99
+ }
100
+ }
101
+
102
+ function getAmountForCommand(command: Command) {
52
103
  switch (command.kind) {
53
104
  case "transfer":
54
105
  case "token.transfer":
55
106
  return new BigNumber(command.amount);
107
+ case "token.createATA":
108
+ return new BigNumber(0);
56
109
  default:
57
- return tx.amount;
110
+ return assertUnreachable(command);
58
111
  }
59
112
  }
60
113
 
61
- function getTotalSpent(
114
+ function getTotalSpentForCommand(
62
115
  command: Command,
63
116
  amount: BigNumber,
64
117
  estimatedFees: BigNumber
@@ -17,7 +17,6 @@ import {
17
17
  SolanaMemoIsTooLong,
18
18
  SolanaTokenAccountHoldsAnotherToken,
19
19
  SolanaRecipientAssociatedTokenAccountWillBeFunded,
20
- SolanaNotEnoughBalanceToPayFees,
21
20
  SolanaTokenRecipientIsSenderATA,
22
21
  SolanaTokenAccounNotInitialized,
23
22
  } from "./errors";
@@ -64,11 +63,13 @@ async function deriveCommandDescriptor(
64
63
  }
65
64
 
66
65
  if (model.uiState.memo) {
67
- const encoder = new TextEncoder();
68
- if (encoder.encode(model.uiState.memo).byteLength > MAX_MEMO_LENGTH) {
66
+ const memoBytes = Buffer.from(model.uiState.memo, "utf-8");
67
+ if (memoBytes.byteLength > MAX_MEMO_LENGTH) {
69
68
  errors.memo = errors.memo = new SolanaMemoIsTooLong(undefined, {
70
69
  maxLength: MAX_MEMO_LENGTH,
71
70
  });
71
+ // LLM expects <transaction> as error key to disable continue button
72
+ errors.transaction = errors.memo;
72
73
  }
73
74
  }
74
75
 
@@ -102,6 +103,8 @@ const prepareTransaction = async (
102
103
 
103
104
  if (tx.feeCalculator === undefined) {
104
105
  patch.feeCalculator = feeCalculator;
106
+ // LLM requires this field to be truthy to show fees
107
+ (patch as any).networkInfo = true;
105
108
  }
106
109
 
107
110
  const txToDeriveFrom = {
@@ -137,7 +140,7 @@ const prepareTransaction = async (
137
140
  const totalFees =
138
141
  feeCalculator.lamportsPerSignature + (commandDescriptor.fees ?? 0);
139
142
  if (mainAccount.balance.lt(totalFees)) {
140
- errors.amount = new SolanaNotEnoughBalanceToPayFees();
143
+ errors.amount = new NotEnoughBalance();
141
144
  }
142
145
  }
143
146
  }
@@ -7,6 +7,7 @@ import type {
7
7
  } from "../../types";
8
8
  import { open, close } from "../../hw";
9
9
  import type {
10
+ Command,
10
11
  TokenCreateATACommand,
11
12
  TokenTransferCommand,
12
13
  Transaction,
@@ -147,9 +148,7 @@ function optimisticOpForTransfer(
147
148
  senders: [account.freshAddress],
148
149
  recipients: [transaction.recipient],
149
150
  value: new BigNumber(command.amount).plus(commons.fee),
150
- extra: {
151
- memo: command.memo,
152
- },
151
+ extra: getOpExtras(command),
153
152
  };
154
153
  }
155
154
 
@@ -170,9 +169,7 @@ function optimisticOpForTokenTransfer(
170
169
  senders: [account.freshAddress],
171
170
  recipients: [transaction.recipient],
172
171
  value: new BigNumber(command.amount),
173
- extra: {
174
- memo: command.memo,
175
- },
172
+ extra: getOpExtras(command),
176
173
  subOperations: [
177
174
  {
178
175
  ...optimisticOpcommons(transaction, commandDescriptor),
@@ -182,9 +179,7 @@ function optimisticOpForTokenTransfer(
182
179
  senders: [account.freshAddress],
183
180
  recipients: [transaction.recipient],
184
181
  value: new BigNumber(command.amount),
185
- extra: {
186
- memo: command.memo,
187
- },
182
+ extra: getOpExtras(command),
188
183
  },
189
184
  ],
190
185
  };
@@ -228,3 +223,20 @@ function optimisticOpcommons(
228
223
  extra: {},
229
224
  };
230
225
  }
226
+
227
+ function getOpExtras(command: Command): Record<string, any> {
228
+ const extra: Record<string, any> = {};
229
+ switch (command.kind) {
230
+ case "transfer":
231
+ case "token.transfer":
232
+ if (command.memo !== undefined) {
233
+ extra.memo = command.memo;
234
+ }
235
+ break;
236
+ case "token.createATA":
237
+ break;
238
+ default:
239
+ return assertUnreachable(command);
240
+ }
241
+ return extra;
242
+ }
@@ -299,9 +299,7 @@ function txToMainAccOperation(
299
299
  hasFailed: !!tx.info.err,
300
300
  blockHeight: tx.info.slot,
301
301
  blockHash: message.recentBlockhash,
302
- extra: {
303
- memo: tx.info.memo ? dropMemoLengthPrefixIfAny(tx.info.memo) : undefined,
304
- },
302
+ extra: getOpExtra(tx),
305
303
  type: opType,
306
304
  senders,
307
305
  recipients,
@@ -311,6 +309,15 @@ function txToMainAccOperation(
311
309
  };
312
310
  }
313
311
 
312
+ function getOpExtra(tx: TransactionDescriptor): Record<string, any> {
313
+ const extra: Record<string, any> = {};
314
+ if (tx.info.memo !== null) {
315
+ extra.memo = dropMemoLengthPrefixIfAny(tx.info.memo);
316
+ }
317
+
318
+ return extra;
319
+ }
320
+
314
321
  function txToTokenAccOperation(
315
322
  tx: TransactionDescriptor,
316
323
  assocTokenAcc: OnChainTokenAccount,
@@ -364,9 +371,7 @@ function txToTokenAccOperation(
364
371
  senders,
365
372
  value: delta.abs(),
366
373
  hasFailed: !!tx.info.err,
367
- extra: {
368
- memo: tx.info.memo ? dropMemoLengthPrefixIfAny(tx.info.memo) : undefined,
369
- },
374
+ extra: getOpExtra(tx),
370
375
  blockHash: tx.parsed.transaction.message.recentBlockhash,
371
376
  };
372
377
  }
@@ -110,7 +110,7 @@ const dataset: DatasetTest<Transaction> = {
110
110
  warnings: {},
111
111
  estimatedFees: fees(1),
112
112
  amount: zero,
113
- totalSpent: zero,
113
+ totalSpent: fees(1),
114
114
  },
115
115
  },
116
116
  {
@@ -175,7 +175,7 @@ const dataset: DatasetTest<Transaction> = {
175
175
  warnings: {},
176
176
  estimatedFees: fees(1),
177
177
  amount: testOnChainData.fundedSenderBalance,
178
- totalSpent: zero,
178
+ totalSpent: testOnChainData.fundedSenderBalance.plus(fees(1)),
179
179
  },
180
180
  },
181
181
  {
@@ -204,7 +204,7 @@ const dataset: DatasetTest<Transaction> = {
204
204
  warnings: {},
205
205
  estimatedFees: testOnChainData.fundedSenderBalance.plus(1),
206
206
  amount: zero,
207
- totalSpent: zero,
207
+ totalSpent: testOnChainData.fundedSenderBalance.plus(1),
208
208
  },
209
209
  },
210
210
  {
@@ -226,7 +226,7 @@ const dataset: DatasetTest<Transaction> = {
226
226
  warnings: {},
227
227
  estimatedFees: fees(1),
228
228
  amount: zero,
229
- totalSpent: zero,
229
+ totalSpent: fees(1),
230
230
  },
231
231
  },
232
232
  {
@@ -248,7 +248,7 @@ const dataset: DatasetTest<Transaction> = {
248
248
  warnings: {},
249
249
  estimatedFees: fees(1),
250
250
  amount: new BigNumber(-1),
251
- totalSpent: zero,
251
+ totalSpent: new BigNumber(-1).plus(fees(1)),
252
252
  },
253
253
  },
254
254
  {
@@ -497,7 +497,7 @@ function recipientRequired(): TransactionTestSpec[] {
497
497
  warnings: {},
498
498
  estimatedFees: fees(1),
499
499
  amount: zero,
500
- totalSpent: zero,
500
+ totalSpent: model.kind === "transfer" ? fees(1) : zero,
501
501
  },
502
502
  };
503
503
  });