@ledgerhq/live-common 21.23.0 → 21.25.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 (117) hide show
  1. package/lib/data/icons/react/index.d.ts +1 -0
  2. package/lib/data/icons/react/index.d.ts.map +1 -1
  3. package/lib/data/icons/react/index.js +4 -2
  4. package/lib/data/icons/react/index.js.map +1 -1
  5. package/lib/data/icons/react/ssv.d.ts +8 -0
  6. package/lib/data/icons/react/ssv.d.ts.map +1 -0
  7. package/lib/data/icons/react/ssv.js +31 -0
  8. package/lib/data/icons/react/ssv.js.map +1 -0
  9. package/lib/data/icons/reactNative/index.d.ts +1 -0
  10. package/lib/data/icons/reactNative/index.d.ts.map +1 -1
  11. package/lib/data/icons/reactNative/index.js +4 -2
  12. package/lib/data/icons/reactNative/index.js.map +1 -1
  13. package/lib/data/icons/reactNative/ssv.d.ts +8 -0
  14. package/lib/data/icons/reactNative/ssv.d.ts.map +1 -0
  15. package/lib/data/icons/reactNative/ssv.js +32 -0
  16. package/lib/data/icons/reactNative/ssv.js.map +1 -0
  17. package/lib/env.d.ts +10 -5
  18. package/lib/env.d.ts.map +1 -1
  19. package/lib/env.js +5 -0
  20. package/lib/env.js.map +1 -1
  21. package/lib/env.react.d.ts +1 -1
  22. package/lib/env.react.d.ts.map +1 -1
  23. package/lib/families/bitcoin/js-synchronisation.d.ts.map +1 -1
  24. package/lib/families/bitcoin/js-synchronisation.js +10 -12
  25. package/lib/families/bitcoin/js-synchronisation.js.map +1 -1
  26. package/lib/families/bitcoin/wallet-btc/crypto/base.d.ts.map +1 -1
  27. package/lib/families/bitcoin/wallet-btc/crypto/base.js +30 -22
  28. package/lib/families/bitcoin/wallet-btc/crypto/base.js.map +1 -1
  29. package/lib/families/bitcoin/wallet-btc/crypto/bip32.d.ts +11 -0
  30. package/lib/families/bitcoin/wallet-btc/crypto/bip32.d.ts.map +1 -0
  31. package/lib/families/bitcoin/wallet-btc/crypto/bip32.js +32 -0
  32. package/lib/families/bitcoin/wallet-btc/crypto/bip32.js.map +1 -0
  33. package/lib/families/bitcoin/wallet-btc/crypto/decred.d.ts.map +1 -1
  34. package/lib/families/bitcoin/wallet-btc/crypto/decred.js +3 -114
  35. package/lib/families/bitcoin/wallet-btc/crypto/decred.js.map +1 -1
  36. package/lib/families/bitcoin/wallet-btc/storage/index.d.ts +24 -8
  37. package/lib/families/bitcoin/wallet-btc/storage/index.d.ts.map +1 -1
  38. package/lib/families/bitcoin/wallet-btc/storage/index.js +114 -128
  39. package/lib/families/bitcoin/wallet-btc/storage/index.js.map +1 -1
  40. package/lib/families/bitcoin/wallet-btc/storage/types.d.ts +8 -7
  41. package/lib/families/bitcoin/wallet-btc/storage/types.d.ts.map +1 -1
  42. package/lib/families/bitcoin/wallet-btc/xpub.d.ts.map +1 -1
  43. package/lib/families/bitcoin/wallet-btc/xpub.js +42 -63
  44. package/lib/families/bitcoin/wallet-btc/xpub.js.map +1 -1
  45. package/lib/families/crypto_org/deviceTransactionConfig.d.ts +11 -0
  46. package/lib/families/crypto_org/deviceTransactionConfig.d.ts.map +1 -0
  47. package/lib/families/crypto_org/deviceTransactionConfig.js +40 -0
  48. package/lib/families/crypto_org/deviceTransactionConfig.js.map +1 -0
  49. package/lib/families/ethereum/modules/compound.d.ts.map +1 -1
  50. package/lib/families/ethereum/modules/compound.js +2 -1
  51. package/lib/families/ethereum/modules/compound.js.map +1 -1
  52. package/lib/families/ethereum/modules/erc1155.d.ts.map +1 -1
  53. package/lib/families/ethereum/modules/erc1155.js +2 -1
  54. package/lib/families/ethereum/modules/erc1155.js.map +1 -1
  55. package/lib/families/ethereum/modules/erc20.d.ts.map +1 -1
  56. package/lib/families/ethereum/modules/erc20.js +2 -1
  57. package/lib/families/ethereum/modules/erc20.js.map +1 -1
  58. package/lib/families/ethereum/modules/erc721.d.ts.map +1 -1
  59. package/lib/families/ethereum/modules/erc721.js +2 -1
  60. package/lib/families/ethereum/modules/erc721.js.map +1 -1
  61. package/lib/families/ethereum/modules/index.d.ts +5 -0
  62. package/lib/families/ethereum/modules/index.d.ts.map +1 -1
  63. package/lib/families/ethereum/modules/index.js.map +1 -1
  64. package/lib/families/ethereum/signOperation.d.ts.map +1 -1
  65. package/lib/families/ethereum/signOperation.js +33 -24
  66. package/lib/families/ethereum/signOperation.js.map +1 -1
  67. package/lib/families/polkadot/address.d.ts.map +1 -1
  68. package/lib/families/polkadot/address.js +5 -1
  69. package/lib/families/polkadot/address.js.map +1 -1
  70. package/lib/families/polkadot/js-getTransactionStatus.d.ts.map +1 -1
  71. package/lib/families/polkadot/js-getTransactionStatus.js +6 -2
  72. package/lib/families/polkadot/js-getTransactionStatus.js.map +1 -1
  73. package/lib/families/tezos/bridge/js.d.ts.map +1 -1
  74. package/lib/families/tezos/bridge/js.js +32 -16
  75. package/lib/families/tezos/bridge/js.js.map +1 -1
  76. package/lib/families/tezos/signOperation.d.ts.map +1 -1
  77. package/lib/families/tezos/signOperation.js +9 -0
  78. package/lib/families/tezos/signOperation.js.map +1 -1
  79. package/lib/families/tezos/synchronisation.js +1 -1
  80. package/lib/families/tezos/synchronisation.js.map +1 -1
  81. package/lib/generated/deviceTransactionConfig.d.ts +2 -0
  82. package/lib/generated/deviceTransactionConfig.d.ts.map +1 -1
  83. package/lib/generated/deviceTransactionConfig.js +18 -16
  84. package/lib/generated/deviceTransactionConfig.js.map +1 -1
  85. package/lib/hw/actions/transaction.d.ts +3 -1
  86. package/lib/hw/actions/transaction.d.ts.map +1 -1
  87. package/lib/hw/actions/transaction.js +4 -2
  88. package/lib/hw/actions/transaction.js.map +1 -1
  89. package/package.json +8 -8
  90. package/src/__tests__/__snapshots__/all.libcore.ts.snap +7 -7
  91. package/src/data/icons/react/index.tsx +1 -0
  92. package/src/data/icons/react/ssv.tsx +12 -0
  93. package/src/data/icons/reactNative/index.tsx +1 -0
  94. package/src/data/icons/reactNative/ssv.tsx +13 -0
  95. package/src/data/icons/svg/SSV.svg +4 -0
  96. package/src/env.ts +5 -0
  97. package/src/families/bitcoin/js-synchronisation.ts +14 -13
  98. package/src/families/bitcoin/wallet-btc/crypto/base.ts +33 -21
  99. package/src/families/bitcoin/wallet-btc/crypto/bip32.ts +36 -0
  100. package/src/families/bitcoin/wallet-btc/crypto/decred.ts +3 -154
  101. package/src/families/bitcoin/wallet-btc/storage/index.ts +23 -11
  102. package/src/families/bitcoin/wallet-btc/storage/types.ts +8 -10
  103. package/src/families/bitcoin/wallet-btc/xpub.ts +14 -10
  104. package/src/families/crypto_org/deviceTransactionConfig.ts +52 -0
  105. package/src/families/ethereum/modules/compound.ts +2 -0
  106. package/src/families/ethereum/modules/erc1155.ts +2 -0
  107. package/src/families/ethereum/modules/erc20.ts +2 -0
  108. package/src/families/ethereum/modules/erc721.ts +2 -0
  109. package/src/families/ethereum/modules/index.ts +10 -0
  110. package/src/families/ethereum/signOperation.ts +27 -8
  111. package/src/families/polkadot/address.ts +8 -2
  112. package/src/families/polkadot/js-getTransactionStatus.ts +6 -2
  113. package/src/families/tezos/bridge/js.ts +38 -17
  114. package/src/families/tezos/signOperation.ts +12 -0
  115. package/src/families/tezos/synchronisation.ts +1 -1
  116. package/src/generated/deviceTransactionConfig.ts +3 -0
  117. package/src/hw/actions/transaction.ts +7 -2
