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
This commit is contained in:
Omar Hatem 2024-05-22 04:18:04 +03:00 committed by GitHub
parent f846f91e5f
commit 7b5204fdaa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 98 additions and 67 deletions

View file

@ -1 +1,3 @@
Bug fixes and generic enhancements Add Tron wallet
Hardware wallets enhancements
Bug fixes

View file

@ -28,6 +28,12 @@ Map<DerivationType, List<DerivationInfo>> electrum_derivations = {
description: "Standard BIP84 native segwit", description: "Standard BIP84 native segwit",
scriptType: "p2wpkh", scriptType: "p2wpkh",
), ),
DerivationInfo(
derivationType: DerivationType.bip39,
derivationPath: "m/86'/0'/0'",
description: "Standard BIP86 Taproot",
scriptType: "p2tr",
),
DerivationInfo( DerivationInfo(
derivationType: DerivationType.bip39, derivationType: DerivationType.bip39,
derivationPath: "m/0'", derivationPath: "m/0'",

View file

@ -14,7 +14,6 @@ import 'package:cw_bitcoin/electrum_wallet.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart';
import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/electrum_balance.dart';
import 'package:cw_bitcoin/litecoin_network.dart'; import 'package:cw_bitcoin/litecoin_network.dart';
import 'package:bitcoin_flutter/bitcoin_flutter.dart' as bitcoin;
import 'package:bip39/bip39.dart' as bip39; import 'package:bip39/bip39.dart' as bip39;
part 'litecoin_wallet.g.dart'; part 'litecoin_wallet.g.dart';

View file

@ -371,6 +371,9 @@ class CWBitcoin extends Bitcoin {
case "p2wpkh-p2sh": case "p2wpkh-p2sh":
address = generateP2SHAddress(hd: hd, network: network); address = generateP2SHAddress(hd: hd, network: network);
break; break;
case "p2tr":
address = generateP2TRAddress(hd: hd, network: network);
break;
default: default:
continue; continue;
} }

View file

@ -281,17 +281,19 @@ class MoonPayProvider extends BuyProvider {
throw Exception('Could not launch URL'); throw Exception('Could not launch URL');
} }
} catch (e) { } catch (e) {
await showDialog<void>( if (context.mounted) {
context: context, await showDialog<void>(
builder: (BuildContext context) { context: context,
return AlertWithOneAction( builder: (BuildContext context) {
alertTitle: 'MoonPay', return AlertWithOneAction(
alertContent: 'The MoonPay service is currently unavailable: $e', alertTitle: 'MoonPay',
buttonText: S.of(context).ok, alertContent: 'The MoonPay service is currently unavailable: $e',
buttonAction: () => Navigator.of(context).pop(), buttonText: S.of(context).ok,
); buttonAction: () => Navigator.of(context).pop(),
}, );
); },
);
}
} }
} }

View file

