From 426ac99e34334e25c29c25325b74fa21ef1a4c2e Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Fri, 13 Oct 2023 14:49:00 +0300 Subject: [PATCH] Generic Fixes (#1122) * Fix Hive issue * Disable RobinHood for Nano * Validate context is still mounted [skip ci] * Disable Exolix for new exchanges Remove duplicate ethereum case * add nano/banano to manifest/info.plist * fix qr code issues for nano * Add Nano-wallet to restore form qr Add iOS keychain accessibility config * support app links for ethereum and nano [skip ci] * catch exceptions from gas price and estimated gas * Add bitcoin cash to app links Fix restore from QR for bitcoin cash * Fixate bottom buttons for create/restore wallet in wallet list page --------- Co-authored-by: fosse --- android/app/src/main/AndroidManifestBase.xml | 9 + cw_ethereum/lib/ethereum_client.dart | 16 +- ios/Runner/InfoBase.plist | 60 ++++ lib/entities/main_actions.dart | 4 +- lib/main.dart | 11 +- .../screens/dashboard/edit_token_page.dart | 4 +- .../exchange_trade/exchange_trade_page.dart | 8 +- .../screens/wallet_list/wallet_list_page.dart | 322 +++++++++--------- lib/utils/exception_handler.dart | 2 +- lib/utils/feature_flag.dart | 1 + lib/utils/payment_request.dart | 10 +- .../dashboard/transaction_list_item.dart | 7 - .../exchange/exchange_view_model.dart | 3 +- .../restore/wallet_restore_from_qr_code.dart | 4 +- lib/view_model/wallet_keys_view_model.dart | 2 +- 15 files changed, 275 insertions(+), 188 deletions(-) diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml index f22ba9c4f..77a555db8 100644 --- a/android/app/src/main/AndroidManifestBase.xml +++ b/android/app/src/main/AndroidManifestBase.xml @@ -52,6 +52,15 @@ + + + + + + + + + getGasUnitPrice() async { - final gasPrice = await _client!.getGasPrice(); - return gasPrice.getInWei.toInt(); + try { + final gasPrice = await _client!.getGasPrice(); + return gasPrice.getInWei.toInt(); + } catch (_) { + return 0; + } } Future getEstimatedGas() async { - final estimatedGas = await _client!.estimateGas(); - return estimatedGas.toInt(); + try { + final estimatedGas = await _client!.estimateGas(); + return estimatedGas.toInt(); + } catch (_) { + return 0; + } } Future signTransaction({ diff --git a/ios/Runner/InfoBase.plist b/ios/Runner/InfoBase.plist index e4d07c717..6cea7a730 100644 --- a/ios/Runner/InfoBase.plist +++ b/ios/Runner/InfoBase.plist @@ -100,6 +100,66 @@ litecoin-wallet + + CFBundleTypeRole + Editor + CFBundleURLName + ethereum + CFBundleURLSchemes + + ethereum + + + + CFBundleTypeRole + Viewer + CFBundleURLName + ethereum-wallet + CFBundleURLSchemes + + ethereum-wallet + + + + CFBundleTypeRole + Editor + CFBundleURLName + nano + CFBundleURLSchemes + + nano + + + + CFBundleTypeRole + Viewer + CFBundleURLName + nano-wallet + CFBundleURLSchemes + + nano-wallet + + + + CFBundleTypeRole + Editor + CFBundleURLName + bitcoincash + CFBundleURLSchemes + + bitcoincash + + + + CFBundleTypeRole + Viewer + CFBundleURLName + bitcoincash-wallet + CFBundleURLSchemes + + bitcoincash-wallet + + CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/lib/entities/main_actions.dart b/lib/entities/main_actions.dart index d2c67ef95..46865cbcc 100644 --- a/lib/entities/main_actions.dart +++ b/lib/entities/main_actions.dart @@ -53,8 +53,6 @@ class MainActions { case WalletType.litecoin: case WalletType.ethereum: case WalletType.bitcoinCash: - case WalletType.nano: - case WalletType.banano: switch (defaultBuyProvider) { case BuyProviderType.AskEachTime: Navigator.pushNamed(context, Routes.buy); @@ -67,6 +65,8 @@ class MainActions { break; } break; + case WalletType.nano: + case WalletType.banano: case WalletType.monero: await getIt.get().launchProvider(context); break; diff --git a/lib/main.dart b/lib/main.dart index 2d76196c3..98ba1e195 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -97,10 +97,10 @@ Future initializeAppConfigs() async { CakeHive.registerAdapter(WalletInfoAdapter()); } - if (!Hive.isAdapterRegistered(DERIVATION_TYPE_TYPE_ID)) { + if (!CakeHive.isAdapterRegistered(DERIVATION_TYPE_TYPE_ID)) { CakeHive.registerAdapter(DerivationTypeAdapter()); } - + if (!CakeHive.isAdapterRegistered(WALLET_TYPE_TYPE_ID)) { CakeHive.registerAdapter(WalletTypeAdapter()); } @@ -125,14 +125,17 @@ Future initializeAppConfigs() async { CakeHive.registerAdapter(AnonpayInvoiceInfoAdapter()); } - final secureStorage = FlutterSecureStorage(); + final secureStorage = FlutterSecureStorage( + iOptions: IOSOptions(accessibility: KeychainAccessibility.first_unlock), + ); final transactionDescriptionsBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: TransactionDescription.boxKey); final tradesBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Trade.boxKey); final ordersBoxKey = await getEncryptionKey(secureStorage: secureStorage, forKey: Order.boxKey); final contacts = await CakeHive.openBox(Contact.boxName); final nodes = await CakeHive.openBox(Node.boxName); - final powNodes = await CakeHive.openBox(Node.boxName + "pow");// must be different from Node.boxName + final powNodes = + await CakeHive.openBox(Node.boxName + "pow"); // must be different from Node.boxName final transactionDescriptions = await CakeHive.openBox( TransactionDescription.boxName, encryptionKey: transactionDescriptionsBoxKey); diff --git a/lib/src/screens/dashboard/edit_token_page.dart b/lib/src/screens/dashboard/edit_token_page.dart index 50bcb24e1..df6d3bd09 100644 --- a/lib/src/screens/dashboard/edit_token_page.dart +++ b/lib/src/screens/dashboard/edit_token_page.dart @@ -194,7 +194,9 @@ class _EditTokenPageBodyState extends State { contractAddress: _contractAddressController.text, decimal: int.parse(_tokenDecimalController.text), )); - Navigator.pop(context); + if (context.mounted) { + Navigator.pop(context); + } } }, text: S.of(context).save, diff --git a/lib/src/screens/exchange_trade/exchange_trade_page.dart b/lib/src/screens/exchange_trade/exchange_trade_page.dart index dbf6676a1..22606c21e 100644 --- a/lib/src/screens/exchange_trade/exchange_trade_page.dart +++ b/lib/src/screens/exchange_trade/exchange_trade_page.dart @@ -5,7 +5,6 @@ import 'package:cake_wallet/utils/request_review_handler.dart'; import 'package:mobx/mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/core/execution_state.dart'; @@ -26,16 +25,15 @@ import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart'; void showInformation( ExchangeTradeViewModel exchangeTradeViewModel, BuildContext context) { - final fetchingLabel = S.current.fetching; final trade = exchangeTradeViewModel.trade; final walletName = exchangeTradeViewModel.wallet.name; final information = exchangeTradeViewModel.isSendable ? S.current.exchange_result_confirm( - trade.amount ?? fetchingLabel, trade.from.toString(), walletName) + + trade.amount, trade.from.toString(), walletName) + exchangeTradeViewModel.extraInfo : S.current.exchange_result_description( - trade.amount ?? fetchingLabel, trade.from.toString()) + + trade.amount, trade.from.toString()) + exchangeTradeViewModel.extraInfo; showPopUp( @@ -177,7 +175,7 @@ class ExchangeTradeState extends State { ), itemBuilder: (context, index) { final item = widget.exchangeTradeViewModel.items[index]; - final value = item.data ?? fetchingLabel; + final value = item.data; final content = ListRow( title: item.title, diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 11b394460..cce4a2581 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -13,7 +13,6 @@ import 'package:cw_core/wallet_type.dart'; import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart'; @@ -65,169 +64,178 @@ class WalletListBodyState extends State { return Container( padding: EdgeInsets.only(top: 16), - child: ScrollableWithBottomSection( - contentPadding: EdgeInsets.only(bottom: 20), - content: Container( - child: Observer( - builder: (_) => ListView.separated( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - separatorBuilder: (_, index) => - Divider(color: Theme.of(context).colorScheme.background, height: 32), - itemCount: widget.walletListViewModel.wallets.length, - itemBuilder: (__, index) { - final wallet = widget.walletListViewModel.wallets[index]; - final currentColor = wallet.isCurrent - ? Theme.of(context) - .extension()! - .createNewWalletButtonBackgroundColor - : Theme.of(context).colorScheme.background; - final row = GestureDetector( - onTap: () => wallet.isCurrent ? null : _loadWallet(wallet), - child: Container( - height: tileHeight, - width: double.infinity, - child: Row( - children: [ - Container( - height: tileHeight, - width: 4, - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topRight: Radius.circular(4), bottomRight: Radius.circular(4)), - color: currentColor), - ), - Expanded( - child: Container( - height: tileHeight, - padding: EdgeInsets.only(left: 20, right: 20), - color: Theme.of(context).colorScheme.background, - alignment: Alignment.centerLeft, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - wallet.isEnabled ? _imageFor(type: wallet.type) : nonWalletTypeIcon, - SizedBox(width: 10), - Flexible( - child: Text( - wallet.name, - maxLines: null, - softWrap: true, - style: TextStyle( - fontSize: 22, - fontWeight: FontWeight.w500, - color: - Theme.of(context).extension()!.titleColor, - ), - ), - ), - ], + child: Column( + children: [ + Expanded( + child: Container( + child: Observer( + builder: (_) => ListView.separated( + physics: const BouncingScrollPhysics(), + separatorBuilder: (_, index) => + Divider(color: Theme.of(context).colorScheme.background, height: 32), + itemCount: widget.walletListViewModel.wallets.length, + itemBuilder: (__, index) { + final wallet = widget.walletListViewModel.wallets[index]; + final currentColor = wallet.isCurrent + ? Theme.of(context) + .extension()! + .createNewWalletButtonBackgroundColor + : Theme.of(context).colorScheme.background; + final row = GestureDetector( + onTap: () => wallet.isCurrent ? null : _loadWallet(wallet), + child: Container( + height: tileHeight, + width: double.infinity, + child: Row( + children: [ + Container( + height: tileHeight, + width: 4, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topRight: Radius.circular(4), + bottomRight: Radius.circular(4)), + color: currentColor), ), - ), - ), - ], - ), - ), - ); - - return wallet.isCurrent - ? row - : Row( - children: [ - Expanded(child: row), - GestureDetector( - onTap: () => Navigator.of(context).pushNamed(Routes.walletEdit, - arguments: [widget.walletListViewModel, wallet]), - child: Container( - padding: EdgeInsets.only(right: 20), - child: Center( - child: Container( - height: 40, - width: 44, - padding: EdgeInsets.all(10), - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context) - .extension()! - .iconsBackgroundColor, - ), - child: Icon( - Icons.edit, - size: 14, - color: - Theme.of(context).extension()!.iconsColor, - ), + Expanded( + child: Container( + height: tileHeight, + padding: EdgeInsets.only(left: 20, right: 20), + color: Theme.of(context).colorScheme.background, + alignment: Alignment.centerLeft, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + wallet.isEnabled + ? _imageFor(type: wallet.type) + : nonWalletTypeIcon, + SizedBox(width: 10), + Flexible( + child: Text( + wallet.name, + maxLines: null, + softWrap: true, + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w500, + color: Theme.of(context) + .extension()! + .titleColor, + ), + ), + ), + ], ), ), ), - ), - ], - ); - }, + ], + ), + ), + ); + + return wallet.isCurrent + ? row + : Row( + children: [ + Expanded(child: row), + GestureDetector( + onTap: () => Navigator.of(context).pushNamed(Routes.walletEdit, + arguments: [widget.walletListViewModel, wallet]), + child: Container( + padding: EdgeInsets.only(right: 20), + child: Center( + child: Container( + height: 40, + width: 44, + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context) + .extension()! + .iconsBackgroundColor, + ), + child: Icon( + Icons.edit, + size: 14, + color: Theme.of(context) + .extension()! + .iconsColor, + ), + ), + ), + ), + ), + ], + ); + }, + ), + ), ), ), - ), - bottomSectionPadding: EdgeInsets.only(bottom: 24, right: 24, left: 24), - bottomSection: Column( - children: [ - PrimaryImageButton( - onPressed: () { - //TODO(David): Find a way to optimize this - if (isSingleCoin) { - if (widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets) { - widget.authService.authenticateAction( - context, - route: Routes.newWallet, - arguments: widget.walletListViewModel.currentWalletType, - conditionToDetermineIfToUse2FA: - widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets, - ); - } else { - Navigator.of(context).pushNamed( - Routes.newWallet, - arguments: widget.walletListViewModel.currentWalletType, - ); - } - } else { - if (widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets) { - widget.authService.authenticateAction( - context, - route: Routes.newWalletType, - conditionToDetermineIfToUse2FA: - widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets, - ); - } else { - Navigator.of(context).pushNamed(Routes.newWalletType); - } - } - }, - image: newWalletImage, - text: S.of(context).wallet_list_create_new_wallet, - color: Theme.of(context).primaryColor, - textColor: Colors.white, + Padding( + padding: const EdgeInsets.all(24), + child: Column( + children: [ + PrimaryImageButton( + onPressed: () { + //TODO(David): Find a way to optimize this + if (isSingleCoin) { + if (widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets) { + widget.authService.authenticateAction( + context, + route: Routes.newWallet, + arguments: widget.walletListViewModel.currentWalletType, + conditionToDetermineIfToUse2FA: + widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets, + ); + } else { + Navigator.of(context).pushNamed( + Routes.newWallet, + arguments: widget.walletListViewModel.currentWalletType, + ); + } + } else { + if (widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets) { + widget.authService.authenticateAction( + context, + route: Routes.newWalletType, + conditionToDetermineIfToUse2FA: + widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets, + ); + } else { + Navigator.of(context).pushNamed(Routes.newWalletType); + } + } + }, + image: newWalletImage, + text: S.of(context).wallet_list_create_new_wallet, + color: Theme.of(context).primaryColor, + textColor: Colors.white, + ), + SizedBox(height: 10.0), + PrimaryImageButton( + onPressed: () { + if (widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets) { + widget.authService.authenticateAction( + context, + route: Routes.restoreOptions, + arguments: false, + conditionToDetermineIfToUse2FA: + widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets, + ); + } else { + Navigator.of(context).pushNamed(Routes.restoreOptions, arguments: false); + } + }, + image: restoreWalletImage, + text: S.of(context).wallet_list_restore_wallet, + color: Theme.of(context).cardColor, + textColor: Theme.of(context).extension()!.buttonTextColor, + ) + ], ), - SizedBox(height: 10.0), - PrimaryImageButton( - onPressed: () { - if (widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets) { - widget.authService.authenticateAction( - context, - route: Routes.restoreOptions, - arguments: false, - conditionToDetermineIfToUse2FA: - widget.walletListViewModel.shouldRequireTOTP2FAForCreatingNewWallets, - ); - } else { - Navigator.of(context).pushNamed(Routes.restoreOptions, arguments: false); - } - }, - image: restoreWalletImage, - text: S.of(context).wallet_list_restore_wallet, - color: Theme.of(context).cardColor, - textColor: Theme.of(context).extension()!.buttonTextColor, - ) - ], - ), + ), + ], ), ); } diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index b9c659872..bea43a6c6 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -161,7 +161,7 @@ class ExceptionHandler { "Handshake error in client", "Error while launching http", "OS Error: Network is unreachable", - "ClientException: Write failed, uri=https:", + "ClientException: Write failed, uri=http", ]; static Future _addDeviceInfo(File file) async { diff --git a/lib/utils/feature_flag.dart b/lib/utils/feature_flag.dart index 2b0d5a2b9..628023f85 100644 --- a/lib/utils/feature_flag.dart +++ b/lib/utils/feature_flag.dart @@ -1,3 +1,4 @@ class FeatureFlag { static const bool isCakePayEnabled = false; + static const bool isExolixEnabled = false; } \ No newline at end of file diff --git a/lib/utils/payment_request.dart b/lib/utils/payment_request.dart index 6f82c3b94..00093b413 100644 --- a/lib/utils/payment_request.dart +++ b/lib/utils/payment_request.dart @@ -17,10 +17,12 @@ class PaymentRequest { } if (nano != null) { - if (address.contains("nano")) { - amount = nanoUtil!.getRawAsUsableString(amount, nanoUtil!.rawPerNano); - } else if (address.contains("ban")) { - amount = nanoUtil!.getRawAsUsableString(amount, nanoUtil!.rawPerBanano); + if (amount.isNotEmpty) { + if (address.contains("nano")) { + amount = nanoUtil!.getRawAsUsableString(amount, nanoUtil!.rawPerNano); + } else if (address.contains("ban")) { + amount = nanoUtil!.getRawAsUsableString(amount, nanoUtil!.rawPerBanano); + } } } diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart index 4e1c1aae0..fd7971001 100644 --- a/lib/view_model/dashboard/transaction_list_item.dart +++ b/lib/view_model/dashboard/transaction_list_item.dart @@ -97,13 +97,6 @@ class TransactionListItem extends ActionListItem with Keyable { nano!.getTransactionAmountRaw(transaction).toString(), nanoUtil!.rawPerNano)), price: price); break; - case WalletType.ethereum: - final asset = ethereum!.assetOfTransaction(balanceViewModel.wallet, transaction); - final price = balanceViewModel.fiatConvertationStore.prices[asset]; - amount = calculateFiatAmountRaw( - cryptoAmount: ethereum!.formatterEthereumAmountToDouble(transaction: transaction), - price: price); - break; default: break; } diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 7bc7927df..f7db98245 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -16,6 +16,7 @@ import 'package:cake_wallet/exchange/simpleswap/simpleswap_exchange_provider.dar import 'package:cake_wallet/exchange/simpleswap/simpleswap_request.dart'; import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart'; import 'package:cake_wallet/exchange/trocador/trocador_request.dart'; +import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cake_wallet/store/app_store.dart'; @@ -154,7 +155,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with SideShiftExchangeProvider(), SimpleSwapExchangeProvider(), TrocadorExchangeProvider(useTorOnly: _useTorOnly), - ExolixExchangeProvider(), + if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(), ]; @observable diff --git a/lib/view_model/restore/wallet_restore_from_qr_code.dart b/lib/view_model/restore/wallet_restore_from_qr_code.dart index dd138b66c..bc100f9fe 100644 --- a/lib/view_model/restore/wallet_restore_from_qr_code.dart +++ b/lib/view_model/restore/wallet_restore_from_qr_code.dart @@ -73,10 +73,12 @@ class WalletRestoreFromQRCode { case 'litecoin-wallet': return WalletType.litecoin; case 'bitcoincash': - case 'bitcoinCash-wallet': + case 'bitcoincash-wallet': return WalletType.bitcoinCash; + case 'ethereum': case 'ethereum-wallet': return WalletType.ethereum; + case 'nano': case 'nano-wallet': return WalletType.nano; default: diff --git a/lib/view_model/wallet_keys_view_model.dart b/lib/view_model/wallet_keys_view_model.dart index e106126bc..81f521d27 100644 --- a/lib/view_model/wallet_keys_view_model.dart +++ b/lib/view_model/wallet_keys_view_model.dart @@ -148,7 +148,7 @@ abstract class WalletKeysViewModelBase with Store { case WalletType.ethereum: return 'ethereum-wallet'; case WalletType.bitcoinCash: - return 'bitcoinCash-wallet'; + return 'bitcoincash-wallet'; case WalletType.nano: return 'nano-wallet'; case WalletType.banano: