From ce4d375abfa90f0a69527ae94fcb87ceb6914f71 Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Thu, 14 Sep 2023 22:14:16 +0300 Subject: [PATCH] Generic fixes and enhancements (#1083) * Add exception handler to fiat APIs Increase send card size for coin control Fix Monero.com unspent coins hive box issue minor bug fix * Remove EIP-1559 parameters from Eth transaction Enhance error reporting * Throw error if not enough monero utx outputs are selected * Fix Search text color * Fix Ethereum sending EIP-1559 transactions * Add transaction data to ERC20 transactions * Add input check in single output transactions as well * Fix Node deletion issue Handle user input error in anonpay * Remove exception handler from fiat conversion since it's not working with isolates * Require enough utxo for amount and fees; More insightful Error messages * Add cakewallet to applinks [skip ci] * Add cakewallet app link for iOS [skip ci] * Add applink depending on app scheme variable * Add applink in iOS custom to the app getting built [skip ci] * Handle normal app links without considering them as Payment URIs * Minor fix [skip ci] * Fixate encrypt package version as the recent update they made has some issues [skip ci] --------- Co-authored-by: Konstantin Ullrich --- android/app/src/main/AndroidManifestBase.xml | 1 + cw_ethereum/lib/ethereum_client.dart | 3 ++- ...onero_transaction_no_inputs_exception.dart | 6 ++++- cw_monero/lib/monero_wallet.dart | 13 ++++++++++- ios/Podfile.lock | 23 +++++++++++++++++++ ios/Runner/InfoBase.plist | 4 ++++ lib/main.dart | 2 +- .../screens/dashboard/home_settings_page.dart | 4 +++- lib/src/screens/root/root.dart | 8 ++++--- lib/src/screens/send/send_page.dart | 2 +- .../screens/settings/manage_nodes_page.dart | 5 ++-- lib/utils/exception_handler.dart | 12 ++++++++++ .../anon_invoice_page_view_model.dart | 6 ++++- .../exchange/exchange_view_model.dart | 5 +++- pubspec_base.yaml | 2 +- scripts/android/app_env.sh | 8 ++++++- scripts/android/inject_app_details.sh | 1 + scripts/ios/app_config.sh | 5 ++++ 18 files changed, 94 insertions(+), 16 deletions(-) diff --git a/android/app/src/main/AndroidManifestBase.xml b/android/app/src/main/AndroidManifestBase.xml index b40aeb7c8..9b3f47314 100644 --- a/android/app/src/main/AndroidManifestBase.xml +++ b/android/app/src/main/AndroidManifestBase.xml @@ -46,6 +46,7 @@ + diff --git a/cw_ethereum/lib/ethereum_client.dart b/cw_ethereum/lib/ethereum_client.dart index 7eba43aa7..e10e79f1e 100644 --- a/cw_ethereum/lib/ethereum_client.dart +++ b/cw_ethereum/lib/ethereum_client.dart @@ -93,6 +93,7 @@ class EthereumClient { EthereumAddress.fromHex(toAddress), BigInt.parse(amount), credentials: privateKey, + transaction: transaction, ); }; } @@ -107,7 +108,7 @@ class EthereumClient { } Future sendTransaction(Uint8List signedTransaction) async => - await _client!.sendRawTransaction(signedTransaction); + await _client!.sendRawTransaction(prependTransactionType(0x02, signedTransaction)); Future getTransactionDetails(String transactionHash) async { // Wait for the transaction receipt to become available diff --git a/cw_monero/lib/exceptions/monero_transaction_no_inputs_exception.dart b/cw_monero/lib/exceptions/monero_transaction_no_inputs_exception.dart index 5d808be8f..453482e0a 100644 --- a/cw_monero/lib/exceptions/monero_transaction_no_inputs_exception.dart +++ b/cw_monero/lib/exceptions/monero_transaction_no_inputs_exception.dart @@ -1,4 +1,8 @@ class MoneroTransactionNoInputsException implements Exception { + MoneroTransactionNoInputsException(this.inputsSize); + + int inputsSize; + @override - String toString() => 'Not enough inputs available. Please select more under Coin Control'; + String toString() => 'Not enough inputs ($inputsSize) selected. Please select more under Coin Control'; } diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index 69cd5458e..f9b3c1997 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -210,7 +210,7 @@ abstract class MoneroWalletBase extends WalletBase acc + (value.formattedCryptoAmount ?? 0)); + final estimatedFee = calculateEstimatedFee(_credentials.priority, totalAmount); if (unlockedBalance < totalAmount) { throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.'); } + if (allInputsAmount < totalAmount + estimatedFee) { + throw MoneroTransactionNoInputsException(inputs.length); + } + final moneroOutputs = outputs.map((output) { final outputAddress = output.isParsedAddress ? output.extractedAddress @@ -262,6 +267,12 @@ abstract class MoneroWalletBase extends WalletBasecakewallet + + CFBundleTypeRole + Editor + CFBundleTypeRole Editor diff --git a/lib/main.dart b/lib/main.dart index 62d18708e..40d7468f2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -114,7 +114,7 @@ Future initializeAppConfigs() async { CakeHive.registerAdapter(OrderAdapter()); } - if (!isMoneroOnly && !CakeHive.isAdapterRegistered(UnspentCoinsInfo.typeId)) { + if (!CakeHive.isAdapterRegistered(UnspentCoinsInfo.typeId)) { CakeHive.registerAdapter(UnspentCoinsInfoAdapter()); } diff --git a/lib/src/screens/dashboard/home_settings_page.dart b/lib/src/screens/dashboard/home_settings_page.dart index 9303cb053..a08b8a8a7 100644 --- a/lib/src/screens/dashboard/home_settings_page.dart +++ b/lib/src/screens/dashboard/home_settings_page.dart @@ -9,6 +9,7 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.da import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; import 'package:cake_wallet/themes/extensions/address_theme.dart'; import 'package:cake_wallet/themes/extensions/menu_theme.dart'; +import 'package:cake_wallet/themes/extensions/picker_theme.dart'; import 'package:cake_wallet/view_model/dashboard/home_settings_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -55,7 +56,8 @@ class HomeSettingsPage extends BasePage { padding: const EdgeInsetsDirectional.only(start: 16), child: TextFormField( controller: _searchController, - style: TextStyle(color: Theme.of(context).dialogTheme.backgroundColor), + style: TextStyle( + color: Theme.of(context).extension()!.searchHintColor), decoration: InputDecoration( hintText: S.of(context).search_add_token, prefixIcon: Image.asset("assets/images/search_icon.png"), diff --git a/lib/src/screens/root/root.dart b/lib/src/screens/root/root.dart index 3298a50c0..af775705b 100644 --- a/lib/src/screens/root/root.dart +++ b/lib/src/screens/root/root.dart @@ -140,7 +140,7 @@ class RootState extends State with WidgetsBindingObserver { } _reset(); totpAuth.close( - route: launchUri != null ? Routes.send : null, + route: _isValidPaymentUri() ? Routes.send : null, arguments: PaymentRequest.fromUri(launchUri), ); launchUri = null; @@ -152,7 +152,7 @@ class RootState extends State with WidgetsBindingObserver { } else { _reset(); auth.close( - route: launchUri != null ? Routes.send : null, + route: _isValidPaymentUri() ? Routes.send : null, arguments: PaymentRequest.fromUri(launchUri), ); launchUri = null; @@ -161,7 +161,7 @@ class RootState extends State with WidgetsBindingObserver { }, ); }); - } else if (launchUri != null) { + } else if (_isValidPaymentUri()) { widget.navigatorKey.currentState?.pushNamed( Routes.send, arguments: PaymentRequest.fromUri(launchUri), @@ -183,4 +183,6 @@ class RootState extends State with WidgetsBindingObserver { _isInactive = value; _isInactiveController.add(value); } + + bool _isValidPaymentUri() => launchUri?.path.isNotEmpty ?? false; } diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index b24e9b01f..961cf4ba5 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -100,7 +100,7 @@ class SendPage extends BasePage { AppBarStyle get appBarStyle => AppBarStyle.transparent; double _sendCardHeight(BuildContext context) { - final double initialHeight = sendViewModel.hasCoinControl ? 490 : 465; + final double initialHeight = sendViewModel.hasCoinControl ? 500 : 465; if (!ResponsiveLayoutUtil.instance.isMobile) { return initialHeight - 66; diff --git a/lib/src/screens/settings/manage_nodes_page.dart b/lib/src/screens/settings/manage_nodes_page.dart index 4b1034f2b..2b96a3053 100644 --- a/lib/src/screens/settings/manage_nodes_page.dart +++ b/lib/src/screens/settings/manage_nodes_page.dart @@ -34,13 +34,12 @@ class ManageNodesPage extends BasePage { SizedBox(height: 20), Observer( builder: (BuildContext context) { + int itemsCount = nodeListViewModel.nodes.length; return Flexible( child: SectionStandardList( sectionCount: 1, dividerPadding: EdgeInsets.symmetric(horizontal: 24), - itemCounter: (int sectionIndex) { - return nodeListViewModel.nodes.length; - }, + itemCounter: (int sectionIndex) => itemsCount, itemBuilder: (_, index) { return Observer( builder: (context) { diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index e8e7702fa..5c51a8757 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -32,6 +32,14 @@ class ExceptionHandler { const String separator = '''\n\n========================================================== ==========================================================\n\n'''; + /// don't save existing errors + if (file.existsSync()) { + final String fileContent = await file.readAsString(); + if (fileContent.contains("${exception.values.first}")) { + return; + } + } + file.writeAsStringSync( "$exception $separator", mode: FileMode.append, @@ -83,6 +91,10 @@ class ExceptionHandler { library: errorDetails.library, ); + if (errorDetails.silent) { + return; + } + final sharedPrefs = await SharedPreferences.getInstance(); final lastPopupDate = diff --git a/lib/view_model/anon_invoice_page_view_model.dart b/lib/view_model/anon_invoice_page_view_model.dart index b9617e6dd..53e8473a0 100644 --- a/lib/view_model/anon_invoice_page_view_model.dart +++ b/lib/view_model/anon_invoice_page_view_model.dart @@ -93,7 +93,11 @@ abstract class AnonInvoicePageViewModelBase with Store { Future createInvoice() async { state = IsExecutingState(); if (amount.isNotEmpty) { - final amountInCrypto = double.parse(amount); + final amountInCrypto = double.tryParse(amount); + if (amountInCrypto == null) { + state = FailureState('Amount is invalid'); + return; + } if (minimum != null && amountInCrypto < minimum!) { state = FailureState('Amount is too small'); return; diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 4ff3cb390..2e90a3a33 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -225,7 +225,7 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with @computed List get walletContactsToShow => contactListViewModel.walletContacts - .where((element) => receiveCurrency == null || element.type == receiveCurrency) + .where((element) => element.type == receiveCurrency) .toList(); @action @@ -550,6 +550,9 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with amount = amount.replaceAll(',', '.'); if (limitsState is LimitsLoadedSuccessfully) { + if (double.tryParse(amount) == null) { + continue; + } if (limits.max != null && double.parse(amount) < limits.min!) { continue; } else if (limits.max != null && double.parse(amount) > limits.max!) { diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 4655a70b5..da79a5076 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -49,7 +49,7 @@ dependencies: lottie: ^1.3.0 animate_do: ^2.1.0 cupertino_icons: ^1.0.5 - encrypt: ^5.0.1 + encrypt: 5.0.1 crypto: ^3.0.2 # password: ^1.0.0 basic_utils: ^5.6.1 diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index 627b8d318..ed86b1933 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -5,6 +5,7 @@ APP_ANDROID_VERSION="" APP_ANDROID_BUILD_VERSION="" APP_ANDROID_ID="" APP_ANDROID_PACKAGE="" +APP_ANDROID_SCHEME="" MONERO_COM="monero.com" CAKEWALLET="cakewallet" @@ -18,12 +19,14 @@ MONERO_COM_VERSION="1.6.0" MONERO_COM_BUILD_NUMBER=56 MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_PACKAGE="com.monero.app" +MONERO_COM_SCHEME="monero.com" CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_VERSION="4.9.0" CAKEWALLET_BUILD_NUMBER=169 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" +CAKEWALLET_SCHEME="cakewallet" HAVEN_NAME="Haven" HAVEN_VERSION="1.0.0" @@ -44,6 +47,7 @@ case $APP_ANDROID_TYPE in APP_ANDROID_BUILD_NUMBER=$MONERO_COM_BUILD_NUMBER APP_ANDROID_BUNDLE_ID=$MONERO_COM_BUNDLE_ID APP_ANDROID_PACKAGE=$MONERO_COM_PACKAGE + APP_ANDROID_SCHEME=$MONERO_COM_SCHEME ;; $CAKEWALLET) APP_ANDROID_NAME=$CAKEWALLET_NAME @@ -51,6 +55,7 @@ case $APP_ANDROID_TYPE in APP_ANDROID_BUILD_NUMBER=$CAKEWALLET_BUILD_NUMBER APP_ANDROID_BUNDLE_ID=$CAKEWALLET_BUNDLE_ID APP_ANDROID_PACKAGE=$CAKEWALLET_PACKAGE + APP_ANDROID_SCHEME=$CAKEWALLET_SCHEME ;; $HAVEN) APP_ANDROID_NAME=$HAVEN_NAME @@ -66,4 +71,5 @@ export APP_ANDROID_NAME export APP_ANDROID_VERSION export APP_ANDROID_BUILD_NUMBER export APP_ANDROID_BUNDLE_ID -export APP_ANDROID_PACKAGE \ No newline at end of file +export APP_ANDROID_PACKAGE +export APP_ANDROID_SCHEME \ No newline at end of file diff --git a/scripts/android/inject_app_details.sh b/scripts/android/inject_app_details.sh index 340966044..27b7efa39 100755 --- a/scripts/android/inject_app_details.sh +++ b/scripts/android/inject_app_details.sh @@ -8,6 +8,7 @@ fi cd ../.. sed -i "0,/version:/{s/version:.*/version: ${APP_ANDROID_VERSION}+${APP_ANDROID_BUILD_NUMBER}/}" ./pubspec.yaml sed -i "0,/version:/{s/__APP_PACKAGE__/${APP_ANDROID_PACKAGE}/}" ./android/app/src/main/AndroidManifest.xml +sed -i "0,/__APP_SCHEME__/s/__APP_SCHEME__/${APP_ANDROID_SCHEME}/" ./android/app/src/main/AndroidManifest.xml sed -i "0,/version:/{s/__versionCode__/${APP_ANDROID_BUILD_NUMBER}/}" ./android/app/src/main/AndroidManifest.xml sed -i "0,/version:/{s/__versionName__/${APP_ANDROID_VERSION}/}" ./android/app/src/main/AndroidManifest.xml cd scripts/android diff --git a/scripts/ios/app_config.sh b/scripts/ios/app_config.sh index 470f47efc..82b333d36 100755 --- a/scripts/ios/app_config.sh +++ b/scripts/ios/app_config.sh @@ -16,6 +16,11 @@ cp -rf ./ios/Runner/InfoBase.plist ./ios/Runner/Info.plist /usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier ${APP_IOS_BUNDLE_ID}" ./ios/Runner/Info.plist /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${APP_IOS_VERSION}" ./ios/Runner/Info.plist /usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${APP_IOS_BUILD_NUMBER}" ./ios/Runner/Info.plist + +/usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:1:CFBundleURLName string ${APP_IOS_TYPE}" ./ios/Runner/Info.plist +/usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:1:CFBundleURLSchemes array" ./ios/Runner/Info.plist +/usr/libexec/PlistBuddy -c "Add :CFBundleURLTypes:1:CFBundleURLSchemes: string ${APP_IOS_TYPE}" ./ios/Runner/Info.plist + CONFIG_ARGS="" case $APP_IOS_TYPE in