From 798d9a1edf24af6cfd60c22f5370c47306ecc19f Mon Sep 17 00:00:00 2001 From: M Date: Tue, 5 Jan 2021 20:31:03 +0200 Subject: [PATCH] Fixes --- lib/bitcoin/bitcoin_amount_format.dart | 34 +++++++-- .../bitcoin_transaction_credentials.dart | 2 +- lib/bitcoin/bitcoin_transaction_info.dart | 4 +- lib/bitcoin/bitcoin_wallet.dart | 40 ++++++----- lib/di.dart | 3 +- lib/main.dart | 1 + lib/src/screens/exchange/exchange_page.dart | 67 ++++++++++-------- .../exchange/widgets/exchange_card.dart | 70 ++++++++++++++----- lib/store/settings_store.dart | 2 +- .../exchange/exchange_view_model.dart | 41 ++++++++--- lib/view_model/send/send_view_model.dart | 2 +- 11 files changed, 181 insertions(+), 85 deletions(-) diff --git a/lib/bitcoin/bitcoin_amount_format.dart b/lib/bitcoin/bitcoin_amount_format.dart index 75db6c314..fa00387f1 100644 --- a/lib/bitcoin/bitcoin_amount_format.dart +++ b/lib/bitcoin/bitcoin_amount_format.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:intl/intl.dart'; import 'package:cake_wallet/entities/crypto_amount_format.dart'; @@ -7,10 +9,32 @@ final bitcoinAmountFormat = NumberFormat() ..maximumFractionDigits = bitcoinAmountLength ..minimumFractionDigits = 1; -String bitcoinAmountToString({int amount}) => - bitcoinAmountFormat.format(cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider)); +String bitcoinAmountToString({int amount}) => bitcoinAmountFormat.format( + cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider)); -double bitcoinAmountToDouble({int amount}) => cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider); +double bitcoinAmountToDouble({int amount}) => + cryptoAmountToDouble(amount: amount, divider: bitcoinAmountDivider); -int doubleToBitcoinAmount(double amount) => - (amount * bitcoinAmountDivider).toInt(); \ No newline at end of file +int stringDoubleToBitcoinAmount(String amount) { + final splitted = amount.split(''); + final dotIndex = amount.indexOf('.'); + int result = 0; + + + for (var i = 0; i < splitted.length; i++) { + try { + if (dotIndex == i) { + continue; + } + + final char = splitted[i]; + final multiplier = dotIndex < i + ? bitcoinAmountDivider ~/ pow(10, (i - dotIndex)) + : (bitcoinAmountDivider * pow(10, (dotIndex - i -1))).toInt(); + final num = int.parse(char) * multiplier; + result += num; + } catch (_) {} + } + + return result; +} diff --git a/lib/bitcoin/bitcoin_transaction_credentials.dart b/lib/bitcoin/bitcoin_transaction_credentials.dart index 9e64634b3..40f7a7aa7 100644 --- a/lib/bitcoin/bitcoin_transaction_credentials.dart +++ b/lib/bitcoin/bitcoin_transaction_credentials.dart @@ -4,6 +4,6 @@ class BitcoinTransactionCredentials { BitcoinTransactionCredentials(this.address, this.amount, this.priority); final String address; - final double amount; + final String amount; TransactionPriority priority; } diff --git a/lib/bitcoin/bitcoin_transaction_info.dart b/lib/bitcoin/bitcoin_transaction_info.dart index f9051a5e0..fb1400c5e 100644 --- a/lib/bitcoin/bitcoin_transaction_info.dart +++ b/lib/bitcoin/bitcoin_transaction_info.dart @@ -47,7 +47,7 @@ class BitcoinTransactionInfo extends TransactionInfo { final out = vin['tx']['vout'][vout] as Map; final outAddresses = (out['scriptPubKey']['addresses'] as List)?.toSet(); - inputsAmount += doubleToBitcoinAmount(out['value'] as double ?? 0); + inputsAmount += stringDoubleToBitcoinAmount((out['value'] as double ?? 0).toString()); if (outAddresses?.intersection(addressesSet)?.isNotEmpty ?? false) { direction = TransactionDirection.outgoing; @@ -58,7 +58,7 @@ class BitcoinTransactionInfo extends TransactionInfo { final outAddresses = out['scriptPubKey']['addresses'] as List ?? []; final ntrs = outAddresses.toSet().intersection(addressesSet); - final value = doubleToBitcoinAmount(out['value'] as double ?? 0.0); + final value = stringDoubleToBitcoinAmount((out['value'] as double ?? 0.0).toString()); totalOutAmount += value; if ((direction == TransactionDirection.incoming && ntrs.isNotEmpty) || diff --git a/lib/bitcoin/bitcoin_wallet.dart b/lib/bitcoin/bitcoin_wallet.dart index 052440f1e..de5ef2119 100644 --- a/lib/bitcoin/bitcoin_wallet.dart +++ b/lib/bitcoin/bitcoin_wallet.dart @@ -116,6 +116,19 @@ abstract class BitcoinWalletBase extends WalletBase with Store { walletInfo: walletInfo); } + static int feeAmountForPriority(TransactionPriority priority) { + switch (priority) { + case TransactionPriority.slow: + return 6000; + case TransactionPriority.regular: + return 22080; + case TransactionPriority.fast: + return 24000; + default: + return 0; + } + } + @override final BitcoinTransactionHistory transactionHistory; final String path; @@ -243,16 +256,20 @@ abstract class BitcoinWalletBase extends WalletBase with Store { Object credentials) async { final transactionCredentials = credentials as BitcoinTransactionCredentials; final inputs = []; - final fee = _feeMultiplier(transactionCredentials.priority); + final fee = feeAmountForPriority(transactionCredentials.priority); final amount = transactionCredentials.amount != null - ? doubleToBitcoinAmount(transactionCredentials.amount) - : balance.total - fee; + ? stringDoubleToBitcoinAmount(transactionCredentials.amount) + : balance.availableBalance - fee; final totalAmount = amount + fee; final txb = bitcoin.TransactionBuilder(network: bitcoin.bitcoin); - var leftAmount = totalAmount; final changeAddress = address; + var leftAmount = totalAmount; var totalInputAmount = 0; + if (totalAmount > balance.availableBalance) { + throw BitcoinTransactionWrongBalanceException(); + } + final unspent = addresses.map((address) => eclient .getListUnspentWithAddress(address.address) .then((unspent) => unspent @@ -334,7 +351,7 @@ abstract class BitcoinWalletBase extends WalletBase with Store { @override double calculateEstimatedFee(TransactionPriority priority) => - bitcoinAmountToDouble(amount: _feeMultiplier(priority)); + bitcoinAmountToDouble(amount: feeAmountForPriority(priority)); @override Future save() async { @@ -386,17 +403,4 @@ abstract class BitcoinWalletBase extends WalletBase with Store { String _getAddress({@required int index}) => generateAddress(hd: hd, index: index); - - int _feeMultiplier(TransactionPriority priority) { - switch (priority) { - case TransactionPriority.slow: - return 6000; - case TransactionPriority.regular: - return 22080; - case TransactionPriority.fast: - return 24000; - default: - return 0; - } - } } diff --git a/lib/di.dart b/lib/di.dart index 0a6646e5d..03038f8c5 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -355,7 +355,8 @@ Future setup( getIt.get().wallet, tradesSource, getIt.get(), - getIt.get())); + getIt.get(), + getIt.get().settingsStore)); getIt.registerFactory(() => ExchangeTradeViewModel( wallet: getIt.get().wallet, diff --git a/lib/main.dart b/lib/main.dart index 38460fcca..6c1c3127a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/lib/src/screens/exchange/exchange_page.dart b/lib/src/screens/exchange/exchange_page.dart index e085d7880..8cedc8c7e 100644 --- a/lib/src/screens/exchange/exchange_page.dart +++ b/lib/src/screens/exchange/exchange_page.dart @@ -62,10 +62,11 @@ class ExchangePage extends BasePage { @override Widget trailing(BuildContext context) => TrailButton( - caption: S.of(context).reset, onPressed: () { + caption: S.of(context).reset, + onPressed: () { _formKey.currentState.reset(); exchangeViewModel.reset(); - }); + }); @override Widget body(BuildContext context) { @@ -95,8 +96,8 @@ class ExchangePage extends BasePage { return KeyboardActions( config: KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.IOS, - keyboardBarColor: Theme.of(context).accentTextTheme.body2 - .backgroundColor, + keyboardBarColor: + Theme.of(context).accentTextTheme.body2.backgroundColor, nextFocus: false, actions: [ KeyboardActionsItem( @@ -160,6 +161,11 @@ class ExchangePage extends BasePage { padding: EdgeInsets.fromLTRB(24, 100, 24, 32), child: Observer( builder: (_) => ExchangeCard( + hasAllAmount: exchangeViewModel.hasAllAmount, + allAmount: exchangeViewModel.hasAllAmount + ? () => exchangeViewModel + .calculateDepositAllAmount() + : null, amountFocusNode: _depositAmountFocus, key: depositKey, title: S.of(context).you_will_send, @@ -394,30 +400,35 @@ class ExchangePage extends BasePage { }), ), Observer( - builder: (_) => LoadingPrimaryButton( - text: S.of(context).exchange, - onPressed: () { - if (_formKey.currentState.validate()) { - if ((exchangeViewModel.depositCurrency == CryptoCurrency.xmr) - &&(!(exchangeViewModel.status is SyncedSyncStatus))) { - showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: S.of(context).exchange, - alertContent: S.of(context).exchange_sync_alert_content, - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop()); - }); - } else { - exchangeViewModel.createTrade(); - } - } - }, - color: Theme.of(context).accentTextTheme.body2.color, - textColor: Colors.white, - isLoading: exchangeViewModel.tradeState - is TradeIsCreating)), + builder: (_) => LoadingPrimaryButton( + text: S.of(context).exchange, + onPressed: () { + if (_formKey.currentState.validate()) { + if ((exchangeViewModel.depositCurrency == + CryptoCurrency.xmr) && + (!(exchangeViewModel.status + is SyncedSyncStatus))) { + showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: S.of(context).exchange, + alertContent: S + .of(context) + .exchange_sync_alert_content, + buttonText: S.of(context).ok, + buttonAction: () => + Navigator.of(context).pop()); + }); + } else { + exchangeViewModel.createTrade(); + } + } + }, + color: Theme.of(context).accentTextTheme.body2.color, + textColor: Colors.white, + isLoading: + exchangeViewModel.tradeState is TradeIsCreating)), ]), )), )); diff --git a/lib/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index 45f6f475c..062e5a1c3 100644 --- a/lib/src/screens/exchange/widgets/exchange_card.dart +++ b/lib/src/screens/exchange/widgets/exchange_card.dart @@ -27,7 +27,9 @@ class ExchangeCard extends StatefulWidget { this.borderColor = Colors.transparent, this.currencyValueValidator, this.addressTextFieldValidator, - this.amountFocusNode}) + this.amountFocusNode, + this.hasAllAmount = false, + this.allAmount}) : super(key: key); final List currencies; @@ -47,6 +49,8 @@ class ExchangeCard extends StatefulWidget { final FormFieldValidator currencyValueValidator; final FormFieldValidator addressTextFieldValidator; final FocusNode amountFocusNode; + final bool hasAllAmount; + Function allAmount; @override ExchangeCardState createState() => ExchangeCardState(); @@ -197,7 +201,36 @@ class ExchangeCardState extends State { ]), ), ), - ) + ), + if (widget.hasAllAmount) + Positioned( + top: 5, + right: 55, + child: Container( + height: 32, + width: 32, + margin: EdgeInsets.only(left: 14, top: 4, bottom: 10), + decoration: BoxDecoration( + color: Theme.of(context) + .primaryTextTheme + .display1 + .color, + borderRadius: BorderRadius.all(Radius.circular(6))), + child: InkWell( + onTap: () => widget.allAmount?.call(), + child: Center( + child: Text(S.of(context).all, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + color: Theme.of(context) + .primaryTextTheme + .display1 + .decorationColor)), + ), + ), + )) ], )), Padding( @@ -232,18 +265,17 @@ class ExchangeCardState extends State { ), !_isAddressEditable && widget.hasRefundAddress ? Padding( - padding: EdgeInsets.only(top: 20), - child: Text( - S.of(context).refund_address, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: - Theme.of(context) - .accentTextTheme - .display4 - .decorationColor), - )) + padding: EdgeInsets.only(top: 20), + child: Text( + S.of(context).refund_address, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .accentTextTheme + .display4 + .decorationColor), + )) : Offstage(), _isAddressEditable ? Padding( @@ -251,7 +283,8 @@ class ExchangeCardState extends State { child: AddressTextField( controller: addressController, placeholder: widget.hasRefundAddress - ? S.of(context).refund_address : null, + ? S.of(context).refund_address + : null, options: [ AddressTextFieldOption.paste, AddressTextFieldOption.qrCode, @@ -265,8 +298,7 @@ class ExchangeCardState extends State { hintStyle: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, - color: - Theme.of(context) + color: Theme.of(context) .accentTextTheme .display4 .decorationColor), @@ -281,8 +313,8 @@ class ExchangeCardState extends State { onTap: () { Clipboard.setData( ClipboardData(text: addressController.text)); - showBar(context, - S.of(context).copied_to_clipboard); + showBar( + context, S.of(context).copied_to_clipboard); }, child: Row( mainAxisSize: MainAxisSize.max, diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 30cf4f238..4d535228b 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -153,7 +153,7 @@ abstract class SettingsStoreBase with Store { .getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? false; final legacyTheme = - sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) + (sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy) ?? false) ? ThemeType.dark.index : ThemeType.bright.index; final savedTheme = ThemeList.deserialize( diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index e533e1e93..d2ac05048 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -1,3 +1,5 @@ +import 'package:cake_wallet/bitcoin/bitcoin_amount_format.dart'; +import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; import 'package:cake_wallet/core/wallet_base.dart'; import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/sync_status.dart'; @@ -7,6 +9,7 @@ import 'package:cake_wallet/exchange/limits.dart'; import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/exchange/limits_state.dart'; import 'package:cake_wallet/store/dashboard/trades_store.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:intl/intl.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -27,8 +30,8 @@ part 'exchange_view_model.g.dart'; class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel; abstract class ExchangeViewModelBase with Store { - ExchangeViewModelBase( - this.wallet, this.trades, this._exchangeTemplateStore, this.tradesStore) { + ExchangeViewModelBase(this.wallet, this.trades, this._exchangeTemplateStore, + this.tradesStore, this._settingsStore) { providerList = [ XMRTOExchangeProvider(), ChangeNowExchangeProvider(), @@ -104,10 +107,6 @@ abstract class ExchangeViewModelBase with Store { @observable bool isReceiveAmountEntered; - Limits limits; - - NumberFormat _cryptoNumberFormat; - @computed SyncStatus get status => wallet.syncStatus; @@ -115,6 +114,15 @@ abstract class ExchangeViewModelBase with Store { ObservableList get templates => _exchangeTemplateStore.templates; + bool get hasAllAmount => + wallet.type == WalletType.bitcoin && depositCurrency == wallet.currency; + + Limits limits; + + NumberFormat _cryptoNumberFormat; + + SettingsStore _settingsStore; + @action void changeProvider({ExchangeProvider provider}) { this.provider = provider; @@ -264,9 +272,8 @@ abstract class ExchangeViewModelBase with Store { await trades.add(trade); tradeState = TradeIsCreatedSuccessfully(trade: trade); } catch (e) { - tradeState = TradeIsCreatedFailure( - title: provider.title, - error: e.toString()); + tradeState = + TradeIsCreatedFailure(title: provider.title, error: e.toString()); } } } else { @@ -291,6 +298,22 @@ abstract class ExchangeViewModelBase with Store { _onPairChange(); } + @action + void calculateDepositAllAmount() { + if (wallet is BitcoinWallet) { + final availableBalance = wallet.balance.availableBalance as int; + final fee = BitcoinWalletBase.feeAmountForPriority( + _settingsStore.transactionPriority); + + if (availableBalance < fee || availableBalance == 0) { + return; + } + + final amount = availableBalance - fee; + depositAmount = bitcoinAmountToString(amount: amount); + } + } + void updateTemplate() => _exchangeTemplateStore.update(); void addTemplate( diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index f3d46ccb3..4fec6aade 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -197,7 +197,7 @@ abstract class SendViewModelBase with Store { switch (_wallet.type) { case WalletType.bitcoin: - final amount = !sendAll ? double.parse(_amount) : null; + final amount = !sendAll ? _amount : null; return BitcoinTransactionCredentials( address, amount, _settingsStore.transactionPriority);