mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-27 06:18:50 +01:00
Merge branch 'cake-tech:dev' into dev
This commit is contained in:
@@ -144,6 +144,21 @@ class BackupMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
class IncompatibleBackupAppException implements Exception {
|
||||
IncompatibleBackupAppException({
|
||||
required this.sourceAppName,
|
||||
required this.currentAppName,
|
||||
});
|
||||
|
||||
final String sourceAppName;
|
||||
final String currentAppName;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'This backup was created in $sourceAppName and cannot be restored in $currentAppName.';
|
||||
}
|
||||
}
|
||||
|
||||
class BackupServiceV3 extends $BackupService {
|
||||
BackupServiceV3(super.secureStorage, super.transactionDescriptionBox, super.keyService, super.sharedPreferences);
|
||||
|
||||
@@ -197,7 +212,8 @@ class BackupServiceV3 extends $BackupService {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> importBackupFile(File file, String password, {String nonce = secrets.backupSalt}) {
|
||||
Future<void> importBackupFile(File file, String password,
|
||||
{String nonce = secrets.backupSalt, bool checkBackupApp = true}) {
|
||||
final version = getVersionFile(file);
|
||||
switch (version) {
|
||||
case BackupVersion.unknown:
|
||||
@@ -210,11 +226,12 @@ class BackupServiceV3 extends $BackupService {
|
||||
case BackupVersion.v2:
|
||||
return super.importBackupV2(file.readAsBytesSync(), password);
|
||||
case BackupVersion.v3:
|
||||
return importBackupFileV3(file, password, nonce: nonce);
|
||||
return importBackupFileV3(file, password, nonce: nonce, checkBackupApp: checkBackupApp);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> importBackupFileV3(File file, String password, {String nonce = secrets.backupSalt}) async{
|
||||
Future<void> importBackupFileV3(File file, String password,
|
||||
{String nonce = secrets.backupSalt,bool checkBackupApp = true}) async{
|
||||
// Overall design of v3 backup is the following:
|
||||
// 1. backup.zip - plaintext zip file that user can open with any archive manager
|
||||
// 2. backup.zip/README.txt - text file to let user know what is inside of this file
|
||||
@@ -223,6 +240,11 @@ class BackupServiceV3 extends $BackupService {
|
||||
|
||||
final inputStream = InputFileStream(file.path);
|
||||
final archive = ZipDecoder().decodeStream(inputStream);
|
||||
|
||||
if (checkBackupApp) {
|
||||
await _throwIfBackupWasCreatedInAnotherApp(archive);
|
||||
}
|
||||
|
||||
final metadataFile = archive.findFile('metadata.json');
|
||||
if (metadataFile == null) {
|
||||
throw Exception('Invalid v3 backup: missing metadata.json');
|
||||
@@ -425,7 +447,7 @@ class BackupServiceV3 extends $BackupService {
|
||||
metadata.sha512sum = (await sha512.bind(dataBinUnencrypted.openRead()).first).toString();
|
||||
|
||||
final raf = await dataBinUnencrypted.open();
|
||||
|
||||
|
||||
|
||||
while (true) {
|
||||
printV("Reading chunk ${chunkIndex++}");
|
||||
@@ -491,6 +513,33 @@ This backup was created on ${DateTime.now().toIso8601String()}
|
||||
return file;
|
||||
}
|
||||
|
||||
Future<void> _throwIfBackupWasCreatedInAnotherApp(Archive archive) async {
|
||||
final readmeFile = archive.findFile('README.txt');
|
||||
if (readmeFile == null) return;
|
||||
|
||||
final readmeBytes = readmeFile.rawContent?.readBytes();
|
||||
if (readmeBytes == null) return;
|
||||
|
||||
final readmeString = utf8.decode(readmeBytes, allowMalformed: true);
|
||||
final sourceAppName = _extractBackupAppName(readmeString);
|
||||
if (sourceAppName == null) return;
|
||||
|
||||
|
||||
final currentAppName = (await PackageInfo.fromPlatform()).appName;
|
||||
if (sourceAppName == currentAppName) return;
|
||||
|
||||
|
||||
throw IncompatibleBackupAppException(
|
||||
sourceAppName: sourceAppName,
|
||||
currentAppName: currentAppName,
|
||||
);
|
||||
}
|
||||
|
||||
String? _extractBackupAppName(String readme) {
|
||||
final match = RegExp(r'^This is a (.+) backup\.', multiLine: true).firstMatch(readme);
|
||||
return match?.group(1)?.trim();
|
||||
}
|
||||
|
||||
static const chunkSize = 24 * 1024 * 1024; // 24MiB
|
||||
|
||||
File setVersionFile(File file, BackupVersion version) {
|
||||
|
||||
@@ -280,8 +280,8 @@ class NearIntentsExchangeProvider extends ExchangeProvider {
|
||||
final trade = Trade(
|
||||
id: depositAddress,
|
||||
// Using deposit address as trade ID
|
||||
from: from,
|
||||
to: to,
|
||||
from: request.fromCurrency,
|
||||
to: request.toCurrency,
|
||||
provider: description,
|
||||
providerName: title,
|
||||
state: TradeState.created,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:cake_wallet/core/utilities.dart';
|
||||
import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/modern_button.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/payjoin_copy_modal.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_address_type.dart';
|
||||
import 'package:cake_wallet/new-ui/widgets/receive_page/receive_address_widget.dart';
|
||||
@@ -22,6 +23,7 @@ import 'package:cw_core/crypto_currency.dart';
|
||||
import 'package:cw_core/payment_uris.dart';
|
||||
import 'package:cw_core/receive_page_option.dart';
|
||||
import 'package:cw_core/utils/print_verbose.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_mobx/flutter_mobx.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
@@ -194,29 +196,37 @@ class _NewReceivePageState extends State<NewReceivePage> {
|
||||
ModalTopBar(
|
||||
title: _largeQrMode ? "" : S.of(context).receive,
|
||||
leadingIcon: Icon(Icons.close),
|
||||
trailingIcon: _largeQrMode
|
||||
? Icon(Icons.share)
|
||||
: widget.addressListViewModel.hasAddressRotation
|
||||
/* TODO rotating is broken on mweb, disabling for now, fix after mvp*/
|
||||
&&
|
||||
!(widget.receiveOptionViewModel.selectedReceiveOption.description ?? "")
|
||||
.toLowerCase()
|
||||
.contains("mweb")
|
||||
? Icon(Icons.refresh)
|
||||
: null,
|
||||
onLeadingPressed: () {
|
||||
Navigator.of(context, rootNavigator: true).pop();
|
||||
},
|
||||
onTrailingPressed: () {
|
||||
if (_largeQrMode) {
|
||||
ShareUtil.share(
|
||||
text: widget.addressListViewModel.uri.toString(),
|
||||
context: context,
|
||||
);
|
||||
} else if (widget.addressListViewModel.hasAddressRotation) {
|
||||
widget.addressListViewModel.rotateAddress();
|
||||
}
|
||||
},
|
||||
trailingWidget: Observer(
|
||||
builder: (_) => AnimatedSwitcher(
|
||||
duration: Duration(milliseconds: 300),
|
||||
child: _largeQrMode || widget.addressListViewModel.hasAddressRotation
|
||||
/* TODO rotating is broken on mweb, disabling for now, fix after mvp*/
|
||||
&&
|
||||
!(widget.receiveOptionViewModel.selectedReceiveOption.description ?? "")
|
||||
.toLowerCase()
|
||||
.contains("mweb") ? ModernButton(
|
||||
key: ValueKey(_largeQrMode),
|
||||
size: 36,
|
||||
icon: _largeQrMode ? Icon(Icons.share) : widget.addressListViewModel.isRotatingAddress
|
||||
? CupertinoActivityIndicator()
|
||||
: Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
if(_largeQrMode) {
|
||||
ShareUtil.share(
|
||||
text: widget.addressListViewModel.uri.toString(),
|
||||
context: context,
|
||||
);
|
||||
} else {
|
||||
if(widget.addressListViewModel.hasAddressRotation) {
|
||||
widget.addressListViewModel.rotateAddress();
|
||||
}
|
||||
}
|
||||
}):SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
|
||||
@@ -101,9 +101,6 @@ class HistorySection extends StatelessWidget {
|
||||
final trade = item.trade;
|
||||
final tradeFrom = trade.from;
|
||||
final tradeTo = trade.to;
|
||||
if (tradeFrom == null || tradeTo == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => Navigator.of(context)
|
||||
|
||||
@@ -15,13 +15,13 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
required this.roundedTop,
|
||||
required this.roundedBottom,
|
||||
required this.bottomSeparator,
|
||||
required this.from,
|
||||
required this.to,
|
||||
this.from,
|
||||
this.to,
|
||||
required this.swapState,
|
||||
required this.provider});
|
||||
|
||||
final CryptoCurrency from;
|
||||
final CryptoCurrency to;
|
||||
final CryptoCurrency? from;
|
||||
final CryptoCurrency? to;
|
||||
final ExchangeProviderDescription provider;
|
||||
final String date;
|
||||
final String amount;
|
||||
@@ -32,6 +32,11 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
final TradeState swapState;
|
||||
|
||||
Widget _getLeadingStack(BuildContext context) {
|
||||
if (from == null || to == null) {
|
||||
return SizedBox(width: 50, height: 50);
|
||||
}
|
||||
|
||||
|
||||
double currencyIconSize = 22.0;
|
||||
|
||||
return SizedBox(
|
||||
@@ -40,7 +45,7 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
child: Stack(
|
||||
children: [
|
||||
CakeImageWidget(
|
||||
imageUrl: _getIconPath(from), width: currencyIconSize, height: currencyIconSize),
|
||||
imageUrl: _getIconPath(from!), width: currencyIconSize, height: currencyIconSize),
|
||||
Positioned(
|
||||
top: currencyIconSize / 2,
|
||||
left: currencyIconSize / 2,
|
||||
@@ -50,7 +55,7 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
Border.all(width: 2, color: Theme.of(context).colorScheme.surfaceContainer),
|
||||
shape: BoxShape.circle),
|
||||
child: CakeImageWidget(
|
||||
imageUrl: _getIconPath(to),
|
||||
imageUrl: _getIconPath(to!),
|
||||
width: currencyIconSize,
|
||||
height: currencyIconSize,
|
||||
),
|
||||
@@ -85,8 +90,8 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
fontWeight: FontWeight.w500)),
|
||||
Text(from.title, style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant)),
|
||||
if (fromChainIcon.isNotEmpty)
|
||||
if(from?.title != null) Text(from!.title, style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant)),
|
||||
if (fromChainIcon?.isNotEmpty ?? false)
|
||||
CakeImageWidget(
|
||||
imageUrl: fromChainIcon,
|
||||
width: 12,
|
||||
@@ -106,10 +111,11 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
receiveAmount.withMaxDecimals(8),
|
||||
style: TextStyle(fontWeight: FontWeight.w500),
|
||||
),
|
||||
if(to!=null)
|
||||
Text(
|
||||
to.title,
|
||||
to!.title,
|
||||
),
|
||||
if (toChainIcon.isNotEmpty) CakeImageWidget(imageUrl: toChainIcon, width: 12, height: 12)
|
||||
if (toChainIcon?.isNotEmpty ?? false) CakeImageWidget(imageUrl: toChainIcon, width: 12, height: 12)
|
||||
],
|
||||
),
|
||||
leadingIcon: _getLeadingStack(context),
|
||||
@@ -137,7 +143,8 @@ class HistoryTradeTile extends StatelessWidget {
|
||||
return "";
|
||||
}
|
||||
|
||||
String _getChainIcon(CryptoCurrency currency) {
|
||||
String? _getChainIcon(CryptoCurrency? currency) {
|
||||
if(currency == null) return null;
|
||||
try {
|
||||
if (currency.title.isNotEmpty) {
|
||||
final parsedCurrency = CryptoCurrency.safeParseCurrencyFromString(currency.title, tag: currency.tag);
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:cake_wallet/new-ui/pages/send_page.dart';
|
||||
import 'package:cake_wallet/routes.dart';
|
||||
import 'package:cake_wallet/utils/feature_flag.dart';
|
||||
import 'package:cake_wallet/utils/payment_request.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
|
||||
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
|
||||
import 'package:cw_core/card_design.dart';
|
||||
@@ -72,8 +73,6 @@ class _CardsViewState extends State<CardsView> {
|
||||
|
||||
final top = baseTop - (howFarBehind * overlapAmount);
|
||||
|
||||
final double cardWidth = MediaQuery.of(context).size.width * 0.878;
|
||||
final double effectiveCardWidth = min(cardWidth, 768);
|
||||
|
||||
final left = (parentWidth - effectiveCardWidth) / 2.0;
|
||||
|
||||
@@ -228,9 +227,9 @@ class _CardsViewState extends State<CardsView> {
|
||||
}
|
||||
}
|
||||
|
||||
double get effectiveCardWidth => min(MediaQuery.of(context).size.width * 0.878, responsiveLayoutUtil.shouldRenderMobileUI ? 768 : 512);
|
||||
|
||||
double _getBoxHeight(int numCards, double overlapAmount) {
|
||||
final double cardWidth = MediaQuery.of(context).size.width * 0.878;
|
||||
final double effectiveCardWidth = min(cardWidth, 768);
|
||||
return
|
||||
/* height of initial card */
|
||||
(2 / 3.2) * (effectiveCardWidth) +
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cake_wallet/di.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
|
||||
@@ -29,7 +31,7 @@ class ReceiveQrCode extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double targetY = largeQrMode ? largeQrModeBottomPadding+50 : 0;
|
||||
final double resolvedSize = MediaQuery.of(context).size.width * 0.5;
|
||||
final double resolvedSize = min(MediaQuery.of(context).size.width, MediaQuery.of(context).size.height) * 0.5;
|
||||
final double resolvedScale = largeQrMode ? 1.7 : 1;
|
||||
|
||||
return Stack(
|
||||
|
||||
@@ -154,8 +154,12 @@ class BackupPage extends BasePage {
|
||||
leftButtonText: S.of(context).share,
|
||||
actionRightButton: () async {
|
||||
await backupViewModelBase.saveToDownload(backup.name, backup.file);
|
||||
Navigator.of(dialogContext).pop();
|
||||
await showBar<void>(context, S.of(context).file_saved);
|
||||
if (dialogContext.mounted && Navigator.canPop(dialogContext)) {
|
||||
Navigator.of(dialogContext).pop();
|
||||
}
|
||||
if (context.mounted && Navigator.canPop(context)) {
|
||||
await showBar<void>(context, S.of(context).file_saved);
|
||||
}
|
||||
},
|
||||
actionLeftButton: () async {
|
||||
Navigator.of(dialogContext).pop();
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:cw_core/root_dir.dart';
|
||||
import 'package:cw_core/wallet_type.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
class DevMoneroWalletCacheDebugPage extends StatelessWidget {
|
||||
@@ -263,6 +264,7 @@ class _JsonExplorerState extends State<JsonExplorer> {
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
controller: ModalScrollController.of(context),
|
||||
itemCount: _filteredItems.length,
|
||||
itemBuilder: (context, index) {
|
||||
final item = _filteredItems[index];
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:cake_wallet/core/backup_service_v3.dart';
|
||||
import 'package:cake_wallet/core/execution_state.dart';
|
||||
import 'package:cake_wallet/generated/i18n.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
|
||||
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
|
||||
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
|
||||
import 'package:cake_wallet/utils/responsive_layout_util.dart';
|
||||
import 'package:cake_wallet/utils/show_pop_up.dart';
|
||||
@@ -18,6 +20,7 @@ class RestoreFromBackupPage extends BasePage {
|
||||
|
||||
final RestoreFromBackupViewModel restoreFromBackupViewModel;
|
||||
final TextEditingController textEditingController;
|
||||
bool _isStateReactionSet = false;
|
||||
|
||||
@override
|
||||
String get title => S.current.restore_title_from_backup;
|
||||
@@ -32,22 +35,26 @@ class RestoreFromBackupPage extends BasePage {
|
||||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
reaction((_) => restoreFromBackupViewModel.state, (ExecutionState state) {
|
||||
if (state is FailureState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
if (!_isStateReactionSet) {
|
||||
_isStateReactionSet = true;
|
||||
|
||||
reaction((_) => restoreFromBackupViewModel.state, (ExecutionState state) {
|
||||
if (state is FailureState) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
showPopUp<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertWithOneAction(
|
||||
alertTitle: S.of(context).error,
|
||||
alertContent: state.error,
|
||||
buttonText: S.of(context).ok,
|
||||
buttonAction: () => Navigator.of(context).pop(),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Center(
|
||||
child: ConstrainedBox(
|
||||
@@ -175,6 +182,29 @@ class RestoreFromBackupPage extends BasePage {
|
||||
}
|
||||
try {
|
||||
await restoreFromBackupViewModel.import(textEditingController.text);
|
||||
} on IncompatibleBackupAppException catch (e) {
|
||||
final isConfirmed = await showPopUp<bool>(
|
||||
context: context,
|
||||
builder: (_) {
|
||||
return AlertWithTwoActions(
|
||||
alertTitle: S.current.warning,
|
||||
alertContent:
|
||||
'This backup was created in ${e.sourceAppName}, but you are restoring it in ${e.currentAppName}. '
|
||||
'Please make sure this is the correct backup file before continuing.',
|
||||
leftButtonText: S.of(context).cancel,
|
||||
rightButtonText: S.of(context).ok,
|
||||
actionLeftButton: () => Navigator.of(context).pop(false),
|
||||
actionRightButton: () => Navigator.of(context).pop(true),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (isConfirmed ?? false) {
|
||||
await restoreFromBackupViewModel.import(
|
||||
textEditingController.text,
|
||||
checkBackupApp: false,
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
await showPopUp<void>(
|
||||
context: context,
|
||||
|
||||
@@ -1242,6 +1242,12 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
|
||||
trade.walletId = wallet.id;
|
||||
trade.chainId = wallet.chainId;
|
||||
trade.fromWalletAddress = wallet.walletAddresses.address;
|
||||
if(trade.from == null) {
|
||||
trade.from = depositCurrency;
|
||||
}
|
||||
if(trade.to == null) {
|
||||
trade.to = receiveCurrency;
|
||||
}
|
||||
|
||||
final canCreateTrade = await isCanCreateTrade(trade);
|
||||
if (!canCreateTrade.result) {
|
||||
|
||||
@@ -161,7 +161,7 @@ abstract class LedgerViewModelBase extends HardwareWalletViewModel with Store {
|
||||
allowChangeWallet: true,
|
||||
isReconnect: true,
|
||||
onConnectDevice: (context, ledgerVM) async {
|
||||
if (context.mounted) {
|
||||
if (context.mounted && Navigator.of(context).canPop()) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
|
||||
@@ -32,7 +32,7 @@ abstract class RestoreFromBackupViewModelBase with Store {
|
||||
void reset() => filePath = '';
|
||||
|
||||
@action
|
||||
Future<void> import(String password) async {
|
||||
Future<void> import(String password,{bool checkBackupApp = true}) async {
|
||||
try {
|
||||
state = IsExecutingState();
|
||||
|
||||
@@ -44,7 +44,9 @@ abstract class RestoreFromBackupViewModelBase with Store {
|
||||
final file = File(filePath);
|
||||
|
||||
try {
|
||||
await backupService.importBackupFile(file, password);
|
||||
await backupService.importBackupFile(file, password, checkBackupApp: checkBackupApp);
|
||||
} on IncompatibleBackupAppException {
|
||||
rethrow;
|
||||
} catch (e, s) {
|
||||
if (e.toString().contains("unknown_backup_version")) {
|
||||
state = FailureState('This is not a valid backup file, please make sure you have selected the correct one');
|
||||
@@ -74,6 +76,9 @@ abstract class RestoreFromBackupViewModelBase with Store {
|
||||
});
|
||||
|
||||
state = ExecutedSuccessfullyState();
|
||||
} on IncompatibleBackupAppException {
|
||||
state = InitialExecutionState();
|
||||
rethrow;
|
||||
} catch (e, s) {
|
||||
var msg = e.toString().toLowerCase();
|
||||
|
||||
|
||||
@@ -614,11 +614,20 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
||||
void setAddress(WalletAddressListItem address) =>
|
||||
wallet.walletAddresses.address = address.address;
|
||||
|
||||
@observable
|
||||
bool isRotatingAddress = false;
|
||||
|
||||
@action
|
||||
Future<void> rotateAddress() async {
|
||||
await createNewAddress(wallet, "");
|
||||
if (isElectrumWallet) {
|
||||
if(isRotatingAddress) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
isRotatingAddress = true;
|
||||
await createNewAddress(wallet, "");
|
||||
wallet.walletAddresses.address = addressList.whereType<WalletAddressListItem>().last.address;
|
||||
} finally {
|
||||
isRotatingAddress = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user