V4.6.4 fixes (#904)

* Check if context is still mounted or not before showing popup

* - Refactor Restore route flow
- Fix Monero.com restore from QR
- Remove deprecated restore classes
- Update Monero.com app version and notes

* Update Macos version and release notes

* Fixate android plugin versions as Flutter published new fail packages version

* Revert desktop changes as it's not supported yet to scan QR code on Desktop [skip ci]

* Revert macos version update [skip ci]
This commit is contained in:
Omar Hatem 2023-04-27 17:06:09 +02:00 committed by GitHub
parent 82b513d1f8
commit 367efb3cae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 62 additions and 524 deletions

View file

@ -1,4 +1 @@
Fix for QR codes
Fix for creating sub-addresses
Fix Add/Edit nodes
Fix issues with text/amount fields
Fix Restore from QR code

View file

@ -41,17 +41,14 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/utils/language_list.dart';
import 'package:cake_wallet/view_model/wallet_new_vm.dart';
import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
import 'package:cake_wallet/view_model/wallet_restoration_from_keys_vm.dart';
import 'package:cake_wallet/exchange/trade.dart';
import 'package:cw_core/transaction_info.dart';
import 'package:cw_core/wallet_type.dart';
@ -65,9 +62,6 @@ import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart';
import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart';
import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_wallet_options_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_wallet_from_seed_page.dart';
import 'package:cake_wallet/src/screens/restore/restore_wallet_from_keys_page.dart';
import 'package:cake_wallet/src/screens/send/send_page.dart';
import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
import 'package:cake_wallet/src/screens/seed_language/seed_language_page.dart';
@ -144,14 +138,6 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SetupPinCodePage>(param1: callback));
case Routes.moneroRestoreWalletFromWelcome:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SetupPinCodePage>(
param1: (PinCodeState<PinCodeWidget> context, dynamic _) =>
Navigator.pushNamed(
context.context, Routes.restoreWallet, arguments: WalletType.monero)),
fullscreenDialog: true);
case Routes.restoreWalletType:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<NewWalletTypePage>(
@ -165,46 +151,35 @@ Route<dynamic> createRoute(RouteSettings settings) {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<RestoreOptionsPage>(param1: isNewInstall));
case Routes.restoreWalletOptions:
final type = WalletType.monero; //settings.arguments as WalletType;
return CupertinoPageRoute<void>(
builder: (_) => RestoreWalletOptionsPage(
type: type,
onRestoreFromSeed: (context) {
final route = type == WalletType.monero
? Routes.seedLanguage
: Routes.restoreWalletFromSeed;
final args = type == WalletType.monero
? [type, Routes.restoreWalletFromSeed]
: [type];
Navigator.of(context).pushNamed(route, arguments: args);
},
onRestoreFromKeys: (context) {
final route = type == WalletType.monero
? Routes.seedLanguage
: Routes.restoreWalletFromKeys;
final args = type == WalletType.monero
? [type, Routes.restoreWalletFromKeys]
: [type];
Navigator.of(context).pushNamed(route, arguments: args);
}));
case Routes.restoreWalletOptionsFromWelcome:
case Routes.restoreWalletFromSeedKeys:
final isNewInstall = settings.arguments as bool;
return isNewInstall ? CupertinoPageRoute<void>(
if (isNewInstall) {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SetupPinCodePage>(
param1: (PinCodeState<PinCodeWidget> context, dynamic _) =>
Navigator.pushNamed(
context.context, Routes.restoreWalletType)),
fullscreenDialog: true) : CupertinoPageRoute<void>(
param1: (PinCodeState<PinCodeWidget> context, dynamic _) {
if (isSingleCoin) {
return Navigator.of(context.context)
.pushNamed(Routes.restoreWallet, arguments: availableWalletTypes.first);
}
return Navigator.pushNamed(
context.context, Routes.restoreWalletType);
}),
fullscreenDialog: true);
} else if (isSingleCoin) {
return MaterialPageRoute<void>(
builder: (_) => getIt.get<WalletRestorePage>(
param1: availableWalletTypes.first
));
} else {
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<NewWalletTypePage>(
param1: (BuildContext context, WalletType type) =>
Navigator.of(context)
.pushNamed(Routes.restoreWallet, arguments: type),
param2: false));
}
case Routes.seed:
return MaterialPageRoute<void>(
@ -216,24 +191,6 @@ Route<dynamic> createRoute(RouteSettings settings) {
builder: (_) => getIt.get<WalletRestorePage>(
param1: settings.arguments as WalletType));
case Routes.restoreWalletFromSeed:
final type = settings.arguments as WalletType;
return CupertinoPageRoute<void>(
builder: (_) => RestoreWalletFromSeedPage(type: type));
case Routes.restoreWalletFromKeys:
final args = settings.arguments as List<dynamic>;
final type = args.first as WalletType;
final language =
type == WalletType.monero ? args[1] as String : LanguageList.english;
final walletRestorationFromKeysVM =
getIt.get<WalletRestorationFromKeysVM>(param1: [type, language]);
return CupertinoPageRoute<void>(
builder: (_) => RestoreWalletFromKeysPage(
walletRestorationFromKeysVM: walletRestorationFromKeysVM));
case Routes.sweepingWalletPage:
return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SweepingWalletPage>());

