From b4316581c8d41eea49b85914f73042823691b123 Mon Sep 17 00:00:00 2001 From: Serhii Date: Sun, 20 Aug 2023 21:46:09 +0300 Subject: [PATCH] add transaction creation --- cw_bitcoin/lib/bitcoin_address_record.dart | 2 +- cw_bitcoin/lib/bitcoin_unspent.dart | 4 + cw_bitcoin/lib/electrum_wallet.dart | 7 +- cw_bitcoin/pubspec.lock | 9 + cw_bitcoin/pubspec.yaml | 4 + .../lib/src/bitcoin_cash_base.dart | 1 - .../bitcoin_cash_transaction_priority.dart | 52 --- .../lib/src/bitcoin_cash_wallet.dart | 390 +++++++++++++++--- .../src/pending_bitcoin_cash_transaction.dart | 62 +++ lib/bitcoin/cw_bitcoin.dart | 4 +- lib/bitcoin_cash/cw_bitcoin_cash.dart | 6 +- lib/core/address_validator.dart | 3 +- lib/view_model/send/output.dart | 10 +- 13 files changed, 425 insertions(+), 129 deletions(-) delete mode 100644 cw_bitcoin_cash/lib/src/bitcoin_cash_transaction_priority.dart create mode 100644 cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart index 392771ab0..8f499fc98 100644 --- a/cw_bitcoin/lib/bitcoin_address_record.dart +++ b/cw_bitcoin/lib/bitcoin_address_record.dart @@ -19,7 +19,7 @@ class BitcoinAddressRecord { bool operator ==(Object o) => o is BitcoinAddressRecord && address == o.address; - final String address; + String address; final bool isHidden; final int index; bool get isUsed => _isUsed; diff --git a/cw_bitcoin/lib/bitcoin_unspent.dart b/cw_bitcoin/lib/bitcoin_unspent.dart index e5a0e8cac..1cd50c806 100644 --- a/cw_bitcoin/lib/bitcoin_unspent.dart +++ b/cw_bitcoin/lib/bitcoin_unspent.dart @@ -21,4 +21,8 @@ class BitcoinUnspent { bool isSending; bool isFrozen; String note; + + void updateAddress(String newAddress) { + address.address = newAddress; + } } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 3bafcf773..8aece846c 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:io'; import 'dart:math'; import 'package:bitcoin_flutter/bitcoin_flutter.dart'; +import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:hive/hive.dart'; @@ -11,6 +12,7 @@ import 'package:mobx/mobx.dart'; import 'package:rxdart/subjects.dart'; import 'package:flutter/foundation.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; +import 'package:bitbox/bitbox.dart'; import 'package:cw_bitcoin/electrum_transaction_info.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cw_bitcoin/address_to_output_script.dart'; @@ -191,7 +193,7 @@ abstract class ElectrumWalletBase } @override - Future createTransaction(Object credentials) async { + Future createTransaction(Object credentials) async { const minAmount = 546; final transactionCredentials = credentials as BitcoinTransactionCredentials; final inputs = []; @@ -668,7 +670,8 @@ abstract class ElectrumWalletBase final addresses = walletAddresses.addresses.toList(); final balanceFutures = >>[]; for (var i = 0; i < addresses.length; i++) { - final addressRecord = addresses[i]; + // walletInfo.type != WalletType.bitcoinCash ? Address : + final addressRecord = addresses[i] ; final sh = scriptHash(addressRecord.address, networkType: networkType); final balanceFuture = electrumClient.getBalance(sh); balanceFutures.add(balanceFuture); diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 4d864059f..6cf75f4f0 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -66,6 +66,15 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + bitbox: + dependency: "direct main" + description: + path: "." + ref: main + resolved-ref: ea65073efbaf395a5557e8cd7bd72f195cd7eb11 + url: "https://github.com/Serhii-Borodenko/cw_bitbox.git" + source: git + version: "1.0.1" bitcoin_flutter: dependency: "direct main" description: diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 481a41ac5..25d8c8fba 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -23,6 +23,10 @@ dependencies: git: url: https://github.com/cake-tech/bitcoin_flutter.git ref: cake-update-v2 + bitbox: + git: + url: https://github.com/Serhii-Borodenko/cw_bitbox.git + ref: main rxdart: ^0.27.5 unorm_dart: ^0.2.0 cryptography: ^2.0.5 diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_base.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_base.dart index e80df0763..7c6038f9b 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_base.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_base.dart @@ -2,7 +2,6 @@ export 'bitcoin_cash_balance.dart'; export 'bitcoin_cash_client.dart'; export 'bitcoin_cash_transaction_history.dart'; export 'bitcoin_cash_transaction_info.dart'; -export 'bitcoin_cash_transaction_priority.dart'; export 'bitcoin_cash_wallet.dart'; export 'bitcoin_cash_wallet_addresses.dart'; export 'bitcoin_cash_wallet_creation_credentials.dart'; diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_transaction_priority.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_transaction_priority.dart deleted file mode 100644 index 7a0ae36d1..000000000 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_transaction_priority.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; - -class BitcoinCashTransactionPriority extends BitcoinTransactionPriority { - const BitcoinCashTransactionPriority({required String title, required int raw}) - : super(title: title, raw: raw); - - static const List all = [fast, medium, slow]; - static const BitcoinCashTransactionPriority slow = - BitcoinCashTransactionPriority(title: 'Slow', raw: 0); - static const BitcoinCashTransactionPriority medium = - BitcoinCashTransactionPriority(title: 'Medium', raw: 1); - static const BitcoinCashTransactionPriority fast = - BitcoinCashTransactionPriority(title: 'Fast', raw: 2); - - static BitcoinCashTransactionPriority deserialize({required int raw}) { - switch (raw) { - case 0: - return slow; - case 1: - return medium; - case 2: - return fast; - default: - throw Exception('Unexpected token: $raw for BitcoinTransactionPriority deserialize'); - } - } - - String get units => 'sat'; - - @override - String toString() { - var label = ''; - - switch (this) { - case BitcoinCashTransactionPriority.slow: - label = 'Slow ~24hrs'; // '${S.current.transaction_priority_slow} ~24hrs'; - break; - case BitcoinCashTransactionPriority.medium: - label = 'Medium'; // S.current.transaction_priority_medium; - break; - case BitcoinCashTransactionPriority.fast: - label = 'Fast'; // S.current.transaction_priority_fast; - break; - default: - break; - } - - return label; - } - - String labelWithRate(int rate) => '${toString()} ($rate ${units}/byte)'; -} diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart index 427339e3f..aa4a4d6c8 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart @@ -1,14 +1,22 @@ +import 'package:bitbox/bitbox.dart' as bitbox; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; +import 'package:bitcoin_flutter/bitcoin_flutter.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; +import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; +import 'package:cw_bitcoin/bitcoin_transaction_no_inputs_exception.dart'; +import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; +import 'package:cw_bitcoin/bitcoin_transaction_wrong_balance_exception.dart'; import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_bitcoin/electrum_wallet_snapshot.dart'; -import 'package:cw_bitcoin/pending_bitcoin_transaction.dart'; +import 'package:cw_bitcoin_cash/src/pending_bitcoin_cash_transaction.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/pending_transaction.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:flutter/foundation.dart'; +import 'package:hex/hex.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; @@ -91,69 +99,325 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { } @override - Future createTransaction(Object credentials, - [List? unspents, Object? wallet]) async { + Future createTransaction(Object credentials) async { + final transactionCredentials = credentials as BitcoinTransactionCredentials; - // final utxoSigningData = await fetchBuildTxData(unspents as List, wallet as BitcoinCashWalletBase); - // final builder = bitbox.Bitbox.transactionBuilder(testnet: false); - // final utxosToUse = unspents as List; - // final _wallet = wallet as BitcoinCashWallet; - // print('unspents: ${unspents.first.address}'); - // - // List _utxos = []; - // for (var element in utxosToUse) { - // _utxos.add(bitbox.Utxo(element.hash, element.vout, - // bitbox.BitcoinCash.fromSatoshi(element.value), element.value, 0, 1)); - // } - // - // final signatures = []; - // int totalBalance = 0; - // - // _utxos.forEach((bitbox.Utxo utxo) { - // // add the utxo as an input for the transaction - // builder.addInput(utxo.txid, utxo.vout); - // - // final ec = utxoSigningData.firstWhere((e) => e.utxo.hash == utxo.txid).keyPair!; - // - // final bitboxEC = bitbox.ECPair.fromWIF(ec.toWIF()); - // - // // add a signature to the list to be used later - // signatures - // .add({"vin": signatures.length, "key_pair": bitboxEC, "original_amount": utxo.satoshis}); - // - // totalBalance += utxo.satoshis; - // }); - // - // // set an address to send the remaining balance to - // final outputAddress = "13Hvge9HRduGiXMfcJHFn6sggequmaKqsZ"; - // - // // if there is an unspent balance, create a spending transaction - // if (totalBalance > 0 && outputAddress != "") { - // // calculate the fee based on number of inputs and one expected output - // final fee = bitbox.BitcoinCash.getByteCount(signatures.length, 1); - // - // // calculate how much balance will be left over to spend after the fee - // final sendAmount = totalBalance - fee; - // - // // add the output based on the address provided in the testing data - // builder.addOutput(outputAddress, sendAmount); - // - // // sign all inputs - // signatures.forEach((signature) { - // builder.sign(signature["vin"], signature["key_pair"], signature["original_amount"]); - // }); - // - // // build the transaction - // final tx = builder.build(); - // - // // broadcast the transaction - // final result = await electrumClient.broadcastTransaction(transactionRaw: tx.toHex()); - // - // // Yatta! - // print("Transaction broadcasted: $result"); - // } - return PendingBitcoinTransaction(bitcoin.Transaction(), type, - electrumClient: electrumClient, amount: 1, fee: 1); + const minAmount = 546; + final inputs = []; + var allInputsAmount = 0; + final outputs = transactionCredentials.outputs; + final hasMultiDestination = outputs.length > 1; + + if (unspentCoins.isEmpty) { + await updateUnspent(); } + for (final utx in unspentCoins) { + if (utx.isSending) { + allInputsAmount += utx.value; + inputs.add(utx); + } + } + + if (inputs.isEmpty) { + throw BitcoinTransactionNoInputsException(); + } + + final int feeRate = transactionCredentials.feeRate != null + ? transactionCredentials.feeRate! + : BitcoinCashFeeRates.feeRate(transactionCredentials.priority!); + + final int allAmountFee = + bitbox.BitcoinCash.getByteCount(inputs.length, transactionCredentials.outputs.length) * + feeRate; + + final allAmount = allInputsAmount - allAmountFee; + var credentialsAmount = 0; + var amount = 0; + var fee = 0; + + if (hasMultiDestination) { + if (outputs.any((item) => item.sendAll || item.formattedCryptoAmount! <= 0)) { + throw BitcoinTransactionWrongBalanceException(currency); + } + + credentialsAmount = outputs.fold(0, (acc, value) { + return acc + value.formattedCryptoAmount!; + }); + + print(credentialsAmount); + + if (allAmount - credentialsAmount < minAmount) { + throw BitcoinTransactionWrongBalanceException(currency); + } + + amount = credentialsAmount; + + if (transactionCredentials.feeRate != null) { + fee = calculateEstimatedFeeWithFeeRate(transactionCredentials.feeRate!, amount, + outputsCount: outputs.length + 1); + } else { + fee = calculateEstimatedFee(transactionCredentials.priority, amount, + outputsCount: outputs.length + 1); + } + } else { + final output = outputs.first; + credentialsAmount = !output.sendAll ? output.formattedCryptoAmount! : 0; + + if (credentialsAmount > allAmount) { + throw BitcoinTransactionWrongBalanceException(currency); + } + + amount = output.sendAll || allAmount - credentialsAmount < minAmount + ? allAmount + : credentialsAmount; + + if (output.sendAll || amount == allAmount) { + fee = allAmountFee; + } else if (transactionCredentials.feeRate != null) { + fee = calculateEstimatedFeeWithFeeRate(transactionCredentials.feeRate!, amount); + } else { + fee = calculateEstimatedFee(transactionCredentials.priority, amount); + } + } + + if (fee == 0) { + throw BitcoinTransactionWrongBalanceException(currency); + } + + final totalAmount = amount + fee; + + if (totalAmount > balance[currency]!.confirmed || totalAmount > allInputsAmount) { + throw BitcoinTransactionWrongBalanceException(currency); + } + + // final changeAddress = await walletAddresses.getChangeAddress(); TODO: BCH: implement change address + var leftAmount = totalAmount; + var totalInputAmount = 0; + + inputs.clear(); + + for (final utx in unspentCoins) { + if (utx.isSending) { + leftAmount = leftAmount - utx.value; + totalInputAmount += utx.value; + inputs.add(utx); + + if (leftAmount <= 0) { + break; + } + } + } + + if (inputs.isEmpty) { + throw BitcoinTransactionNoInputsException(); + } + + if (amount <= 0 || totalInputAmount < totalAmount) { + throw BitcoinTransactionWrongBalanceException(currency); + } + + final builder = bitbox.Bitbox.transactionBuilder(testnet: false); + final _wallet = hd; + + final utxoSigningData = await fetchBuildTxData(inputs, _wallet); + + List _utxos = []; + for (var element in inputs) { + _utxos.add(bitbox.Utxo(element.hash, element.vout, + bitbox.BitcoinCash.fromSatoshi(element.value), element.value, 0, 1)); + } + + final signatures = []; + int totalBalance = 0; + + _utxos.forEach((bitbox.Utxo utxo) { + builder.addInput(utxo.txid, utxo.vout); + + final ec = utxoSigningData.firstWhere((e) => e.utxo.hash == utxo.txid).keyPair!; + + final bitboxEC = bitbox.ECPair.fromWIF(ec.toWIF()); + + signatures + .add({"vin": signatures.length, "key_pair": bitboxEC, "original_amount": utxo.satoshis}); + + totalBalance += utxo.satoshis; + }); + + outputs.forEach((item) { + final outputAmount = hasMultiDestination ? item.formattedCryptoAmount : amount; + final outputAddress = item.isParsedAddress ? item.extractedAddress! : item.address; + builder.addOutput(outputAddress, outputAmount!); + }); + + signatures.forEach((signature) { + builder.sign(signature["vin"], signature["key_pair"], signature["original_amount"]); + }); + + // build the transaction + final tx = builder.build(); + return PendingBitcoinCashTransaction(tx, type, + electrumClient: electrumClient, amount: amount, fee: fee) + ..addListener((transaction) async { + transactionHistory.addOne(transaction); + await updateBalance(); + }); } + + Future> fetchBuildTxData( + List utxosToUse, HDWallet wallet) async { + // Initialize the list to store signing data + List signingData = []; + + try { + // Iterate over UTXOs to populate the addresses and fetch transaction details + for (var i = 0; i < utxosToUse.length; i++) { + final txid = utxosToUse[i].hash; + final tx = await electrumClient.getTransactionRaw( + hash: txid); //TODO: BCH: replace with getting from local storage if possible + + // Iterate through transaction outputs to find corresponding addresses + for (final output in tx["vout"] as List) { + // Handle each transaction output + await handleTransactionOutput(output, utxosToUse[i]); + } + + // Determine address type and create signing data object + signingData.add(SigningData( + derivePathType: DerivePathType.bch44, + utxo: utxosToUse[i])); //TODO: BCH: hardcoded DerivePathType.bch44 + + // Determine public key (pubKey) and Wallet Import Format (wif) here + // TODO: You need to implement logic to determine pubKey and wif + String? pubKey = wallet.pubKey; + String? wif = wallet.wif; + + // Then call the preparePaymentData function + preparePaymentData( + signingData[i], pubKey, wif, bitcoincash); //TODO: BCH: hardcoded bitcoincash + } + + // Return the signing data for later use + return signingData; + } catch (e) { + print(e); + rethrow; + } + } + +// Function to handle each transaction output + Future handleTransactionOutput(Map output, BitcoinUnspent utxo) async { + final n = output["n"]; + if (n != null && n == utxo.vout) { + String address = output["scriptPubKey"]?["addresses"]?[0] as String? ?? + output["scriptPubKey"]["address"] as String; + + // Convert to Cash Address format if needed + if (bitbox.Address.detectFormat(address) != bitbox.Address.formatCashAddr) { + try { + address = bitbox.Address.toCashAddress(address); + } catch (_) { + rethrow; + } + } + + // Update UTXO with the new address + utxo.updateAddress(address); // Make sure 'address' is mutable or create a method to update it + } + } + +// Function to prepare payment data + void preparePaymentData( + SigningData sd, String? pubKey, String? wif, bitcoin.NetworkType _network) { + if (wif != null && pubKey != null) { + PaymentData data; // Removed 'final' modifier + final Uint8List? redeemScript; + + switch (sd.derivePathType) { + case DerivePathType.bip44: + case DerivePathType.bch44: + data = P2PKH( + data: PaymentData( + pubkey: Uint8List.fromList(HEX.decode(pubKey)), + ), + network: _network, + ).data; + redeemScript = null; + break; + + default: + throw Exception("DerivePathType unsupported"); + } + + final keyPair = ECPair.fromWIF( + wif, + network: _network, + ); + + sd.redeemScript = redeemScript; + sd.output = data.output; + sd.keyPair = keyPair; + } + } +} + +class SigningData { + SigningData({ + required this.derivePathType, + required this.utxo, + this.output, + this.keyPair, + this.redeemScript, + }); + + final DerivePathType derivePathType; + final BitcoinUnspent utxo; + Uint8List? output; + ECPair? keyPair; + Uint8List? redeemScript; +} + +enum DerivePathType { + bip44, + bch44, + bip49, + bip84, + eth, + eCash44, +} + +// Bitcoincash Network +final bitcoincash = bitcoin.NetworkType( + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'bc', + bip32: bitcoin.Bip32Type(public: 0x0488b21e, private: 0x0488ade4), + pubKeyHash: 0x00, + scriptHash: 0x05, + wif: 0x80); + +final bitcoincashtestnet = bitcoin.NetworkType( + messagePrefix: '\x18Bitcoin Signed Message:\n', + bech32: 'tb', + bip32: bitcoin.Bip32Type(public: 0x043587cf, private: 0x04358394), + pubKeyHash: 0x6f, + scriptHash: 0xc4, + wif: 0xef); + +class BitcoinCashFeeRates { + static const int highPriority = 10; + static const int mediumPriority = 5; + static const int lowPriority = 1; + + static int feeRate(BitcoinTransactionPriority priority) { + switch (priority) { + case BitcoinTransactionPriority.fast: + return highPriority; + case BitcoinTransactionPriority.medium: + return mediumPriority; + case BitcoinTransactionPriority.slow: + return lowPriority; + default: + throw Exception("Unknown priority level"); + } + } +} diff --git a/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart b/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart new file mode 100644 index 000000000..d5ac36ce2 --- /dev/null +++ b/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart @@ -0,0 +1,62 @@ +import 'package:cw_bitcoin/bitcoin_commit_transaction_exception.dart'; +import 'package:bitbox/bitbox.dart' as bitbox; +import 'package:cw_core/pending_transaction.dart'; +import 'package:cw_bitcoin/electrum.dart'; +import 'package:cw_bitcoin/bitcoin_amount_format.dart'; +import 'package:cw_bitcoin/electrum_transaction_info.dart'; +import 'package:cw_core/transaction_direction.dart'; +import 'package:cw_core/wallet_type.dart'; + +class PendingBitcoinCashTransaction with PendingTransaction { + PendingBitcoinCashTransaction(this._tx, this.type, + {required this.electrumClient, + required this.amount, + required this.fee}) + : _listeners = []; + + final WalletType type; + final bitbox.Transaction _tx; + final ElectrumClient electrumClient; + final int amount; + final int fee; + + @override + String get id => _tx.getId(); + + @override + String get hex => _tx.toHex(); + + @override + String get amountFormatted => bitcoinAmountToString(amount: amount); + + @override + String get feeFormatted => bitcoinAmountToString(amount: fee); + + final List _listeners; + + @override + Future commit() async { + final result = + await electrumClient.broadcastTransaction(transactionRaw: _tx.toHex()); + + if (result.isEmpty) { + throw BitcoinCommitTransactionException(); + } + + _listeners?.forEach((listener) => listener(transactionInfo())); + } + + void addListener( + void Function(ElectrumTransactionInfo transaction) listener) => + _listeners.add(listener); + + ElectrumTransactionInfo transactionInfo() => ElectrumTransactionInfo(type, + id: id, + height: 0, + amount: amount, + direction: TransactionDirection.outgoing, + date: DateTime.now(), + isPending: true, + confirmations: 0, + fee: fee); +} diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 71e87cd00..b6525e1e2 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -50,7 +50,7 @@ class CWBitcoin extends Bitcoin { @override List getBitcoinCashTransactionPriorities() - => BitcoinCashTransactionPriority.all; + => BitcoinTransactionPriority.all; @override TransactionPriority deserializeBitcoinTransactionPriority(int raw) @@ -62,7 +62,7 @@ class CWBitcoin extends Bitcoin { @override TransactionPriority deserializeBitcoinCashTransactionPriority(int raw) - => BitcoinCashTransactionPriority.deserialize(raw: raw); + => BitcoinTransactionPriority.deserialize(raw: raw); @override int getFeeRate(Object wallet, TransactionPriority priority) { diff --git a/lib/bitcoin_cash/cw_bitcoin_cash.dart b/lib/bitcoin_cash/cw_bitcoin_cash.dart index 50643a492..b39a4b9ec 100644 --- a/lib/bitcoin_cash/cw_bitcoin_cash.dart +++ b/lib/bitcoin_cash/cw_bitcoin_cash.dart @@ -28,11 +28,11 @@ class CWBitcoinCash extends BitcoinCash { @override TransactionPriority deserializeBitcoinCashTransactionPriority(int raw) => - BitcoinCashTransactionPriority.deserialize(raw: raw); + BitcoinTransactionPriority.deserialize(raw: raw); @override - TransactionPriority getDefaultTransactionPriority() => BitcoinCashTransactionPriority.medium; + TransactionPriority getDefaultTransactionPriority() => BitcoinTransactionPriority.medium; @override - List getTransactionPriorities() => BitcoinCashTransactionPriority.all; + List getTransactionPriorities() => BitcoinTransactionPriority.all; } diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index 121e6c231..de35b23d9 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -87,6 +87,7 @@ class AddressValidator extends TextValidator { case CryptoCurrency.dash: case CryptoCurrency.eos: case CryptoCurrency.bch: + return '[0-9a-zA-Z,:]'; case CryptoCurrency.bnb: return '[0-9a-zA-Z]'; case CryptoCurrency.ltc: @@ -171,7 +172,7 @@ class AddressValidator extends TextValidator { case CryptoCurrency.shib: case CryptoCurrency.avaxc: case CryptoCurrency.bch: - return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44]; //TODO: BCH: replace with correct length + return [32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,54,55,56]; //TODO: BCH: replace with correct length case CryptoCurrency.bnb: return [42]; case CryptoCurrency.ltc: diff --git a/lib/view_model/send/output.dart b/lib/view_model/send/output.dart index 8008812ba..2e696e16f 100644 --- a/lib/view_model/send/output.dart +++ b/lib/view_model/send/output.dart @@ -81,10 +81,8 @@ abstract class OutputBase with Store { _amount = monero!.formatterMoneroParseAmount(amount: _cryptoAmount); break; case WalletType.bitcoin: - _amount = - bitcoin!.formatterStringDoubleToBitcoinAmount(_cryptoAmount); - break; case WalletType.litecoin: + case WalletType.bitcoinCash: _amount = bitcoin!.formatterStringDoubleToBitcoinAmount(_cryptoAmount); break; @@ -116,7 +114,8 @@ abstract class OutputBase with Store { _settingsStore.priority[_wallet.type]!, formattedCryptoAmount); if (_wallet.type == WalletType.bitcoin || - _wallet.type == WalletType.litecoin) { + _wallet.type == WalletType.litecoin || + _wallet.type == WalletType.bitcoinCash) { return bitcoin!.formatterBitcoinAmountToDouble(amount: fee); } @@ -234,6 +233,9 @@ abstract class OutputBase with Store { case WalletType.litecoin: maximumFractionDigits = 8; break; + case WalletType.bitcoinCash: + maximumFractionDigits = 8; + break; case WalletType.haven: maximumFractionDigits = 12; break;