@ledgerhq/live-common 21.16.1 → 21.17.0
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/__tests__/families/bitcoin/wallet-btc/utils.test.js +95 -7
- package/lib/__tests__/families/bitcoin/wallet-btc/utils.test.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.js +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.js +2 -2
- package/lib/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.pickingStrategies.integration.test.js +20 -10
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.pickingStrategies.integration.test.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.NP2WPKH.integration.test.js +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.NP2WPKH.integration.test.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.P2WPKH.integration.test.js +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.P2WPKH.integration.test.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.integration.test.js +2 -2
- package/lib/__tests__/families/bitcoin/wallet-btc/xpub.txs.integration.test.js.map +1 -1
- package/lib/apps/polyfill.d.ts.map +1 -1
- package/lib/apps/polyfill.js +3 -0
- package/lib/apps/polyfill.js.map +1 -1
- package/lib/currencies/support.d.ts.map +1 -1
- package/lib/currencies/support.js.map +1 -1
- package/lib/families/bitcoin/bech32m.d.ts +22 -0
- package/lib/families/bitcoin/bech32m.d.ts.map +1 -0
- package/lib/families/bitcoin/bech32m.js +166 -0
- package/lib/families/bitcoin/bech32m.js.map +1 -0
- package/lib/families/bitcoin/js-buildTransaction.d.ts.map +1 -1
- package/lib/families/bitcoin/js-buildTransaction.js +1 -1
- package/lib/families/bitcoin/js-buildTransaction.js.map +1 -1
- package/lib/families/bitcoin/js-estimateMaxSpendable.d.ts.map +1 -1
- package/lib/families/bitcoin/js-estimateMaxSpendable.js +1 -1
- package/lib/families/bitcoin/js-estimateMaxSpendable.js.map +1 -1
- package/lib/families/bitcoin/js-synchronisation.d.ts.map +1 -1
- package/lib/families/bitcoin/js-synchronisation.js +3 -2
- package/lib/families/bitcoin/js-synchronisation.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.js +11 -11
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.js +2 -1
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/digibyte.js +21 -2
- package/lib/families/bitcoin/wallet-btc/crypto/digibyte.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/litecoin.js +2 -2
- package/lib/families/bitcoin/wallet-btc/crypto/litecoin.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/zec.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/zec.js +3 -2
- package/lib/families/bitcoin/wallet-btc/crypto/zec.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/zen.d.ts +2 -1
- package/lib/families/bitcoin/wallet-btc/crypto/zen.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/zen.js +7 -4
- package/lib/families/bitcoin/wallet-btc/crypto/zen.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.d.ts +2 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.js +16 -12
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.d.ts +2 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.js +12 -7
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.d.ts +2 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.js +12 -7
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/types.d.ts +2 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/types.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/types.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/utils.d.ts +38 -2
- package/lib/families/bitcoin/wallet-btc/utils.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/utils.js +103 -35
- package/lib/families/bitcoin/wallet-btc/utils.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/wallet.d.ts +1 -1
- package/lib/families/bitcoin/wallet-btc/wallet.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/wallet.js +9 -8
- package/lib/families/bitcoin/wallet-btc/wallet.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/xpub.js +2 -2
- package/lib/families/bitcoin/wallet-btc/xpub.js.map +1 -1
- package/lib/families/elrond/logic.js +22 -3
- package/lib/families/elrond/logic.js.map +1 -1
- package/lib/operation.js +1 -1
- package/lib/operation.js.map +1 -1
- package/package.json +16 -16
- package/src/.DS_Store +0 -0
- package/src/__tests__/__snapshots__/all.libcore.ts.snap +1188 -630
- package/src/__tests__/families/bitcoin/wallet-btc/utils.test.ts +109 -2
- package/src/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.ts +3 -2
- package/src/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.ts +2 -2
- package/src/__tests__/families/bitcoin/wallet-btc/xpub.pickingStrategies.integration.test.ts +35 -31
- package/src/__tests__/families/bitcoin/wallet-btc/xpub.txs.NP2WPKH.integration.test.ts +3 -2
- package/src/__tests__/families/bitcoin/wallet-btc/xpub.txs.P2WPKH.integration.test.ts +3 -2
- package/src/__tests__/families/bitcoin/wallet-btc/xpub.txs.integration.test.ts +6 -4
- package/src/apps/polyfill.ts +3 -0
- package/src/currencies/.DS_Store +0 -0
- package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +68 -1325
- package/src/currencies/support.ts +1 -0
- package/src/families/bitcoin/bech32m.ts +197 -0
- package/src/families/bitcoin/js-buildTransaction.ts +2 -1
- package/src/families/bitcoin/js-estimateMaxSpendable.ts +2 -1
- package/src/families/bitcoin/js-synchronisation.ts +4 -2
- package/src/families/bitcoin/wallet-btc/crypto/bitcoin.ts +5 -4
- package/src/families/bitcoin/wallet-btc/crypto/bitcoincash.ts +2 -1
- package/src/families/bitcoin/wallet-btc/crypto/digibyte.ts +1 -1
- package/src/families/bitcoin/wallet-btc/crypto/litecoin.ts +1 -1
- package/src/families/bitcoin/wallet-btc/crypto/zec.ts +3 -2
- package/src/families/bitcoin/wallet-btc/crypto/zen.ts +10 -4
- package/src/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.ts +34 -22
- package/src/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.ts +30 -9
- package/src/families/bitcoin/wallet-btc/pickingstrategies/Merge.ts +30 -9
- package/src/families/bitcoin/wallet-btc/pickingstrategies/types.ts +3 -3
- package/src/families/bitcoin/wallet-btc/utils.ts +113 -41
- package/src/families/bitcoin/wallet-btc/wallet.ts +10 -8
- package/src/families/bitcoin/wallet-btc/xpub.ts +5 -5
- package/src/families/elrond/logic.ts +1 -1
- package/src/operation.ts +1 -1
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
hasCryptoCurrencyId,
|
|
10
10
|
} from "@ledgerhq/cryptoassets";
|
|
11
11
|
import { getEnv } from "../env";
|
|
12
|
+
|
|
12
13
|
// set by user side effect to precise which currencies are considered supported (typically by live)
|
|
13
14
|
let userSupportedCurrencies: CryptoCurrency[] = [];
|
|
14
15
|
let userSupportedFiats: FiatCurrency[] = [];
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// Ported from https://github.com/bitcoinjs/bech32/tree/605655d6b37a782345549cd1388db688a96ad56f
|
|
2
|
+
// until we can use bech32 2.0.0
|
|
3
|
+
// We can't directly use bech32 2.0.0 because many of our dependencies are still using bech32 ^1.1.3
|
|
4
|
+
// which conflict on mobile (root cause of https://ledgerhq.atlassian.net/browse/LL-7930)
|
|
5
|
+
// FIXME Remove that file and use bech32 2.0.0 when all dependencies also uses it.
|
|
6
|
+
|
|
7
|
+
const ENCODING_CONST = 0x2bc830a3;
|
|
8
|
+
const ALPHABET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
9
|
+
const ALPHABET_MAP: { [key: string]: number } = {};
|
|
10
|
+
for (let z = 0; z < ALPHABET.length; z++) {
|
|
11
|
+
const x = ALPHABET.charAt(z);
|
|
12
|
+
ALPHABET_MAP[x] = z;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function prefixChk(prefix) {
|
|
16
|
+
let chk = 1;
|
|
17
|
+
for (let i = 0; i < prefix.length; ++i) {
|
|
18
|
+
const c = prefix.charCodeAt(i);
|
|
19
|
+
if (c < 33 || c > 126) return "Invalid prefix (" + prefix + ")";
|
|
20
|
+
chk = polymodStep(chk) ^ (c >> 5);
|
|
21
|
+
}
|
|
22
|
+
chk = polymodStep(chk);
|
|
23
|
+
for (let i = 0; i < prefix.length; ++i) {
|
|
24
|
+
const v = prefix.charCodeAt(i);
|
|
25
|
+
chk = polymodStep(chk) ^ (v & 0x1f);
|
|
26
|
+
}
|
|
27
|
+
return chk;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function polymodStep(pre: number): number {
|
|
31
|
+
const b = pre >> 25;
|
|
32
|
+
return (
|
|
33
|
+
((pre & 0x1ffffff) << 5) ^
|
|
34
|
+
(-((b >> 0) & 1) & 0x3b6a57b2) ^
|
|
35
|
+
(-((b >> 1) & 1) & 0x26508e6d) ^
|
|
36
|
+
(-((b >> 2) & 1) & 0x1ea119fa) ^
|
|
37
|
+
(-((b >> 3) & 1) & 0x3d4233dd) ^
|
|
38
|
+
(-((b >> 4) & 1) & 0x2a1462b3)
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function encode(
|
|
43
|
+
prefix: string,
|
|
44
|
+
words: ArrayLike<number>,
|
|
45
|
+
LIMIT?: number
|
|
46
|
+
): string {
|
|
47
|
+
LIMIT = LIMIT || 90;
|
|
48
|
+
if (prefix.length + 7 + words.length > LIMIT)
|
|
49
|
+
throw new TypeError("Exceeds length limit");
|
|
50
|
+
|
|
51
|
+
prefix = prefix.toLowerCase();
|
|
52
|
+
|
|
53
|
+
// determine chk mod
|
|
54
|
+
let chk = prefixChk(prefix);
|
|
55
|
+
if (typeof chk === "string") throw new Error(chk);
|
|
56
|
+
|
|
57
|
+
let result = prefix + "1";
|
|
58
|
+
for (let i = 0; i < words.length; ++i) {
|
|
59
|
+
const x = words[i];
|
|
60
|
+
if (x >> 5 !== 0) throw new Error("Non 5-bit word");
|
|
61
|
+
|
|
62
|
+
chk = polymodStep(chk) ^ x;
|
|
63
|
+
result += ALPHABET.charAt(x);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (let i = 0; i < 6; ++i) {
|
|
67
|
+
chk = polymodStep(chk);
|
|
68
|
+
}
|
|
69
|
+
chk ^= ENCODING_CONST;
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i < 6; ++i) {
|
|
72
|
+
const v = (chk >> ((5 - i) * 5)) & 0x1f;
|
|
73
|
+
result += ALPHABET.charAt(v);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function __decode(str: string, LIMIT?: number) {
|
|
80
|
+
LIMIT = LIMIT || 90;
|
|
81
|
+
if (str.length < 8) return str + " too short";
|
|
82
|
+
if (str.length > LIMIT) return "Exceeds length limit";
|
|
83
|
+
|
|
84
|
+
// don't allow mixed case
|
|
85
|
+
const lowered = str.toLowerCase();
|
|
86
|
+
const uppered = str.toUpperCase();
|
|
87
|
+
if (str !== lowered && str !== uppered) return "Mixed-case string " + str;
|
|
88
|
+
str = lowered;
|
|
89
|
+
|
|
90
|
+
const split = str.lastIndexOf("1");
|
|
91
|
+
if (split === -1) return "No separator character for " + str;
|
|
92
|
+
if (split === 0) return "Missing prefix for " + str;
|
|
93
|
+
|
|
94
|
+
const prefix = str.slice(0, split);
|
|
95
|
+
const wordChars = str.slice(split + 1);
|
|
96
|
+
if (wordChars.length < 6) return "Data too short";
|
|
97
|
+
|
|
98
|
+
let chk = prefixChk(prefix);
|
|
99
|
+
if (typeof chk === "string") return chk;
|
|
100
|
+
|
|
101
|
+
const words: number[] = [];
|
|
102
|
+
for (let i = 0; i < wordChars.length; ++i) {
|
|
103
|
+
const c = wordChars.charAt(i);
|
|
104
|
+
const v = ALPHABET_MAP[c];
|
|
105
|
+
if (v === undefined) return "Unknown character " + c;
|
|
106
|
+
chk = polymodStep(chk) ^ v;
|
|
107
|
+
|
|
108
|
+
// not in the checksum?
|
|
109
|
+
if (i + 6 >= wordChars.length) continue;
|
|
110
|
+
words.push(v);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (chk !== ENCODING_CONST) return "Invalid checksum for " + str;
|
|
114
|
+
return { prefix, words };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function decodeUnsafe(str: string, LIMIT?: number) {
|
|
118
|
+
const res = __decode(str, LIMIT);
|
|
119
|
+
if (typeof res === "object") return res;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function decode(str: string, LIMIT?: number) {
|
|
123
|
+
const res = __decode(str, LIMIT);
|
|
124
|
+
if (typeof res === "object") return res;
|
|
125
|
+
|
|
126
|
+
throw new Error(res);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function convert(
|
|
130
|
+
data: ArrayLike<number>,
|
|
131
|
+
inBits: number,
|
|
132
|
+
outBits: number,
|
|
133
|
+
pad: true
|
|
134
|
+
): number[];
|
|
135
|
+
function convert(
|
|
136
|
+
data: ArrayLike<number>,
|
|
137
|
+
inBits: number,
|
|
138
|
+
outBits: number,
|
|
139
|
+
pad: false
|
|
140
|
+
): number[] | string;
|
|
141
|
+
function convert(
|
|
142
|
+
data: ArrayLike<number>,
|
|
143
|
+
inBits: number,
|
|
144
|
+
outBits: number,
|
|
145
|
+
pad: boolean
|
|
146
|
+
): number[] | string {
|
|
147
|
+
let value = 0;
|
|
148
|
+
let bits = 0;
|
|
149
|
+
const maxV = (1 << outBits) - 1;
|
|
150
|
+
|
|
151
|
+
const result: number[] = [];
|
|
152
|
+
for (let i = 0; i < data.length; ++i) {
|
|
153
|
+
value = (value << inBits) | data[i];
|
|
154
|
+
bits += inBits;
|
|
155
|
+
|
|
156
|
+
while (bits >= outBits) {
|
|
157
|
+
bits -= outBits;
|
|
158
|
+
result.push((value >> bits) & maxV);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (pad) {
|
|
163
|
+
if (bits > 0) {
|
|
164
|
+
result.push((value << (outBits - bits)) & maxV);
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
if (bits >= inBits) return "Excess padding";
|
|
168
|
+
if ((value << (outBits - bits)) & maxV) return "Non-zero padding";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function toWords(bytes: ArrayLike<number>): number[] {
|
|
175
|
+
return convert(bytes, 8, 5, true);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function fromWordsUnsafe(words: ArrayLike<number>): number[] | undefined {
|
|
179
|
+
const res = convert(words, 5, 8, false);
|
|
180
|
+
if (Array.isArray(res)) return res;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function fromWords(words: ArrayLike<number>): number[] {
|
|
184
|
+
const res = convert(words, 5, 8, false);
|
|
185
|
+
if (Array.isArray(res)) return res;
|
|
186
|
+
|
|
187
|
+
throw new Error(res);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export const bech32m = {
|
|
191
|
+
decodeUnsafe,
|
|
192
|
+
decode,
|
|
193
|
+
encode,
|
|
194
|
+
toWords,
|
|
195
|
+
fromWordsUnsafe,
|
|
196
|
+
fromWords,
|
|
197
|
+
};
|
|
@@ -44,7 +44,8 @@ export const buildTransaction = async (
|
|
|
44
44
|
walletAccount,
|
|
45
45
|
transaction.feePerByte.toNumber(), //!\ wallet-btc handles fees as JS number
|
|
46
46
|
transaction.utxoStrategy.excludeUTXOs,
|
|
47
|
-
transaction.utxoStrategy.pickUnconfirmedRBF
|
|
47
|
+
transaction.utxoStrategy.pickUnconfirmedRBF,
|
|
48
|
+
[transaction.recipient]
|
|
48
49
|
);
|
|
49
50
|
log("btcwallet", "building transaction", transaction);
|
|
50
51
|
const txInfo = await wallet.buildAccountTx({
|
|
@@ -30,7 +30,8 @@ const estimateMaxSpendable = async ({
|
|
|
30
30
|
walletAccount,
|
|
31
31
|
feePerByte.toNumber(), //!\ wallet-btc handles fees as JS number
|
|
32
32
|
transaction?.utxoStrategy?.excludeUTXOs || [],
|
|
33
|
-
transaction?.utxoStrategy?.pickUnconfirmedRBF || false
|
|
33
|
+
transaction?.utxoStrategy?.pickUnconfirmedRBF || false,
|
|
34
|
+
transaction ? [transaction.recipient] : []
|
|
34
35
|
);
|
|
35
36
|
return maxSpendable.lt(0) ? new BigNumber(0) : maxSpendable;
|
|
36
37
|
};
|
|
@@ -139,8 +139,10 @@ const mapTxToOperations = (
|
|
|
139
139
|
if (output.address) {
|
|
140
140
|
if (!accountAddresses.includes(output.address)) {
|
|
141
141
|
// The output doesn't belong to this account
|
|
142
|
-
if (
|
|
143
|
-
// The
|
|
142
|
+
if (
|
|
143
|
+
tx.outputs.length === 1 || // The transaction has only 1 output
|
|
144
|
+
output.output_index < changeOutputIndex // The output isn't the change output
|
|
145
|
+
) {
|
|
144
146
|
recipients.push(
|
|
145
147
|
syncReplaceAddress
|
|
146
148
|
? syncReplaceAddress(output.address)
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
// from https://github.com/LedgerHQ/xpub-scan/blob/master/src/actions/deriveAddresses.ts
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
2
|
+
|
|
3
|
+
import * as bech32 from "bech32";
|
|
4
|
+
import { bech32m } from "../../bech32m";
|
|
5
5
|
import * as bjs from "bitcoinjs-lib";
|
|
6
6
|
import { publicKeyTweakAdd } from "secp256k1";
|
|
7
|
+
import { InvalidAddress } from "@ledgerhq/errors";
|
|
7
8
|
import { DerivationModes } from "../types";
|
|
8
9
|
import Base from "./base";
|
|
9
10
|
|
|
@@ -87,7 +88,7 @@ class Bitcoin extends Base {
|
|
|
87
88
|
// Make sure the address is valid on this network
|
|
88
89
|
// otherwise we can't call toOutputScriptTemporary.
|
|
89
90
|
if (!this.validateAddress(address)) {
|
|
90
|
-
throw new
|
|
91
|
+
throw new InvalidAddress();
|
|
91
92
|
}
|
|
92
93
|
// bitcoinjs-lib/src/address doesn't yet have released support for bech32m,
|
|
93
94
|
// so we'll implement our own version of toOutputScript while waiting.
|
|
@@ -5,6 +5,7 @@ import bchaddr from "bchaddrjs";
|
|
|
5
5
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
6
6
|
// @ts-ignore
|
|
7
7
|
import { toOutputScript } from "bitcoinjs-lib/src/address";
|
|
8
|
+
import { InvalidAddress } from "@ledgerhq/errors";
|
|
8
9
|
import { DerivationModes } from "../types";
|
|
9
10
|
import { ICrypto } from "./types";
|
|
10
11
|
|
|
@@ -60,7 +61,7 @@ class BitcoinCash implements ICrypto {
|
|
|
60
61
|
|
|
61
62
|
toOutputScript(address: string) {
|
|
62
63
|
if (!this.validateAddress(address)) {
|
|
63
|
-
throw new
|
|
64
|
+
throw new InvalidAddress();
|
|
64
65
|
}
|
|
65
66
|
// TODO find a better way to calculate the script from bch address instead of converting to bitcoin address
|
|
66
67
|
return toOutputScript(bchaddr.toLegacyAddress(address), this.network);
|
|
@@ -3,7 +3,7 @@ import * as bip32 from "bip32";
|
|
|
3
3
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
4
4
|
// @ts-ignore
|
|
5
5
|
import { toOutputScript } from "bitcoinjs-lib/src/address";
|
|
6
|
-
import
|
|
6
|
+
import * as bech32 from "bech32";
|
|
7
7
|
import { DerivationModes } from "../types";
|
|
8
8
|
import { ICrypto } from "./types";
|
|
9
9
|
import Base from "./base";
|
|
@@ -5,9 +5,10 @@ import { toOutputScript } from "bitcoinjs-lib/src/address";
|
|
|
5
5
|
// @ts-ignore
|
|
6
6
|
import zec from "zcash-bitcore-lib";
|
|
7
7
|
import bs58check from "bs58check";
|
|
8
|
+
import coininfo from "coininfo";
|
|
9
|
+
import { InvalidAddress } from "@ledgerhq/errors";
|
|
8
10
|
import { DerivationModes } from "../types";
|
|
9
11
|
import { ICrypto } from "./types";
|
|
10
|
-
import coininfo from "coininfo";
|
|
11
12
|
|
|
12
13
|
class ZCash implements ICrypto {
|
|
13
14
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -61,7 +62,7 @@ class ZCash implements ICrypto {
|
|
|
61
62
|
|
|
62
63
|
toOutputScript(address: string) {
|
|
63
64
|
if (!this.validateAddress(address)) {
|
|
64
|
-
throw new
|
|
65
|
+
throw new InvalidAddress();
|
|
65
66
|
}
|
|
66
67
|
// TODO find a better way to calculate the script from zec address instead of converting to bitcoin address
|
|
67
68
|
return toOutputScript(
|
|
@@ -5,9 +5,10 @@ import { toOutputScript } from "bitcoinjs-lib/src/address";
|
|
|
5
5
|
// @ts-ignore
|
|
6
6
|
import zec from "zcash-bitcore-lib";
|
|
7
7
|
import bs58check from "bs58check";
|
|
8
|
+
import coininfo from "coininfo";
|
|
9
|
+
import { InvalidAddress } from "@ledgerhq/errors";
|
|
8
10
|
import { DerivationModes } from "../types";
|
|
9
11
|
import { ICrypto } from "./types";
|
|
10
|
-
import coininfo from "coininfo";
|
|
11
12
|
|
|
12
13
|
class Zen implements ICrypto {
|
|
13
14
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -81,13 +82,18 @@ class Zen implements ICrypto {
|
|
|
81
82
|
|
|
82
83
|
toOutputScript(address: string) {
|
|
83
84
|
if (!this.validateAddress(address)) {
|
|
84
|
-
throw new
|
|
85
|
+
throw new InvalidAddress();
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
-
return toOutputScript(
|
|
87
|
+
const outputScript = toOutputScript(
|
|
88
88
|
Zen.toBitcoinAddr(address),
|
|
89
89
|
coininfo.bitcoin.main.toBitcoinJS()
|
|
90
90
|
);
|
|
91
|
+
// refer to https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/scripts/BitcoinLikeScript.cpp#L139 and https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/networks.cpp#L39 for bip115 Script and its network parameters
|
|
92
|
+
const bip115Script = Buffer.from(
|
|
93
|
+
"209ec9845acb02fab24e1c0368b3b517c1a4488fba97f0e3459ac053ea0100000003c01f02b4",
|
|
94
|
+
"hex"
|
|
95
|
+
);
|
|
96
|
+
return Buffer.concat([outputScript, bip115Script]);
|
|
91
97
|
}
|
|
92
98
|
|
|
93
99
|
// eslint-disable-next-line class-methods-use-this
|
|
@@ -7,14 +7,14 @@ import { PickingStrategy } from "./types";
|
|
|
7
7
|
import * as utils from "../utils";
|
|
8
8
|
import { DeepFirst } from "./DeepFirst";
|
|
9
9
|
import { log } from "@ledgerhq/logs";
|
|
10
|
+
import { OutputInfo } from "..";
|
|
10
11
|
|
|
11
12
|
export class CoinSelect extends PickingStrategy {
|
|
12
13
|
// eslint-disable-next-line class-methods-use-this
|
|
13
14
|
async selectUnspentUtxosToUse(
|
|
14
15
|
xpub: Xpub,
|
|
15
|
-
|
|
16
|
-
feePerByte: number
|
|
17
|
-
nbOutputsWithoutChange: number
|
|
16
|
+
outputs: OutputInfo[],
|
|
17
|
+
feePerByte: number
|
|
18
18
|
) {
|
|
19
19
|
// get the utxos to use as input
|
|
20
20
|
// from all addresses of the account
|
|
@@ -34,18 +34,30 @@ export class CoinSelect extends PickingStrategy {
|
|
|
34
34
|
const TOTAL_TRIES = 100000;
|
|
35
35
|
log("picking strategy", "utxos", unspentUtxos);
|
|
36
36
|
// Compute cost of change
|
|
37
|
-
const fixedSize = utils.
|
|
38
|
-
0,
|
|
37
|
+
const fixedSize = utils.maxTxSize(
|
|
39
38
|
0,
|
|
39
|
+
[],
|
|
40
|
+
false,
|
|
40
41
|
this.crypto,
|
|
41
42
|
this.derivationMode
|
|
42
43
|
);
|
|
44
|
+
const outputAddresses = outputs.map((o) => o.address);
|
|
43
45
|
// Size of only 1 output (without fixed size)
|
|
44
46
|
const oneOutputSize =
|
|
45
|
-
|
|
47
|
+
outputAddresses.length > 0
|
|
48
|
+
? utils.maxTxSize(
|
|
49
|
+
0,
|
|
50
|
+
[outputAddresses[0]],
|
|
51
|
+
false,
|
|
52
|
+
this.crypto,
|
|
53
|
+
this.derivationMode
|
|
54
|
+
) - fixedSize
|
|
55
|
+
: utils.maxTxSize(0, [], true, this.crypto, this.derivationMode) -
|
|
56
|
+
fixedSize;
|
|
46
57
|
// Size 1 signed UTXO (signed input)
|
|
47
58
|
const oneInputSize =
|
|
48
|
-
utils.
|
|
59
|
+
utils.maxTxSize(1, [], false, this.crypto, this.derivationMode) -
|
|
60
|
+
fixedSize;
|
|
49
61
|
|
|
50
62
|
// Calculate effective value of outputs
|
|
51
63
|
let currentAvailableValue = 0;
|
|
@@ -70,12 +82,16 @@ export class CoinSelect extends PickingStrategy {
|
|
|
70
82
|
// Get no inputs fees
|
|
71
83
|
// At beginning, there are no outputs in tx, so noInputFees are fixed fees
|
|
72
84
|
const notInputFees =
|
|
73
|
-
feePerByte * (fixedSize + oneOutputSize *
|
|
85
|
+
feePerByte * (fixedSize + oneOutputSize * outputs.length);
|
|
74
86
|
|
|
75
87
|
// Start coin selection algorithm (according to SelectCoinBnb from Bitcoin Core)
|
|
76
88
|
let currentValue = 0;
|
|
77
89
|
const currentSelection: boolean[] = [];
|
|
78
90
|
|
|
91
|
+
const amount = outputs.reduce(
|
|
92
|
+
(sum, output) => sum.plus(output.value),
|
|
93
|
+
new BigNumber(0)
|
|
94
|
+
);
|
|
79
95
|
// Actual amount we are targetting
|
|
80
96
|
const actualTarget = notInputFees + amount.toNumber();
|
|
81
97
|
|
|
@@ -102,9 +118,10 @@ export class CoinSelect extends PickingStrategy {
|
|
|
102
118
|
currentSelectionNeedChangeoutput = true;
|
|
103
119
|
currentWaste =
|
|
104
120
|
feePerByte *
|
|
105
|
-
utils.
|
|
121
|
+
utils.maxTxSizeCeil(
|
|
106
122
|
nbInput,
|
|
107
|
-
|
|
123
|
+
outputAddresses,
|
|
124
|
+
true,
|
|
108
125
|
this.crypto,
|
|
109
126
|
this.derivationMode
|
|
110
127
|
);
|
|
@@ -113,9 +130,10 @@ export class CoinSelect extends PickingStrategy {
|
|
|
113
130
|
currentSelectionNeedChangeoutput = false;
|
|
114
131
|
currentWaste =
|
|
115
132
|
feePerByte *
|
|
116
|
-
utils.
|
|
133
|
+
utils.maxTxSizeCeil(
|
|
117
134
|
nbInput,
|
|
118
|
-
|
|
135
|
+
outputAddresses,
|
|
136
|
+
false,
|
|
119
137
|
this.crypto,
|
|
120
138
|
this.derivationMode
|
|
121
139
|
) +
|
|
@@ -170,11 +188,10 @@ export class CoinSelect extends PickingStrategy {
|
|
|
170
188
|
}
|
|
171
189
|
const fee =
|
|
172
190
|
feePerByte *
|
|
173
|
-
utils.
|
|
191
|
+
utils.maxTxSizeCeil(
|
|
174
192
|
unspentUtxoSelected.length,
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
: nbOutputsWithoutChange,
|
|
193
|
+
outputAddresses,
|
|
194
|
+
bestSelectionNeedChangeoutput,
|
|
178
195
|
this.crypto,
|
|
179
196
|
this.derivationMode
|
|
180
197
|
);
|
|
@@ -191,11 +208,6 @@ export class CoinSelect extends PickingStrategy {
|
|
|
191
208
|
this.derivationMode,
|
|
192
209
|
this.excludedUTXOs
|
|
193
210
|
);
|
|
194
|
-
return pickingStrategy.selectUnspentUtxosToUse(
|
|
195
|
-
xpub,
|
|
196
|
-
amount,
|
|
197
|
-
feePerByte,
|
|
198
|
-
nbOutputsWithoutChange
|
|
199
|
-
);
|
|
211
|
+
return pickingStrategy.selectUnspentUtxosToUse(xpub, outputs, feePerByte);
|
|
200
212
|
}
|
|
201
213
|
}
|
|
@@ -6,14 +6,14 @@ import Xpub from "../xpub";
|
|
|
6
6
|
import { PickingStrategy } from "./types";
|
|
7
7
|
import * as utils from "../utils";
|
|
8
8
|
import { log } from "@ledgerhq/logs";
|
|
9
|
+
import { OutputInfo } from "..";
|
|
9
10
|
|
|
10
11
|
export class DeepFirst extends PickingStrategy {
|
|
11
12
|
// eslint-disable-next-line class-methods-use-this
|
|
12
13
|
async selectUnspentUtxosToUse(
|
|
13
14
|
xpub: Xpub,
|
|
14
|
-
|
|
15
|
-
feePerByte: number
|
|
16
|
-
nbOutputsWithoutChange: number
|
|
15
|
+
outputs: OutputInfo[],
|
|
16
|
+
feePerByte: number
|
|
17
17
|
) {
|
|
18
18
|
// get the utxos to use as input
|
|
19
19
|
// from all addresses of the account
|
|
@@ -31,26 +31,47 @@ export class DeepFirst extends PickingStrategy {
|
|
|
31
31
|
).length
|
|
32
32
|
);
|
|
33
33
|
|
|
34
|
+
const outputAddresses = outputs.map((o) => o.address);
|
|
34
35
|
unspentUtxos = sortBy(unspentUtxos, "block_height");
|
|
35
36
|
// https://metamug.com/article/security/bitcoin-transaction-fee-satoshi-per-byte.html
|
|
36
|
-
const txSizeNoInput = utils.
|
|
37
|
+
const txSizeNoInput = utils.maxTxSize(
|
|
37
38
|
0,
|
|
38
|
-
|
|
39
|
+
outputAddresses,
|
|
40
|
+
false,
|
|
39
41
|
this.crypto,
|
|
40
42
|
this.derivationMode
|
|
41
43
|
);
|
|
42
44
|
let fee = txSizeNoInput * feePerByte;
|
|
45
|
+
const emptyTxSize = utils.maxTxSizeCeil(
|
|
46
|
+
0,
|
|
47
|
+
[],
|
|
48
|
+
false,
|
|
49
|
+
this.crypto,
|
|
50
|
+
this.derivationMode
|
|
51
|
+
);
|
|
43
52
|
const sizePerInput =
|
|
44
|
-
utils.
|
|
45
|
-
|
|
53
|
+
utils.maxTxSize(1, [], false, this.crypto, this.derivationMode) -
|
|
54
|
+
emptyTxSize;
|
|
46
55
|
|
|
47
56
|
const sizePerOutput =
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
(outputAddresses[0]
|
|
58
|
+
? utils.maxTxSize(
|
|
59
|
+
0,
|
|
60
|
+
[outputAddresses[0]],
|
|
61
|
+
false,
|
|
62
|
+
this.crypto,
|
|
63
|
+
this.derivationMode
|
|
64
|
+
)
|
|
65
|
+
: utils.maxTxSize(0, [], true, this.crypto, this.derivationMode)) -
|
|
66
|
+
emptyTxSize;
|
|
50
67
|
|
|
51
68
|
let total = new BigNumber(0);
|
|
52
69
|
const unspentUtxoSelected: Output[] = [];
|
|
53
70
|
|
|
71
|
+
const amount = outputs.reduce(
|
|
72
|
+
(sum, output) => sum.plus(output.value),
|
|
73
|
+
new BigNumber(0)
|
|
74
|
+
);
|
|
54
75
|
let i = 0;
|
|
55
76
|
while (total.lt(amount.plus(fee))) {
|
|
56
77
|
if (!unspentUtxos[i]) {
|
|
@@ -6,14 +6,14 @@ import Xpub from "../xpub";
|
|
|
6
6
|
import { PickingStrategy } from "./types";
|
|
7
7
|
import * as utils from "../utils";
|
|
8
8
|
import { log } from "@ledgerhq/logs";
|
|
9
|
+
import { OutputInfo } from "..";
|
|
9
10
|
|
|
10
11
|
export class Merge extends PickingStrategy {
|
|
11
12
|
// eslint-disable-next-line class-methods-use-this
|
|
12
13
|
async selectUnspentUtxosToUse(
|
|
13
14
|
xpub: Xpub,
|
|
14
|
-
|
|
15
|
-
feePerByte: number
|
|
16
|
-
nbOutputsWithoutChange: number
|
|
15
|
+
outputs: OutputInfo[],
|
|
16
|
+
feePerByte: number
|
|
17
17
|
) {
|
|
18
18
|
// get the utxos to use as input
|
|
19
19
|
// from all addresses of the account
|
|
@@ -31,19 +31,36 @@ export class Merge extends PickingStrategy {
|
|
|
31
31
|
).length
|
|
32
32
|
);
|
|
33
33
|
|
|
34
|
+
const outputAddresses = outputs.map((o) => o.address);
|
|
35
|
+
const emptyTxSize = utils.maxTxSizeCeil(
|
|
36
|
+
0,
|
|
37
|
+
[],
|
|
38
|
+
false,
|
|
39
|
+
this.crypto,
|
|
40
|
+
this.derivationMode
|
|
41
|
+
);
|
|
34
42
|
const sizePerInput =
|
|
35
|
-
utils.
|
|
36
|
-
|
|
43
|
+
utils.maxTxSize(1, [], false, this.crypto, this.derivationMode) -
|
|
44
|
+
emptyTxSize;
|
|
37
45
|
|
|
38
46
|
const sizePerOutput =
|
|
39
|
-
|
|
40
|
-
|
|
47
|
+
(outputAddresses[0]
|
|
48
|
+
? utils.maxTxSize(
|
|
49
|
+
0,
|
|
50
|
+
[outputAddresses[0]],
|
|
51
|
+
false,
|
|
52
|
+
this.crypto,
|
|
53
|
+
this.derivationMode
|
|
54
|
+
)
|
|
55
|
+
: utils.maxTxSize(0, [], true, this.crypto, this.derivationMode)) -
|
|
56
|
+
emptyTxSize;
|
|
41
57
|
|
|
42
58
|
unspentUtxos = sortBy(unspentUtxos, (utxo) => parseInt(utxo.value, 10));
|
|
43
59
|
// https://metamug.com/article/security/bitcoin-transaction-fee-satoshi-per-byte.html
|
|
44
|
-
const txSizeNoInput = utils.
|
|
60
|
+
const txSizeNoInput = utils.maxTxSize(
|
|
45
61
|
0,
|
|
46
|
-
|
|
62
|
+
outputAddresses,
|
|
63
|
+
false,
|
|
47
64
|
this.crypto,
|
|
48
65
|
this.derivationMode
|
|
49
66
|
);
|
|
@@ -53,6 +70,10 @@ export class Merge extends PickingStrategy {
|
|
|
53
70
|
const unspentUtxoSelected: Output[] = [];
|
|
54
71
|
|
|
55
72
|
let i = 0;
|
|
73
|
+
const amount = outputs.reduce(
|
|
74
|
+
(sum, output) => sum.plus(output.value),
|
|
75
|
+
new BigNumber(0)
|
|
76
|
+
);
|
|
56
77
|
while (total.lt(amount.plus(fee))) {
|
|
57
78
|
if (!unspentUtxos[i]) {
|
|
58
79
|
throw new NotEnoughBalance();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import BigNumber from "bignumber.js";
|
|
2
|
+
import { OutputInfo } from "..";
|
|
2
3
|
import { ICrypto } from "../crypto/types";
|
|
3
4
|
import { Output } from "../storage/types";
|
|
4
5
|
import Xpub from "../xpub";
|
|
@@ -29,9 +30,8 @@ export abstract class PickingStrategy {
|
|
|
29
30
|
|
|
30
31
|
abstract selectUnspentUtxosToUse(
|
|
31
32
|
xpub: Xpub,
|
|
32
|
-
|
|
33
|
-
feePerByte: number
|
|
34
|
-
nbOutputsWithoutChange: number
|
|
33
|
+
outputs: OutputInfo[],
|
|
34
|
+
feePerByte: number
|
|
35
35
|
): Promise<{
|
|
36
36
|
unspentUtxos: Output[];
|
|
37
37
|
totalValue: BigNumber;
|