mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-12-23 03:49:22 +00:00
Merge branch 'staging' into rylee-tmp
This commit is contained in:
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
|
|
@ -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",
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
widget.unawaitedLoad();
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
|
|
|
@ -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(
|
||||
|
|
140
lib/services/change_now/change_now_loading_service.dart
Normal file
140
lib/services/change_now/change_now_loading_service.dart
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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 = [];
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
);
|
||||
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,27 +4075,22 @@ class FiroWallet extends CoinServiceAPI {
|
|||
|
||||
bool longMutex = false;
|
||||
|
||||
/// Recovers wallet from [suppliedMnemonic]. Expects a valid mnemonic.
|
||||
Future<void> _recoverWalletFromBIP32SeedPhrase(
|
||||
String suppliedMnemonic, int maxUnusedAddressGap) async {
|
||||
longMutex = true;
|
||||
Logging.instance
|
||||
.log("PROCESSORS ${Platform.numberOfProcessors}", level: LogLevel.Info);
|
||||
try {
|
||||
Future<Map<int, dynamic>> getSetDataMap(int latestSetId) async {
|
||||
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: () => {});
|
||||
final setData = anonymitySets
|
||||
.firstWhere((element) => element["setId"] == setId, orElse: () => {});
|
||||
|
||||
if (setData.isNotEmpty) {
|
||||
setDataMap[setId] = setData;
|
||||
}
|
||||
}
|
||||
final usedSerialNumbers = getUsedCoinSerials();
|
||||
return setDataMap;
|
||||
}
|
||||
|
||||
Future<void> _makeDerivations(
|
||||
String suppliedMnemonic, int maxUnusedAddressGap) async {
|
||||
List<String> receivingAddressArray = [];
|
||||
List<String> changeAddressArray = [];
|
||||
|
||||
|
@ -4188,12 +4202,29 @@ class FiroWallet extends CoinServiceAPI {
|
|||
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 {
|
||||
longMutex = true;
|
||||
Logging.instance
|
||||
.log("PROCESSORS ${Platform.numberOfProcessors}", level: LogLevel.Info);
|
||||
try {
|
||||
final latestSetId = await getLatestSetId();
|
||||
final setDataMap = getSetDataMap(latestSetId);
|
||||
final usedSerialNumbers = getUsedCoinSerials();
|
||||
final makeDerivations =
|
||||
_makeDerivations(suppliedMnemonic, maxUnusedAddressGap);
|
||||
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
@ -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?;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>>>);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>>>);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
||||
];
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -22,3 +22,18 @@ abstract class GetUtxoSampleData {
|
|||
}
|
||||
];
|
||||
}
|
||||
|
||||
final batchUtxoRequest = {
|
||||
"843be6c3b2d3fafc8a4eca78b1e1226961a3572357d7486b3a596cfaaf25fce8": [
|
||||
"843be6c3b2d3fafc8a4eca78b1e1226961a3572357d7486b3a596cfaaf25fce8"
|
||||
],
|
||||
"477906c20249d06e4891c5c252957cdc4dd38b7932144a8b5407e2cdcdbf8be8": [
|
||||
"477906c20249d06e4891c5c252957cdc4dd38b7932144a8b5407e2cdcdbf8be8"
|
||||
],
|
||||
"c6e07f2a0eec2f50d2938b79e4e570d7edd1c83ae1f4512d2041a1c07d84f3b2": [
|
||||
"c6e07f2a0eec2f50d2938b79e4e570d7edd1c83ae1f4512d2041a1c07d84f3b2"
|
||||
],
|
||||
"0d23f206295307df70e97534c4ceb92f82b4ebe7983342f8fd287071f90caf38": [
|
||||
"0d23f206295307df70e97534c4ceb92f82b4ebe7983342f8fd287071f90caf38"
|
||||
]
|
||||
};
|
||||
|
|
|
@ -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": [],
|
||||
};
|
||||
|
|
|
@ -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].
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue