diff --git a/src/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.ts b/src/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.ts index 3f4eb2ac3d..dad226e4d2 100644 --- a/src/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.ts +++ b/src/__tests__/families/bitcoin/wallet-btc/wallet.estimateMaxSpendable.test.ts @@ -27,12 +27,7 @@ describe("testing estimateMaxSpendable", () => { it("should estimate max spendable correctly", async () => { await wallet.syncAccount(account); - let maxSpendable = await wallet.estimateAccountMaxSpendable( - account, - 0, - [], - true - ); + let maxSpendable = await wallet.estimateAccountMaxSpendable(account, 0, []); const balance = 109088; expect(maxSpendable.toNumber()).toEqual(balance); const maxSpendableExcludeUtxo = await wallet.estimateAccountMaxSpendable( @@ -43,16 +38,14 @@ describe("testing estimateMaxSpendable", () => { hash: "f80246be50064bb254d2cad82fb0d4ce7768582b99c113694e72411f8032fd7a", outputIndex: 0, }, - ], - true + ] ); expect(maxSpendableExcludeUtxo.toNumber()).toEqual(balance - 1000); let feesPerByte = 100; maxSpendable = await wallet.estimateAccountMaxSpendable( account, feesPerByte, - [], - true + [] ); expect(maxSpendable.toNumber()).toEqual( balance - @@ -69,8 +62,7 @@ describe("testing estimateMaxSpendable", () => { maxSpendable = await wallet.estimateAccountMaxSpendable( account, feesPerByte, - [], - true + [] ); expect(maxSpendable.toNumber()).toEqual(0); }, 60000); diff --git a/src/families/bitcoin/bridge/mock.ts b/src/families/bitcoin/bridge/mock.ts index ca5a7e78de..76179770a0 100644 --- a/src/families/bitcoin/bridge/mock.ts +++ b/src/families/bitcoin/bridge/mock.ts @@ -32,7 +32,6 @@ const createTransaction = (): Transaction => ({ rbf: false, utxoStrategy: { strategy: 0, - pickUnconfirmedRBF: false, excludeUTXOs: [], }, }); diff --git a/src/families/bitcoin/cache.ts b/src/families/bitcoin/cache.ts index 64ec71a4bb..c1a5012fe4 100644 --- a/src/families/bitcoin/cache.ts +++ b/src/families/bitcoin/cache.ts @@ -15,9 +15,9 @@ const getCacheKeyForCalculateFees = ({ }) => `${a.id}_${a.blockHeight || 0}_${t.amount.toString()}_${String( t.useAllAmount - )}_${t.recipient}_${t.feePerByte ? t.feePerByte.toString() : ""}_${ - t.utxoStrategy.pickUnconfirmedRBF ? 1 : 0 - }_${t.utxoStrategy.strategy}_${String(t.rbf)}_${t.utxoStrategy.excludeUTXOs + )}_${t.recipient}_${t.feePerByte ? t.feePerByte.toString() : ""}_${0}_${ + t.utxoStrategy.strategy + }_${String(t.rbf)}_${t.utxoStrategy.excludeUTXOs .map(({ hash, outputIndex }) => `${hash}@${outputIndex}`) .join("+")}`; diff --git a/src/families/bitcoin/cli-transaction.ts b/src/families/bitcoin/cli-transaction.ts index ba3e299da6..c51c08165b 100644 --- a/src/families/bitcoin/cli-transaction.ts +++ b/src/families/bitcoin/cli-transaction.ts @@ -8,11 +8,6 @@ const options = [ type: String, desc: "how much fee per byte", }, - { - name: "pickUnconfirmedRBF", - type: Boolean, - desc: "also pick unconfirmed replaceable txs", - }, { name: "excludeUTXO", alias: "E", @@ -52,7 +47,6 @@ function inferTransactions( rbf: opts.rbf || false, utxoStrategy: { strategy: bitcoinPickingStrategy[opts["bitcoin-pick-strategy"]] || 0, - pickUnconfirmedRBF: opts.pickUnconfirmedRBF || false, excludeUTXOs: (opts.excludeUTXO || []).map((str) => { const [hash, index] = str.split("@"); invariant( diff --git a/src/families/bitcoin/datasets/bitcoin.ts b/src/families/bitcoin/datasets/bitcoin.ts index ef74ce0aef..c0c2669547 100644 --- a/src/families/bitcoin/datasets/bitcoin.ts +++ b/src/families/bitcoin/datasets/bitcoin.ts @@ -47,7 +47,6 @@ const dataset: CurrenciesData = { rbf: false, utxoStrategy: { strategy: 0, - pickUnconfirmedRBF: false, excludeUTXOs: [], }, }), @@ -73,7 +72,6 @@ const dataset: CurrenciesData = { rbf: false, utxoStrategy: { strategy: 0, - pickUnconfirmedRBF: false, excludeUTXOs: [], }, }), @@ -99,7 +97,6 @@ const dataset: CurrenciesData = { rbf: false, utxoStrategy: { strategy: 0, - pickUnconfirmedRBF: false, excludeUTXOs: [], }, }), diff --git a/src/families/bitcoin/datasets/digibyte.ts b/src/families/bitcoin/datasets/digibyte.ts index 3b1235f9d0..8f95648bfe 100644 --- a/src/families/bitcoin/datasets/digibyte.ts +++ b/src/families/bitcoin/datasets/digibyte.ts @@ -46,7 +46,6 @@ const dataset: CurrenciesData = { rbf: false, utxoStrategy: { strategy: 0, - pickUnconfirmedRBF: false, excludeUTXOs: [], }, }), @@ -69,7 +68,6 @@ const dataset: CurrenciesData = { rbf: false, utxoStrategy: { strategy: 0, - pickUnconfirmedRBF: false, excludeUTXOs: [], }, }), @@ -92,7 +90,6 @@ const dataset: CurrenciesData = { rbf: false, utxoStrategy: { strategy: 0, - pickUnconfirmedRBF: false, excludeUTXOs: [], }, }), diff --git a/src/families/bitcoin/datasets/litecoin.ts b/src/families/bitcoin/datasets/litecoin.ts index 4457209d5f..dc0cc9c432 100644 --- a/src/families/bitcoin/datasets/litecoin.ts +++ b/src/families/bitcoin/datasets/litecoin.ts @@ -47,7 +47,6 @@ const dataset: CurrenciesData = { rbf: false, utxoStrategy: { strategy: 0, - pickUnconfirmedRBF: false, excludeUTXOs: [], }, }), @@ -70,7 +69,6 @@ const dataset: CurrenciesData = { rbf: false, utxoStrategy: { strategy: 0, - pickUnconfirmedRBF: false, excludeUTXOs: [], }, }), @@ -93,7 +91,6 @@ const dataset: CurrenciesData = { rbf: false, utxoStrategy: { strategy: 0, - pickUnconfirmedRBF: false, excludeUTXOs: [], }, }), diff --git a/src/families/bitcoin/js-buildTransaction.ts b/src/families/bitcoin/js-buildTransaction.ts index daa3aa240e..99dd5be327 100644 --- a/src/families/bitcoin/js-buildTransaction.ts +++ b/src/families/bitcoin/js-buildTransaction.ts @@ -44,7 +44,6 @@ export const buildTransaction = async ( walletAccount, transaction.feePerByte.toNumber(), //!\ wallet-btc handles fees as JS number transaction.utxoStrategy.excludeUTXOs, - transaction.utxoStrategy.pickUnconfirmedRBF, [transaction.recipient] ); log("btcwallet", "building transaction", transaction); diff --git a/src/families/bitcoin/js-createTransaction.ts b/src/families/bitcoin/js-createTransaction.ts index 13826499bc..a2447cb727 100644 --- a/src/families/bitcoin/js-createTransaction.ts +++ b/src/families/bitcoin/js-createTransaction.ts @@ -12,7 +12,6 @@ const createTransaction = (): Transaction => { amount: new BigNumber(0), utxoStrategy: { strategy: 0, - pickUnconfirmedRBF: false, excludeUTXOs: [], }, recipient: "", diff --git a/src/families/bitcoin/js-estimateMaxSpendable.ts b/src/families/bitcoin/js-estimateMaxSpendable.ts index 8cc20745ef..c703988fcb 100644 --- a/src/families/bitcoin/js-estimateMaxSpendable.ts +++ b/src/families/bitcoin/js-estimateMaxSpendable.ts @@ -30,7 +30,6 @@ const estimateMaxSpendable = async ({ walletAccount, feePerByte.toNumber(), //!\ wallet-btc handles fees as JS number transaction?.utxoStrategy?.excludeUTXOs || [], - transaction?.utxoStrategy?.pickUnconfirmedRBF || false, transaction ? [transaction.recipient] : [] ); return maxSpendable.lt(0) ? new BigNumber(0) : maxSpendable; diff --git a/src/families/bitcoin/js-synchronisation.ts b/src/families/bitcoin/js-synchronisation.ts index d17729575b..b4e1378495 100644 --- a/src/families/bitcoin/js-synchronisation.ts +++ b/src/families/bitcoin/js-synchronisation.ts @@ -53,7 +53,10 @@ const toWalletNetwork = (currencyId: string): "testnet" | "mainnet" => { }; // Map wallet-btc's Output to LL's BitcoinOutput -const fromWalletUtxo = (utxo: WalletOutput): BitcoinOutput => { +const fromWalletUtxo = ( + utxo: WalletOutput, + changeAddresses: Set +): BitcoinOutput => { return { hash: utxo.output_hash, outputIndex: utxo.output_index, @@ -61,7 +64,7 @@ const fromWalletUtxo = (utxo: WalletOutput): BitcoinOutput => { address: utxo.address, value: new BigNumber(utxo.value), rbf: utxo.rbf, - isChange: false, // wallet-btc limitation: doesn't provide it + isChange: changeAddresses.has(utxo.address), }; }; @@ -349,7 +352,7 @@ const getAccountShape: GetAccountShape = async (info) => { const newUniqueOperations = deduplicateOperations(newOperations); const operations = mergeOps(oldOperations, newUniqueOperations); const rawUtxos = await wallet.getAccountUnspentUtxos(walletAccount); - const utxos = rawUtxos.map(fromWalletUtxo); + const utxos = rawUtxos.map((utxo) => fromWalletUtxo(utxo, changeAddresses)); return { id: accountId, xpub, diff --git a/src/families/bitcoin/logic.ts b/src/families/bitcoin/logic.ts index 6027872f08..1c9c03f9c5 100644 --- a/src/families/bitcoin/logic.ts +++ b/src/families/bitcoin/logic.ts @@ -68,7 +68,7 @@ export const isValidRecipient = async (params: { type UTXOStatus = | { excluded: true; - reason: "pickUnconfirmedRBF" | "pickPendingNonRBF" | "userExclusion"; + reason: "pickPendingUtxo" | "userExclusion"; } | { excluded: false; @@ -77,16 +77,11 @@ export const getUTXOStatus = ( utxo: BitcoinOutput, utxoStrategy: UtxoStrategy ): UTXOStatus => { - if (!utxoStrategy.pickUnconfirmedRBF && utxo.rbf && !utxo.blockHeight) { + if (!utxo.blockHeight && !utxo.isChange) { + // exclude pending and not change utxo return { excluded: true, - reason: "pickUnconfirmedRBF", - }; - } - if (!utxo.rbf && !utxo.blockHeight) { - return { - excluded: true, - reason: "pickPendingNonRBF", + reason: "pickPendingUtxo", }; } if ( diff --git a/src/families/bitcoin/specs.ts b/src/families/bitcoin/specs.ts index d4600d42b4..ba6937bac4 100644 --- a/src/families/bitcoin/specs.ts +++ b/src/families/bitcoin/specs.ts @@ -252,7 +252,6 @@ const bitcoinLikeMutations = ({ { utxoStrategy: { ...transaction.utxoStrategy, - pickUnconfirmedRBF: true, }, }, { diff --git a/src/families/bitcoin/transaction.ts b/src/families/bitcoin/transaction.ts index ce614e4bdb..648e929a05 100644 --- a/src/families/bitcoin/transaction.ts +++ b/src/families/bitcoin/transaction.ts @@ -80,7 +80,7 @@ const formatNetworkInfo = ( export const formatTransaction = (t: Transaction, account: Account): string => { const n = getEnv("DEBUG_UTXO_DISPLAY"); - const { excludeUTXOs, strategy, pickUnconfirmedRBF } = t.utxoStrategy; + const { excludeUTXOs, strategy } = t.utxoStrategy; const displayAll = excludeUTXOs.length <= n; return ` SEND ${ @@ -99,7 +99,7 @@ ${[ Object.keys(bitcoinPickingStrategy).find( (k) => bitcoinPickingStrategy[k] === strategy ), - pickUnconfirmedRBF && "pick-unconfirmed", + "pick-unconfirmed", t.rbf && "RBF-enabled", ] .filter(Boolean) diff --git a/src/families/bitcoin/types.ts b/src/families/bitcoin/types.ts index 2f237fc26e..6a558cba00 100644 --- a/src/families/bitcoin/types.ts +++ b/src/families/bitcoin/types.ts @@ -126,7 +126,6 @@ export type BitcoinPickingStrategy = typeof bitcoinPickingStrategy[keyof typeof bitcoinPickingStrategy]; export type UtxoStrategy = { strategy: BitcoinPickingStrategy; - pickUnconfirmedRBF: boolean; excludeUTXOs: Array<{ hash: string; outputIndex: number; diff --git a/src/families/bitcoin/wallet-btc/wallet.ts b/src/families/bitcoin/wallet-btc/wallet.ts index bbd3cbca27..fec571066a 100644 --- a/src/families/bitcoin/wallet-btc/wallet.ts +++ b/src/families/bitcoin/wallet-btc/wallet.ts @@ -122,10 +122,12 @@ class BitcoinLikeWallet { account: Account, feePerByte: number, excludeUTXOs: Array<{ hash: string; outputIndex: number }>, - pickUnconfirmedRBF: boolean, outputAddresses: string[] = [] ) { const addresses = await account.xpub.getXpubAddresses(); + const changeAddresses = (await account.xpub.getAccountAddresses(1)).map( + (item) => item.address + ); const utxos = flatten( await Promise.all( addresses.map((address) => @@ -144,7 +146,11 @@ class BitcoinLikeWallet { excludeUtxo.outputIndex === utxo.output_index ) ) { - if ((pickUnconfirmedRBF && utxo.rbf) || utxo.block_height !== null) { + // we can use either non pending utxo or change utxo + if ( + changeAddresses.includes(utxo.address) || + utxo.block_height !== null + ) { usableUtxoCount++; balance = balance.plus(utxo.value); }