detect hogex inputs and only require 6 confs if so (#2786)

Hector Chu committed Jan 9, 2026 at 16:19 UTC ff88fa8dc8ced05705e2c7ad55b15db8ccfda35d
4 files changed +16 -9
cw_bitcoin/lib/bitcoin_unspent.dart
+1
@@ -25,6 +25,7 @@ class BitcoinUnspent extends Unspent {
25 }
26
27 final BaseBitcoinAddressRecord bitcoinAddressRecord;
28 + bool? isPegOut;
29 }
30
31 class BitcoinSilentPaymentsUnspent extends BitcoinUnspent {
cw_bitcoin/lib/electrum_transaction_info.dart
+12
@@ -25,6 +25,7 @@ class ElectrumTransactionBundle {
25 class ElectrumTransactionInfo extends TransactionInfo {
26 List<BitcoinSilentPaymentsUnspent>? unspents;
27 bool isReceivedSilentPayment;
28 + bool isHogEx;
29
30 ElectrumTransactionInfo(
31 this.type, {
@@ -42,6 +43,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
43 String? to,
44 this.unspents,
45 this.isReceivedSilentPayment = false,
46 + this.isHogEx = false,
47 Map<String, dynamic>? additionalInfo,
48 }) {
49 this.id = id;
@@ -174,6 +176,15 @@ class ElectrumTransactionInfo extends TransactionInfo {
176 amount = receivedAmounts.reduce((a, b) => a + b);
177 }
178
179 + // MWEB HogEx
180 + final isHogExTx = (BtcTransaction tx) {
181 + if (tx.inputs.isEmpty || tx.inputs.first.txIndex > 0 || tx.outputs.isEmpty)
182 + return false;
183 + final b = tx.outputs.first.scriptPubKey.toBytes();
184 + return b.length == 34 && b[0] == 88 && b[1] == 32;
185 + };
186 + final isHogEx = isHogExTx(bundle.originalTransaction) && isHogExTx(bundle.ins.first);
187 +
188 final fee = inputAmount - totalOutAmount;
189 return ElectrumTransactionInfo(type,
190 id: bundle.originalTransaction.txId(),
@@ -186,6 +197,7 @@ class ElectrumTransactionInfo extends TransactionInfo {
197 direction: direction,
198 amount: amount,
199 date: date,
200 + isHogEx: isHogEx,
201 confirmations: bundle.confirmations);
202 }
203
cw_bitcoin/lib/electrum_wallet.dart
+1
@@ -1740,6 +1740,7 @@ abstract class ElectrumWalletBase
1740 final tx = await fetchTransactionInfo(hash: coin.hash);
1741 coin.isChange = address.isHidden;
1742 coin.confirmations = tx?.confirmations;
1743 + coin.isPegOut = tx?.isHogEx;
1744
1745 updatedUnspentCoins.add(coin);
1746 } catch (_) {}
cw_bitcoin/lib/litecoin_wallet.dart
+2 -9
@@ -1214,21 +1214,14 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
1214 } else {
1215 // check if any of the inputs of this transaction are hog-ex:
1216 // this list is only non-mweb inputs:
1217 - bool isHogEx = true;
1218 -
1217 final coin = unspentCoins
1218 .firstWhere((coin) => coin.hash == utxo.utxo.txHash && coin.vout == utxo.utxo.vout);
1221 -
1222 - // TODO: detect actual hog-ex inputs
1223 -
1224 - if (!isHogEx) {
1225 - continue;
1226 - }
1219 + if (coin.isPegOut != true) continue;
1220
1221 int confirmations = coin.confirmations ?? 0;
1222 if (confirmations < 6) {
1223 throw Exception(
1231 - "A transaction input has less than 6 confirmations, please try again later.");
1224 + "A transaction input is an MWEB peg-out and has less than 6 confirmations, please try again later.");
1225 }
1226 }
1227 }