View file

@ -3,14 +3,9 @@ class Routes {
static const newWallet = '/new_wallet';
static const setupPin = '/setup_pin_code';
static const newWalletFromWelcome = '/new_wallet_from_welcome';
static const restoreFromWelcome = '/restore_from_welcome';
static const seed = '/seed';
static const restoreOptions = '/restore_options';
static const restoreOptionsFromWelcome = '/restore_options_from_welcome';
static const restoreWalletOptions = '/restore_seed_keys';
static const restoreWalletOptionsFromWelcome = '/restore_wallet_options_from_welcome';
static const restoreWalletFromSeed = '/restore_wallet_from_seed';
static const restoreWalletFromKeys = '/restore_wallet_from_keys';
static const restoreWalletFromSeedKeys = '/restore_wallet_from_seeds_keys';
static const dashboard = '/dashboard';
static const send = '/send';
static const transactionDetails = '/transaction_info';
@ -57,8 +52,6 @@ class Routes {
static const buyWebView = '/buy_web_view';
static const unspentCoinsList = '/unspent_coins_list';
static const unspentCoinsDetails = '/unspent_coins_details';
static const moneroRestoreWalletFromWelcome = '/monero_restore_wallet';
static const moneroNewWalletFromWelcome = '/monero_new_wallet';
static const addressPage = '/address_page';
static const fullscreenQR = '/fullscreen_qr';
static const ioniaWelcomePage = '/cake_pay_welcome_page';

View file

@ -1,6 +1,5 @@
import 'package:another_flushbar/flushbar.dart';
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/entities/desktop_dropdown_item.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/routes.dart';

View file

@ -7,6 +7,7 @@ import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/restore/wallet_restore_from_qr_code.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/wallet_type_utils.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/routes.dart';
import 'package:flutter/cupertino.dart';
@ -38,7 +39,7 @@ class RestoreOptionsPage extends BasePage {
children: <Widget>[
RestoreButton(
onPressed: () => Navigator.pushNamed(
context, Routes.restoreWalletOptionsFromWelcome,
context, Routes.restoreWalletFromSeedKeys,
arguments: isNewInstall),
image: imageSeedKeys,
title: S.of(context).restore_title_from_seed_keys,

View file

@ -1,212 +0,0 @@
import 'package:cake_wallet/core/wallet_name_validator.dart';
import 'package:cake_wallet/palette.dart';
import 'package:flutter/services.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/view_model/wallet_restoration_from_keys_vm.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
class RestoreWalletFromKeysPage extends BasePage {
RestoreWalletFromKeysPage(
{required this.walletRestorationFromKeysVM});
final WalletRestorationFromKeysVM walletRestorationFromKeysVM;
@override
String get title => S.current.restore_title_from_keys;
@override
Widget body(BuildContext context) => RestoreFromKeysFrom(walletRestorationFromKeysVM);
}
class RestoreFromKeysFrom extends StatefulWidget {
RestoreFromKeysFrom(this.walletRestorationFromKeysVM);
final WalletRestorationFromKeysVM walletRestorationFromKeysVM;
@override
_RestoreFromKeysFromState createState() => _RestoreFromKeysFromState();
}
class _RestoreFromKeysFromState extends State<RestoreFromKeysFrom> {
final _formKey = GlobalKey<FormState>();
final _blockchainHeightKey = GlobalKey<BlockchainHeightState>();
final _nameController = TextEditingController();
final _addressController = TextEditingController();
final _viewKeyController = TextEditingController();
final _spendKeyController = TextEditingController();
final _wifController = TextEditingController();
@override
void initState() {
_nameController.addListener(() =>
widget.walletRestorationFromKeysVM.name = _nameController.text);
_addressController.addListener(() =>
widget.walletRestorationFromKeysVM.address = _addressController.text);
_viewKeyController.addListener(() =>
widget.walletRestorationFromKeysVM.viewKey = _viewKeyController.text);
_spendKeyController.addListener(() =>
widget.walletRestorationFromKeysVM.spendKey = _spendKeyController.text);
_wifController.addListener(() =>
widget.walletRestorationFromKeysVM.wif = _wifController.text);
super.initState();
}
@override
void dispose() {
_nameController.dispose();
_addressController.dispose();
_viewKeyController.dispose();
_spendKeyController.dispose();
_wifController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
/*reaction((_) => walletRestorationStore.state, (WalletRestorationState state) {
if (state is WalletRestoredSuccessfully) {
Navigator.of(context).popUntil((route) => route.isFirst);
}
if (state is WalletRestorationFailure) {
WidgetsBinding.instance.addPostFrameCallback((_) {
showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.restore_title_from_keys,
alertContent: state.error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
});
}
});*/
return Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.only(bottom: 24.0),
content: Form(
key: _formKey,
child: Column(children: <Widget>[
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: _nameController,
hintText: S.of(context).restore_wallet_name,
validator: WalletNameValidator(),
)
))
],
),
if (!widget.walletRestorationFromKeysVM.hasRestorationHeight)
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: _wifController,
hintText: 'WIF',
)
))
],
),
if (widget.walletRestorationFromKeysVM.hasRestorationHeight) ... [
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: _addressController,
keyboardType: TextInputType.multiline,
maxLines: null,
hintText: S.of(context).restore_address,
)
))
],
),
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: _viewKeyController,
hintText: S.of(context).restore_view_key_private,
)
))
],
),
Row(
children: <Widget>[
Flexible(
child: Container(
padding: EdgeInsets.only(top: 20.0),
child: BaseTextFormField(
controller: _spendKeyController,
hintText: S.of(context).restore_spend_key_private,
)
))
],
),
BlockchainHeightWidget(
key: _blockchainHeightKey,
onHeightChange: (height) {
widget.walletRestorationFromKeysVM.height = height;
print(height);
}),
Padding(
padding: EdgeInsets.only(left: 40, right: 40, top: 24),
child: Text(
S.of(context).restore_from_date_or_blockheight,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.normal,
color: Theme.of(context).hintColor
),
),
)],
]),
),
bottomSectionPadding: EdgeInsets.only(bottom: 24),
bottomSection: Observer(builder: (_) {
return LoadingPrimaryButton(
onPressed: () {
if (_formKey.currentState != null && _formKey.currentState!.validate()) {
/*walletRestorationStore.restoreFromKeys(
name: _nameController.text,
language: seedLanguageStore.selectedSeedLanguage,
address: _addressController.text,
viewKey: _viewKeyController.text,
spendKey: _spendKeyController.text,
restoreHeight: _blockchainHeightKey.currentState.height);*/
}
},
text: S.of(context).restore_recover,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
textColor: Colors.white,
//isDisabled: walletRestorationStore.disabledState,
);
}),
),
);
}
}

