Merge branch 'staging' into rylee-tmp

This commit is contained in:
ryleedavis 2022-09-12 13:35:07 -06:00
commit 7eed8337be
57 changed files with 2859 additions and 1261 deletions

@ -1 +1 @@
Subproject commit 588281a5280a62fff624e6a96cbe5dc2e2154245
Subproject commit 9a150d8cd2c3625424b0059e6b7306f3659fdbe0

@ -1 +1 @@
Subproject commit 6242046217abf47b61d9397ae447632b06f853fa
Subproject commit b1e0b20621be3ebb280ab3e3de10afe0c11db073

@ -1 +1 @@
Subproject commit cd6f9cf62afcb6c1e55b16a76374a8577d85352f
Subproject commit 4bffa40cb60ad3d98cf0ea5b5d819f3f4895dcd6

View file

@ -589,9 +589,16 @@ class _MaterialAppWithThemeState extends ConsumerState<MaterialAppWithTheme>
_loadChangeNowData();
}
return const LockscreenView(
String? startupWalletId;
if (ref.read(prefsChangeNotifierProvider).gotoWalletOnStartup) {
startupWalletId =
ref.read(prefsChangeNotifierProvider).startupWalletId;
}
return LockscreenView(
isInitialAppLogin: true,
routeOnSuccess: HomeView.routeName,
routeOnSuccessArguments: startupWalletId,
biometricsAuthenticationTitle: "Unlock Stack",
biometricsLocalizedReason:
"Unlock your stack wallet using biometrics",

View file

@ -1,20 +1,18 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/exchange/available_currencies_state_provider.dart';
import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_state_provider.dart';
import 'package:stackwallet/providers/exchange/change_now_provider.dart';
import 'package:stackwallet/providers/exchange/changenow_initial_load_status.dart';
import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
class ExchangeLoadingOverlayView extends ConsumerStatefulWidget {
const ExchangeLoadingOverlayView({Key? key}) : super(key: key);
const ExchangeLoadingOverlayView({
Key? key,
required this.unawaitedLoad,
}) : super(key: key);
final VoidCallback unawaitedLoad;
@override
ConsumerState<ExchangeLoadingOverlayView> createState() =>
@ -28,103 +26,6 @@ class _ExchangeLoadingOverlayViewState
bool userReloaded = false;
Future<void> _loadFixedRateMarkets() async {
if (ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state ==
ChangeNowLoadStatus.loading) {
// already in progress so just
return;
}
ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.loading;
final response3 =
await ref.read(changeNowProvider).getAvailableFixedRateMarkets();
if (response3.value != null) {
ref.read(fixedRateMarketPairsStateProvider.state).state =
response3.value!;
if (ref.read(fixedRateExchangeFormProvider).market == null) {
final matchingMarkets =
response3.value!.where((e) => e.to == "doge" && e.from == "btc");
if (matchingMarkets.isNotEmpty) {
await ref
.read(fixedRateExchangeFormProvider)
.updateMarket(matchingMarkets.first, true);
}
}
} else {
Logging.instance.log(
"Failed to load changeNOW fixed rate markets: ${response3.exception?.errorMessage}",
level: LogLevel.Error);
ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.failed;
return;
}
ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.success;
}
Future<void> _loadChangeNowStandardCurrencies() async {
if (ref
.read(changeNowEstimatedInitialLoadStatusStateProvider.state)
.state ==
ChangeNowLoadStatus.loading) {
// already in progress so just
return;
}
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.loading;
final response = await ref.read(changeNowProvider).getAvailableCurrencies();
final response2 =
await ref.read(changeNowProvider).getAvailableFloatingRatePairs();
if (response.value != null) {
ref.read(availableChangeNowCurrenciesStateProvider.state).state =
response.value!;
if (response2.value != null) {
ref.read(availableFloatingRatePairsStateProvider.state).state =
response2.value!;
if (response.value!.length > 1) {
if (ref.read(estimatedRateExchangeFormProvider).from == null) {
if (response.value!.where((e) => e.ticker == "btc").isNotEmpty) {
await ref.read(estimatedRateExchangeFormProvider).updateFrom(
response.value!.firstWhere((e) => e.ticker == "btc"), false);
}
}
if (ref.read(estimatedRateExchangeFormProvider).to == null) {
if (response.value!.where((e) => e.ticker == "doge").isNotEmpty) {
await ref.read(estimatedRateExchangeFormProvider).updateTo(
response.value!.firstWhere((e) => e.ticker == "doge"), false);
}
}
}
} else {
Logging.instance.log(
"Failed to load changeNOW available floating rate pairs: ${response2.exception?.errorMessage}",
level: LogLevel.Error);
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.failed;
return;
}
} else {
Logging.instance.log(
"Failed to load changeNOW currencies: ${response.exception?.errorMessage}",
level: LogLevel.Error);
await Future<void>.delayed(const Duration(seconds: 3));
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.failed;
return;
}
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.success;
}
@override
void initState() {
_statusEst =
@ -168,8 +69,10 @@ class _ExchangeLoadingOverlayViewState
child: const CustomLoadingOverlay(
message: "Loading ChangeNOW data", eventBus: null),
),
if (_statusEst == ChangeNowLoadStatus.failed ||
_statusFixed == ChangeNowLoadStatus.failed)
if ((_statusEst == ChangeNowLoadStatus.failed ||
_statusFixed == ChangeNowLoadStatus.failed) &&
_statusEst != ChangeNowLoadStatus.loading &&
_statusFixed != ChangeNowLoadStatus.loading)
Container(
color: CFColors.stackAccent.withOpacity(0.7),
child: Column(
@ -186,13 +89,8 @@ class _ExchangeLoadingOverlayViewState
.copyWith(color: CFColors.stackAccent),
),
onPressed: () {
if (_statusEst == ChangeNowLoadStatus.failed) {
_loadChangeNowStandardCurrencies();
}
if (_statusFixed == ChangeNowLoadStatus.failed) {
userReloaded = true;
_loadFixedRateMarkets();
}
userReloaded = true;
widget.unawaitedLoad();
},
),
),

View file

@ -29,7 +29,6 @@ import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_loading_overlay.dart';
@ -1272,55 +1271,55 @@ class _WalletInitiatedExchangeViewState
.read(estimatedRateExchangeFormProvider)
.fromAmountString);
if (ft.toLowerCase() ==
coin.ticker.toLowerCase()) {
bool shouldPop = false;
bool wasPopped = false;
unawaited(showDialog<void>(
context: context,
builder: (_) => WillPopScope(
onWillPop: () async {
if (shouldPop) {
wasPopped = true;
}
return shouldPop;
},
child: const CustomLoadingOverlay(
message: "Checking available balance",
eventBus: null,
),
),
));
final availableBalance =
await manager.availableBalance;
final feeObject = await manager.fees;
final fee = await manager.estimateFeeFor(
Format.decimalAmountToSatoshis(
sendAmount),
feeObject.medium);
shouldPop = true;
if (!wasPopped && mounted) {
Navigator.of(context).pop();
}
if (availableBalance <
sendAmount +
Format.satoshisToAmount(fee)) {
unawaited(showDialog<void>(
context: context,
builder: (_) => StackOkDialog(
title: "Insufficient balance",
message:
"Current ${coin.prettyName} wallet does not have enough ${coin.ticker} for this trade",
),
));
return;
}
}
// if (ft.toLowerCase() ==
// coin.ticker.toLowerCase()) {
// bool shouldPop = false;
// bool wasPopped = false;
// unawaited(showDialog<void>(
// context: context,
// builder: (_) => WillPopScope(
// onWillPop: () async {
// if (shouldPop) {
// wasPopped = true;
// }
// return shouldPop;
// },
// child: const CustomLoadingOverlay(
// message: "Checking available balance",
// eventBus: null,
// ),
// ),
// ));
//
// final availableBalance =
// await manager.availableBalance;
//
// final feeObject = await manager.fees;
//
// final fee = await manager.estimateFeeFor(
// Format.decimalAmountToSatoshis(
// sendAmount),
// feeObject.medium);
//
// shouldPop = true;
// if (!wasPopped && mounted) {
// Navigator.of(context).pop();
// }
//
// if (availableBalance <
// sendAmount +
// Format.satoshisToAmount(fee)) {
// unawaited(showDialog<void>(
// context: context,
// builder: (_) => StackOkDialog(
// title: "Insufficient balance",
// message:
// "Current ${coin.prettyName} wallet does not have enough ${coin.ticker} for this trade",
// ),
// ));
// return;
// }
// }
if (isEstimated) {
final fromTicker = ref

View file

@ -13,6 +13,7 @@ import 'package:stackwallet/pages/wallets_view/wallets_view.dart';
import 'package:stackwallet/providers/global/notifications_provider.dart';
import 'package:stackwallet/providers/ui/home_view_index_provider.dart';
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
import 'package:stackwallet/services/change_now/change_now_loading_service.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart';
@ -40,7 +41,16 @@ class _HomeViewState extends ConsumerState<HomeView> {
bool _exitEnabled = false;
final _cnLoadingService = ChangeNowLoadingService();
Future<bool> _onWillPop() async {
// go to home view when tapping back on the main exchange view
if (ref.read(homeViewPageIndexStateProvider.state).state == 1) {
ref.read(homeViewPageIndexStateProvider.state).state = 0;
return false;
}
if (_exitEnabled) {
return true;
}
@ -70,6 +80,11 @@ class _HomeViewState extends ConsumerState<HomeView> {
return _exitEnabled;
}
void _loadCNData() {
// unawaited future
_cnLoadingService.loadAll(ref);
}
@override
void initState() {
_pageController = PageController();
@ -77,9 +92,11 @@ class _HomeViewState extends ConsumerState<HomeView> {
const WalletsView(),
if (Constants.enableExchange)
Stack(
children: const [
ExchangeView(),
ExchangeLoadingOverlayView(),
children: [
const ExchangeView(),
ExchangeLoadingOverlayView(
unawaitedLoad: _loadCNData,
),
],
),
// const BuyView(),

View file

@ -1,9 +1,14 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:stackwallet/notifications/show_flush_bar.dart';
import 'package:stackwallet/pages/home_view/home_view.dart';
import 'package:stackwallet/pages/wallet_view/wallet_view.dart';
// import 'package:stackwallet/providers/global/has_authenticated_start_state_provider.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/providers/global/wallets_provider.dart';
// import 'package:stackwallet/providers/global/should_show_lockscreen_on_resume_state_provider.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/biometrics.dart';
@ -15,6 +20,7 @@ import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_pin_put/custom_pin_put.dart';
import 'package:stackwallet/widgets/shake/shake.dart';
import 'package:tuple/tuple.dart';
class LockscreenView extends ConsumerStatefulWidget {
const LockscreenView({
@ -75,10 +81,20 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
if (widget.popOnSuccess) {
Navigator.of(context).pop(widget.routeOnSuccessArguments);
} else {
Navigator.of(context).pushReplacementNamed(
unawaited(Navigator.of(context).pushReplacementNamed(
widget.routeOnSuccess,
arguments: widget.routeOnSuccessArguments,
);
));
if (widget.routeOnSuccess == HomeView.routeName &&
widget.routeOnSuccessArguments is String) {
final walletId = widget.routeOnSuccessArguments as String;
unawaited(Navigator.of(context).pushNamed(WalletView.routeName,
arguments: Tuple2(
walletId,
ref
.read(walletsChangeNotifierProvider)
.getManagerProvider(walletId))));
}
}
}
@ -105,7 +121,7 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
// await walletsService.getWalletId(currentWalletName));
// }
_onUnlock();
unawaited(_onUnlock());
}
// leave this commented to enable pin fall back should biometrics not work properly
// else {
@ -250,10 +266,10 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
_timeout = const Duration(minutes: 60);
}
Future<void>.delayed(_timeout).then((_) {
unawaited(Future<void>.delayed(_timeout).then((_) {
_attemptLock = false;
_attempts = 0;
});
}));
}
if (_attemptLock) {
@ -264,13 +280,13 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
prettyTime += "${_timeout.inSeconds} seconds";
}
showFloatingFlushBar(
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message:
"Incorrect PIN entered too many times. Please wait $prettyTime",
context: context,
iconAsset: Assets.svg.alertCircle,
);
));
await Future<void>.delayed(
const Duration(milliseconds: 100));
@ -286,15 +302,15 @@ class _LockscreenViewState extends ConsumerState<LockscreenView> {
if (storedPin == pin) {
await Future<void>.delayed(
const Duration(milliseconds: 200));
_onUnlock();
unawaited(_onUnlock());
} else {
_shakeController.shake();
showFloatingFlushBar(
unawaited(_shakeController.shake());
unawaited(showFloatingFlushBar(
type: FlushBarType.warning,
message: "Incorrect PIN. Please try again",
context: context,
iconAsset: Assets.svg.alertCircle,
);
));
await Future<void>.delayed(
const Duration(milliseconds: 100));

View file

@ -1,3 +1,6 @@
import 'dart:convert';
import 'package:http/http.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -8,6 +11,85 @@ import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:flutter_libepiccash/git_versions.dart' as EPIC_VERSIONS;
import 'package:flutter_libmonero/git_versions.dart' as MONERO_VERSIONS;
import 'package:lelantus/git_versions.dart' as FIRO_VERSIONS;
import 'package:stackwallet/utilities/logger.dart';
const kGithubAPI = "https://api.github.com";
const kGithubSearch = "/search/commits";
const kGithubHead = "/repos";
enum CommitStatus { isHead, isOldCommit, notACommit, notLoaded }
Future<bool> doesCommitExist(
String organization,
String project,
String commit,
) async {
Logging.instance.log("doesCommitExist", level: LogLevel.Info);
final Client client = Client();
try {
final uri = Uri.parse(
"$kGithubAPI$kGithubHead/$organization/$project/commits/$commit");
final commitQuery = await client.get(
uri,
headers: {'Content-Type': 'application/json'},
);
final response = jsonDecode(commitQuery.body.toString());
Logging.instance.log("doesCommitExist $project $commit $response",
level: LogLevel.Info);
bool isThereCommit;
try {
isThereCommit = response['sha'] == commit;
Logging.instance
.log("isThereCommit $isThereCommit", level: LogLevel.Info);
return isThereCommit;
} catch (e, s) {
return false;
}
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Error);
return false;
}
}
Future<bool> isHeadCommit(
String organization,
String project,
String branch,
String commit,
) async {
Logging.instance.log("doesCommitExist", level: LogLevel.Info);
final Client client = Client();
try {
final uri = Uri.parse(
"$kGithubAPI$kGithubHead/$organization/$project/commits/$branch");
final commitQuery = await client.get(
uri,
headers: {'Content-Type': 'application/json'},
);
final response = jsonDecode(commitQuery.body.toString());
Logging.instance.log("isHeadCommit $project $commit $branch $response",
level: LogLevel.Info);
bool isHead;
try {
isHead = response['sha'] == commit;
Logging.instance.log("isHead $isHead", level: LogLevel.Info);
return isHead;
} catch (e, s) {
return false;
}
} catch (e, s) {
Logging.instance.log("$e $s", level: LogLevel.Error);
return false;
}
}
class AboutView extends ConsumerWidget {
const AboutView({Key? key}) : super(key: key);
@ -16,6 +98,26 @@ class AboutView extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
String firoCommit = FIRO_VERSIONS.getPluginVersion();
String epicCashCommit = EPIC_VERSIONS.getPluginVersion();
String moneroCommit = MONERO_VERSIONS.getPluginVersion();
List<Future> futureFiroList = [
doesCommitExist("cypherstack", "flutter_liblelantus", firoCommit),
isHeadCommit("cypherstack", "flutter_liblelantus", "main", firoCommit),
];
Future commitFiroFuture = Future.wait(futureFiroList);
List<Future> futureEpicList = [
doesCommitExist("cypherstack", "flutter_libepiccash", epicCashCommit),
isHeadCommit(
"cypherstack", "flutter_libepiccash", "main", epicCashCommit),
];
Future commitEpicFuture = Future.wait(futureEpicList);
List<Future> futureMoneroList = [
doesCommitExist("cypherstack", "flutter_libmonero", moneroCommit),
isHeadCommit("cypherstack", "flutter_libmonero", "main", moneroCommit),
];
Future commitMoneroFuture = Future.wait(futureMoneroList);
return Scaffold(
backgroundColor: CFColors.almostWhite,
appBar: AppBar(
@ -142,6 +244,186 @@ class AboutView extends ConsumerWidget {
const SizedBox(
height: 12,
),
FutureBuilder(
future: commitFiroFuture,
builder: (context, AsyncSnapshot<dynamic> snapshot) {
bool commitExists = false;
bool isHead = false;
CommitStatus stateOfCommit = CommitStatus.notLoaded;
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
commitExists = snapshot.data![0] as bool;
isHead = snapshot.data![1] as bool;
if (commitExists && isHead) {
stateOfCommit = CommitStatus.isHead;
} else if (commitExists) {
stateOfCommit = CommitStatus.isOldCommit;
} else {
stateOfCommit = CommitStatus.notACommit;
}
}
TextStyle indicationStyle =
STextStyles.itemSubtitle;
switch (stateOfCommit) {
case CommitStatus.isHead:
indicationStyle = STextStyles.itemSubtitle
.copyWith(color: CFColors.stackGreen);
break;
case CommitStatus.isOldCommit:
indicationStyle = STextStyles.itemSubtitle
.copyWith(color: CFColors.stackYellow);
break;
case CommitStatus.notACommit:
indicationStyle = STextStyles.itemSubtitle
.copyWith(color: CFColors.stackRed);
break;
default:
break;
}
return RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Firo Build Commit",
style: STextStyles.titleBold12,
),
const SizedBox(
height: 4,
),
SelectableText(
firoCommit,
style: indicationStyle,
),
],
),
);
}),
const SizedBox(
height: 12,
),
FutureBuilder(
future: commitEpicFuture,
builder: (context, AsyncSnapshot<dynamic> snapshot) {
bool commitExists = false;
bool isHead = false;
CommitStatus stateOfCommit = CommitStatus.notLoaded;
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
commitExists = snapshot.data![0] as bool;
isHead = snapshot.data![1] as bool;
if (commitExists && isHead) {
stateOfCommit = CommitStatus.isHead;
} else if (commitExists) {
stateOfCommit = CommitStatus.isOldCommit;
} else {
stateOfCommit = CommitStatus.notACommit;
}
}
TextStyle indicationStyle =
STextStyles.itemSubtitle;
switch (stateOfCommit) {
case CommitStatus.isHead:
indicationStyle = STextStyles.itemSubtitle
.copyWith(color: CFColors.stackGreen);
break;
case CommitStatus.isOldCommit:
indicationStyle = STextStyles.itemSubtitle
.copyWith(color: CFColors.stackYellow);
break;
case CommitStatus.notACommit:
indicationStyle = STextStyles.itemSubtitle
.copyWith(color: CFColors.stackRed);
break;
default:
break;
}
return RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Epic Cash Build Commit",
style: STextStyles.titleBold12,
),
const SizedBox(
height: 4,
),
SelectableText(
epicCashCommit,
style: indicationStyle,
),
],
),
);
}),
const SizedBox(
height: 12,
),
FutureBuilder(
future: commitMoneroFuture,
builder: (context, AsyncSnapshot<dynamic> snapshot) {
bool commitExists = false;
bool isHead = false;
CommitStatus stateOfCommit = CommitStatus.notLoaded;
if (snapshot.connectionState ==
ConnectionState.done &&
snapshot.hasData) {
commitExists = snapshot.data![0] as bool;
isHead = snapshot.data![1] as bool;
if (commitExists && isHead) {
stateOfCommit = CommitStatus.isHead;
} else if (commitExists) {
stateOfCommit = CommitStatus.isOldCommit;
} else {
stateOfCommit = CommitStatus.notACommit;
}
}
TextStyle indicationStyle =
STextStyles.itemSubtitle;
switch (stateOfCommit) {
case CommitStatus.isHead:
indicationStyle = STextStyles.itemSubtitle
.copyWith(color: CFColors.stackGreen);
break;
case CommitStatus.isOldCommit:
indicationStyle = STextStyles.itemSubtitle
.copyWith(color: CFColors.stackYellow);
break;
case CommitStatus.notACommit:
indicationStyle = STextStyles.itemSubtitle
.copyWith(color: CFColors.stackRed);
break;
default:
break;
}
return RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"Monero Build Commit",
style: STextStyles.titleBold12,
),
const SizedBox(
height: 4,
),
SelectableText(
moneroCommit,
style: indicationStyle,
),
],
),
);
}),
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,

View file

@ -9,6 +9,7 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/language_v
import 'package:stackwallet/pages/settings_views/global_settings_view/manage_nodes_views/manage_nodes_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/security_views/security_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/stack_backup_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/support_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart';
import 'package:stackwallet/pages/settings_views/sub_widgets/settings_list_button.dart';
@ -166,6 +167,18 @@ class GlobalSettingsView extends StatelessWidget {
const SizedBox(
height: 8,
),
SettingsListButton(
iconAssetName: Assets.svg.arrowUpRight,
iconSize: 16,
title: "Startup",
onPressed: () {
Navigator.of(context).pushNamed(
StartupPreferencesView.routeName);
},
),
const SizedBox(
height: 8,
),
SettingsListButton(
iconAssetName: Assets.svg.sun,
iconSize: 18,

View file

@ -0,0 +1,269 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart';
import 'package:stackwallet/providers/global/prefs_provider.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class StartupPreferencesView extends ConsumerStatefulWidget {
const StartupPreferencesView({Key? key}) : super(key: key);
static const String routeName = "/startupPreferences";
@override
ConsumerState<StartupPreferencesView> createState() =>
_StartupPreferencesViewState();
}
class _StartupPreferencesViewState
extends ConsumerState<StartupPreferencesView> {
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: CFColors.almostWhite,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
Navigator.of(context).pop();
},
),
title: Text(
"Startup preferences",
style: STextStyles.navBarTitle,
),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight,
),
child: IntrinsicHeight(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.all(4.0),
child: RawMaterialButton(
// splashColor: CFColors.splashLight,
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () {
ref
.read(prefsChangeNotifierProvider)
.gotoWalletOnStartup = false;
},
child: Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: CFColors.link2,
value: false,
groupValue: ref.watch(
prefsChangeNotifierProvider
.select((value) => value
.gotoWalletOnStartup),
),
onChanged: (value) {
if (value is bool) {
ref
.read(
prefsChangeNotifierProvider)
.gotoWalletOnStartup = value;
}
},
),
),
const SizedBox(
width: 12,
),
Flexible(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"Home screen",
style: STextStyles.titleBold12,
textAlign: TextAlign.left,
),
Text(
"Stack Wallet home screen",
style: STextStyles.itemSubtitle,
textAlign: TextAlign.left,
),
],
),
),
],
),
),
),
),
),
Padding(
padding: const EdgeInsets.all(4),
child: RawMaterialButton(
// splashColor: CFColors.splashLight,
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
onPressed: () {
ref
.read(prefsChangeNotifierProvider)
.gotoWalletOnStartup = true;
},
child: Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.all(8),
child: Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
SizedBox(
width: 20,
height: 20,
child: Radio(
activeColor: CFColors.link2,
value: true,
groupValue: ref.watch(
prefsChangeNotifierProvider
.select((value) => value
.gotoWalletOnStartup),
),
onChanged: (value) {
if (value is bool) {
ref
.read(
prefsChangeNotifierProvider)
.gotoWalletOnStartup = value;
}
},
),
),
const SizedBox(
width: 12,
),
Flexible(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"Specific wallet",
style: STextStyles.titleBold12,
textAlign: TextAlign.left,
),
Text(
"Select a specific wallet to load into on startup",
style: STextStyles.itemSubtitle,
textAlign: TextAlign.left,
),
],
),
),
],
),
),
),
),
),
if (!ref.watch(prefsChangeNotifierProvider
.select((value) => value.gotoWalletOnStartup)))
const SizedBox(
height: 12,
),
if (ref.watch(prefsChangeNotifierProvider
.select((value) => value.gotoWalletOnStartup)))
Container(
color: Colors.transparent,
child: Padding(
padding: const EdgeInsets.only(
left: 12.0,
right: 12,
bottom: 12,
),
child: Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
const SizedBox(
width: 12 + 20,
height: 12,
),
Flexible(
child: RawMaterialButton(
// splashColor: CFColors.splashLight,
materialTapTargetSize:
MaterialTapTargetSize.shrinkWrap,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(
Constants
.size.circularBorderRadius,
),
),
onPressed: () {
Navigator.of(context).pushNamed(
StartupWalletSelectionView
.routeName);
},
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
"Select wallet...",
style: STextStyles.link2,
textAlign: TextAlign.left,
),
],
),
),
),
],
),
),
),
],
),
),
],
),
),
),
);
},
),
),
);
}
}

