diff --git a/assets/text/Release_Notes.txt b/assets/text/Release_Notes.txt index faad67777..483f249cf 100644 --- a/assets/text/Release_Notes.txt +++ b/assets/text/Release_Notes.txt @@ -1 +1,3 @@ -Bug fixes and generic enhancements \ No newline at end of file +Add Tron wallet +Hardware wallets enhancements +Bug fixes \ No newline at end of file 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/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 9bf322d72..94145c1d7 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -877,7 +877,6 @@ abstract class ElectrumWalletBase coin.isFrozen = coinInfo.isFrozen; coin.isSending = coinInfo.isSending; coin.note = coinInfo.note; - coin.bitcoinAddressRecord.balance += coinInfo.value; } else { _addCoinInfo(coin); } diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index a459f83ce..28fb3a430 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 56c9c7dff..082e9b22f 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -371,6 +371,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 0bc8c5011..e8450e8ff 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -406,7 +406,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); @@ -426,11 +425,12 @@ Future setup({ getIt.registerSingleton( ExchangeTemplateStore(templateSource: _exchangeTemplates)); getIt.registerSingleton( - YatStore(appStore: getIt.get(), secureStorage: getIt.get()) - ..init()); + YatStore(appStore: getIt.get(), secureStorage: getIt.get())..init()); getIt.registerSingleton( AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource)); + getIt.registerLazySingleton(() => LedgerViewModel()); + final secretStore = await SecretStoreBase.load(getIt.get()); getIt.registerSingleton(secretStore); @@ -722,7 +722,7 @@ Future setup({ getIt.get(), getIt.get(), _transactionDescriptionBox, - getIt.get(), + getIt.get().wallet!.isHardwareWallet ? getIt.get() : null, ), ); @@ -928,10 +928,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, @@ -1034,9 +1038,9 @@ Future setup({ (derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations)); getIt.registerFactoryParam, void>( - (credentials, _) => + (derivations, _) => WalletRestoreChooseDerivationPage(getIt.get( - param1: credentials, + param1: derivations, ))); getIt.registerFactoryParam( @@ -1084,8 +1088,8 @@ Future setup({ getIt.registerFactory(() => BackupPage(getIt.get())); - getIt.registerFactory(() => - EditBackupPasswordViewModel(getIt.get(), getIt.get())); + getIt.registerFactory( + () => EditBackupPasswordViewModel(getIt.get(), getIt.get())); getIt.registerFactory(() => EditBackupPasswordPage(getIt.get())); @@ -1133,8 +1137,8 @@ Future setup({ getIt.registerFactory(() => SupportPage(getIt.get())); - getIt.registerFactory(() => SupportChatPage(getIt.get(), - secureStorage: getIt.get())); + getIt.registerFactory(() => + SupportChatPage(getIt.get(), secureStorage: getIt.get())); getIt.registerFactory(() => SupportOtherLinksPage(getIt.get())); diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 79177178c..d184c74b1 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -65,6 +65,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 d9b74869f..65c5a07f6 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -35,7 +35,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 { @@ -373,17 +372,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 803e27c5c..00b409c66 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -334,6 +334,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 @@ -343,6 +345,8 @@ abstract class DashboardViewModelBase with Store { .toList(); } + bool get hasSellProviders => ProvidersHelper.getAvailableSellProviderTypes(wallet.type).isNotEmpty; + bool get shouldShowYatPopup => settingsStore.shouldShowYatPopup; @action @@ -360,13 +364,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; @@ -522,34 +526,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 a633d3982..4b30ecef3 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -269,7 +269,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; @@ -365,7 +365,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 b520a3179..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.16.2" -CAKEWALLET_BUILD_NUMBER=212 +CAKEWALLET_VERSION="4.17.0" +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 f70963745..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.16.2" -CAKEWALLET_BUILD_NUMBER=240 +CAKEWALLET_VERSION="4.17.0" +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 afdac3e6c..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.9.2" -CAKEWALLET_BUILD_NUMBER=73 +CAKEWALLET_VERSION="1.10.0" +CAKEWALLET_BUILD_NUMBER=76 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then