feat: Implement sweep all

This commit is contained in:
Blazebrain 2023-07-14 09:40:10 +01:00
parent 857e355ee6
commit 49734332a1
6 changed files with 166 additions and 63 deletions

View file

@ -260,6 +260,17 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
final amount = final amount =
output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.'); output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.');
final unlockedBalance = balance[currency]?.unlockedBalance;
if (unlockedBalance == null || unlockedBalance == 0) {
final formattedBalance =
moneroAmountToString(amount: unlockedBalance ?? 0);
throw MoneroTransactionCreationException(
'You do not have enough unlocked balance. Unlocked: $formattedBalance. Transaction amount: ${output.cryptoAmount}.',
);
}
pendingTransactionDescription = await transaction_history.createTransaction( pendingTransactionDescription = await transaction_history.createTransaction(
address: address!, address: address!,
amount: amount, amount: amount,
@ -454,7 +465,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
syncStatus = SyncedSyncStatus(); syncStatus = SyncedSyncStatus();
//! Introduce completer //! Introduce completer
if (!syncCompleter.isCompleted) { if (!syncCompleter.isCompleted) {
syncCompleter.complete(); if (blocksLeft == 0) {
syncCompleter.complete();
}
} }
if (!_hasSyncAfterStartup) { if (!_hasSyncAfterStartup) {
_hasSyncAfterStartup = true; _hasSyncAfterStartup = true;

View file

@ -19,6 +19,7 @@ import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet
import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/transactions_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart';
import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart';
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/display_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/other_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/other_settings_page.dart';
import 'package:cake_wallet/src/screens/settings/privacy_page.dart'; import 'package:cake_wallet/src/screens/settings/privacy_page.dart';
@ -59,6 +60,7 @@ import 'package:cake_wallet/src/screens/dashboard/widgets/balance_page.dart';
import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_account_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_gift_cards_list_view_model.dart';
import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart'; import 'package:cake_wallet/view_model/ionia/ionia_purchase_merch_view_model.dart';
import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart'; import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart'; import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
@ -808,6 +810,11 @@ Future setup({
getIt.registerFactoryParam<RestoreOptionsPage, bool, void>( getIt.registerFactoryParam<RestoreOptionsPage, bool, void>(
(bool isNewInstall, _) => RestoreOptionsPage(isNewInstall: isNewInstall)); (bool isNewInstall, _) => RestoreOptionsPage(isNewInstall: isNewInstall));
getIt.registerFactoryParam<SweepingWalletPage, SweepingWalletPageData, void>(
(sweepingWalletPageData, _) =>
SweepingWalletPage(sweepingWalletPageData: sweepingWalletPageData),
);
getIt.registerFactory(() => RestoreFromBackupViewModel(getIt.get<BackupService>())); getIt.registerFactory(() => RestoreFromBackupViewModel(getIt.get<BackupService>()));
getIt.registerFactory(() => RestoreFromBackupPage(getIt.get<RestoreFromBackupViewModel>())); getIt.registerFactory(() => RestoreFromBackupPage(getIt.get<RestoreFromBackupViewModel>()));

View file

@ -199,7 +199,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
case Routes.sweepingWalletPage: case Routes.sweepingWalletPage:
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(
builder: (_) => getIt.get<SweepingWalletPage>()); builder: (_) => getIt.get<SweepingWalletPage>(
param1: settings.arguments as SweepingWalletPageData,
),
);
case Routes.dashboard: case Routes.dashboard:
return CupertinoPageRoute<void>( return CupertinoPageRoute<void>(

View file

@ -1,13 +1,12 @@
import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/utils/language_list.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/utils/show_pop_up.dart'; 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/restore_from_qr_vm.dart';
import 'package:cake_wallet/view_model/restore/wallet_restore_from_qr_code.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:flutter/material.dart';
import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/routes.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
@ -37,8 +36,13 @@ class RestoreOptionsPage extends BasePage {
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
RestoreButton( RestoreButton(
onPressed: () => Navigator.pushNamed(context, Routes.restoreWalletFromSeedKeys, onPressed: () {
arguments: isNewInstall), Navigator.pushNamed(
context,
Routes.restoreWalletFromSeedKeys,
arguments: isNewInstall,
);
},
image: imageSeedKeys, image: imageSeedKeys,
title: S.of(context).restore_title_from_seed_keys, title: S.of(context).restore_title_from_seed_keys,
description: S.of(context).restore_description_from_seed_keys), description: S.of(context).restore_description_from_seed_keys),
@ -49,7 +53,9 @@ class RestoreOptionsPage extends BasePage {
onPressed: () => Navigator.pushNamed(context, Routes.restoreFromBackup), onPressed: () => Navigator.pushNamed(context, Routes.restoreFromBackup),
image: imageBackup, image: imageBackup,
title: S.of(context).restore_title_from_backup, title: S.of(context).restore_title_from_backup,
description: S.of(context).restore_description_from_backup), description:
S.of(context).restore_description_from_backup,
),
), ),
Padding( Padding(
padding: EdgeInsets.only(top: 24), padding: EdgeInsets.only(top: 24),
@ -61,24 +67,46 @@ class RestoreOptionsPage extends BasePage {
arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) { arguments: (PinCodeState<PinCodeWidget> setupPinContext, String _) {
setupPinContext.close(); setupPinContext.close();
isPinSet = true; isPinSet = true;
}); },
);
} }
if (!isNewInstall || isPinSet) { if (!isNewInstall || isPinSet) {
try { try {
final restoreWallet = final restoreWallet = await WalletRestoreFromQRCode
await WalletRestoreFromQRCode.scanQRCodeForRestoring(context); .scanQRCodeForRestoring(context);
final restoreFromQRViewModel = final restoreFromQRViewModel =
getIt.get<WalletRestorationFromQRVM>(param1: restoreWallet.type); getIt.get<WalletRestorationFromQRVM>(
param1: restoreWallet.type,
);
await restoreFromQRViewModel.create(restoreWallet: restoreWallet); if (restoreWallet.txId != null &&
restoreWallet.txId!.isNotEmpty) {
Navigator.pushNamed(
context,
Routes.sweepingWalletPage,
arguments: SweepingWalletPageData(
restorationFromQRVM: restoreFromQRViewModel,
restoredWallet: restoreWallet,
),
);
} else {
await restoreFromQRViewModel.create(
restoreWallet: restoreWallet,
);
if (restoreFromQRViewModel.state is FailureState) { if (restoreFromQRViewModel.state
final errorState = restoreFromQRViewModel.state as FailureState; is FailureState) {
_onWalletCreateFailure(context, final errorState = restoreFromQRViewModel.state
'Create wallet state: ${errorState.error}'); as FailureState;
_onWalletCreateFailure(
context,
'Create wallet state: ${errorState.error}',
);
}
} }
} catch (e) { } catch (e) {
_onWalletCreateFailure(context, e.toString()); _onWalletCreateFailure(context, e.toString());
} }
@ -94,15 +122,19 @@ class RestoreOptionsPage extends BasePage {
); );
} }
void _onWalletCreateFailure(BuildContext context, String error) { void _onWalletCreateFailure(BuildContext context, String error) async {
showPopUp<void>( await showPopUp<void>(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
return AlertWithOneAction( return AlertWithOneAction(
alertTitle: S.current.error, alertTitle: S.current.error,
alertContent: error, alertContent: error,
buttonText: S.of(context).ok, buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()); buttonAction: () => Navigator.of(context).pop(),
}); );
},
);
Navigator.pop(context);
} }
} }