View file

@ -0,0 +1,197 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/utilities/assets.dart';
import 'package:stackwallet/utilities/cfcolors.dart';
import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/draggable_switch_button.dart';
import 'package:stackwallet/widgets/rounded_white_container.dart';
class StartupWalletSelectionView extends ConsumerStatefulWidget {
const StartupWalletSelectionView({Key? key}) : super(key: key);
static const String routeName = "/startupWalletSelection";
@override
ConsumerState<StartupWalletSelectionView> createState() =>
_StartupWalletSelectionViewState();
}
class _StartupWalletSelectionViewState
extends ConsumerState<StartupWalletSelectionView> {
final Map<String, DSBController> _controllers = {};
@override
Widget build(BuildContext context) {
final managers = ref
.watch(walletsChangeNotifierProvider.select((value) => value.managers));
_controllers.clear();
for (final manager in managers) {
_controllers[manager.walletId] = DSBController();
}
return Scaffold(
backgroundColor: CFColors.almostWhite,
appBar: AppBar(
leading: AppBarBackButton(
onPressed: () async {
Navigator.of(context).pop();
},
),
title: FittedBox(
fit: BoxFit.scaleDown,
child: Text(
"Select startup wallet",
style: STextStyles.navBarTitle,
),
),
),
body: LayoutBuilder(builder: (context, constraints) {
return Padding(
padding: const EdgeInsets.only(
left: 12,
top: 12,
right: 12,
),
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight - 24,
),
child: IntrinsicHeight(
child: Padding(
padding: const EdgeInsets.all(4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(
height: 4,
),
Text(
"Select a wallet to load into immediately on startup",
style: STextStyles.smallMed12,
),
const SizedBox(
height: 12,
),
RoundedWhiteContainer(
padding: const EdgeInsets.all(0),
child: Column(
children: [
...managers.map(
(manager) => Padding(
padding: const EdgeInsets.all(12),
child: Row(
key: Key(
"startupWalletSelectionGroupKey_${manager.walletId}"),
children: [
Container(
decoration: BoxDecoration(
color: CFColors.coin
.forCoin(manager.coin)
.withOpacity(0.5),
borderRadius: BorderRadius.circular(
Constants.size.circularBorderRadius,
),
),
child: Padding(
padding: const EdgeInsets.all(4),
child: SvgPicture.asset(
Assets.svg
.iconFor(coin: manager.coin),
width: 20,
height: 20,
),
),
),
const SizedBox(
width: 12,
),
Column(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
manager.walletName,
style: STextStyles.titleBold12,
),
// const SizedBox(
// height: 2,
// ),
// FutureBuilder(
// future: manager.totalBalance,
// builder: (builderContext,
// AsyncSnapshot<Decimal> snapshot) {
// if (snapshot.connectionState ==
// ConnectionState.done &&
// snapshot.hasData) {
// return Text(
// "${Format.localizedStringAsFixed(
// value: snapshot.data!,
// locale: ref.watch(
// localeServiceChangeNotifierProvider
// .select((value) =>
// value.locale)),
// decimalPlaces: 8,
// )} ${manager.coin.ticker}",
// style: STextStyles.itemSubtitle,
// );
// } else {
// return AnimatedText(
// stringsToLoopThrough: const [
// "Loading balance",
// "Loading balance.",
// "Loading balance..",
// "Loading balance..."
// ],
// style: STextStyles.itemSubtitle,
// );
// }
// },
// ),
],
),
const Spacer(),
SizedBox(
height: 20,
width: 20,
child: Radio(
activeColor: CFColors.link2,
value: manager.walletId,
groupValue: ref.watch(
prefsChangeNotifierProvider.select(
(value) => value.startupWalletId),
),
onChanged: (value) {
if (value is String) {
ref
.read(
prefsChangeNotifierProvider)
.startupWalletId = value;
}
},
),
),
],
),
),
),
],
),
),
],
),
),
),
),
),
);
}),
);
}
}

View file

