Generic enhancements (#1456)

* minor enhancement * show camera consent at least once, even if camera permission is granted already * minor enhancement [skip ci] * Add taproot derivation path to electrum_derivations.dart * remove unused import [skip ci] * Initialize Ledger only when necessary * Update app versions

Omar Hatem committed May 22, 2024 at 04:18 UTC 7b5204fdaabd3b1f56646ba9d5fe72f22a67759c
14 files changed +103 -72
assets/text/Release_Notes.txt
+3 -1
@@ -1 +1,3 @@
1 -Bug fixes and generic enhancements
\ No newline at end of file
1 +Add Tron wallet
2 +Hardware wallets enhancements
3 +Bug fixes
\ No newline at end of file
cw_bitcoin/lib/electrum_derivations.dart
+6
@@ -28,6 +28,12 @@ Map<DerivationType, List<DerivationInfo>> electrum_derivations = {
28 description: "Standard BIP84 native segwit",
29 scriptType: "p2wpkh",
30 ),
31 + DerivationInfo(
32 + derivationType: DerivationType.bip39,
33 + derivationPath: "m/86'/0'/0'",
34 + description: "Standard BIP86 Taproot",
35 + scriptType: "p2tr",
36 + ),
37 DerivationInfo(
38 derivationType: DerivationType.bip39,
39 derivationPath: "m/0'",
cw_bitcoin/lib/litecoin_wallet.dart
-1
@@ -14,7 +14,6 @@ import 'package:cw_bitcoin/electrum_wallet.dart';
14 import 'package:cw_bitcoin/bitcoin_address_record.dart';
15 import 'package:cw_bitcoin/electrum_balance.dart';
16 import 'package:cw_bitcoin/litecoin_network.dart';
17 -import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
17 import 'package:bip39/bip39.dart' as bip39;
18
19 part 'litecoin_wallet.g.dart';
lib/bitcoin/cw_bitcoin.dart
+3
@@ -371,6 +371,9 @@ class CWBitcoin extends Bitcoin {
371 case "p2wpkh-p2sh":
372 address = generateP2SHAddress(hd: hd, network: network);
373 break;
374 + case "p2tr":
375 + address = generateP2TRAddress(hd: hd, network: network);
376 + break;
377 default:
378 continue;
379 }
lib/buy/moonpay/moonpay_provider.dart
+13 -11
@@ -281,17 +281,19 @@ class MoonPayProvider extends BuyProvider {
281 throw Exception('Could not launch URL');
282 }
283 } catch (e) {
284 - await showDialog<void>(
285 - context: context,
286 - builder: (BuildContext context) {
287 - return AlertWithOneAction(
288 - alertTitle: 'MoonPay',
289 - alertContent: 'The MoonPay service is currently unavailable: $e',
290 - buttonText: S.of(context).ok,
291 - buttonAction: () => Navigator.of(context).pop(),
292 - );
293 - },
294 - );
284 + if (context.mounted) {
285 + await showDialog<void>(
286 + context: context,
287 + builder: (BuildContext context) {
288 + return AlertWithOneAction(
289 + alertTitle: 'MoonPay',
290 + alertContent: 'The MoonPay service is currently unavailable: $e',
291 + buttonText: S.of(context).ok,
292 + buttonAction: () => Navigator.of(context).pop(),
293 + );
294 + },
295 + );
296 + }
297 }
298 }
299
lib/di.dart
+14 -11
@@ -330,8 +330,7 @@ Future<void> setup({
330 getIt.registerSingleton<ExchangeTemplateStore>(
331 ExchangeTemplateStore(templateSource: _exchangeTemplates));
332 getIt.registerSingleton<YatStore>(
333 - YatStore(appStore: getIt.get<AppStore>(), secureStorage: getIt.get<SecureStorage>())
334 - ..init());
333 + YatStore(appStore: getIt.get<AppStore>(), secureStorage: getIt.get<SecureStorage>())..init());
334 getIt.registerSingleton<AnonpayTransactionsStore>(
335 AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource));
336
@@ -626,7 +625,7 @@ Future<void> setup({
625 getIt.get<BalanceViewModel>(),
626 getIt.get<ContactListViewModel>(),
627 _transactionDescriptionBox,
629 - getIt.get<LedgerViewModel>(),
628 + getIt.get<AppStore>().wallet!.isHardwareWallet ? getIt.get<LedgerViewModel>() : null,
629 ),
630 );
631
@@ -833,10 +832,14 @@ Future<void> setup({
832 isSelected: isSelected));
833
834 getIt.registerFactory<RobinhoodBuyProvider>(() => RobinhoodBuyProvider(
836 - wallet: getIt.get<AppStore>().wallet!, ledgerVM: getIt.get<LedgerViewModel>()));
835 + wallet: getIt.get<AppStore>().wallet!,
836 + ledgerVM:
837 + getIt.get<AppStore>().wallet!.isHardwareWallet ? getIt.get<LedgerViewModel>() : null));
838
839 getIt.registerFactory<DFXBuyProvider>(() => DFXBuyProvider(
839 - wallet: getIt.get<AppStore>().wallet!, ledgerVM: getIt.get<LedgerViewModel>()));
840 + wallet: getIt.get<AppStore>().wallet!,
841 + ledgerVM:
842 + getIt.get<AppStore>().wallet!.isHardwareWallet ? getIt.get<LedgerViewModel>() : null));
843
844 getIt.registerFactory<MoonPayProvider>(() => MoonPayProvider(
845 settingsStore: getIt.get<AppStore>().settingsStore,
@@ -937,9 +940,9 @@ Future<void> setup({
940 (derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations));
941
942 getIt.registerFactoryParam<WalletRestoreChooseDerivationPage, List<DerivationInfo>, void>(
940 - (credentials, _) =>
943 + (derivations, _) =>
944 WalletRestoreChooseDerivationPage(getIt.get<WalletRestoreChooseDerivationViewModel>(
942 - param1: credentials,
945 + param1: derivations,
946 )));
947
948 getIt.registerFactoryParam<TransactionDetailsViewModel, TransactionInfo, void>(
@@ -987,8 +990,8 @@ Future<void> setup({
990
991 getIt.registerFactory(() => BackupPage(getIt.get<BackupViewModel>()));
992
990 - getIt.registerFactory(() =>
991 - EditBackupPasswordViewModel(getIt.get<SecureStorage>(), getIt.get<SecretStore>()));
993 + getIt.registerFactory(
994 + () => EditBackupPasswordViewModel(getIt.get<SecureStorage>(), getIt.get<SecretStore>()));
995
996 getIt.registerFactory(() => EditBackupPasswordPage(getIt.get<EditBackupPasswordViewModel>()));
997
@@ -1036,8 +1039,8 @@ Future<void> setup({
1039
1040 getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>()));
1041
1039 - getIt.registerFactory(() => SupportChatPage(getIt.get<SupportViewModel>(),
1040 - secureStorage: getIt.get<SecureStorage>()));
1042 + getIt.registerFactory(() =>
1043 + SupportChatPage(getIt.get<SupportViewModel>(), secureStorage: getIt.get<SecureStorage>()));
1044
1045 getIt.registerFactory(() => SupportOtherLinksPage(getIt.get<SupportViewModel>()));
1046
lib/entities/preferences_key.dart
+1
@@ -65,6 +65,7 @@ class PreferencesKey {
65 static const lookupsUnstoppableDomains = 'looks_up_unstoppable_domain';
66 static const lookupsOpenAlias = 'looks_up_open_alias';
67 static const lookupsENS = 'looks_up_ens';
68 + static const showCameraConsent = 'show_camera_consent';
69
70 static String moneroWalletUpdateV1Key(String name) =>
71 '${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}';
lib/src/screens/buy/webview_page.dart
+11 -3
@@ -1,10 +1,14 @@
1 +import 'package:cake_wallet/di.dart';
2 +import 'package:cake_wallet/entities/preferences_key.dart';
3 import 'package:cake_wallet/generated/i18n.dart';
4 import 'package:cake_wallet/src/screens/base_page.dart';
5 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
6 +import 'package:cake_wallet/utils/permission_handler.dart';
7 import 'package:cake_wallet/utils/show_pop_up.dart';
8 import 'package:flutter/material.dart';
9 import 'package:flutter_inappwebview/flutter_inappwebview.dart';
10 import 'package:permission_handler/permission_handler.dart';
11 +import 'package:shared_preferences/shared_preferences.dart';
12
13 class WebViewPage extends BasePage {
14 WebViewPage(this._title, this._url);
@@ -42,8 +46,9 @@ class WebViewPageBodyState extends State<WebViewPageBody> {
46 ),
47 initialUrlRequest: URLRequest(url: WebUri.uri(widget.uri)),
48 onPermissionRequest: (controller, request) async {
45 - bool permissionGranted = await Permission.camera.status == PermissionStatus.granted;
46 - if (!permissionGranted) {
49 + final sharedPrefs = getIt.get<SharedPreferences>();
50 +
51 + if (sharedPrefs.getBool(PreferencesKey.showCameraConsent) ?? true) {
52 final bool userConsent = await showPopUp<bool>(
53 context: context,
54 builder: (BuildContext context) {
@@ -65,9 +70,12 @@ class WebViewPageBodyState extends State<WebViewPageBody> {
70 );
71 }
72
68 - permissionGranted = await Permission.camera.request().isGranted;
73 + sharedPrefs.setBool(PreferencesKey.showCameraConsent, false);
74 }
75
76 + bool permissionGranted =
77 + await PermissionHandler.checkPermission(Permission.camera, context);
78 +
79 return PermissionResponse(
80 resources: request.resources,
81 action: permissionGranted
lib/src/screens/send/send_page.dart
+3 -4
@@ -35,7 +35,6 @@ import 'package:flutter/material.dart';
35 import 'package:flutter_mobx/flutter_mobx.dart';
36 import 'package:mobx/mobx.dart';
37 import 'package:smooth_page_indicator/smooth_page_indicator.dart';
38 -import 'package:cw_core/crypto_currency.dart';
38 import 'package:url_launcher/url_launcher.dart';
39
40 class SendPage extends BasePage {
@@ -373,17 +372,17 @@ class SendPage extends BasePage {
372 }
373
374 if (sendViewModel.wallet.isHardwareWallet) {
376 - if (!sendViewModel.ledgerViewModel.isConnected) {
375 + if (!sendViewModel.ledgerViewModel!.isConnected) {
376 await Navigator.of(context).pushNamed(Routes.connectDevices,
377 arguments: ConnectDevicePageParams(
378 walletType: sendViewModel.walletType,
379 onConnectDevice: (BuildContext context, _) {
381 - sendViewModel.ledgerViewModel.setLedger(sendViewModel.wallet);
380 + sendViewModel.ledgerViewModel!.setLedger(sendViewModel.wallet);
381 Navigator.of(context).pop();
382 },
383 ));
384 } else {
386 - sendViewModel.ledgerViewModel.setLedger(sendViewModel.wallet);
385 + sendViewModel.ledgerViewModel!.setLedger(sendViewModel.wallet);
386 }
387 }
388
lib/view_model/dashboard/dashboard_view_model.dart
+35 -27
@@ -329,6 +329,8 @@ abstract class DashboardViewModelBase with Store {
329 .toList();
330 }
331
332 + bool get hasBuyProviders => ProvidersHelper.getAvailableBuyProviderTypes(wallet.type).isNotEmpty;
333 +
334 List<BuyProvider> get availableSellProviders {
335 final providerTypes = ProvidersHelper.getAvailableSellProviderTypes(wallet.type);
336 return providerTypes
@@ -338,6 +340,8 @@ abstract class DashboardViewModelBase with Store {
340 .toList();
341 }
342
343 + bool get hasSellProviders => ProvidersHelper.getAvailableSellProviderTypes(wallet.type).isNotEmpty;
344 +
345 bool get shouldShowYatPopup => settingsStore.shouldShowYatPopup;
346
347 @action
@@ -350,13 +354,13 @@ abstract class DashboardViewModelBase with Store {
354 bool hasExchangeAction;
355
356 @computed
353 - bool get isEnabledBuyAction => !settingsStore.disableBuy && availableBuyProviders.isNotEmpty;
357 + bool get isEnabledBuyAction => !settingsStore.disableBuy && hasBuyProviders;
358
359 @observable
360 bool hasBuyAction;
361
362 @computed
359 - bool get isEnabledSellAction => !settingsStore.disableSell && availableSellProviders.isNotEmpty;
363 + bool get isEnabledSellAction => !settingsStore.disableSell && hasSellProviders;
364
365 @observable
366 bool hasSellAction;
@@ -495,34 +499,38 @@ abstract class DashboardViewModelBase with Store {
499 void setSyncAll(bool value) => settingsStore.currentSyncAll = value;
500
501 Future<List<String>> checkAffectedWallets() async {
498 - // await load file
499 - final vulnerableSeedsString = await rootBundle
500 - .loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt');
501 - final vulnerableSeeds = vulnerableSeedsString.split("\n");
502 -
503 - final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
504 -
505 - List<String> affectedWallets = [];
506 - for (var walletInfo in walletInfoSource.values) {
507 - if (walletInfo.type == WalletType.bitcoin) {
508 - final password = await keyService.getWalletPassword(walletName: walletInfo.name);
509 - final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type);
510 - final jsonSource = await read(path: path, password: password);
511 - final data = json.decode(jsonSource) as Map;
512 - final mnemonic = data['mnemonic'] as String?;
513 -
514 - if (mnemonic == null) continue;
515 -
516 - final hash = await Cryptography.instance.sha256().hash(utf8.encode(mnemonic));
517 - final seedSha = bytesToHex(hash.bytes);
518 -
519 - if (vulnerableSeeds.contains(seedSha)) {
520 - affectedWallets.add(walletInfo.name);
502 + try {
503 + // await load file
504 + final vulnerableSeedsString = await rootBundle
505 + .loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt');
506 + final vulnerableSeeds = vulnerableSeedsString.split("\n");
507 +
508 + final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
509 +
510 + List<String> affectedWallets = [];
511 + for (var walletInfo in walletInfoSource.values) {
512 + if (walletInfo.type == WalletType.bitcoin) {
513 + final password = await keyService.getWalletPassword(walletName: walletInfo.name);
514 + final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type);
515 + final jsonSource = await read(path: path, password: password);
516 + final data = json.decode(jsonSource) as Map;
517 + final mnemonic = data['mnemonic'] as String?;
518 +
519 + if (mnemonic == null) continue;
520 +
521 + final hash = await Cryptography.instance.sha256().hash(utf8.encode(mnemonic));
522 + final seedSha = bytesToHex(hash.bytes);
523 +
524 + if (vulnerableSeeds.contains(seedSha)) {
525 + affectedWallets.add(walletInfo.name);
526 + }
527 }
528 }
523 - }
529
525 - return affectedWallets;
530 + return affectedWallets;
531 + } catch (_) {
532 + return [];
533 + }
534 }
535
536 Future<ServicesResponse> getServicesStatus() async {
lib/view_model/send/send_view_model.dart
+2 -2
@@ -269,7 +269,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
269 final SendTemplateViewModel sendTemplateViewModel;
270 final BalanceViewModel balanceViewModel;
271 final ContactListViewModel contactListViewModel;
272 - final LedgerViewModel ledgerViewModel;
272 + final LedgerViewModel? ledgerViewModel;
273 final FiatConversionStore _fiatConversationStore;
274 final Box<TransactionDescription> transactionDescriptionBox;
275
@@ -365,7 +365,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
365 final errorCode = e.errorCode.toRadixString(16);
366 final fallbackMsg =
367 e.message.isNotEmpty ? e.message : "Unexpected Ledger Error Code: $errorCode";
368 - final errorMsg = ledgerViewModel.interpretErrorCode(errorCode) ?? fallbackMsg;
368 + final errorMsg = ledgerViewModel!.interpretErrorCode(errorCode) ?? fallbackMsg;
369
370 state = FailureState(errorMsg);
371 } else {
scripts/android/app_env.sh
+4 -4
@@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
15 APP_ANDROID_TYPE=$1
16
17 MONERO_COM_NAME="Monero.com"
18 -MONERO_COM_VERSION="1.13.2"
19 -MONERO_COM_BUILD_NUMBER=88
18 +MONERO_COM_VERSION="1.14.0"
19 +MONERO_COM_BUILD_NUMBER=89
20 MONERO_COM_BUNDLE_ID="com.monero.app"
21 MONERO_COM_PACKAGE="com.monero.app"
22 MONERO_COM_SCHEME="monero.com"
23
24 CAKEWALLET_NAME="Cake Wallet"
25 -CAKEWALLET_VERSION="4.16.2"
26 -CAKEWALLET_BUILD_NUMBER=212
25 +CAKEWALLET_VERSION="4.17.0"
26 +CAKEWALLET_BUILD_NUMBER=213
27 CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
28 CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
29 CAKEWALLET_SCHEME="cakewallet"
scripts/ios/app_env.sh
+4 -4
@@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
13 APP_IOS_TYPE=$1
14
15 MONERO_COM_NAME="Monero.com"
16 -MONERO_COM_VERSION="1.13.2"
17 -MONERO_COM_BUILD_NUMBER=86
16 +MONERO_COM_VERSION="1.14.0"
17 +MONERO_COM_BUILD_NUMBER=87
18 MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
19
20 CAKEWALLET_NAME="Cake Wallet"
21 -CAKEWALLET_VERSION="4.16.2"
22 -CAKEWALLET_BUILD_NUMBER=240
21 +CAKEWALLET_VERSION="4.17.0"
22 +CAKEWALLET_BUILD_NUMBER=244
23 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
24
25 HAVEN_NAME="Haven"
scripts/macos/app_env.sh
+4 -4
@@ -16,13 +16,13 @@ if [ -n "$1" ]; then
16 fi
17
18 MONERO_COM_NAME="Monero.com"
19 -MONERO_COM_VERSION="1.3.2"
20 -MONERO_COM_BUILD_NUMBER=19
19 +MONERO_COM_VERSION="1.4.0"
20 +MONERO_COM_BUILD_NUMBER=20
21 MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
22
23 CAKEWALLET_NAME="Cake Wallet"
24 -CAKEWALLET_VERSION="1.9.2"
25 -CAKEWALLET_BUILD_NUMBER=73
24 +CAKEWALLET_VERSION="1.10.0"
25 +CAKEWALLET_BUILD_NUMBER=76
26 CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
27
28 if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then