diff --git a/lib/wallets/wallet/impl/bitcoincash_wallet.dart b/lib/wallets/wallet/impl/bitcoincash_wallet.dart index 345a3f170..d617dc64f 100644 --- a/lib/wallets/wallet/impl/bitcoincash_wallet.dart +++ b/lib/wallets/wallet/impl/bitcoincash_wallet.dart @@ -16,12 +16,17 @@ import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/wallets/crypto_currency/coins/bitcoincash.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart'; +import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/bcash_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart'; class BitcoincashWallet extends Bip39HDWallet - with ElectrumXInterface, CoinControlInterface, CashFusionInterface { + with + ElectrumXInterface, + BCashInterface, + CoinControlInterface, + CashFusionInterface { @override int get isarTransactionVersion => 2; diff --git a/lib/wallets/wallet/impl/ecash_wallet.dart b/lib/wallets/wallet/impl/ecash_wallet.dart index a53ef0f14..2946c6069 100644 --- a/lib/wallets/wallet/impl/ecash_wallet.dart +++ b/lib/wallets/wallet/impl/ecash_wallet.dart @@ -16,12 +16,17 @@ import 'package:stackwallet/utilities/logger.dart'; import 'package:stackwallet/wallets/crypto_currency/coins/ecash.dart'; import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart'; +import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/bcash_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/cash_fusion_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/coin_control_interface.dart'; import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart'; class EcashWallet extends Bip39HDWallet - with ElectrumXInterface, CoinControlInterface, CashFusionInterface { + with + ElectrumXInterface, + BCashInterface, + CoinControlInterface, + CashFusionInterface { @override int get isarTransactionVersion => 2; @@ -299,8 +304,12 @@ class EcashWallet extends Bip39HDWallet } @override - Future<({String? blockedReason, bool blocked, String? utxoLabel})> - checkBlockUTXO( + Future< + ({ + String? blockedReason, + bool blocked, + String? utxoLabel, + })> checkBlockUTXO( Map jsonUTXO, String? scriptPubKeyHex, Map jsonTX, diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/bcash_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/bcash_interface.dart new file mode 100644 index 000000000..e9a3ffab3 --- /dev/null +++ b/lib/wallets/wallet/wallet_mixin_interfaces/bcash_interface.dart @@ -0,0 +1,134 @@ +import 'package:bitbox/bitbox.dart' as bitbox; +import 'package:isar/isar.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/v2/input_v2.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/v2/output_v2.dart'; +import 'package:stackwallet/models/isar/models/blockchain_data/v2/transaction_v2.dart'; +import 'package:stackwallet/models/isar/models/isar_models.dart'; +import 'package:stackwallet/models/signing_data.dart'; +import 'package:stackwallet/utilities/logger.dart'; +import 'package:stackwallet/wallets/crypto_currency/crypto_currency.dart'; +import 'package:stackwallet/wallets/models/tx_data.dart'; +import 'package:stackwallet/wallets/wallet/intermediate/bip39_hd_wallet.dart'; +import 'package:stackwallet/wallets/wallet/wallet_mixin_interfaces/electrumx_interface.dart'; + +mixin BCashInterface on Bip39HDWallet, ElectrumXInterface { + @override + Future buildTransaction({ + required TxData txData, + required List utxoSigningData, + }) async { + Logging.instance + .log("Starting buildTransaction ----------", level: LogLevel.Info); + + // TODO: use coinlib + + final builder = bitbox.Bitbox.transactionBuilder( + testnet: cryptoCurrency.network == CryptoCurrencyNetwork.test, + ); + + // temp tx data to show in gui while waiting for real data from server + final List tempInputs = []; + final List tempOutputs = []; + + // Add transaction inputs + for (int i = 0; i < utxoSigningData.length; i++) { + builder.addInput( + utxoSigningData[i].utxo.txid, + utxoSigningData[i].utxo.vout, + ); + + tempInputs.add( + InputV2.isarCantDoRequiredInDefaultConstructor( + scriptSigHex: "000000", + scriptSigAsm: null, + sequence: 0xffffffff - 1, + outpoint: OutpointV2.isarCantDoRequiredInDefaultConstructor( + txid: utxoSigningData[i].utxo.txid, + vout: utxoSigningData[i].utxo.vout, + ), + addresses: utxoSigningData[i].utxo.address == null + ? [] + : [utxoSigningData[i].utxo.address!], + valueStringSats: utxoSigningData[i].utxo.value.toString(), + witness: null, + innerRedeemScriptAsm: null, + coinbase: null, + walletOwns: true, + ), + ); + } + + // Add transaction output + for (var i = 0; i < txData.recipients!.length; i++) { + builder.addOutput( + normalizeAddress(txData.recipients![i].address), + txData.recipients![i].amount.raw.toInt(), + ); + + tempOutputs.add( + OutputV2.isarCantDoRequiredInDefaultConstructor( + scriptPubKeyHex: "000000", + valueStringSats: txData.recipients![i].amount.raw.toString(), + addresses: [ + txData.recipients![i].address.toString(), + ], + walletOwns: (await mainDB.isar.addresses + .where() + .walletIdEqualTo(walletId) + .filter() + .valueEqualTo(txData.recipients![i].address) + .or() + .valueEqualTo(normalizeAddress(txData.recipients![i].address)) + .valueProperty() + .findFirst()) != + null, + ), + ); + } + + try { + // Sign the transaction accordingly + for (int i = 0; i < utxoSigningData.length; i++) { + final bitboxEC = bitbox.ECPair.fromWIF( + utxoSigningData[i].keyPair!.toWIF(), + ); + + builder.sign( + i, + bitboxEC, + utxoSigningData[i].utxo.value, + ); + } + } catch (e, s) { + Logging.instance.log("Caught exception while signing transaction: $e\n$s", + level: LogLevel.Error); + rethrow; + } + + final builtTx = builder.build(); + final vSize = builtTx.virtualSize(); + + return txData.copyWith( + raw: builtTx.toHex(), + vSize: vSize, + tempTx: TransactionV2( + walletId: walletId, + blockHash: null, + hash: builtTx.getId(), + txid: builtTx.getId(), + height: null, + timestamp: DateTime.timestamp().millisecondsSinceEpoch ~/ 1000, + inputs: List.unmodifiable(tempInputs), + outputs: List.unmodifiable(tempOutputs), + version: builtTx.version, + type: + tempOutputs.map((e) => e.walletOwns).fold(true, (p, e) => p &= e) && + txData.paynymAccountLite == null + ? TransactionType.sentToSelf + : TransactionType.outgoing, + subType: TransactionSubType.none, + otherData: null, + ), + ); + } +}