@ledgerhq/live-common 21.13.2 → 21.16.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__/derivation.js +1 -1
- package/lib/__tests__/derivation.js.map +1 -1
- package/lib/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.js +2 -2
- package/lib/account/groupOperations.js +1 -1
- package/lib/account/groupOperations.js.map +1 -1
- package/lib/account/pending.d.ts +1 -0
- package/lib/account/pending.d.ts.map +1 -1
- package/lib/account/serialization.d.ts +6 -4
- package/lib/account/serialization.d.ts.map +1 -1
- package/lib/account/serialization.js +47 -9
- package/lib/account/serialization.js.map +1 -1
- package/lib/api/Ethereum.d.ts +22 -1
- package/lib/api/Ethereum.d.ts.map +1 -1
- package/lib/api/Ethereum.js +21 -1
- package/lib/api/Ethereum.js.map +1 -1
- package/lib/apps/polyfill.d.ts.map +1 -1
- package/lib/apps/polyfill.js +1 -0
- package/lib/apps/polyfill.js.map +1 -1
- package/lib/bridge/jsHelpers.d.ts +2 -1
- package/lib/bridge/jsHelpers.d.ts.map +1 -1
- package/lib/bridge/jsHelpers.js +27 -1
- package/lib/bridge/jsHelpers.js.map +1 -1
- package/lib/csvExport.d.ts.map +1 -1
- package/lib/csvExport.js +3 -1
- package/lib/csvExport.js.map +1 -1
- package/lib/currencies/sortByMarketcap.d.ts.map +1 -1
- package/lib/currencies/sortByMarketcap.js +8 -1
- package/lib/currencies/sortByMarketcap.js.map +1 -1
- package/lib/derivation.js +1 -1
- package/lib/derivation.js.map +1 -1
- package/lib/env.d.ts +15 -5
- package/lib/env.d.ts.map +1 -1
- package/lib/env.js +12 -1
- 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/cache.d.ts +1 -0
- package/lib/families/bitcoin/cache.d.ts.map +1 -1
- package/lib/families/bitcoin/cache.js +7 -1
- package/lib/families/bitcoin/cache.js.map +1 -1
- package/lib/families/bitcoin/errors.d.ts +4 -0
- package/lib/families/bitcoin/errors.d.ts.map +1 -0
- package/lib/families/bitcoin/errors.js +6 -0
- package/lib/families/bitcoin/errors.js.map +1 -0
- package/lib/families/bitcoin/js-getTransactionStatus.d.ts.map +1 -1
- package/lib/families/bitcoin/js-getTransactionStatus.js +24 -11
- package/lib/families/bitcoin/js-getTransactionStatus.js.map +1 -1
- package/lib/families/bitcoin/js-synchronisation.d.ts.map +1 -1
- package/lib/families/bitcoin/js-synchronisation.js +8 -19
- package/lib/families/bitcoin/js-synchronisation.js.map +1 -1
- package/lib/families/bitcoin/logic.d.ts +1 -1
- package/lib/families/bitcoin/logic.d.ts.map +1 -1
- package/lib/families/bitcoin/logic.js +6 -0
- package/lib/families/bitcoin/logic.js.map +1 -1
- package/lib/families/bitcoin/specs.d.ts.map +1 -1
- package/lib/families/bitcoin/specs.js +2 -1
- package/lib/families/bitcoin/specs.js.map +1 -1
- package/lib/families/bitcoin/test-dataset.js +2 -2
- package/lib/families/bitcoin/test-dataset.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/base.d.ts +4 -0
- package/lib/families/bitcoin/wallet-btc/crypto/base.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/base.js +36 -5
- package/lib/families/bitcoin/wallet-btc/crypto/base.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.d.ts +1 -0
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.js +19 -1
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoin.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.d.ts +1 -0
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.js +4 -0
- package/lib/families/bitcoin/wallet-btc/crypto/bitcoincash.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/types.d.ts +1 -0
- package/lib/families/bitcoin/wallet-btc/crypto/types.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/zec.d.ts +1 -0
- package/lib/families/bitcoin/wallet-btc/crypto/zec.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/zec.js +4 -0
- package/lib/families/bitcoin/wallet-btc/crypto/zec.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/zen.d.ts +1 -0
- package/lib/families/bitcoin/wallet-btc/crypto/zen.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/crypto/zen.js +4 -0
- package/lib/families/bitcoin/wallet-btc/crypto/zen.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/explorer/index.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/explorer/index.js +0 -3
- package/lib/families/bitcoin/wallet-btc/explorer/index.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/index.d.ts +2 -2
- package/lib/families/bitcoin/wallet-btc/index.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/index.js +4 -5
- package/lib/families/bitcoin/wallet-btc/index.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.js +3 -3
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.js +5 -5
- package/lib/families/bitcoin/wallet-btc/pickingstrategies/Merge.js +5 -5
- package/lib/families/bitcoin/wallet-btc/utils.d.ts +3 -1
- package/lib/families/bitcoin/wallet-btc/utils.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/utils.js +30 -10
- package/lib/families/bitcoin/wallet-btc/utils.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/wallet.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/wallet.js +5 -3
- package/lib/families/bitcoin/wallet-btc/wallet.js.map +1 -1
- package/lib/families/bitcoin/wallet-btc/xpub.d.ts +2 -6
- package/lib/families/bitcoin/wallet-btc/xpub.d.ts.map +1 -1
- package/lib/families/bitcoin/wallet-btc/xpub.js +62 -52
- package/lib/families/bitcoin/wallet-btc/xpub.js.map +1 -1
- package/lib/families/elrond/js-getTransactionStatus.d.ts.map +1 -1
- package/lib/families/elrond/js-getTransactionStatus.js +3 -0
- package/lib/families/elrond/js-getTransactionStatus.js.map +1 -1
- package/lib/families/elrond/js-signOperation.d.ts.map +1 -1
- package/lib/families/elrond/js-signOperation.js +3 -1
- package/lib/families/elrond/js-signOperation.js.map +1 -1
- package/lib/families/ethereum/signOperation.d.ts.map +1 -1
- package/lib/families/ethereum/signOperation.js +7 -0
- package/lib/families/ethereum/signOperation.js.map +1 -1
- package/lib/families/ethereum/specs.d.ts +1 -0
- package/lib/families/ethereum/specs.d.ts.map +1 -1
- package/lib/families/ethereum/specs.js +41 -0
- package/lib/families/ethereum/specs.js.map +1 -1
- package/lib/families/ethereum/synchronisation.d.ts.map +1 -1
- package/lib/families/ethereum/synchronisation.js +175 -15
- package/lib/families/ethereum/synchronisation.js.map +1 -1
- package/lib/families/ethereum/versions.d.ts +2 -0
- package/lib/families/ethereum/versions.d.ts.map +1 -0
- package/lib/families/ethereum/versions.js +5 -0
- package/lib/families/ethereum/versions.js.map +1 -0
- package/lib/generated/specs.d.ts +1 -0
- package/lib/generated/specs.d.ts.map +1 -1
- package/lib/nft/NftMetadataProvider/index.d.ts +10 -0
- package/lib/nft/NftMetadataProvider/index.d.ts.map +1 -0
- package/lib/nft/NftMetadataProvider/index.js +244 -0
- package/lib/nft/NftMetadataProvider/index.js.map +1 -0
- package/lib/nft/NftMetadataProvider/logic.d.ts +3 -0
- package/lib/nft/NftMetadataProvider/logic.d.ts.map +1 -0
- package/lib/nft/NftMetadataProvider/logic.js +22 -0
- package/lib/nft/NftMetadataProvider/logic.js.map +1 -0
- package/lib/nft/NftMetadataProvider/types.d.ts +44 -0
- package/lib/nft/NftMetadataProvider/types.d.ts.map +1 -0
- package/lib/nft/NftMetadataProvider/types.js +3 -0
- package/lib/nft/NftMetadataProvider/types.js.map +1 -0
- package/lib/nft/helpers.d.ts +10 -0
- package/lib/nft/helpers.d.ts.map +1 -0
- package/lib/nft/helpers.js +85 -0
- package/lib/nft/helpers.js.map +1 -0
- package/lib/nft/index.d.ts +4 -0
- package/lib/nft/index.d.ts.map +1 -0
- package/lib/nft/index.js +16 -0
- package/lib/nft/index.js.map +1 -0
- package/lib/nft/nftId.d.ts +3 -0
- package/lib/nft/nftId.d.ts.map +1 -0
- package/lib/nft/nftId.js +33 -0
- package/lib/nft/nftId.js.map +1 -0
- package/lib/operation.d.ts +1 -1
- package/lib/operation.d.ts.map +1 -1
- package/lib/operation.js +19 -5
- package/lib/operation.js.map +1 -1
- package/lib/reconciliation.d.ts.map +1 -1
- package/lib/reconciliation.js +6 -0
- package/lib/reconciliation.js.map +1 -1
- package/lib/types/account.d.ts +3 -0
- package/lib/types/account.d.ts.map +1 -1
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +1 -0
- package/lib/types/index.js.map +1 -1
- package/lib/types/nft.d.ts +29 -0
- package/lib/types/nft.d.ts.map +1 -0
- package/lib/types/nft.js +3 -0
- package/lib/types/nft.js.map +1 -0
- package/lib/types/operation.d.ts +12 -1
- package/lib/types/operation.d.ts.map +1 -1
- package/package.json +13 -13
- package/src/__tests__/__snapshots__/all.libcore.ts.snap +64428 -57815
- package/src/__tests__/derivation.ts +1 -1
- package/src/__tests__/families/bitcoin/wallet-btc/wallet.integration.test.ts +2 -2
- package/src/account/groupOperations.ts +2 -2
- package/src/account/serialization.ts +52 -0
- package/src/api/Ethereum.ts +40 -2
- package/src/apps/polyfill.ts +1 -0
- package/src/bridge/jsHelpers.ts +35 -0
- package/src/csvExport.ts +3 -2
- package/src/currencies/__snapshots__/sortByMarketcap.test.ts.snap +1361 -1
- package/src/currencies/sortByMarketcap.ts +9 -1
- package/src/derivation.ts +1 -1
- package/src/env.ts +12 -1
- package/src/families/bitcoin/cache.ts +12 -0
- package/src/families/bitcoin/errors.ts +5 -0
- package/src/families/bitcoin/js-getTransactionStatus.ts +15 -1
- package/src/families/bitcoin/js-synchronisation.ts +11 -25
- package/src/families/bitcoin/logic.ts +7 -2
- package/src/families/bitcoin/specs.ts +6 -1
- package/src/families/bitcoin/test-dataset.ts +2 -2
- package/src/families/bitcoin/wallet-btc/crypto/base.ts +40 -5
- package/src/families/bitcoin/wallet-btc/crypto/bitcoin.ts +21 -1
- package/src/families/bitcoin/wallet-btc/crypto/bitcoincash.ts +5 -0
- package/src/families/bitcoin/wallet-btc/crypto/types.ts +1 -0
- package/src/families/bitcoin/wallet-btc/crypto/zec.ts +5 -0
- package/src/families/bitcoin/wallet-btc/crypto/zen.ts +5 -0
- package/src/families/bitcoin/wallet-btc/explorer/index.ts +0 -5
- package/src/families/bitcoin/wallet-btc/index.ts +4 -4
- package/src/families/bitcoin/wallet-btc/pickingstrategies/CoinSelect.ts +3 -3
- package/src/families/bitcoin/wallet-btc/pickingstrategies/DeepFirst.ts +5 -5
- package/src/families/bitcoin/wallet-btc/pickingstrategies/Merge.ts +5 -5
- package/src/families/bitcoin/wallet-btc/utils.ts +34 -9
- package/src/families/bitcoin/wallet-btc/wallet.ts +4 -2
- package/src/families/bitcoin/wallet-btc/xpub.ts +37 -53
- package/src/families/elrond/js-getTransactionStatus.ts +4 -0
- package/src/families/elrond/js-signOperation.ts +3 -1
- package/src/families/ethereum/modules/__snapshots__/compound.resilience.test.ts.snap +2876 -0
- package/src/families/ethereum/signOperation.ts +8 -0
- package/src/families/ethereum/specs.ts +47 -0
- package/src/families/ethereum/synchronisation.ts +185 -6
- package/src/families/ethereum/versions.ts +1 -0
- package/src/nft/NftMetadataProvider/index.tsx +205 -0
- package/src/nft/NftMetadataProvider/logic.ts +20 -0
- package/src/nft/NftMetadataProvider/types.ts +60 -0
- package/src/nft/helpers.ts +78 -0
- package/src/nft/index.ts +3 -0
- package/src/nft/nftId.ts +17 -0
- package/src/operation.ts +22 -3
- package/src/reconciliation.ts +7 -0
- package/src/types/account.ts +4 -0
- package/src/types/index.ts +1 -0
- package/src/types/nft.ts +38 -0
- package/src/types/operation.ts +23 -1
|
@@ -81,7 +81,15 @@ export const getMarketcapTickers: () => Promise<string[]> = makeLRUCache(() =>
|
|
|
81
81
|
export const useMarketcapTickers = (): string[] | null | undefined => {
|
|
82
82
|
const [tickers, setMarketcapTickers] = useState(marketcapTickersCache);
|
|
83
83
|
useEffect(() => {
|
|
84
|
-
|
|
84
|
+
let isMounted = true;
|
|
85
|
+
|
|
86
|
+
getMarketcapTickers().then((data) => {
|
|
87
|
+
if (isMounted) setMarketcapTickers(data);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return () => {
|
|
91
|
+
isMounted = false;
|
|
92
|
+
};
|
|
85
93
|
}, []);
|
|
86
94
|
return tickers;
|
|
87
95
|
};
|
package/src/derivation.ts
CHANGED
|
@@ -470,7 +470,7 @@ export const getDerivationModesForCurrency = (
|
|
|
470
470
|
|
|
471
471
|
// taproot logic. FIXME should move per family
|
|
472
472
|
if (currency.family === "bitcoin") {
|
|
473
|
-
if (currency.id === "bitcoin_testnet") {
|
|
473
|
+
if (currency.id === "bitcoin" || currency.id === "bitcoin_testnet") {
|
|
474
474
|
all.push("taproot");
|
|
475
475
|
}
|
|
476
476
|
}
|
package/src/env.ts
CHANGED
|
@@ -67,7 +67,7 @@ const envDefinitions = {
|
|
|
67
67
|
},
|
|
68
68
|
API_RIPPLE_WS: {
|
|
69
69
|
parser: stringParser,
|
|
70
|
-
def: "wss://xrplcluster.com",
|
|
70
|
+
def: "wss://xrplcluster.com/ledgerlive",
|
|
71
71
|
desc: "XRP Ledger full history open WebSocket endpoint",
|
|
72
72
|
},
|
|
73
73
|
API_FILECOIN_ENDPOINT: {
|
|
@@ -376,6 +376,17 @@ const envDefinitions = {
|
|
|
376
376
|
parser: stringParser,
|
|
377
377
|
desc: "mock the server response for the exchange KYC check, options are 'open', 'pending', 'closed' or 'approved'.",
|
|
378
378
|
},
|
|
379
|
+
NFT: {
|
|
380
|
+
def: false,
|
|
381
|
+
parser: boolParser,
|
|
382
|
+
desc: "synchronizing nfts",
|
|
383
|
+
},
|
|
384
|
+
NFT_ETH_METADATA_SERVICE: {
|
|
385
|
+
// FIXME LL-8001
|
|
386
|
+
def: "https://nft.staging.aws.ledger.fr/v1/ethereum/1/contracts/tokens/infos",
|
|
387
|
+
parser: stringParser,
|
|
388
|
+
desc: "service uri used to get the metadata of an nft",
|
|
389
|
+
},
|
|
379
390
|
OPERATION_ADDRESSES_LIMIT: {
|
|
380
391
|
def: 100,
|
|
381
392
|
parser: intParser,
|
|
@@ -4,6 +4,7 @@ import type { Account, CryptoCurrency } from "./../../types";
|
|
|
4
4
|
import type { Transaction } from "./types";
|
|
5
5
|
import getFeesForTransaction from "./js-getFeesForTransaction";
|
|
6
6
|
import { isValidRecipient } from "./logic";
|
|
7
|
+
import { Currency, isTaprootAddress } from "./wallet-btc";
|
|
7
8
|
|
|
8
9
|
const getCacheKeyForCalculateFees = ({
|
|
9
10
|
a,
|
|
@@ -41,6 +42,7 @@ export const calculateFees = makeLRUCache(
|
|
|
41
42
|
maxAge: 5 * 60 * 1000, // 5 minutes
|
|
42
43
|
}
|
|
43
44
|
);
|
|
45
|
+
|
|
44
46
|
export const validateRecipient: (
|
|
45
47
|
arg0: CryptoCurrency,
|
|
46
48
|
arg1: string | null | undefined
|
|
@@ -74,3 +76,13 @@ export const validateRecipient: (
|
|
|
74
76
|
},
|
|
75
77
|
(currency, recipient) => `${currency.id}_${recipient || ""}`
|
|
76
78
|
);
|
|
79
|
+
|
|
80
|
+
export const isTaprootRecipient: (
|
|
81
|
+
arg0: CryptoCurrency,
|
|
82
|
+
arg1: string
|
|
83
|
+
) => Promise<boolean> = makeLRUCache(
|
|
84
|
+
async (currency, recipient) => {
|
|
85
|
+
return isTaprootAddress(recipient, <Currency>currency.id);
|
|
86
|
+
},
|
|
87
|
+
(currency, recipient) => `${currency.id}_${recipient || ""}`
|
|
88
|
+
);
|
|
@@ -9,7 +9,8 @@ import {
|
|
|
9
9
|
} from "@ledgerhq/errors";
|
|
10
10
|
import type { Account, TransactionStatus } from "./../../types";
|
|
11
11
|
import type { Transaction } from "./types";
|
|
12
|
-
import { calculateFees, validateRecipient } from "./cache";
|
|
12
|
+
import { calculateFees, validateRecipient, isTaprootRecipient } from "./cache";
|
|
13
|
+
import { TaprootNotActivated } from "./errors";
|
|
13
14
|
|
|
14
15
|
const getTransactionStatus = async (
|
|
15
16
|
a: Account,
|
|
@@ -31,6 +32,19 @@ const getTransactionStatus = async (
|
|
|
31
32
|
warnings.recipient = recipientWarning;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
// Safeguard before Taproot activation
|
|
36
|
+
if (
|
|
37
|
+
t.recipient &&
|
|
38
|
+
!errors.recipient &&
|
|
39
|
+
a.currency.id === "bitcoin" &&
|
|
40
|
+
a.blockHeight <= 709632
|
|
41
|
+
) {
|
|
42
|
+
const isTaproot = await isTaprootRecipient(a.currency, t.recipient);
|
|
43
|
+
if (isTaproot) {
|
|
44
|
+
errors.recipient = new TaprootNotActivated();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
34
48
|
let txInputs;
|
|
35
49
|
let txOutputs;
|
|
36
50
|
let estimatedFees = new BigNumber(0);
|
|
@@ -128,34 +128,20 @@ const mapTxToOperations = (
|
|
|
128
128
|
// All inputs of a same transaction have the same sequence
|
|
129
129
|
const transactionSequenceNumber =
|
|
130
130
|
(accountInputs.length > 0 && accountInputs[0].sequence) || undefined;
|
|
131
|
-
|
|
131
|
+
|
|
132
|
+
// Change output is the last one
|
|
133
|
+
const changeOutputIndex = tx.outputs
|
|
134
|
+
.map((o) => o.output_index)
|
|
135
|
+
.reduce((p, c) => (p > c ? p : c));
|
|
132
136
|
|
|
133
137
|
for (const output of tx.outputs) {
|
|
134
138
|
if (output.address) {
|
|
135
139
|
if (accountAddresses.includes(output.address)) {
|
|
136
140
|
accountOutputs.push(output);
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
output.output_hash ===
|
|
142
|
-
tx.outputs[tx.outputs.length - 1].output_hash) ||
|
|
143
|
-
hasSpentNothing
|
|
144
|
-
) {
|
|
145
|
-
recipients.push(
|
|
146
|
-
syncReplaceAddress
|
|
147
|
-
? syncReplaceAddress(output.address)
|
|
148
|
-
: output.address
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
} else {
|
|
152
|
-
recipients.push(
|
|
153
|
-
syncReplaceAddress
|
|
154
|
-
? syncReplaceAddress(output.address)
|
|
155
|
-
: output.address
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
} else {
|
|
141
|
+
} else if (
|
|
142
|
+
!changeAddresses.includes(output.address) ||
|
|
143
|
+
output.output_index !== changeOutputIndex
|
|
144
|
+
) {
|
|
159
145
|
recipients.push(
|
|
160
146
|
syncReplaceAddress
|
|
161
147
|
? syncReplaceAddress(output.address)
|
|
@@ -342,8 +328,8 @@ const getAccountShape: GetAccountShape = async (info) => {
|
|
|
342
328
|
spendableBalance: balance,
|
|
343
329
|
operations,
|
|
344
330
|
operationsCount: operations.length,
|
|
345
|
-
freshAddress: walletAccount.xpub.freshAddress
|
|
346
|
-
freshAddressPath: `${accountPath}/0/${walletAccount.xpub.freshAddressIndex
|
|
331
|
+
freshAddress: walletAccount.xpub.freshAddress,
|
|
332
|
+
freshAddressPath: `${accountPath}/0/${walletAccount.xpub.freshAddressIndex}`,
|
|
347
333
|
blockHeight,
|
|
348
334
|
bitcoinResources: {
|
|
349
335
|
utxos,
|
|
@@ -79,7 +79,7 @@ export function isChangeOutput(output: BitcoinOutput): boolean {
|
|
|
79
79
|
type UTXOStatus =
|
|
80
80
|
| {
|
|
81
81
|
excluded: true;
|
|
82
|
-
reason: "pickUnconfirmedRBF" | "userExclusion";
|
|
82
|
+
reason: "pickUnconfirmedRBF" | "pickPendingNonRBF" | "userExclusion";
|
|
83
83
|
}
|
|
84
84
|
| {
|
|
85
85
|
excluded: false;
|
|
@@ -94,7 +94,12 @@ export const getUTXOStatus = (
|
|
|
94
94
|
reason: "pickUnconfirmedRBF",
|
|
95
95
|
};
|
|
96
96
|
}
|
|
97
|
-
|
|
97
|
+
if (!utxo.rbf && !utxo.blockHeight) {
|
|
98
|
+
return {
|
|
99
|
+
excluded: true,
|
|
100
|
+
reason: "pickPendingNonRBF",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
98
103
|
if (
|
|
99
104
|
utxoStrategy.excludeUTXOs.some(
|
|
100
105
|
(u) => u.hash === utxo.hash && u.outputIndex === utxo.outputIndex
|
|
@@ -254,7 +254,12 @@ const bitcoinLikeMutations = ({
|
|
|
254
254
|
},
|
|
255
255
|
recoverBadTransactionStatus,
|
|
256
256
|
test: ({ account }) => {
|
|
257
|
-
expect(
|
|
257
|
+
expect(
|
|
258
|
+
account.bitcoinResources?.utxos
|
|
259
|
+
.filter((u) => u.blockHeight) // Exclude pending UTXOs
|
|
260
|
+
.reduce((p, c) => p.plus(c.value), new BigNumber(0))
|
|
261
|
+
.toString()
|
|
262
|
+
).toBe("0");
|
|
258
263
|
},
|
|
259
264
|
},
|
|
260
265
|
];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { DatasetTest } from "../../types";
|
|
2
2
|
import type { Transaction } from "./types";
|
|
3
3
|
import bitcoin_cash from "./datasets/bitcoin_cash";
|
|
4
|
-
import bitcoin_gold from "./datasets/bitcoin_gold";
|
|
4
|
+
// import bitcoin_gold from "./datasets/bitcoin_gold";
|
|
5
5
|
import bitcoin from "./datasets/bitcoin";
|
|
6
6
|
import dash from "./datasets/dash";
|
|
7
7
|
// import decred from "./datasets/decred";
|
|
@@ -22,7 +22,7 @@ const dataset: DatasetTest<Transaction> = {
|
|
|
22
22
|
currencies: {
|
|
23
23
|
bitcoin,
|
|
24
24
|
bitcoin_cash,
|
|
25
|
-
bitcoin_gold,
|
|
25
|
+
// bitcoin_gold,
|
|
26
26
|
dash,
|
|
27
27
|
// decred, // BACK-2443
|
|
28
28
|
digibyte,
|
|
@@ -27,6 +27,9 @@ export function fallbackValidateAddress(address: string): boolean {
|
|
|
27
27
|
class Base implements ICrypto {
|
|
28
28
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
29
|
network: any;
|
|
30
|
+
protected static publickeyCache = {}; // xpub + account + index to publicKey
|
|
31
|
+
protected static addressCache = {}; // derivationMode + xpub + account + index to address
|
|
32
|
+
protected static bech32Cache = {}; // xpub to bech32 interface
|
|
30
33
|
|
|
31
34
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
35
|
constructor({ network }: { network: any }) {
|
|
@@ -37,9 +40,26 @@ class Base implements ICrypto {
|
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
protected getPubkeyAt(xpub: string, account: number, index: number): Buffer {
|
|
40
|
-
|
|
41
|
-
.
|
|
43
|
+
if (!Base.bech32Cache[xpub]) {
|
|
44
|
+
Base.bech32Cache[xpub] = bip32.fromBase58(xpub, this.network);
|
|
45
|
+
}
|
|
46
|
+
if (Base.publickeyCache[`${xpub}-${account}-${index}`]) {
|
|
47
|
+
return Base.publickeyCache[`${xpub}-${account}-${index}`];
|
|
48
|
+
}
|
|
49
|
+
if (Base.publickeyCache[`${xpub}-${account}`]) {
|
|
50
|
+
const publicKey =
|
|
51
|
+
Base.publickeyCache[`${xpub}-${account}`].derive(index).publicKey;
|
|
52
|
+
Base.publickeyCache[`${xpub}-${account}-${index}`] = publicKey;
|
|
53
|
+
return publicKey;
|
|
54
|
+
}
|
|
55
|
+
Base.publickeyCache[`${xpub}-${account}`] =
|
|
56
|
+
Base.bech32Cache[xpub].derive(account);
|
|
57
|
+
const publicKey =
|
|
58
|
+
Base.publickeyCache[`${xpub}-${account}`].derive(index).publicKey;
|
|
59
|
+
Base.publickeyCache[`${xpub}-${account}-${index}`] = publicKey;
|
|
60
|
+
return publicKey;
|
|
42
61
|
}
|
|
62
|
+
|
|
43
63
|
// derive legacy address at account and index positions
|
|
44
64
|
getLegacyAddress(xpub: string, account: number, index: number): string {
|
|
45
65
|
const { address } = bjs.payments.p2pkh({
|
|
@@ -78,16 +98,26 @@ class Base implements ICrypto {
|
|
|
78
98
|
account: number,
|
|
79
99
|
index: number
|
|
80
100
|
): string {
|
|
101
|
+
if (Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`]) {
|
|
102
|
+
return Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`];
|
|
103
|
+
}
|
|
81
104
|
switch (derivationMode) {
|
|
82
105
|
case DerivationModes.LEGACY:
|
|
83
|
-
|
|
106
|
+
Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`] =
|
|
107
|
+
this.getLegacyAddress(xpub, account, index);
|
|
108
|
+
break;
|
|
84
109
|
case DerivationModes.SEGWIT:
|
|
85
|
-
|
|
110
|
+
Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`] =
|
|
111
|
+
this.getSegWitAddress(xpub, account, index);
|
|
112
|
+
break;
|
|
86
113
|
case DerivationModes.NATIVE_SEGWIT:
|
|
87
|
-
|
|
114
|
+
Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`] =
|
|
115
|
+
this.getNativeSegWitAddress(xpub, account, index);
|
|
116
|
+
break;
|
|
88
117
|
default:
|
|
89
118
|
throw new Error(`Invalid derivation Mode: ${derivationMode}`);
|
|
90
119
|
}
|
|
120
|
+
return Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`];
|
|
91
121
|
}
|
|
92
122
|
|
|
93
123
|
// infer address type from its syntax
|
|
@@ -119,6 +149,11 @@ class Base implements ICrypto {
|
|
|
119
149
|
(res[0] === this.network.pubKeyHash || res[0] === this.network.scriptHash)
|
|
120
150
|
);
|
|
121
151
|
}
|
|
152
|
+
|
|
153
|
+
// eslint-disable-next-line
|
|
154
|
+
isTaprootAddress(address: string): boolean {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
122
157
|
}
|
|
123
158
|
|
|
124
159
|
export default Base;
|
|
@@ -124,9 +124,16 @@ class Bitcoin extends Base {
|
|
|
124
124
|
account: number,
|
|
125
125
|
index: number
|
|
126
126
|
): string {
|
|
127
|
+
if (Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`]) {
|
|
128
|
+
return Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`];
|
|
129
|
+
}
|
|
127
130
|
switch (derivationMode) {
|
|
128
131
|
case DerivationModes.TAPROOT:
|
|
129
|
-
|
|
132
|
+
Base.addressCache[`${derivationMode}-${xpub}-${account}-${index}`] =
|
|
133
|
+
this.getTaprootAddress(xpub, account, index);
|
|
134
|
+
return Base.addressCache[
|
|
135
|
+
`${derivationMode}-${xpub}-${account}-${index}`
|
|
136
|
+
];
|
|
130
137
|
default:
|
|
131
138
|
return super.getAddress(derivationMode, xpub, account, index);
|
|
132
139
|
}
|
|
@@ -202,6 +209,19 @@ class Bitcoin extends Base {
|
|
|
202
209
|
// Create address
|
|
203
210
|
return toBech32(outputSchnorrKey, 1, this.network.bech32);
|
|
204
211
|
}
|
|
212
|
+
|
|
213
|
+
isTaprootAddress(address: string): boolean {
|
|
214
|
+
// This prefix check is to avoid returning false in cases where a valid base58 address also happens
|
|
215
|
+
// to be a valid bech32(m) string (but invalid segwit address).
|
|
216
|
+
if (address.toLowerCase().startsWith(`${this.network.bech32}1`)) {
|
|
217
|
+
try {
|
|
218
|
+
bjs.address.fromBech32(address);
|
|
219
|
+
} catch {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
205
225
|
}
|
|
206
226
|
|
|
207
227
|
export default Bitcoin;
|
|
@@ -70,6 +70,11 @@ class BitcoinCash implements ICrypto {
|
|
|
70
70
|
validateAddress(address: string): boolean {
|
|
71
71
|
return bchaddr.isValidAddress(address);
|
|
72
72
|
}
|
|
73
|
+
|
|
74
|
+
// eslint-disable-next-line
|
|
75
|
+
isTaprootAddress(address: string): boolean {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
export default BitcoinCash;
|
|
@@ -74,6 +74,11 @@ class ZCash implements ICrypto {
|
|
|
74
74
|
validateAddress(address: string): boolean {
|
|
75
75
|
return zec.Address.isValid(address, "livenet");
|
|
76
76
|
}
|
|
77
|
+
|
|
78
|
+
// eslint-disable-next-line
|
|
79
|
+
isTaprootAddress(address: string): boolean {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
77
82
|
}
|
|
78
83
|
|
|
79
84
|
export default ZCash;
|
|
@@ -285,12 +285,7 @@ class BitcoinLikeExplorer extends EventEmitter implements IExplorer {
|
|
|
285
285
|
|
|
286
286
|
// faster than mapping
|
|
287
287
|
res.txs.forEach((tx) => {
|
|
288
|
-
if (!tx.block) {
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
288
|
this.hydrateTx(address, tx);
|
|
293
|
-
|
|
294
289
|
hydratedTxs.push(tx);
|
|
295
290
|
});
|
|
296
291
|
|
|
@@ -11,7 +11,7 @@ import { TX, Input, Output } from "./storage/types";
|
|
|
11
11
|
import { CoinSelect } from "./pickingstrategies/CoinSelect";
|
|
12
12
|
import { DeepFirst } from "./pickingstrategies/DeepFirst";
|
|
13
13
|
import { Merge } from "./pickingstrategies/Merge";
|
|
14
|
-
import { isValidAddress } from "./utils";
|
|
14
|
+
import { isValidAddress, isTaprootAddress } from "./utils";
|
|
15
15
|
|
|
16
16
|
import type { Account as WalletAccount } from "./account";
|
|
17
17
|
import type { Account as LiveAccount } from "./../../../types";
|
|
@@ -32,16 +32,16 @@ export {
|
|
|
32
32
|
DeepFirst,
|
|
33
33
|
Merge,
|
|
34
34
|
isValidAddress,
|
|
35
|
+
isTaprootAddress,
|
|
35
36
|
Currency,
|
|
36
37
|
};
|
|
37
38
|
|
|
38
39
|
let wallet: BitcoinLikeWallet | null = null;
|
|
39
40
|
|
|
40
41
|
export const getWalletAccount = (account: LiveAccount): WalletAccount => {
|
|
41
|
-
if (account.id.startsWith("libcore")) throw new AccountNeedResync();
|
|
42
42
|
const walletAccount = account.bitcoinResources?.walletAccount;
|
|
43
|
-
if (!walletAccount) {
|
|
44
|
-
throw new
|
|
43
|
+
if (account.id.startsWith("libcore") || !walletAccount) {
|
|
44
|
+
throw new AccountNeedResync();
|
|
45
45
|
}
|
|
46
46
|
return walletAccount;
|
|
47
47
|
};
|
|
@@ -34,7 +34,7 @@ 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.
|
|
37
|
+
const fixedSize = utils.accurateTxSize(
|
|
38
38
|
0,
|
|
39
39
|
0,
|
|
40
40
|
this.crypto,
|
|
@@ -42,10 +42,10 @@ export class CoinSelect extends PickingStrategy {
|
|
|
42
42
|
);
|
|
43
43
|
// Size of only 1 output (without fixed size)
|
|
44
44
|
const oneOutputSize =
|
|
45
|
-
utils.
|
|
45
|
+
utils.accurateTxSize(0, 1, this.crypto, this.derivationMode) - fixedSize;
|
|
46
46
|
// Size 1 signed UTXO (signed input)
|
|
47
47
|
const oneInputSize =
|
|
48
|
-
utils.
|
|
48
|
+
utils.accurateTxSize(1, 0, this.crypto, this.derivationMode) - fixedSize;
|
|
49
49
|
|
|
50
50
|
// Calculate effective value of outputs
|
|
51
51
|
let currentAvailableValue = 0;
|
|
@@ -33,7 +33,7 @@ export class DeepFirst extends PickingStrategy {
|
|
|
33
33
|
|
|
34
34
|
unspentUtxos = sortBy(unspentUtxos, "block_height");
|
|
35
35
|
// https://metamug.com/article/security/bitcoin-transaction-fee-satoshi-per-byte.html
|
|
36
|
-
const txSizeNoInput = utils.
|
|
36
|
+
const txSizeNoInput = utils.accurateTxSize(
|
|
37
37
|
0,
|
|
38
38
|
nbOutputsWithoutChange,
|
|
39
39
|
this.crypto,
|
|
@@ -41,12 +41,12 @@ export class DeepFirst extends PickingStrategy {
|
|
|
41
41
|
);
|
|
42
42
|
let fee = txSizeNoInput * feePerByte;
|
|
43
43
|
const sizePerInput =
|
|
44
|
-
utils.
|
|
45
|
-
utils.
|
|
44
|
+
utils.accurateTxSize(1, 0, this.crypto, this.derivationMode) -
|
|
45
|
+
utils.accurateTxSize(0, 0, this.crypto, this.derivationMode);
|
|
46
46
|
|
|
47
47
|
const sizePerOutput =
|
|
48
|
-
utils.
|
|
49
|
-
utils.
|
|
48
|
+
utils.accurateTxSize(0, 1, this.crypto, this.derivationMode) -
|
|
49
|
+
utils.accurateTxSize(0, 0, this.crypto, this.derivationMode);
|
|
50
50
|
|
|
51
51
|
let total = new BigNumber(0);
|
|
52
52
|
const unspentUtxoSelected: Output[] = [];
|
|
@@ -32,16 +32,16 @@ export class Merge extends PickingStrategy {
|
|
|
32
32
|
);
|
|
33
33
|
|
|
34
34
|
const sizePerInput =
|
|
35
|
-
utils.
|
|
36
|
-
utils.
|
|
35
|
+
utils.accurateTxSize(1, 0, this.crypto, this.derivationMode) -
|
|
36
|
+
utils.accurateTxSize(0, 0, this.crypto, this.derivationMode);
|
|
37
37
|
|
|
38
38
|
const sizePerOutput =
|
|
39
|
-
utils.
|
|
40
|
-
utils.
|
|
39
|
+
utils.accurateTxSize(0, 1, this.crypto, this.derivationMode) -
|
|
40
|
+
utils.accurateTxSize(0, 0, this.crypto, this.derivationMode);
|
|
41
41
|
|
|
42
42
|
unspentUtxos = sortBy(unspentUtxos, (utxo) => parseInt(utxo.value, 10));
|
|
43
43
|
// https://metamug.com/article/security/bitcoin-transaction-fee-satoshi-per-byte.html
|
|
44
|
-
const txSizeNoInput = utils.
|
|
44
|
+
const txSizeNoInput = utils.accurateTxSize(
|
|
45
45
|
0,
|
|
46
46
|
nbOutputsWithoutChange,
|
|
47
47
|
this.crypto,
|
|
@@ -83,16 +83,16 @@ export function byteSize(count: number) {
|
|
|
83
83
|
return 1;
|
|
84
84
|
}
|
|
85
85
|
if (count <= 0xffff) {
|
|
86
|
-
return
|
|
86
|
+
return 3;
|
|
87
87
|
}
|
|
88
88
|
if (count <= 0xffffffff) {
|
|
89
|
-
return
|
|
89
|
+
return 5;
|
|
90
90
|
}
|
|
91
|
-
return
|
|
91
|
+
return 9;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// refer to https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/api_impl/BitcoinLikeTransactionApi.cpp#L217
|
|
95
|
-
export function
|
|
95
|
+
export function accurateTxSize(
|
|
96
96
|
inputCount: number,
|
|
97
97
|
outputCount: number,
|
|
98
98
|
currency: ICrypto,
|
|
@@ -106,11 +106,15 @@ export function estimateTxSize(
|
|
|
106
106
|
fixedSize += byteSize(inputCount); // Number of inputs
|
|
107
107
|
fixedSize += byteSize(outputCount); // Number of outputs
|
|
108
108
|
fixedSize += 4; // Timelock
|
|
109
|
+
if (derivationMode !== DerivationModes.LEGACY) {
|
|
110
|
+
fixedSize += 0.5; // Segwit marker & segwit flag
|
|
111
|
+
}
|
|
109
112
|
// refer to https://medium.com/coinmonks/on-bitcoin-transaction-sizes-part-2-9445373d17f4
|
|
110
113
|
// and https://bitcoin.stackexchange.com/questions/96017/what-are-the-sizes-of-single-sig-and-2-of-3-multisig-taproot-inputs
|
|
114
|
+
// and https://bitcoinops.org/en/tools/calc-size/
|
|
111
115
|
if (derivationMode === DerivationModes.TAPROOT) {
|
|
112
|
-
txSize = fixedSize + 57.
|
|
113
|
-
return
|
|
116
|
+
txSize = fixedSize + 57.75 * inputCount + 43 * outputCount;
|
|
117
|
+
return txSize;
|
|
114
118
|
}
|
|
115
119
|
const isSegwit =
|
|
116
120
|
derivationMode === DerivationModes.NATIVE_SEGWIT ||
|
|
@@ -120,14 +124,25 @@ export function estimateTxSize(
|
|
|
120
124
|
// P2SH: 32 PrevTxHash + 4 Index + 23 scriptPubKey + 4 sequence
|
|
121
125
|
const isNativeSegwit = derivationMode === DerivationModes.NATIVE_SEGWIT;
|
|
122
126
|
const inputSize = isNativeSegwit ? 41 : 63;
|
|
123
|
-
const noWitness = fixedSize + inputSize * inputCount +
|
|
127
|
+
const noWitness = fixedSize + inputSize * inputCount + 43 * outputCount; //43 is the biggest output(taproot output) size, we use the biggest one for safety
|
|
124
128
|
// Include flag and marker size (one byte each)
|
|
125
129
|
const witnessSize = noWitness + 108 * inputCount + 2;
|
|
126
130
|
txSize = (noWitness * 3 + witnessSize) / 4;
|
|
127
131
|
} else {
|
|
128
|
-
txSize = fixedSize + 148 * inputCount +
|
|
132
|
+
txSize = fixedSize + 148 * inputCount + 43 * outputCount;
|
|
129
133
|
}
|
|
130
|
-
return
|
|
134
|
+
return txSize;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function estimateTxSize(
|
|
138
|
+
inputCount: number,
|
|
139
|
+
outputCount: number,
|
|
140
|
+
currency: ICrypto,
|
|
141
|
+
derivationMode: string
|
|
142
|
+
) {
|
|
143
|
+
return Math.ceil(
|
|
144
|
+
accurateTxSize(inputCount, outputCount, currency, derivationMode)
|
|
145
|
+
); // We don't allow floating value
|
|
131
146
|
}
|
|
132
147
|
|
|
133
148
|
// refer to https://github.com/LedgerHQ/lib-ledger-core/blob/fc9d762b83fc2b269d072b662065747a64ab2816/core/src/wallet/bitcoin/api_impl/BitcoinLikeTransactionApi.cpp#L253
|
|
@@ -156,3 +171,13 @@ export function isValidAddress(address: string, currency?: Currency) {
|
|
|
156
171
|
const crypto = cryptoFactory(currency);
|
|
157
172
|
return crypto.validateAddress(address);
|
|
158
173
|
}
|
|
174
|
+
|
|
175
|
+
export function isTaprootAddress(address: string, currency?: Currency) {
|
|
176
|
+
if (currency === "bitcoin") {
|
|
177
|
+
return cryptoFactory("bitcoin").isTaprootAddress(address);
|
|
178
|
+
} else if (currency === "bitcoin_testnet") {
|
|
179
|
+
return cryptoFactory("bitcoin_testnet").isTaprootAddress(address);
|
|
180
|
+
} else {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -133,6 +133,7 @@ class BitcoinLikeWallet {
|
|
|
133
133
|
)
|
|
134
134
|
);
|
|
135
135
|
let balance = new BigNumber(0);
|
|
136
|
+
let availableUtxoCount = 0;
|
|
136
137
|
log("btcwallet", "estimateAccountMaxSpendable utxos", utxos);
|
|
137
138
|
utxos.forEach((utxo) => {
|
|
138
139
|
if (
|
|
@@ -144,6 +145,7 @@ class BitcoinLikeWallet {
|
|
|
144
145
|
) {
|
|
145
146
|
if ((pickUnconfirmedRBF && utxo.rbf) || utxo.block_height !== null) {
|
|
146
147
|
balance = balance.plus(utxo.value);
|
|
148
|
+
availableUtxoCount += 1;
|
|
147
149
|
}
|
|
148
150
|
}
|
|
149
151
|
});
|
|
@@ -151,7 +153,7 @@ class BitcoinLikeWallet {
|
|
|
151
153
|
const fees =
|
|
152
154
|
feePerByte *
|
|
153
155
|
utils.estimateTxSize(
|
|
154
|
-
|
|
156
|
+
availableUtxoCount,
|
|
155
157
|
1,
|
|
156
158
|
account.xpub.crypto,
|
|
157
159
|
account.xpub.derivationMode
|
|
@@ -263,7 +265,7 @@ class BitcoinLikeWallet {
|
|
|
263
265
|
return [
|
|
264
266
|
btc.splitTransaction(
|
|
265
267
|
i.txHex,
|
|
266
|
-
|
|
268
|
+
true,
|
|
267
269
|
false, // FIXME hasTimestamp needed for LL-7539
|
|
268
270
|
hasExtraData,
|
|
269
271
|
additionals
|