@@ -1,4 +1,5 @@
1
1
  import { findLast, filter, uniqBy, findIndex, has } from "lodash";
2
+ import Base from "../crypto/base";
2
3
  import { Input, IStorage, Output, TX, Address } from "./types";
3
4
 
4
5
  // a mock storage class that just use js objects
@@ -12,12 +13,14 @@ class BitcoinLikeStorage implements IStorage {
12
13
  // accounting
13
14
  unspentUtxos: { [key: string]: Output[] } = {};
14
15
 
16
+ addressCache: { [key: string]: string } = {};
17
+
15
18
  // only needed to handle the case when the input
16
19
  // is seen before the output (typically explorer
17
20
  // returning unordered tx within the same block)
18
21
  spentUtxos: { [key: string]: Input[] } = {};
19
22
 
20
- async getLastTx(txFilter: {
23
+ getLastTx(txFilter: {
21
24
  account?: number;
22
25
  index?: number;
23
26
  address?: string;
@@ -38,18 +41,18 @@ class BitcoinLikeStorage implements IStorage {
38
41
  return tx;
39
42
  }
40
43
 
41
- async getTx(address: string, hash: string) {
44
+ getTx(address: string, hash: string) {
42
45
  const index = `${address}-${hash}`;
43
46
  return this.txs[this.primaryIndex[index]];
44
47
  }
45
48
 
46
49
  // TODO: only expose unspentUtxos
47
- async getAddressUnspentUtxos(address: Address) {
50
+ getAddressUnspentUtxos(address: Address) {
48
51
  const indexAddress = address.address;
49
52
  return this.unspentUtxos[indexAddress];
50
53
  }
51
54
 
52
- async appendTxs(txs: TX[]) {
55
+ appendTxs(txs: TX[]) {
53
56
  const lastLength = this.txs.length;
54
57
 
55
58
  txs.forEach((tx) => {
@@ -97,10 +100,7 @@ class BitcoinLikeStorage implements IStorage {
97
100
  return this.txs.length - lastLength;
98
101
  }
99
102
 
100
- async getUniquesAddresses(addressesFilter: {
101
- account?: number;
102
- index?: number;
103
- }) {
103
+ getUniquesAddresses(addressesFilter: { account?: number; index?: number }) {
104
104
  // TODO: to speed up, create more useful indexes in appendTxs
105
105
  return uniqBy(
106
106
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -114,7 +114,7 @@ class BitcoinLikeStorage implements IStorage {
114
114
  );
115
115
  }
116
116
 
117
- async removeTxs(txsFilter: { account: number; index: number }) {
117
+ removeTxs(txsFilter: { account: number; index: number }) {
118
118
  const newTxs: TX[] = [];
119
119
  this.primaryIndex = {};
120
120
 
@@ -137,7 +137,7 @@ class BitcoinLikeStorage implements IStorage {
137
137
 
138
138
  // We are a bit ugly because we can't rely undo unspentUTXO
139
139
  // So we clean the address and rebuild without the pendings
140
- async removePendingTxs(txsFilter: { account: number; index: number }) {
140
+ removePendingTxs(txsFilter: { account: number; index: number }) {
141
141
  const newTxs: TX[] = [];
142
142
  const txsToReAdd: TX[] = [];
143
143
  this.primaryIndex = {};
@@ -161,7 +161,14 @@ class BitcoinLikeStorage implements IStorage {
161
161
  });
162
162
 
163
163
  this.txs = newTxs;
164
- await this.appendTxs(txsToReAdd);
164
+ this.appendTxs(txsToReAdd);
165
+ }
166
+
167
+ addAddress(key: string, address: string): void {
168
+ if (!this.addressCache) {
169
+ this.addressCache = {};
170
+ }
171
+ this.addressCache[key] = address;
165
172
  }
166
173
 
167
174
  exportSync() {
@@ -169,6 +176,7 @@ class BitcoinLikeStorage implements IStorage {
169
176
  txs: this.txs,
170
177
  primaryIndex: this.primaryIndex,
171
178
  unspentUtxos: this.unspentUtxos,
179
+ addressCache: this.addressCache,
172
180
  };
173
181
  }
174
182
 
@@ -176,10 +184,13 @@ class BitcoinLikeStorage implements IStorage {
176
184
  txs: TX[];
177
185
  primaryIndex: { [key: string]: number };
178
186
  unspentUtxos: { [key: string]: Output[] };
187
+ addressCache: { [key: string]: string };
179
188
  }) {
180
189
  this.txs = data.txs;
181
190
  this.primaryIndex = data.primaryIndex;
182
191
  this.unspentUtxos = data.unspentUtxos;
192
+ this.addressCache = data.addressCache;
193
+ Base.addressCache = { ...Base.addressCache, ...this.addressCache };
183
194
  }
184
195
 
185
196
  async export() {
@@ -190,6 +201,7 @@ class BitcoinLikeStorage implements IStorage {
190
201
  txs: TX[];
191
202
  primaryIndex: { [key: string]: number };
192
203
  unspentUtxos: { [key: string]: Output[] };
204
+ addressCache: { [key: string]: string };
193
205
  }) {
194
206
  return this.loadSync(data);
195
207
  }
@@ -43,24 +43,22 @@ export interface Address {
43
43
  }
44
44
 
45
45
  export interface IStorage {
46
- appendTxs(txs: TX[]): Promise<number>;
47
- getAddressUnspentUtxos(address: Address): Promise<Output[]>;
46
+ appendTxs(txs: TX[]): number;
47
+ getAddressUnspentUtxos(address: Address): Output[];
48
48
  getLastTx(txFilter: {
49
49
  account?: number;
50
50
  index?: number;
51
51
  address?: string;
52
52
  confirmed?: boolean;
53
- }): Promise<TX | undefined>;
54
- getTx(address: string, hash: string): Promise<TX | undefined>;
53
+ }): TX | undefined;
54
+ getTx(address: string, hash: string): TX | undefined;
55
55
  getUniquesAddresses(addressesFilter: {
56
56
  account?: number;
57
57
  index?: number;
58
- }): Promise<Address[]>;
59
- removeTxs(txsFilter: { account: number; index: number }): Promise<void>;
60
- removePendingTxs(txsFilter: {
61
- account: number;
62
- index: number;
63
- }): Promise<void>;
58
+ }): Address[];
59
+ removeTxs(txsFilter: { account: number; index: number }): void;
60
+ removePendingTxs(txsFilter: { account: number; index: number }): void;
61
+ addAddress(key: string, address: string): void;
64
62
  export(): Promise<unknown>;
65
63
  load(data: unknown): Promise<void>;
66
64
  exportSync(): unknown;
@@ -66,6 +66,10 @@ class Xpub extends EventEmitter {
66
66
  index
67
67
  );
68
68
 
69
+ this.storage.addAddress(
70
+ `${this.derivationMode}-${this.xpub}-${account}-${index}`,
71
+ address
72
+ );
69
73
  await this.whenSynced("address", address);
70
74
 
71
75
  const data = {
@@ -87,13 +91,13 @@ class Xpub extends EventEmitter {
87
91
 
88
92
  // in case pendings have changed we clean them out
89
93
  // TODO perf : bad : looping in the tx array
90
- const hasPendings = !!(await this.storage.getLastTx({
94
+ const hasPendings = !!this.storage.getLastTx({
91
95
  confirmed: false,
92
96
  account,
93
97
  index,
94
- }));
98
+ });
95
99
  if (hasPendings) {
96
- await this.storage.removePendingTxs({ account, index });
100
+ this.storage.removePendingTxs({ account, index });
97
101
  }
98
102
  total = await this.fetchHydrateAndStoreNewTxs(address, account, index);
99
103
  } catch (e) {
@@ -103,7 +107,7 @@ class Xpub extends EventEmitter {
103
107
 
104
108
  this.emitSynced({ ...data, total });
105
109
 
106
- const lastTx = await this.storage.getLastTx({
110
+ const lastTx = this.storage.getLastTx({
107
111
  account,
108
112
  index,
109
113
  });
@@ -200,7 +204,7 @@ class Xpub extends EventEmitter {
200
204
  async getAddressBalance(address: Address) {
201
205
  await this.whenSynced("address", address.address);
202
206
 
203
- const unspentUtxos = await this.storage.getAddressUnspentUtxos(address);
207
+ const unspentUtxos = this.storage.getAddressUnspentUtxos(address);
204
208
 
205
209
  return unspentUtxos.reduce(
206
210
  (total, { value }) => total.plus(value),
@@ -419,7 +423,7 @@ class Xpub extends EventEmitter {
419
423
  let txs: TX[] = [];
420
424
  let inserted = 0;
421
425
  do {
422
- const lastTx = await this.storage.getLastTx({
426
+ const lastTx = this.storage.getLastTx({
423
427
  account,
424
428
  index,
425
429
  confirmed: true,
@@ -433,14 +437,14 @@ class Xpub extends EventEmitter {
433
437
  if (pendingTxs.length === 0) {
434
438
  pendingTxs = txs.filter((tx) => !tx.block);
435
439
  }
436
- inserted += await this.storage.appendTxs(txs.filter((tx) => tx.block)); // only insert not pending tx
440
+ inserted += this.storage.appendTxs(txs.filter((tx) => tx.block)); // only insert not pending tx
437
441
  } while (txs.length - pendingTxs.length >= this.txsSyncArraySize); // check whether page is full, if not, it is the last page
438
- inserted += await this.storage.appendTxs(pendingTxs);
442
+ inserted += this.storage.appendTxs(pendingTxs);
439
443
  return inserted;
440
444
  }
441
445
 
442
446
  async checkAddressReorg(account: number, index: number) {
443
- const lastTx = await this.storage.getLastTx({
447
+ const lastTx = this.storage.getLastTx({
444
448
  account,
445
449
  index,
446
450
  confirmed: true,
@@ -462,7 +466,7 @@ class Xpub extends EventEmitter {
462
466
  // TODO: delete only everything for this (address, block)
463
467
  // but need to think if its possible with current storage implem
464
468
 
465
- await this.storage.removeTxs({
469
+ this.storage.removeTxs({
466
470
  account,
467
471
  index,
468
472
  });
@@ -0,0 +1,52 @@
1
+ import type { AccountLike, Account, TransactionStatus } from "../../types";
2
+ import type { Transaction } from "./types";
3
+ import type { DeviceTransactionField } from "../../transaction";
4
+
5
+ function getDeviceTransactionConfig({
6
+ transaction,
7
+ status: { estimatedFees },
8
+ }: {
9
+ account: AccountLike;
10
+ parentAccount?: Account;
11
+ transaction: Transaction;
12
+ status: TransactionStatus;
13
+ }): Array<DeviceTransactionField> {
14
+ const fields: Array<DeviceTransactionField> = [];
15
+
16
+ if (transaction.useAllAmount) {
17
+ fields.push({
18
+ type: "text",
19
+ label: "Method",
20
+ value: "Send all",
21
+ });
22
+ } else {
23
+ fields.push({
24
+ type: "text",
25
+ label: "Method",
26
+ value: "Send",
27
+ });
28
+ fields.push({
29
+ type: "amount",
30
+ label: "Amount",
31
+ });
32
+ }
33
+
34
+ if (!estimatedFees.isZero()) {
35
+ fields.push({
36
+ type: "fees",
37
+ label: "Fees",
38
+ });
39
+ }
40
+
41
+ if (transaction.memo) {
42
+ fields.push({
43
+ type: "text",
44
+ label: "Memo",
45
+ value: transaction.memo,
46
+ });
47
+ }
48
+
49
+ return fields;
50
+ }
51
+
52
+ export default getDeviceTransactionConfig;
@@ -160,6 +160,8 @@ const compoundSupply: ModeModule = {
160
160
  ];
161
161
  }
162
162
  },
163
+
164
+ getResolutionConfig: () => ({ erc20: true, externalPlugins: true }),
163
165
  };
164
166
 
165
167
  /**
@@ -122,6 +122,8 @@ const erc1155Transfer: ModeModule = {
122
122
  },
123
123
 
124
124
  prepareTransaction,
125
+
126
+ getResolutionConfig: () => ({ nft: true }),
125
127
  };
126
128
 
127
129
  function serializeTransactionData(
@@ -138,6 +138,8 @@ const erc20approve: ModeModule = {
138
138
  fillOptimisticOperation(_account, _transaction, operation) {
139
139
  operation.type = "FEES";
140
140
  },
141
+
142
+ getResolutionConfig: () => ({ erc20: true, externalPlugins: true }),
141
143
  };
142
144
  export const modes: Record<Modes, ModeModule> = {
143
145
  "erc20.approve": erc20approve,
@@ -119,6 +119,8 @@ const erc721Transfer: ModeModule = {
119
119
  fillOptimisticOperation(a, t, op) {
120
120
  op.type = "FEES";
121
121
  },
122
+
123
+ getResolutionConfig: () => ({ nft: true }),
122
124
  };
123
125
 
124
126
  function serializeTransactionData(
@@ -20,6 +20,8 @@ import type { Modes as ERC20Modes } from "./erc20";
20
20
  import type { Modes as SendModes } from "./send";
21
21
  import type { Modes as ERC721Modes } from "./erc721";
22
22
  import type { Modes as ERC1155Modes } from "./erc1155";
23
+ import { ResolutionConfig } from "@ledgerhq/hw-app-eth/lib/services/types";
24
+
23
25
  const modules = {
24
26
  erc20,
25
27
  compound,
@@ -98,6 +100,14 @@ export type ModeModule = {
98
100
  account: Account,
99
101
  transaction: Transaction
100
102
  ) => Promise<Transaction>;
103
+
104
+ /**
105
+ * tells what needs to be resolved in the transaction
106
+ */
107
+ getResolutionConfig?: (
108
+ account: Account,
109
+ transaction: Transaction
110
+ ) => ResolutionConfig;
101
111
  };
102
112
  export const modes: Record<TransactionMode, ModeModule> = {} as Record<
103
113
  TransactionMode,
@@ -7,6 +7,7 @@ import { log } from "@ledgerhq/logs";
7
7
  import { FeeNotLoaded } from "@ledgerhq/errors";
8
8
  import Eth from "@ledgerhq/hw-app-eth";
9
9
  import { byContractAddressAndChainId } from "@ledgerhq/hw-app-eth/erc20";
10
+ import ethLedgerServices from "@ledgerhq/hw-app-eth/lib/services/ledger";
10
11
  import type { Transaction } from "./types";
11
12
  import type { Operation, Account, SignOperationEvent } from "../../types";
12
13
  import { getGasLimit, buildEthereumTx } from "./transaction";
@@ -15,6 +16,7 @@ import { withDevice } from "../../hw/deviceAccess";
15
16
  import { modes } from "./modules";
16
17
  import { isNFTActive } from "../../nft";
17
18
  import { getEnv } from "../../env";
19
+ import { LoadConfig } from "@ledgerhq/hw-app-eth/lib/services/types";
18
20
  export const signOperation = ({
19
21
  account,
20
22
  deviceId,
@@ -61,13 +63,31 @@ export const signOperation = ({
61
63
  const value = new BigNumber(
62
64
  "0x" + (tx.value.toString("hex") || "0")
63
65
  );
64
- const eth = new Eth(transport);
66
+
67
+ const txHex = tx.serialize().toString("hex");
68
+
69
+ const loadConfig: LoadConfig = {};
65
70
  if (isNFTActive(account.currency)) {
66
- eth.setLoadConfig({
67
- nftExplorerBaseURL:
68
- getEnv("NFT_ETH_METADATA_SERVICE") + "/v1/ethereum",
69
- });
71
+ loadConfig.nftExplorerBaseURL =
72
+ getEnv("NFT_ETH_METADATA_SERVICE") + "/v1/ethereum";
70
73
  }
74
+
75
+ const m = modes[transaction.mode];
76
+ invariant(m, "missing module for mode=" + transaction.mode);
77
+
78
+ const resolutionConfig = m.getResolutionConfig
79
+ ? m.getResolutionConfig(account, transaction)
80
+ : {};
81
+
82
+ const resolution = await ethLedgerServices.resolveTransaction(
83
+ txHex,
84
+ loadConfig,
85
+ resolutionConfig
86
+ );
87
+
88
+ const eth = new Eth(transport);
89
+ eth.setLoadConfig(loadConfig);
90
+
71
91
  // FIXME this part is still required for compound to correctly display info on the device
72
92
  const addrs =
73
93
  (fillTransactionDataResult &&
@@ -91,7 +111,8 @@ export const signOperation = ({
91
111
  });
92
112
  const result = await eth.signTransaction(
93
113
  freshAddressPath,
94
- tx.serialize().toString("hex")
114
+ txHex,
115
+ resolution
95
116
  );
96
117
  if (cancelled) return;
97
118
  o.next({
@@ -131,8 +152,6 @@ export const signOperation = ({
131
152
  date: new Date(),
132
153
  extra: {},
133
154
  };
134
- const m = modes[transaction.mode];
135
- invariant(m, "missing module for mode=" + transaction.mode);
136
155
  m.fillOptimisticOperation(account, transaction, operation);
137
156
  o.next({
138
157
  type: "signed",
@@ -1,4 +1,5 @@
1
- import { decodeAddress } from "@polkadot/util-crypto";
1
+ import { encodeAddress, decodeAddress } from "@polkadot/util-crypto";
2
+ import { hexToU8a, isHex } from "@polkadot/util";
2
3
 
3
4
  const POLKADOT_SS58_PREFIX = 0;
4
5
 
@@ -7,6 +8,7 @@ const POLKADOT_SS58_PREFIX = 0;
7
8
  *
8
9
  * @param {*} address
9
10
  */
11
+ // TODO Cache this to improve perf
10
12
  export const isValidAddress = (
11
13
  address: string,
12
14
  ss58Format: number = POLKADOT_SS58_PREFIX
@@ -14,7 +16,11 @@ export const isValidAddress = (
14
16
  if (!address) return false;
15
17
 
16
18
  try {
17
- decodeAddress(address, false, ss58Format);
19
+ encodeAddress(
20
+ isHex(address)
21
+ ? hexToU8a(address)
22
+ : decodeAddress(address, false, ss58Format)
23
+ );
18
24
  return true;
19
25
  } catch (err) {
20
26
  return false;
@@ -58,7 +58,9 @@ const getSendTransactionStatus = async (
58
58
  } else if (a.freshAddress === t.recipient) {
59
59
  errors.recipient = new InvalidAddressBecauseDestinationIsAlsoSource();
60
60
  } else if (!isValidAddress(t.recipient)) {
61
- errors.recipient = new InvalidAddress("");
61
+ errors.recipient = new InvalidAddress("", {
62
+ currencyName: a.currency.name,
63
+ });
62
64
  }
63
65
 
64
66
  const estimatedFees = t.fees || new BigNumber(0);
@@ -181,7 +183,9 @@ const getTransactionStatus = async (a: Account, t: Transaction) => {
181
183
  if (!t.recipient) {
182
184
  errors.recipient = new RecipientRequired("");
183
185
  } else if (!isValidAddress(t.recipient)) {
184
- errors.recipient = new InvalidAddress("");
186
+ errors.recipient = new InvalidAddress("", {
187
+ currencyName: a.currency.name,
188
+ });
185
189
  } else if (await isControllerAddress(t.recipient)) {
186
190
  errors.recipient = new PolkadotUnauthorizedOperation(
187
191
  "Recipient is already a controller"
@@ -182,11 +182,14 @@ const prepareTransaction = async (
182
182
  },
183
183
  });
184
184
 
185
+ const t: Transaction = { ...transaction, taquitoError: null };
186
+
187
+ let amount = transaction.amount;
185
188
  if (transaction.useAllAmount) {
186
189
  // taquito does not accept 0, so we do 1
187
190
  // only failing case is when the account precisely has fees + 1. which is
188
191
  // unlikely
189
- transaction.amount = new BigNumber(1);
192
+ amount = new BigNumber(1);
190
193
  }
191
194
 
192
195
  try {
@@ -196,7 +199,7 @@ const prepareTransaction = async (
196
199
  out = await tezos.estimate.transfer({
197
200
  mutez: true,
198
201
  to: transaction.recipient,
199
- amount: transaction.amount.toNumber(),
202
+ amount: amount.toNumber(),
200
203
  });
201
204
  break;
202
205
  case "delegate":
@@ -214,7 +217,7 @@ const prepareTransaction = async (
214
217
  throw new Error("unsupported mode=" + transaction.mode);
215
218
  }
216
219
 
217
- if (transaction.useAllAmount) {
220
+ if (t.useAllAmount) {
218
221
  const totalFees = out.suggestedFeeMutez + out.burnFeeMutez;
219
222
  const maxAmount = account.balance
220
223
  .minus(totalFees + (tezosResources.revealed ? 0 : DEFAULT_FEE.REVEAL))
@@ -228,41 +231,59 @@ const prepareTransaction = async (
228
231
  return gasBuffer * MINIMAL_FEE_PER_GAS_MUTEZ + opSize;
229
232
  };
230
233
  const incr = increasedFee(gasBuffer, Number(out.opSize));
231
- transaction.fees = new BigNumber(out.suggestedFeeMutez + incr);
232
- transaction.gasLimit = new BigNumber(out.gasLimit + gasBuffer);
233
- transaction.amount = new BigNumber(maxAmount - incr);
234
+ t.fees = new BigNumber(out.suggestedFeeMutez + incr);
235
+ t.gasLimit = new BigNumber(out.gasLimit + gasBuffer);
236
+ t.amount = new BigNumber(maxAmount - incr);
234
237
  } else {
235
- transaction.fees = new BigNumber(out.suggestedFeeMutez);
236
- transaction.gasLimit = new BigNumber(out.gasLimit);
237
- transaction.storageLimit = new BigNumber(out.storageLimit);
238
+ t.fees = new BigNumber(out.suggestedFeeMutez);
239
+ t.gasLimit = new BigNumber(out.gasLimit);
240
+ t.storageLimit = new BigNumber(out.storageLimit);
238
241
  }
239
242
 
240
- transaction.storageLimit = new BigNumber(out.storageLimit);
241
- transaction.estimatedFees = transaction.fees;
243
+ t.storageLimit = new BigNumber(out.storageLimit);
244
+ t.estimatedFees = t.fees;
242
245
  if (!tezosResources.revealed) {
243
- transaction.estimatedFees = transaction.estimatedFees.plus(
244
- DEFAULT_FEE.REVEAL
245
- );
246
+ t.estimatedFees = t.estimatedFees.plus(DEFAULT_FEE.REVEAL);
246
247
  }
247
248
  } catch (e) {
248
249
  if (typeof e !== "object" || !e) throw e;
249
250
  if ("id" in e) {
250
- transaction.taquitoError = (e as { id: string }).id;
251
+ t.taquitoError = (e as { id: string }).id;
251
252
  } else if ("status" in e) {
252
253
  // in case of http 400, log & ignore (more case to handle)
253
254
  log(
254
255
  "taquito-network-error",
255
256
  String((e as { message: string }).message || ""),
256
- { transaction }
257
+ { transaction: t }
257
258
  );
258
259
  } else {
259
260
  throw e;
260
261
  }
261
262
  }
262
263
 
263
- return transaction;
264
+ // nothing changed
265
+ if (
266
+ bnEq(t.fees, transaction.fees) &&
267
+ bnEq(t.fees, transaction.fees) &&
268
+ bnEq(t.gasLimit, transaction.gasLimit) &&
269
+ bnEq(t.amount, transaction.amount) &&
270
+ bnEq(t.storageLimit, transaction.storageLimit) &&
271
+ bnEq(t.estimatedFees, transaction.estimatedFees) &&
272
+ t.taquitoError === transaction.taquitoError
273
+ ) {
274
+ return transaction;
275
+ }
276
+
277
+ return t;
264
278
  };
265
279
 
280
+ function bnEq(
281
+ a: BigNumber | null | undefined,
282
+ b: BigNumber | null | undefined
283
+ ): boolean {
284
+ return !a && !b ? true : !a || !b ? false : a.eq(b);
285
+ }
286
+
266
287
  const estimateMaxSpendable = async ({
267
288
  account,
268
289
  parentAccount,
@@ -8,6 +8,8 @@ import type { Account, SignOperationEvent } from "../../types";
8
8
  import { withDevice } from "../../hw/deviceAccess";
9
9
  import { getEnv } from "../../env";
10
10
  import { FeeNotLoaded } from "@ledgerhq/errors";
11
+ import { upperModulo } from "../../modulo";
12
+ import BigNumber from "bignumber.js";
11
13
 
12
14
  export const signOperation = ({
13
15
  account,
@@ -53,6 +55,16 @@ export const signOperation = ({
53
55
  storageLimit: transaction.storageLimit?.toNumber() || 0,
54
56
  gasLimit: transaction.gasLimit?.toNumber() || 0,
55
57
  };
58
+
59
+ if (["delegate", "undelegate"].includes(transaction.mode)) {
60
+ // https://ledgerhq.atlassian.net/browse/LL-8821
61
+ params.gasLimit = upperModulo(
62
+ transaction.gasLimit || new BigNumber(0),
63
+ new BigNumber(136),
64
+ new BigNumber(1000)
65
+ ).toNumber();
66
+ }
67
+
56
68
  switch (transaction.mode) {
57
69
  case "send":
58
70
  res = await tezos.contract.transfer({
@@ -226,7 +226,7 @@ const txToOp =
226
226
  }
227
227
 
228
228
  return {
229
- id: encodeOperationId(accountId, hash, type),
229
+ id: encodeOperationId(accountId, hash + String(tx.id), type),
230
230
  hash,
231
231
  type,
232
232
  value,
@@ -4,6 +4,8 @@ import bitcoin from "../families/bitcoin/deviceTransactionConfig";
4
4
 
5
5
  import cosmos from "../families/cosmos/deviceTransactionConfig";
6
6
 
7
+ import crypto_org from "../families/crypto_org/deviceTransactionConfig";
8
+
7
9
  import elrond from "../families/elrond/deviceTransactionConfig";
8
10
 
9
11
  import ethereum from "../families/ethereum/deviceTransactionConfig";
@@ -25,6 +27,7 @@ export default {
25
27
  algorand,
26
28
  bitcoin,
27
29
  cosmos,
30
+ crypto_org,
28
31
  elrond,
29
32
  ethereum,
30
33
  polkadot,
@@ -17,7 +17,7 @@ import { getMainAccount } from "../../account";
17
17
  import { getAccountBridge } from "../../bridge";
18
18
  import type { ConnectAppEvent, Input as ConnectAppInput } from "../connectApp";
19
19
  import type { Action, Device } from "./types";
20
- import type { AppState } from "./app";
20
+ import type { AppRequest, AppState } from "./app";
21
21
  import { createAction as createAppAction } from "./app";
22
22
  type State = {
23
23
  signedOperation: SignedOperation | null | undefined;
@@ -33,6 +33,8 @@ type TransactionRequest = {
33
33
  transaction: Transaction;
34
34
  status: TransactionStatus;
35
35
  appName?: string;
36
+ dependencies?: AppRequest[];
37
+ requireLatestFirmware?: boolean;
36
38
  };
37
39
  type TransactionResult =
38
40
  | {
@@ -112,7 +114,8 @@ export const createAction = (
112
114
  reduxDevice: Device | null | undefined,
113
115
  txRequest: TransactionRequest
114
116
  ): TransactionState => {
115
- const { transaction, appName } = txRequest;
117
+ const { transaction, appName, dependencies, requireLatestFirmware } =
118
+ txRequest;
116
119
  const mainAccount = getMainAccount(
117
120
  txRequest.account,
118
121
  txRequest.parentAccount
@@ -120,6 +123,8 @@ export const createAction = (
120
123
  const appState = createAppAction(connectAppExec).useHook(reduxDevice, {
121
124
  account: mainAccount,
122
125
  appName,
126
+ dependencies,
127
+ requireLatestFirmware,
123
128
  });
124
129
  const { device, opened, inWrongDeviceForAccount, error } = appState;
125
130
  const [state, setState] = useState(initialState);