@ -330,8 +330,7 @@ Future<void> setup({
getIt.registerSingleton<ExchangeTemplateStore>( getIt.registerSingleton<ExchangeTemplateStore>(
ExchangeTemplateStore(templateSource: _exchangeTemplates)); ExchangeTemplateStore(templateSource: _exchangeTemplates));
getIt.registerSingleton<YatStore>( getIt.registerSingleton<YatStore>(
YatStore(appStore: getIt.get<AppStore>(), secureStorage: getIt.get<SecureStorage>()) YatStore(appStore: getIt.get<AppStore>(), secureStorage: getIt.get<SecureStorage>())..init());
..init());
getIt.registerSingleton<AnonpayTransactionsStore>( getIt.registerSingleton<AnonpayTransactionsStore>(
AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource)); AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource));
@ -626,7 +625,7 @@ Future<void> setup({
getIt.get<BalanceViewModel>(), getIt.get<BalanceViewModel>(),
getIt.get<ContactListViewModel>(), getIt.get<ContactListViewModel>(),
_transactionDescriptionBox, _transactionDescriptionBox,
getIt.get<LedgerViewModel>(), getIt.get<AppStore>().wallet!.isHardwareWallet ? getIt.get<LedgerViewModel>() : null,
), ),
); );
@ -833,10 +832,14 @@ Future<void> setup({
isSelected: isSelected)); isSelected: isSelected));
getIt.registerFactory<RobinhoodBuyProvider>(() => RobinhoodBuyProvider( getIt.registerFactory<RobinhoodBuyProvider>(() => RobinhoodBuyProvider(
wallet: getIt.get<AppStore>().wallet!, ledgerVM: getIt.get<LedgerViewModel>())); wallet: getIt.get<AppStore>().wallet!,
ledgerVM:
getIt.get<AppStore>().wallet!.isHardwareWallet ? getIt.get<LedgerViewModel>() : null));
getIt.registerFactory<DFXBuyProvider>(() => DFXBuyProvider( getIt.registerFactory<DFXBuyProvider>(() => DFXBuyProvider(
wallet: getIt.get<AppStore>().wallet!, ledgerVM: getIt.get<LedgerViewModel>())); wallet: getIt.get<AppStore>().wallet!,
ledgerVM:
getIt.get<AppStore>().wallet!.isHardwareWallet ? getIt.get<LedgerViewModel>() : null));
getIt.registerFactory<MoonPayProvider>(() => MoonPayProvider( getIt.registerFactory<MoonPayProvider>(() => MoonPayProvider(
settingsStore: getIt.get<AppStore>().settingsStore, settingsStore: getIt.get<AppStore>().settingsStore,
@ -937,9 +940,9 @@ Future<void> setup({
(derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations)); (derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations));
getIt.registerFactoryParam<WalletRestoreChooseDerivationPage, List<DerivationInfo>, void>( getIt.registerFactoryParam<WalletRestoreChooseDerivationPage, List<DerivationInfo>, void>(
(credentials, _) => (derivations, _) =>
WalletRestoreChooseDerivationPage(getIt.get<WalletRestoreChooseDerivationViewModel>( WalletRestoreChooseDerivationPage(getIt.get<WalletRestoreChooseDerivationViewModel>(
param1: credentials, param1: derivations,
))); )));
getIt.registerFactoryParam<TransactionDetailsViewModel, TransactionInfo, void>( getIt.registerFactoryParam<TransactionDetailsViewModel, TransactionInfo, void>(
@ -987,8 +990,8 @@ Future<void> setup({
getIt.registerFactory(() => BackupPage(getIt.get<BackupViewModel>())); getIt.registerFactory(() => BackupPage(getIt.get<BackupViewModel>()));
getIt.registerFactory(() => getIt.registerFactory(
EditBackupPasswordViewModel(getIt.get<SecureStorage>(), getIt.get<SecretStore>())); () => EditBackupPasswordViewModel(getIt.get<SecureStorage>(), getIt.get<SecretStore>()));
getIt.registerFactory(() => EditBackupPasswordPage(getIt.get<EditBackupPasswordViewModel>())); getIt.registerFactory(() => EditBackupPasswordPage(getIt.get<EditBackupPasswordViewModel>()));
@ -1036,8 +1039,8 @@ Future<void> setup({
getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>())); getIt.registerFactory(() => SupportPage(getIt.get<SupportViewModel>()));
getIt.registerFactory(() => SupportChatPage(getIt.get<SupportViewModel>(), getIt.registerFactory(() =>
secureStorage: getIt.get<SecureStorage>())); SupportChatPage(getIt.get<SupportViewModel>(), secureStorage: getIt.get<SecureStorage>()));
getIt.registerFactory(() => SupportOtherLinksPage(getIt.get<SupportViewModel>())); getIt.registerFactory(() => SupportOtherLinksPage(getIt.get<SupportViewModel>()));

View file

@ -65,6 +65,7 @@ class PreferencesKey {
static const lookupsUnstoppableDomains = 'looks_up_unstoppable_domain'; static const lookupsUnstoppableDomains = 'looks_up_unstoppable_domain';
static const lookupsOpenAlias = 'looks_up_open_alias'; static const lookupsOpenAlias = 'looks_up_open_alias';
static const lookupsENS = 'looks_up_ens'; static const lookupsENS = 'looks_up_ens';
static const showCameraConsent = 'show_camera_consent';
static String moneroWalletUpdateV1Key(String name) => static String moneroWalletUpdateV1Key(String name) =>
'${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}'; '${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}';

View file

@ -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/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.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/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:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';
class WebViewPage extends BasePage { class WebViewPage extends BasePage {
WebViewPage(this._title, this._url); WebViewPage(this._title, this._url);
@ -42,8 +46,9 @@ class WebViewPageBodyState extends State<WebViewPageBody> {
), ),
initialUrlRequest: URLRequest(url: WebUri.uri(widget.uri)), initialUrlRequest: URLRequest(url: WebUri.uri(widget.uri)),
onPermissionRequest: (controller, request) async { onPermissionRequest: (controller, request) async {
bool permissionGranted = await Permission.camera.status == PermissionStatus.granted; final sharedPrefs = getIt.get<SharedPreferences>();
if (!permissionGranted) {
if (sharedPrefs.getBool(PreferencesKey.showCameraConsent) ?? true) {
final bool userConsent = await showPopUp<bool>( final bool userConsent = await showPopUp<bool>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -65,9 +70,12 @@ class WebViewPageBodyState extends State<WebViewPageBody> {
); );
} }
permissionGranted = await Permission.camera.request().isGranted; sharedPrefs.setBool(PreferencesKey.showCameraConsent, false);
} }
bool permissionGranted =
await PermissionHandler.checkPermission(Permission.camera, context);
return PermissionResponse( return PermissionResponse(
resources: request.resources, resources: request.resources,
action: permissionGranted action: permissionGranted

View file

@ -35,7 +35,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:mobx/mobx.dart'; import 'package:mobx/mobx.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart';
import 'package:cw_core/crypto_currency.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
class SendPage extends BasePage { class SendPage extends BasePage {
@ -373,17 +372,17 @@ class SendPage extends BasePage {
} }
if (sendViewModel.wallet.isHardwareWallet) { if (sendViewModel.wallet.isHardwareWallet) {
if (!sendViewModel.ledgerViewModel.isConnected) { if (!sendViewModel.ledgerViewModel!.isConnected) {
await Navigator.of(context).pushNamed(Routes.connectDevices, await Navigator.of(context).pushNamed(Routes.connectDevices,
arguments: ConnectDevicePageParams( arguments: ConnectDevicePageParams(
walletType: sendViewModel.walletType, walletType: sendViewModel.walletType,
onConnectDevice: (BuildContext context, _) { onConnectDevice: (BuildContext context, _) {
sendViewModel.ledgerViewModel.setLedger(sendViewModel.wallet); sendViewModel.ledgerViewModel!.setLedger(sendViewModel.wallet);
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
)); ));
} else { } else {
sendViewModel.ledgerViewModel.setLedger(sendViewModel.wallet); sendViewModel.ledgerViewModel!.setLedger(sendViewModel.wallet);
} }
} }

View file

@ -329,6 +329,8 @@ abstract class DashboardViewModelBase with Store {
.toList(); .toList();
} }
bool get hasBuyProviders => ProvidersHelper.getAvailableBuyProviderTypes(wallet.type).isNotEmpty;
List<BuyProvider> get availableSellProviders { List<BuyProvider> get availableSellProviders {
final providerTypes = ProvidersHelper.getAvailableSellProviderTypes(wallet.type); final providerTypes = ProvidersHelper.getAvailableSellProviderTypes(wallet.type);
return providerTypes return providerTypes
@ -338,6 +340,8 @@ abstract class DashboardViewModelBase with Store {
.toList(); .toList();
} }
bool get hasSellProviders => ProvidersHelper.getAvailableSellProviderTypes(wallet.type).isNotEmpty;
bool get shouldShowYatPopup => settingsStore.shouldShowYatPopup; bool get shouldShowYatPopup => settingsStore.shouldShowYatPopup;
@action @action
@ -350,13 +354,13 @@ abstract class DashboardViewModelBase with Store {
bool hasExchangeAction; bool hasExchangeAction;
@computed @computed
bool get isEnabledBuyAction => !settingsStore.disableBuy && availableBuyProviders.isNotEmpty; bool get isEnabledBuyAction => !settingsStore.disableBuy && hasBuyProviders;
@observable @observable
bool hasBuyAction; bool hasBuyAction;
@computed @computed
bool get isEnabledSellAction => !settingsStore.disableSell && availableSellProviders.isNotEmpty; bool get isEnabledSellAction => !settingsStore.disableSell && hasSellProviders;
@observable @observable
bool hasSellAction; bool hasSellAction;
@ -495,34 +499,38 @@ abstract class DashboardViewModelBase with Store {
void setSyncAll(bool value) => settingsStore.currentSyncAll = value; void setSyncAll(bool value) => settingsStore.currentSyncAll = value;
Future<List<String>> checkAffectedWallets() async { Future<List<String>> checkAffectedWallets() async {
// await load file try {
final vulnerableSeedsString = await rootBundle // await load file
.loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt'); final vulnerableSeedsString = await rootBundle
final vulnerableSeeds = vulnerableSeedsString.split("\n"); .loadString('assets/text/cakewallet_weak_bitcoin_seeds_hashed_sorted_version1.txt');
final vulnerableSeeds = vulnerableSeedsString.split("\n");
final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName); final walletInfoSource = await CakeHive.openBox<WalletInfo>(WalletInfo.boxName);
List<String> affectedWallets = []; List<String> affectedWallets = [];
for (var walletInfo in walletInfoSource.values) { for (var walletInfo in walletInfoSource.values) {
if (walletInfo.type == WalletType.bitcoin) { if (walletInfo.type == WalletType.bitcoin) {
final password = await keyService.getWalletPassword(walletName: walletInfo.name); final password = await keyService.getWalletPassword(walletName: walletInfo.name);
final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type); final path = await pathForWallet(name: walletInfo.name, type: walletInfo.type);
final jsonSource = await read(path: path, password: password); final jsonSource = await read(path: path, password: password);
final data = json.decode(jsonSource) as Map; final data = json.decode(jsonSource) as Map;
final mnemonic = data['mnemonic'] as String?; 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 hash = await Cryptography.instance.sha256().hash(utf8.encode(mnemonic));
final seedSha = bytesToHex(hash.bytes); final seedSha = bytesToHex(hash.bytes);
if (vulnerableSeeds.contains(seedSha)) { if (vulnerableSeeds.contains(seedSha)) {
affectedWallets.add(walletInfo.name); affectedWallets.add(walletInfo.name);
}
} }
} }
}
return affectedWallets; return affectedWallets;
} catch (_) {
return [];
}
} }
Future<ServicesResponse> getServicesStatus() async { Future<ServicesResponse> getServicesStatus() async {

View file

@ -269,7 +269,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
final SendTemplateViewModel sendTemplateViewModel; final SendTemplateViewModel sendTemplateViewModel;
final BalanceViewModel balanceViewModel; final BalanceViewModel balanceViewModel;
final ContactListViewModel contactListViewModel; final ContactListViewModel contactListViewModel;
final LedgerViewModel ledgerViewModel; final LedgerViewModel? ledgerViewModel;
final FiatConversionStore _fiatConversationStore; final FiatConversionStore _fiatConversationStore;
final Box<TransactionDescription> transactionDescriptionBox; final Box<TransactionDescription> transactionDescriptionBox;
@ -365,7 +365,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
final errorCode = e.errorCode.toRadixString(16); final errorCode = e.errorCode.toRadixString(16);
final fallbackMsg = final fallbackMsg =
e.message.isNotEmpty ? e.message : "Unexpected Ledger Error Code: $errorCode"; 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); state = FailureState(errorMsg);
} else { } else {

View file

@ -15,15 +15,15 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_ANDROID_TYPE=$1 APP_ANDROID_TYPE=$1
MONERO_COM_NAME="Monero.com" MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.13.2" MONERO_COM_VERSION="1.14.0"
MONERO_COM_BUILD_NUMBER=88 MONERO_COM_BUILD_NUMBER=89
MONERO_COM_BUNDLE_ID="com.monero.app" MONERO_COM_BUNDLE_ID="com.monero.app"
MONERO_COM_PACKAGE="com.monero.app" MONERO_COM_PACKAGE="com.monero.app"
MONERO_COM_SCHEME="monero.com" MONERO_COM_SCHEME="monero.com"
CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="4.16.2" CAKEWALLET_VERSION="4.17.0"
CAKEWALLET_BUILD_NUMBER=212 CAKEWALLET_BUILD_NUMBER=213
CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet" CAKEWALLET_BUNDLE_ID="com.cakewallet.cake_wallet"
CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet" CAKEWALLET_PACKAGE="com.cakewallet.cake_wallet"
CAKEWALLET_SCHEME="cakewallet" CAKEWALLET_SCHEME="cakewallet"

View file

@ -13,13 +13,13 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_IOS_TYPE=$1 APP_IOS_TYPE=$1
MONERO_COM_NAME="Monero.com" MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.13.2" MONERO_COM_VERSION="1.14.0"
MONERO_COM_BUILD_NUMBER=86 MONERO_COM_BUILD_NUMBER=87
MONERO_COM_BUNDLE_ID="com.cakewallet.monero" MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="4.16.2" CAKEWALLET_VERSION="4.17.0"
CAKEWALLET_BUILD_NUMBER=240 CAKEWALLET_BUILD_NUMBER=244
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
HAVEN_NAME="Haven" HAVEN_NAME="Haven"

View file

@ -16,13 +16,13 @@ if [ -n "$1" ]; then
fi fi
MONERO_COM_NAME="Monero.com" MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.3.2" MONERO_COM_VERSION="1.4.0"
MONERO_COM_BUILD_NUMBER=19 MONERO_COM_BUILD_NUMBER=20
MONERO_COM_BUNDLE_ID="com.cakewallet.monero" MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet" CAKEWALLET_NAME="Cake Wallet"
CAKEWALLET_VERSION="1.9.2" CAKEWALLET_VERSION="1.10.0"
CAKEWALLET_BUILD_NUMBER=73 CAKEWALLET_BUILD_NUMBER=76
CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet" CAKEWALLET_BUNDLE_ID="com.fotolockr.cakewallet"
if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then if ! [[ " ${TYPES[*]} " =~ " ${APP_MACOS_TYPE} " ]]; then