@ -24,6 +24,9 @@ import 'package:stackwallet/providers/global/auto_swb_service_provider.dart';
import 'package:stackwallet/providers/providers.dart';
import 'package:stackwallet/providers/ui/transaction_filter_provider.dart';
import 'package:stackwallet/providers/ui/unread_notifications_provider.dart';
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
import 'package:stackwallet/providers/wallet/wallet_balance_toggle_state_provider.dart';
import 'package:stackwallet/services/change_now/change_now_loading_service.dart';
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
import 'package:stackwallet/services/coins/manager.dart';
import 'package:stackwallet/services/event_bus/events/global/node_connection_status_changed_event.dart';
@ -35,6 +38,7 @@ import 'package:stackwallet/utilities/constants.dart';
import 'package:stackwallet/utilities/enums/backup_frequency_type.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/enums/flush_bar_type.dart';
import 'package:stackwallet/utilities/enums/wallet_balance_toggle_state.dart';
import 'package:stackwallet/utilities/text_styles.dart';
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
@ -42,10 +46,6 @@ import 'package:stackwallet/widgets/custom_loading_overlay.dart';
import 'package:stackwallet/widgets/stack_dialog.dart';
import 'package:tuple/tuple.dart';
import '../../providers/wallet/public_private_balance_state_provider.dart';
import '../../providers/wallet/wallet_balance_toggle_state_provider.dart';
import '../../utilities/enums/wallet_balance_toggle_state.dart';
/// [eventBus] should only be set during testing
class WalletView extends ConsumerStatefulWidget {
const WalletView({
@ -79,6 +79,8 @@ class _WalletViewState extends ConsumerState<WalletView> {
late StreamSubscription<dynamic> _syncStatusSubscription;
late StreamSubscription<dynamic> _nodeStatusSubscription;
final _cnLoadingService = ChangeNowLoadingService();
@override
void initState() {
walletId = widget.walletId;
@ -272,9 +274,10 @@ class _WalletViewState extends ConsumerState<WalletView> {
unawaited(Navigator.of(context).pushNamed(
WalletInitiatedExchangeView.routeName,
arguments: Tuple2(
arguments: Tuple3(
walletId,
coin,
_loadCNData,
),
));
}
@ -346,6 +349,11 @@ class _WalletViewState extends ConsumerState<WalletView> {
}
}
void _loadCNData() {
// unawaited future
_cnLoadingService.loadAll(ref, coin: ref.read(managerProvider).coin);
}
@override
Widget build(BuildContext context) {
debugPrint("BUILD: $runtimeType");

View file

@ -56,6 +56,8 @@ import 'package:stackwallet/pages/settings_views/global_settings_view/stack_back
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/restore_from_encrypted_string_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/restore_from_file_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/stack_backup_views/stack_backup_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_preferences_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/startup_preferences/startup_wallet_selection_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/support_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_options_view.dart';
import 'package:stackwallet/pages/settings_views/global_settings_view/syncing_preferences_views/syncing_preferences_view.dart';
@ -224,6 +226,18 @@ class RouteGenerator {
builder: (_) => const SyncingPreferencesView(),
settings: RouteSettings(name: settings.name));
case StartupPreferencesView.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const StartupPreferencesView(),
settings: RouteSettings(name: settings.name));
case StartupWalletSelectionView.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => const StartupWalletSelectionView(),
settings: RouteSettings(name: settings.name));
case ManageNodesView.routeName:
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
@ -719,7 +733,7 @@ class RouteGenerator {
return _routeError("${settings.name} invalid args: ${args.toString()}");
case WalletInitiatedExchangeView.routeName:
if (args is Tuple2<String, Coin>) {
if (args is Tuple3<String, Coin, VoidCallback>) {
return getRoute(
shouldUseMaterialRoute: useMaterialPageRoute,
builder: (_) => Stack(
@ -728,7 +742,9 @@ class RouteGenerator {
walletId: args.item1,
coin: args.item2,
),
const ExchangeLoadingOverlayView(),
ExchangeLoadingOverlayView(
unawaitedLoad: args.item3,
),
],
),
settings: RouteSettings(

View file

@ -0,0 +1,140 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:stackwallet/providers/exchange/available_currencies_state_provider.dart';
import 'package:stackwallet/providers/exchange/available_floating_rate_pairs_state_provider.dart';
import 'package:stackwallet/providers/exchange/change_now_provider.dart';
import 'package:stackwallet/providers/exchange/changenow_initial_load_status.dart';
import 'package:stackwallet/providers/exchange/estimate_rate_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_exchange_form_provider.dart';
import 'package:stackwallet/providers/exchange/fixed_rate_market_pairs_provider.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/logger.dart';
class ChangeNowLoadingService {
Future<void> loadAll(WidgetRef ref, {Coin? coin}) async {
try {
await Future.wait([
_loadFixedRateMarkets(ref, coin: coin),
_loadChangeNowStandardCurrencies(ref, coin: coin),
]);
} catch (e, s) {
Logging.instance.log("ChangeNowLoadingService.loadAll failed: $e\n$s",
level: LogLevel.Error);
}
}
Future<void> _loadFixedRateMarkets(WidgetRef ref, {Coin? coin}) async {
if (ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state ==
ChangeNowLoadStatus.loading) {
// already in progress so just
return;
}
ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.loading;
final response3 =
await ref.read(changeNowProvider).getAvailableFixedRateMarkets();
if (response3.value != null) {
ref.read(fixedRateMarketPairsStateProvider.state).state =
response3.value!;
if (ref.read(fixedRateExchangeFormProvider).market == null) {
String fromTicker = "btc";
String toTicker = "xmr";
if (coin != null) {
fromTicker = coin.ticker.toLowerCase();
}
final matchingMarkets = response3.value!
.where((e) => e.to == toTicker && e.from == fromTicker);
if (matchingMarkets.isNotEmpty) {
await ref
.read(fixedRateExchangeFormProvider)
.updateMarket(matchingMarkets.first, true);
}
}
} else {
Logging.instance.log(
"Failed to load changeNOW fixed rate markets: ${response3.exception?.errorMessage}",
level: LogLevel.Error);
ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.failed;
return;
}
ref.read(changeNowFixedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.success;
}
Future<void> _loadChangeNowStandardCurrencies(WidgetRef ref,
{Coin? coin}) async {
if (ref
.read(changeNowEstimatedInitialLoadStatusStateProvider.state)
.state ==
ChangeNowLoadStatus.loading) {
// already in progress so just
return;
}
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.loading;
final response = await ref.read(changeNowProvider).getAvailableCurrencies();
final response2 =
await ref.read(changeNowProvider).getAvailableFloatingRatePairs();
if (response.value != null) {
ref.read(availableChangeNowCurrenciesStateProvider.state).state =
response.value!;
if (response2.value != null) {
ref.read(availableFloatingRatePairsStateProvider.state).state =
response2.value!;
String fromTicker = "btc";
String toTicker = "xmr";
if (coin != null) {
fromTicker = coin.ticker.toLowerCase();
}
if (response.value!.length > 1) {
if (ref.read(estimatedRateExchangeFormProvider).from == null) {
if (response.value!
.where((e) => e.ticker == fromTicker)
.isNotEmpty) {
await ref.read(estimatedRateExchangeFormProvider).updateFrom(
response.value!.firstWhere((e) => e.ticker == fromTicker),
false);
}
}
if (ref.read(estimatedRateExchangeFormProvider).to == null) {
if (response.value!.where((e) => e.ticker == toTicker).isNotEmpty) {
await ref.read(estimatedRateExchangeFormProvider).updateTo(
response.value!.firstWhere((e) => e.ticker == toTicker),
false);
}
}
}
} else {
Logging.instance.log(
"Failed to load changeNOW available floating rate pairs: ${response2.exception?.errorMessage}",
level: LogLevel.Error);
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.failed;
return;
}
} else {
Logging.instance.log(
"Failed to load changeNOW currencies: ${response.exception?.errorMessage}",
level: LogLevel.Error);
await Future<void>.delayed(const Duration(seconds: 3));
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.failed;
return;
}
ref.read(changeNowEstimatedInitialLoadStatusStateProvider.state).state =
ChangeNowLoadStatus.success;
}
}

View file

@ -399,6 +399,138 @@ class BitcoinWallet extends CoinServiceAPI {
level: LogLevel.Info);
}
Future<Map<String, dynamic>> _checkGaps(
int maxNumberOfIndexesToCheck,
int maxUnusedAddressGap,
int txCountBatchSize,
bip32.BIP32 root,
DerivePathType type,
int account) async {
List<String> addressArray = [];
int returningIndex = -1;
Map<String, Map<String, String>> derivations = {};
int gapCounter = 0;
for (int index = 0;
index < maxNumberOfIndexesToCheck && gapCounter < maxUnusedAddressGap;
index += txCountBatchSize) {
List<String> iterationsAddressArray = [];
Logging.instance.log(
"index: $index, \t GapCounter $account ${type.name}: $gapCounter",
level: LogLevel.Info);
final _id = "k_$index";
Map<String, String> txCountCallArgs = {};
final Map<String, dynamic> receivingNodes = {};
for (int j = 0; j < txCountBatchSize; j++) {
final node = await compute(
getBip32NodeFromRootWrapper,
Tuple4(
account,
index + j,
root,
type,
),
);
String? address;
switch (type) {
case DerivePathType.bip44:
address = P2PKH(
data: PaymentData(pubkey: node.publicKey),
network: _network)
.data
.address!;
break;
case DerivePathType.bip49:
address = P2SH(
data: PaymentData(
redeem: P2WPKH(
data: PaymentData(pubkey: node.publicKey),
network: _network)
.data),
network: _network)
.data
.address!;
break;
case DerivePathType.bip84:
address = P2WPKH(
network: _network,
data: PaymentData(pubkey: node.publicKey))
.data
.address!;
break;
default:
throw Exception("No Path type $type exists");
}
receivingNodes.addAll({
"${_id}_$j": {
"node": node,
"address": address,
}
});
txCountCallArgs.addAll({
"${_id}_$j": address,
});
}
// get address tx counts
final counts = await _getBatchTxCount(addresses: txCountCallArgs);
// check and add appropriate addresses
for (int k = 0; k < txCountBatchSize; k++) {
int count = counts["${_id}_$k"]!;
if (count > 0) {
final node = receivingNodes["${_id}_$k"];
// add address to array
addressArray.add(node["address"] as String);
iterationsAddressArray.add(node["address"] as String);
// set current index
returningIndex = index + k;
// reset counter
gapCounter = 0;
// add info to derivations
derivations[node["address"] as String] = {
"pubKey": Format.uint8listToString(
(node["node"] as bip32.BIP32).publicKey),
"wif": (node["node"] as bip32.BIP32).toWIF(),
};
}
// increase counter when no tx history found
if (count == 0) {
gapCounter++;
}
}
// cache all the transactions while waiting for the current function to finish.
unawaited(getTransactionCacheEarly(iterationsAddressArray));
}
return {
"addressArray": addressArray,
"index": returningIndex,
"derivations": derivations
};
}
Future<void> getTransactionCacheEarly(List<String> allAddresses) async {
try {
final List<Map<String, dynamic>> allTxHashes =
await _fetchHistory(allAddresses);
for (final txHash in allTxHashes) {
try {
unawaited(cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
));
} catch (e) {
continue;
}
}
} catch (e) {
//
}
}
Future<void> _recoverWalletFromBIP32SeedPhrase({
required String mnemonic,
int maxUnusedAddressGap = 20,
@ -429,10 +561,6 @@ class BitcoinWallet extends CoinServiceAPI {
int p2shChangeIndex = -1;
int p2wpkhChangeIndex = -1;
// The gap limit will be capped at [maxUnusedAddressGap]
int receivingGapCounter = 0;
int changeGapCounter = 0;
// actual size is 36 due to p2pkh, p2sh, and p2wpkh so 12x3
const txCountBatchSize = 12;
@ -440,323 +568,71 @@ class BitcoinWallet extends CoinServiceAPI {
// receiving addresses
Logging.instance
.log("checking receiving addresses...", level: LogLevel.Info);
for (int index = 0;
index < maxNumberOfIndexesToCheck &&
receivingGapCounter < maxUnusedAddressGap;
index += txCountBatchSize) {
Logging.instance.log(
"index: $index, \t receivingGapCounter: $receivingGapCounter",
level: LogLevel.Info);
final resultReceive44 = _checkGaps(maxNumberOfIndexesToCheck,
maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip44, 0);
final receivingP2pkhID = "k_$index";
final receivingP2shID = "s_$index";
final receivingP2wpkhID = "w_$index";
Map<String, String> txCountCallArgs = {};
final Map<String, dynamic> receivingNodes = {};
final resultReceive49 = _checkGaps(maxNumberOfIndexesToCheck,
maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip49, 0);
for (int j = 0; j < txCountBatchSize; j++) {
// bip44 / P2PKH
final node44 = await compute(
getBip32NodeFromRootWrapper,
Tuple4(
0,
index + j,
root,
DerivePathType.bip44,
),
);
final p2pkhReceiveAddress = P2PKH(
data: PaymentData(pubkey: node44.publicKey),
network: _network)
.data
.address!;
receivingNodes.addAll({
"${receivingP2pkhID}_$j": {
"node": node44,
"address": p2pkhReceiveAddress,
}
});
txCountCallArgs.addAll({
"${receivingP2pkhID}_$j": p2pkhReceiveAddress,
});
// bip49 / P2SH
final node49 = await compute(
getBip32NodeFromRootWrapper,
Tuple4(
0,
index + j,
root,
DerivePathType.bip49,
),
);
final p2shReceiveAddress = P2SH(
data: PaymentData(
redeem: P2WPKH(
data: PaymentData(pubkey: node49.publicKey),
network: _network)
.data),
network: _network)
.data
.address!;
receivingNodes.addAll({
"${receivingP2shID}_$j": {
"node": node49,
"address": p2shReceiveAddress,
}
});
txCountCallArgs.addAll({
"${receivingP2shID}_$j": p2shReceiveAddress,
});
// bip84 / P2WPKH
final node84 = await compute(
getBip32NodeFromRootWrapper,
Tuple4(
0,
index + j,
root,
DerivePathType.bip84,
),
);
final p2wpkhReceiveAddress = P2WPKH(
network: _network,
data: PaymentData(pubkey: node84.publicKey))
.data
.address!;
receivingNodes.addAll({
"${receivingP2wpkhID}_$j": {
"node": node84,
"address": p2wpkhReceiveAddress,
}
});
txCountCallArgs.addAll({
"${receivingP2wpkhID}_$j": p2wpkhReceiveAddress,
});
}
// get address tx counts
final counts = await _getBatchTxCount(addresses: txCountCallArgs);
// check and add appropriate addresses
for (int k = 0; k < txCountBatchSize; k++) {
int p2pkhTxCount = counts["${receivingP2pkhID}_$k"]!;
if (p2pkhTxCount > 0) {
final node = receivingNodes["${receivingP2pkhID}_$k"];
// add address to array
p2pkhReceiveAddressArray.add(node["address"] as String);
// set current index
p2pkhReceiveIndex = index + k;
// reset counter
receivingGapCounter = 0;
// add info to derivations
p2pkhReceiveDerivations[node["address"] as String] = {
"pubKey": Format.uint8listToString(
(node["node"] as bip32.BIP32).publicKey),
"wif": (node["node"] as bip32.BIP32).toWIF(),
};
}
int p2shTxCount = counts["${receivingP2shID}_$k"]!;
if (p2shTxCount > 0) {
final node = receivingNodes["${receivingP2shID}_$k"];
// add address to array
p2shReceiveAddressArray.add(node["address"] as String);
// set current index
p2shReceiveIndex = index + k;
// reset counter
receivingGapCounter = 0;
// add info to derivations
p2shReceiveDerivations[node["address"] as String] = {
"pubKey": Format.uint8listToString(
(node["node"] as bip32.BIP32).publicKey),
"wif": (node["node"] as bip32.BIP32).toWIF(),
};
}
int p2wpkhTxCount = counts["${receivingP2wpkhID}_$k"]!;
if (p2wpkhTxCount > 0) {
final node = receivingNodes["${receivingP2wpkhID}_$k"];
// add address to array
p2wpkhReceiveAddressArray.add(node["address"] as String);
// set current index
p2wpkhReceiveIndex = index + k;
// reset counter
receivingGapCounter = 0;
// add info to derivations
p2wpkhReceiveDerivations[node["address"] as String] = {
"pubKey": Format.uint8listToString(
(node["node"] as bip32.BIP32).publicKey),
"wif": (node["node"] as bip32.BIP32).toWIF(),
};
}
// increase counter when no tx history found
if (p2wpkhTxCount == 0 && p2pkhTxCount == 0 && p2shTxCount == 0) {
receivingGapCounter++;
}
}
}
final resultReceive84 = _checkGaps(maxNumberOfIndexesToCheck,
maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip84, 0);
Logging.instance
.log("checking change addresses...", level: LogLevel.Info);
// change addresses
for (int index = 0;
index < maxNumberOfIndexesToCheck &&
changeGapCounter < maxUnusedAddressGap;
index += txCountBatchSize) {
Logging.instance.log(
"index: $index, \t changeGapCounter: $changeGapCounter",
level: LogLevel.Info);
final changeP2pkhID = "k_$index";
final changeP2shID = "s_$index";
final changeP2wpkhID = "w_$index";
Map<String, String> args = {};
final Map<String, dynamic> changeNodes = {};
final resultChange44 = _checkGaps(maxNumberOfIndexesToCheck,
maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip44, 1);
for (int j = 0; j < txCountBatchSize; j++) {
// bip44 / P2PKH
final node44 = await compute(
getBip32NodeFromRootWrapper,
Tuple4(
1,
index + j,
root,
DerivePathType.bip44,
),
);
final p2pkhChangeAddress = P2PKH(
data: PaymentData(pubkey: node44.publicKey),
network: _network)
.data
.address!;
changeNodes.addAll({
"${changeP2pkhID}_$j": {
"node": node44,
"address": p2pkhChangeAddress,
}
});
args.addAll({
"${changeP2pkhID}_$j": p2pkhChangeAddress,
});
final resultChange49 = _checkGaps(maxNumberOfIndexesToCheck,
maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip49, 1);
// bip49 / P2SH
final node49 = await compute(
getBip32NodeFromRootWrapper,
Tuple4(
1,
index + j,
root,
DerivePathType.bip49,
),
);
final p2shChangeAddress = P2SH(
data: PaymentData(
redeem: P2WPKH(
data: PaymentData(pubkey: node49.publicKey),
network: _network)
.data),
network: _network)
.data
.address!;
changeNodes.addAll({
"${changeP2shID}_$j": {
"node": node49,
"address": p2shChangeAddress,
}
});
args.addAll({
"${changeP2shID}_$j": p2shChangeAddress,
});
final resultChange84 = _checkGaps(maxNumberOfIndexesToCheck,
maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip84, 1);
// bip84 / P2WPKH
final node84 = await compute(
getBip32NodeFromRootWrapper,
Tuple4(
1,
index + j,
root,
DerivePathType.bip84,
),
);
final p2wpkhChangeAddress = P2WPKH(
network: _network,
data: PaymentData(pubkey: node84.publicKey))
.data
.address!;
changeNodes.addAll({
"${changeP2wpkhID}_$j": {
"node": node84,
"address": p2wpkhChangeAddress,
}
});
args.addAll({
"${changeP2wpkhID}_$j": p2wpkhChangeAddress,
});
}
await Future.wait([
resultReceive44,
resultReceive49,
resultReceive84,
resultChange44,
resultChange49,
resultChange84
]);
// get address tx counts
final counts = await _getBatchTxCount(addresses: args);
p2pkhReceiveAddressArray =
(await resultReceive44)['addressArray'] as List<String>;
p2pkhReceiveIndex = (await resultReceive44)['index'] as int;
p2pkhReceiveDerivations = (await resultReceive44)['derivations']
as Map<String, Map<String, String>>;
// check and add appropriate addresses
for (int k = 0; k < txCountBatchSize; k++) {
int p2pkhTxCount = counts["${changeP2pkhID}_$k"]!;
if (p2pkhTxCount > 0) {
final node = changeNodes["${changeP2pkhID}_$k"];
// add address to array
p2pkhChangeAddressArray.add(node["address"] as String);
// set current index
p2pkhChangeIndex = index + k;
// reset counter
changeGapCounter = 0;
// add info to derivations
p2pkhChangeDerivations[node["address"] as String] = {
"pubKey": Format.uint8listToString(
(node["node"] as bip32.BIP32).publicKey),
"wif": (node["node"] as bip32.BIP32).toWIF(),
};
}
p2shReceiveAddressArray =
(await resultReceive49)['addressArray'] as List<String>;
p2shReceiveIndex = (await resultReceive49)['index'] as int;
p2shReceiveDerivations = (await resultReceive49)['derivations']
as Map<String, Map<String, String>>;
int p2shTxCount = counts["${changeP2shID}_$k"]!;
if (p2shTxCount > 0) {
final node = changeNodes["${changeP2shID}_$k"];
// add address to array
p2shChangeAddressArray.add(node["address"] as String);
// set current index
p2shChangeIndex = index + k;
// reset counter
changeGapCounter = 0;
// add info to derivations
p2shChangeDerivations[node["address"] as String] = {
"pubKey": Format.uint8listToString(
(node["node"] as bip32.BIP32).publicKey),
"wif": (node["node"] as bip32.BIP32).toWIF(),
};
}
p2wpkhReceiveAddressArray =
(await resultReceive84)['addressArray'] as List<String>;
p2wpkhReceiveIndex = (await resultReceive84)['index'] as int;
p2wpkhReceiveDerivations = (await resultReceive84)['derivations']
as Map<String, Map<String, String>>;
int p2wpkhTxCount = counts["${changeP2wpkhID}_$k"]!;
if (p2wpkhTxCount > 0) {
final node = changeNodes["${changeP2wpkhID}_$k"];
// add address to array
p2wpkhChangeAddressArray.add(node["address"] as String);
// set current index
p2wpkhChangeIndex = index + k;
// reset counter
changeGapCounter = 0;
// add info to derivations
p2wpkhChangeDerivations[node["address"] as String] = {
"pubKey": Format.uint8listToString(
(node["node"] as bip32.BIP32).publicKey),
"wif": (node["node"] as bip32.BIP32).toWIF(),
};
}
p2pkhChangeAddressArray =
(await resultChange44)['addressArray'] as List<String>;
p2pkhChangeIndex = (await resultChange44)['index'] as int;
p2pkhChangeDerivations = (await resultChange44)['derivations']
as Map<String, Map<String, String>>;
// increase counter when no tx history found
if (p2wpkhTxCount == 0 && p2pkhTxCount == 0 && p2shTxCount == 0) {
changeGapCounter++;
}
}
}
p2shChangeAddressArray =
(await resultChange49)['addressArray'] as List<String>;
p2shChangeIndex = (await resultChange49)['index'] as int;
p2shChangeDerivations = (await resultChange49)['derivations']
as Map<String, Map<String, String>>;
p2wpkhChangeAddressArray =
(await resultChange84)['addressArray'] as List<String>;
p2wpkhChangeIndex = (await resultChange84)['index'] as int;
p2wpkhChangeDerivations = (await resultChange84)['derivations']
as Map<String, Map<String, String>>;
// save the derivations (if any)
if (p2pkhReceiveDerivations.isNotEmpty) {
@ -975,7 +851,7 @@ class BitcoinWallet extends CoinServiceAPI {
// notify on unconfirmed transactions
for (final tx in unconfirmedTxnsToNotifyPending) {
if (tx.txType == "Received") {
NotificationApi.showNotification(
unawaited(NotificationApi.showNotification(
title: "Incoming transaction",
body: walletName,
walletId: walletId,
@ -986,10 +862,10 @@ class BitcoinWallet extends CoinServiceAPI {
txid: tx.txid,
confirmations: tx.confirmations,
requiredConfirmations: MINIMUM_CONFIRMATIONS,
);
));
await txTracker.addNotifiedPending(tx.txid);
} else if (tx.txType == "Sent") {
NotificationApi.showNotification(
unawaited(NotificationApi.showNotification(
title: "Sending transaction",
body: walletName,
walletId: walletId,
@ -1000,7 +876,7 @@ class BitcoinWallet extends CoinServiceAPI {
txid: tx.txid,
confirmations: tx.confirmations,
requiredConfirmations: MINIMUM_CONFIRMATIONS,
);
));
await txTracker.addNotifiedPending(tx.txid);
}
}
@ -1008,7 +884,7 @@ class BitcoinWallet extends CoinServiceAPI {
// notify on confirmed
for (final tx in unconfirmedTxnsToNotifyConfirmed) {
if (tx.txType == "Received") {
NotificationApi.showNotification(
unawaited(NotificationApi.showNotification(
title: "Incoming transaction confirmed",
body: walletName,
walletId: walletId,
@ -1016,10 +892,10 @@ class BitcoinWallet extends CoinServiceAPI {
date: DateTime.fromMillisecondsSinceEpoch(tx.timestamp * 1000),
shouldWatchForUpdates: false,
coinName: coin.name,
);
));
await txTracker.addNotifiedConfirmed(tx.txid);
} else if (tx.txType == "Sent") {
NotificationApi.showNotification(
unawaited(NotificationApi.showNotification(
title: "Outgoing transaction confirmed",
body: walletName,
walletId: walletId,
@ -1027,7 +903,7 @@ class BitcoinWallet extends CoinServiceAPI {
date: DateTime.fromMillisecondsSinceEpoch(tx.timestamp * 1000),
shouldWatchForUpdates: false,
coinName: coin.name,
);
));
await txTracker.addNotifiedConfirmed(tx.txid);
}
}
@ -1094,14 +970,16 @@ class BitcoinWallet extends CoinServiceAPI {
if (currentHeight != storedHeight) {
if (currentHeight != -1) {
// -1 failed to fetch current height
updateStoredChainHeight(newHeight: currentHeight);
unawaited(updateStoredChainHeight(newHeight: currentHeight));
}
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.2, walletId));
await _checkChangeAddressForTransactions(DerivePathType.bip84);
final changeAddressForTransactions =
_checkChangeAddressForTransactions(DerivePathType.bip84);
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.3, walletId));
await _checkCurrentReceivingAddressesForTransactions();
final currentReceivingAddressesForTransactions =
_checkCurrentReceivingAddressesForTransactions();
final newTxData = _fetchTransactionData();
GlobalEventBus.instance
@ -1121,7 +999,15 @@ class BitcoinWallet extends CoinServiceAPI {
GlobalEventBus.instance
.fire(RefreshPercentChangedEvent(0.80, walletId));
await getAllTxsToWatch(await newTxData);
final allTxsToWatch = getAllTxsToWatch(await newTxData);
await Future.wait([
newTxData,
changeAddressForTransactions,
currentReceivingAddressesForTransactions,
newUtxoData,
feeObj,
allTxsToWatch,
]);
GlobalEventBus.instance
.fire(RefreshPercentChangedEvent(0.90, walletId));
}
@ -1137,7 +1023,7 @@ class BitcoinWallet extends CoinServiceAPI {
);
if (shouldAutoSync) {
timer ??= Timer.periodic(const Duration(seconds: 150), (timer) async {
timer ??= Timer.periodic(const Duration(seconds: 30), (timer) async {
Logging.instance.log(
"Periodic refresh check for $walletId $walletName in object instance: $hashCode",
level: LogLevel.Info);
@ -1472,7 +1358,7 @@ class BitcoinWallet extends CoinServiceAPI {
);
if (shouldRefresh) {
refresh();
unawaited(refresh());
}
}
@ -1972,7 +1858,7 @@ class BitcoinWallet extends CoinServiceAPI {
final fetchedUtxoList = <List<Map<String, dynamic>>>[];
final Map<int, Map<String, List<dynamic>>> batches = {};
const batchSizeMax = 10;
const batchSizeMax = 100;
int batchNumber = 0;
for (int i = 0; i < allAddresses.length; i++) {
if (batches[batchNumber] == null) {
@ -2357,7 +2243,7 @@ class BitcoinWallet extends CoinServiceAPI {
final Map<int, Map<String, List<dynamic>>> batches = {};
final Map<String, String> requestIdToAddressMap = {};
const batchSizeMax = 10;
const batchSizeMax = 100;
int batchNumber = 0;
for (int i = 0; i < allAddresses.length; i++) {
if (batches[batchNumber] == null) {
@ -2404,6 +2290,43 @@ class BitcoinWallet extends CoinServiceAPI {
return false;
}
Future<List<Map<String, dynamic>>> fastFetch(List<String> allTxHashes) async {
List<Map<String, dynamic>> allTransactions = [];
const futureLimit = 30;
List<Future<Map<String, dynamic>>> transactionFutures = [];
int currentFutureCount = 0;
for (final txHash in allTxHashes) {
Future<Map<String, dynamic>> transactionFuture =
cachedElectrumXClient.getTransaction(
txHash: txHash,
verbose: true,
coin: coin,
);
transactionFutures.add(transactionFuture);
currentFutureCount++;
if (currentFutureCount > futureLimit) {
currentFutureCount = 0;
await Future.wait(transactionFutures);
for (final fTx in transactionFutures) {
final tx = await fTx;
allTransactions.add(tx);
}
}
}
if (currentFutureCount != 0) {
currentFutureCount = 0;
await Future.wait(transactionFutures);
for (final fTx in transactionFutures) {
final tx = await fTx;
allTransactions.add(tx);
}
}
return allTransactions;
}
Future<TransactionData> _fetchTransactionData() async {
final List<String> allAddresses = await _fetchAllOwnAddresses();
@ -2451,6 +2374,11 @@ class BitcoinWallet extends CoinServiceAPI {
}
}
Set<String> hashes = {};
for (var element in allTxHashes) {
hashes.add(element['tx_hash'] as String);
}
await fastFetch(hashes.toList());
List<Map<String, dynamic>> allTransactions = [];
for (final txHash in allTxHashes) {
@ -2480,6 +2408,16 @@ class BitcoinWallet extends CoinServiceAPI {
Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero;
final List<Map<String, dynamic>> midSortedArray = [];
Set<String> vHashes = {};
for (final txObject in allTransactions) {
for (int i = 0; i < (txObject["vin"] as List).length; i++) {
final input = txObject["vin"]![i] as Map;
final prevTxid = input["txid"] as String;
vHashes.add(prevTxid);
}
}
await fastFetch(vHashes.toList());
for (final txObject in allTransactions) {
List<String> sendersArray = [];
List<String> recipientsArray = [];

View file

@ -251,7 +251,7 @@ class DogecoinWallet extends CoinServiceAPI {
}
Future<void> updateStoredChainHeight({required int newHeight}) async {
DB.instance.put<dynamic>(
await DB.instance.put<dynamic>(
boxName: walletId, key: "storedChainHeight", value: newHeight);
}
@ -346,6 +346,120 @@ class DogecoinWallet extends CoinServiceAPI {
level: LogLevel.Info);
}
Future<Map<String, dynamic>> _checkGaps(
int maxNumberOfIndexesToCheck,
int maxUnusedAddressGap,
int txCountBatchSize,
bip32.BIP32 root,
DerivePathType type,
int account) async {
List<String> addressArray = [];
int returningIndex = -1;
Map<String, Map<String, String>> derivations = {};
int gapCounter = 0;
for (int index = 0;
index < maxNumberOfIndexesToCheck && gapCounter < maxUnusedAddressGap;
index += txCountBatchSize) {
List<String> iterationsAddressArray = [];
Logging.instance.log(
"index: $index, \t GapCounter $account ${type.name}: $gapCounter",
level: LogLevel.Info);
final _id = "k_$index";
Map<String, String> txCountCallArgs = {};
final Map<String, dynamic> receivingNodes = {};
for (int j = 0; j < txCountBatchSize; j++) {
final node = await compute(
getBip32NodeFromRootWrapper,
Tuple4(
account,
index + j,
root,
type,
),
);
String? address;
switch (type) {
case DerivePathType.bip44:
address = P2PKH(
data: PaymentData(pubkey: node.publicKey),
network: _network)
.data
.address!;
break;
default:
throw Exception("No Path type $type exists");
}
receivingNodes.addAll({
"${_id}_$j": {
"node": node,
"address": address,
}
});
txCountCallArgs.addAll({
"${_id}_$j": address,
});
}
// get address tx counts
final counts = await _getBatchTxCount(addresses: txCountCallArgs);
// check and add appropriate addresses
for (int k = 0; k < txCountBatchSize; k++) {
int count = counts["${_id}_$k"]!;
if (count > 0) {
final node = receivingNodes["${_id}_$k"];
// add address to array
addressArray.add(node["address"] as String);
iterationsAddressArray.add(node["address"] as String);
// set current index
returningIndex = index + k;
// reset counter
gapCounter = 0;
// add info to derivations
derivations[node["address"] as String] = {
"pubKey": Format.uint8listToString(
(node["node"] as bip32.BIP32).publicKey),
"wif": (node["node"] as bip32.BIP32).toWIF(),
};
}
// increase counter when no tx history found
if (count == 0) {
gapCounter++;
}
}
// cache all the transactions while waiting for the current function to finish.
unawaited(getTransactionCacheEarly(iterationsAddressArray));
}
return {
"addressArray": addressArray,
"index": returningIndex,
"derivations": derivations
};
}
Future<void> getTransactionCacheEarly(List<String> allAddresses) async {
try {
final List<Map<String, dynamic>> allTxHashes =
await _fetchHistory(allAddresses);
for (final txHash in allTxHashes) {
try {
unawaited(cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
));
} catch (e) {
continue;
}
}
} catch (e) {
//
}
}
Future<void> _recoverWalletFromBIP32SeedPhrase({
required String mnemonic,
int maxUnusedAddressGap = 20,
@ -364,10 +478,6 @@ class DogecoinWallet extends CoinServiceAPI {
List<String> p2pkhChangeAddressArray = [];
int p2pkhChangeIndex = -1;
// The gap limit will be capped at [maxUnusedAddressGap]
int receivingGapCounter = 0;
int changeGapCounter = 0;
// actual size is 12 due to p2pkh so 12x1
const txCountBatchSize = 12;
@ -375,143 +485,31 @@ class DogecoinWallet extends CoinServiceAPI {
// receiving addresses
Logging.instance
.log("checking receiving addresses...", level: LogLevel.Info);
for (int index = 0;
index < maxNumberOfIndexesToCheck &&
receivingGapCounter < maxUnusedAddressGap;
index += txCountBatchSize) {
Logging.instance.log(
"index: $index, \t receivingGapCounter: $receivingGapCounter",
level: LogLevel.Info);
final receivingP2pkhID = "k_$index";
Map<String, String> txCountCallArgs = {};
final Map<String, dynamic> receivingNodes = {};
for (int j = 0; j < txCountBatchSize; j++) {
// bip44 / P2PKH
final node44 = await compute(
getBip32NodeFromRootWrapper,
Tuple4(
0,
index + j,
root,
DerivePathType.bip44,
),
);
final p2pkhReceiveAddress = P2PKH(
data: PaymentData(pubkey: node44.publicKey),
network: _network)
.data
.address!;
receivingNodes.addAll({
"${receivingP2pkhID}_$j": {
"node": node44,
"address": p2pkhReceiveAddress,
}
});
txCountCallArgs.addAll({
"${receivingP2pkhID}_$j": p2pkhReceiveAddress,
});
}
// get address tx counts
final counts = await _getBatchTxCount(addresses: txCountCallArgs);
// check and add appropriate addresses
for (int k = 0; k < txCountBatchSize; k++) {
int p2pkhTxCount = counts["${receivingP2pkhID}_$k"]!;
if (p2pkhTxCount > 0) {
final node = receivingNodes["${receivingP2pkhID}_$k"];
// add address to array
p2pkhReceiveAddressArray.add(node["address"] as String);
// set current index
p2pkhReceiveIndex = index + k;
// reset counter
receivingGapCounter = 0;
// add info to derivations
p2pkhReceiveDerivations[node["address"] as String] = {
"pubKey": Format.uint8listToString(
(node["node"] as bip32.BIP32).publicKey),
"wif": (node["node"] as bip32.BIP32).toWIF(),
};
}
// increase counter when no tx history found
if (p2pkhTxCount == 0) {
receivingGapCounter++;
}
}
}
final resultReceive44 = _checkGaps(maxNumberOfIndexesToCheck,
maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip44, 0);
Logging.instance
.log("checking change addresses...", level: LogLevel.Info);
// change addresses
for (int index = 0;
index < maxNumberOfIndexesToCheck &&
changeGapCounter < maxUnusedAddressGap;
index += txCountBatchSize) {
Logging.instance.log(
"index: $index, \t changeGapCounter: $changeGapCounter",
level: LogLevel.Info);
final changeP2pkhID = "k_$index";
Map<String, String> args = {};
final Map<String, dynamic> changeNodes = {};
final resultChange44 = _checkGaps(maxNumberOfIndexesToCheck,
maxUnusedAddressGap, txCountBatchSize, root, DerivePathType.bip44, 1);
for (int j = 0; j < txCountBatchSize; j++) {
// bip44 / P2PKH
final node44 = await compute(
getBip32NodeFromRootWrapper,
Tuple4(
1,
index + j,
root,
DerivePathType.bip44,
),
);
final p2pkhChangeAddress = P2PKH(
data: PaymentData(pubkey: node44.publicKey),
network: _network)
.data
.address!;
changeNodes.addAll({
"${changeP2pkhID}_$j": {
"node": node44,
"address": p2pkhChangeAddress,
}
});
args.addAll({
"${changeP2pkhID}_$j": p2pkhChangeAddress,
});
}
await Future.wait([
resultReceive44,
resultChange44,
]);
// get address tx counts
final counts = await _getBatchTxCount(addresses: args);
p2pkhReceiveAddressArray =
(await resultReceive44)['addressArray'] as List<String>;
p2pkhReceiveIndex = (await resultReceive44)['index'] as int;
p2pkhReceiveDerivations = (await resultReceive44)['derivations']
as Map<String, Map<String, String>>;
// check and add appropriate addresses
for (int k = 0; k < txCountBatchSize; k++) {
int p2pkhTxCount = counts["${changeP2pkhID}_$k"]!;
if (p2pkhTxCount > 0) {
final node = changeNodes["${changeP2pkhID}_$k"];
// add address to array
p2pkhChangeAddressArray.add(node["address"] as String);
// set current index
p2pkhChangeIndex = index + k;
// reset counter
changeGapCounter = 0;
// add info to derivations
p2pkhChangeDerivations[node["address"] as String] = {
"pubKey": Format.uint8listToString(
(node["node"] as bip32.BIP32).publicKey),
"wif": (node["node"] as bip32.BIP32).toWIF(),
};
}
// increase counter when no tx history found
if (p2pkhTxCount == 0) {
changeGapCounter++;
}
}
}
p2pkhChangeAddressArray =
(await resultChange44)['addressArray'] as List<String>;
p2pkhChangeIndex = (await resultChange44)['index'] as int;
p2pkhChangeDerivations = (await resultChange44)['derivations']
as Map<String, Map<String, String>>;
// save the derivations (if any)
if (p2pkhReceiveDerivations.isNotEmpty) {
@ -655,7 +653,7 @@ class DogecoinWallet extends CoinServiceAPI {
// notify on new incoming transaction
for (final tx in unconfirmedTxnsToNotifyPending) {
if (tx.txType == "Received") {
NotificationApi.showNotification(
unawaited(NotificationApi.showNotification(
title: "Incoming transaction",
body: walletName,
walletId: walletId,
@ -666,10 +664,10 @@ class DogecoinWallet extends CoinServiceAPI {
txid: tx.txid,
confirmations: tx.confirmations,
requiredConfirmations: MINIMUM_CONFIRMATIONS,
);
));
await txTracker.addNotifiedPending(tx.txid);
} else if (tx.txType == "Sent") {
NotificationApi.showNotification(
unawaited(NotificationApi.showNotification(
title: "Sending transaction",
body: walletName,
walletId: walletId,
@ -680,7 +678,7 @@ class DogecoinWallet extends CoinServiceAPI {
txid: tx.txid,
confirmations: tx.confirmations,
requiredConfirmations: MINIMUM_CONFIRMATIONS,
);
));
await txTracker.addNotifiedPending(tx.txid);
}
}
@ -688,7 +686,7 @@ class DogecoinWallet extends CoinServiceAPI {
// notify on confirmed
for (final tx in unconfirmedTxnsToNotifyConfirmed) {
if (tx.txType == "Received") {
NotificationApi.showNotification(
unawaited(NotificationApi.showNotification(
title: "Incoming transaction confirmed",
body: walletName,
walletId: walletId,
@ -696,11 +694,11 @@ class DogecoinWallet extends CoinServiceAPI {
date: DateTime.now(),
shouldWatchForUpdates: false,
coinName: coin.name,
);
));
await txTracker.addNotifiedConfirmed(tx.txid);
} else if (tx.txType == "Sent") {
NotificationApi.showNotification(
unawaited(NotificationApi.showNotification(
title: "Outgoing transaction confirmed",
body: walletName,
walletId: walletId,
@ -708,7 +706,7 @@ class DogecoinWallet extends CoinServiceAPI {
date: DateTime.now(),
shouldWatchForUpdates: false,
coinName: coin.name,
);
));
await txTracker.addNotifiedConfirmed(tx.txid);
}
}
@ -772,7 +770,7 @@ class DogecoinWallet extends CoinServiceAPI {
if (currentHeight != storedHeight) {
if (currentHeight != -1) {
// -1 failed to fetch current height
updateStoredChainHeight(newHeight: currentHeight);
unawaited(updateStoredChainHeight(newHeight: currentHeight));
}
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.2, walletId));
@ -815,7 +813,7 @@ class DogecoinWallet extends CoinServiceAPI {
refreshMutex = false;
if (shouldAutoSync) {
timer ??= Timer.periodic(const Duration(seconds: 150), (timer) async {
timer ??= Timer.periodic(const Duration(seconds: 30), (timer) async {
// chain height check currently broken
// if ((await chainHeight) != (await storedChainHeight)) {
if (await refreshIfThereIsNewData()) {
@ -1129,7 +1127,7 @@ class DogecoinWallet extends CoinServiceAPI {
);
if (shouldRefresh) {
refresh();
unawaited(refresh());
}
}
@ -1496,6 +1494,43 @@ class DogecoinWallet extends CoinServiceAPI {
await _secureStore.write(key: key, value: newReceiveDerivationsString);
}
Future<List<Map<String, dynamic>>> fastFetch(List<String> allTxHashes) async {
List<Map<String, dynamic>> allTransactions = [];
const futureLimit = 30;
List<Future<Map<String, dynamic>>> transactionFutures = [];
int currentFutureCount = 0;
for (final txHash in allTxHashes) {
Future<Map<String, dynamic>> transactionFuture =
cachedElectrumXClient.getTransaction(
txHash: txHash,
verbose: true,
coin: coin,
);
transactionFutures.add(transactionFuture);
currentFutureCount++;
if (currentFutureCount > futureLimit) {
currentFutureCount = 0;
await Future.wait(transactionFutures);
for (final fTx in transactionFutures) {
final tx = await fTx;
allTransactions.add(tx);
}
}
}
if (currentFutureCount != 0) {
currentFutureCount = 0;
await Future.wait(transactionFutures);
for (final fTx in transactionFutures) {
final tx = await fTx;
allTransactions.add(tx);
}
}
return allTransactions;
}
Future<UtxoData> _fetchUtxoData() async {
final List<String> allAddresses = await _fetchAllOwnAddresses();
@ -1503,7 +1538,7 @@ class DogecoinWallet extends CoinServiceAPI {
final fetchedUtxoList = <List<Map<String, dynamic>>>[];
final Map<int, Map<String, List<dynamic>>> batches = {};
const batchSizeMax = 10;
const batchSizeMax = 100;
int batchNumber = 0;
for (int i = 0; i < allAddresses.length; i++) {
if (batches[batchNumber] == null) {
@ -1872,14 +1907,14 @@ class DogecoinWallet extends CoinServiceAPI {
final Map<int, Map<String, List<dynamic>>> batches = {};
final Map<String, String> requestIdToAddressMap = {};
const batchSizeMax = 10;
const batchSizeMax = 100;
int batchNumber = 0;
for (int i = 0; i < allAddresses.length; i++) {
if (batches[batchNumber] == null) {
batches[batchNumber] = {};
}
final scripthash = _convertToScriptHash(allAddresses[i], _network);
final id = const Uuid().v1();
final id = Logger.isTestEnv ? "$i" : const Uuid().v1();
requestIdToAddressMap[id] = allAddresses[i];
batches[batchNumber]!.addAll({
id: [scripthash]
@ -1954,6 +1989,11 @@ class DogecoinWallet extends CoinServiceAPI {
}
}
List<String> hashes = [];
for (var element in allTxHashes) {
hashes.add(element['tx_hash'] as String);
}
await fastFetch(hashes);
List<Map<String, dynamic>> allTransactions = [];
for (final txHash in allTxHashes) {
@ -1983,6 +2023,16 @@ class DogecoinWallet extends CoinServiceAPI {
Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero;
final List<Map<String, dynamic>> midSortedArray = [];
Set<String> vHashes = {};
for (final txObject in allTransactions) {
for (int i = 0; i < (txObject["vin"] as List).length; i++) {
final input = txObject["vin"]![i] as Map;
final prevTxid = input["txid"] as String;
vHashes.add(prevTxid);
}
}
await fastFetch(vHashes.toList());
for (final txObject in allTransactions) {
List<String> sendersArray = [];
List<String> recipientsArray = [];
@ -2746,7 +2796,7 @@ class DogecoinWallet extends CoinServiceAPI {
);
// clear cache
_cachedElectrumXClient.clearSharedTransactionCache(coin: coin);
await _cachedElectrumXClient.clearSharedTransactionCache(coin: coin);
// back up data
await _rescanBackup();
@ -3012,6 +3062,7 @@ class DogecoinWallet extends CoinServiceAPI {
return available - estimatedFee;
}
@override
Future<bool> generateNewAddress() async {
try {
await _incrementAddressIndexForChain(

View file

@ -225,7 +225,7 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
sendPort
.send("Error An error was thrown in this isolate $function: $e\n$s");
} finally {
Logging.instance.isar?.close();
await Logging.instance.isar?.close();
}
}
@ -539,7 +539,7 @@ class EpicCashWallet extends CoinServiceAPI {
// TODO notify ui/ fire event for node changed?
if (shouldRefresh) {
refresh();
unawaited(refresh());
}
}
@ -705,7 +705,7 @@ class EpicCashWallet extends CoinServiceAPI {
try {
result = await cancelPendingTransaction(tx_slate_id);
Logging.instance.log("result?: $result", level: LogLevel.Info);
if (result != null && !(result.toLowerCase().contains("error"))) {
if (!(result.toLowerCase().contains("error"))) {
await postCancel(
receiveAddress, tx_slate_id, signature, sendersAddress);
}
@ -1180,10 +1180,10 @@ class EpicCashWallet extends CoinServiceAPI {
transactionFees = message['result'] as String;
});
debugPrint(transactionFees);
var decodeData;
dynamic decodeData;
try {
decodeData = json.decode(transactionFees!);
} catch (e, s) {
} catch (e) {
if (ifErrorEstimateFee) {
//Error Not enough funds. Required: 0.56500000, Available: 0.56200000
if (transactionFees!.contains("Required")) {
@ -1705,7 +1705,7 @@ class EpicCashWallet extends CoinServiceAPI {
try {
final String response = message['result'] as String;
if (response == null || response == "") {
if (response == "") {
Logging.instance.log("response: ${response.runtimeType}",
level: LogLevel.Info);
await deleteSlate(currentAddress,
@ -1877,6 +1877,9 @@ class EpicCashWallet extends CoinServiceAPI {
boxName: walletId, key: "creationHeight", value: await chainHeight);
}
final int curAdd = await setCurrentIndex();
_currentReceivingAddress = _getCurrentAddressForChain(curAdd);
if (!await startScans()) {
refreshMutex = false;
GlobalEventBus.instance.fire(
@ -1895,9 +1898,6 @@ class EpicCashWallet extends CoinServiceAPI {
);
return;
}
final int curAdd = await setCurrentIndex();
_currentReceivingAddress = _getCurrentAddressForChain(curAdd);
await processAllSlates();
await processAllCancels();
@ -1921,7 +1921,7 @@ class EpicCashWallet extends CoinServiceAPI {
if (currentHeight != storedHeight) {
if (currentHeight != -1) {
// -1 failed to fetch current height
updateStoredChainHeight(newHeight: currentHeight);
unawaited(updateStoredChainHeight(newHeight: currentHeight));
}
final newTxData = _fetchTransactionData();
@ -2288,7 +2288,7 @@ class EpicCashWallet extends CoinServiceAPI {
timer?.cancel();
timer = null;
if (isActive) {
startSync();
unawaited(startSync());
} else {
for (final isolate in isolates.values) {
isolate.kill(priority: Isolate.immediate);

View file

@ -43,6 +43,7 @@ import 'package:stackwallet/utilities/format.dart';
import 'package:stackwallet/utilities/logger.dart';
import 'package:stackwallet/utilities/prefs.dart';
import 'package:tuple/tuple.dart';
import 'package:uuid/uuid.dart';
const DUST_LIMIT = 1000;
const MINIMUM_CONFIRMATIONS = 1;
@ -145,25 +146,17 @@ Future<void> executeNative(Map<String, dynamic> arguments) async {
final setDataMap = arguments['setDataMap'] as Map;
final usedSerialNumbers = arguments['usedSerialNumbers'] as List?;
final mnemonic = arguments['mnemonic'] as String;
final transactionData =
arguments['transactionData'] as models.TransactionData;
final currency = arguments['currency'] as String;
final coin = arguments['coin'] as Coin;
final network = arguments['network'] as NetworkType?;
final currentPrice = arguments['currentPrice'] as Decimal;
final locale = arguments['locale'] as String;
if (!(usedSerialNumbers == null || network == null)) {
var restoreData = await isolateRestore(
mnemonic,
transactionData,
currency,
coin,
latestSetId,
setDataMap,
usedSerialNumbers,
network,
currentPrice,
locale);
mnemonic,
coin,
latestSetId,
setDataMap,
usedSerialNumbers,
network,
);
sendPort.send(restoreData);
return;
}
@ -240,15 +233,11 @@ Future<Map<String, dynamic>> isolateDerive(
Future<Map<String, dynamic>> isolateRestore(
String mnemonic,
models.TransactionData data,
String currency,
Coin coin,
int _latestSetId,
Map<dynamic, dynamic> _setDataMap,
List<dynamic> _usedSerialNumbers,
NetworkType network,
Decimal currentPrice,
String locale,
) async {
List<int> jindexes = [];
List<Map<dynamic, LelantusCoin>> lelantusCoins = [];
@ -373,6 +362,20 @@ Future<Map<String, dynamic>> isolateRestore(
result['_lelantus_coins'] = lelantusCoins;
result['mintIndex'] = lastFoundIndex + 1;
result['jindex'] = jindexes;
result['spendTxIds'] = spendTxIds;
return result;
}
Future<Map<dynamic, dynamic>> staticProcessRestore(
models.TransactionData data,
Map<dynamic, dynamic> result,
) async {
List<dynamic>? _l = result['_lelantus_coins'] as List?;
final List<Map<dynamic, LelantusCoin>> lelantusCoins = [];
for (var el in _l ?? []) {
lelantusCoins.add({el.keys.first: el.values.first as LelantusCoin});
}
// Edit the receive transactions with the mint fees.
Map<String, models.Transaction> editedTransactions =
@ -437,7 +440,6 @@ Future<Map<String, dynamic>> isolateRestore(
(value.height == -1 && !value.confirmedStatus));
result['newTxMap'] = transactionMap;
result['spendTxIds'] = spendTxIds;
return result;
}
@ -1125,7 +1127,7 @@ class FiroWallet extends CoinServiceAPI {
final balance =
Format.decimalAmountToSatoshis(await availablePrivateBalance());
if (satoshiAmount == balance) {
print("is send all");
// print("is send all");
isSendAll = true;
}
dynamic txHexOrError =
@ -2193,7 +2195,7 @@ class FiroWallet extends CoinServiceAPI {
final newTxData = _fetchTransactionData();
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.35, walletId));
final FeeObject feeObj = await _getFees();
final feeObj = _getFees();
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.50, walletId));
_utxoData = Future(() => newUtxoData);
@ -2217,9 +2219,6 @@ class FiroWallet extends CoinServiceAPI {
GlobalEventBus.instance.fire(RefreshPercentChangedEvent(0.95, walletId));
final maxFee = await _fetchMaxFee();
_maxFee = Future(() => maxFee);
var txData = (await _txnData);
var lTxData = (await lelantusTransactionData);
await getAllTxsToWatch(txData, lTxData);
@ -2236,7 +2235,7 @@ class FiroWallet extends CoinServiceAPI {
refreshMutex = false;
if (isActive || shouldAutoSync) {
timer ??= Timer.periodic(const Duration(seconds: 150), (timer) async {
timer ??= Timer.periodic(const Duration(seconds: 30), (timer) async {
bool shouldNotify = await refreshIfThereIsNewData();
if (shouldNotify) {
await refresh();
@ -2744,7 +2743,7 @@ class FiroWallet extends CoinServiceAPI {
};
}
Future<void> _refreshLelantusData() async {
Future<models.TransactionData> _refreshLelantusData() async {
final List<Map<dynamic, LelantusCoin>> lelantusCoins = getLelantusCoinMap();
final jindexes =
DB.instance.get<dynamic>(boxName: walletId, key: 'jindex') as List?;
@ -2797,18 +2796,18 @@ class FiroWallet extends CoinServiceAPI {
final listTxData = txData.getAllTransactions();
listTxData.forEach((key, value) {
// ignore change addresses
bool hasAtLeastOneReceive = false;
// bool hasAtLeastOneReceive = false;
// int howManyReceiveInputs = 0;
for (var element in value.inputs) {
if (listLelantusTxData.containsKey(element.txid) &&
listLelantusTxData[element.txid]!.txType == "Received"
// &&
// listLelantusTxData[element.txid].subType != "mint"
) {
hasAtLeastOneReceive = true;
// howManyReceiveInputs++;
}
}
// for (var element in value.inputs) {
// if (listLelantusTxData.containsKey(element.txid) &&
// listLelantusTxData[element.txid]!.txType == "Received"
// // &&
// // listLelantusTxData[element.txid].subType != "mint"
// ) {
// // hasAtLeastOneReceive = true;
// // howManyReceiveInputs++;
// }
// }
if (value.txType == "Received" && value.subType != "mint") {
// Every receive other than a mint should be shown. Mints will be collected and shown from the send side
@ -2826,6 +2825,7 @@ class FiroWallet extends CoinServiceAPI {
_lelantusTransactionData = Future(() => newTxData);
await DB.instance.put<dynamic>(
boxName: walletId, key: 'latest_lelantus_tx_model', value: newTxData);
return newTxData;
}
Future<String> _getMintHex(int amount, int index) async {
@ -3122,24 +3122,44 @@ class FiroWallet extends CoinServiceAPI {
List<String> allAddresses) async {
try {
List<Map<String, dynamic>> allTxHashes = [];
// int latestTxnBlockHeight = 0;
for (final address in allAddresses) {
final scripthash = AddressUtils.convertToScriptHash(address, _network);
final txs = await electrumXClient.getHistory(scripthash: scripthash);
for (final map in txs) {
if (!allTxHashes.contains(map)) {
map['address'] = address;
allTxHashes.add(map);
final Map<int, Map<String, List<dynamic>>> batches = {};
final Map<String, String> requestIdToAddressMap = {};
const batchSizeMax = 100;
int batchNumber = 0;
for (int i = 0; i < allAddresses.length; i++) {
if (batches[batchNumber] == null) {
batches[batchNumber] = {};
}
final scripthash =
AddressUtils.convertToScriptHash(allAddresses[i], _network);
final id = Logger.isTestEnv ? "$i" : const Uuid().v1();
requestIdToAddressMap[id] = allAddresses[i];
batches[batchNumber]!.addAll({
id: [scripthash]
});
if (i % batchSizeMax == batchSizeMax - 1) {
batchNumber++;
}
}
for (int i = 0; i < batches.length; i++) {
final response =
await _electrumXClient.getBatchHistory(args: batches[i]!);
for (final entry in response.entries) {
for (int j = 0; j < entry.value.length; j++) {
entry.value[j]["address"] = requestIdToAddressMap[entry.key];
if (!allTxHashes.contains(entry.value[j])) {
allTxHashes.add(entry.value[j]);
}
}
}
}
return allTxHashes;
} catch (e, s) {
Logging.instance.log("Exception caught in _fetchHistory(): $e\n$s",
level: LogLevel.Error);
return [];
Logging.instance.log("_fetchHistory: $e\n$s", level: LogLevel.Error);
rethrow;
}
}
@ -3178,20 +3198,11 @@ class FiroWallet extends CoinServiceAPI {
}
}
List<Map<String, dynamic>> allTransactions = [];
for (final txHash in allTxHashes) {
final tx = await cachedElectrumXClient.getTransaction(
txHash: txHash["tx_hash"] as String,
verbose: true,
coin: coin,
);
// delete unused large parts
tx.remove("hex");
tx.remove("lelantusData");
allTransactions.add(tx);
List<String> hashes = [];
for (var element in allTxHashes) {
hashes.add(element['tx_hash'] as String);
}
List<Map<String, dynamic>> allTransactions = await fastFetch(hashes);
Logging.instance.log("allTransactions length: ${allTransactions.length}",
level: LogLevel.Info);
@ -3441,81 +3452,83 @@ class FiroWallet extends CoinServiceAPI {
}
Future<UtxoData> _fetchUtxoData() async {
final List<String> allAddresses = [];
final receivingAddresses =
DB.instance.get<dynamic>(boxName: walletId, key: 'receivingAddresses')
as List<dynamic>;
final changeAddresses =
DB.instance.get<dynamic>(boxName: walletId, key: 'changeAddresses')
as List<dynamic>;
for (var i = 0; i < receivingAddresses.length; i++) {
if (!allAddresses.contains(receivingAddresses[i])) {
allAddresses.add(receivingAddresses[i] as String);
}
}
for (var i = 0; i < changeAddresses.length; i++) {
if (!allAddresses.contains(changeAddresses[i])) {
allAddresses.add(changeAddresses[i] as String);
}
}
final List<String> allAddresses = await _fetchAllOwnAddresses();
try {
final utxoData = <List<Map<String, dynamic>>>[];
final fetchedUtxoList = <List<Map<String, dynamic>>>[];
final Map<int, Map<String, List<dynamic>>> batches = {};
const batchSizeMax = 100;
int batchNumber = 0;
for (int i = 0; i < allAddresses.length; i++) {
if (batches[batchNumber] == null) {
batches[batchNumber] = {};
}
final scripthash =
AddressUtils.convertToScriptHash(allAddresses[i], _network);
final utxos = await electrumXClient.getUTXOs(scripthash: scripthash);
if (utxos.isNotEmpty) {
utxoData.add(utxos);
batches[batchNumber]!.addAll({
scripthash: [scripthash]
});
if (i % batchSizeMax == batchSizeMax - 1) {
batchNumber++;
}
}
Decimal currentPrice = await firoPrice;
for (int i = 0; i < batches.length; i++) {
final response =
await _electrumXClient.getBatchUTXOs(args: batches[i]!);
for (final entry in response.entries) {
if (entry.value.isNotEmpty) {
fetchedUtxoList.add(entry.value);
}
}
}
final priceData =
await _priceAPI.getPricesAnd24hChange(baseCurrency: _prefs.currency);
Decimal currentPrice = priceData[coin]?.item1 ?? Decimal.zero;
final List<Map<String, dynamic>> outputArray = [];
int satoshiBalance = 0;
int satoshiBalancePending = 0;
for (int i = 0; i < utxoData.length; i++) {
for (int j = 0; j < utxoData[i].length; j++) {
int value = utxoData[i][j]["value"] as int;
for (int i = 0; i < fetchedUtxoList.length; i++) {
for (int j = 0; j < fetchedUtxoList[i].length; j++) {
int value = fetchedUtxoList[i][j]["value"] as int;
satoshiBalance += value;
final txn = await cachedElectrumXClient.getTransaction(
txHash: utxoData[i][j]["tx_hash"] as String,
txHash: fetchedUtxoList[i][j]["tx_hash"] as String,
verbose: true,
coin: coin,
);
final Map<String, dynamic> tx = {};
final Map<String, dynamic> utxo = {};
final int confirmations = txn["confirmations"] as int? ?? 0;
final bool confirmed = confirmations >= MINIMUM_CONFIRMATIONS;
if (!confirmed) {
satoshiBalancePending += value;
}
tx["txid"] = txn["txid"];
tx["vout"] = utxoData[i][j]["tx_pos"];
tx["value"] = value;
utxo["txid"] = txn["txid"];
utxo["vout"] = fetchedUtxoList[i][j]["tx_pos"];
utxo["value"] = value;
tx["status"] = <String, dynamic>{};
tx["status"]["confirmed"] = confirmed;
tx["status"]["confirmations"] = confirmations;
tx["status"]["confirmed"] =
utxo["status"] = <String, dynamic>{};
utxo["status"]["confirmed"] = confirmed;
utxo["status"]["confirmations"] = confirmations;
utxo["status"]["confirmed"] =
txn["confirmations"] == null ? false : txn["confirmations"] > 0;
tx["status"]["block_height"] = txn["height"];
tx["status"]["block_hash"] = txn["blockhash"];
tx["status"]["block_time"] = txn["blocktime"];
utxo["status"]["block_height"] = fetchedUtxoList[i][j]["height"];
utxo["status"]["block_hash"] = txn["blockhash"];
utxo["status"]["block_time"] = txn["blocktime"];
final fiatValue = ((Decimal.fromInt(value) * currentPrice) /
Decimal.fromInt(Constants.satsPerCoin))
.toDecimal(scaleOnInfinitePrecision: 2);
tx["rawWorth"] = fiatValue;
tx["fiatWorth"] = fiatValue.toString();
tx["is_coinbase"] = txn['vin'][0]['coinbase'] != null;
outputArray.add(tx);
utxo["rawWorth"] = fiatValue;
utxo["fiatWorth"] = fiatValue.toString();
utxo["is_coinbase"] = txn['vin'][0]['coinbase'] != null;
outputArray.add(utxo);
}
}
@ -3538,13 +3551,19 @@ class FiroWallet extends CoinServiceAPI {
final dataModel = UtxoData.fromJson(result);
final List<UtxoObject> allOutputs = dataModel.unspentOutputArray;
// Logging.instance.log('Outputs fetched: $allOutputs');
Logging.instance
.log('Outputs fetched: $allOutputs', level: LogLevel.Info);
await _sortOutputs(allOutputs);
await DB.instance.put<dynamic>(
boxName: walletId, key: 'latest_utxo_model', value: dataModel);
// await DB.instance.put<dynamic>(
// boxName: walletId,
// key: 'totalBalance',
// value: dataModel.satoshiBalance);
return dataModel;
} catch (e) {
// Logging.instance.log("Output fetch unsuccessful: $e\n$s");
} catch (e, s) {
Logging.instance
.log("Output fetch unsuccessful: $e\n$s", level: LogLevel.Error);
final latestTxModel =
DB.instance.get<dynamic>(boxName: walletId, key: 'latest_utxo_model')
as models.UtxoData?;
@ -4056,6 +4075,135 @@ class FiroWallet extends CoinServiceAPI {
bool longMutex = false;
Future<Map<int, dynamic>> getSetDataMap(int latestSetId) async {
final Map<int, dynamic> setDataMap = {};
final anonymitySets = await fetchAnonymitySets();
for (int setId = 1; setId <= latestSetId; setId++) {
final setData = anonymitySets
.firstWhere((element) => element["setId"] == setId, orElse: () => {});
if (setData.isNotEmpty) {
setDataMap[setId] = setData;
}
}
return setDataMap;
}
Future<void> _makeDerivations(
String suppliedMnemonic, int maxUnusedAddressGap) async {
List<String> receivingAddressArray = [];
List<String> changeAddressArray = [];
int receivingIndex = -1;
int changeIndex = -1;
// The gap limit will be capped at 20
int receivingGapCounter = 0;
int changeGapCounter = 0;
await fillAddresses(suppliedMnemonic,
numberOfThreads: Platform.numberOfProcessors - isolates.length - 1);
final receiveDerivationsString =
await _secureStore.read(key: "${walletId}_receiveDerivations");
final changeDerivationsString =
await _secureStore.read(key: "${walletId}_changeDerivations");
final receiveDerivations = Map<String, dynamic>.from(
jsonDecode(receiveDerivationsString ?? "{}") as Map);
final changeDerivations = Map<String, dynamic>.from(
jsonDecode(changeDerivationsString ?? "{}") as Map);
// log("rcv: $receiveDerivations");
// log("chg: $changeDerivations");
// Deriving and checking for receiving addresses
for (var i = 0; i < receiveDerivations.length; i++) {
// Break out of loop when receivingGapCounter hits maxUnusedAddressGap
// Same gap limit for change as for receiving, breaks when it hits maxUnusedAddressGap
if (receivingGapCounter >= maxUnusedAddressGap &&
changeGapCounter >= maxUnusedAddressGap) {
break;
}
final receiveDerivation = receiveDerivations["$i"];
final address = receiveDerivation['address'] as String;
final changeDerivation = changeDerivations["$i"];
final _address = changeDerivation['address'] as String;
Future<int>? futureNumTxs;
Future<int>? _futureNumTxs;
if (receivingGapCounter < maxUnusedAddressGap) {
futureNumTxs = _getReceivedTxCount(address: address);
}
if (changeGapCounter < maxUnusedAddressGap) {
_futureNumTxs = _getReceivedTxCount(address: _address);
}
try {
if (futureNumTxs != null) {
int numTxs = await futureNumTxs;
if (numTxs >= 1) {
receivingIndex = i;
receivingAddressArray.add(address);
} else if (numTxs == 0) {
receivingGapCounter += 1;
}
}
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from recoverWalletFromBIP32SeedPhrase(): $e\n$s",
level: LogLevel.Error);
rethrow;
}
try {
if (_futureNumTxs != null) {
int numTxs = await _futureNumTxs;
if (numTxs >= 1) {
changeIndex = i;
changeAddressArray.add(_address);
} else if (numTxs == 0) {
changeGapCounter += 1;
}
}
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from recoverWalletFromBIP32SeedPhrase(): $e\n$s",
level: LogLevel.Error);
rethrow;
}
}
// If restoring a wallet that never received any funds, then set receivingArray manually
// If we didn't do this, it'd store an empty array
if (receivingIndex == -1) {
final String receivingAddress = await _generateAddressForChain(0, 0);
receivingAddressArray.add(receivingAddress);
}
// If restoring a wallet that never sent any funds with change, then set changeArray
// manually. If we didn't do this, it'd store an empty array.
if (changeIndex == -1) {
final String changeAddress = await _generateAddressForChain(1, 0);
changeAddressArray.add(changeAddress);
}
await DB.instance.put<dynamic>(
boxName: walletId,
key: 'receivingAddresses',
value: receivingAddressArray);
await DB.instance.put<dynamic>(
boxName: walletId, key: 'changeAddresses', value: changeAddressArray);
await DB.instance.put<dynamic>(
boxName: walletId,
key: 'receivingIndex',
value: receivingIndex == -1 ? 0 : receivingIndex);
await DB.instance.put<dynamic>(
boxName: walletId,
key: 'changeIndex',
value: changeIndex == -1 ? 0 : changeIndex);
}
/// Recovers wallet from [suppliedMnemonic]. Expects a valid mnemonic.
Future<void> _recoverWalletFromBIP32SeedPhrase(
String suppliedMnemonic, int maxUnusedAddressGap) async {
@ -4063,137 +4211,20 @@ class FiroWallet extends CoinServiceAPI {
Logging.instance
.log("PROCESSORS ${Platform.numberOfProcessors}", level: LogLevel.Info);
try {
final Map<int, dynamic> setDataMap = {};
final latestSetId = await getLatestSetId();
final anonymitySets = await fetchAnonymitySets();
for (int setId = 1; setId <= latestSetId; setId++) {
final setData = anonymitySets.firstWhere(
(element) => element["setId"] == setId,
orElse: () => {});
if (setData.isNotEmpty) {
setDataMap[setId] = setData;
}
}
final setDataMap = getSetDataMap(latestSetId);
final usedSerialNumbers = getUsedCoinSerials();
final makeDerivations =
_makeDerivations(suppliedMnemonic, maxUnusedAddressGap);
List<String> receivingAddressArray = [];
List<String> changeAddressArray = [];
int receivingIndex = -1;
int changeIndex = -1;
// The gap limit will be capped at 20
int receivingGapCounter = 0;
int changeGapCounter = 0;
await fillAddresses(suppliedMnemonic,
numberOfThreads: Platform.numberOfProcessors - isolates.length - 1);
final receiveDerivationsString =
await _secureStore.read(key: "${walletId}_receiveDerivations");
final changeDerivationsString =
await _secureStore.read(key: "${walletId}_changeDerivations");
final receiveDerivations = Map<String, dynamic>.from(
jsonDecode(receiveDerivationsString ?? "{}") as Map);
final changeDerivations = Map<String, dynamic>.from(
jsonDecode(changeDerivationsString ?? "{}") as Map);
// log("rcv: $receiveDerivations");
// log("chg: $changeDerivations");
// Deriving and checking for receiving addresses
for (var i = 0; i < receiveDerivations.length; i++) {
// Break out of loop when receivingGapCounter hits maxUnusedAddressGap
// Same gap limit for change as for receiving, breaks when it hits maxUnusedAddressGap
if (receivingGapCounter >= maxUnusedAddressGap &&
changeGapCounter >= maxUnusedAddressGap) {
break;
}
final receiveDerivation = receiveDerivations["$i"];
final address = receiveDerivation['address'] as String;
final changeDerivation = changeDerivations["$i"];
final _address = changeDerivation['address'] as String;
Future<int>? futureNumTxs;
Future<int>? _futureNumTxs;
if (receivingGapCounter < maxUnusedAddressGap) {
futureNumTxs = _getReceivedTxCount(address: address);
}
if (changeGapCounter < maxUnusedAddressGap) {
_futureNumTxs = _getReceivedTxCount(address: _address);
}
try {
if (futureNumTxs != null) {
int numTxs = await futureNumTxs;
if (numTxs >= 1) {
receivingIndex = i;
receivingAddressArray.add(address);
} else if (numTxs == 0) {
receivingGapCounter += 1;
}
}
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from recoverWalletFromBIP32SeedPhrase(): $e\n$s",
level: LogLevel.Error);
rethrow;
}
try {
if (_futureNumTxs != null) {
int numTxs = await _futureNumTxs;
if (numTxs >= 1) {
changeIndex = i;
changeAddressArray.add(_address);
} else if (numTxs == 0) {
changeGapCounter += 1;
}
}
} catch (e, s) {
Logging.instance.log(
"Exception rethrown from recoverWalletFromBIP32SeedPhrase(): $e\n$s",
level: LogLevel.Error);
rethrow;
}
}
// If restoring a wallet that never received any funds, then set receivingArray manually
// If we didn't do this, it'd store an empty array
if (receivingIndex == -1) {
final String receivingAddress = await _generateAddressForChain(0, 0);
receivingAddressArray.add(receivingAddress);
}
// If restoring a wallet that never sent any funds with change, then set changeArray
// manually. If we didn't do this, it'd store an empty array.
if (changeIndex == -1) {
final String changeAddress = await _generateAddressForChain(1, 0);
changeAddressArray.add(changeAddress);
}
await DB.instance.put<dynamic>(
boxName: walletId,
key: 'receivingAddresses',
value: receivingAddressArray);
await DB.instance.put<dynamic>(
boxName: walletId, key: 'changeAddresses', value: changeAddressArray);
await DB.instance.put<dynamic>(
boxName: walletId,
key: 'receivingIndex',
value: receivingIndex == -1 ? 0 : receivingIndex);
await DB.instance.put<dynamic>(
boxName: walletId,
key: 'changeIndex',
value: changeIndex == -1 ? 0 : changeIndex);
await DB.instance
.put<dynamic>(boxName: walletId, key: "id", value: _walletId);
await DB.instance
.put<dynamic>(boxName: walletId, key: "isFavorite", value: false);
await _restore(latestSetId, setDataMap, await usedSerialNumbers);
await Future.wait([usedSerialNumbers, setDataMap, makeDerivations]);
await _restore(latestSetId, await setDataMap, await usedSerialNumbers);
longMutex = false;
} catch (e, s) {
longMutex = false;
@ -4207,27 +4238,23 @@ class FiroWallet extends CoinServiceAPI {
Future<void> _restore(int latestSetId, Map<dynamic, dynamic> setDataMap,
dynamic usedSerialNumbers) async {
final mnemonic = await _secureStore.read(key: '${_walletId}_mnemonic');
models.TransactionData data = await _txnData;
final dataFuture = _txnData;
final String currency = _prefs.currency;
final Decimal currentPrice = await firoPrice;
final locale = await Devicelocale.currentLocale;
ReceivePort receivePort = await getIsolate({
"function": "restore",
"mnemonic": mnemonic,
"transactionData": data,
"currency": currency,
"coin": coin,
"latestSetId": latestSetId,
"setDataMap": setDataMap,
"usedSerialNumbers": usedSerialNumbers,
"network": _network,
"currentPrice": currentPrice,
"locale": locale,
});
var message = await receivePort.first;
if (message is String) {
await Future.wait([dataFuture]);
var result = await receivePort.first;
if (result is String) {
Logging.instance
.log("restore() ->> this is a string", level: LogLevel.Error);
stop(receivePort);
@ -4235,6 +4262,9 @@ class FiroWallet extends CoinServiceAPI {
}
stop(receivePort);
final message = await staticProcessRestore(
(await dataFuture), result as Map<dynamic, dynamic>);
await DB.instance.put<dynamic>(
boxName: walletId, key: 'mintIndex', value: message['mintIndex']);
await DB.instance.put<dynamic>(
@ -4274,11 +4304,17 @@ class FiroWallet extends CoinServiceAPI {
final latestSetId = await getLatestSetId();
final List<Map<String, dynamic>> sets = [];
List<Future<Map<String, dynamic>>> anonFutures = [];
for (int i = 1; i <= latestSetId; i++) {
Map<String, dynamic> set = await cachedElectrumXClient.getAnonymitySet(
final set = cachedElectrumXClient.getAnonymitySet(
groupId: "$i",
coin: coin,
);
anonFutures.add(set);
}
await Future.wait(anonFutures);
for (int i = 1; i <= latestSetId; i++) {
Map<String, dynamic> set = (await anonFutures[i - 1]);
set["setId"] = i;
sets.add(set);
}
@ -4567,6 +4603,49 @@ class FiroWallet extends CoinServiceAPI {
return available - estimatedFee;
}
Future<List<Map<String, dynamic>>> fastFetch(List<String> allTxHashes) async {
List<Map<String, dynamic>> allTransactions = [];
const futureLimit = 30;
List<Future<Map<String, dynamic>>> transactionFutures = [];
int currentFutureCount = 0;
for (final txHash in allTxHashes) {
Future<Map<String, dynamic>> transactionFuture =
cachedElectrumXClient.getTransaction(
txHash: txHash,
verbose: true,
coin: coin,
);
transactionFutures.add(transactionFuture);
currentFutureCount++;
if (currentFutureCount > futureLimit) {
currentFutureCount = 0;
await Future.wait(transactionFutures);
for (final fTx in transactionFutures) {
final tx = await fTx;
// delete unused large parts
tx.remove("hex");
tx.remove("lelantusData");
allTransactions.add(tx);
}
}
}
if (currentFutureCount != 0) {
currentFutureCount = 0;
await Future.wait(transactionFutures);
for (final fTx in transactionFutures) {
final tx = await fTx;
// delete unused large parts
tx.remove("hex");
tx.remove("lelantusData");
allTransactions.add(tx);
}
}
return allTransactions;
}
Future<List<models.Transaction>> getJMintTransactions(
CachedElectrumX cachedClient,
List<String> transactions,
@ -4577,14 +4656,12 @@ class FiroWallet extends CoinServiceAPI {
) async {
try {
List<models.Transaction> txs = [];
List<Map<String, dynamic>> allTransactions =
await fastFetch(transactions);
for (int i = 0; i < transactions.length; i++) {
for (int i = 0; i < allTransactions.length; i++) {
try {
final tx = await cachedClient.getTransaction(
txHash: transactions[i],
verbose: true,
coin: coin,
);
final tx = allTransactions[i];
tx["confirmed_status"] =
tx["confirmations"] != null && tx["confirmations"] as int > 0;

View file

@ -34,6 +34,8 @@ class Prefs extends ChangeNotifier {
_backupFrequencyType = await _getBackupFrequencyType();
_lastAutoBackup = await _getLastAutoBackup();
_hideBlockExplorerWarning = await _getHideBlockExplorerWarning();
_gotoWalletOnStartup = await _getGotoWalletOnStartup();
_startupWalletId = await _getStartupWalletId();
_initialized = true;
}
@ -468,8 +470,6 @@ class Prefs extends ChangeNotifier {
boxName: DB.boxNamePrefs, key: "autoBackupFileUri") as DateTime?;
}
// auto backup
bool _hideBlockExplorerWarning = false;
@ -480,9 +480,9 @@ class Prefs extends ChangeNotifier {
if (_hideBlockExplorerWarning != hideBlockExplorerWarning) {
DB.instance
.put<dynamic>(
boxName: DB.boxNamePrefs,
key: "hideBlockExplorerWarning",
value: hideBlockExplorerWarning)
boxName: DB.boxNamePrefs,
key: "hideBlockExplorerWarning",
value: hideBlockExplorerWarning)
.then((_) {
_hideBlockExplorerWarning = hideBlockExplorerWarning;
notifyListeners();
@ -492,7 +492,56 @@ class Prefs extends ChangeNotifier {
Future<bool> _getHideBlockExplorerWarning() async {
return await DB.instance.get<dynamic>(
boxName: DB.boxNamePrefs, key: "hideBlockExplorerWarning") as bool? ??
boxName: DB.boxNamePrefs,
key: "hideBlockExplorerWarning") as bool? ??
false;
}
// auto backup
bool _gotoWalletOnStartup = false;
bool get gotoWalletOnStartup => _gotoWalletOnStartup;
set gotoWalletOnStartup(bool gotoWalletOnStartup) {
if (_gotoWalletOnStartup != gotoWalletOnStartup) {
DB.instance
.put<dynamic>(
boxName: DB.boxNamePrefs,
key: "gotoWalletOnStartup",
value: gotoWalletOnStartup)
.then((_) {
_gotoWalletOnStartup = gotoWalletOnStartup;
notifyListeners();
});
}
}
Future<bool> _getGotoWalletOnStartup() async {
return await DB.instance.get<dynamic>(
boxName: DB.boxNamePrefs, key: "gotoWalletOnStartup") as bool? ??
false;
}
// startup wallet id
String? _startupWalletId;
String? get startupWalletId => _startupWalletId;
set startupWalletId(String? startupWalletId) {
if (this.startupWalletId != startupWalletId) {
DB.instance.put<dynamic>(
boxName: DB.boxNamePrefs,
key: "startupWalletId",
value: startupWalletId);
_startupWalletId = startupWalletId;
notifyListeners();
}
}
Future<String?> _getStartupWalletId() async {
return await DB.instance.get<dynamic>(
boxName: DB.boxNamePrefs, key: "startupWalletId") as String?;
}
}

View file

@ -358,6 +358,28 @@ class MockPrefs extends _i1.Mock implements _i5.Prefs {
super.noSuchMethod(Invocation.setter(#lastAutoBackup, lastAutoBackup),
returnValueForMissingStub: null);
@override
bool get hideBlockExplorerWarning =>
(super.noSuchMethod(Invocation.getter(#hideBlockExplorerWarning),
returnValue: false) as bool);
@override
set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) =>
super.noSuchMethod(
Invocation.setter(
#hideBlockExplorerWarning, hideBlockExplorerWarning),
returnValueForMissingStub: null);
@override
bool get gotoWalletOnStartup =>
(super.noSuchMethod(Invocation.getter(#gotoWalletOnStartup),
returnValue: false) as bool);
@override
set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod(
Invocation.setter(#gotoWalletOnStartup, gotoWalletOnStartup),
returnValueForMissingStub: null);
@override
set startupWalletId(String? startupWalletId) =>
super.noSuchMethod(Invocation.setter(#startupWalletId, startupWalletId),
returnValueForMissingStub: null);
@override
bool get hasListeners =>
(super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
as bool);

View file

@ -207,6 +207,28 @@ class MockPrefs extends _i1.Mock implements _i4.Prefs {
super.noSuchMethod(Invocation.setter(#lastAutoBackup, lastAutoBackup),
returnValueForMissingStub: null);
@override
bool get hideBlockExplorerWarning =>
(super.noSuchMethod(Invocation.getter(#hideBlockExplorerWarning),
returnValue: false) as bool);
@override
set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) =>
super.noSuchMethod(
Invocation.setter(
#hideBlockExplorerWarning, hideBlockExplorerWarning),
returnValueForMissingStub: null);
@override
bool get gotoWalletOnStartup =>
(super.noSuchMethod(Invocation.getter(#gotoWalletOnStartup),
returnValue: false) as bool);
@override
set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod(
Invocation.setter(#gotoWalletOnStartup, gotoWalletOnStartup),
returnValueForMissingStub: null);
@override
set startupWalletId(String? startupWalletId) =>
super.noSuchMethod(Invocation.setter(#startupWalletId, startupWalletId),
returnValueForMissingStub: null);
@override
bool get hasListeners =>
(super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
as bool);

View file

@ -8,18 +8,20 @@ import 'package:decimal/decimal.dart' as _i7;
import 'package:http/http.dart' as _i4;
import 'package:mockito/mockito.dart' as _i1;
import 'package:stackwallet/models/exchange/change_now/available_floating_rate_pair.dart'
as _i12;
as _i13;
import 'package:stackwallet/models/exchange/change_now/change_now_response.dart'
as _i2;
import 'package:stackwallet/models/exchange/change_now/cn_exchange_estimate.dart'
as _i9;
import 'package:stackwallet/models/exchange/change_now/currency.dart' as _i6;
import 'package:stackwallet/models/exchange/change_now/estimated_exchange_amount.dart'
as _i8;
import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart'
as _i10;
import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart'
as _i11;
import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart'
as _i12;
import 'package:stackwallet/models/exchange/change_now/fixed_rate_market.dart'
as _i9;
as _i10;
import 'package:stackwallet/services/change_now/change_now.dart' as _i3;
// ignore_for_file: type=lint
@ -98,38 +100,42 @@ class MockChangeNow extends _i1.Mock implements _i3.ChangeNow {
as _i5
.Future<_i2.ChangeNowResponse<_i8.EstimatedExchangeAmount>>);
@override
_i5.Future<_i2.ChangeNowResponse<_i8.EstimatedExchangeAmount>>
getEstimatedFixedRateExchangeAmount(
_i5.Future<_i2.ChangeNowResponse<_i9.CNExchangeEstimate>>
getEstimatedExchangeAmountV2(
{String? fromTicker,
String? toTicker,
_i7.Decimal? fromAmount,
bool? useRateId = true,
_i9.CNEstimateType? fromOrTo,
_i7.Decimal? amount,
String? fromNetwork,
String? toNetwork,
_i9.CNFlowType? flow = _i9.CNFlowType.standard,
String? apiKey}) =>
(super.noSuchMethod(
Invocation.method(#getEstimatedFixedRateExchangeAmount, [], {
Invocation.method(#getEstimatedExchangeAmountV2, [], {
#fromTicker: fromTicker,
#toTicker: toTicker,
#fromAmount: fromAmount,
#useRateId: useRateId,
#fromOrTo: fromOrTo,
#amount: amount,
#fromNetwork: fromNetwork,
#toNetwork: toNetwork,
#flow: flow,
#apiKey: apiKey
}),
returnValue: Future<
_i2.ChangeNowResponse<
_i8.EstimatedExchangeAmount>>.value(
_FakeChangeNowResponse_0<_i8.EstimatedExchangeAmount>()))
as _i5
.Future<_i2.ChangeNowResponse<_i8.EstimatedExchangeAmount>>);
_i2.ChangeNowResponse<_i9.CNExchangeEstimate>>.value(
_FakeChangeNowResponse_0<_i9.CNExchangeEstimate>()))
as _i5.Future<_i2.ChangeNowResponse<_i9.CNExchangeEstimate>>);
@override
_i5.Future<_i2.ChangeNowResponse<List<_i9.FixedRateMarket>>>
_i5.Future<_i2.ChangeNowResponse<List<_i10.FixedRateMarket>>>
getAvailableFixedRateMarkets({String? apiKey}) => (super.noSuchMethod(
Invocation.method(
#getAvailableFixedRateMarkets, [], {#apiKey: apiKey}),
returnValue:
Future<_i2.ChangeNowResponse<List<_i9.FixedRateMarket>>>.value(
_FakeChangeNowResponse_0<List<_i9.FixedRateMarket>>())) as _i5
.Future<_i2.ChangeNowResponse<List<_i9.FixedRateMarket>>>);
Future<_i2.ChangeNowResponse<List<_i10.FixedRateMarket>>>.value(
_FakeChangeNowResponse_0<List<_i10.FixedRateMarket>>())) as _i5
.Future<_i2.ChangeNowResponse<List<_i10.FixedRateMarket>>>);
@override
_i5.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>
_i5.Future<_i2.ChangeNowResponse<_i11.ExchangeTransaction>>
createStandardExchangeTransaction(
{String? fromTicker,
String? toTicker,
@ -155,11 +161,11 @@ class MockChangeNow extends _i1.Mock implements _i3.ChangeNow {
#apiKey: apiKey
}),
returnValue: Future<
_i2.ChangeNowResponse<_i10.ExchangeTransaction>>.value(
_FakeChangeNowResponse_0<_i10.ExchangeTransaction>())) as _i5
.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>);
_i2.ChangeNowResponse<_i11.ExchangeTransaction>>.value(
_FakeChangeNowResponse_0<_i11.ExchangeTransaction>())) as _i5
.Future<_i2.ChangeNowResponse<_i11.ExchangeTransaction>>);
@override
_i5.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>
_i5.Future<_i2.ChangeNowResponse<_i11.ExchangeTransaction>>
createFixedRateExchangeTransaction(
{String? fromTicker,
String? toTicker,
@ -187,26 +193,26 @@ class MockChangeNow extends _i1.Mock implements _i3.ChangeNow {
#apiKey: apiKey
}),
returnValue: Future<
_i2.ChangeNowResponse<_i10.ExchangeTransaction>>.value(
_FakeChangeNowResponse_0<_i10.ExchangeTransaction>())) as _i5
.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>);
_i2.ChangeNowResponse<_i11.ExchangeTransaction>>.value(
_FakeChangeNowResponse_0<_i11.ExchangeTransaction>())) as _i5
.Future<_i2.ChangeNowResponse<_i11.ExchangeTransaction>>);
@override
_i5.Future<_i2.ChangeNowResponse<_i11.ExchangeTransactionStatus>>
_i5.Future<_i2.ChangeNowResponse<_i12.ExchangeTransactionStatus>>
getTransactionStatus({String? id, String? apiKey}) => (super.noSuchMethod(
Invocation.method(
#getTransactionStatus, [], {#id: id, #apiKey: apiKey}),
returnValue:
Future<_i2.ChangeNowResponse<_i11.ExchangeTransactionStatus>>.value(
_FakeChangeNowResponse_0<_i11.ExchangeTransactionStatus>())) as _i5
.Future<_i2.ChangeNowResponse<_i11.ExchangeTransactionStatus>>);
Future<_i2.ChangeNowResponse<_i12.ExchangeTransactionStatus>>.value(
_FakeChangeNowResponse_0<_i12.ExchangeTransactionStatus>())) as _i5
.Future<_i2.ChangeNowResponse<_i12.ExchangeTransactionStatus>>);
@override
_i5.Future<_i2.ChangeNowResponse<List<_i12.AvailableFloatingRatePair>>>
_i5.Future<_i2.ChangeNowResponse<List<_i13.AvailableFloatingRatePair>>>
getAvailableFloatingRatePairs({bool? includePartners = false}) => (super
.noSuchMethod(
Invocation.method(#getAvailableFloatingRatePairs, [],
{#includePartners: includePartners}),
returnValue:
Future<_i2.ChangeNowResponse<List<_i12.AvailableFloatingRatePair>>>.value(
_FakeChangeNowResponse_0<List<_i12.AvailableFloatingRatePair>>())) as _i5
.Future<_i2.ChangeNowResponse<List<_i12.AvailableFloatingRatePair>>>);
Future<_i2.ChangeNowResponse<List<_i13.AvailableFloatingRatePair>>>.value(
_FakeChangeNowResponse_0<List<_i13.AvailableFloatingRatePair>>())) as _i5
.Future<_i2.ChangeNowResponse<List<_i13.AvailableFloatingRatePair>>>);
}

View file

@ -334,6 +334,10 @@ class MockManager extends _i1.Mock implements _i11.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i8.Future<int>);
@override
_i8.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i8.Future<bool>);
@override
void addListener(_i10.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -315,6 +315,10 @@ class MockManager extends _i1.Mock implements _i9.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -313,6 +313,10 @@ class MockManager extends _i1.Mock implements _i9.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -9,18 +9,20 @@ import 'package:decimal/decimal.dart' as _i15;
import 'package:http/http.dart' as _i13;
import 'package:mockito/mockito.dart' as _i1;
import 'package:stackwallet/models/exchange/change_now/available_floating_rate_pair.dart'
as _i19;
as _i20;
import 'package:stackwallet/models/exchange/change_now/change_now_response.dart'
as _i2;
import 'package:stackwallet/models/exchange/change_now/cn_exchange_estimate.dart'
as _i17;
import 'package:stackwallet/models/exchange/change_now/currency.dart' as _i14;
import 'package:stackwallet/models/exchange/change_now/estimated_exchange_amount.dart'
as _i16;
import 'package:stackwallet/models/exchange/change_now/exchange_transaction.dart'
as _i10;
import 'package:stackwallet/models/exchange/change_now/exchange_transaction_status.dart'
as _i18;
as _i19;
import 'package:stackwallet/models/exchange/change_now/fixed_rate_market.dart'
as _i17;
as _i18;
import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'
as _i5;
import 'package:stackwallet/services/change_now/change_now.dart' as _i12;
@ -183,6 +185,28 @@ class MockPrefs extends _i1.Mock implements _i3.Prefs {
super.noSuchMethod(Invocation.setter(#lastAutoBackup, lastAutoBackup),
returnValueForMissingStub: null);
@override
bool get hideBlockExplorerWarning =>
(super.noSuchMethod(Invocation.getter(#hideBlockExplorerWarning),
returnValue: false) as bool);
@override
set hideBlockExplorerWarning(bool? hideBlockExplorerWarning) =>
super.noSuchMethod(
Invocation.setter(
#hideBlockExplorerWarning, hideBlockExplorerWarning),
returnValueForMissingStub: null);
@override
bool get gotoWalletOnStartup =>
(super.noSuchMethod(Invocation.getter(#gotoWalletOnStartup),
returnValue: false) as bool);
@override
set gotoWalletOnStartup(bool? gotoWalletOnStartup) => super.noSuchMethod(
Invocation.setter(#gotoWalletOnStartup, gotoWalletOnStartup),
returnValueForMissingStub: null);
@override
set startupWalletId(String? startupWalletId) =>
super.noSuchMethod(Invocation.setter(#startupWalletId, startupWalletId),
returnValueForMissingStub: null);
@override
bool get hasListeners =>
(super.noSuchMethod(Invocation.getter(#hasListeners), returnValue: false)
as bool);
@ -386,36 +410,40 @@ class MockChangeNow extends _i1.Mock implements _i12.ChangeNow {
as _i7
.Future<_i2.ChangeNowResponse<_i16.EstimatedExchangeAmount>>);
@override
_i7.Future<_i2.ChangeNowResponse<_i16.EstimatedExchangeAmount>>
getEstimatedFixedRateExchangeAmount(
_i7.Future<_i2.ChangeNowResponse<_i17.CNExchangeEstimate>>
getEstimatedExchangeAmountV2(
{String? fromTicker,
String? toTicker,
_i15.Decimal? fromAmount,
bool? useRateId = true,
_i17.CNEstimateType? fromOrTo,
_i15.Decimal? amount,
String? fromNetwork,
String? toNetwork,
_i17.CNFlowType? flow = _i17.CNFlowType.standard,
String? apiKey}) =>
(super.noSuchMethod(
Invocation.method(#getEstimatedFixedRateExchangeAmount, [], {
Invocation.method(#getEstimatedExchangeAmountV2, [], {
#fromTicker: fromTicker,
#toTicker: toTicker,
#fromAmount: fromAmount,
#useRateId: useRateId,
#fromOrTo: fromOrTo,
#amount: amount,
#fromNetwork: fromNetwork,
#toNetwork: toNetwork,
#flow: flow,
#apiKey: apiKey
}),
returnValue: Future<
_i2.ChangeNowResponse<
_i16.EstimatedExchangeAmount>>.value(
_FakeChangeNowResponse_0<_i16.EstimatedExchangeAmount>()))
as _i7
.Future<_i2.ChangeNowResponse<_i16.EstimatedExchangeAmount>>);
_i2.ChangeNowResponse<_i17.CNExchangeEstimate>>.value(
_FakeChangeNowResponse_0<_i17.CNExchangeEstimate>()))
as _i7.Future<_i2.ChangeNowResponse<_i17.CNExchangeEstimate>>);
@override
_i7.Future<_i2.ChangeNowResponse<List<_i17.FixedRateMarket>>>
_i7.Future<_i2.ChangeNowResponse<List<_i18.FixedRateMarket>>>
getAvailableFixedRateMarkets({String? apiKey}) => (super.noSuchMethod(
Invocation.method(
#getAvailableFixedRateMarkets, [], {#apiKey: apiKey}),
returnValue:
Future<_i2.ChangeNowResponse<List<_i17.FixedRateMarket>>>.value(
_FakeChangeNowResponse_0<List<_i17.FixedRateMarket>>())) as _i7
.Future<_i2.ChangeNowResponse<List<_i17.FixedRateMarket>>>);
Future<_i2.ChangeNowResponse<List<_i18.FixedRateMarket>>>.value(
_FakeChangeNowResponse_0<List<_i18.FixedRateMarket>>())) as _i7
.Future<_i2.ChangeNowResponse<List<_i18.FixedRateMarket>>>);
@override
_i7.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>
createStandardExchangeTransaction(
@ -479,22 +507,22 @@ class MockChangeNow extends _i1.Mock implements _i12.ChangeNow {
_FakeChangeNowResponse_0<_i10.ExchangeTransaction>())) as _i7
.Future<_i2.ChangeNowResponse<_i10.ExchangeTransaction>>);
@override
_i7.Future<_i2.ChangeNowResponse<_i18.ExchangeTransactionStatus>>
_i7.Future<_i2.ChangeNowResponse<_i19.ExchangeTransactionStatus>>
getTransactionStatus({String? id, String? apiKey}) => (super.noSuchMethod(
Invocation.method(
#getTransactionStatus, [], {#id: id, #apiKey: apiKey}),
returnValue:
Future<_i2.ChangeNowResponse<_i18.ExchangeTransactionStatus>>.value(
_FakeChangeNowResponse_0<_i18.ExchangeTransactionStatus>())) as _i7
.Future<_i2.ChangeNowResponse<_i18.ExchangeTransactionStatus>>);
Future<_i2.ChangeNowResponse<_i19.ExchangeTransactionStatus>>.value(
_FakeChangeNowResponse_0<_i19.ExchangeTransactionStatus>())) as _i7
.Future<_i2.ChangeNowResponse<_i19.ExchangeTransactionStatus>>);
@override
_i7.Future<_i2.ChangeNowResponse<List<_i19.AvailableFloatingRatePair>>>
_i7.Future<_i2.ChangeNowResponse<List<_i20.AvailableFloatingRatePair>>>
getAvailableFloatingRatePairs({bool? includePartners = false}) => (super
.noSuchMethod(
Invocation.method(#getAvailableFloatingRatePairs, [],
{#includePartners: includePartners}),
returnValue:
Future<_i2.ChangeNowResponse<List<_i19.AvailableFloatingRatePair>>>.value(
_FakeChangeNowResponse_0<List<_i19.AvailableFloatingRatePair>>())) as _i7
.Future<_i2.ChangeNowResponse<List<_i19.AvailableFloatingRatePair>>>);
Future<_i2.ChangeNowResponse<List<_i20.AvailableFloatingRatePair>>>.value(
_FakeChangeNowResponse_0<List<_i20.AvailableFloatingRatePair>>())) as _i7
.Future<_i2.ChangeNowResponse<List<_i20.AvailableFloatingRatePair>>>);
}

View file

@ -483,6 +483,10 @@ class MockManager extends _i1.Mock implements _i12.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i9.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -373,6 +373,10 @@ class MockManager extends _i1.Mock implements _i9.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i6.Future<int>);
@override
_i6.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i6.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -373,6 +373,10 @@ class MockManager extends _i1.Mock implements _i9.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i6.Future<int>);
@override
_i6.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i6.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -373,6 +373,10 @@ class MockManager extends _i1.Mock implements _i9.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i6.Future<int>);
@override
_i6.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i6.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -248,6 +248,10 @@ class MockManager extends _i1.Mock implements _i5.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -371,6 +371,10 @@ class MockManager extends _i1.Mock implements _i9.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i6.Future<int>);
@override
_i6.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i6.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -483,6 +483,10 @@ class MockManager extends _i1.Mock implements _i12.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i9.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -399,6 +399,10 @@ class MockManager extends _i1.Mock implements _i12.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i8.Future<int>);
@override
_i8.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i8.Future<bool>);
@override
void addListener(_i11.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -248,6 +248,10 @@ class MockManager extends _i1.Mock implements _i5.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -248,6 +248,10 @@ class MockManager extends _i1.Mock implements _i5.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -360,6 +360,10 @@ class MockManager extends _i1.Mock implements _i11.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i8.Future<int>);
@override
_i8.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i8.Future<bool>);
@override
void addListener(_i10.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -360,6 +360,10 @@ class MockManager extends _i1.Mock implements _i11.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i8.Future<int>);
@override
_i8.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i8.Future<bool>);
@override
void addListener(_i10.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -248,6 +248,10 @@ class MockManager extends _i1.Mock implements _i5.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -248,6 +248,10 @@ class MockManager extends _i1.Mock implements _i5.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -371,6 +371,10 @@ class MockManager extends _i1.Mock implements _i9.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i6.Future<int>);
@override
_i6.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i6.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -527,6 +527,10 @@ class MockManager extends _i1.Mock implements _i15.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i8.Future<int>);
@override
_i8.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i8.Future<bool>);
@override
void addListener(_i14.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -371,6 +371,10 @@ class MockManager extends _i1.Mock implements _i9.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i6.Future<int>);
@override
_i6.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i6.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -250,6 +250,10 @@ class MockManager extends _i1.Mock implements _i5.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -249,6 +249,10 @@ class MockManager extends _i1.Mock implements _i5.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -248,6 +248,10 @@ class MockManager extends _i1.Mock implements _i5.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -270,6 +270,10 @@ class MockManager extends _i1.Mock implements _i8.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i10.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -250,6 +250,10 @@ class MockManager extends _i1.Mock implements _i5.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);

View file

@ -1,98 +1,98 @@
final Map<String, List<dynamic>> historyBatchArgs0 = {
"k_0_0": ["269df6359accfa9b761f22c42bd10dcda6fbc6ad708476d2e1efd4b3aa3f6e2f"],
"k_0_1": ["a328ae88ebce63c0010709ae900c199df2b585cdebce53a6291886dfdcc28c63"],
"k_0_2": ["16a765b755fcd070f5196f5a31d6370a799c5f2adac922bff8768f1c4e710b25"],
"k_0_3": ["f3baba5f725918c2038e7a80539088b2c0c636c6a27afb52b94b908241c7a5f8"],
"k_0_4": ["e72d39bd9e7b455503a2acae33647cb5ce33c1dc99ef9ebed0e7501b5e387f86"],
"k_0_5": ["7d9beb602c191843af7e0aef8d588f01bb9c5e2252a3eb29d92bb05833f924df"],
"k_0_6": ["6ab497d1554279dc0723ba1513ff687991454e6f720a79de2c1403b0de7fef73"],
"k_0_7": ["fb33f2529224ddbb65056dd98296f5b9aad5fd31d2ec4db51520e38809d4ec38"],
"k_0_8": ["e7cb000615b4ecf2d483279fa437f37acd9deab1e9f3f4e94d4419c33e521753"],
"k_0_9": ["d07ba311749923b3d6c2a291a3be9bcaf43e1fb1b97cb1b262c93672eda50823"],
"k_0_10": [
"cae047098603b4f4dedb5a5f7f3a0e0f6f0233eed8e0c06f47855c11ccf0a664"
],
"k_0_11": ["6bdefed2a12896dd9840efd83c265e08f9131406c98839d4089377f517cbb977"]
};
final Map<String, List<dynamic>> historyBatchArgs1 = {
"k_0_0": ["8e33ea48a5fabb82e44e9bb11560e853041eacda2b5e88746331d1104b19a4f9"],
"k_0_1": ["2f18558e5d3015cb6578aee1c3e4b645725fa4e1d26ce22cb31c9949f3b4957c"],
"k_0_2": ["0a9d1dd7a5add91ebff4568ce089d2186f5cb6c5a8e5575a7911fa8ecdfbe812"],
"k_0_3": ["764109deac38c18dddd859f5a00858d4a96ff581a6cc53b17690656d7a34f93c"],
"k_0_4": ["da6c2426877e96589922557d776960ba128d5b1cea6c10cde5e13139e9995acb"],
"k_0_5": ["0a97fa18f6b11103b75006601a8f0c172bf20f79b9e81c95c0e8a5209f9ca573"],
"k_0_6": ["3f2403aee4715d46734dc026a6a6a095d417a6f15cb7144eb1ebdc8891ebb462"],
"k_0_7": ["e44745af4355eac1eda621c8fd17668b2d9ab3329af51abf6f5dfb9baf637a07"],
"k_0_8": ["947757dab98934e87c70171b67160777658eeccf9b74f3d6d9ad3f5ef14d0445"],
"k_0_9": ["b8401c59efd8d163fadc70f39cb02abcd8f8682cc3d5bfa8663c967ea1a6b962"],
"k_0_10": [
"92edcb3e9da95893192481594ee8e9bef02e941bdf6e957731de967d2265fae6"
],
"k_0_11": ["9dab36382b9ad792fb07816c6196a30a94c33f62814972da06552614ce97eb3a"]
};
final Map<String, List<dynamic>> historyBatchArgs2 = {
"k_0_0": ["dc64e048a76a88a721abe8b1e53d5a44bbbc83ff9b29449dffcd176d43e36e84"],
"s_0_0": ["5d2a7a01f09f3e1df3fb44d25cc53cfd4e6df25b9c3e3f710f4dbc3e2be3a8fc"],
"w_0_0": ["8e33ea48a5fabb82e44e9bb11560e853041eacda2b5e88746331d1104b19a4f9"],
"k_0_1": ["06593b2d896751e8dda288bb6587b6bb6a1dee71d82a85457f5654f781e37b12"],
"s_0_1": ["26f92666caebb9a17b14f5b573b385348cdc80065472b8961091f3226d2f650f"],
"w_0_1": ["2f18558e5d3015cb6578aee1c3e4b645725fa4e1d26ce22cb31c9949f3b4957c"],
"k_0_2": ["1478257da775ac14cc1120d022999fd35a8f51d5037e6fc11a17ca3dff43a28b"],
"s_0_2": ["20eef9f15b0707cb5779f077dadcaf0be96e7212f76557f96e5dc5fb2e5aafa7"],
"w_0_2": ["0a9d1dd7a5add91ebff4568ce089d2186f5cb6c5a8e5575a7911fa8ecdfbe812"],
"k_0_3": ["69e435cb8ba991d2aced5b62271dcd40f0d3b19c38f19d5bc837231b3aef8475"],
"s_0_3": ["eca08355027cb9f27500ea2588ee532b87b1061f1931b00b57244efad2081b99"],
"w_0_3": ["764109deac38c18dddd859f5a00858d4a96ff581a6cc53b17690656d7a34f93c"],
"k_0_4": ["a91771ce1cde7467c044c99165c3bc3239f402d18fe4e0eaf69ddf954f9545b4"],
"s_0_4": ["f85fb2cfd89ca882aa93546ce5c6aaed7eb13bd60417822dbfe8514eaa32260d"],
"w_0_4": ["da6c2426877e96589922557d776960ba128d5b1cea6c10cde5e13139e9995acb"],
"k_0_5": ["d9d5e8d9b3090e310a40fb7ef3a398fbab300df4c4a3f729f7134caae2027a20"],
"s_0_5": ["4e87132185225b4c79d69ca74a1d1ea329b0864af2b2b216c126fda4dfe44bc3"],
"w_0_5": ["0a97fa18f6b11103b75006601a8f0c172bf20f79b9e81c95c0e8a5209f9ca573"],
"k_0_6": ["29099731133ee2536a4f180e618e39669b7ae97867840736e02929d642ae05c9"],
"s_0_6": ["46ade50d7c161264887d93ea77aa86d01c74ce296ce4d6f44a97c82b4023bf5d"],
"w_0_6": ["3f2403aee4715d46734dc026a6a6a095d417a6f15cb7144eb1ebdc8891ebb462"],
"k_0_7": ["9c898b834aeb60230a0c13ce1eec9efad0f6045a5ca4243287ab4c2d8c5484fe"],
"s_0_7": ["22ff3049e6ef43f20289b0ac8c8480c61b5502b32e07d5bb57fa8029d6943596"],
"w_0_7": ["e44745af4355eac1eda621c8fd17668b2d9ab3329af51abf6f5dfb9baf637a07"],
"k_0_8": ["c1d5dfc51ae2bd98c3d7f61ca5187ba414e5aee7f11bbdb20685e66dfa758a63"],
"s_0_8": ["01c387618378a58c221b51ab65a7f8e95a6932cdb0a8634acfe62acee1cb6cdc"],
"w_0_8": ["947757dab98934e87c70171b67160777658eeccf9b74f3d6d9ad3f5ef14d0445"],
"k_0_9": ["1335353bad1e12a2954ffdcc74d734d062a43c9750b7aee7eaa60bfd315fe0a4"],
"s_0_9": ["51928f94d8c56318810e604dfb7b4862755ce5b005ec4242ea46a485e356d24f"],
"w_0_9": ["b8401c59efd8d163fadc70f39cb02abcd8f8682cc3d5bfa8663c967ea1a6b962"],
"k_0_10": [
"77a9b30ea64e6ea73566b850f39c792cc7e31e3134831860b3a7582372036029"
],
"s_0_10": [
"8ea9acdeaa65f2d99ed93a3b0691601e768f6695fb46cd9b1edc0bca10406767"
],
"w_0_10": [
"92edcb3e9da95893192481594ee8e9bef02e941bdf6e957731de967d2265fae6"
],
"k_0_11": [
"f9692ce7c5edf102c891a980c732e2c058694cc9a5bcc92994175f8c21eed6ae"
],
"s_0_11": [
"7cda59a23e03a997b852df04c8368a0e7dba6583b87855c6dbe857254e28805e"
],
"w_0_11": ["9dab36382b9ad792fb07816c6196a30a94c33f62814972da06552614ce97eb3a"]
"k_0_11": ["f9692ce7c5edf102c891a980c732e2c058694cc9a5bcc92994175f8c21eed6ae"]
};
final Map<String, List<dynamic>> historyBatchArgs1 = {
final Map<String, List<dynamic>> historyBatchArgs3 = {
"k_0_0": ["edf1976568f1aa676482c1882d44617a60476a1ceb92dd553143d43b9d44b32a"],
"s_0_0": ["02f31c1a078f4c8c18321538fe8213b4289f9a24e7718096b6e92969438e110f"],
"w_0_0": ["269df6359accfa9b761f22c42bd10dcda6fbc6ad708476d2e1efd4b3aa3f6e2f"],
"k_0_1": ["bf5a6c56814e80eed11e1e459801515f8c2b83da812568aa9dc26e6356f6965b"],
"s_0_1": ["11663d093cb17dfbed4a96d148b22d3e094b31d23c639c2814beb79f2ab0ca75"],
"w_0_1": ["a328ae88ebce63c0010709ae900c199df2b585cdebce53a6291886dfdcc28c63"],
"k_0_2": ["c10be67f5a01256e4c84fc9ed3f2b9f98b2ef20cf27b4ea908bd7aafb1d1f023"],
"s_0_2": ["11f1b4a8d257d865abd2f2a48e779f699c7db528f83ad85d9182156580b5b198"],
"w_0_2": ["16a765b755fcd070f5196f5a31d6370a799c5f2adac922bff8768f1c4e710b25"],
"k_0_3": ["ce24ba1237e2306f7348f5f02e33c66c73c36c09b026ab4f08ef22f9b8a1e984"],
"s_0_3": ["4fed678c085df3073ac86ea88d70f01d6d2a27d48230935b0514c07400b9aa57"],
"w_0_3": ["f3baba5f725918c2038e7a80539088b2c0c636c6a27afb52b94b908241c7a5f8"],
"k_0_4": ["f040d5195d547fc75660ef85417728ffbab6911dea95c49e28c56c589a2d7d9e"],
"s_0_4": ["08598648eaa4193eeedfccb5b98fd291927d762481e637d3fc3633b826f1d11b"],
"w_0_4": ["e72d39bd9e7b455503a2acae33647cb5ce33c1dc99ef9ebed0e7501b5e387f86"],
"k_0_5": ["9766272abb45b0de891ad0f8492a5131e0e5ebc0072b9c1efb23303046bd05a5"],
"s_0_5": ["b5d735762cc4caec5a9445b62e9cdb2219a4d34be9fdf021c7831635e74b22ca"],
"w_0_5": ["7d9beb602c191843af7e0aef8d588f01bb9c5e2252a3eb29d92bb05833f924df"],
"k_0_6": ["e6844bc07ae8834056698ad30d0ada23bec769fdd0b5ffbe20626c7d8693000a"],
"s_0_6": ["8ad2f479c64645d16e6a67f0f8532f621b43c7be8b9ba3cf9a52b36572761900"],
"w_0_6": ["6ab497d1554279dc0723ba1513ff687991454e6f720a79de2c1403b0de7fef73"],
"k_0_7": ["ebff62957000f362d807db28b21af89a5bc9347346fc6434b2fe4e31e1b8dd7c"],
"s_0_7": ["90cf90aef62db7572cb000fe718141bb46086610bbe72779393999f427bef381"],
"w_0_7": ["fb33f2529224ddbb65056dd98296f5b9aad5fd31d2ec4db51520e38809d4ec38"],
"k_0_8": ["51c818cf59c11676df096f5759d0d0c098baefe4e6fd240587cf7d751fe5ef8c"],
"s_0_8": ["21090d0c982699238e4261ae6caa45785e18f0c389e0aa36f708ba88edc0655c"],
"w_0_8": ["e7cb000615b4ecf2d483279fa437f37acd9deab1e9f3f4e94d4419c33e521753"],
"k_0_9": ["3c942fe69ce42a2f59c94d2365fb805988e6e3e72376ff658a6aa0a926e1ff79"],
"s_0_9": ["e446fec7b44710407d1bdd531578916c28333c75553aa6384c74f1474eff758a"],
"w_0_9": ["d07ba311749923b3d6c2a291a3be9bcaf43e1fb1b97cb1b262c93672eda50823"],
"k_0_10": [
"fb29e1b177589820ad4685cc2fa555f170d885366f5046fd043b1891a43fa8ec"
],
"s_0_10": [
"k_0_11": ["e0b955a5d5bde05cb45e0c7ba95afa8e3e09b04a2cab70e8f1383258471d089a"]
};
final Map<String, List<dynamic>> historyBatchArgs4 = {
"k_0_0": ["02f31c1a078f4c8c18321538fe8213b4289f9a24e7718096b6e92969438e110f"],
"k_0_1": ["11663d093cb17dfbed4a96d148b22d3e094b31d23c639c2814beb79f2ab0ca75"],
"k_0_2": ["11f1b4a8d257d865abd2f2a48e779f699c7db528f83ad85d9182156580b5b198"],
"k_0_3": ["4fed678c085df3073ac86ea88d70f01d6d2a27d48230935b0514c07400b9aa57"],
"k_0_4": ["08598648eaa4193eeedfccb5b98fd291927d762481e637d3fc3633b826f1d11b"],
"k_0_5": ["b5d735762cc4caec5a9445b62e9cdb2219a4d34be9fdf021c7831635e74b22ca"],
"k_0_6": ["8ad2f479c64645d16e6a67f0f8532f621b43c7be8b9ba3cf9a52b36572761900"],
"k_0_7": ["90cf90aef62db7572cb000fe718141bb46086610bbe72779393999f427bef381"],
"k_0_8": ["21090d0c982699238e4261ae6caa45785e18f0c389e0aa36f708ba88edc0655c"],
"k_0_9": ["e446fec7b44710407d1bdd531578916c28333c75553aa6384c74f1474eff758a"],
"k_0_10": [
"fd1564d56943bd604d0c246c1e5ab1edc8d3c8152ffea3c519aec8c300731517"
],
"w_0_10": [
"cae047098603b4f4dedb5a5f7f3a0e0f6f0233eed8e0c06f47855c11ccf0a664"
"k_0_11": ["b67bc093b3b10d3aad39ef94e161a5fb4ad72bec0a3423d33c7fbc063e1016cc"]
};
final Map<String, List<dynamic>> historyBatchArgs5 = {
"k_0_0": ["5d2a7a01f09f3e1df3fb44d25cc53cfd4e6df25b9c3e3f710f4dbc3e2be3a8fc"],
"k_0_1": ["26f92666caebb9a17b14f5b573b385348cdc80065472b8961091f3226d2f650f"],
"k_0_2": ["20eef9f15b0707cb5779f077dadcaf0be96e7212f76557f96e5dc5fb2e5aafa7"],
"k_0_3": ["eca08355027cb9f27500ea2588ee532b87b1061f1931b00b57244efad2081b99"],
"k_0_4": ["f85fb2cfd89ca882aa93546ce5c6aaed7eb13bd60417822dbfe8514eaa32260d"],
"k_0_5": ["4e87132185225b4c79d69ca74a1d1ea329b0864af2b2b216c126fda4dfe44bc3"],
"k_0_6": ["46ade50d7c161264887d93ea77aa86d01c74ce296ce4d6f44a97c82b4023bf5d"],
"k_0_7": ["22ff3049e6ef43f20289b0ac8c8480c61b5502b32e07d5bb57fa8029d6943596"],
"k_0_8": ["01c387618378a58c221b51ab65a7f8e95a6932cdb0a8634acfe62acee1cb6cdc"],
"k_0_9": ["51928f94d8c56318810e604dfb7b4862755ce5b005ec4242ea46a485e356d24f"],
"k_0_10": [
"8ea9acdeaa65f2d99ed93a3b0691601e768f6695fb46cd9b1edc0bca10406767"
],
"k_0_11": [
"e0b955a5d5bde05cb45e0c7ba95afa8e3e09b04a2cab70e8f1383258471d089a"
],
"s_0_11": [
"b67bc093b3b10d3aad39ef94e161a5fb4ad72bec0a3423d33c7fbc063e1016cc"
],
"w_0_11": ["6bdefed2a12896dd9840efd83c265e08f9131406c98839d4089377f517cbb977"]
"k_0_11": ["7cda59a23e03a997b852df04c8368a0e7dba6583b87855c6dbe857254e28805e"]
};
final Map<String, List<Map<String, dynamic>>> historyBatchResponse = {
@ -172,3 +172,12 @@ final Map<String, List<Map<String, dynamic>>> emptyHistoryBatchResponse = {
"s_0_11": [],
"w_0_11": []
};
final List<String> activeScriptHashes = [
"11663d093cb17dfbed4a96d148b22d3e094b31d23c639c2814beb79f2ab0ca75",
"06593b2d896751e8dda288bb6587b6bb6a1dee71d82a85457f5654f781e37b12",
"a328ae88ebce63c0010709ae900c199df2b585cdebce53a6291886dfdcc28c63",
"26f92666caebb9a17b14f5b573b385348cdc80065472b8961091f3226d2f650f",
"2f18558e5d3015cb6578aee1c3e4b645725fa4e1d26ce22cb31c9949f3b4957c",
"bf5a6c56814e80eed11e1e459801515f8c2b83da812568aa9dc26e6356f6965b",
];

View file

@ -15,6 +15,7 @@ import 'package:stackwallet/services/price.dart';
import 'package:stackwallet/services/transaction_notification_tracker.dart';
import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'package:tuple/tuple.dart';
import 'bitcoin_history_sample_data.dart';
import 'bitcoin_transaction_data_samples.dart';
@ -2233,6 +2234,14 @@ void main() {
.thenAnswer((_) async => emptyHistoryBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => emptyHistoryBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs2))
.thenAnswer((_) async => emptyHistoryBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs3))
.thenAnswer((_) async => emptyHistoryBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs4))
.thenAnswer((_) async => emptyHistoryBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs5))
.thenAnswer((_) async => emptyHistoryBatchResponse);
// await DB.instance.init();
final wallet = await Hive.openBox(testWalletId);
bool hasThrown = false;
@ -2250,6 +2259,10 @@ void main() {
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1);
expect(secureStore?.interactions, 20);
expect(secureStore?.writes, 7);
@ -2276,6 +2289,14 @@ void main() {
.thenAnswer((_) async => emptyHistoryBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => emptyHistoryBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs2))
.thenAnswer((_) async => emptyHistoryBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs3))
.thenAnswer((_) async => emptyHistoryBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs4))
.thenAnswer((_) async => emptyHistoryBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs5))
.thenAnswer((_) async => emptyHistoryBatchResponse);
final wallet = await Hive.openBox(testWalletId);
@ -2290,6 +2311,10 @@ void main() {
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
@ -2312,8 +2337,27 @@ void main() {
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs2))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs3))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs4))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs5))
.thenAnswer((_) async => historyBatchResponse);
final wallet = await Hive.openBox(testWalletId);
List<dynamic> dynamicArgValues = [];
when(client?.getBatchHistory(args: anyNamed("args")))
.thenAnswer((realInvocation) async {
if (realInvocation.namedArguments.values.first.length == 1) {
dynamicArgValues.add(realInvocation.namedArguments.values.first);
}
return historyBatchResponse;
});
await Hive.openBox<dynamic>(testWalletId);
bool hasThrown = false;
try {
@ -2330,6 +2374,18 @@ void main() {
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1);
for (final arg in dynamicArgValues) {
final map = Map<String, List<dynamic>>.from(arg as Map);
verify(client?.getBatchHistory(args: map)).called(1);
expect(activeScriptHashes.contains(map.values.first.first as String),
true);
}
expect(secureStore?.interactions, 14);
expect(secureStore?.writes, 7);
@ -2356,10 +2412,29 @@ void main() {
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs2))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs3))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs4))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs5))
.thenAnswer((_) async => historyBatchResponse);
when(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoin))
.thenAnswer((realInvocation) async {});
final wallet = await Hive.openBox(testWalletId);
List<dynamic> dynamicArgValues = [];
when(client?.getBatchHistory(args: anyNamed("args")))
.thenAnswer((realInvocation) async {
if (realInvocation.namedArguments.values.first.length == 1) {
dynamicArgValues.add(realInvocation.namedArguments.values.first);
}
return historyBatchResponse;
});
final wallet = await Hive.openBox<dynamic>(testWalletId);
// restore so we have something to rescan
await btc?.recoverFromMnemonic(
@ -2491,9 +2566,21 @@ void main() {
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs2)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs3)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs4)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs5)).called(2);
verify(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoin))
.called(1);
for (final arg in dynamicArgValues) {
final map = Map<String, List<dynamic>>.from(arg as Map);
verify(client?.getBatchHistory(args: map)).called(1);
expect(activeScriptHashes.contains(map.values.first.first as String),
true);
}
expect(secureStore?.writes, 25);
expect(secureStore?.reads, 32);
expect(secureStore?.deletes, 6);
@ -2519,10 +2606,29 @@ void main() {
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs2))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs3))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs4))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs5))
.thenAnswer((_) async => historyBatchResponse);
when(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoin))
.thenAnswer((realInvocation) async {});
final wallet = await Hive.openBox(testWalletId);
List<dynamic> dynamicArgValues = [];
when(client?.getBatchHistory(args: anyNamed("args")))
.thenAnswer((realInvocation) async {
if (realInvocation.namedArguments.values.first.length == 1) {
dynamicArgValues.add(realInvocation.namedArguments.values.first);
}
return historyBatchResponse;
});
final wallet = await Hive.openBox<dynamic>(testWalletId);
// restore so we have something to rescan
await btc?.recoverFromMnemonic(
@ -2624,10 +2730,22 @@ void main() {
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs2)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs3)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs4)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs5)).called(2);
verify(cachedClient?.clearSharedTransactionCache(coin: Coin.bitcoin))
.called(1);
for (final arg in dynamicArgValues) {
final map = Map<String, List<dynamic>>.from(arg as Map);
verify(client?.getBatchHistory(args: map)).called(1);
expect(activeScriptHashes.contains(map.values.first.first as String),
true);
}
expect(secureStore?.writes, 19);
expect(secureStore?.reads, 32);
expect(secureStore?.deletes, 12);
@ -3911,8 +4029,27 @@ void main() {
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs2))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs3))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs4))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs5))
.thenAnswer((_) async => historyBatchResponse);
final wallet = await Hive.openBox(testWalletId);
List<dynamic> dynamicArgValues = [];
when(client?.getBatchHistory(args: anyNamed("args")))
.thenAnswer((realInvocation) async {
if (realInvocation.namedArguments.values.first.length == 1) {
dynamicArgValues.add(realInvocation.namedArguments.values.first);
}
return historyBatchResponse;
});
await Hive.openBox<dynamic>(testWalletId);
when(cachedClient?.getTransaction(
txHash:
@ -3995,6 +4132,18 @@ void main() {
// .called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1);
for (final arg in dynamicArgValues) {
final map = Map<String, List<dynamic>>.from(arg as Map);
verify(client?.getBatchHistory(args: map)).called(1);
expect(activeScriptHashes.contains(map.values.first.first as String),
true);
}
expect(secureStore?.interactions, 20);
expect(secureStore?.writes, 10);
@ -4169,8 +4318,27 @@ void main() {
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs2))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs3))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs4))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs5))
.thenAnswer((_) async => historyBatchResponse);
final wallet = await Hive.openBox(testWalletId);
List<dynamic> dynamicArgValues = [];
when(client?.getBatchHistory(args: anyNamed("args")))
.thenAnswer((realInvocation) async {
if (realInvocation.namedArguments.values.first.length == 1) {
dynamicArgValues.add(realInvocation.namedArguments.values.first);
}
return historyBatchResponse;
});
await Hive.openBox<dynamic>(testWalletId);
// recover to fill data
await btc?.recoverFromMnemonic(
@ -4186,6 +4354,18 @@ void main() {
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs2)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs3)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs4)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs5)).called(1);
for (final arg in dynamicArgValues) {
final map = Map<String, List<dynamic>>.from(arg as Map);
verify(client?.getBatchHistory(args: map)).called(1);
expect(activeScriptHashes.contains(map.values.first.first as String),
true);
}
expect(secureStore?.interactions, 14);
expect(secureStore?.writes, 7);
@ -4198,8 +4378,9 @@ void main() {
verifyNoMoreInteractions(priceAPI);
});
test("refresh wallet throws", () async {
when(client?.getBlockHeadTip()).thenThrow(Exception("some exception"));
test("refresh wallet normally", () async {
when(client?.getBlockHeadTip()).thenAnswer((realInvocation) async =>
{"height": 520481, "hex": "some block hex"});
when(client?.getServerFeatures()).thenAnswer((_) async => {
"hosts": {},
"pruning": null,
@ -4210,14 +4391,23 @@ void main() {
"hash_function": "sha256",
"services": []
});
when(client?.getBatchHistory(args: historyBatchArgs0))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getHistory(scripthash: anyNamed("scripthash")))
.thenThrow(Exception("some exception"));
.thenAnswer((_) async => []);
when(client?.estimateFee(blocks: anyNamed("blocks")))
.thenAnswer((_) async => Decimal.one);
final wallet = await Hive.openBox(testWalletId);
when(priceAPI?.getPricesAnd24hChange(baseCurrency: "USD"))
.thenAnswer((_) async => {Coin.bitcoin: Tuple2(Decimal.one, 0.3)});
final List<dynamic> dynamicArgValues = [];
when(client?.getBatchHistory(args: anyNamed("args")))
.thenAnswer((realInvocation) async {
dynamicArgValues.add(realInvocation.namedArguments.values.first);
return historyBatchResponse;
});
await Hive.openBox<dynamic>(testWalletId);
// recover to fill data
await btc?.recoverFromMnemonic(
@ -4226,83 +4416,35 @@ void main() {
maxNumberOfIndexesToCheck: 1000,
height: 4000);
when(client?.getBatchHistory(args: anyNamed("args")))
.thenAnswer((_) async => {});
when(client?.getBatchUTXOs(args: anyNamed("args")))
.thenAnswer((_) async => emptyHistoryBatchResponse);
await btc?.refresh();
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(4);
verify(client?.estimateFee(blocks: anyNamed("blocks"))).called(3);
verify(client?.getBlockHeadTip()).called(1);
verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(1);
verify(priceAPI?.getPricesAnd24hChange(baseCurrency: "USD")).called(2);
for (final arg in dynamicArgValues) {
final map = Map<String, List<dynamic>>.from(arg as Map);
verify(client?.getBatchHistory(args: map)).called(1);
}
expect(secureStore?.interactions, 14);
expect(secureStore?.writes, 7);
expect(secureStore?.reads, 7);
expect(secureStore?.deletes, 0);
verifyNoMoreInteractions(client);
// verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(tracker);
verifyNoMoreInteractions(priceAPI);
});
// test("refresh wallet normally", () async {
// when(client?.getBlockHeadTip()).thenAnswer((realInvocation) async =>
// {"height": 520481, "hex": "some block hex"});
// when(client?.getServerFeatures()).thenAnswer((_) async => {
// "hosts": {},
// "pruning": null,
// "server_version": "Unit tests",
// "protocol_min": "1.4",
// "protocol_max": "1.4.2",
// "genesis_hash": GENESIS_HASH_MAINNET,
// "hash_function": "sha256",
// "services": []
// });
// when(client?.getBatchHistory(args: historyBatchArgs0))
// .thenAnswer((_) async => historyBatchResponse);
// when(client?.getBatchHistory(args: historyBatchArgs1))
// .thenAnswer((_) async => historyBatchResponse);
// when(client?.getHistory(scripthash: anyNamed("scripthash")))
// .thenAnswer((_) async => []);
// when(client?.estimateFee(blocks: anyNamed("blocks")))
// .thenAnswer((_) async => Decimal.one);
// // when(priceAPI.getBitcoinPrice(baseCurrency: "USD"))
// // .thenAnswer((_) async => Decimal.one);
//
// // recover to fill data
// await btc?.recoverFromMnemonic(
// mnemonic: TEST_MNEMONIC,
// maxUnusedAddressGap: 2,
// maxNumberOfIndexesToCheck: 1000,
// height: 4000);
//
// when(client?.getBatchHistory(args: anyNamed("args")))
// .thenAnswer((_) async => {});
// when(client?.getBatchUTXOs(args: anyNamed("args")))
// .thenAnswer((_) async => emptyHistoryBatchResponse);
//
// await btc?.refresh();
//
// verify(client?.getServerFeatures()).called(1);
// verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
// verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
// verify(client?.getBatchHistory(args: anyNamed("args"))).called(1);
// verify(client?.getBatchUTXOs(args: anyNamed("args"))).called(1);
// verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(4);
// verify(client?.estimateFee(blocks: anyNamed("blocks"))).called(3);
// verify(client?.getBlockHeadTip()).called(1);
// // verify(priceAPI.getBitcoinPrice(baseCurrency: "USD")).called(2);
//
// expect(secureStore?.interactions, 14);
// expect(secureStore?.writes, 7);
// expect(secureStore?.reads, 7);
// expect(secureStore?.deletes, 0);
//
// verifyNoMoreInteractions(client);
// verifyNoMoreInteractions(cachedClient);
// verifyNoMoreInteractions(priceAPI);
// });
tearDown(() async {
await tearDownTestHive();
});

View file

@ -20,8 +20,6 @@ import 'package:stackwallet/utilities/enums/coin_enum.dart';
import 'package:stackwallet/utilities/flutter_secure_storage_interface.dart';
import 'dogecoin_history_sample_data.dart';
import 'dogecoin_transaction_data_samples.dart';
import 'dogecoin_utxo_sample_data.dart';
import 'dogecoin_wallet_test.mocks.dart';
import 'dogecoin_wallet_test_parameters.dart';
@ -1853,102 +1851,6 @@ void main() {
// // verifyNoMoreInteractions(priceAPI);
// // });
test("fullRescan succeeds", () async {
when(client?.getServerFeatures()).thenAnswer((_) async => {
"hosts": {},
"pruning": null,
"server_version": "Unit tests",
"protocol_min": "1.4",
"protocol_max": "1.4.2",
"genesis_hash": GENESIS_HASH_MAINNET,
"hash_function": "sha256",
"services": []
});
when(client?.getBatchHistory(args: historyBatchArgs0))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => historyBatchResponse);
when(cachedClient?.clearSharedTransactionCache(coin: Coin.dogecoin))
.thenAnswer((realInvocation) async {});
final wallet = await Hive.openBox(testWalletId);
// restore so we have something to rescan
await doge?.recoverFromMnemonic(
mnemonic: TEST_MNEMONIC,
maxUnusedAddressGap: 2,
maxNumberOfIndexesToCheck: 1000,
height: 4000);
// fetch valid wallet data
final preReceivingAddressesP2PKH =
await wallet.get('receivingAddressesP2PKH');
final preChangeAddressesP2PKH = await wallet.get('changeAddressesP2PKH');
final preReceivingIndexP2PKH = await wallet.get('receivingIndexP2PKH');
final preChangeIndexP2PKH = await wallet.get('changeIndexP2PKH');
final preUtxoData = await wallet.get('latest_utxo_model');
final preReceiveDerivationsStringP2PKH = await secureStore?.read(
key: "${testWalletId}_receiveDerivationsP2PKH");
final preChangeDerivationsStringP2PKH = await secureStore?.read(
key: "${testWalletId}_changeDerivationsP2PKH");
// destroy the data that the rescan will fix
await wallet.put(
'receivingAddressesP2PKH', ["some address", "some other address"]);
await wallet
.put('changeAddressesP2PKH', ["some address", "some other address"]);
await wallet.put('receivingIndexP2PKH', 123);
await wallet.put('changeIndexP2PKH', 123);
await secureStore?.write(
key: "${testWalletId}_receiveDerivationsP2PKH", value: "{}");
await secureStore?.write(
key: "${testWalletId}_changeDerivationsP2PKH", value: "{}");
bool hasThrown = false;
try {
await doge?.fullRescan(2, 1000);
} catch (_) {
hasThrown = true;
}
expect(hasThrown, false);
// fetch wallet data again
final receivingAddressesP2PKH =
await wallet.get('receivingAddressesP2PKH');
final changeAddressesP2PKH = await wallet.get('changeAddressesP2PKH');
final receivingIndexP2PKH = await wallet.get('receivingIndexP2PKH');
final changeIndexP2PKH = await wallet.get('changeIndexP2PKH');
final utxoData = await wallet.get('latest_utxo_model');
final receiveDerivationsStringP2PKH = await secureStore?.read(
key: "${testWalletId}_receiveDerivationsP2PKH");
final changeDerivationsStringP2PKH = await secureStore?.read(
key: "${testWalletId}_changeDerivationsP2PKH");
expect(preReceivingAddressesP2PKH, receivingAddressesP2PKH);
expect(preChangeAddressesP2PKH, changeAddressesP2PKH);
expect(preReceivingIndexP2PKH, receivingIndexP2PKH);
expect(preChangeIndexP2PKH, changeIndexP2PKH);
expect(preUtxoData, utxoData);
expect(preReceiveDerivationsStringP2PKH, receiveDerivationsStringP2PKH);
expect(preChangeDerivationsStringP2PKH, changeDerivationsStringP2PKH);
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(2);
verify(cachedClient?.clearSharedTransactionCache(coin: Coin.dogecoin))
.called(1);
expect(secureStore?.writes, 9);
expect(secureStore?.reads, 12);
expect(secureStore?.deletes, 2);
verifyNoMoreInteractions(client);
verifyNoMoreInteractions(cachedClient);
verifyNoMoreInteractions(tracker);
verifyNoMoreInteractions(priceAPI);
});
test("get mnemonic list", () async {
when(client?.getServerFeatures()).thenAnswer((_) async => {
"hosts": {},
@ -2118,7 +2020,19 @@ void main() {
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => historyBatchResponse);
final wallet = await Hive.openBox(testWalletId);
when(client?.getBatchHistory(args: {
"0": [
"c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc"
]
})).thenAnswer((realInvocation) async => {"0": []});
when(client?.getBatchHistory(args: {
"0": [
"80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c"
]
})).thenAnswer((realInvocation) async => {"0": []});
final wallet = await Hive.openBox<dynamic>(testWalletId);
bool hasThrown = false;
try {
@ -2135,6 +2049,16 @@ void main() {
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
verify(client?.getBatchHistory(args: {
"0": [
"c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc"
]
})).called(1);
verify(client?.getBatchHistory(args: {
"0": [
"80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c"
]
})).called(1);
expect(secureStore?.interactions, 6);
expect(secureStore?.writes, 3);
@ -2165,7 +2089,19 @@ void main() {
when(cachedClient?.clearSharedTransactionCache(coin: Coin.dogecoin))
.thenAnswer((realInvocation) async {});
final wallet = await Hive.openBox(testWalletId);
when(client?.getBatchHistory(args: {
"0": [
"c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc"
]
})).thenAnswer((realInvocation) async => {"0": []});
when(client?.getBatchHistory(args: {
"0": [
"80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c"
]
})).thenAnswer((realInvocation) async => {"0": []});
final wallet = await Hive.openBox<dynamic>(testWalletId);
// restore so we have something to rescan
await doge?.recoverFromMnemonic(
@ -2230,6 +2166,16 @@ void main() {
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(2);
verify(client?.getBatchHistory(args: {
"0": [
"c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc"
]
})).called(2);
verify(client?.getBatchHistory(args: {
"0": [
"80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c"
]
})).called(2);
verify(cachedClient?.clearSharedTransactionCache(coin: Coin.dogecoin))
.called(1);
@ -2259,6 +2205,18 @@ void main() {
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: {
"0": [
"c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc"
]
})).thenAnswer((realInvocation) async => {"0": []});
when(client?.getBatchHistory(args: {
"0": [
"80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c"
]
})).thenAnswer((realInvocation) async => {"0": []});
when(cachedClient?.clearSharedTransactionCache(coin: Coin.dogecoin))
.thenAnswer((realInvocation) async {});
@ -2318,7 +2276,17 @@ void main() {
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(2);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(2);
verify(client?.getBatchHistory(args: {
"0": [
"c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc"
]
})).called(2);
verify(client?.getBatchHistory(args: {
"0": [
"80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c"
]
})).called(1);
verify(cachedClient?.clearSharedTransactionCache(coin: Coin.dogecoin))
.called(1);
@ -2814,6 +2782,17 @@ void main() {
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: {
"0": [
"c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc"
]
})).thenAnswer((realInvocation) async => {"0": []});
when(client?.getBatchHistory(args: {
"0": [
"80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c"
]
})).thenAnswer((realInvocation) async => {"0": []});
final wallet = await Hive.openBox(testWalletId);
@ -2831,6 +2810,16 @@ void main() {
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
verify(client?.getBatchHistory(args: {
"0": [
"c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc"
]
})).called(1);
verify(client?.getBatchHistory(args: {
"0": [
"80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c"
]
})).called(1);
expect(secureStore?.interactions, 6);
expect(secureStore?.writes, 3);
@ -2859,6 +2848,17 @@ void main() {
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: historyBatchArgs1))
.thenAnswer((_) async => historyBatchResponse);
when(client?.getBatchHistory(args: {
"0": [
"c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc"
]
})).thenAnswer((realInvocation) async => {"0": []});
when(client?.getBatchHistory(args: {
"0": [
"80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c"
]
})).thenAnswer((realInvocation) async => {"0": []});
when(client?.getHistory(scripthash: anyNamed("scripthash")))
.thenThrow(Exception("some exception"));
@ -2876,6 +2876,16 @@ void main() {
verify(client?.getServerFeatures()).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs0)).called(1);
verify(client?.getBatchHistory(args: historyBatchArgs1)).called(1);
verify(client?.getBatchHistory(args: {
"0": [
"c82d4ac9697408d423d59dc53267f6474bbd4c22c55fd42ba766e80c6068e7dc"
]
})).called(1);
verify(client?.getBatchHistory(args: {
"0": [
"80badd62a8dd884cc7f61d962484564929340debb27f88fef270e553306a030c"
]
})).called(1);
verify(client?.getBlockHeadTip()).called(1);
verify(client?.getHistory(scripthash: anyNamed("scripthash"))).called(1);

View file

@ -26,6 +26,7 @@ import 'firo_wallet_test.mocks.dart';
import 'firo_wallet_test_parameters.dart';
import 'sample_data/get_anonymity_set_sample_data.dart';
import 'sample_data/get_used_serials_sample_data.dart';
import 'sample_data/get_utxos_sample_data.dart';
import 'sample_data/gethistory_samples.dart';
import 'sample_data/transaction_data_samples.dart';
@ -66,18 +67,15 @@ void main() {
return SampleGetTransactionData.txData7;
});
final result = await isolateRestore(
final message = await isolateRestore(
TEST_MNEMONIC,
txData,
"USD",
Coin.firo,
1,
setData,
usedSerials,
firoNetwork,
Decimal.ten,
"en_US",
);
final result = await staticProcessRestore(txData, message);
expect(result, isA<Map<String, dynamic>>());
expect(result["mintIndex"], 8);
@ -88,22 +86,17 @@ void main() {
});
test("isolateRestore throws", () async {
final txData = TransactionData();
final Map setData = {};
final usedSerials = [];
expect(
() => isolateRestore(
TEST_MNEMONIC,
txData,
"USD",
Coin.firo,
1,
setData,
usedSerials,
firoNetwork,
Decimal.ten,
"en_US",
),
throwsA(isA<Error>()));
});
@ -1018,15 +1011,8 @@ void main() {
"f4217364cbe6a81ef7ecaaeba0a6d6b576a9850b3e891fa7b88ed4927c505218"))
.thenAnswer((realInvocation) => true);
// mock history calls
when(client.getHistory(scripthash: SampleGetHistoryData.scripthash0))
.thenAnswer((_) async => SampleGetHistoryData.data0);
when(client.getHistory(scripthash: SampleGetHistoryData.scripthash1))
.thenAnswer((_) async => SampleGetHistoryData.data1);
when(client.getHistory(scripthash: SampleGetHistoryData.scripthash2))
.thenAnswer((_) async => SampleGetHistoryData.data2);
when(client.getHistory(scripthash: SampleGetHistoryData.scripthash3))
.thenAnswer((_) async => SampleGetHistoryData.data3);
when(client.getBatchHistory(args: batchHistoryRequest0))
.thenAnswer((realInvocation) async => batchHistoryResponse0);
// mock transaction calls
when(cachedClient.getTransaction(
@ -1321,6 +1307,48 @@ void main() {
(_) async => GetAnonymitySetSampleData.data,
);
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash0,
coin: Coin.firo,
)).thenAnswer((_) async {
return SampleGetTransactionData.txData0;
});
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash1,
coin: Coin.firo,
)).thenAnswer((_) async {
return SampleGetTransactionData.txData1;
});
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash2,
coin: Coin.firo,
)).thenAnswer((_) async {
return SampleGetTransactionData.txData2;
});
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash3,
coin: Coin.firo,
)).thenAnswer((_) async {
return SampleGetTransactionData.txData3;
});
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash4,
coin: Coin.firo,
)).thenAnswer((_) async {
return SampleGetTransactionData.txData4;
});
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash5,
coin: Coin.firo,
)).thenAnswer((_) async {
return SampleGetTransactionData.txData5;
});
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash6,
coin: Coin.firo,
)).thenAnswer((_) async {
return SampleGetTransactionData.txData6;
});
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash8,
coin: Coin.firo,
@ -1415,6 +1443,14 @@ void main() {
.thenAnswer((_) async => data);
}
when(client.getBatchHistory(args: {
"0": [SampleGetHistoryData.scripthash0],
"1": [SampleGetHistoryData.scripthash3]
})).thenAnswer((realInvocation) async => {
"0": SampleGetHistoryData.data0,
"1": SampleGetHistoryData.data3,
});
await firo.recoverFromMnemonic(
mnemonic: TEST_MNEMONIC,
maxUnusedAddressGap: 20,
@ -1473,8 +1509,8 @@ void main() {
expect(jIndex, [2, 4, 6]);
final lelantusTxModel = await wallet.get('latest_lelantus_tx_model');
expect(lelantusTxModel.getAllTransactions().length, 3);
}, timeout: const Timeout(Duration(minutes: 3)));
expect(lelantusTxModel.getAllTransactions().length, 5);
}, timeout: const Timeout(Duration(minutes: 5)));
test("fullRescan succeeds", () async {
TestWidgetsFlutterBinding.ensureInitialized();
@ -1580,6 +1616,14 @@ void main() {
.thenAnswer((_) async => data);
}
when(client.getBatchHistory(args: {
"0": [SampleGetHistoryData.scripthash0],
"1": [SampleGetHistoryData.scripthash3]
})).thenAnswer((realInvocation) async => {
"0": SampleGetHistoryData.data0,
"1": SampleGetHistoryData.data3,
});
// mock transaction calls
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash0,
@ -1680,8 +1724,8 @@ void main() {
expect(jIndex, [2, 4, 6]);
final lelantusTxModel = await wallet.get('latest_lelantus_tx_model');
expect(lelantusTxModel.getAllTransactions().length, 3);
}, timeout: Timeout(Duration(minutes: 3)));
expect(lelantusTxModel.getAllTransactions().length, 5);
}, timeout: const Timeout(Duration(minutes: 3)));
test("fullRescan fails", () async {
TestWidgetsFlutterBinding.ensureInitialized();
@ -1939,6 +1983,14 @@ void main() {
.thenAnswer((_) async => data);
}
when(client.getBatchHistory(args: {
"0": [SampleGetHistoryData.scripthash0],
"1": [SampleGetHistoryData.scripthash3]
})).thenAnswer((realInvocation) async => {
"0": SampleGetHistoryData.data0,
"1": SampleGetHistoryData.data3,
});
// mock transaction calls
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash0,
@ -2043,7 +2095,7 @@ void main() {
expect(jIndex, [2, 4, 6]);
final lelantusTxModel = await wallet.get('latest_lelantus_tx_model');
expect(lelantusTxModel.getAllTransactions().length, 3);
expect(lelantusTxModel.getAllTransactions().length, 5);
await firo.fullRescan(20, 1000);
@ -2099,7 +2151,7 @@ void main() {
expect(_jIndex, [2, 4, 6]);
final _lelantusTxModel = await wallet.get('latest_lelantus_tx_model');
expect(_lelantusTxModel.getAllTransactions().length, 3);
expect(_lelantusTxModel.getAllTransactions().length, 5);
}, timeout: const Timeout(Duration(minutes: 6)));
test("recoverFromMnemonic fails testnet", () async {
@ -2397,7 +2449,11 @@ void main() {
expect(serials, GetUsedSerialsSampleData.serials['serials']);
});
test("refresh", () async {
test("firo refresh", () async {
TestWidgetsFlutterBinding.ensureInitialized();
const MethodChannel('uk.spiralarm.flutter/devicelocale')
.setMockMethodCallHandler((methodCall) async => 'en_US');
final client = MockElectrumX();
final cachedClient = MockCachedElectrumX();
final secureStore = FakeSecureStorage();
@ -2408,6 +2464,21 @@ void main() {
key: "${testWalletId}refresh_mnemonic",
value: RefreshTestParams.mnemonic);
when(client.getBatchUTXOs(args: batchUtxoRequest))
.thenAnswer((realInvocation) async => {});
when(client.getBatchHistory(args: {
"0": [SampleGetHistoryData.scripthash1],
"1": [SampleGetHistoryData.scripthash0],
"2": [SampleGetHistoryData.scripthash2],
"3": [SampleGetHistoryData.scripthash3],
})).thenAnswer((realInvocation) async => {
"0": SampleGetHistoryData.data1,
"1": SampleGetHistoryData.data0,
"2": SampleGetHistoryData.data2,
"3": SampleGetHistoryData.data3,
});
// mock electrumx client calls
when(client.getServerFeatures()).thenAnswer((_) async => {
"hosts": {},
@ -2688,6 +2759,10 @@ void main() {
// }, timeout: const Timeout(Duration(minutes: 3)));
test("send fails due to insufficient balance", () async {
TestWidgetsFlutterBinding.ensureInitialized();
const MethodChannel('uk.spiralarm.flutter/devicelocale')
.setMockMethodCallHandler((methodCall) async => 'en_US');
final client = MockElectrumX();
final cachedClient = MockCachedElectrumX();
final secureStore = FakeSecureStorage();
@ -2711,6 +2786,8 @@ void main() {
final txid = Format.uint8listToString(reversedBytes);
return txid;
});
when(client.getBatchHistory(args: batchHistoryRequest0))
.thenAnswer((realInvocation) async => batchHistoryResponse0);
when(cachedClient.getAnonymitySet(
groupId: "1",
@ -2859,9 +2936,13 @@ void main() {
() async => await firo.send(
toAddress: "aHZJsucDrhr4Uzzx6XXrKnaTgLxsEAokvV", amount: 100),
throwsA(isA<Exception>()));
}, timeout: Timeout(const Duration(minutes: 3)));
}, timeout: const Timeout(Duration(minutes: 3)));
test("send fails due to bad transaction created", () async {
TestWidgetsFlutterBinding.ensureInitialized();
const MethodChannel('uk.spiralarm.flutter/devicelocale')
.setMockMethodCallHandler((methodCall) async => 'en_US');
final client = MockElectrumX();
final cachedClient = MockCachedElectrumX();
final secureStore = FakeSecureStorage();
@ -2875,6 +2956,9 @@ void main() {
return "some bad txid";
});
when(client.getBatchHistory(args: batchHistoryRequest0))
.thenAnswer((realInvocation) async => batchHistoryResponse0);
when(cachedClient.getAnonymitySet(
groupId: "1",
coin: Coin.firo,
@ -3022,9 +3106,13 @@ void main() {
() async => await firo.send(
toAddress: "aHZJsucDrhr4Uzzx6XXrKnaTgLxsEAokvV", amount: 100),
throwsA(isA<Exception>()));
}, timeout: Timeout(const Duration(minutes: 3)));
}, timeout: const Timeout(Duration(minutes: 3)));
test("wallet balances", () async {
TestWidgetsFlutterBinding.ensureInitialized();
const MethodChannel('uk.spiralarm.flutter/devicelocale')
.setMockMethodCallHandler((methodCall) async => 'en_US');
final client = MockElectrumX();
final cachedClient = MockCachedElectrumX();
final priceAPI = MockPriceAPI();
@ -3042,6 +3130,12 @@ void main() {
when(client.getHistory(scripthash: SampleGetHistoryData.scripthash3))
.thenAnswer((_) async => SampleGetHistoryData.data3);
when(client.getBatchHistory(args: batchHistoryRequest0))
.thenAnswer((realInvocation) async => batchHistoryResponse0);
when(client.getBatchUTXOs(args: batchUtxoRequest))
.thenAnswer((realInvocation) async => {});
// mock transaction calls
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash0,
@ -3112,6 +3206,12 @@ void main() {
final priceAPI = MockPriceAPI();
final secureStore = FakeSecureStorage();
when(client.getBatchHistory(args: batchHistoryRequest0))
.thenAnswer((realInvocation) async => batchHistoryResponse0);
when(client.getBatchUTXOs(args: batchUtxoRequest))
.thenAnswer((realInvocation) async => {});
// mock price calls
when(priceAPI.getPricesAnd24hChange(baseCurrency: "USD")).thenAnswer(
(_) async => {Coin.firo: Tuple2(Decimal.fromInt(10), 1.0)});
@ -3189,6 +3289,10 @@ void main() {
});
test("wallet balance minus maxfee - wallet balance is not zero", () async {
TestWidgetsFlutterBinding.ensureInitialized();
const MethodChannel('uk.spiralarm.flutter/devicelocale')
.setMockMethodCallHandler((methodCall) async => 'en_US');
final client = MockElectrumX();
final cachedClient = MockCachedElectrumX();
final priceAPI = MockPriceAPI();
@ -3208,6 +3312,12 @@ void main() {
when(client.getHistory(scripthash: SampleGetHistoryData.scripthash3))
.thenAnswer((_) async => SampleGetHistoryData.data3);
when(client.getBatchHistory(args: batchHistoryRequest0))
.thenAnswer((realInvocation) async => batchHistoryResponse0);
when(client.getBatchUTXOs(args: batchUtxoRequest))
.thenAnswer((realInvocation) async => {});
// mock transaction calls
when(cachedClient.getTransaction(
txHash: SampleGetTransactionData.txHash0,

View file

@ -22,3 +22,18 @@ abstract class GetUtxoSampleData {
}
];
}
final batchUtxoRequest = {
"843be6c3b2d3fafc8a4eca78b1e1226961a3572357d7486b3a596cfaaf25fce8": [
"843be6c3b2d3fafc8a4eca78b1e1226961a3572357d7486b3a596cfaaf25fce8"
],
"477906c20249d06e4891c5c252957cdc4dd38b7932144a8b5407e2cdcdbf8be8": [
"477906c20249d06e4891c5c252957cdc4dd38b7932144a8b5407e2cdcdbf8be8"
],
"c6e07f2a0eec2f50d2938b79e4e570d7edd1c83ae1f4512d2041a1c07d84f3b2": [
"c6e07f2a0eec2f50d2938b79e4e570d7edd1c83ae1f4512d2041a1c07d84f3b2"
],
"0d23f206295307df70e97534c4ceb92f82b4ebe7983342f8fd287071f90caf38": [
"0d23f206295307df70e97534c4ceb92f82b4ebe7983342f8fd287071f90caf38"
]
};

View file

@ -52,3 +52,54 @@ abstract class SampleGetHistoryData {
"0d23f206295307df70e97534c4ceb92f82b4ebe7983342f8fd287071f90caf38";
static List<Map<String, dynamic>> data3 = [];
}
final batchHistoryRequest0 = {
"0": ["477906c20249d06e4891c5c252957cdc4dd38b7932144a8b5407e2cdcdbf8be8"],
"1": ["843be6c3b2d3fafc8a4eca78b1e1226961a3572357d7486b3a596cfaaf25fce8"],
"2": ["c6e07f2a0eec2f50d2938b79e4e570d7edd1c83ae1f4512d2041a1c07d84f3b2"],
"3": ["0d23f206295307df70e97534c4ceb92f82b4ebe7983342f8fd287071f90caf38"],
};
final Map<String, List<Map<String, dynamic>>> batchHistoryResponse0 = {
"0": [
{
"tx_hash":
"76032a2408e7fefd62a0c7e793f93a2984621e37a625cc1f9e8febadbe583a40",
"height": 455866
},
{
"tx_hash":
"36c92daa4005d368e28cea917fdb2c1e7069319a4a79fb2ff45c089100680232",
"height": 455876
}
],
"1": [
{
"tx_hash":
"9201d7a58185f000c312a8b0c19d8e5c61c1ce1b69201c1a4dc2bce289794280",
"height": 457339
},
{
"tx_hash":
"ea77e74edecd8c14ff5a8ddeb54e9e5e9c7c301c6f76f0ac1ac8119c6cc15e35",
"height": 457341
},
{
"tx_hash":
"ac0322cfdd008fa2a79bec525468fd05cf51a5a4e2c2e9c15598b659ec71ac68",
"height": 457373
},
{
"tx_hash":
"e8e4bfc080bd6133d38263d2ac7ef6f60dfd73eb29b464e34766ebb5a0d27dd8",
"height": 457376
},
{
"tx_hash":
"51576e2230c2911a508aabb85bb50045f04b8dc958790ce2372986c3ebbe7d3e",
"height": 457379
},
],
"2": [],
"3": [],
};

View file

@ -232,6 +232,22 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet {
super.noSuchMethod(Invocation.method(#stopNetworkAlivePinging, []),
returnValueForMissingStub: null);
@override
_i8.Future<Map<String, dynamic>> prepareSendPublic(
{String? address, int? satoshiAmount, Map<String, dynamic>? args}) =>
(super.noSuchMethod(
Invocation.method(#prepareSendPublic, [], {
#address: address,
#satoshiAmount: satoshiAmount,
#args: args
}),
returnValue:
Future<Map<String, dynamic>>.value(<String, dynamic>{}))
as _i8.Future<Map<String, dynamic>>);
@override
_i8.Future<String> confirmSendPublic({dynamic txData}) => (super.noSuchMethod(
Invocation.method(#confirmSendPublic, [], {#txData: txData}),
returnValue: Future<String>.value('')) as _i8.Future<String>);
@override
_i8.Future<Map<String, dynamic>> prepareSend(
{String? address, int? satoshiAmount, Map<String, dynamic>? args}) =>
(super.noSuchMethod(
@ -257,6 +273,47 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet {
#send, [], {#toAddress: toAddress, #amount: amount, #args: args}),
returnValue: Future<String>.value('')) as _i8.Future<String>);
@override
int estimateTxFee({int? vSize, int? feeRatePerKB}) => (super.noSuchMethod(
Invocation.method(
#estimateTxFee, [], {#vSize: vSize, #feeRatePerKB: feeRatePerKB}),
returnValue: 0) as int);
@override
dynamic coinSelection(int? satoshiAmountToSend, int? selectedTxFeeRate,
String? _recipientAddress, bool? isSendAll,
{int? additionalOutputs = 0, List<_i4.UtxoObject>? utxos}) =>
super.noSuchMethod(Invocation.method(#coinSelection, [
satoshiAmountToSend,
selectedTxFeeRate,
_recipientAddress,
isSendAll
], {
#additionalOutputs: additionalOutputs,
#utxos: utxos
}));
@override
_i8.Future<Map<String, dynamic>> fetchBuildTxData(
List<_i4.UtxoObject>? utxosToUse) =>
(super.noSuchMethod(Invocation.method(#fetchBuildTxData, [utxosToUse]),
returnValue:
Future<Map<String, dynamic>>.value(<String, dynamic>{}))
as _i8.Future<Map<String, dynamic>>);
@override
_i8.Future<Map<String, dynamic>> buildTransaction(
{List<_i4.UtxoObject>? utxosToUse,
Map<String, dynamic>? utxoSigningData,
List<String>? recipients,
List<int>? satoshiAmounts}) =>
(super.noSuchMethod(
Invocation.method(#buildTransaction, [], {
#utxosToUse: utxosToUse,
#utxoSigningData: utxoSigningData,
#recipients: recipients,
#satoshiAmounts: satoshiAmounts
}),
returnValue:
Future<Map<String, dynamic>>.value(<String, dynamic>{}))
as _i8.Future<Map<String, dynamic>>);
@override
_i8.Future<void> updateNode(bool? shouldRefresh) =>
(super.noSuchMethod(Invocation.method(#updateNode, [shouldRefresh]),
returnValue: Future<void>.value(),
@ -293,8 +350,8 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet {
returnValue: <Map<dynamic, _i10.LelantusCoin>>[])
as List<Map<dynamic, _i10.LelantusCoin>>);
@override
_i8.Future<void> autoMint() =>
(super.noSuchMethod(Invocation.method(#autoMint, []),
_i8.Future<void> anonymizeAllPublicFunds() =>
(super.noSuchMethod(Invocation.method(#anonymizeAllPublicFunds, []),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i8.Future<void>);
@override
@ -325,6 +382,11 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet {
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i8.Future<void>);
@override
_i8.Future<void> checkChangeAddressForTransactions() => (super.noSuchMethod(
Invocation.method(#checkChangeAddressForTransactions, []),
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i8.Future<void>);
@override
_i8.Future<void> fillAddresses(String? suppliedMnemonic,
{int? perBatch = 50, int? numberOfThreads = 4}) =>
(super.noSuchMethod(
@ -367,6 +429,11 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet {
returnValue: Future<void>.value(),
returnValueForMissingStub: Future<void>.value()) as _i8.Future<void>);
@override
_i8.Future<Map<int, dynamic>> getSetDataMap(int? latestSetId) =>
(super.noSuchMethod(Invocation.method(#getSetDataMap, [latestSetId]),
returnValue: Future<Map<int, dynamic>>.value(<int, dynamic>{}))
as _i8.Future<Map<int, dynamic>>);
@override
_i8.Future<List<Map<String, dynamic>>> fetchAnonymitySets() =>
(super.noSuchMethod(Invocation.method(#fetchAnonymitySets, []),
returnValue: Future<List<Map<String, dynamic>>>.value(
@ -387,11 +454,11 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet {
returnValueForMissingStub: Future<void>.value()) as _i8.Future<void>);
@override
_i8.Future<dynamic> getCoinsToJoinSplit(int? required) =>
(super.noSuchMethod(Invocation.method(#GetCoinsToJoinSplit, [required]),
(super.noSuchMethod(Invocation.method(#getCoinsToJoinSplit, [required]),
returnValue: Future<dynamic>.value()) as _i8.Future<dynamic>);
@override
_i8.Future<int> estimateJoinSplitFee(int? spendAmount) => (super.noSuchMethod(
Invocation.method(#EstimateJoinSplitFee, [spendAmount]),
Invocation.method(#estimateJoinSplitFee, [spendAmount]),
returnValue: Future<int>.value(0)) as _i8.Future<int>);
@override
_i8.Future<int> estimateFeeFor(int? satoshiAmount, int? feeRate) =>
@ -399,6 +466,27 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i8.Future<int>);
@override
_i8.Future<int> estimateFeeForPublic(int? satoshiAmount, int? feeRate) =>
(super.noSuchMethod(
Invocation.method(#estimateFeeForPublic, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i8.Future<int>);
@override
int roughFeeEstimate(int? inputCount, int? outputCount, int? feeRatePerKB) =>
(super.noSuchMethod(
Invocation.method(
#roughFeeEstimate, [inputCount, outputCount, feeRatePerKB]),
returnValue: 0) as int);
@override
int sweepAllEstimate(int? feeRate) =>
(super.noSuchMethod(Invocation.method(#sweepAllEstimate, [feeRate]),
returnValue: 0) as int);
@override
_i8.Future<List<Map<String, dynamic>>> fastFetch(List<String>? allTxHashes) =>
(super.noSuchMethod(Invocation.method(#fastFetch, [allTxHashes]),
returnValue: Future<List<Map<String, dynamic>>>.value(
<Map<String, dynamic>>[]))
as _i8.Future<List<Map<String, dynamic>>>);
@override
_i8.Future<List<_i4.Transaction>> getJMintTransactions(
_i6.CachedElectrumX? cachedClient,
List<String>? transactions,
@ -418,6 +506,20 @@ class MockFiroWallet extends _i1.Mock implements _i7.FiroWallet {
returnValue:
Future<List<_i4.Transaction>>.value(<_i4.Transaction>[]))
as _i8.Future<List<_i4.Transaction>>);
@override
_i8.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i8.Future<bool>);
@override
_i8.Future<_i3.Decimal> availablePrivateBalance() =>
(super.noSuchMethod(Invocation.method(#availablePrivateBalance, []),
returnValue: Future<_i3.Decimal>.value(_FakeDecimal_1()))
as _i8.Future<_i3.Decimal>);
@override
_i8.Future<_i3.Decimal> availablePublicBalance() =>
(super.noSuchMethod(Invocation.method(#availablePublicBalance, []),
returnValue: Future<_i3.Decimal>.value(_FakeDecimal_1()))
as _i8.Future<_i3.Decimal>);
}
/// A class which mocks [ElectrumX].

View file

@ -250,6 +250,10 @@ class MockManager extends _i1.Mock implements _i5.Manager {
Invocation.method(#estimateFeeFor, [satoshiAmount, feeRate]),
returnValue: Future<int>.value(0)) as _i7.Future<int>);
@override
_i7.Future<bool> generateNewAddress() =>
(super.noSuchMethod(Invocation.method(#generateNewAddress, []),
returnValue: Future<bool>.value(false)) as _i7.Future<bool>);
@override
void addListener(_i8.VoidCallback? listener) =>
super.noSuchMethod(Invocation.method(#addListener, [listener]),
returnValueForMissingStub: null);