View file

@ -1,199 +0,0 @@
import 'package:cake_wallet/src/screens/restore/restore_from_keys.dart';
import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart';
import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/utils/show_pop_up.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:cake_wallet/routes.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/seed_widget.dart';
import 'package:cw_core/wallet_type.dart';
import 'package:cake_wallet/core/seed_validator.dart';
import 'package:cake_wallet/core/mnemonic_length.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
class RestoreWalletFromSeedPage extends BasePage {
RestoreWalletFromSeedPage({required this.type})
: _pages = <Widget>[];
final WalletType type;
final String language = 'en';
// final formKey = GlobalKey<_RestoreFromSeedFormState>();
// final formKey = GlobalKey<_RestoreFromSeedFormState>();
@override
String get title => S.current.restore_title_from_seed;
final controller = PageController(initialPage: 0);
List<Widget> _pages;
Widget _page(BuildContext context, int index) {
if (_pages == null || _pages.isEmpty) {
_setPages(context);
}
return _pages[index];
}
int _pageLength(BuildContext context) {
if (_pages == null || _pages.isEmpty) {
_setPages(context);
}
return _pages.length;
}
void _setPages(BuildContext context) {
_pages = <Widget>[
// FIX-ME: Added args (displayBlockHeightSelector: true, displayLanguageSelector: true, type: type)
WalletRestoreFromSeedForm(displayBlockHeightSelector: true, displayLanguageSelector: true, type: type),
RestoreFromKeysFrom(),
];
}
@override
Widget body(BuildContext context) {
return Column(mainAxisAlignment: MainAxisAlignment.center, children: [
Expanded(
child: PageView.builder(
onPageChanged: (page) {
print('Page index $page');
},
controller: controller,
itemCount: _pageLength(context),
itemBuilder: (context, index) => _page(context, index))),
Padding(
padding: EdgeInsets.only(top: 10),
child: SmoothPageIndicator(
controller: controller,
count: _pageLength(context),
effect: ColorTransitionEffect(
spacing: 6.0,
radius: 6.0,
dotWidth: 6.0,
dotHeight: 6.0,
dotColor: Theme.of(context).hintColor.withOpacity(0.5),
activeDotColor: Theme.of(context).hintColor),
)),
Padding(
padding: EdgeInsets.only(top: 20, bottom: 24, left: 24, right: 24),
child: PrimaryButton(
text: S.of(context).restore_recover,
isDisabled: false,
onPressed: () => null,
color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
textColor: Colors.white)),
]);
// return GestureDetector(
// onTap: () =>
// SystemChannels.textInput.invokeMethod<void>('TextInput.hide'),
// child: ScrollableWithBottomSection(
// bottomSection: Column(children: [
// GestureDetector(
// onTap: () {},
// child: Text('Switch to restore from keys',
// style: TextStyle(fontSize: 15, color: Theme.of(context).hintColor))),
// SizedBox(height: 30),
// PrimaryButton(
// text: S.of(context).restore_next,
// isDisabled: false,
// onPressed: () => null,
// color: Theme.of(context).accentTextTheme!.bodyText1!.color!,
// textColor: Colors.white)
// ]),
// contentPadding: EdgeInsets.only(bottom: 24),
// content: Container(
// padding: EdgeInsets.only(left: 25, right: 25),
// child: Column(children: [
// SeedWidget(
// maxLength: mnemonicLength(type),
// onMnemonicChange: (seed) => null,
// onFinish: () => Navigator.of(context).pushNamed(
// Routes.restoreWalletFromSeedDetails,
// arguments: [type, language, '']),
// validator: SeedValidator(type: type, language: language),
// ),
// // SizedBox(height: 15),
// // BaseTextFormField(hintText: 'Language', initialValue: 'English'),
// BlockchainHeightWidget(
// // key: _blockchainHeightKey,
// onHeightChange: (height) {
// // widget.walletRestorationFromKeysVM.height = height;
// print(height);
// })
// ]))),
// );
}
}
class RestoreFromSeedForm extends StatefulWidget {
RestoreFromSeedForm(
{Key? key,
required this.type,
this.language,
this.leading,
this.middle})
: super(key: key);
final WalletType type;
final String? language;
final Widget? leading;
final Widget? middle;
@override
_RestoreFromSeedFormState createState() => _RestoreFromSeedFormState();
}
class _RestoreFromSeedFormState extends State<RestoreFromSeedForm> {
// final _seedKey = GlobalKey<SeedWidgetState>();
String mnemonic() =>
''; // _seedKey.currentState.items.map((e) => e.text).join(' ');
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () =>
SystemChannels.textInput.invokeMethod<void>('TextInput.hide'),
child: Container(
padding: EdgeInsets.only(left: 24, right: 24),
// color: Colors.blue,
// height: 300,
child: Column(children: [
SeedWidget(
type: widget.type,
language: widget.language ?? '',
// key: _seedKey,
// maxLength: mnemonicLength(widget.type),
// onMnemonicChange: (seed) => null,
// onFinish: () => Navigator.of(context).pushNamed(
// Routes.restoreWalletFromSeedDetails,
// arguments: [widget.type, widget.language, mnemonic()]),
// leading: widget.leading,
// middle: widget.middle,
// validator:
// SeedValidator(type: widget.type, language: widget.language),
),
BlockchainHeightWidget(
// key: _blockchainHeightKey,
onHeightChange: (height) {
// widget.walletRestorationFromKeysVM.height = height;
print(height);
}),
Container(
color: Colors.green,
width: 100,
height: 56,
child: BaseTextFormField(
hintText: 'Language', initialValue: 'English')),
])),
);
}
}

