import 'package:isar/isar.dart'; import 'package:stackwallet/models/isar/models/blockchain_data/address.dart'; import 'package:stackwallet/utilities/amount/amount.dart'; import 'package:stackwallet/wallets/crypto_currency/coins/bitcoin.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; import 'package:stackwallet/wallets/crypto_currency/interfaces/paynym_currency_interface.dart'; import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/paynym_interface.dart'; class BitcoinWallet extends Bip39HDWallet with ElectrumXInterface, CoinControlInterface, PaynymInterface { @override int get isarTransactionVersion => 2; BitcoinWallet(CryptoCurrencyNetwork network) : super(Bitcoin(network) as T); @override FilterOperation? get changeAddressFilterOperation => FilterGroup.and(standardChangeAddressFilters); @override FilterOperation? get receivingAddressFilterOperation => FilterGroup.and(standardReceivingAddressFilters); // =========================================================================== @override Future> fetchAddressesForElectrumXScan() async { final allAddresses = await mainDB .getAddresses(walletId) .filter() .not() .group( (q) => q .typeEqualTo(AddressType.nonWallet) .or() .subTypeEqualTo(AddressSubType.nonWallet), ) .findAll(); return allAddresses; } // =========================================================================== @override Amount roughFeeEstimate(int inputCount, int outputCount, int feeRatePerKB) { return Amount( rawValue: BigInt.from( ((42 + (272 * inputCount) + (128 * outputCount)) / 4).ceil() * (feeRatePerKB / 1000).ceil()), fractionDigits: cryptoCurrency.fractionDigits, ); } @override int estimateTxFee({required int vSize, required int feeRatePerKB}) { return vSize * (feeRatePerKB / 1000).ceil(); } // // @override // Future coinSelection({required TxData txData}) async { // final isCoinControl = txData.utxos != null; // final isSendAll = txData.amount == info.cachedBalance.spendable; // // final utxos = // txData.utxos?.toList() ?? await mainDB.getUTXOs(walletId).findAll(); // // final currentChainHeight = await chainHeight; // final List spendableOutputs = []; // int spendableSatoshiValue = 0; // // // Build list of spendable outputs and totaling their satoshi amount // for (final utxo in utxos) { // if (utxo.isBlocked == false && // utxo.isConfirmed(currentChainHeight, cryptoCurrency.minConfirms) && // utxo.used != true) { // spendableOutputs.add(utxo); // spendableSatoshiValue += utxo.value; // } // } // // if (isCoinControl && spendableOutputs.length < utxos.length) { // throw ArgumentError("Attempted to use an unavailable utxo"); // } // // if (spendableSatoshiValue < txData.amount!.raw.toInt()) { // throw Exception("Insufficient balance"); // } else if (spendableSatoshiValue == txData.amount!.raw.toInt() && // !isSendAll) { // throw Exception("Insufficient balance to pay transaction fee"); // } // // if (isCoinControl) { // } else { // final selection = cs.coinSelection( // spendableOutputs // .map((e) => cs.InputModel( // i: e.vout, // txid: e.txid, // value: e.value, // address: e.address, // )) // .toList(), // txData.recipients! // .map((e) => cs.OutputModel( // address: e.address, // value: e.amount.raw.toInt(), // )) // .toList(), // txData.feeRateAmount!, // 10, // TODO: ??????????????????????????????? // ); // // // .inputs and .outputs will be null if no solution was found // if (selection.inputs!.isEmpty || selection.outputs!.isEmpty) { // throw Exception("coin selection failed"); // } // } // } }