@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.
- package/lib/data/icons/react/index.d.ts +1 -0
- package/lib/data/icons/react/index.d.ts.map +1 -1
- package/lib/data/icons/react/index.js +4 -2
- package/lib/data/icons/react/index.js.map +1 -1
- package/lib/data/icons/react/ssv.d.ts +8 -0
- package/lib/data/icons/react/ssv.d.ts.map +1 -0
- package/lib/data/icons/react/ssv.js +31 -0
- package/lib/data/icons/react/ssv.js.map +1 -0
- package/lib/data/icons/reactNative/index.d.ts +1 -0
- package/lib/data/icons/reactNative/index.d.ts.map +1 -1
- package/lib/data/icons/reactNative/index.js +4 -2
- package/lib/data/icons/reactNative/index.js.map +1 -1
- package/lib/data/icons/reactNative/ssv.d.ts +8 -0
- package/lib/data/icons/reactNative/ssv.d.ts.map +1 -0
- package/lib/data/icons/reactNative/ssv.js +32 -0
- package/lib/data/icons/reactNative/ssv.js.map +1 -0
- package/lib/env.d.ts +10 -5
- package/lib/env.d.ts.map +1 -1
- package/lib/env.js +5 -0
- package/lib/env.js.map +1 -1
- package/lib/env.react.d.ts +1 -1
- package/lib/env.react.d.ts.map +1 -1
- package/lib/families/bitcoin/js-synchronisation.d.ts.map +1 -1
- package/lib/families/bitcoin/js-synchronisation.js +10 -12
- package/lib/families/bitcoin/js-synchronisation.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/base.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/base.js +30 -22
- package/lib/families/bitcoin/wallet-btc/crypto/base.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/bip32.d.ts +11 -0
- package/lib/families/bitcoin/wallet-btc/crypto/bip32.d.ts.map +1 -0
- package/lib/families/bitcoin/wallet-btc/crypto/bip32.js +32 -0
- package/lib/families/bitcoin/wallet-btc/crypto/bip32.js.map +1 -0
- package/lib/families/bitcoin/wallet-btc/crypto/decred.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/decred.js +3 -114
- package/lib/families/bitcoin/wallet-btc/crypto/decred.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/storage/index.d.ts +24 -8
- package/lib/families/bitcoin/wallet-btc/storage/index.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/storage/index.js +114 -128
- package/lib/families/bitcoin/wallet-btc/storage/index.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/storage/types.d.ts +8 -7
- package/lib/families/bitcoin/wallet-btc/storage/types.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/xpub.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/xpub.js +42 -63
- package/lib/families/bitcoin/wallet-btc/xpub.js.map +1 -1
- package/lib/families/crypto_org/deviceTransactionConfig.d.ts +11 -0
- package/lib/families/crypto_org/deviceTransactionConfig.d.ts.map +1 -0
- package/lib/families/crypto_org/deviceTransactionConfig.js +40 -0
- package/lib/families/crypto_org/deviceTransactionConfig.js.map +1 -0
- package/lib/families/ethereum/modules/compound.d.ts.map +1 -1
- package/lib/families/ethereum/modules/compound.js +2 -1
- package/lib/families/ethereum/modules/compound.js.map +1 -1
- package/lib/families/ethereum/modules/erc1155.d.ts.map +1 -1
- package/lib/families/ethereum/modules/erc1155.js +2 -1
- package/lib/families/ethereum/modules/erc1155.js.map +1 -1
- package/lib/families/ethereum/modules/erc20.d.ts.map +1 -1
- package/lib/families/ethereum/modules/erc20.js +2 -1
- package/lib/families/ethereum/modules/erc20.js.map +1 -1
- package/lib/families/ethereum/modules/erc721.d.ts.map +1 -1
- package/lib/families/ethereum/modules/erc721.js +2 -1
- package/lib/families/ethereum/modules/erc721.js.map +1 -1
- package/lib/families/ethereum/modules/index.d.ts +5 -0
- package/lib/families/ethereum/modules/index.d.ts.map +1 -1
- package/lib/families/ethereum/modules/index.js.map +1 -1
- package/lib/families/ethereum/signOperation.d.ts.map +1 -1
- package/lib/families/ethereum/signOperation.js +33 -24
- package/lib/families/ethereum/signOperation.js.map +1 -1
- package/lib/families/polkadot/address.d.ts.map +1 -1
- package/lib/families/polkadot/address.js +5 -1
- package/lib/families/polkadot/address.js.map +1 -1
- package/lib/families/polkadot/js-getTransactionStatus.d.ts.map +1 -1
- package/lib/families/polkadot/js-getTransactionStatus.js +6 -2
- package/lib/families/polkadot/js-getTransactionStatus.js.map +1 -1
- package/lib/families/tezos/bridge/js.d.ts.map +1 -1
- package/lib/families/tezos/bridge/js.js +32 -16
- package/lib/families/tezos/bridge/js.js.map +1 -1
- package/lib/families/tezos/signOperation.d.ts.map +1 -1
- package/lib/families/tezos/signOperation.js +9 -0
- package/lib/families/tezos/signOperation.js.map +1 -1
- package/lib/families/tezos/synchronisation.js +1 -1
- package/lib/families/tezos/synchronisation.js.map +1 -1
- package/lib/generated/deviceTransactionConfig.d.ts +2 -0
- package/lib/generated/deviceTransactionConfig.d.ts.map +1 -1
- package/lib/generated/deviceTransactionConfig.js +18 -16
- package/lib/generated/deviceTransactionConfig.js.map +1 -1
- package/lib/hw/actions/transaction.d.ts +3 -1
- package/lib/hw/actions/transaction.d.ts.map +1 -1
- package/lib/hw/actions/transaction.js +4 -2
- package/lib/hw/actions/transaction.js.map +1 -1
- package/package.json +8 -8
- package/src/__tests__/__snapshots__/all.libcore.ts.snap +7 -7
- package/src/data/icons/react/index.tsx +1 -0
- package/src/data/icons/react/ssv.tsx +12 -0
- package/src/data/icons/reactNative/index.tsx +1 -0
- package/src/data/icons/reactNative/ssv.tsx +13 -0
- package/src/data/icons/svg/SSV.svg +4 -0
- package/src/env.ts +5 -0
- package/src/families/bitcoin/js-synchronisation.ts +14 -13
- package/src/families/bitcoin/wallet-btc/crypto/base.ts +33 -21
- package/src/families/bitcoin/wallet-btc/crypto/bip32.ts +36 -0
- package/src/families/bitcoin/wallet-btc/crypto/decred.ts +3 -154
- package/src/families/bitcoin/wallet-btc/storage/index.ts +23 -11
- package/src/families/bitcoin/wallet-btc/storage/types.ts +8 -10
- package/src/families/bitcoin/wallet-btc/xpub.ts +14 -10
- package/src/families/crypto_org/deviceTransactionConfig.ts +52 -0
- package/src/families/ethereum/modules/compound.ts +2 -0
- package/src/families/ethereum/modules/erc1155.ts +2 -0
- package/src/families/ethereum/modules/erc20.ts +2 -0
- package/src/families/ethereum/modules/erc721.ts +2 -0
- package/src/families/ethereum/modules/index.ts +10 -0
- package/src/families/ethereum/signOperation.ts +27 -8
- package/src/families/polkadot/address.ts +8 -2
- package/src/families/polkadot/js-getTransactionStatus.ts +6 -2
- package/src/families/tezos/bridge/js.ts +38 -17
- package/src/families/tezos/signOperation.ts +12 -0
- package/src/families/tezos/synchronisation.ts +1 -1
- package/src/generated/deviceTransactionConfig.ts +3 -0
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
50
|
+
getAddressUnspentUtxos(address: Address) {
|
|
48
51
|
const indexAddress = address.address;
|
|
49
52
|
return this.unspentUtxos[indexAddress];
|
|
50
53
|
}
|
|
51
54
|
|
|
52
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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[]):
|
|
47
|
-
getAddressUnspentUtxos(address: Address):
|
|
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
|
-
}):
|
|
54
|
-
getTx(address: string, hash: string):
|
|
53
|
+
}): TX | undefined;
|
|
54
|
+
getTx(address: string, hash: string): TX | undefined;
|
|
55
55
|
getUniquesAddresses(addressesFilter: {
|
|
56
56
|
account?: number;
|
|
57
57
|
index?: number;
|
|
58
|
-
}):
|
|
59
|
-
removeTxs(txsFilter: { account: number; index: number }):
|
|
60
|
-
removePendingTxs(txsFilter: {
|
|
61
|
-
|
|
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 = !!
|
|
94
|
+
const hasPendings = !!this.storage.getLastTx({
|
|
91
95
|
confirmed: false,
|
|
92
96
|
account,
|
|
93
97
|
index,
|
|
94
|
-
})
|
|
98
|
+
});
|
|
95
99
|
if (hasPendings) {
|
|
96
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 +=
|
|
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 +=
|
|
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 =
|
|
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
|
-
|
|
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;
|
|
@@ -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,
|
|
@@ -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
|
-
|
|
66
|
+
|
|
67
|
+
const txHex = tx.serialize().toString("hex");
|
|
68
|
+
|
|
69
|
+
const loadConfig: LoadConfig = {};
|
|
65
70
|
if (isNFTActive(account.currency)) {
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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 (
|
|
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
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
241
|
-
|
|
243
|
+
t.storageLimit = new BigNumber(out.storageLimit);
|
|
244
|
+
t.estimatedFees = t.fees;
|
|
242
245
|
if (!tezosResources.revealed) {
|
|
243
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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({
|
|
@@ -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 } =
|
|
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);
|