diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt index d8d4ed830..83e18c18e 100644 --- a/assets/text/Release_Notes.txt +++ b/assets/text/Release_Notes.txt @@ -1,5 +1 @@ -Monero enhancements -Bitcoin support different address types (Taproot, Segwit P2WPKH/P2WSH, Legacy) -In-App live status page for the app services -Add Exolix exchange provider Bug fixes and enhancements \ No newline at end of file diff --git a/cw_bitcoin/lib/address_to_output_script.dart b/cw_bitcoin/lib/address_to_output_script.dart index 6ae50132b..892f7a0d6 100644 --- a/cw_bitcoin/lib/address_to_output_script.dart +++ b/cw_bitcoin/lib/address_to_output_script.dart @@ -3,6 +3,9 @@ import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin; List addressToOutputScript(String address, bitcoin.BasedUtxoNetwork network) { try { + if (network == bitcoin.BitcoinCashNetwork.mainnet) { + return bitcoin.BitcoinCashAddress(address).baseAddress.toScriptPubKey().toBytes(); + } return bitcoin.addressToOutputScript(address: address, network: network); } catch (err) { print(err); diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart index d8d908230..d1c3b6a61 100644 --- a/cw_bitcoin/lib/bitcoin_address_record.dart +++ b/cw_bitcoin/lib/bitcoin_address_record.dart @@ -1,5 +1,4 @@ import 'dart:convert'; -import 'package:bitbox/bitbox.dart' as bitbox; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/script_hash.dart' as sh; @@ -20,10 +19,9 @@ class BitcoinAddressRecord { _balance = balance, _name = name, _isUsed = isUsed, - scriptHash = - scriptHash ?? (network != null ? sh.scriptHash(address, network: network) : null); + scriptHash = scriptHash ?? sh.scriptHash(address, network: network); - factory BitcoinAddressRecord.fromJSON(String jsonSource, BasedUtxoNetwork? network) { + factory BitcoinAddressRecord.fromJSON(String jsonSource, BasedUtxoNetwork network) { final decoded = json.decode(jsonSource) as Map; return BitcoinAddressRecord( @@ -39,9 +37,7 @@ class BitcoinAddressRecord { .firstWhere((type) => type.toString() == decoded['type'] as String) : SegwitAddresType.p2wpkh, scriptHash: decoded['scriptHash'] as String?, - network: (decoded['network'] as String?) == null - ? network - : BasedUtxoNetwork.fromName(decoded['network'] as String), + network: network, ); } @@ -56,7 +52,7 @@ class BitcoinAddressRecord { String _name; bool _isUsed; String? scriptHash; - BasedUtxoNetwork? network; + BasedUtxoNetwork network; int get txCount => _txCount; @@ -76,8 +72,6 @@ class BitcoinAddressRecord { @override int get hashCode => address.hashCode; - String get cashAddr => bitbox.Address.toCashAddress(address); - BitcoinAddressType type; String updateScriptHash(BasedUtxoNetwork network) { @@ -95,6 +89,5 @@ class BitcoinAddressRecord { 'balance': balance, 'type': type.toString(), 'scriptHash': scriptHash, - 'network': network?.value, }); } diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index 3b3e9c636..bf59e8637 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -92,8 +92,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { required Box unspentCoinsInfo, required String password, }) async { - final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, - walletInfo.network != null ? BasedUtxoNetwork.fromName(walletInfo.network!) : null); + final network = walletInfo.network != null + ? BasedUtxoNetwork.fromName(walletInfo.network!) + : BitcoinNetwork.mainnet; + final snp = await ElectrumWalletSnapshot.load(name, walletInfo.type, password, network); return BitcoinWallet( mnemonic: snp.mnemonic, @@ -106,7 +108,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { initialRegularAddressIndex: snp.regularAddressIndex, initialChangeAddressIndex: snp.changeAddressIndex, addressPageType: snp.addressPageType, - networkParam: snp.network, + networkParam: network, ); } } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index c3f40a235..86fbd6dbe 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -75,11 +75,7 @@ abstract class ElectrumWalletBase } : {}), this.unspentCoinsInfo = unspentCoinsInfo, - this.network = networkType == bitcoin.bitcoin - ? BitcoinNetwork.mainnet - : networkType == litecoinNetwork - ? LitecoinNetwork.mainnet - : BitcoinNetwork.testnet, + this.network = _getNetwork(networkType, currency), this.isTestnet = networkType == bitcoin.testnet, super(walletInfo) { this.electrumClient = electrumClient ?? ElectrumClient(); @@ -192,12 +188,13 @@ abstract class ElectrumWalletBase } } - Future _estimateTxFeeAndInputsToUse( + Future estimateTxFeeAndInputsToUse( int credentialsAmount, bool sendAll, List outputAddresses, List outputs, - BitcoinTransactionCredentials transactionCredentials, + int? feeRate, + BitcoinTransactionPriority? priority, {int? inputsCount}) async { final utxos = []; List privateKeys = []; @@ -212,7 +209,7 @@ abstract class ElectrumWalletBase allInputsAmount += utx.value; leftAmount = leftAmount - utx.value; - final address = _addressTypeFromStr(utx.address, network); + final address = addressTypeFromStr(utx.address, network); final privkey = generateECPrivate( hd: utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd, index: utx.bitcoinAddressRecord.index, @@ -249,7 +246,7 @@ abstract class ElectrumWalletBase if (!sendAll) { if (changeValue > 0) { final changeAddress = await walletAddresses.getChangeAddress(); - final address = _addressTypeFromStr(changeAddress, network); + final address = addressTypeFromStr(changeAddress, network); outputAddresses.add(address); outputs.add(BitcoinOutput(address: address, value: BigInt.from(changeValue))); } @@ -258,9 +255,9 @@ abstract class ElectrumWalletBase final estimatedSize = BitcoinTransactionBuilder.estimateTransactionSize( utxos: utxos, outputs: outputs, network: network); - final fee = transactionCredentials.feeRate != null - ? feeAmountWithFeeRate(transactionCredentials.feeRate!, 0, 0, size: estimatedSize) - : feeAmountForPriority(transactionCredentials.priority!, 0, 0, size: estimatedSize); + int fee = feeRate != null + ? feeAmountWithFeeRate(feeRate, 0, 0, size: estimatedSize) + : feeAmountForPriority(priority!, 0, 0, size: estimatedSize); if (fee == 0) { throw BitcoinTransactionWrongBalanceException(currency); @@ -297,8 +294,8 @@ abstract class ElectrumWalletBase outputs.removeLast(); } - return _estimateTxFeeAndInputsToUse( - credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials, + return estimateTxFeeAndInputsToUse( + credentialsAmount, sendAll, outputAddresses, outputs, feeRate, priority, inputsCount: utxos.length + 1); } } @@ -319,7 +316,7 @@ abstract class ElectrumWalletBase for (final out in transactionCredentials.outputs) { final outputAddress = out.isParsedAddress ? out.extractedAddress! : out.address; - final address = _addressTypeFromStr(outputAddress, network); + final address = addressTypeFromStr(outputAddress, network); outputAddresses.add(address); @@ -344,8 +341,14 @@ abstract class ElectrumWalletBase } } - final estimatedTx = await _estimateTxFeeAndInputsToUse( - credentialsAmount, sendAll, outputAddresses, outputs, transactionCredentials); + final estimatedTx = await estimateTxFeeAndInputsToUse( + credentialsAmount, + sendAll, + outputAddresses, + outputs, + transactionCredentials.feeRate, + transactionCredentials.priority, + ); final txb = BitcoinTransactionBuilder( utxos: estimatedTx.utxos, @@ -391,7 +394,6 @@ abstract class ElectrumWalletBase ? SegwitAddresType.p2wpkh.toString() : walletInfo.addressPageType.toString(), 'balance': balance[currency]?.toJSON(), - 'network_type': network == BitcoinNetwork.testnet ? 'testnet' : 'mainnet', }); int feeRate(TransactionPriority priority) { @@ -852,6 +854,22 @@ abstract class ElectrumWalletBase final HD = index == null ? hd : hd.derive(index); return base64Encode(HD.signMessage(message)); } + + static BasedUtxoNetwork _getNetwork(bitcoin.NetworkType networkType, CryptoCurrency? currency) { + if (networkType == bitcoin.bitcoin && currency == CryptoCurrency.bch) { + return BitcoinCashNetwork.mainnet; + } + + if (networkType == litecoinNetwork) { + return LitecoinNetwork.mainnet; + } + + if (networkType == bitcoin.testnet) { + return BitcoinNetwork.testnet; + } + + return BitcoinNetwork.mainnet; + } } class EstimateTxParams { @@ -879,7 +897,7 @@ class EstimatedTxResult { final int amount; } -BitcoinBaseAddress _addressTypeFromStr(String address, BasedUtxoNetwork network) { +BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) { if (P2pkhAddress.regex.hasMatch(address)) { return P2pkhAddress.fromAddress(address: address, network: network); } else if (P2shAddress.regex.hasMatch(address)) { diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 5880f5a19..828bda8af 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -1,6 +1,5 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; -import 'package:bitbox/bitbox.dart' as bitbox; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum.dart'; import 'package:cw_core/wallet_addresses.dart'; @@ -30,6 +29,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { List? initialAddresses, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, + BitcoinAddressType? initialAddressPageType, }) : _addresses = ObservableList.of((initialAddresses ?? []).toSet()), addressesByReceiveType = ObservableList.of(([]).toSet()), @@ -41,9 +41,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { .toSet()), currentReceiveAddressIndexByType = initialRegularAddressIndex ?? {}, currentChangeAddressIndexByType = initialChangeAddressIndex ?? {}, - _addressPageType = walletInfo.addressPageType != null - ? BitcoinAddressType.fromValue(walletInfo.addressPageType!) - : SegwitAddresType.p2wpkh, + _addressPageType = initialAddressPageType ?? + (walletInfo.addressPageType != null + ? BitcoinAddressType.fromValue(walletInfo.addressPageType!) + : SegwitAddresType.p2wpkh), super(walletInfo) { updateAddressesByMatch(); } @@ -52,10 +53,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { static const defaultChangeAddressesCount = 17; static const gap = 20; - static String toCashAddr(String address) => bitbox.Address.toCashAddress(address); - - static String toLegacy(String address) => bitbox.Address.toLegacyAddress(address); - final ObservableList _addresses; // Matched by addressPageType late ObservableList addressesByReceiveType; @@ -67,7 +64,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { final bitcoin.HDWallet sideHd; @observable - BitcoinAddressType _addressPageType = SegwitAddresType.p2wpkh; + late BitcoinAddressType _addressPageType; @computed BitcoinAddressType get addressPageType => _addressPageType; @@ -97,7 +94,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } } - return walletInfo.type == WalletType.bitcoinCash ? toCashAddr(receiveAddress) : receiveAddress; + return receiveAddress; } @observable @@ -105,9 +102,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @override set address(String addr) { - if (addr.startsWith('bitcoincash:')) { - addr = toLegacy(addr); - } final addressRecord = _addresses.firstWhere((addressRecord) => addressRecord.address == addr); previousAddressRecord = addressRecord; @@ -155,11 +149,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @override Future init() async { - await _generateInitialAddresses(); - await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); - await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh); - await _generateInitialAddresses(type: SegwitAddresType.p2tr); - await _generateInitialAddresses(type: SegwitAddresType.p2wsh); + if (walletInfo.type == WalletType.bitcoinCash) { + await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); + } else if (walletInfo.type == WalletType.litecoin) { + await _generateInitialAddresses(); + } else if (walletInfo.type == WalletType.bitcoin) { + await _generateInitialAddresses(); + await _generateInitialAddresses(type: P2pkhAddressType.p2pkh); + await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh); + await _generateInitialAddresses(type: SegwitAddresType.p2tr); + await _generateInitialAddresses(type: SegwitAddresType.p2wsh); + } updateAddressesByMatch(); updateReceiveAddresses(); updateChangeAddresses(); @@ -229,9 +229,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { @action void updateAddress(String address, String label) { - if (address.startsWith('bitcoincash:')) { - address = toLegacy(address); - } final addressRecord = _addresses.firstWhere((addressRecord) => addressRecord.address == address); addressRecord.setNewName(label); @@ -261,7 +258,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { addressRecord.isHidden && !addressRecord.isUsed && // TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type - addressRecord.type == SegwitAddresType.p2wpkh); + (walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddresType.p2wpkh)); changeAddresses.addAll(newAddresses); } diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index 98c3753db..6f76ab312 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -17,14 +17,12 @@ class ElectrumWalletSnapshot { required this.regularAddressIndex, required this.changeAddressIndex, required this.addressPageType, - required this.network, }); final String name; final String password; final WalletType type; - final String addressPageType; - final BasedUtxoNetwork network; + final String? addressPageType; String mnemonic; List addresses; @@ -32,7 +30,8 @@ class ElectrumWalletSnapshot { Map regularAddressIndex; Map changeAddressIndex; - static Future load(String name, WalletType type, String password, BasedUtxoNetwork? network) async { + static Future load( + String name, WalletType type, String password, BasedUtxoNetwork network) async { final path = await pathForWallet(name: name, type: type); final jsonSource = await read(path: path, password: password); final data = json.decode(jsonSource) as Map; @@ -71,8 +70,7 @@ class ElectrumWalletSnapshot { balance: balance, regularAddressIndex: regularAddressIndexByType, changeAddressIndex: changeAddressIndexByType, - addressPageType: data['address_page_type'] as String? ?? SegwitAddresType.p2wpkh.toString(), - network: data['network_type'] == 'testnet' ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet, + addressPageType: data['address_page_type'] as String?, ); } } diff --git a/cw_bitcoin/lib/script_hash.dart b/cw_bitcoin/lib/script_hash.dart index 620d3d28a..2130fcbbe 100644 --- a/cw_bitcoin/lib/script_hash.dart +++ b/cw_bitcoin/lib/script_hash.dart @@ -1,8 +1,9 @@ -import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:crypto/crypto.dart'; +import 'package:cw_bitcoin/address_to_output_script.dart'; +import 'package:bitcoin_base/bitcoin_base.dart' as bitcoin; -String scriptHash(String address, {required BasedUtxoNetwork network}) { - final outputScript = addressToOutputScript(address: address, network: network); +String scriptHash(String address, {required bitcoin.BasedUtxoNetwork network}) { + final outputScript = addressToOutputScript(address, network); final parts = sha256.convert(outputScript).toString().split(''); var res = ''; diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 25e6f269d..b39dcae07 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -79,11 +79,11 @@ packages: dependency: "direct main" description: path: "." - ref: cake-update-v1 - resolved-ref: "9611e9db77e92a8434e918cdfb620068f6fcb1aa" + ref: cake-update-v2 + resolved-ref: "3fd81d238b990bb767fc7a4fdd5053a22a142e2e" url: "https://github.com/cake-tech/bitcoin_base.git" source: git - version: "4.0.0" + version: "4.2.0" bitcoin_flutter: dependency: "direct main" description: @@ -97,10 +97,10 @@ packages: dependency: "direct main" description: name: blockchain_utils - sha256: "9701dfaa74caad4daae1785f1ec4445cf7fb94e45620bc3a4aca1b9b281dc6c9" + sha256: "38ef5f4a22441ac4370aed9071dc71c460acffc37c79b344533f67d15f24c13c" url: "https://pub.dev" source: hosted - version: "1.6.0" + version: "2.1.1" boolean_selector: dependency: transitive description: diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 847b77773..bcbb55e11 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -33,8 +33,8 @@ dependencies: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base.git - ref: cake-update-v1 - blockchain_utils: ^1.6.0 + ref: cake-update-v2 + blockchain_utils: ^2.1.1 dev_dependencies: flutter_test: diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart index 3c40cf9e9..f5835e728 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart @@ -34,7 +34,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { required WalletInfo walletInfo, required Box unspentCoinsInfo, required Uint8List seedBytes, - String? addressPageType, + BitcoinAddressType? addressPageType, List? initialAddresses, ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, @@ -58,6 +58,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { mainHd: hd, sideHd: bitcoin.HDWallet.fromSeed(seedBytes).derivePath("m/44'/145'/0'/1"), network: network, + initialAddressPageType: addressPageType, ); autorun((_) { this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress; @@ -84,7 +85,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { seedBytes: await Mnemonic.toSeed(mnemonic), initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, - addressPageType: addressPageType, + addressPageType: P2pkhAddressType.p2pkh, ); } @@ -101,12 +102,31 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { password: password, walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfo, - initialAddresses: snp.addresses, + initialAddresses: snp.addresses.map((addr) { + try { + BitcoinCashAddress(addr.address); + return BitcoinAddressRecord( + addr.address, + index: addr.index, + isHidden: addr.isHidden, + type: P2pkhAddressType.p2pkh, + network: BitcoinCashNetwork.mainnet, + ); + } catch (_) { + return BitcoinAddressRecord( + AddressUtils.getCashAddrFormat(addr.address), + index: addr.index, + isHidden: addr.isHidden, + type: P2pkhAddressType.p2pkh, + network: BitcoinCashNetwork.mainnet, + ); + } + }).toList(), initialBalance: snp.balance, seedBytes: await Mnemonic.toSeed(snp.mnemonic), initialRegularAddressIndex: snp.regularAddressIndex, initialChangeAddressIndex: snp.changeAddressIndex, - addressPageType: snp.addressPageType, + addressPageType: P2pkhAddressType.p2pkh, ); } diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart index 8291ce2a5..3164651f3 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart @@ -19,6 +19,7 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi super.initialAddresses, super.initialRegularAddressIndex, super.initialChangeAddressIndex, + super.initialAddressPageType, }) : super(walletInfo); @override diff --git a/cw_bitcoin_cash/pubspec.yaml b/cw_bitcoin_cash/pubspec.yaml index 9c098c0ff..7130b3c58 100644 --- a/cw_bitcoin_cash/pubspec.yaml +++ b/cw_bitcoin_cash/pubspec.yaml @@ -32,7 +32,7 @@ dependencies: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base.git - ref: cake-update-v1 + ref: cake-update-v2 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 4f3aea7ec..be72b992d 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -277,7 +277,7 @@ SPEC CHECKSUMS: flutter_inappwebview_ios: 97215cf7d4677db55df76782dbd2930c5e1c1ea0 flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be - fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0 + fluttertoast: 48c57db1b71b0ce9e6bba9f31c940ff4b001293c in_app_review: 318597b3a06c22bb46dc454d56828c85f444f99d local_auth_ios: 1ba1475238daa33a6ffa2a29242558437be435ac MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb @@ -302,4 +302,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: fcb1b8418441a35b438585c9dd8374e722e6c6ca -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index b36421608..709dc9a04 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -113,13 +113,35 @@ class CWBitcoin extends Bitcoin { .map((BitcoinAddressRecord addr) => ElectrumSubAddress( id: addr.index, name: addr.name, - address: electrumWallet.type == WalletType.bitcoinCash ? addr.cashAddr : addr.address, + address: addr.address, txCount: addr.txCount, balance: addr.balance, isChange: addr.isHidden)) .toList(); } + @override + Future estimateFakeSendAllTxAmount(Object wallet, TransactionPriority priority) async { + final electrumWallet = wallet as ElectrumWallet; + final sk = ECPrivate.random(); + + final p2shAddr = sk.getPublic().toP2pkhInP2sh(); + final p2wpkhAddr = sk.getPublic().toP2wpkhAddress(); + final estimatedTx = await electrumWallet.estimateTxFeeAndInputsToUse( + 0, + true, + // Deposit address + change address + [p2shAddr, p2wpkhAddr], + [ + BitcoinOutput(address: p2shAddr, value: BigInt.zero), + BitcoinOutput(address: p2wpkhAddr, value: BigInt.zero) + ], + null, + priority as BitcoinTransactionPriority); + + return estimatedTx.amount; + } + @override String getAddress(Object wallet) { final bitcoinWallet = wallet as ElectrumWallet; diff --git a/lib/buy/moonpay/moonpay_provider.dart b/lib/buy/moonpay/moonpay_provider.dart index 0ccb73e1c..75ba45ce1 100644 --- a/lib/buy/moonpay/moonpay_provider.dart +++ b/lib/buy/moonpay/moonpay_provider.dart @@ -81,7 +81,7 @@ class MoonPaySellProvider extends BuyProvider { '', { 'apiKey': _apiKey, - 'defaultBaseCurrencyCode': currency.toString().toLowerCase(), + 'defaultBaseCurrencyCode': _normalizeCurrency(currency), 'refundWalletAddress': refundWalletAddress, }..addAll(customParams), ); @@ -134,6 +134,14 @@ class MoonPaySellProvider extends BuyProvider { ); } } + + String _normalizeCurrency(CryptoCurrency currency) { + if (currency == CryptoCurrency.maticpoly) { + return "MATIC_POLYGON"; + } + + return currency.toString().toLowerCase(); + } } class MoonPayBuyProvider extends BuyProvider { diff --git a/lib/core/address_validator.dart b/lib/core/address_validator.dart index ad2c761a3..967cf9bf0 100644 --- a/lib/core/address_validator.dart +++ b/lib/core/address_validator.dart @@ -274,7 +274,7 @@ class AddressValidator extends TextValidator { '|([^0-9a-zA-Z]|^)([23][a-km-zA-HJ-NP-Z1-9]{25,34})([^0-9a-zA-Z]|\$)' //P2shAddress type '|([^0-9a-zA-Z]|^)((bc|tb)1q[ac-hj-np-z02-9]{25,39})([^0-9a-zA-Z]|\$)' //P2wpkhAddress type '|([^0-9a-zA-Z]|^)((bc|tb)1q[ac-hj-np-z02-9]{40,80})([^0-9a-zA-Z]|\$)' //P2wshAddress type - '|([^0-9a-zA-Z]|^)((bc|tb)1p([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59}|[ac-hj-np-z02-9]{8,89}))([^0-9a-zA-Z]|\$)'; //P2trAddress type + '|([^0-9a-zA-Z]|^)((bc|tb)1p([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59}|[ac-hj-np-z02-9]{8,89}))([^0-9a-zA-Z]|\$)'; //P2trAddress type case CryptoCurrency.ltc: return '([^0-9a-zA-Z]|^)^L[a-zA-Z0-9]{26,33}([^0-9a-zA-Z]|\$)' '|([^0-9a-zA-Z]|^)[LM][a-km-zA-HJ-NP-Z1-9]{26,33}([^0-9a-zA-Z]|\$)' diff --git a/lib/entities/provider_types.dart b/lib/entities/provider_types.dart index f9c2f1a82..b7336c2a7 100644 --- a/lib/entities/provider_types.dart +++ b/lib/entities/provider_types.dart @@ -89,7 +89,12 @@ class ProvidersHelper { case WalletType.bitcoinCash: return [ProviderType.askEachTime, ProviderType.moonpaySell]; case WalletType.polygon: - return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.dfx]; + return [ + ProviderType.askEachTime, + ProviderType.onramper, + ProviderType.moonpaySell, + ProviderType.dfx, + ]; case WalletType.solana: return [ ProviderType.askEachTime, diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index 94b51301c..61355df05 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -384,7 +384,7 @@ class ExchangePage extends BasePage { (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, depositKey)); reaction((_) => exchangeViewModel.depositAmount, (String amount) { - if (depositKey.currentState!.amountController.text != amount) { + if (depositKey.currentState!.amountController.text != amount && amount != S.of(context).all) { depositKey.currentState!.amountController.text = amount; } }); @@ -467,7 +467,9 @@ class ExchangePage extends BasePage { .addListener(() => exchangeViewModel.depositAddress = depositAddressController.text); depositAmountController.addListener(() { - if (depositAmountController.text != exchangeViewModel.depositAmount) { + if (depositAmountController.text != exchangeViewModel.depositAmount && + depositAmountController.text != S.of(context).all) { + exchangeViewModel.isSendAllEnabled = false; _depositAmountDebounce.run(() { exchangeViewModel.changeDepositAmount(amount: depositAmountController.text); exchangeViewModel.isReceiveAmountEntered = false; @@ -589,8 +591,9 @@ class ExchangePage extends BasePage { onDispose: disposeBestRateSync, hasAllAmount: exchangeViewModel.hasAllAmount, allAmount: exchangeViewModel.hasAllAmount - ? () => exchangeViewModel.calculateDepositAllAmount() + ? () => exchangeViewModel.enableSendAllAmount() : null, + isAllAmountEnabled: exchangeViewModel.isSendAllEnabled, amountFocusNode: _depositAmountFocus, addressFocusNode: _depositAddressFocus, key: depositKey, @@ -626,8 +629,10 @@ class ExchangePage extends BasePage { }, imageArrow: arrowBottomPurple, currencyButtonColor: Colors.transparent, - addressButtonsColor: Theme.of(context).extension()!.textFieldButtonColor, - borderColor: Theme.of(context).extension()!.textFieldBorderTopPanelColor, + addressButtonsColor: + Theme.of(context).extension()!.textFieldButtonColor, + borderColor: + Theme.of(context).extension()!.textFieldBorderTopPanelColor, currencyValueValidator: (value) { return !exchangeViewModel.isFixedRateMode ? AmountValidator( @@ -673,8 +678,10 @@ class ExchangePage extends BasePage { exchangeViewModel.changeReceiveCurrency(currency: currency), imageArrow: arrowBottomCakeGreen, currencyButtonColor: Colors.transparent, - addressButtonsColor: Theme.of(context).extension()!.textFieldButtonColor, - borderColor: Theme.of(context).extension()!.textFieldBorderBottomPanelColor, + addressButtonsColor: + Theme.of(context).extension()!.textFieldButtonColor, + borderColor: + Theme.of(context).extension()!.textFieldBorderBottomPanelColor, currencyValueValidator: (value) { return exchangeViewModel.isFixedRateMode ? AmountValidator( diff --git a/lib/src/screens/exchange/exchange_template_page.dart b/lib/src/screens/exchange/exchange_template_page.dart index 3a7456dd8..d24c91dad 100644 --- a/lib/src/screens/exchange/exchange_template_page.dart +++ b/lib/src/screens/exchange/exchange_template_page.dart @@ -56,17 +56,14 @@ class ExchangeTemplatePage extends BasePage { height: 8, ); - final depositWalletName = - exchangeViewModel.depositCurrency == CryptoCurrency.xmr + final depositWalletName = exchangeViewModel.depositCurrency == CryptoCurrency.xmr ? exchangeViewModel.wallet.name : null; - final receiveWalletName = - exchangeViewModel.receiveCurrency == CryptoCurrency.xmr + final receiveWalletName = exchangeViewModel.receiveCurrency == CryptoCurrency.xmr ? exchangeViewModel.wallet.name : null; - WidgetsBinding.instance - .addPostFrameCallback((_) => _setReactions(context, exchangeViewModel)); + WidgetsBinding.instance.addPostFrameCallback((_) => _setReactions(context, exchangeViewModel)); return KeyboardActions( disableScroll: true, @@ -76,128 +73,125 @@ class ExchangeTemplatePage extends BasePage { nextFocus: false, actions: [ KeyboardActionsItem( - focusNode: _depositAmountFocus, - toolbarButtons: [(_) => KeyboardDoneButton()]), + focusNode: _depositAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()]), KeyboardActionsItem( - focusNode: _receiveAmountFocus, - toolbarButtons: [(_) => KeyboardDoneButton()]) + focusNode: _receiveAmountFocus, toolbarButtons: [(_) => KeyboardDoneButton()]) ]), child: Container( - color: Theme.of(context).colorScheme.background, - child: Form( - key: _formKey, - child: ScrollableWithBottomSection( - contentPadding: EdgeInsets.only(bottom: 24), - content: Container( - padding: EdgeInsets.only(bottom: 32), - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(24), - bottomRight: Radius.circular(24) - ), - gradient: LinearGradient( - colors: [ - Theme.of(context).extension()!.firstGradientBottomPanelColor, - Theme.of(context).extension()!.secondGradientBottomPanelColor, - ], - stops: [0.35, 1.0], - begin: Alignment.topLeft, - end: Alignment.bottomRight), - ), - child: FocusTraversalGroup( - policy: OrderedTraversalPolicy(), - child: Column( - children: [ - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(24), - bottomRight: Radius.circular(24) + color: Theme.of(context).colorScheme.background, + child: Form( + key: _formKey, + child: ScrollableWithBottomSection( + contentPadding: EdgeInsets.only(bottom: 24), + content: Container( + padding: EdgeInsets.only(bottom: 32), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)), + gradient: LinearGradient(colors: [ + Theme.of(context) + .extension()! + .firstGradientBottomPanelColor, + Theme.of(context) + .extension()! + .secondGradientBottomPanelColor, + ], stops: [ + 0.35, + 1.0 + ], begin: Alignment.topLeft, end: Alignment.bottomRight), + ), + child: FocusTraversalGroup( + policy: OrderedTraversalPolicy(), + child: Column( + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), + bottomRight: Radius.circular(24)), + gradient: LinearGradient(colors: [ + Theme.of(context) + .extension()! + .firstGradientTopPanelColor, + Theme.of(context) + .extension()! + .secondGradientTopPanelColor, + ], begin: Alignment.topLeft, end: Alignment.bottomRight), + ), + padding: EdgeInsets.fromLTRB(24, 100, 24, 32), + child: Observer( + builder: (_) => ExchangeCard( + amountFocusNode: _depositAmountFocus, + key: depositKey, + title: S.of(context).you_will_send, + initialCurrency: exchangeViewModel.depositCurrency, + initialWalletName: depositWalletName ?? '', + initialAddress: exchangeViewModel.depositCurrency == + exchangeViewModel.wallet.currency + ? exchangeViewModel.wallet.walletAddresses.address + : exchangeViewModel.depositAddress, + initialIsAmountEditable: true, + initialIsAddressEditable: exchangeViewModel.isDepositAddressEnabled, + isAmountEstimated: false, + hasRefundAddress: true, + isMoneroWallet: exchangeViewModel.isMoneroWallet, + currencies: CryptoCurrency.all, + onCurrencySelected: (currency) => + exchangeViewModel.changeDepositCurrency(currency: currency), + imageArrow: arrowBottomPurple, + currencyButtonColor: Colors.transparent, + addressButtonsColor: Theme.of(context) + .extension()! + .textFieldButtonColor, + borderColor: Theme.of(context) + .extension()! + .textFieldBorderBottomPanelColor, + currencyValueValidator: + AmountValidator(currency: exchangeViewModel.depositCurrency), + //addressTextFieldValidator: AddressValidator( + // type: exchangeViewModel.depositCurrency), + ), + ), ), - gradient: LinearGradient( - colors: [ - Theme.of(context).extension()!.firstGradientTopPanelColor, - Theme.of(context).extension()!.secondGradientTopPanelColor, - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight), - ), - padding: EdgeInsets.fromLTRB(24, 100, 24, 32), - child: Observer( - builder: (_) => ExchangeCard( - amountFocusNode: _depositAmountFocus, - key: depositKey, - title: S.of(context).you_will_send, - initialCurrency: - exchangeViewModel.depositCurrency, - initialWalletName: depositWalletName ?? '', - initialAddress: exchangeViewModel - .depositCurrency == - exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.walletAddresses.address - : exchangeViewModel.depositAddress, - initialIsAmountEditable: true, - initialIsAddressEditable: exchangeViewModel - .isDepositAddressEnabled, - isAmountEstimated: false, - hasRefundAddress: true, - isMoneroWallet: exchangeViewModel.isMoneroWallet, - currencies: CryptoCurrency.all, - onCurrencySelected: (currency) => - exchangeViewModel.changeDepositCurrency( - currency: currency), - imageArrow: arrowBottomPurple, - currencyButtonColor: Colors.transparent, - addressButtonsColor: - Theme.of(context).extension()!.textFieldButtonColor, - borderColor: Theme.of(context).extension()!.textFieldBorderBottomPanelColor, - currencyValueValidator: AmountValidator( - currency: exchangeViewModel.depositCurrency), - //addressTextFieldValidator: AddressValidator( - // type: exchangeViewModel.depositCurrency), - ), - ), + Padding( + padding: EdgeInsets.only(top: 29, left: 24, right: 24), + child: Observer( + builder: (_) => ExchangeCard( + amountFocusNode: _receiveAmountFocus, + key: receiveKey, + title: S.of(context).you_will_get, + initialCurrency: exchangeViewModel.receiveCurrency, + initialWalletName: receiveWalletName ?? '', + initialAddress: exchangeViewModel.receiveCurrency == + exchangeViewModel.wallet.currency + ? exchangeViewModel.wallet.walletAddresses.address + : exchangeViewModel.receiveAddress, + initialIsAmountEditable: false, + isAmountEstimated: true, + isMoneroWallet: exchangeViewModel.isMoneroWallet, + currencies: exchangeViewModel.receiveCurrencies, + onCurrencySelected: (currency) => exchangeViewModel + .changeReceiveCurrency(currency: currency), + imageArrow: arrowBottomCakeGreen, + currencyButtonColor: Colors.transparent, + addressButtonsColor: Theme.of(context) + .extension()! + .textFieldButtonColor, + borderColor: Theme.of(context) + .extension()! + .textFieldBorderBottomPanelColor, + currencyValueValidator: AmountValidator( + currency: exchangeViewModel.receiveCurrency), + //addressTextFieldValidator: AddressValidator( + // type: exchangeViewModel.receiveCurrency), + )), + ) + ], ), - Padding( - padding: EdgeInsets.only(top: 29, left: 24, right: 24), - child: Observer( - builder: (_) => ExchangeCard( - amountFocusNode: _receiveAmountFocus, - key: receiveKey, - title: S.of(context).you_will_get, - initialCurrency: - exchangeViewModel.receiveCurrency, - initialWalletName: receiveWalletName ?? '', - initialAddress: - exchangeViewModel.receiveCurrency == - exchangeViewModel.wallet.currency - ? exchangeViewModel.wallet.walletAddresses.address - : exchangeViewModel.receiveAddress, - initialIsAmountEditable: false, - isAmountEstimated: true, - isMoneroWallet: exchangeViewModel.isMoneroWallet, - currencies: exchangeViewModel.receiveCurrencies, - onCurrencySelected: (currency) => - exchangeViewModel.changeReceiveCurrency( - currency: currency), - imageArrow: arrowBottomCakeGreen, - currencyButtonColor: Colors.transparent, - addressButtonsColor: - Theme.of(context).extension()!.textFieldButtonColor, - borderColor: Theme.of(context).extension()!.textFieldBorderBottomPanelColor, - currencyValueValidator: AmountValidator( - currency: exchangeViewModel.receiveCurrency), - //addressTextFieldValidator: AddressValidator( - // type: exchangeViewModel.receiveCurrency), - )), - ) - ], + ), ), - ), - ), - bottomSectionPadding: - EdgeInsets.only(left: 24, right: 24, bottom: 24), - bottomSection: Column(children: [ + bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), + bottomSection: Column(children: [ Padding( padding: EdgeInsets.only(bottom: 15), child: Observer( @@ -217,36 +211,31 @@ class ExchangeTemplatePage extends BasePage { ), ), PrimaryButton( - onPressed: () { - if (_formKey.currentState != null && _formKey.currentState!.validate()) { - exchangeViewModel.addTemplate( - amount: exchangeViewModel.depositAmount, - depositCurrency: - exchangeViewModel.depositCurrency.name, - depositCurrencyTitle: exchangeViewModel - .depositCurrency.title + ' ${exchangeViewModel.depositCurrency.tag ?? ''}', - receiveCurrency: - exchangeViewModel.receiveCurrency.name, - receiveCurrencyTitle: exchangeViewModel - .receiveCurrency.title + ' ${exchangeViewModel.receiveCurrency.tag ?? ''}', - provider: exchangeViewModel.provider.toString(), - depositAddress: exchangeViewModel.depositAddress, - receiveAddress: exchangeViewModel.receiveAddress); - exchangeViewModel.updateTemplate(); - Navigator.of(context).pop(); - } - }, - text: S.of(context).save, - color: Theme.of(context).primaryColor, - textColor: Colors.white), - ]), - )) - ) - ); + onPressed: () { + if (_formKey.currentState != null && _formKey.currentState!.validate()) { + exchangeViewModel.addTemplate( + amount: exchangeViewModel.depositAmount, + depositCurrency: exchangeViewModel.depositCurrency.name, + depositCurrencyTitle: exchangeViewModel.depositCurrency.title + + ' ${exchangeViewModel.depositCurrency.tag ?? ''}', + receiveCurrency: exchangeViewModel.receiveCurrency.name, + receiveCurrencyTitle: exchangeViewModel.receiveCurrency.title + + ' ${exchangeViewModel.receiveCurrency.tag ?? ''}', + provider: exchangeViewModel.provider.toString(), + depositAddress: exchangeViewModel.depositAddress, + receiveAddress: exchangeViewModel.receiveAddress); + exchangeViewModel.updateTemplate(); + Navigator.of(context).pop(); + } + }, + text: S.of(context).save, + color: Theme.of(context).primaryColor, + textColor: Colors.white), + ]), + )))); } - void _setReactions( - BuildContext context, ExchangeViewModel exchangeViewModel) { + void _setReactions(BuildContext context, ExchangeViewModel exchangeViewModel) { if (_isReactionsSet) { return; } @@ -272,33 +261,27 @@ class ExchangeTemplatePage extends BasePage { // key.currentState.changeLimits(min: min, max: max); // } - _onCurrencyChange( - exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey); - _onCurrencyChange( - exchangeViewModel.depositCurrency, exchangeViewModel, depositKey); + _onCurrencyChange(exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey); + _onCurrencyChange(exchangeViewModel.depositCurrency, exchangeViewModel, depositKey); reaction( - (_) => exchangeViewModel.wallet.name, - (String _) => _onWalletNameChange( - exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey)); + (_) => exchangeViewModel.wallet.name, + (String _) => + _onWalletNameChange(exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey)); reaction( - (_) => exchangeViewModel.wallet.name, - (String _) => _onWalletNameChange( - exchangeViewModel, exchangeViewModel.depositCurrency, depositKey)); + (_) => exchangeViewModel.wallet.name, + (String _) => + _onWalletNameChange(exchangeViewModel, exchangeViewModel.depositCurrency, depositKey)); - reaction( - (_) => exchangeViewModel.receiveCurrency, - (CryptoCurrency currency) => - _onCurrencyChange(currency, exchangeViewModel, receiveKey)); + reaction((_) => exchangeViewModel.receiveCurrency, + (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, receiveKey)); - reaction( - (_) => exchangeViewModel.depositCurrency, - (CryptoCurrency currency) => - _onCurrencyChange(currency, exchangeViewModel, depositKey)); + reaction((_) => exchangeViewModel.depositCurrency, + (CryptoCurrency currency) => _onCurrencyChange(currency, exchangeViewModel, depositKey)); reaction((_) => exchangeViewModel.depositAmount, (String amount) { - if (depositKey.currentState!.amountController.text != amount) { + if (depositKey.currentState!.amountController.text != amount && amount != S.of(context).all) { depositKey.currentState!.amountController.text = amount; } }); @@ -309,10 +292,9 @@ class ExchangeTemplatePage extends BasePage { } }); - reaction((_) => exchangeViewModel.isDepositAddressEnabled, - (bool isEnabled) { - depositKey.currentState!.isAddressEditable(isEditable: isEnabled); - }); + reaction((_) => exchangeViewModel.isDepositAddressEnabled, (bool isEnabled) { + depositKey.currentState!.isAddressEditable(isEditable: isEnabled); + }); reaction((_) => exchangeViewModel.receiveAmount, (String amount) { if (receiveKey.currentState!.amountController.text != amount) { @@ -353,30 +335,28 @@ class ExchangeTemplatePage extends BasePage { receiveKey.currentState.changeLimits(min: null, max: null); });*/ - depositAddressController.addListener( - () => exchangeViewModel.depositAddress = depositAddressController.text); + depositAddressController + .addListener(() => exchangeViewModel.depositAddress = depositAddressController.text); depositAmountController.addListener(() { - if (depositAmountController.text != exchangeViewModel.depositAmount) { - exchangeViewModel.changeDepositAmount( - amount: depositAmountController.text); + if (depositAmountController.text != exchangeViewModel.depositAmount && + exchangeViewModel.depositAmount != S.of(context).all) { + exchangeViewModel.changeDepositAmount(amount: depositAmountController.text); exchangeViewModel.isReceiveAmountEntered = false; } }); - receiveAddressController.addListener( - () => exchangeViewModel.receiveAddress = receiveAddressController.text); + receiveAddressController + .addListener(() => exchangeViewModel.receiveAddress = receiveAddressController.text); receiveAmountController.addListener(() { if (receiveAmountController.text != exchangeViewModel.receiveAmount) { - exchangeViewModel.changeReceiveAmount( - amount: receiveAmountController.text); + exchangeViewModel.changeReceiveAmount(amount: receiveAmountController.text); exchangeViewModel.isReceiveAmountEntered = true; } }); - reaction((_) => exchangeViewModel.wallet.walletAddresses.address, - (String address) { + reaction((_) => exchangeViewModel.wallet.walletAddresses.address, (String address) { if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) { depositKey.currentState!.changeAddress(address: address); } @@ -389,29 +369,26 @@ class ExchangeTemplatePage extends BasePage { _isReactionsSet = true; } - void _onCurrencyChange(CryptoCurrency currency, - ExchangeViewModel exchangeViewModel, GlobalKey key) { + void _onCurrencyChange(CryptoCurrency currency, ExchangeViewModel exchangeViewModel, + GlobalKey key) { final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency; key.currentState!.changeSelectedCurrency(currency); - key.currentState!.changeWalletName( - isCurrentTypeWallet ? exchangeViewModel.wallet.name : ''); + key.currentState!.changeWalletName(isCurrentTypeWallet ? exchangeViewModel.wallet.name : ''); key.currentState!.changeAddress( - address: isCurrentTypeWallet - ? exchangeViewModel.wallet.walletAddresses.address : ''); + address: isCurrentTypeWallet ? exchangeViewModel.wallet.walletAddresses.address : ''); key.currentState!.changeAmount(amount: ''); } - void _onWalletNameChange(ExchangeViewModel exchangeViewModel, - CryptoCurrency currency, GlobalKey key) { + void _onWalletNameChange(ExchangeViewModel exchangeViewModel, CryptoCurrency currency, + GlobalKey key) { final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency; if (isCurrentTypeWallet) { key.currentState!.changeWalletName(exchangeViewModel.wallet.name); - key.currentState!.addressController.text = - exchangeViewModel.wallet.walletAddresses.address; + key.currentState!.addressController.text = exchangeViewModel.wallet.walletAddresses.address; } else if (key.currentState!.addressController.text == exchangeViewModel.wallet.walletAddresses.address) { key.currentState!.changeWalletName(''); diff --git a/lib/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index 706ace7de..d2e3c27d4 100644 --- a/lib/src/screens/exchange/widgets/exchange_card.dart +++ b/lib/src/screens/exchange/widgets/exchange_card.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/core/amount_validator.dart'; import 'package:cake_wallet/entities/contact_base.dart'; import 'package:cake_wallet/themes/extensions/qr_code_theme.dart'; import 'package:cake_wallet/routes.dart'; @@ -37,6 +38,7 @@ class ExchangeCard extends StatefulWidget { this.addressButtonsColor = Colors.transparent, this.borderColor = Colors.transparent, this.hasAllAmount = false, + this.isAllAmountEnabled = false, this.amountFocusNode, this.addressFocusNode, this.allAmount, @@ -62,9 +64,11 @@ class ExchangeCard extends StatefulWidget { final Color borderColor; final FormFieldValidator? currencyValueValidator; final FormFieldValidator? addressTextFieldValidator; + final FormFieldValidator allAmountValidator = AllAmountValidator(); final FocusNode? amountFocusNode; final FocusNode? addressFocusNode; final bool hasAllAmount; + final bool isAllAmountEnabled; final VoidCallback? allAmount; final void Function(BuildContext context)? onPushPasteButton; final void Function(BuildContext context)? onPushAddressBookButton; @@ -76,15 +80,15 @@ class ExchangeCard extends StatefulWidget { class ExchangeCardState extends State { ExchangeCardState() - : _title = '', - _min = '', - _max = '', - _isAmountEditable = false, - _isAddressEditable = false, - _walletName = '', - _selectedCurrency = CryptoCurrency.btc, - _isAmountEstimated = false, - _isMoneroWallet = false; + : _title = '', + _min = '', + _max = '', + _isAmountEditable = false, + _isAddressEditable = false, + _walletName = '', + _selectedCurrency = CryptoCurrency.btc, + _isAmountEstimated = false, + _isMoneroWallet = false; final addressController = TextEditingController(); final amountController = TextEditingController(); @@ -160,6 +164,12 @@ class ExchangeCardState extends State { @override Widget build(BuildContext context) { + if (widget.isAllAmountEnabled) { + WidgetsBinding.instance.addPostFrameCallback((_) { + amountController.text = S.of(context).all; + }); + } + final copyImage = Image.asset('assets/images/copy_content.png', height: 16, width: 16, @@ -168,8 +178,7 @@ class ExchangeCardState extends State { return Container( width: double.infinity, color: Colors.transparent, - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: < - Widget>[ + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -202,40 +211,38 @@ class ExchangeCardState extends State { ), Text(_selectedCurrency.toString(), style: TextStyle( - fontWeight: FontWeight.w600, - fontSize: 16, - color: Colors.white)) + fontWeight: FontWeight.w600, fontSize: 16, color: Colors.white)) ]), ), ), - _selectedCurrency.tag != null ? Padding( - padding: const EdgeInsets.only(right:3.0), - child: Container( - height: 32, - decoration: BoxDecoration( - color: widget.addressButtonsColor ?? - Theme.of(context).extension()!.textFieldButtonColor, - borderRadius: - BorderRadius.all(Radius.circular(6))), - child: Center( - child: Padding( - padding: const EdgeInsets.all(6.0), - child: Text(_selectedCurrency.tag!, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: Theme.of(context).extension()!.textFieldButtonIconColor)), + if (_selectedCurrency.tag != null) + Padding( + padding: const EdgeInsets.only(right: 3.0), + child: Container( + height: 32, + decoration: BoxDecoration( + color: widget.addressButtonsColor ?? + Theme.of(context).extension()!.textFieldButtonColor, + borderRadius: BorderRadius.all(Radius.circular(6))), + child: Center( + child: Padding( + padding: const EdgeInsets.all(6.0), + child: Text(_selectedCurrency.tag!, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + color: Theme.of(context) + .extension()! + .textFieldButtonIconColor)), + ), ), ), ), - ) : Container(), Padding( padding: const EdgeInsets.only(right: 4.0), child: Text(':', style: TextStyle( - fontWeight: FontWeight.w600, - fontSize: 16, - color: Colors.white)), + fontWeight: FontWeight.w600, fontSize: 16, color: Colors.white)), ), Expanded( child: Row( @@ -249,26 +256,27 @@ class ExchangeCardState extends State { controller: amountController, enabled: _isAmountEditable, textAlign: TextAlign.left, - keyboardType: TextInputType.numberWithOptions( - signed: false, decimal: true), + keyboardType: + TextInputType.numberWithOptions(signed: false, decimal: true), inputFormatters: [ - FilteringTextInputFormatter.deny( - RegExp('[\\-|\\ ]')) + FilteringTextInputFormatter.deny(RegExp('[\\-|\\ ]')) ], hintText: '0.0000', borderColor: Colors.transparent, //widget.borderColor, textStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.white), + fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white), placeholderTextStyle: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: Theme.of(context).extension()!.hintTextColor), - validator: _isAmountEditable - ? widget.currencyValueValidator - : null), + color: Theme.of(context) + .extension()! + .hintTextColor), + validator: widget.hasAllAmount + ? widget.allAmountValidator + : _isAmountEditable + ? widget.currencyValueValidator + : null), ), ), if (widget.hasAllAmount) @@ -276,9 +284,10 @@ class ExchangeCardState extends State { height: 32, width: 32, decoration: BoxDecoration( - color: Theme.of(context).extension()!.textFieldButtonColor, - borderRadius: - BorderRadius.all(Radius.circular(6))), + color: Theme.of(context) + .extension()! + .textFieldButtonColor, + borderRadius: BorderRadius.all(Radius.circular(6))), child: InkWell( onTap: () => widget.allAmount?.call(), child: Center( @@ -287,7 +296,9 @@ class ExchangeCardState extends State { style: TextStyle( fontSize: 12, fontWeight: FontWeight.bold, - color: Theme.of(context).extension()!.textFieldButtonIconColor)), + color: Theme.of(context) + .extension()! + .textFieldButtonIconColor)), ), ), ) @@ -296,39 +307,30 @@ class ExchangeCardState extends State { ), ], )), - Divider( - height: 1, - color: Theme.of(context).extension()!.textFieldHintColor), + Divider(height: 1, color: Theme.of(context).extension()!.textFieldHintColor), Padding( padding: EdgeInsets.only(top: 5), child: Container( height: 15, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - _min != null - ? Text( - S - .of(context) - .min_value(_min ?? '', _selectedCurrency.toString()), - style: TextStyle( - fontSize: 10, - height: 1.2, - color: Theme.of(context).extension()!.hintTextColor), - ) - : Offstage(), - _min != null ? SizedBox(width: 10) : Offstage(), - _max != null - ? Text( - S - .of(context) - .max_value(_max ?? '', _selectedCurrency.toString()), - style: TextStyle( - fontSize: 10, - height: 1.2, - color: Theme.of(context).extension()!.hintTextColor)) - : Offstage(), - ])), + child: Row(mainAxisAlignment: MainAxisAlignment.start, children: [ + _min != null + ? Text( + S.of(context).min_value(_min ?? '', _selectedCurrency.toString()), + style: TextStyle( + fontSize: 10, + height: 1.2, + color: Theme.of(context).extension()!.hintTextColor), + ) + : Offstage(), + _min != null ? SizedBox(width: 10) : Offstage(), + _max != null + ? Text(S.of(context).max_value(_max ?? '', _selectedCurrency.toString()), + style: TextStyle( + fontSize: 10, + height: 1.2, + color: Theme.of(context).extension()!.hintTextColor)) + : Offstage(), + ])), ), !_isAddressEditable && widget.hasRefundAddress ? Padding( @@ -343,7 +345,7 @@ class ExchangeCardState extends State { : Offstage(), _isAddressEditable ? FocusTraversalOrder( - order: NumericFocusOrder(2), + order: NumericFocusOrder(2), child: Padding( padding: EdgeInsets.only(top: 20), child: AddressTextField( @@ -352,27 +354,23 @@ class ExchangeCardState extends State { onURIScanned: (uri) { final paymentRequest = PaymentRequest.fromUri(uri); addressController.text = paymentRequest.address; - + if (amountController.text.isNotEmpty) { _showAmountPopup(context, paymentRequest); return; } widget.amountFocusNode?.requestFocus(); - amountController.text = paymentRequest.amount; + amountController.text = paymentRequest.amount; }, - placeholder: widget.hasRefundAddress - ? S.of(context).refund_address - : null, + placeholder: widget.hasRefundAddress ? S.of(context).refund_address : null, options: [ AddressTextFieldOption.paste, AddressTextFieldOption.qrCode, AddressTextFieldOption.addressBook, ], isBorderExist: false, - textStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.white), + textStyle: + TextStyle(fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white), hintStyle: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, @@ -381,27 +379,22 @@ class ExchangeCardState extends State { validator: widget.addressTextFieldValidator, onPushPasteButton: widget.onPushPasteButton, onPushAddressBookButton: widget.onPushAddressBookButton, - selectedCurrency: _selectedCurrency - ), - + selectedCurrency: _selectedCurrency), ), - ) + ) : Padding( padding: EdgeInsets.only(top: 10), child: Builder( builder: (context) => Stack(children: [ - FocusTraversalOrder( - order: NumericFocusOrder(3), - child: BaseTextFormField( - controller: addressController, - borderColor: Colors.transparent, - suffixIcon: - SizedBox(width: _isMoneroWallet ? 80 : 36), - textStyle: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: Colors.white), - validator: widget.addressTextFieldValidator), + FocusTraversalOrder( + order: NumericFocusOrder(3), + child: BaseTextFormField( + controller: addressController, + borderColor: Colors.transparent, + suffixIcon: SizedBox(width: _isMoneroWallet ? 80 : 36), + textStyle: TextStyle( + fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white), + validator: widget.addressTextFieldValidator), ), Positioned( top: 2, @@ -421,33 +414,28 @@ class ExchangeCardState extends State { child: InkWell( onTap: () async { final contact = - await Navigator.of(context) - .pushNamed( + await Navigator.of(context).pushNamed( Routes.pickerAddressBook, arguments: widget.initialCurrency, ); - if (contact is ContactBase && - contact.address != null) { + if (contact is ContactBase) { setState(() => - addressController.text = - contact.address); - widget.onPushAddressBookButton - ?.call(context); + addressController.text = contact.address); + widget.onPushAddressBookButton?.call(context); } }, child: Container( padding: EdgeInsets.all(8), decoration: BoxDecoration( - color: widget - .addressButtonsColor, + color: widget.addressButtonsColor, borderRadius: - BorderRadius.all( - Radius.circular( - 6))), + BorderRadius.all(Radius.circular(6))), child: Image.asset( 'assets/images/open_book.png', - color: Theme.of(context).extension()!.textFieldButtonIconColor, + color: Theme.of(context) + .extension()! + .textFieldButtonIconColor, )), ), )), @@ -462,18 +450,13 @@ class ExchangeCardState extends State { label: S.of(context).copy_address, child: InkWell( onTap: () { - Clipboard.setData(ClipboardData( - text: addressController - .text)); + Clipboard.setData( + ClipboardData(text: addressController.text)); showBar( - context, - S - .of(context) - .copied_to_clipboard); + context, S.of(context).copied_to_clipboard); }, child: Container( - padding: EdgeInsets.fromLTRB( - 8, 8, 0, 8), + padding: EdgeInsets.fromLTRB(8, 8, 0, 8), color: Colors.transparent, child: copyImage), ), @@ -514,7 +497,6 @@ class ExchangeCardState extends State { Navigator.of(context).pop(); }, actionLeftButton: () => Navigator.of(dialogContext).pop()); - } - ); + }); } } diff --git a/lib/src/screens/send/send_template_page.dart b/lib/src/screens/send/send_template_page.dart index 205fd62e1..52458942c 100644 --- a/lib/src/screens/send/send_template_page.dart +++ b/lib/src/screens/send/send_template_page.dart @@ -1,7 +1,5 @@ import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; -import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; import 'package:cake_wallet/themes/extensions/seed_widget_theme.dart'; -import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/src/widgets/trail_button.dart'; import 'package:cake_wallet/view_model/send/template_view_model.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -11,7 +9,6 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/view_model/send/send_template_view_model.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; -import 'package:cake_wallet/src/screens/send/widgets/prefix_currency_icon_widget.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; import 'package:cake_wallet/src/screens/send/widgets/send_template_card.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart'; @@ -97,8 +94,13 @@ class SendTemplatePage extends BasePage { radius: 6.0, dotWidth: 6.0, dotHeight: 6.0, - dotColor: Theme.of(context).extension()!.indicatorDotColor, - activeDotColor: Theme.of(context).extension()!.indicatorDotTheme.activeIndicatorColor)) + dotColor: Theme.of(context) + .extension()! + .indicatorDotColor, + activeDotColor: Theme.of(context) + .extension()! + .indicatorDotTheme + .activeIndicatorColor)) : Offstage(); }, ), diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart index 6bd2d81e9..3f5714be9 100644 --- a/lib/src/screens/send/widgets/send_card.dart +++ b/lib/src/screens/send/widgets/send_card.dart @@ -80,15 +80,17 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: S.of(context).error, - alertContent: S.of(context).unmatched_currencies, - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }); + if (context.mounted) { + showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: S.of(context).error, + alertContent: S.of(context).unmatched_currencies, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); + } }); } } @@ -321,7 +323,8 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin calculateDepositAllAmount() async { + if (wallet.type == WalletType.litecoin || wallet.type == WalletType.bitcoinCash) { final availableBalance = wallet.balance[wallet.currency]!.available; final priority = _settingsStore.priority[wallet.type]!; final fee = wallet.calculateEstimatedFee(priority, null); @@ -545,6 +553,13 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with final amount = availableBalance - fee; changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount)); + } else if (wallet.type == WalletType.bitcoin) { + final priority = _settingsStore.priority[wallet.type]!; + + final amount = await bitcoin!.estimateFakeSendAllTxAmount( + wallet, bitcoin!.deserializeBitcoinTransactionPriority(priority.raw)); + + changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount)); } } diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 106a8a652..b82513de2 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -125,4 +125,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 65ec1541137fb5b35d00490dec1bb48d4d9586bb -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/pubspec_base.yaml b/pubspec_base.yaml index d4bf981cd..0293df1d1 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -110,7 +110,7 @@ dependencies: bitcoin_base: git: url: https://github.com/cake-tech/bitcoin_base.git - ref: cake-update-v1 + ref: cake-update-v2 dev_dependencies: flutter_test: diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index 02f874d77..ec851a89b 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -22,8 +22,8 @@ MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_SCHEME="monero.com" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.15.0" -CAKEWALLET_BUILD_NUMBER=198 +CAKEWALLET_VERSION="4.15.1" +CAKEWALLET_BUILD_NUMBER=199 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index b65d3e7a6..53bbf4022 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -18,8 +18,8 @@ MONERO_COM_BUILD_NUMBER=77 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="4.15.0" -CAKEWALLET_BUILD_NUMBER=217 +CAKEWALLET_VERSION="4.15.1" +CAKEWALLET_BUILD_NUMBER=218 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index 19a1e6846..1e8022b7b 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -21,8 +21,8 @@ MONERO_COM_BUILD_NUMBER=10 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" -CAKEWALLET_VERSION="1.8.0" -CAKEWALLET_BUILD_NUMBER=57 +CAKEWALLET_VERSION="1.8.1" +CAKEWALLET_BUILD_NUMBER=58 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then diff --git a/tool/configure.dart b/tool/configure.dart index 3c1587a98..962731d06 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -70,7 +70,6 @@ import 'package:cw_core/output_info.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:cake_wallet/view_model/send/output.dart'; -import 'package:cw_core/wallet_type.dart'; import 'package:hive/hive.dart'; import 'package:bitcoin_base/bitcoin_base.dart';"""; const bitcoinCWHeaders = """ @@ -127,6 +126,7 @@ abstract class Bitcoin { List getAddresses(Object wallet); String getAddress(Object wallet); + Future estimateFakeSendAllTxAmount(Object wallet, TransactionPriority priority); List getSubAddresses(Object wallet); String formatterBitcoinAmountToString({required int amount});