View file

@ -174,12 +174,7 @@ class WalletListBodyState extends State<WalletListBody> {
SizedBox(height: 10.0),
PrimaryImageButton(
onPressed: () {
if (isSingleCoin) {
Navigator.of(context).pushNamed(Routes.restoreWallet,
arguments: widget.walletListViewModel.currentWalletType);
} else {
Navigator.of(context).pushNamed(Routes.restoreOptions, arguments: false);
}
},
image: restoreWalletImage,
text: S.of(context).wallet_list_restore_wallet,

View file

@ -1,14 +1,14 @@
import 'package:flutter/material.dart';
Future<T?> showPopUp<T>({
required BuildContext context,
Future<T?> showPopUp<T>(
{required BuildContext context,
required WidgetBuilder builder,
bool barrierDismissible = true,
Color? barrierColor,
bool useSafeArea = false,
bool useRootNavigator = true,
RouteSettings? routeSettings
}) {
RouteSettings? routeSettings}) async {
if (context.mounted) {
return showDialog<T>(
context: context,
builder: builder,
@ -17,4 +17,7 @@ Future<T?> showPopUp<T>({
useSafeArea: useSafeArea,
useRootNavigator: useRootNavigator,
routeSettings: routeSettings);
}
return null;
}

View file

@ -73,6 +73,10 @@ dependencies:
url: https://github.com/cake-tech/cake_backup.git
ref: main
version: 1.0.0
flutter_plugin_android_lifecycle: 2.0.9
path_provider_android: 2.0.24
shared_preferences_android: 2.0.17
url_launcher_android: 6.0.24
dev_dependencies:
flutter_test:

View file

@ -14,8 +14,8 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_ANDROID_TYPE=$1
MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.3.4"
MONERO_COM_BUILD_NUMBER=47
MONERO_COM_VERSION="1.3.5"
MONERO_COM_BUILD_NUMBER=48
MONERO_COM_BUNDLE_ID="com.monero.app"
MONERO_COM_PACKAGE="com.monero.app"

View file

@ -13,8 +13,8 @@ TYPES=($MONERO_COM $CAKEWALLET $HAVEN)
APP_IOS_TYPE=$1
MONERO_COM_NAME="Monero.com"
MONERO_COM_VERSION="1.3.4"
MONERO_COM_BUILD_NUMBER=45
MONERO_COM_VERSION="1.3.5"
MONERO_COM_BUILD_NUMBER=46
MONERO_COM_BUNDLE_ID="com.cakewallet.monero"
CAKEWALLET_NAME="Cake Wallet"