diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt index c0021dc6e..0a1424f8c 100644 --- a/assets/text/Release_Notes.txt +++ b/assets/text/Release_Notes.txt @@ -1,2 +1,4 @@ Bitcoin Silent Payments -Add Tron wallet \ No newline at end of file +Add Tron wallet +Hardware wallets enhancements +Bug fixes diff --git a/cw_bitcoin/lib/electrum_derivations.dart b/cw_bitcoin/lib/electrum_derivations.dart index 19d444a41..7e19f1cb4 100644 --- a/cw_bitcoin/lib/electrum_derivations.dart +++ b/cw_bitcoin/lib/electrum_derivations.dart @@ -28,6 +28,12 @@ Map> electrum_derivations = { description: "Standard BIP84 native segwit", scriptType: "p2wpkh", ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/86'/0'/0'", + description: "Standard BIP86 Taproot", + scriptType: "p2tr", + ), DerivationInfo( derivationType: DerivationType.bip39, derivationPath: "m/0'", diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 7cf76a0bf..209ddc774 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -14,7 +14,6 @@ import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/litecoin_network.dart'; -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin; import 'package:bip39/bip39.dart' as bip39; part 'litecoin_wallet.g.dart'; diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index dc4f6947b..75e7eb2a6 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -377,6 +377,9 @@ class CWBitcoin extends Bitcoin { case "p2wpkh-p2sh": address = generateP2SHAddress(hd: hd, network: network); break; + case "p2tr": + address = generateP2TRAddress(hd: hd, network: network); + break; default: continue; } diff --git a/lib/buy/moonpay/moonpay_provider.dart b/lib/buy/moonpay/moonpay_provider.dart index 53f018d57..59251e064 100644 --- a/lib/buy/moonpay/moonpay_provider.dart +++ b/lib/buy/moonpay/moonpay_provider.dart @@ -281,17 +281,19 @@ class MoonPayProvider extends BuyProvider { throw Exception('Could not launch URL'); } } catch (e) { - await showDialog( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: 'MoonPay', - alertContent: 'The MoonPay service is currently unavailable: $e', - buttonText: S.of(context).ok, - buttonAction: () => Navigator.of(context).pop(), - ); - }, - ); + if (context.mounted) { + await showDialog( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: 'MoonPay', + alertContent: 'The MoonPay service is currently unavailable: $e', + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop(), + ); + }, + ); + } } } diff --git a/lib/di.dart b/lib/di.dart index b31ed075e..98ca1f284 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -313,7 +313,6 @@ Future setup({ getIt.registerFactory>(() => _powNodeSource, instanceName: Node.boxName + "pow"); getIt.registerSingleton(AuthenticationStore()); - getIt.registerSingleton(LedgerViewModel()); getIt.registerSingleton(WalletListStore()); getIt.registerSingleton(NodeListStoreBase.instance); getIt.registerSingleton(settingsStore); @@ -337,6 +336,8 @@ Future setup({ getIt.registerSingleton( AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource)); + getIt.registerLazySingleton(() => LedgerViewModel()); + final secretStore = await SecretStoreBase.load(getIt.get()); getIt.registerSingleton(secretStore); @@ -626,7 +627,7 @@ Future setup({ getIt.get(), getIt.get(), _transactionDescriptionBox, - getIt.get(), + getIt.get().wallet!.isHardwareWallet ? getIt.get() : null, ), ); @@ -839,10 +840,14 @@ Future setup({ isSelected: isSelected)); getIt.registerFactory(() => RobinhoodBuyProvider( - wallet: getIt.get().wallet!, ledgerVM: getIt.get())); + wallet: getIt.get().wallet!, + ledgerVM: + getIt.get().wallet!.isHardwareWallet ? getIt.get() : null)); getIt.registerFactory(() => DFXBuyProvider( - wallet: getIt.get().wallet!, ledgerVM: getIt.get())); + wallet: getIt.get().wallet!, + ledgerVM: + getIt.get().wallet!.isHardwareWallet ? getIt.get() : null)); getIt.registerFactory(() => MoonPayProvider( settingsStore: getIt.get().settingsStore, @@ -948,9 +953,9 @@ Future setup({ (derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations)); getIt.registerFactoryParam, void>( - (credentials, _) => + (derivations, _) => WalletRestoreChooseDerivationPage(getIt.get( - param1: credentials, + param1: derivations, ))); getIt.registerFactoryParam( diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 5e1a1286d..aebf9ccd5 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -67,6 +67,7 @@ class PreferencesKey { static const lookupsUnstoppableDomains = 'looks_up_unstoppable_domain'; static const lookupsOpenAlias = 'looks_up_open_alias'; static const lookupsENS = 'looks_up_ens'; + static const showCameraConsent = 'show_camera_consent'; static String moneroWalletUpdateV1Key(String name) => '${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}'; diff --git a/lib/src/screens/buy/webview_page.dart b/lib/src/screens/buy/webview_page.dart index 0af5952c4..d6f171f36 100644 --- a/lib/src/screens/buy/webview_page.dart +++ b/lib/src/screens/buy/webview_page.dart @@ -1,10 +1,14 @@ +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/utils/permission_handler.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class WebViewPage extends BasePage { WebViewPage(this._title, this._url); @@ -42,8 +46,9 @@ class WebViewPageBodyState extends State { ), initialUrlRequest: URLRequest(url: WebUri.uri(widget.uri)), onPermissionRequest: (controller, request) async { - bool permissionGranted = await Permission.camera.status == PermissionStatus.granted; - if (!permissionGranted) { + final sharedPrefs = getIt.get(); + + if (sharedPrefs.getBool(PreferencesKey.showCameraConsent) ?? true) { final bool userConsent = await showPopUp( context: context, builder: (BuildContext context) { @@ -65,9 +70,12 @@ class WebViewPageBodyState extends State { ); } - permissionGranted = await Permission.camera.request().isGranted; + sharedPrefs.setBool(PreferencesKey.showCameraConsent, false); } + bool permissionGranted = + await PermissionHandler.checkPermission(Permission.camera, context); + return PermissionResponse( resources: request.resources, action: permissionGranted diff --git a/lib/src/screens/connect_device/connect_device_page.dart b/lib/src/screens/connect_device/connect_device_page.dart index 80b396a34..a482b1c41 100644 --- a/lib/src/screens/connect_device/connect_device_page.dart +++ b/lib/src/screens/connect_device/connect_device_page.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; -// import 'package:cake_wallet/src/screens/connect_device/debug_device_page.dart'; +import 'package:cake_wallet/src/screens/connect_device/debug_device_page.dart'; import 'package:cake_wallet/src/screens/connect_device/widgets/device_tile.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; @@ -105,7 +105,7 @@ class ConnectDevicePageBodyState extends State { try { _bleRefresh = ledger.scan().listen((device) => setState(() => bleDevices.add(device))) ..onError((e) { - throw e as Exception; + throw e.toString(); }); setState(() => bleIsEnabled = true); _bleRefreshTimer?.cancel(); diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index bdfc0656f..438c22c1d 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -36,7 +36,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:mobx/mobx.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart'; -import 'package:cw_core/crypto_currency.dart'; import 'package:url_launcher/url_launcher.dart'; class SendPage extends BasePage { @@ -374,17 +373,17 @@ class SendPage extends BasePage { } if (sendViewModel.wallet.isHardwareWallet) { - if (!sendViewModel.ledgerViewModel.isConnected) { + if (!sendViewModel.ledgerViewModel!.isConnected) { await Navigator.of(context).pushNamed(Routes.connectDevices, arguments: ConnectDevicePageParams( walletType: sendViewModel.walletType, onConnectDevice: (BuildContext context, _) { - sendViewModel.ledgerViewModel.setLedger(sendViewModel.wallet); + sendViewModel.ledgerViewModel!.setLedger(sendViewModel.wallet); Navigator.of(context).pop(); }, )); } else { - sendViewModel.ledgerViewModel.setLedger(sendViewModel.wallet); + sendViewModel.ledgerViewModel!.setLedger(sendViewModel.wallet); } } diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index a50147c70..ed8d7593d 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -363,6 +363,8 @@ abstract class DashboardViewModelBase with Store { .toList(); } + bool get hasBuyProviders => ProvidersHelper.getAvailableBuyProviderTypes(wallet.type).isNotEmpty; + List get availableSellProviders { final providerTypes = ProvidersHelper.getAvailableSellProviderTypes(wallet.type); return providerTypes @@ -372,6 +374,8 @@ abstract class DashboardViewModelBase with Store { .toList(); } + bool get hasSellProviders => ProvidersHelper.getAvailableSellProviderTypes(wallet.type).isNotEmpty; + bool get shouldShowYatPopup => settingsStore.shouldShowYatPopup; @action @@ -384,13 +388,13 @@ abstract class DashboardViewModelBase with Store { bool hasExchangeAction; @computed - bool get isEnabledBuyAction => !settingsStore.disableBuy && availableBuyProviders.isNotEmpty; + bool get isEnabledBuyAction => !settingsStore.disableBuy && hasBuyProviders; @observable bool hasBuyAction; @computed - bool get isEnabledSellAction => !settingsStore.disableSell && availableSellProviders.isNotEmpty; + bool get isEnabledSellAction => !settingsStore.disableSell && hasSellProviders; @observable bool hasSellAction; @@ -529,34 +533,38 @@ abstract class DashboardViewModelBase with Store { void setSyncAll(bool value) => settingsStore.currentSyncAll = value; Future> checkAffectedWallets() async { - // await load file - final vulnerableSeedsString = await rootBundle - .loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt'); - final vulnerableSeeds = vulnerableSeedsString.split("\n"); + try { + // await load file + final vulnerableSeedsString = await rootBundle + .loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt'); + final vulnerableSeeds = vulnerableSeedsString.split("\n"); - final walletInfoSource = await CakeHive.openBox(WalletInfo.boxName); + final walletInfoSource = await CakeHive.openBox(WalletInfo.boxName); - List affectedWallets = []; - for (var walletInfo in walletInfoSource.values) { - if (walletInfo.type == WalletType.bitcoin) { - final password = await keyService.getWalletPassword(walletName: walletInfo.name); - final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type); - final jsonSource = await read(path: path, password: password); - final data = json.decode(jsonSource) as Map; - final mnemonic = data['mnemonic'] as String?; + List affectedWallets = []; + for (var walletInfo in walletInfoSource.values) { + if (walletInfo.type == WalletType.bitcoin) { + final password = await keyService.getWalletPassword(walletName: walletInfo.name); + final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type); + final jsonSource = await read(path: path, password: password); + final data = json.decode(jsonSource) as Map; + final mnemonic = data['mnemonic'] as String?; - if (mnemonic == null) continue; + if (mnemonic == null) continue; - final hash = await Cryptography.instance.sha256().hash(utf8.encode(mnemonic)); - final seedSha = bytesToHex(hash.bytes); + final hash = await Cryptography.instance.sha256().hash(utf8.encode(mnemonic)); + final seedSha = bytesToHex(hash.bytes); - if (vulnerableSeeds.contains(seedSha)) { - affectedWallets.add(walletInfo.name); + if (vulnerableSeeds.contains(seedSha)) { + affectedWallets.add(walletInfo.name); + } } } - } - return affectedWallets; + return affectedWallets; + } catch (_) { + return []; + } } Future getServicesStatus() async { diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 2fa596311..037f50055 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -272,7 +272,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor final SendTemplateViewModel sendTemplateViewModel; final BalanceViewModel balanceViewModel; final ContactListViewModel contactListViewModel; - final LedgerViewModel ledgerViewModel; + final LedgerViewModel? ledgerViewModel; final FiatConversionStore _fiatConversationStore; final Box transactionDescriptionBox; @@ -368,7 +368,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor final errorCode = e.errorCode.toRadixString(16); final fallbackMsg = e.message.isNotEmpty ? e.message : "Unexpected Ledger Error Code: $errorCode"; - final errorMsg = ledgerViewModel.interpretErrorCode(errorCode) ?? fallbackMsg; + final errorMsg = ledgerViewModel!.interpretErrorCode(errorCode) ?? fallbackMsg; state = FailureState(errorMsg); } else { diff --git a/scripts/android/app_env.sh b/scripts/android/app_env.sh index bb1d4558a..fe0d1a524 100644 --- a/scripts/android/app_env.sh +++ b/scripts/android/app_env.sh @@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_ANDROID_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.13.2" -MONERO_COM_BUILD_NUMBER=88 +MONERO_COM_VERSION="1.14.0" +MONERO_COM_BUILD_NUMBER=89 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.17.0" -CAKEWALLET_BUILD_NUMBER=213 +CAKEWALLET_BUILD_NUMBER=215 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_SCHEME="cakewallet" @@ -72,4 +72,4 @@ export APP_ANDROID_VERSION export APP_ANDROID_BUILD_NUMBER export APP_ANDROID_BUNDLE_ID export APP_ANDROID_PACKAGE -export APP_ANDROID_SCHEME \ No newline at end of file +export APP_ANDROID_SCHEME diff --git a/scripts/ios/app_env.sh b/scripts/ios/app_env.sh index 9e36b41ae..82da5fa41 100644 --- a/scripts/ios/app_env.sh +++ b/scripts/ios/app_env.sh @@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN) APP_IOS_TYPE=$1 MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.13.2" -MONERO_COM_BUILD_NUMBER=86 +MONERO_COM_VERSION="1.14.0" +MONERO_COM_BUILD_NUMBER=87 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_VERSION="4.17.0" -CAKEWALLET_BUILD_NUMBER=241 +CAKEWALLET_BUILD_NUMBER=244 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" HAVEN_NAME="Haven" diff --git a/scripts/macos/app_env.sh b/scripts/macos/app_env.sh index b1eee6a57..196d49eaf 100755 --- a/scripts/macos/app_env.sh +++ b/scripts/macos/app_env.sh @@ -16,13 +16,13 @@ if [ -n "$1" ]; then fi MONERO_COM_NAME="Monero.com" -MONERO_COM_VERSION="1.3.2" -MONERO_COM_BUILD_NUMBER=19 +MONERO_COM_VERSION="1.4.0" +MONERO_COM_BUILD_NUMBER=20 MONERO_COM_BUNDLE_ID="com.cakewallet.monero" CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_VERSION="1.10.0" -CAKEWALLET_BUILD_NUMBER=75 +CAKEWALLET_BUILD_NUMBER=76 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then