View file

@ -1,18 +1,23 @@
import 'package:cake_wallet/core/execution_state.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_base.dart';
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/restore_wallet.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
class SweepingWalletPage extends BasePage { class SweepingWalletPage extends BasePage {
SweepingWalletPage(); SweepingWalletPage({required this.sweepingWalletPageData});
final SweepingWalletPageData sweepingWalletPageData;
static const aspectRatioImage = 1.25; static const aspectRatioImage = 1.25;
final welcomeImageLight = Image.asset('assets/images/welcome_light.png'); final welcomeImageLight = Image.asset('assets/images/welcome_light.png');
final welcomeImageDark = Image.asset('assets/images/welcome.png'); final welcomeImageDark = Image.asset('assets/images/welcome.png');
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -23,23 +28,31 @@ class SweepingWalletPage extends BasePage {
@override @override
Widget body(BuildContext context) { Widget body(BuildContext context) {
final welcomeImage = currentTheme.type == ThemeType.dark ? welcomeImageDark : welcomeImageLight; final welcomeImage = currentTheme.type == ThemeType.dark
? welcomeImageDark
: welcomeImageLight;
return SweepingWalletWidget( return SweepingWalletWidget(
aspectRatioImage: aspectRatioImage,
welcomeImage: welcomeImage, welcomeImage: welcomeImage,
restoredWallet: sweepingWalletPageData.restoredWallet,
aspectRatioImage: aspectRatioImage,
restoreFromQRViewModel: sweepingWalletPageData.restorationFromQRVM,
); );
} }
} }
class SweepingWalletWidget extends StatefulWidget { class SweepingWalletWidget extends StatefulWidget {
const SweepingWalletWidget({ const SweepingWalletWidget({
required this.aspectRatioImage,
required this.welcomeImage, required this.welcomeImage,
required this.restoredWallet,
required this.aspectRatioImage,
required this.restoreFromQRViewModel,
}); });
final double aspectRatioImage;
final Image welcomeImage; final Image welcomeImage;
final double aspectRatioImage;
final RestoredWallet restoredWallet;
final WalletRestorationFromQRVM restoreFromQRViewModel;
@override @override
State<SweepingWalletWidget> createState() => _SweepingWalletWidgetState(); State<SweepingWalletWidget> createState() => _SweepingWalletWidgetState();
@ -49,11 +62,42 @@ class _SweepingWalletWidgetState extends State<SweepingWalletWidget> {
@override @override
void initState() { void initState() {
SchedulerBinding.instance.addPostFrameCallback((_) async { SchedulerBinding.instance.addPostFrameCallback((_) async {
await _initializeRestoreFromQR();
}); });
super.initState(); super.initState();
} }
Future<void> _initializeRestoreFromQR() async {
try {
await widget.restoreFromQRViewModel
.createFlowForSweepAll(restoreWallet: widget.restoredWallet);
if (widget.restoreFromQRViewModel.state is FailureState) {
final errorState = widget.restoreFromQRViewModel.state as FailureState;
_onWalletCreateFailure(
context, 'Create wallet state: ${errorState.error}');
}
} catch (e) {
_onWalletCreateFailure(context, e.toString());
}
}
void _onWalletCreateFailure(BuildContext context, String error) async {
await showPopUp<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.error,
alertContent: error,
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop(),
);
},
);
Navigator.pop(context);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return WillPopScope( return WillPopScope(
@ -61,15 +105,20 @@ class _SweepingWalletWidgetState extends State<SweepingWalletWidget> {
child: Container( child: Container(
padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24), padding: EdgeInsets.only(top: 64, bottom: 24, left: 24, right: 24),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Flexible( Flexible(
flex: 2, flex: 4,
child: AspectRatio( child: AspectRatio(
aspectRatio: widget.aspectRatioImage, aspectRatio: widget.aspectRatioImage,
child: FittedBox(child: widget.welcomeImage, fit: BoxFit.fill))), child: FittedBox(
child: widget.welcomeImage,
fit: BoxFit.fill,
),
),
),
Flexible( Flexible(
flex: 3, flex: 2,
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
@ -83,7 +132,7 @@ class _SweepingWalletWidgetState extends State<SweepingWalletWidget> {
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Theme.of(context) color: Theme.of(context)
.accentTextTheme! .accentTextTheme
.displayMedium! .displayMedium!
.color, .color,
), ),
@ -98,7 +147,7 @@ class _SweepingWalletWidgetState extends State<SweepingWalletWidget> {
fontSize: 36, fontSize: 36,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: Theme.of(context) color: Theme.of(context)
.primaryTextTheme! .primaryTextTheme
.titleLarge! .titleLarge!
.color!, .color!,
), ),
@ -113,7 +162,7 @@ class _SweepingWalletWidgetState extends State<SweepingWalletWidget> {
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
color: Theme.of(context) color: Theme.of(context)
.accentTextTheme! .accentTextTheme
.displayMedium! .displayMedium!
.color, .color,
), ),
@ -129,4 +178,12 @@ class _SweepingWalletWidgetState extends State<SweepingWalletWidget> {
} }
} }
class SweepingWalletPageData {
final WalletRestorationFromQRVM restorationFromQRVM;
final RestoredWallet restoredWallet;
SweepingWalletPageData({
required this.restorationFromQRVM,
required this.restoredWallet,
});
}

View file

@ -68,18 +68,7 @@ abstract class WalletCreationVMBase with Store {
bool typeExists(WalletType type) => walletCreationService.typeExists(type); bool typeExists(WalletType type) => walletCreationService.typeExists(type);
Future<void> create({dynamic options, RestoredWallet? restoreWallet}) async { Future<void> create({dynamic options, RestoredWallet? restoreWallet}) async {
// if (restoreWallet != null && print('Inside normal create function');
// restoreWallet.restoreMode == WalletRestoreMode.txids) {
await _createFlowForSweepAll(options, restoreWallet);
// }
// await _createTransactionFlowNormally(options, restoreWallet);
}
Future<void> _createTransactionFlowNormally(
dynamic options,
RestoredWallet? restoreWallet,
) async {
try { try {
final restoredWallet = await _createNewWalletWithoutSwitching( final restoredWallet = await _createNewWalletWithoutSwitching(
options: options, options: options,
@ -104,10 +93,11 @@ abstract class WalletCreationVMBase with Store {
} }
} }
Future<void> _createFlowForSweepAll( Future<void> createFlowForSweepAll({
dynamic options, dynamic options,
RestoredWallet? restoreWallet, RestoredWallet? restoreWallet,
) async { }) async {
print('Inside sweep all create function');
state = IsExecutingState(); state = IsExecutingState();
final type = restoreWallet?.type ?? this.type; final type = restoreWallet?.type ?? this.type;
@ -195,7 +185,7 @@ abstract class WalletCreationVMBase with Store {
name: name, name: name,
type: type, type: type,
//TODO(David): Ask Omar about this, was previous isRecovery //TODO(David): Ask Omar about this, was previous isRecovery
isRecovery: restoreWallet != null ? true : false, isRecovery: restoreWallet != null,
restoreHeight: credentials.height ?? 0, restoreHeight: credentials.height ?? 0,
date: DateTime.now(), date: DateTime.now(),
path: path, path: path,
@ -311,7 +301,8 @@ abstract class WalletCreationVMBase with Store {
Future<void> _createTransaction(WalletBase wallet, Object credentials) async { Future<void> _createTransaction(WalletBase wallet, Object credentials) async {
try { try {
print('about to enter wallet create transaction function'); print('about to enter wallet create transaction function');
pendingTransaction = await wallet.createTransaction(credentials); pendingTransaction =
await wallet.createTransactionForSweepAll(credentials);
} catch (e) { } catch (e) {
state = FailureState(e.toString()); state = FailureState(e.toString());
} }